Merge "ART: Add primitive array reporting"
diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc
index 7efeea7..2fbc12b 100644
--- a/runtime/openjdkjvmti/ti_heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -31,6 +31,7 @@
 #include "object_callbacks.h"
 #include "object_tagging.h"
 #include "obj_ptr-inl.h"
+#include "primitive.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
@@ -89,6 +90,75 @@
   return 0;
 }
 
+// Report the contents of a primitive array, if a callback is set.
+jint ReportPrimitiveArray(art::ObjPtr<art::mirror::Object> obj,
+                          jvmtiEnv* env,
+                          ObjectTagTable* tag_table,
+                          const jvmtiHeapCallbacks* cb,
+                          const void* user_data) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  if (UNLIKELY(cb->array_primitive_value_callback != nullptr) &&
+      obj->IsArrayInstance() &&
+      !obj->IsObjectArray()) {
+    art::ObjPtr<art::mirror::Array> array = obj->AsArray();
+    int32_t array_length = array->GetLength();
+    size_t component_size = array->GetClass()->GetComponentSize();
+    art::Primitive::Type art_prim_type = array->GetClass()->GetComponentType()->GetPrimitiveType();
+    jvmtiPrimitiveType prim_type =
+        static_cast<jvmtiPrimitiveType>(art::Primitive::Descriptor(art_prim_type)[0]);
+    DCHECK(prim_type == JVMTI_PRIMITIVE_TYPE_BOOLEAN ||
+           prim_type == JVMTI_PRIMITIVE_TYPE_BYTE ||
+           prim_type == JVMTI_PRIMITIVE_TYPE_CHAR ||
+           prim_type == JVMTI_PRIMITIVE_TYPE_SHORT ||
+           prim_type == JVMTI_PRIMITIVE_TYPE_INT ||
+           prim_type == JVMTI_PRIMITIVE_TYPE_LONG ||
+           prim_type == JVMTI_PRIMITIVE_TYPE_FLOAT ||
+           prim_type == JVMTI_PRIMITIVE_TYPE_DOUBLE);
+
+    const jlong class_tag = tag_table->GetTagOrZero(obj->GetClass());
+    jlong array_tag = tag_table->GetTagOrZero(obj.Ptr());
+    const jlong saved_array_tag = array_tag;
+
+    jint result;
+    if (array_length == 0) {
+      result = cb->array_primitive_value_callback(class_tag,
+                                                  obj->SizeOf(),
+                                                  &array_tag,
+                                                  0,
+                                                  prim_type,
+                                                  nullptr,
+                                                  const_cast<void*>(user_data));
+    } else {
+      jvmtiError alloc_error;
+      JvmtiUniquePtr<char[]> data = AllocJvmtiUniquePtr<char[]>(env,
+                                                                array_length * component_size,
+                                                                &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 array reporting! Silently dropping value.";
+        return 0;
+      }
+
+      memcpy(data.get(), array->GetRawData(component_size, 0), array_length * component_size);
+
+      result = cb->array_primitive_value_callback(class_tag,
+                                                  obj->SizeOf(),
+                                                  &array_tag,
+                                                  array_length,
+                                                  prim_type,
+                                                  data.get(),
+                                                  const_cast<void*>(user_data));
+    }
+
+    if (array_tag != saved_array_tag) {
+      tag_table->Set(obj.Ptr(), array_tag);
+    }
+
+    return result;
+  }
+  return 0;
+}
+
 }  // namespace
 
 struct IterateThroughHeapData {
@@ -202,7 +272,15 @@
     ithd->stop_reports = (string_ret & JVMTI_VISIT_ABORT) != 0;
   }
 
-  // TODO Implement array primitive callback.
+  if (!ithd->stop_reports) {
+    jint array_ret = ReportPrimitiveArray(obj,
+                                          ithd->env,
+                                          ithd->heap_util->GetTags(),
+                                          ithd->callbacks,
+                                          ithd->user_data);
+    ithd->stop_reports = (array_ret & JVMTI_VISIT_ABORT) != 0;
+  }
+
   // TODO Implement primitive field callback.
 }
 
