diff options
author | 2019-07-23 13:10:20 -0700 | |
---|---|---|
committer | 2019-08-13 17:54:27 +0000 | |
commit | 72d7e94d70923c5ff4cbe947117801583eae4672 (patch) | |
tree | cde1932ed801dc7f388b56282d3e2bca000e428a | |
parent | f1bb75abb38609d72648e03eefac7320b5e804b4 (diff) |
Add obsolete object event
Add an extension event to notify agents that an object is becoming
obsolete. This is meant to be used by agents performing allocation
tracking using the VMObjectAlloc event to let them know that an object
replacement is occurring. This event is only triggered by calls to
JVMTI functions that create obsolete objects. Normal GC actions
(including a moving compaction) will not cause this event to trigger.
Test: ./test.py --host
Bug: 134162467
Change-Id: If48b880814a751ba6c24c18d0ad116db4f8fdf64
-rw-r--r-- | openjdkjvmti/OpenjdkJvmTi.cc | 1 | ||||
-rw-r--r-- | openjdkjvmti/events-inl.h | 21 | ||||
-rw-r--r-- | openjdkjvmti/events.cc | 6 | ||||
-rw-r--r-- | openjdkjvmti/events.h | 20 | ||||
-rw-r--r-- | openjdkjvmti/ti_extension.cc | 34 | ||||
-rw-r--r-- | openjdkjvmti/ti_heap.cc | 66 | ||||
-rw-r--r-- | openjdkjvmti/ti_heap.h | 6 | ||||
-rw-r--r-- | test/1974-resize-array/expected.txt | 6 | ||||
-rw-r--r-- | test/1974-resize-array/resize_array.cc | 119 | ||||
-rw-r--r-- | test/1974-resize-array/src/art/Test1974.java | 61 |
10 files changed, 334 insertions, 6 deletions
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index e4ce825c2a..3a6fcef784 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -1532,6 +1532,7 @@ extern "C" bool ArtPlugin_Initialize() { ClassUtil::Register(gEventHandler); DumpUtil::Register(gEventHandler); MethodUtil::Register(gEventHandler); + HeapExtensions::Register(gEventHandler); SearchUtil::Register(); HeapUtil::Register(); Transformer::Setup(); diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h index 627129a20b..22822f8b40 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -123,7 +123,8 @@ class ScopedEventDispatchEnvironment final : public art::ValueObject { fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \ fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \ fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) \ - fn(DdmPublishChunk, ArtJvmtiEvent::kDdmPublishChunk) + fn(DdmPublishChunk, ArtJvmtiEvent::kDdmPublishChunk) \ + fn(ObsoleteObjectCreated, ArtJvmtiEvent::kObsoleteObjectCreated) template <ArtJvmtiEvent kEvent> struct EventFnType { @@ -318,6 +319,24 @@ inline void EventHandler::DispatchEventOnEnv( } } +template <> +inline void EventHandler::DispatchEventOnEnv<ArtJvmtiEvent::kObsoleteObjectCreated>( + ArtJvmTiEnv* env, art::Thread* thread, jlong* obsolete_tag, jlong* new_tag) const { + static constexpr ArtJvmtiEvent kEvent = ArtJvmtiEvent::kObsoleteObjectCreated; + DCHECK(env != nullptr); + if (ShouldDispatch<kEvent>(env, thread, obsolete_tag, new_tag)) { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + impl::EventHandlerFunc<kEvent> func(env); + ExecuteCallback<kEvent>(func, obsolete_tag, new_tag); + } else { + // Unlike most others this has a default action to make sure that agents without knowledge of + // this extension get reasonable behavior. + jlong temp = *obsolete_tag; + *obsolete_tag = *new_tag; + *new_tag = temp; + } +} + template <ArtJvmtiEvent kEvent, typename ...Args> inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc<kEvent> handler, Args... args) { handler.ExecuteCallback(args...); diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index faa47c53dc..77acd562d1 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -92,6 +92,9 @@ void ArtJvmtiEventCallbacks::CopyExtensionsFrom(const ArtJvmtiEventCallbacks* cb jvmtiError ArtJvmtiEventCallbacks::Set(jint index, jvmtiExtensionEvent cb) { switch (index) { + case static_cast<jint>(ArtJvmtiEvent::kObsoleteObjectCreated): + ObsoleteObjectCreated = reinterpret_cast<ArtJvmtiEventObsoleteObjectCreated>(cb); + return OK; case static_cast<jint>(ArtJvmtiEvent::kDdmPublishChunk): DdmPublishChunk = reinterpret_cast<ArtJvmtiEventDdmPublishChunk>(cb); return OK; @@ -110,6 +113,7 @@ bool IsExtensionEvent(jint e) { bool IsExtensionEvent(ArtJvmtiEvent e) { switch (e) { case ArtJvmtiEvent::kDdmPublishChunk: + case ArtJvmtiEvent::kObsoleteObjectCreated: return true; default: return false; @@ -243,6 +247,7 @@ static bool IsThreadControllable(ArtJvmtiEvent event) { case ArtJvmtiEvent::kCompiledMethodUnload: case ArtJvmtiEvent::kDynamicCodeGenerated: case ArtJvmtiEvent::kDataDumpRequest: + case ArtJvmtiEvent::kObsoleteObjectCreated: return false; default: @@ -1158,6 +1163,7 @@ static DeoptRequirement GetDeoptRequirement(ArtJvmtiEvent event, jthread thread) case ArtJvmtiEvent::kVmObjectAlloc: case ArtJvmtiEvent::kClassFileLoadHookRetransformable: case ArtJvmtiEvent::kDdmPublishChunk: + case ArtJvmtiEvent::kObsoleteObjectCreated: return DeoptRequirement::kNone; } } diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index ac86d0cd9b..d5ab4fbc98 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -80,7 +80,8 @@ enum class ArtJvmtiEvent : jint { // capability. kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1, kDdmPublishChunk = JVMTI_MAX_EVENT_TYPE_VAL + 2, - kMaxNormalEventTypeVal = kDdmPublishChunk, + kObsoleteObjectCreated = JVMTI_MAX_EVENT_TYPE_VAL + 3, + kMaxNormalEventTypeVal = kObsoleteObjectCreated, // All that follow are events used to implement internal JVMTI functions. They are not settable // directly by agents. @@ -102,6 +103,10 @@ using ArtJvmtiEventDdmPublishChunk = void (*)(jvmtiEnv *jvmti_env, jint data_len, const jbyte* data); +using ArtJvmtiEventObsoleteObjectCreated = void (*)(jvmtiEnv *jvmti_env, + jlong* obsolete_tag, + jlong* new_tag); + // It is not enough to store a Thread pointer, as these may be reused. Use the pointer and the // thread id. // Note: We could just use the tid like tracing does. @@ -114,7 +119,7 @@ struct UniqueThreadHasher { }; struct ArtJvmtiEventCallbacks : jvmtiEventCallbacks { - ArtJvmtiEventCallbacks() : DdmPublishChunk(nullptr) { + ArtJvmtiEventCallbacks() : DdmPublishChunk(nullptr), ObsoleteObjectCreated(nullptr) { memset(this, 0, sizeof(jvmtiEventCallbacks)); } @@ -125,6 +130,7 @@ struct ArtJvmtiEventCallbacks : jvmtiEventCallbacks { jvmtiError Set(jint index, jvmtiExtensionEvent cb); ArtJvmtiEventDdmPublishChunk DdmPublishChunk; + ArtJvmtiEventObsoleteObjectCreated ObsoleteObjectCreated; }; bool IsExtensionEvent(jint e); @@ -285,6 +291,16 @@ class EventHandler { REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(art::Locks::user_code_suspension_lock_, art::Locks::thread_list_lock_); + template<typename Visitor> + void ForEachEnv(art::Thread* self, Visitor v) REQUIRES(!envs_lock_) { + art::ReaderMutexLock mu(self, envs_lock_); + for (ArtJvmTiEnv* e : envs) { + if (e != nullptr) { + v(e); + } + } + } + private: void SetupTraceListener(JvmtiMethodTraceListener* listener, ArtJvmtiEvent event, bool enable); diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index 9d31a934f3..9666562ceb 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -496,6 +496,40 @@ jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env, if (error != OK) { return error; } + error = add_extension( + ArtJvmtiEvent::kObsoleteObjectCreated, + "com.android.art.heap.obsolete_object_created", + "Called when an obsolete object is created.\n" + "An object becomes obsolete when, due to some jvmti function call all references to the" + " object are replaced with a reference to a different object. After this call finishes there" + " will be no strong references to the obsolete object anywere. If the object is retrieved" + " using GetObjectsWithTags its type (class) may have changed and any data it contains may" + " have been deleted. This is primarily designed to support memory tracking agents which make" + " use of the ObjectFree and VMObjectAlloc events for tracking. To support this use-case if" + " this event is not being handled it will by default act as though the following code was" + " registered as a handler:\n" + "\n" + " void HandleObsoleteObjectCreated(jvmtiEnv* env, jlong* obsolete_tag, jlong* new_tag) {\n" + " jlong temp = *obsolete_tag;\n" + " *obsolete_tag = *new_tag;\n" + " *new_tag = temp;\n" + " }\n" + "\n" + "Note that this event does not support filtering based on thread. This event has the same" + " restrictions on JNI and JVMTI function calls as the ObjectFree event.\n" + "\n" + "Arguments:\n" + " obsolete_tag: Pointer to the tag the old object (now obsolete) has. Setting the pointer" + " will update the tag value.\n" + " new_tag: Pointer to the tag the new object (replacing the obsolete one) has. Setting the" + " pointer will update the tag value.", + { + { "obsolete_tag", JVMTI_KIND_IN_PTR, JVMTI_TYPE_JLONG, false }, + { "new_tag", JVMTI_KIND_IN_PTR, JVMTI_TYPE_JLONG, false }, + }); + if (error != OK) { + return error; + } // Copy into output buffer. diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc index 3c2b82e9bc..0c06ae1169 100644 --- a/openjdkjvmti/ti_heap.cc +++ b/openjdkjvmti/ti_heap.cc @@ -30,6 +30,7 @@ #include "class_root.h" #include "deopt_manager.h" #include "dex/primitive.h" +#include "events-inl.h" #include "gc/collector_type.h" #include "gc/gc_cause.h" #include "gc/heap-visit-objects-inl.h" @@ -67,6 +68,8 @@ namespace openjdkjvmti { +EventHandler* HeapExtensions::gEventHandler = nullptr; + namespace { struct IndexCache { @@ -1736,10 +1739,49 @@ static void ReplaceStrongRoots(art::Thread* self, ArrayPtr old_arr_ptr, ArrayPtr } } -static void ReplaceWeakRoots(ArrayPtr old_arr_ptr, ArrayPtr new_arr_ptr) +static void ReplaceWeakRoots(art::Thread* self, + EventHandler* event_handler, + ArrayPtr old_arr_ptr, + ArrayPtr new_arr_ptr) REQUIRES(art::Locks::mutator_lock_, art::Locks::user_code_suspension_lock_, art::Roles::uninterruptible_) { + // Handle tags. We want to do this seprately from other weak-refs (handled below) because we need + // to send additional events and handle cases where the agent might have tagged the new + // replacement object during the VMObjectAlloc. We do this by removing all tags associated with + // both the obsolete and the new arrays. Then we send the ObsoleteObjectCreated event and cache + // the new tag values. We next update all the other weak-references (the tags have been removed) + // and finally update the tag table with the new values. Doing things in this way (1) keeps all + // code relating to updating weak-references together and (2) ensures we don't end up in strange + // situations where the order of weak-ref visiting affects the final tagging state. Since we have + // the mutator_lock_ and gc-paused throughout this whole process no threads should be able to see + // the interval where the objects are not tagged. + std::unordered_map<ArtJvmTiEnv*, jlong> obsolete_tags; + std::unordered_map<ArtJvmTiEnv*, jlong> non_obsolete_tags; + event_handler->ForEachEnv(self, [&](ArtJvmTiEnv* env) { + // Cannot have REQUIRES(art::Locks::mutator_lock_) since ForEachEnv doesn't require it. + art::Locks::mutator_lock_->AssertExclusiveHeld(self); + env->object_tag_table->Lock(); + // Get the tags and clear them (so we don't need to special-case the normal weak-ref visitor) + jlong new_tag = 0; + jlong obsolete_tag = 0; + bool had_new_tag = env->object_tag_table->RemoveLocked(new_arr_ptr, &new_tag); + bool had_obsolete_tag = env->object_tag_table->RemoveLocked(old_arr_ptr, &obsolete_tag); + // Dispatch event. + if (had_obsolete_tag || had_new_tag) { + event_handler->DispatchEventOnEnv<ArtJvmtiEvent::kObsoleteObjectCreated>(env, + self, + &obsolete_tag, + &new_tag); + obsolete_tags[env] = obsolete_tag; + non_obsolete_tags[env] = new_tag; + } + // After weak-ref update we need to go back and re-add obsoletes. We wait to avoid having to + // deal with the visit-weaks overwriting the initial new_arr_ptr tag and generally making things + // difficult. + env->object_tag_table->Unlock(); + }); + // Handle weak-refs. struct ReplaceWeaksVisitor : public art::IsMarkedVisitor { public: ReplaceWeaksVisitor(ArrayPtr old_arr, ArrayPtr new_arr) @@ -1760,9 +1802,23 @@ static void ReplaceWeakRoots(ArrayPtr old_arr_ptr, ArrayPtr new_arr_ptr) }; ReplaceWeaksVisitor rwv(old_arr_ptr, new_arr_ptr); art::Runtime::Current()->SweepSystemWeaks(&rwv); + // Re-add the object tags. At this point all weak-references to the old_arr_ptr are gone. + event_handler->ForEachEnv(self, [&](ArtJvmTiEnv* env) { + // Cannot have REQUIRES(art::Locks::mutator_lock_) since ForEachEnv doesn't require it. + art::Locks::mutator_lock_->AssertExclusiveHeld(self); + env->object_tag_table->Lock(); + if (obsolete_tags.find(env) != obsolete_tags.end()) { + env->object_tag_table->SetLocked(old_arr_ptr, obsolete_tags[env]); + } + if (non_obsolete_tags.find(env) != non_obsolete_tags.end()) { + env->object_tag_table->SetLocked(new_arr_ptr, non_obsolete_tags[env]); + } + env->object_tag_table->Unlock(); + }); } static void PerformArrayReferenceReplacement(art::Thread* self, + EventHandler* event_handler, ArrayPtr old_arr_ptr, ArrayPtr new_arr_ptr) REQUIRES(art::Locks::mutator_lock_, @@ -1770,7 +1826,7 @@ static void PerformArrayReferenceReplacement(art::Thread* self, art::Roles::uninterruptible_) { ReplaceObjectReferences(old_arr_ptr, new_arr_ptr); ReplaceStrongRoots(self, old_arr_ptr, new_arr_ptr); - ReplaceWeakRoots(old_arr_ptr, new_arr_ptr); + ReplaceWeakRoots(self, event_handler, old_arr_ptr, new_arr_ptr); } } // namespace @@ -1863,8 +1919,12 @@ jvmtiError HeapExtensions::ChangeArraySize(jvmtiEnv* env, jobject arr, jsize new UNREACHABLE(); } // Actually replace all the pointers. - PerformArrayReferenceReplacement(self, old_arr.Get(), new_arr.Get()); + PerformArrayReferenceReplacement(self, gEventHandler, old_arr.Get(), new_arr.Get()); return OK; } +void HeapExtensions::Register(EventHandler* eh) { + gEventHandler = eh; +} + } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_heap.h b/openjdkjvmti/ti_heap.h index 5a32436aa5..bf6fce6ba8 100644 --- a/openjdkjvmti/ti_heap.h +++ b/openjdkjvmti/ti_heap.h @@ -21,6 +21,7 @@ namespace openjdkjvmti { +class EventHandler; class ObjectTagTable; class HeapUtil { @@ -64,6 +65,8 @@ class HeapUtil { class HeapExtensions { public: + static void Register(EventHandler* eh); + static jvmtiError JNICALL GetObjectHeapId(jvmtiEnv* env, jlong tag, jint* heap_id, ...); static jvmtiError JNICALL GetHeapName(jvmtiEnv* env, jint heap_id, char** heap_name, ...); @@ -74,6 +77,9 @@ class HeapExtensions { const void* user_data); static jvmtiError JNICALL ChangeArraySize(jvmtiEnv* env, jobject arr, jsize new_size); + + private: + static EventHandler* gEventHandler; }; } // namespace openjdkjvmti diff --git a/test/1974-resize-array/expected.txt b/test/1974-resize-array/expected.txt index 4b0d432f77..4eeb651f83 100644 --- a/test/1974-resize-array/expected.txt +++ b/test/1974-resize-array/expected.txt @@ -76,3 +76,9 @@ val is: [[3, 33, 333]] resize +5 val is: [[3, 33, 333, null, null, null, null, null]] Same value? true +Test jvmti-tags with obsolete +val is: [[4, 44, 444]] resize +5 +val is: [[4, 44, 444, null, null, null, null, null]] +Same value? true +Everything looks good WRT obsolete object + diff --git a/test/1974-resize-array/resize_array.cc b/test/1974-resize-array/resize_array.cc index dadcbeae22..60037b8045 100644 --- a/test/1974-resize-array/resize_array.cc +++ b/test/1974-resize-array/resize_array.cc @@ -16,6 +16,7 @@ #include <cstdio> #include <memory> +#include <mutex> #include <string> #include <vector> @@ -54,6 +55,34 @@ static void DeallocParams(jvmtiParamInfo* params, jint n_params) { } } +static jint FindExtensionEvent(JNIEnv* env, const std::string& name) { + jint n_ext; + jvmtiExtensionEventInfo* infos; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionEvents(&n_ext, &infos))) { + return -1; + } + jint res = -1; + bool found = false; + for (jint i = 0; i < n_ext; i++) { + jvmtiExtensionEventInfo* cur_info = &infos[i]; + if (strcmp(name.c_str(), cur_info->id) == 0) { + res = cur_info->extension_event_index; + found = true; + } + // Cleanup the cur_info + DeallocParams(cur_info->params, cur_info->param_count); + Dealloc(cur_info->id, cur_info->short_description, cur_info->params); + } + // Cleanup the array. + Dealloc(infos); + if (!found) { + ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), (name + " extensions not found").c_str()); + return -1; + } + return res; +} + static jvmtiExtensionFunction FindExtensionMethod(JNIEnv* env, const std::string& name) { jint n_ext; jvmtiExtensionFunctionInfo* infos; @@ -145,5 +174,95 @@ extern "C" JNIEXPORT void JNICALL Java_art_Test1974_runNativeTest(JNIEnv* env, env->CallVoidMethod(print, accept, arr); env->CallVoidMethod(check, accept, arr); } + +struct JvmtiInfo { + std::mutex mu_; + std::vector<jlong> freed_tags_; +}; + +extern "C" JNIEXPORT void JNICALL Java_art_Test1974_StartCollectFrees(JNIEnv* env, + jclass k ATTRIBUTE_UNUSED) { + jvmtiEventCallbacks cb{ + .ObjectFree = + [](jvmtiEnv* jvmti, jlong tag) { + JvmtiInfo* dat = nullptr; + CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&dat)), + JVMTI_ERROR_NONE); + std::lock_guard<std::mutex> mu(dat->mu_); + dat->freed_tags_.push_back(tag); + }, + }; + JvmtiInfo* info = new JvmtiInfo; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(info))) { + LOG(INFO) << "couldn't set env-local storage"; + return; + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) { + LOG(INFO) << "couldn't set event callback"; + return; + } + JvmtiErrorToException( + env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, nullptr)); +} + +extern "C" JNIEXPORT void JNICALL +Java_art_Test1974_StartAssignObsoleteIncrementedId(JNIEnv* env, jclass k ATTRIBUTE_UNUSED) { + jint id = FindExtensionEvent(env, "com.android.art.heap.obsolete_object_created"); + if (env->ExceptionCheck()) { + LOG(INFO) << "Could not find extension event!"; + return; + } + using ObsoleteEvent = void (*)(jvmtiEnv * env, jlong * obsolete, jlong * non_obsolete); + ObsoleteEvent oe = [](jvmtiEnv* env ATTRIBUTE_UNUSED, jlong* obsolete, jlong* non_obsolete) { + *non_obsolete = *obsolete; + *obsolete = *obsolete + 1; + }; + JvmtiErrorToException( + env, + jvmti_env, + jvmti_env->SetExtensionEventCallback(id, reinterpret_cast<jvmtiExtensionEvent>(oe))); +} + +extern "C" JNIEXPORT void JNICALL +Java_art_Test1974_EndAssignObsoleteIncrementedId(JNIEnv* env, jclass k ATTRIBUTE_UNUSED) { + jint id = FindExtensionEvent(env, "com.android.art.heap.obsolete_object_created"); + if (env->ExceptionCheck()) { + LOG(INFO) << "Could not find extension event!"; + return; + } + JvmtiErrorToException(env, jvmti_env, jvmti_env->SetExtensionEventCallback(id, nullptr)); +} + +extern "C" JNIEXPORT jlongArray JNICALL +Java_art_Test1974_CollectFreedTags(JNIEnv* env, jclass k ATTRIBUTE_UNUSED) { + if (JvmtiErrorToException( + env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_OBJECT_FREE, nullptr))) { + return nullptr; + } + JvmtiInfo* info_p = nullptr; + if (JvmtiErrorToException( + env, + jvmti_env, + jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&info_p)))) { + return nullptr; + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(nullptr))) { + return nullptr; + } + std::unique_ptr<JvmtiInfo> info(info_p); + ScopedLocalRef<jlongArray> arr(env, env->NewLongArray(info->freed_tags_.size())); + if (env->ExceptionCheck()) { + return nullptr; + } + env->SetLongArrayRegion(arr.get(), 0, info->freed_tags_.size(), info->freed_tags_.data()); + if (env->ExceptionCheck()) { + return nullptr; + } + return arr.release(); +} } // namespace Test1974ResizeArray } // namespace art diff --git a/test/1974-resize-array/src/art/Test1974.java b/test/1974-resize-array/src/art/Test1974.java index 8460e827eb..c4427c7b62 100644 --- a/test/1974-resize-array/src/art/Test1974.java +++ b/test/1974-resize-array/src/art/Test1974.java @@ -422,6 +422,56 @@ public class Test1974 { System.out.println("Same value? " + (after_tagged_obj[0] == arr)); } + public static void runWithJvmtiTagsObsolete() throws Exception { + Object[] arr = new Object[] {"4", "44", "444"}; + long globalID = 444_444_444l; + System.out.println("Test jvmti-tags with obsolete"); + Main.setTag(arr, globalID); + StartCollectFrees(); + StartAssignObsoleteIncrementedId(); + DbgPrintln("Pre hash: " + arr.hashCode()); + System.out.println( + "val is: " + Arrays.deepToString(GetObjectsWithTag(globalID)) + " resize +5"); + ResizeArray(() -> arr, arr.length + 5); + Object[] after_tagged_obj = GetObjectsWithTag(globalID); + Object[] obsolete_tagged_obj = GetObjectsWithTag(globalID + 1); + System.out.println("val is: " + Arrays.deepToString(GetObjectsWithTag(globalID))); + EndAssignObsoleteIncrementedId(); + long[] obsoletes_freed = CollectFreedTags(); + DbgPrintln("Post hash: " + after_tagged_obj[0].hashCode()); + System.out.println("Same value? " + (after_tagged_obj[0] == arr)); + if (obsolete_tagged_obj.length >= 1) { + DbgPrintln("Found objects with obsolete tag: " + Arrays.deepToString(obsolete_tagged_obj)); + boolean bad = false; + if (obsolete_tagged_obj.length != 1) { + System.out.println( + "Found obsolete tag'd objects: " + + Arrays.deepHashCode(obsolete_tagged_obj) + + " but only expected one!"); + bad = true; + } + if (!Arrays.deepEquals( + Arrays.copyOf(arr, ((Object[]) obsolete_tagged_obj[0]).length), + (Object[]) obsolete_tagged_obj[0])) { + System.out.println("Obsolete array was unexpectedly different than non-obsolete one!"); + bad = true; + } + if (!Arrays.stream(obsoletes_freed).anyMatch((l) -> l == globalID + 1)) { + DbgPrintln("Didn't see a free of the obsolete id"); + } + if (!bad) { + System.out.println("Everything looks good WRT obsolete object"); + } + } else { + if (!Arrays.stream(obsoletes_freed).anyMatch((l) -> l == globalID + 1)) { + System.out.println("Didn't see a free of the obsolete id"); + } else { + DbgPrintln("Saw a free of obsolete id!"); + System.out.println("Everything looks good WRT obsolete object!"); + } + } + } + public static void run() throws Exception { // Simple runAsThread(Test1974::runInstance); @@ -457,6 +507,9 @@ public class Test1974 { // Basic jvmti tags runAsThread(Test1974::runWithJvmtiTags); + + // Grab obsolete reference using tags/detect free + runAsThread(Test1974::runWithJvmtiTagsObsolete); } // Use a supplier so that we don't have to have a local ref to the resized @@ -470,4 +523,12 @@ public class Test1974 { public static native <T> T ReadJniRef(long t); public static native Object[] GetObjectsWithTag(long tag); + + public static native void StartCollectFrees(); + + public static native void StartAssignObsoleteIncrementedId(); + + public static native void EndAssignObsoleteIncrementedId(); + + public static native long[] CollectFreedTags(); } |