ART: Add klass filter support

Add support for klass filter to FollowReferences. Update test.

Bug: 31385354
Test: m test-art-host-run-test-913-heaps
Change-Id: If8f0fb67a931fb358d99b49e774a197388796b6e
diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc
index 2fbc12b..eb2cbbd 100644
--- a/runtime/openjdkjvmti/ti_heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -314,11 +314,13 @@
                          jvmtiEnv* jvmti_env,
                          art::ObjPtr<art::mirror::Object> initial_object,
                          const jvmtiHeapCallbacks* callbacks,
+                         art::ObjPtr<art::mirror::Class> class_filter,
                          const void* user_data)
       : env(jvmti_env),
         tag_table_(h->GetTags()),
         initial_object_(initial_object),
         callbacks_(callbacks),
+        class_filter_(class_filter),
         user_data_(user_data),
         start_(0),
         stop_reports_(false) {
@@ -760,6 +762,10 @@
       return 0;
     }
 
+    if (UNLIKELY(class_filter_ != nullptr) && class_filter_ != referree->GetClass()) {
+      return JVMTI_VISIT_OBJECTS;
+    }
+
     const jlong class_tag = tag_table_->GetTagOrZero(referree->GetClass());
     const jlong referrer_class_tag =
         referrer == nullptr ? 0 : tag_table_->GetTagOrZero(referrer->GetClass());
@@ -779,6 +785,7 @@
         referrer_tag_ptr = &referrer_tag;
       }
     }
+
     jint length = -1;
     if (referree->IsArrayInstance()) {
       length = referree->AsArray()->GetLength();
@@ -808,6 +815,7 @@
   ObjectTagTable* tag_table_;
   art::ObjPtr<art::mirror::Object> initial_object_;
   const jvmtiHeapCallbacks* callbacks_;
+  art::ObjPtr<art::mirror::Class> class_filter_;
   const void* user_data_;
 
   std::vector<art::mirror::Object*> worklist_;
@@ -823,7 +831,7 @@
 
 jvmtiError HeapUtil::FollowReferences(jvmtiEnv* env,
                                       jint heap_filter ATTRIBUTE_UNUSED,
-                                      jclass klass ATTRIBUTE_UNUSED,
+                                      jclass klass,
                                       jobject initial_object,
                                       const jvmtiHeapCallbacks* callbacks,
                                       const void* user_data) {
@@ -844,10 +852,14 @@
     art::ScopedThreadSuspension sts(self, art::kWaitingForVisitObjects);
     art::ScopedSuspendAll ssa("FollowReferences");
 
+    art::ObjPtr<art::mirror::Class> class_filter = klass == nullptr
+        ? nullptr
+        : art::ObjPtr<art::mirror::Class>::DownCast(self->DecodeJObject(klass));
     FollowReferencesHelper frh(this,
                                env,
                                self->DecodeJObject(initial_object),
                                callbacks,
+                               class_filter,
                                user_data);
     frh.Init();
     frh.Work();
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index 6432172..ff668df 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -90,3 +90,32 @@
 4@0 (18, 3xS '010002000300')
 1@0 (14, 2xZ '0001')
 23456789
+--- klass ---
+root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1]
+0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
+1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
+3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
+5@1002 --(field@24)--> 6@1000 [size=16, length=-1]
+5@1002 --(field@28)--> 1@1000 [size=16, length=-1]
+---
+1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
+3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
+5@1002 --(field@24)--> 6@1000 [size=16, length=-1]
+5@1002 --(field@28)--> 1@1000 [size=16, length=-1]
+---
+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[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
+root@root --(thread)--> 1@1000 [size=16, length=-1]
+1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
+3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
+5@1002 --(field@24)--> 6@1000 [size=16, length=-1]
+5@1002 --(field@28)--> 1@1000 [size=16, length=-1]
+---
+1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
+3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
+5@1002 --(field@24)--> 6@1000 [size=16, length=-1]
+5@1002 --(field@28)--> 1@1000 [size=16, length=-1]
+---
diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java
index 9a16196..7a91d1f 100644
--- a/test/913-heaps/src/Main.java
+++ b/test/913-heaps/src/Main.java
@@ -27,6 +27,10 @@
 
     doStringTest();
     doPrimitiveArrayTest();
+
+    // Test klass filter.
+    System.out.println("--- klass ---");
+    new TestConfig(A.class, 0).doFollowReferencesTest();
   }
 
   public static void doTest() throws Exception {