@@ -215,11 +293,6 @@
     return ERR(NULL_POINTER);
   }
 
-  if (callbacks->array_primitive_value_callback != nullptr) {
-    // TODO: Implement.
-    return ERR(NOT_IMPLEMENTED);
-  }
-
   art::Thread* self = art::Thread::Current();
   art::ScopedObjectAccess soa(self);      // Now we know we have the shared lock.
 
@@ -569,6 +642,11 @@
           }
         }
       }
+    } else {
+      if (!stop_reports_) {
+        jint array_ret = ReportPrimitiveArray(array, env, tag_table_, callbacks_, user_data_);
+        stop_reports_ = (array_ret & JVMTI_VISIT_ABORT) != 0;
+      }
     }
   }
 
@@ -753,11 +831,6 @@
     return ERR(NULL_POINTER);
   }
 
-  if (callbacks->array_primitive_value_callback != nullptr) {
-    // TODO: Implement.
-    return ERR(NOT_IMPLEMENTED);
-  }
-
   art::Thread* self = art::Thread::Current();
 
   art::gc::Heap* heap = art::Runtime::Current()->GetHeap();
diff --git a/test/906-iterate-heap/expected.txt b/test/906-iterate-heap/expected.txt
index d636286..c8228d6 100644
--- a/test/906-iterate-heap/expected.txt
+++ b/test/906-iterate-heap/expected.txt
@@ -1,4 +1,20 @@
 [{tag=1, class-tag=0, size=8, length=-1}, {tag=2, class-tag=100, size=8, length=-1}, {tag=3, class-tag=100, size=8, length=-1}, {tag=4, class-tag=0, size=32, length=5}, {tag=5, class-tag=0, size=40, length=-1}, {tag=100, class-tag=0, size=<class>, length=-1}]
 [{tag=11, class-tag=0, size=8, length=-1}, {tag=12, class-tag=110, size=8, length=-1}, {tag=13, class-tag=110, size=8, length=-1}, {tag=14, class-tag=0, size=32, length=5}, {tag=15, class-tag=0, size=40, length=-1}, {tag=110, class-tag=0, size=<class>, length=-1}]
-15@0 ( 40, 'Hello World')
+15@0 (40, 'Hello World')
 16
+1@0 (14, 2xZ '0001')
+2
+1@0 (15, 3xB '010203')
+2
+1@0 (16, 2xC '41005a00')
+2
+1@0 (18, 3xS '010002000300')
+2
+1@0 (24, 3xI '010000000200000003000000')
+2
+1@0 (20, 2xF '000000000000803f')
+2
+1@0 (40, 3xJ '010000000000000002000000000000000300000000000000')
+2
+1@0 (32, 2xD '0000000000000000000000000000f03f')
+2
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
index 0a0c68a..890220e 100644
--- a/test/906-iterate-heap/iterate_heap.cc
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -16,8 +16,10 @@
 
 #include "inttypes.h"
 
+#include <iomanip>
 #include <iostream>
 #include <pthread.h>
+#include <sstream>
 #include <stdio.h>
 #include <vector>
 
@@ -181,11 +183,11 @@
   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) {
+    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;
     }
 
@@ -204,7 +206,7 @@
         if (!p->data.empty()) {
           p->data += "\n";
         }
