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);
}