ART: Dump referenced type in IRT overflows
When the runtime dumps a reference table, e.g., when aborting for
an overflow, dump the type of stored referents for reference types
to aid in debugging leaks.
Bug: 31600693
Test: m test-art-host-gtest-reference_table_test
Change-Id: Ia892dc84ca8827dd93a8b75d6f571c392f94859c
(cherry picked from commit 280f32b095f55f24dc557f9a9067d223901214ce)
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index f04d41d..0be79ef 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -192,6 +192,13 @@
} else {
StringAppendF(&extras, " \"%.16s... (%d chars)", utf8.c_str(), s->GetLength());
}
+ } else if (ref->IsReferenceInstance()) {
+ mirror::Object* referent = ref->AsReference()->GetReferent();
+ if (referent == nullptr) {
+ extras = " (referent is null)";
+ } else {
+ extras = StringPrintf(" (referent is a %s)", PrettyTypeOf(referent).c_str());
+ }
}
os << StringPrintf(" %5d: ", idx) << ref << " " << className << extras << "\n";
}
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index fae8e72..819e17a 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -16,11 +16,15 @@
#include "reference_table.h"
+#include "class_linker.h"
#include "common_runtime_test.h"
+#include "handle_scope-inl.h"
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
#include "mirror/string.h"
#include "primitive.h"
+#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "thread-inl.h"
@@ -28,6 +32,39 @@
class ReferenceTableTest : public CommonRuntimeTest {};
+static mirror::Object* CreateWeakReference(mirror::Object* referent)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* self = Thread::Current();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+ StackHandleScope<3> scope(self);
+ Handle<mirror::Object> h_referent(scope.NewHandle<mirror::Object>(referent));
+
+ Handle<mirror::Class> h_ref_class(scope.NewHandle<mirror::Class>(
+ class_linker->FindClass(self,
+ "Ljava/lang/ref/WeakReference;",
+ ScopedNullHandle<mirror::ClassLoader>())));
+ CHECK(h_ref_class.Get() != nullptr);
+ CHECK(class_linker->EnsureInitialized(self, h_ref_class, true, true));
+
+ Handle<mirror::Object> h_ref_instance(scope.NewHandle<mirror::Object>(
+ h_ref_class->AllocObject(self)));
+ CHECK(h_ref_instance.Get() != nullptr);
+
+ ArtMethod* constructor = h_ref_class->FindDeclaredDirectMethod(
+ "<init>", "(Ljava/lang/Object;)V", class_linker->GetImagePointerSize());
+ CHECK(constructor != nullptr);
+
+ uint32_t args[2];
+ args[0] = PointerToLowMemUInt32(h_ref_instance.Get());
+ args[1] = PointerToLowMemUInt32(h_referent.Get());
+ JValue result;
+ constructor->Invoke(self, args, sizeof(uint32_t), &result, constructor->GetShorty());
+ CHECK(!self->IsExceptionPending());
+
+ return h_ref_instance.Get();
+}
+
TEST_F(ReferenceTableTest, Basics) {
ScopedObjectAccess soa(Thread::Current());
mirror::Object* o1 = mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello");
@@ -104,6 +141,29 @@
std::string::npos) << oss.str();
}
}
+
+ // Add a reference and check that the type of the referent is dumped.
+ {
+ mirror::Object* empty_reference = CreateWeakReference(nullptr);
+ ASSERT_TRUE(empty_reference->IsReferenceInstance());
+ rt.Add(empty_reference);
+ std::ostringstream oss;
+ rt.Dump(oss);
+ EXPECT_NE(oss.str().find("java.lang.ref.WeakReference (referent is null)"), std::string::npos)
+ << oss.str();
+ }
+
+ {
+ mirror::Object* string_referent = mirror::String::AllocFromModifiedUtf8(Thread::Current(), "A");
+ mirror::Object* non_empty_reference = CreateWeakReference(string_referent);
+ ASSERT_TRUE(non_empty_reference->IsReferenceInstance());
+ rt.Add(non_empty_reference);
+ std::ostringstream oss;
+ rt.Dump(oss);
+ EXPECT_NE(oss.str().find("java.lang.ref.WeakReference (referent is a java.lang.String)"),
+ std::string::npos)
+ << oss.str();
+ }
}
} // namespace art