-        p->data += android::base::StringPrintf("%" PRId64 "@%" PRId64 " (% " PRId64 ", '%s')",
+        p->data += android::base::StringPrintf("%" PRId64 "@%" PRId64 " (%" PRId64 ", '%s')",
                                                *tag_ptr,
                                                class_tag,
                                                size,
@@ -232,5 +234,93 @@
   return env->NewStringUTF(fsc.data.c_str());
 }
 
+extern "C" JNIEXPORT jstring JNICALL Java_Main_iterateThroughHeapPrimitiveArray(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
+  struct FindArrayCallbacks {
+    explicit FindArrayCallbacks(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 ArrayValueCallback(jlong class_tag,
+                                           jlong size,
+                                           jlong* tag_ptr,
+                                           jint element_count,
+                                           jvmtiPrimitiveType element_type,
+                                           const void* elements,
+                                           void* user_data) {
+      FindArrayCallbacks* p = reinterpret_cast<FindArrayCallbacks*>(user_data);
+      if (*tag_ptr == p->tag_to_find) {
+        std::ostringstream oss;
+        oss << *tag_ptr
+            << '@'
+            << class_tag
+            << " ("
+            << size
+            << ", "
+            << element_count
+            << "x"
+            << static_cast<char>(element_type)
+            << " '";
+        size_t element_size;
+        switch (element_type) {
+          case JVMTI_PRIMITIVE_TYPE_BOOLEAN:
+          case JVMTI_PRIMITIVE_TYPE_BYTE:
+            element_size = 1;
+            break;
+          case JVMTI_PRIMITIVE_TYPE_CHAR:
+          case JVMTI_PRIMITIVE_TYPE_SHORT:
+            element_size = 2;
+            break;
+          case JVMTI_PRIMITIVE_TYPE_INT:
+          case JVMTI_PRIMITIVE_TYPE_FLOAT:
+            element_size = 4;
+            break;
+          case JVMTI_PRIMITIVE_TYPE_LONG:
+          case JVMTI_PRIMITIVE_TYPE_DOUBLE:
+            element_size = 8;
+            break;
+          default:
+            LOG(FATAL) << "Unknown type " << static_cast<size_t>(element_type);
+            UNREACHABLE();
+        }
+        const uint8_t* data = reinterpret_cast<const uint8_t*>(elements);
+        for (size_t i = 0; i != element_size * element_count; ++i) {
+          oss << android::base::StringPrintf("%02x", data[i]);
+        }
+        oss << "')";
+
+        if (!p->data.empty()) {
+          p->data += "\n";
+        }
+        p->data += oss.str();
+        // 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 = FindArrayCallbacks::HeapIterationCallback;
+  callbacks.array_primitive_value_callback = FindArrayCallbacks::ArrayValueCallback;
+
+  FindArrayCallbacks fac(tag);
+  jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fac);
+  if (JvmtiErrorToException(env, ret)) {
+    return nullptr;
+  }
+  return env->NewStringUTF(fac.data.c_str());
+}
+
 }  // namespace Test906IterateHeap
 }  // namespace art
diff --git a/test/906-iterate-heap/src/Main.java b/test/906-iterate-heap/src/Main.java
index 755d23c..d499886 100644
--- a/test/906-iterate-heap/src/Main.java
+++ b/test/906-iterate-heap/src/Main.java
@@ -79,6 +79,46 @@
 
     System.out.println(iterateThroughHeapString(getTag(s)));
     System.out.println(getTag(s));
+
+    boolean[] zArray = new boolean[] { false, true };
+    setTag(zArray, 1);
+    System.out.println(iterateThroughHeapPrimitiveArray(getTag(zArray)));
+    System.out.println(getTag(zArray));
+
+    byte[] bArray = new byte[] { 1, 2, 3 };
+    setTag(bArray, 1);
+    System.out.println(iterateThroughHeapPrimitiveArray(getTag(bArray)));
+    System.out.println(getTag(bArray));
+
+    char[] cArray = new char[] { 'A', 'Z' };
+    setTag(cArray, 1);
+    System.out.println(iterateThroughHeapPrimitiveArray(getTag(cArray)));
+    System.out.println(getTag(cArray));
+
+    short[] sArray = new short[] { 1, 2, 3 };
+    setTag(sArray, 1);
+    System.out.println(iterateThroughHeapPrimitiveArray(getTag(sArray)));
+    System.out.println(getTag(sArray));
+
+    int[] iArray = new int[] { 1, 2, 3 };
+    setTag(iArray, 1);
+    System.out.println(iterateThroughHeapPrimitiveArray(getTag(iArray)));
+    System.out.println(getTag(iArray));
+
+    float[] fArray = new float[] { 0.0f, 1.0f };
+    setTag(fArray, 1);
+    System.out.println(iterateThroughHeapPrimitiveArray(getTag(fArray)));
+    System.out.println(getTag(fArray));
+
+    long[] lArray = new long[] { 1, 2, 3 };
+    setTag(lArray, 1);
+    System.out.println(iterateThroughHeapPrimitiveArray(getTag(lArray)));
+    System.out.println(getTag(lArray));
+
+    double[] dArray = new double[] { 0.0, 1.0 };
+    setTag(dArray, 1);
+    System.out.println(iterateThroughHeapPrimitiveArray(getTag(dArray)));
+    System.out.println(getTag(dArray));
   }
 
   static class A {
@@ -147,4 +187,5 @@
   private static native int iterateThroughHeapAdd(int heapFilter,
       Class<?> klassFilter);
   private static native String iterateThroughHeapString(long tag);
+  private static native String iterateThroughHeapPrimitiveArray(long tag);
 }
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index 3125d2b..6432172 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -79,5 +79,14 @@
 5@1002 --(field@28)--> 1@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
-[1@0 ( 40, 'HelloWorld')]
+[1@0 (40, 'HelloWorld')]
 2
+2@0 (15, 3xB '010203')
+3@0 (16, 2xC '41005a00')
+8@0 (32, 2xD '0000000000000000000000000000f03f')
+6@0 (20, 2xF '000000000000803f')
+5@0 (24, 3xI '010000000200000003000000')
+7@0 (40, 3xJ '010000000000000002000000000000000300000000000000')
+4@0 (18, 3xS '010002000300')
+1@0 (14, 2xZ '0001')
+23456789
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 0c2361a..1de1a69 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -521,7 +521,7 @@
         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);
