return error;
}
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::IterateThroughHeapExt),
+ "com.android.art.heap.iterate_through_heap_ext",
+ "Iterate through a heap. This is equivalent to the standard IterateThroughHeap function,"
+ " except for additionally passing the heap id of the current object. The jvmtiHeapCallbacks"
+ " structure is reused, with the callbacks field overloaded to a signature of "
+ "jint (*)(jlong, jlong, jlong*, jint length, void*, jint).",
+ 4,
+ { // NOLINT [whitespace/braces] [4]
+ { "heap_filter", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false},
+ { "klass", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, true},
+ { "callbacks", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, false},
+ { "user_data", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, true}
+ },
+ 3,
+ { // NOLINT [whitespace/braces] [4]
+ JVMTI_ERROR_MUST_POSSESS_CAPABILITY,
+ JVMTI_ERROR_INVALID_CLASS,
+ JVMTI_ERROR_NULL_POINTER
+ });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+
// Copy into output buffer.
*extension_count_ptr = ext_vector.size();
static constexpr jint kHeapIdZygote = 2;
static constexpr jint kHeapIdApp = 3;
+static jint GetHeapId(art::ObjPtr<art::mirror::Object> obj)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (obj == nullptr) {
+ return -1;
+ }
+
+ art::gc::Heap* const heap = art::Runtime::Current()->GetHeap();
+ const art::gc::space::ContinuousSpace* const space =
+ heap->FindContinuousSpaceFromObject(obj, true);
+ jint heap_type = kHeapIdApp;
+ if (space != nullptr) {
+ if (space->IsZygoteSpace()) {
+ heap_type = kHeapIdZygote;
+ } else if (space->IsImageSpace() && heap->ObjectIsInBootImageSpace(obj)) {
+ // Only count objects in the boot image as HPROF_HEAP_IMAGE, this leaves app image objects
+ // as HPROF_HEAP_APP. b/35762934
+ heap_type = kHeapIdImage;
+ }
+ } else {
+ const auto* los = heap->GetLargeObjectsSpace();
+ if (los->Contains(obj.Ptr()) && los->IsZygoteLargeObject(art::Thread::Current(), obj.Ptr())) {
+ heap_type = kHeapIdZygote;
+ }
+ }
+ return heap_type;
+};
+
jvmtiError HeapExtensions::GetObjectHeapId(jvmtiEnv* env, jlong tag, jint* heap_id, ...) {
if (heap_id == nullptr) {
return ERR(NULL_POINTER);
auto work = [&]() REQUIRES_SHARED(art::Locks::mutator_lock_) {
ObjectTagTable* tag_table = ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get();
art::ObjPtr<art::mirror::Object> obj = tag_table->Find(tag);
- if (obj == nullptr) {
+ jint heap_type = GetHeapId(obj);
+ if (heap_type == -1) {
return ERR(NOT_FOUND);
}
-
- art::gc::Heap* const heap = art::Runtime::Current()->GetHeap();
- const art::gc::space::ContinuousSpace* const space =
- heap->FindContinuousSpaceFromObject(obj, true);
- jint heap_type = kHeapIdApp;
- if (space != nullptr) {
- if (space->IsZygoteSpace()) {
- heap_type = kHeapIdZygote;
- } else if (space->IsImageSpace() && heap->ObjectIsInBootImageSpace(obj)) {
- // Only count objects in the boot image as HPROF_HEAP_IMAGE, this leaves app image objects
- // as HPROF_HEAP_APP. b/35762934
- heap_type = kHeapIdImage;
- }
- } else {
- const auto* los = heap->GetLargeObjectsSpace();
- if (los->Contains(obj.Ptr()) && los->IsZygoteLargeObject(self, obj.Ptr())) {
- heap_type = kHeapIdZygote;
- }
- }
*heap_id = heap_type;
return ERR(NONE);
};
}
}
+jvmtiError HeapExtensions::IterateThroughHeapExt(jvmtiEnv* env,
+ jint heap_filter,
+ jclass klass,
+ const jvmtiHeapCallbacks* callbacks,
+ const void* user_data) {
+ if (ArtJvmTiEnv::AsArtJvmTiEnv(env)->capabilities.can_tag_objects != 1) { \
+ return ERR(MUST_POSSESS_CAPABILITY); \
+ }
+
+ // ART extension API: Also pass the heap id.
+ auto ArtIterateHeap = [](art::mirror::Object* obj,
+ const jvmtiHeapCallbacks* cb_callbacks,
+ jlong class_tag,
+ jlong size,
+ jlong* tag,
+ jint length,
+ void* cb_user_data)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ jint heap_id = GetHeapId(obj);
+ using ArtExtensionAPI = jint (*)(jlong, jlong, jlong*, jint length, void*, jint);
+ return reinterpret_cast<ArtExtensionAPI>(cb_callbacks->heap_iteration_callback)(
+ class_tag, size, tag, length, cb_user_data, heap_id);
+ };
+ return DoIterateThroughHeap(ArtIterateHeap,
+ env,
+ ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get(),
+ heap_filter,
+ klass,
+ callbacks,
+ user_data);
+}
+
} // namespace openjdkjvmti
public:
static jvmtiError JNICALL GetObjectHeapId(jvmtiEnv* env, jlong tag, jint* heap_id, ...);
static jvmtiError JNICALL GetHeapName(jvmtiEnv* env, jint heap_id, char** heap_name, ...);
+
+ static jvmtiError JNICALL IterateThroughHeapExt(jvmtiEnv* env,
+ jint heap_filter,
+ jclass klass,
+ const jvmtiHeapCallbacks* callbacks,
+ const void* user_data);
};
} // namespace openjdkjvmti
using GetHeapName = jvmtiError(*)(jvmtiEnv*, jint, char**, ...);
static GetHeapName gGetHeapNameFn = nullptr;
+using IterateThroughHeapExt = jvmtiError(*)(jvmtiEnv*,
+ jint,
+ jclass,
+ const jvmtiHeapCallbacks*,
+ const void*);
+static IterateThroughHeapExt gIterateThroughHeapExt = nullptr;
+
+
static void FreeExtensionFunctionInfo(jvmtiExtensionFunctionInfo* extensions, jint count) {
for (size_t i = 0; i != static_cast<size_t>(count); ++i) {
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].id));
CHECK(extensions[i].errors != nullptr);
CHECK(extensions[i].errors[0] == JVMTI_ERROR_ILLEGAL_ARGUMENT);
}
+
+ if (strcmp("com.android.art.heap.iterate_through_heap_ext", extensions[i].id) == 0) {
+ CHECK(gIterateThroughHeapExt == nullptr);
+ gIterateThroughHeapExt = reinterpret_cast<IterateThroughHeapExt>(extensions[i].func);
+
+ CHECK_EQ(extensions[i].param_count, 4);
+
+ CHECK_EQ(strcmp("heap_filter", extensions[i].params[0].name), 0);
+ CHECK_EQ(extensions[i].params[0].base_type, JVMTI_TYPE_JINT);
+ CHECK_EQ(extensions[i].params[0].kind, JVMTI_KIND_IN);
+
+ CHECK_EQ(strcmp("klass", extensions[i].params[1].name), 0);
+ CHECK_EQ(extensions[i].params[1].base_type, JVMTI_TYPE_JCLASS);
+ CHECK_EQ(extensions[i].params[1].kind, JVMTI_KIND_IN);
+ CHECK_EQ(extensions[i].params[1].null_ok, true);
+
+ CHECK_EQ(strcmp("callbacks", extensions[i].params[2].name), 0);
+ CHECK_EQ(extensions[i].params[2].base_type, JVMTI_TYPE_CVOID);
+ CHECK_EQ(extensions[i].params[2].kind, JVMTI_KIND_IN_PTR);
+ CHECK_EQ(extensions[i].params[2].null_ok, false);
+
+ CHECK_EQ(strcmp("user_data", extensions[i].params[3].name), 0);
+ CHECK_EQ(extensions[i].params[3].base_type, JVMTI_TYPE_CVOID);
+ CHECK_EQ(extensions[i].params[3].kind, JVMTI_KIND_IN_PTR);
+ CHECK_EQ(extensions[i].params[3].null_ok, true);
+
+ CHECK_EQ(extensions[i].error_count, 3);
+ CHECK(extensions[i].errors != nullptr);
+ CHECK(extensions[i].errors[0] == JVMTI_ERROR_MUST_POSSESS_CAPABILITY);
+ CHECK(extensions[i].errors[1] == JVMTI_ERROR_INVALID_CLASS);
+ CHECK(extensions[i].errors[2] == JVMTI_ERROR_NULL_POINTER);
+ }
}
CHECK(gGetObjectHeapIdFn != nullptr);
}
}
+static bool gFoundExt = false;
+
+static jint JNICALL HeapIterationExtCallback(jlong class_tag ATTRIBUTE_UNUSED,
+ jlong size ATTRIBUTE_UNUSED,
+ jlong* tag_ptr,
+ jint length ATTRIBUTE_UNUSED,
+ void* user_data ATTRIBUTE_UNUSED,
+ jint heap_id) {
+ // We expect some tagged objects at or above the threshold, where the expected heap id is
+ // encoded into lowest byte.
+ constexpr jlong kThreshold = 30000000;
+ jlong tag = *tag_ptr;
+ if (tag >= kThreshold) {
+ jint expected_heap_id = static_cast<jint>(tag - kThreshold);
+ CHECK_EQ(expected_heap_id, heap_id);
+ gFoundExt = true;
+ }
+ return 0;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test913_iterateThroughHeapExt(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ CHECK(gIterateThroughHeapExt != nullptr);
+
+ jvmtiHeapCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
+ callbacks.heap_iteration_callback =
+ reinterpret_cast<decltype(callbacks.heap_iteration_callback)>(HeapIterationExtCallback);
+
+ jvmtiError ret = gIterateThroughHeapExt(jvmti_env, 0, nullptr, &callbacks, nullptr);
+ JvmtiErrorToException(env, jvmti_env, ret);
+ CHECK(gFoundExt);
+}
+
} // namespace Test913Heaps
} // namespace art
checkGetObjectHeapIdInCallback(100000, objClassExpectedHeapId);
checkGetObjectHeapIdInCallback(100001, 3);
+ long baseTag = 30000000;
+ setTag(Object.class, baseTag + objClassExpectedHeapId);
+ setTag(Class.class, baseTag + objClassExpectedHeapId);
+ Object o = new Object();
+ extensionTestHolder.add(o);
+ setTag(o, baseTag + 3);
+
+ iterateThroughHeapExt();
+
extensionTestHolder = null;
}
public static native String[] followReferencesString(Object initialObject);
public static native String followReferencesPrimitiveArray(Object initialObject);
public static native String followReferencesPrimitiveFields(Object initialObject);
+
+ private static native void iterateThroughHeapExt();
}