ART: Add string reporting

Add support for string_primitive_value_callback.

Bug: 31385354
Test: m test-art-host-run-test-906-iterate-heap
Test: m test-art-host-run-test-913-heaps
Change-Id: I69f68fd07869ba3a156a84fcb806821fce1d7c03
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
index 1362d47..0a0c68a 100644
--- a/test/906-iterate-heap/iterate_heap.cc
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -14,17 +14,21 @@
  * limitations under the License.
  */
 
+#include "inttypes.h"
+
 #include <iostream>
 #include <pthread.h>
 #include <stdio.h>
 #include <vector>
 
+#include "android-base/stringprintf.h"
 #include "base/logging.h"
 #include "jni.h"
 #include "openjdkjvmti/jvmti.h"
 #include "ScopedPrimitiveArray.h"
 #include "ti-agent/common_helper.h"
 #include "ti-agent/common_load.h"
+#include "utf.h"
 
 namespace art {
 namespace Test906IterateHeap {
@@ -172,5 +176,61 @@
   Run(heap_filter, klass_filter, &config);
 }
 
+extern "C" JNIEXPORT jstring JNICALL Java_Main_iterateThroughHeapString(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
+  struct FindStringCallbacks {
+    explicit FindStringCallbacks(jlong t) : tag_to_find(t) {}
+
+    static jint JNICALL  HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED,
+                                               jlong size ATTRIBUTE_UNUSED,
+                                               jlong* tag_ptr ATTRIBUTE_UNUSED,
+                                               jint length ATTRIBUTE_UNUSED,
+                                               void* user_data ATTRIBUTE_UNUSED) {
+      return 0;
+    }
+
+    static jint JNICALL StringValueCallback(jlong class_tag,
+                                            jlong size,
+                                            jlong* tag_ptr,
+                                            const jchar* value,
+                                            jint value_length,
+                                            void* user_data) {
+      FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data);
+      if (*tag_ptr == p->tag_to_find) {
+        size_t utf_byte_count = CountUtf8Bytes(value, value_length);
+        std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
+        memset(mod_utf.get(), 0, utf_byte_count + 1);
+        ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
+        if (!p->data.empty()) {
+          p->data += "\n";
+        }
+        p->data += android::base::StringPrintf("%" PRId64 "@%" PRId64 " (% " PRId64 ", '%s')",
+                                               *tag_ptr,
+                                               class_tag,
+                                               size,
+                                               mod_utf.get());
+        // Update the tag to test whether that works.
+        *tag_ptr = *tag_ptr + 1;
+      }
+      return 0;
+    }
+
+    std::string data;
+    const jlong tag_to_find;
+  };
+
+  jvmtiHeapCallbacks callbacks;
+  memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
+  callbacks.heap_iteration_callback = FindStringCallbacks::HeapIterationCallback;
+  callbacks.string_primitive_value_callback = FindStringCallbacks::StringValueCallback;
+
+  FindStringCallbacks fsc(tag);
+  jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fsc);
+  if (JvmtiErrorToException(env, ret)) {
+    return nullptr;
+  }
+  return env->NewStringUTF(fsc.data.c_str());
+}
+
 }  // namespace Test906IterateHeap
 }  // namespace art