-        p->data.push_back(android::base::StringPrintf("%" PRId64 "@%" PRId64 " (% " PRId64 ", '%s')",
+        p->data.push_back(android::base::StringPrintf("%" PRId64 "@%" PRId64 " (%" PRId64 ", '%s')",
                                                       *tag_ptr,
                                                       class_tag,
                                                       size,
@@ -555,5 +555,96 @@
   return retArray;
 }
 
+
+extern "C" JNIEXPORT jstring JNICALL Java_Main_followReferencesPrimitiveArray(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject initial_object) {
+  struct FindArrayCallbacks {
+    static jint JNICALL FollowReferencesCallback(
+        jvmtiHeapReferenceKind reference_kind ATTRIBUTE_UNUSED,
+        const jvmtiHeapReferenceInfo* reference_info ATTRIBUTE_UNUSED,
+        jlong class_tag ATTRIBUTE_UNUSED,
+        jlong referrer_class_tag ATTRIBUTE_UNUSED,
+        jlong size ATTRIBUTE_UNUSED,
+        jlong* tag_ptr ATTRIBUTE_UNUSED,
+        jlong* referrer_tag_ptr ATTRIBUTE_UNUSED,
+        jint length ATTRIBUTE_UNUSED,
+        void* user_data ATTRIBUTE_UNUSED) {
+      return JVMTI_VISIT_OBJECTS;  // Continue visiting.
+    }
+
+    static jint JNICALL ArrayValueCallback(jlong class_tag,
+                                           jlong size,
+                                           jlong* tag_ptr,
+                                           jint element_count,
+                                           jvmtiPrimitiveType element_type,
+                                           const void* elements,
+                                           void* user_data) {
+      FindArrayCallbacks* p = reinterpret_cast<FindArrayCallbacks*>(user_data);
+      if (*tag_ptr != 0) {
+        std::ostringstream oss;
+        oss << *tag_ptr
+            << '@'
+            << class_tag
+            << " ("
+            << size
+            << ", "
+            << element_count
+            << "x"
+            << static_cast<char>(element_type)
+            << " '";
+        size_t element_size;
+        switch (element_type) {
+          case JVMTI_PRIMITIVE_TYPE_BOOLEAN:
+          case JVMTI_PRIMITIVE_TYPE_BYTE:
+            element_size = 1;
+            break;
+          case JVMTI_PRIMITIVE_TYPE_CHAR:
+          case JVMTI_PRIMITIVE_TYPE_SHORT:
+            element_size = 2;
+            break;
+          case JVMTI_PRIMITIVE_TYPE_INT:
+          case JVMTI_PRIMITIVE_TYPE_FLOAT:
+            element_size = 4;
+            break;
+          case JVMTI_PRIMITIVE_TYPE_LONG:
+          case JVMTI_PRIMITIVE_TYPE_DOUBLE:
+            element_size = 8;
+            break;
+          default:
+            LOG(FATAL) << "Unknown type " << static_cast<size_t>(element_type);
+            UNREACHABLE();
+        }
+        const uint8_t* data = reinterpret_cast<const uint8_t*>(elements);
+        for (size_t i = 0; i != element_size * element_count; ++i) {
+          oss << android::base::StringPrintf("%02x", data[i]);
+        }
+        oss << "')";
+
+        if (!p->data.empty()) {
+          p->data += "\n";
+        }
+        p->data += oss.str();
+        // Update the tag to test whether that works.
+        *tag_ptr = *tag_ptr + 1;
+      }
+      return 0;
+    }
+
+    std::string data;
+  };
+
+  jvmtiHeapCallbacks callbacks;
+  memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
+  callbacks.heap_reference_callback = FindArrayCallbacks::FollowReferencesCallback;
+  callbacks.array_primitive_value_callback = FindArrayCallbacks::ArrayValueCallback;
+
+  FindArrayCallbacks fac;
+  jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, initial_object, &callbacks, &fac);
+  if (JvmtiErrorToException(env, ret)) {
+    return nullptr;
+  }
+  return env->NewStringUTF(fac.data.c_str());
+}
+
 }  // namespace Test913Heaps
 }  // namespace art
