ART: Stack locals

Add reporting of stack-locals roots. Use the new
precise root visiting to get dalvik register
information for compiled frames.

Bug: 31385354
Test: m test-art-host-run-test-913-heaps
Change-Id: Ieb86f67829e546692c30faa08eb44e8dcf2b2c6a
diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc
index 894cec2..7b2521d 100644
--- a/runtime/openjdkjvmti/ti_heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -190,7 +190,12 @@
       REQUIRES(!*tag_table_->GetAllowDisallowLock()) {
     if (initial_object_.IsNull()) {
       CollectAndReportRootsVisitor carrv(this, tag_table_, &worklist_, &visited_);
-      art::Runtime::Current()->VisitRoots(&carrv);
+
+      // We need precise info (e.g., vregs).
+      constexpr art::VisitRootFlags kRootFlags = static_cast<art::VisitRootFlags>(
+          art::VisitRootFlags::kVisitRootFlagAllRoots | art::VisitRootFlags::kVisitRootFlagPrecise);
+      art::Runtime::Current()->VisitRoots(&carrv, kRootFlags);
+
       art::Runtime::Current()->VisitImageRoots(&carrv);
       stop_reports_ = carrv.IsStopReports();
 
@@ -322,7 +327,36 @@
         }
 
         case art::RootType::kRootJavaFrame:
+        {
+          uint32_t thread_id = info.GetThreadId();
+          ref_info->stack_local.thread_id = thread_id;
+
+          art::Thread* thread = FindThread(info);
+          if (thread != nullptr) {
+            art::mirror::Object* thread_obj = thread->GetPeer();
+            if (thread->IsStillStarting()) {
+              thread_obj = nullptr;
+            } else {
+              thread_obj = thread->GetPeer();
+            }
+            if (thread_obj != nullptr) {
+              ref_info->stack_local.thread_tag = tag_table_->GetTagOrZero(thread_obj);
+            }
+          }
+
+          auto& java_info = static_cast<const art::JavaFrameRootInfo&>(info);
+          ref_info->stack_local.slot = static_cast<jint>(java_info.GetVReg());
+          const art::StackVisitor* visitor = java_info.GetVisitor();
+          ref_info->stack_local.location =
+              static_cast<jlocation>(visitor->GetDexPc(false /* abort_on_failure */));
+          ref_info->stack_local.depth = static_cast<jint>(visitor->GetFrameDepth());
+          art::ArtMethod* method = visitor->GetMethod();
+          if (method != nullptr) {
+            ref_info->stack_local.method = art::jni::EncodeArtMethod(method);
+          }
+
           return JVMTI_HEAP_REFERENCE_STACK_LOCAL;
+        }
 
         case art::RootType::kRootNativeStack:
         case art::RootType::kRootThreadBlock:
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index aba2a9a..e5fa53f 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -1,7 +1,7 @@
 ---
 true true
-root@root --(stack-local)--> 1@1000 [size=16, length=-1]
-root@root --(stack-local)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=11,location= 31])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1]
 root@root --(thread)--> 3000@0 [size=132, length=-1]
 0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
@@ -40,7 +40,9 @@
 ---
 root@root --(jni-global)--> 1@1000 [size=16, length=-1]
 root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local)--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 6])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 6])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=3,location= 18])--> 1@1000 [size=16, length=-1]
 root@root --(thread)--> 1@1000 [size=16, length=-1]
 root@root --(thread)--> 3000@0 [size=132, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 340671d..7b00fcd 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -269,6 +269,43 @@
       jvmtiHeapReferenceInfo info_;
     };
 
+    class StackLocalElement : public Elem {
+     public:
+      StackLocalElement(const std::string& referrer,
+                        const std::string& referree,
+                        jlong size,
+                        jint length,
+                        const jvmtiHeapReferenceInfo* reference_info)
+          : Elem(referrer, referree, size, length) {
+        memcpy(&info_, reference_info, sizeof(jvmtiHeapReferenceInfo));
+      }
+
+     protected:
+      std::string PrintArrowType() const OVERRIDE {
+        char* name = nullptr;
+        if (info_.stack_local.method != nullptr) {
+          jvmti_env->GetMethodName(info_.stack_local.method, &name, nullptr, nullptr);
+        }
+        std::string ret = StringPrintf("stack-local[id=%" PRId64 ",tag=%" PRId64 ",depth=%d,"
+                                       "method=%s,vreg=%d,location=% " PRId64 "]",
+                                       info_.stack_local.thread_id,
+                                       info_.stack_local.thread_tag,
+                                       info_.stack_local.depth,
+                                       name == nullptr ? "<null>" : name,
+                                       info_.stack_local.slot,
+                                       info_.stack_local.location);
+        if (name != nullptr) {
+          jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name));
+        }
+
+        return ret;
+      }
+
+     private:
+      const std::string string_;
+      jvmtiHeapReferenceInfo info_;
+    };
+
     // For simple or unimplemented cases.
     class StringElement : public Elem {
      public:
@@ -380,11 +417,11 @@
                                                          length,
                                                          "monitor"));
         case JVMTI_HEAP_REFERENCE_STACK_LOCAL:
-          return std::unique_ptr<Elem>(new StringElement(referrer,
-                                                         referree,
-                                                         size,
-                                                         length,
-                                                         "stack-local"));
+          return std::unique_ptr<Elem>(new StackLocalElement(referrer,
+                                                             referree,
+                                                             size,
+                                                             length,
+                                                             reference_info));
         case JVMTI_HEAP_REFERENCE_JNI_LOCAL:
           return std::unique_ptr<Elem>(new JNILocalElement(referrer,
                                                            referree,