diff options
-rw-r--r-- | runtime/openjdkjvmti/OpenjdkJvmTi.cc | 153 | ||||
-rw-r--r-- | runtime/openjdkjvmti/jvmti_weak_table-inl.h | 17 | ||||
-rw-r--r-- | runtime/openjdkjvmti/jvmti_weak_table.h | 4 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_heap.cc | 91 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_heap.h | 6 | ||||
-rw-r--r-- | test/913-heaps/expected.txt | 7 | ||||
-rw-r--r-- | test/913-heaps/heaps.cc | 187 | ||||
-rw-r--r-- | test/913-heaps/src/art/Test913.java | 72 |
8 files changed, 534 insertions, 3 deletions
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index c3a94b93a0..4c00317d8e 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -1078,9 +1078,156 @@ class JvmtiFunctions { jint* extension_count_ptr, jvmtiExtensionFunctionInfo** extensions) { ENSURE_VALID_ENV(env); - // We do not have any extension functions. - *extension_count_ptr = 0; - *extensions = nullptr; + ENSURE_NON_NULL(extension_count_ptr); + ENSURE_NON_NULL(extensions); + + std::vector<jvmtiExtensionFunctionInfo> ext_vector; + + // Holders for allocated values. + std::vector<JvmtiUniquePtr<char[]>> char_buffers; + std::vector<JvmtiUniquePtr<jvmtiParamInfo[]>> param_buffers; + std::vector<JvmtiUniquePtr<jvmtiError[]>> error_buffers; + + // Add a helper struct that takes an arbitrary const char*. add_extension will use Allocate + // appropriately. + struct CParamInfo { + const char* name; + jvmtiParamKind kind; + jvmtiParamTypes base_type; + jboolean null_ok; + }; + + auto add_extension = [&](jvmtiExtensionFunction func, + const char* id, + const char* short_description, + jint param_count, + const std::vector<CParamInfo>& params, + jint error_count, + const std::vector<jvmtiError>& errors) { + jvmtiExtensionFunctionInfo func_info; + jvmtiError error; + + func_info.func = func; + + JvmtiUniquePtr<char[]> id_ptr = CopyString(env, id, &error); + if (id_ptr == nullptr) { + return error; + } + func_info.id = id_ptr.get(); + char_buffers.push_back(std::move(id_ptr)); + + JvmtiUniquePtr<char[]> descr = CopyString(env, short_description, &error); + if (descr == nullptr) { + return error; + } + func_info.short_description = descr.get(); + char_buffers.push_back(std::move(descr)); + + func_info.param_count = param_count; + if (param_count > 0) { + JvmtiUniquePtr<jvmtiParamInfo[]> params_ptr = + AllocJvmtiUniquePtr<jvmtiParamInfo[]>(env, param_count, &error); + if (params_ptr == nullptr) { + return error; + } + func_info.params = params_ptr.get(); + param_buffers.push_back(std::move(params_ptr)); + + for (jint i = 0; i != param_count; ++i) { + JvmtiUniquePtr<char[]> param_name = CopyString(env, params[i].name, &error); + if (param_name == nullptr) { + return error; + } + func_info.params[i].name = param_name.get(); + char_buffers.push_back(std::move(param_name)); + + func_info.params[i].kind = params[i].kind; + func_info.params[i].base_type = params[i].base_type; + func_info.params[i].null_ok = params[i].null_ok; + } + } else { + func_info.params = nullptr; + } + + func_info.error_count = error_count; + if (error_count > 0) { + JvmtiUniquePtr<jvmtiError[]> errors_ptr = + AllocJvmtiUniquePtr<jvmtiError[]>(env, error_count, &error); + if (errors_ptr == nullptr) { + return error; + } + func_info.errors = errors_ptr.get(); + error_buffers.push_back(std::move(errors_ptr)); + + for (jint i = 0; i != error_count; ++i) { + func_info.errors[i] = errors[i]; + } + } else { + func_info.errors = nullptr; + } + + ext_vector.push_back(func_info); + + return ERR(NONE); + }; + + jvmtiError error; + + // Heap extensions. + error = add_extension( + reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetObjectHeapId), + "com.android.art.heap.get_object_heap_id", + "Retrieve the heap id of the the object tagged with the given argument. An " + "arbitrary object is chosen if multiple objects exist with the same tag.", + 2, + { // NOLINT [whitespace/braces] [4] + { "tag", JVMTI_KIND_IN, JVMTI_TYPE_JLONG, false}, + { "heap_id", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false} + }, + 1, + { JVMTI_ERROR_NOT_FOUND }); + if (error != ERR(NONE)) { + return error; + } + + error = add_extension( + reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetHeapName), + "com.android.art.heap.get_heap_name", + "Retrieve the name of the heap with the given id.", + 2, + { // NOLINT [whitespace/braces] [4] + { "heap_id", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false}, + { "heap_name", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false} + }, + 1, + { JVMTI_ERROR_ILLEGAL_ARGUMENT }); + if (error != ERR(NONE)) { + return error; + } + + // Copy into output buffer. + + *extension_count_ptr = ext_vector.size(); + JvmtiUniquePtr<jvmtiExtensionFunctionInfo[]> out_data = + AllocJvmtiUniquePtr<jvmtiExtensionFunctionInfo[]>(env, ext_vector.size(), &error); + if (out_data == nullptr) { + return error; + } + memcpy(out_data.get(), + ext_vector.data(), + ext_vector.size() * sizeof(jvmtiExtensionFunctionInfo)); + *extensions = out_data.release(); + + // Release all the buffer holders, we're OK now. + for (auto& holder : char_buffers) { + holder.release(); + } + for (auto& holder : param_buffers) { + holder.release(); + } + for (auto& holder : error_buffers) { + holder.release(); + } return ERR(NONE); } diff --git a/runtime/openjdkjvmti/jvmti_weak_table-inl.h b/runtime/openjdkjvmti/jvmti_weak_table-inl.h index f67fffccbb..64ab3e7b2e 100644 --- a/runtime/openjdkjvmti/jvmti_weak_table-inl.h +++ b/runtime/openjdkjvmti/jvmti_weak_table-inl.h @@ -384,6 +384,23 @@ jvmtiError JvmtiWeakTable<T>::GetTaggedObjects(jvmtiEnv* jvmti_env, return ERR(NONE); } +template <typename T> +art::mirror::Object* JvmtiWeakTable<T>::Find(T tag) { + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, allow_disallow_lock_); + Wait(self); + + for (auto& pair : tagged_objects_) { + if (tag == pair.second) { + art::mirror::Object* obj = pair.first.template Read<art::kWithReadBarrier>(); + if (obj != nullptr) { + return obj; + } + } + } + return nullptr; +} + } // namespace openjdkjvmti #endif // ART_RUNTIME_OPENJDKJVMTI_JVMTI_WEAK_TABLE_INL_H_ diff --git a/runtime/openjdkjvmti/jvmti_weak_table.h b/runtime/openjdkjvmti/jvmti_weak_table.h index eeea75aa9d..a6fd247c51 100644 --- a/runtime/openjdkjvmti/jvmti_weak_table.h +++ b/runtime/openjdkjvmti/jvmti_weak_table.h @@ -116,6 +116,10 @@ class JvmtiWeakTable : public art::gc::SystemWeakHolder { void Unlock() RELEASE(allow_disallow_lock_); void AssertLocked() ASSERT_CAPABILITY(allow_disallow_lock_); + art::mirror::Object* Find(T tag) + REQUIRES_SHARED(art::Locks::mutator_lock_) + REQUIRES(!allow_disallow_lock_); + protected: // Should HandleNullSweep be called when Sweep detects the release of an object? virtual bool DoesHandleNullOnSweep() { diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc index 7fc5104bce..9b4dcaa9d0 100644 --- a/runtime/openjdkjvmti/ti_heap.cc +++ b/runtime/openjdkjvmti/ti_heap.cc @@ -1400,4 +1400,95 @@ jvmtiError HeapUtil::ForceGarbageCollection(jvmtiEnv* env ATTRIBUTE_UNUSED) { return ERR(NONE); } + +static constexpr jint kHeapIdDefault = 0; +static constexpr jint kHeapIdImage = 1; +static constexpr jint kHeapIdZygote = 2; +static constexpr jint kHeapIdApp = 3; + +jvmtiError HeapExtensions::GetObjectHeapId(jvmtiEnv* env, jlong tag, jint* heap_id, ...) { + if (heap_id == nullptr) { + return ERR(NULL_POINTER); + } + + art::Thread* self = art::Thread::Current(); + + 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) { + 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); + }; + + if (!art::Locks::mutator_lock_->IsSharedHeld(self)) { + if (!self->IsThreadSuspensionAllowable()) { + return ERR(INTERNAL); + } + art::ScopedObjectAccess soa(self); + return work(); + } else { + // We cannot use SOA in this case. We might be holding the lock, but may not be in the + // runnable state (e.g., during GC). + art::Locks::mutator_lock_->AssertSharedHeld(self); + // TODO: Investigate why ASSERT_SHARED_CAPABILITY doesn't work. + auto annotalysis_workaround = [&]() NO_THREAD_SAFETY_ANALYSIS { + return work(); + }; + return annotalysis_workaround(); + } +} + +static jvmtiError CopyStringAndReturn(jvmtiEnv* env, const char* in, char** out) { + jvmtiError error; + JvmtiUniquePtr<char[]> param_name = CopyString(env, in, &error); + if (param_name == nullptr) { + return error; + } + *out = param_name.release(); + return ERR(NONE); +} + +static constexpr const char* kHeapIdDefaultName = "default"; +static constexpr const char* kHeapIdImageName = "image"; +static constexpr const char* kHeapIdZygoteName = "zygote"; +static constexpr const char* kHeapIdAppName = "app"; + +jvmtiError HeapExtensions::GetHeapName(jvmtiEnv* env, jint heap_id, char** heap_name, ...) { + switch (heap_id) { + case kHeapIdDefault: + return CopyStringAndReturn(env, kHeapIdDefaultName, heap_name); + case kHeapIdImage: + return CopyStringAndReturn(env, kHeapIdImageName, heap_name); + case kHeapIdZygote: + return CopyStringAndReturn(env, kHeapIdZygoteName, heap_name); + case kHeapIdApp: + return CopyStringAndReturn(env, kHeapIdAppName, heap_name); + + default: + return ERR(ILLEGAL_ARGUMENT); + } +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_heap.h b/runtime/openjdkjvmti/ti_heap.h index dccecb4aa3..b4b71ba88e 100644 --- a/runtime/openjdkjvmti/ti_heap.h +++ b/runtime/openjdkjvmti/ti_heap.h @@ -56,6 +56,12 @@ class HeapUtil { ObjectTagTable* tags_; }; +class HeapExtensions { + public: + static jvmtiError JNICALL GetObjectHeapId(jvmtiEnv* env, jlong tag, jint* heap_id, ...); + static jvmtiError JNICALL GetHeapName(jvmtiEnv* env, jint heap_id, char** heap_name, ...); +}; + } // namespace openjdkjvmti #endif // ART_RUNTIME_OPENJDKJVMTI_TI_HEAP_H_ diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt index 702b247819..b128d1cb70 100644 --- a/test/913-heaps/expected.txt +++ b/test/913-heaps/expected.txt @@ -385,3 +385,10 @@ root@root --(thread)--> 1@1000 [size=16, length=-1] 5@1002 --(field@10)--> 1@1000 [size=16, length=-1] 5@1002 --(field@9)--> 6@1000 [size=16, length=-1] --- + +default +image +zygote +app + +3 diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc index e319f7d98c..f39c5f16d7 100644 --- a/test/913-heaps/heaps.cc +++ b/test/913-heaps/heaps.cc @@ -817,5 +817,192 @@ extern "C" JNIEXPORT jint JNICALL Java_art_Test913_getGcFinishes(JNIEnv* env ATT return result; } +using GetObjectHeapId = jvmtiError(*)(jvmtiEnv*, jlong, jint*, ...); +static GetObjectHeapId gGetObjectHeapIdFn = nullptr; + +using GetHeapName = jvmtiError(*)(jvmtiEnv*, jint, char**, ...); +static GetHeapName gGetHeapNameFn = 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)); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].short_description)); + for (size_t j = 0; j != static_cast<size_t>(extensions[i].param_count); ++j) { + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].params[j].name)); + } + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].params)); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(extensions[i].errors)); + } +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test913_checkForExtensionApis( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) { + jint extension_count; + jvmtiExtensionFunctionInfo* extensions; + jvmtiError result = jvmti_env->GetExtensionFunctions(&extension_count, &extensions); + if (JvmtiErrorToException(env, jvmti_env, result)) { + return; + } + + for (size_t i = 0; i != static_cast<size_t>(extension_count); ++i) { + if (strcmp("com.android.art.heap.get_object_heap_id", extensions[i].id) == 0) { + CHECK(gGetObjectHeapIdFn == nullptr); + gGetObjectHeapIdFn = reinterpret_cast<GetObjectHeapId>(extensions[i].func); + + CHECK_EQ(extensions[i].param_count, 2); + + CHECK_EQ(strcmp("tag", extensions[i].params[0].name), 0); + CHECK_EQ(extensions[i].params[0].base_type, JVMTI_TYPE_JLONG); + CHECK_EQ(extensions[i].params[0].kind, JVMTI_KIND_IN); + + CHECK_EQ(strcmp("heap_id", extensions[i].params[1].name), 0); + CHECK_EQ(extensions[i].params[1].base_type, JVMTI_TYPE_JINT); + CHECK_EQ(extensions[i].params[1].kind, JVMTI_KIND_OUT); + CHECK_EQ(extensions[i].params[1].null_ok, false); + + CHECK_EQ(extensions[i].error_count, 1); + CHECK(extensions[i].errors != nullptr); + CHECK(extensions[i].errors[0] == JVMTI_ERROR_NOT_FOUND); + + continue; + } + + if (strcmp("com.android.art.heap.get_heap_name", extensions[i].id) == 0) { + CHECK(gGetHeapNameFn == nullptr); + gGetHeapNameFn = reinterpret_cast<GetHeapName>(extensions[i].func); + + CHECK_EQ(extensions[i].param_count, 2); + + CHECK_EQ(strcmp("heap_id", 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("heap_name", extensions[i].params[1].name), 0); + CHECK_EQ(extensions[i].params[1].base_type, JVMTI_TYPE_CCHAR); + CHECK_EQ(extensions[i].params[1].kind, JVMTI_KIND_ALLOC_BUF); + CHECK_EQ(extensions[i].params[1].null_ok, false); + + CHECK_EQ(extensions[i].error_count, 1); + CHECK(extensions[i].errors != nullptr); + CHECK(extensions[i].errors[0] == JVMTI_ERROR_ILLEGAL_ARGUMENT); + } + } + + CHECK(gGetObjectHeapIdFn != nullptr); + CHECK(gGetHeapNameFn != nullptr); + + FreeExtensionFunctionInfo(extensions, extension_count); +} + +extern "C" JNIEXPORT jint JNICALL Java_art_Test913_getObjectHeapId( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) { + CHECK(gGetObjectHeapIdFn != nullptr); + jint heap_id; + jvmtiError result = gGetObjectHeapIdFn(jvmti_env, tag, &heap_id); + JvmtiErrorToException(env, jvmti_env, result); + return heap_id; +} + +extern "C" JNIEXPORT jstring JNICALL Java_art_Test913_getHeapName( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint heap_id) { + CHECK(gGetHeapNameFn != nullptr); + char* heap_name; + jvmtiError result = gGetHeapNameFn(jvmti_env, heap_id, &heap_name); + if (JvmtiErrorToException(env, jvmti_env, result)) { + return nullptr; + } + jstring ret = env->NewStringUTF(heap_name); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(heap_name)); + return ret; +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test913_checkGetObjectHeapIdInCallback( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag, jint heap_id) { + CHECK(gGetObjectHeapIdFn != nullptr); + + { + struct GetObjectHeapIdCallbacks { + 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, + jlong* referrer_tag_ptr ATTRIBUTE_UNUSED, + jint length ATTRIBUTE_UNUSED, + void* user_data) { + if (*tag_ptr != 0) { + GetObjectHeapIdCallbacks* p = reinterpret_cast<GetObjectHeapIdCallbacks*>(user_data); + if (*tag_ptr == p->check_callback_tag) { + jint tag_heap_id; + jvmtiError result = gGetObjectHeapIdFn(jvmti_env, *tag_ptr, &tag_heap_id); + CHECK_EQ(result, JVMTI_ERROR_NONE); + CHECK_EQ(tag_heap_id, p->check_callback_id); + return JVMTI_VISIT_ABORT; + } + } + + return JVMTI_VISIT_OBJECTS; // Continue visiting. + } + + jlong check_callback_tag; + jint check_callback_id; + }; + + jvmtiHeapCallbacks callbacks; + memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); + callbacks.heap_reference_callback = GetObjectHeapIdCallbacks::FollowReferencesCallback; + + GetObjectHeapIdCallbacks ffc; + ffc.check_callback_tag = tag; + ffc.check_callback_id = heap_id; + + jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, nullptr, &callbacks, &ffc); + if (JvmtiErrorToException(env, jvmti_env, ret)) { + return; + } + } + + { + struct GetObjectHeapIdCallbacks { + static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED, + jlong size ATTRIBUTE_UNUSED, + jlong* tag_ptr, + jint length ATTRIBUTE_UNUSED, + void* user_data) { + if (*tag_ptr != 0) { + GetObjectHeapIdCallbacks* p = reinterpret_cast<GetObjectHeapIdCallbacks*>(user_data); + if (*tag_ptr == p->check_callback_tag) { + jint tag_heap_id; + jvmtiError result = gGetObjectHeapIdFn(jvmti_env, *tag_ptr, &tag_heap_id); + CHECK_EQ(result, JVMTI_ERROR_NONE); + CHECK_EQ(tag_heap_id, p->check_callback_id); + return JVMTI_VISIT_ABORT; + } + } + + return 0; // Continue visiting. + } + + jlong check_callback_tag; + jint check_callback_id; + }; + + jvmtiHeapCallbacks callbacks; + memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks)); + callbacks.heap_iteration_callback = GetObjectHeapIdCallbacks::HeapIterationCallback; + + GetObjectHeapIdCallbacks ffc; + ffc.check_callback_tag = tag; + ffc.check_callback_id = heap_id; + + jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &ffc); + if (JvmtiErrorToException(env, jvmti_env, ret)) { + return; + } + } +} + } // namespace Test913Heaps } // namespace art diff --git a/test/913-heaps/src/art/Test913.java b/test/913-heaps/src/art/Test913.java index 8800b1a4d7..6694aad868 100644 --- a/test/913-heaps/src/art/Test913.java +++ b/test/913-heaps/src/art/Test913.java @@ -16,6 +16,9 @@ package art; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -44,6 +47,8 @@ public class Test913 { }; t.start(); cdl1.await(); + + doExtensionTests(); } public static void runFollowReferences() throws Exception { @@ -215,6 +220,50 @@ public class Test913 { System.out.println(getTag(floatObject)); } + static ArrayList<Object> extensionTestHolder; + + private static void doExtensionTests() { + checkForExtensionApis(); + + extensionTestHolder = new ArrayList<>(); + System.out.println(); + + try { + getHeapName(-1); + System.out.println("Expected failure for -1"); + } catch (Exception e) { + } + System.out.println(getHeapName(0)); + System.out.println(getHeapName(1)); + System.out.println(getHeapName(2)); + System.out.println(getHeapName(3)); + try { + getHeapName(4); + System.out.println("Expected failure for -1"); + } catch (Exception e) { + } + + System.out.println(); + + setTag(Object.class, 100000); + int objectClassHeapId = getObjectHeapId(100000); + int objClassExpectedHeapId = hasImage() ? 1 : 3; + if (objectClassHeapId != objClassExpectedHeapId) { + throw new RuntimeException("Expected object class in heap " + objClassExpectedHeapId + + " but received " + objectClassHeapId); + } + + A a = new A(); + extensionTestHolder.add(a); + setTag(a, 100001); + System.out.println(getObjectHeapId(100001)); + + checkGetObjectHeapIdInCallback(100000, objClassExpectedHeapId); + checkGetObjectHeapIdInCallback(100001, 3); + + extensionTestHolder = null; + } + private static void runGc() { clearStats(); forceGarbageCollection(); @@ -233,6 +282,24 @@ public class Test913 { System.out.println((s > 0) + " " + (f > 0)); } + private static boolean hasImage() { + try { + int pid = Integer.parseInt(new File("/proc/self").getCanonicalFile().getName()); + BufferedReader reader = new BufferedReader(new FileReader("/proc/" + pid + "/maps")); + String line; + while ((line = reader.readLine()) != null) { + if (line.endsWith(".art")) { + reader.close(); + return true; + } + } + reader.close(); + return false; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + private static class TestConfig { private Class<?> klass = null; private int heapFilter = 0; @@ -642,6 +709,11 @@ public class Test913 { private static native int getGcFinishes(); private static native void forceGarbageCollection(); + private static native void checkForExtensionApis(); + private static native int getObjectHeapId(long tag); + private static native String getHeapName(int heapId); + private static native void checkGetObjectHeapIdInCallback(long tag, int heapId); + public static native String[] followReferences(int heapFilter, Class<?> klassFilter, Object initialObject, int stopAfter, int followSet, Object jniRef); public static native String[] followReferencesString(Object initialObject); |