diff --git a/test/913-heaps/src/Main.java b/test/913-heaps/src/Main.java
index 4402072..2767d89 100644
--- a/test/913-heaps/src/Main.java
+++ b/test/913-heaps/src/Main.java
@@ -26,6 +26,7 @@
     new TestConfig().doFollowReferencesTest();
 
     doStringTest();
+    doPrimitiveArrayTest();
   }
 
   public static void doTest() throws Exception {
@@ -47,6 +48,53 @@
     System.out.println(getTag(str));
   }
 
+  public static void doPrimitiveArrayTest() throws Exception {
+    final boolean[] zArray = new boolean[] { false, true };
+    setTag(zArray, 1);
+
+    final byte[] bArray = new byte[] { 1, 2, 3 };
+    setTag(bArray, 2);
+
+    final char[] cArray = new char[] { 'A', 'Z' };
+    setTag(cArray, 3);
+
+    final short[] sArray = new short[] { 1, 2, 3 };
+    setTag(sArray, 4);
+
+    final int[] iArray = new int[] { 1, 2, 3 };
+    setTag(iArray, 5);
+
+    final float[] fArray = new float[] { 0.0f, 1.0f };
+    setTag(fArray, 6);
+
+    final long[] lArray = new long[] { 1, 2, 3 };
+    setTag(lArray, 7);
+
+    final double[] dArray = new double[] { 0.0, 1.0 };
+    setTag(dArray, 8);
+
+    Object o = new Object() {
+      Object z = zArray;
+      Object b = bArray;
+      Object c = cArray;
+      Object s = sArray;
+      Object i = iArray;
+      Object f = fArray;
+      Object l = lArray;
+      Object d = dArray;
+    };
+
+    System.out.println(followReferencesPrimitiveArray(o));
+    System.out.print(getTag(zArray));
+    System.out.print(getTag(bArray));
+    System.out.print(getTag(cArray));
+    System.out.print(getTag(sArray));
+    System.out.print(getTag(iArray));
+    System.out.print(getTag(fArray));
+    System.out.print(getTag(lArray));
+    System.out.println(getTag(dArray));
+  }
+
   private static void run() {
     clearStats();
     forceGarbageCollection();
@@ -424,4 +472,5 @@
   public static native String[] followReferences(int heapFilter, Class<?> klassFilter,
       Object initialObject, int stopAfter, int followSet, Object jniRef);
   public static native String[] followReferencesString(Object initialObject);
+  public static native String followReferencesPrimitiveArray(Object initialObject);
 }