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.cc b/runtime/reference_table.cc
index e6e588e..a6df27b 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -19,6 +19,8 @@
 #include "android-base/stringprintf.h"
 
 #include "base/mutex.h"
+#include "gc/allocation_record.h"
+#include "gc/heap.h"
 #include "indirect_reference_table.h"
 #include "mirror/array-inl.h"
 #include "mirror/array.h"
@@ -206,6 +208,54 @@
       }
     }
     os << StringPrintf("    %5d: ", idx) << ref << " " << className << extras << "\n";
+    if (runtime->GetHeap()->IsAllocTrackingEnabled()) {
+      MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_);
+
+      gc::AllocRecordObjectMap* records = runtime->GetHeap()->GetAllocationRecords();
+      DCHECK(records != nullptr);
+      // It's annoying that this is a list. But this code should be very uncommon to be executed.
+
+      auto print_stack = [&](ObjPtr<mirror::Object> to_print, const std::string& msg)
+          REQUIRES_SHARED(Locks::mutator_lock_)
+          REQUIRES(Locks::alloc_tracker_lock_) {
+        for (auto it = records->Begin(), end = records->End(); it != end; ++it) {
+          GcRoot<mirror::Object>& stack_for_object = it->first;
+          gc::AllocRecord& record = it->second;
+          if (stack_for_object.Read() == to_print.Ptr()) {
+            os << "          " << msg << "\n";
+            const gc::AllocRecordStackTrace* trace = record.GetStackTrace();
+            size_t depth = trace->GetDepth();
+            if (depth == 0) {
+              os << "            (No managed frames)\n";
+            } else {
+              for (size_t i = 0; i < depth; ++i) {
+                const gc::AllocRecordStackTraceElement& frame = trace->GetStackElement(i);
+                os << "            ";
+                if (frame.GetMethod() == nullptr) {
+                  os << "(missing method data)\n";
+                  continue;
+                }
+                os << frame.GetMethod()->PrettyMethod(true)
+                   << ":"
+                   << frame.ComputeLineNumber()
+                   << "\n";
+              }
+            }
+            break;
+          }
+        }
+      };
+      // Print the stack trace of the ref.
+      print_stack(ref, "Allocated at:");
+
+      // If it's a reference, see if we have data about the referent.
+      if (ref->IsReferenceInstance()) {
+        ObjPtr<mirror::Object> referent = ref->AsReference()->GetReferent();
+        if (referent != nullptr) {
+          print_stack(referent, "Referent allocated at:");
+        }
+      }
+    }
   }
 
   // Make a copy of the table and sort it, only adding non null and not cleared elements.