ART: Fix string reporting

Correctly report zero-length strings.

Bug: 31385354
Test: m test-art-host-run-test-906-iterate-heap
Test: m test-art-host-run-test-913-heaps
Change-Id: Ic37d5c4b350cc8d04faebec54494ed6fe19eece8
diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc
index 976ce66..c7294a9 100644
--- a/runtime/openjdkjvmti/ti_heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -50,25 +50,28 @@
   if (UNLIKELY(cb->string_primitive_value_callback != nullptr) && obj->IsString()) {
     art::ObjPtr<art::mirror::String> str = obj->AsString();
     int32_t string_length = str->GetLength();
-    jvmtiError alloc_error;
-    JvmtiUniquePtr<uint16_t[]> data = AllocJvmtiUniquePtr<uint16_t[]>(env,
-                                                                      string_length,
-                                                                      &alloc_error);
-    if (data == nullptr) {
-      // TODO: Not really sure what to do here. Should we abort the iteration and go all the way
-      //       back? For now just warn.
-      LOG(WARNING) << "Unable to allocate buffer for string reporting! Silently dropping value.";
-      return 0;
-    }
+    JvmtiUniquePtr<uint16_t[]> data;
 
-    if (str->IsCompressed()) {
-      uint8_t* compressed_data = str->GetValueCompressed();
-      for (int32_t i = 0; i != string_length; ++i) {
-        data[i] = compressed_data[i];
+    if (string_length > 0) {
+      jvmtiError alloc_error;
+      data = AllocJvmtiUniquePtr<uint16_t[]>(env, string_length, &alloc_error);
+      if (data == nullptr) {
+        // TODO: Not really sure what to do here. Should we abort the iteration and go all the way
+        //       back? For now just warn.
+        LOG(WARNING) << "Unable to allocate buffer for string reporting! Silently dropping value."
+                     << " >" << str->ToModifiedUtf8() << "<";
+        return 0;
       }
-    } else {
-      // Can copy directly.
-      memcpy(data.get(), str->GetValue(), string_length * sizeof(uint16_t));
+
+      if (str->IsCompressed()) {
+        uint8_t* compressed_data = str->GetValueCompressed();
+        for (int32_t i = 0; i != string_length; ++i) {
+          data[i] = compressed_data[i];
+        }
+      } else {
+        // Can copy directly.
+        memcpy(data.get(), str->GetValue(), string_length * sizeof(uint16_t));
+      }
     }
 
     const jlong class_tag = tag_table->GetTagOrZero(obj->GetClass());
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index 46805d7..c96edef 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -79,8 +79,9 @@
 5@1002 --(field@28)--> 1@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
-[1@0 (32, 'HelloWorld')]
+[1@0 (32, 'HelloWorld'), 2@0 (16, '')]
 2
+3
 2@0 (15, 3xB '010203')
 3@0 (16, 2xC '41005a00')
 8@0 (32, 2xD '0000000000000000000000000000f03f')
@@ -128,7 +129,6 @@
 ---- untagged objects
 root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 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 --(system-class)--> 2@0 [size=32, 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]
@@ -170,7 +170,6 @@
 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 --(system-class)--> 2@0 [size=32, 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]
@@ -209,7 +208,6 @@
 ---
 ---- tagged classes
 root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1]
-root@root --(system-class)--> 2@0 [size=32, length=-1]
 root@root --(thread)--> 3000@0 [size=132, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
 1002@0 --(interface)--> 2001@0 [size=124, length=-1]
@@ -233,7 +231,6 @@
 5@1002 --(class)--> 1002@0 [size=123, length=-1]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
-root@root --(system-class)--> 2@0 [size=32, length=-1]
 root@root --(thread)--> 3000@0 [size=132, length=-1]
 1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
 1002@0 --(interface)--> 2001@0 [size=124, length=-1]
diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java
index df89f34..14ee268 100644
--- a/test/913-heaps/src/Main.java
+++ b/test/913-heaps/src/Main.java
@@ -25,9 +25,19 @@
     doTest();
     new TestConfig().doFollowReferencesTest();
 
+    Runtime.getRuntime().gc();
+    Runtime.getRuntime().gc();
+
     doStringTest();
+
+    Runtime.getRuntime().gc();
+    Runtime.getRuntime().gc();
+
     doPrimitiveArrayTest();
 
+    Runtime.getRuntime().gc();
+    Runtime.getRuntime().gc();
+
     // Test klass filter.
     System.out.println("--- klass ---");
     new TestConfig(A.class, 0).doFollowReferencesTest();
@@ -53,14 +63,18 @@
   }
 
   public static void doStringTest() throws Exception {
-    final String str = "HelloWorld";
+    final String str = new String("HelloWorld");
+    final String str2 = new String("");
     Object o = new Object() {
       String s = str;
+      String s2 = str2;
     };
 
     setTag(str, 1);
+    setTag(str2, 2);
     System.out.println(Arrays.toString(followReferencesString(o)));
     System.out.println(getTag(str));
+    System.out.println(getTag(str2));
   }
 
   public static void doPrimitiveArrayTest() throws Exception {