ART: Dump allocation stacks in reference table dumps
When allocation tracking is enabled and allocation stacks are available,
print the stack traces of the objects in a reference table dumps, to
aid tracking table overflows.
Extend reference_table_test.
Bug: 67044702
Test: m test-art-host
Change-Id: I0118ba095f08dc66739707cd6a184487974b1570
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index d830387..1e7fc3e 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -16,6 +16,8 @@
#include "reference_table.h"
+#include <regex>
+
#include "android-base/stringprintf.h"
#include "art_method-inl.h"
@@ -30,6 +32,7 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
+#include "well_known_classes.h"
namespace art {
@@ -156,6 +159,7 @@
rt.Dump(oss);
EXPECT_NE(oss.str().find("java.lang.ref.WeakReference (referent is null)"), std::string::npos)
<< oss.str();
+ rt.Remove(empty_reference);
}
{
@@ -168,6 +172,86 @@
EXPECT_NE(oss.str().find("java.lang.ref.WeakReference (referent is a java.lang.String)"),
std::string::npos)
<< oss.str();
+ rt.Remove(non_empty_reference);
+ }
+
+ // Add two objects. Enable allocation tracking for the latter.
+ {
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::String> h_without_trace(hs.NewHandle(
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), "Without")));
+
+ {
+ ScopedThreadSuspension sts(soa.Self(), ThreadState::kSuspended);
+ gc::AllocRecordObjectMap::SetAllocTrackingEnabled(true);
+ }
+
+ // To get a stack, actually make a call. Use substring, that's simple. Calling through JNI
+ // avoids having to create the low-level args array ourselves.
+ Handle<mirror::Object> h_with_trace;
+ {
+ jmethodID substr = soa.Env()->GetMethodID(WellKnownClasses::java_lang_String,
+ "substring",
+ "(II)Ljava/lang/String;");
+ ASSERT_TRUE(substr != nullptr);
+ jobject jobj = soa.Env()->AddLocalReference<jobject>(h_without_trace.Get());
+ ASSERT_TRUE(jobj != nullptr);
+ jobject result = soa.Env()->CallObjectMethod(jobj,
+ substr,
+ static_cast<jint>(0),
+ static_cast<jint>(4));
+ ASSERT_TRUE(result != nullptr);
+ h_with_trace = hs.NewHandle(soa.Self()->DecodeJObject(result));
+ }
+
+ Handle<mirror::Object> h_ref;
+ {
+ jclass weak_ref_class = soa.Env()->FindClass("java/lang/ref/WeakReference");
+ ASSERT_TRUE(weak_ref_class != nullptr);
+ jmethodID init = soa.Env()->GetMethodID(weak_ref_class,
+ "<init>",
+ "(Ljava/lang/Object;)V");
+ ASSERT_TRUE(init != nullptr);
+ jobject referent = soa.Env()->AddLocalReference<jobject>(h_with_trace.Get());
+ jobject result = soa.Env()->NewObject(weak_ref_class, init, referent);
+ ASSERT_TRUE(result != nullptr);
+ h_ref = hs.NewHandle(soa.Self()->DecodeJObject(result));
+ }
+
+ rt.Add(h_without_trace.Get());
+ rt.Add(h_with_trace.Get());
+ rt.Add(h_ref.Get());
+
+ std::ostringstream oss;
+ rt.Dump(oss);
+
+ constexpr const char* kStackTracePattern =
+ R"(test reference table dump:\n)"
+ R"( Last 3 entries \(of 3\):\n)" // NOLINT
+ R"( 2: 0x[0-9a-f]* java.lang.ref.WeakReference \(referent is a java.lang.String\)\n)" // NOLINT
+ R"( Allocated at:\n)"
+ R"( \(No managed frames\)\n)" // NOLINT
+ R"( Referent allocated at:\n)"
+ R"( java.lang.String java.lang.String.fastSubstring\(int, int\):-2\n)" // NOLINT
+ R"( java.lang.String java.lang.String.substring\(int, int\):[0-9]*\n)" // NOLINT
+ R"( 1: 0x[0-9a-f]* java.lang.String "With"\n)"
+ R"( Allocated at:\n)"
+ R"( java.lang.String java.lang.String.fastSubstring\(int, int\):-2\n)" // NOLINT
+ R"( java.lang.String java.lang.String.substring\(int, int\):[0-9]*\n)" // NOLINT
+ R"( 0: 0x[0-9a-f]* java.lang.String "Without"\n)"
+ R"( Summary:\n)"
+ R"( 2 of java.lang.String \(2 unique instances\)\n)" // NOLINT
+ R"( 1 of java.lang.ref.WeakReference\n)";
+ std::regex stack_trace_regex(kStackTracePattern);
+ std::smatch stack_trace_match;
+ std::string str = oss.str();
+ bool found = std::regex_search(str, stack_trace_match, stack_trace_regex);
+ EXPECT_TRUE(found) << str;
+
+ {
+ ScopedThreadSuspension sts(soa.Self(), ThreadState::kSuspended);
+ gc::AllocRecordObjectMap::SetAllocTrackingEnabled(false);
+ }
}
}