--- old/src/hotspot/share/prims/jvmtiExport.cpp 2019-11-22 14:30:44.961667582 -0500 +++ new/src/hotspot/share/prims/jvmtiExport.cpp 2019-11-22 14:30:44.677660136 -0500 @@ -1349,20 +1349,27 @@ if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) { return; } - Thread *thread = Thread::current(); + + // postings to the service thread so that it can perform them in a safe + // context and in-order. + MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + ResourceMark rm; + // JvmtiDeferredEvent copies the string. + JvmtiDeferredEvent event = JvmtiDeferredEvent::class_unload_event(klass->name()->as_C_string()); + JvmtiDeferredEventQueue::enqueue(event); +} + + +void JvmtiExport::post_class_unload_internal(const char* name) { + if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) { + return; + } + assert(Thread::current()->is_Java_thread(), "must be called from ServiceThread"); + JavaThread *thread = JavaThread::current(); HandleMark hm(thread); EVT_TRIG_TRACE(EXT_EVENT_CLASS_UNLOAD, ("[?] Trg Class Unload triggered" )); if (JvmtiEventController::is_enabled((jvmtiEvent)EXT_EVENT_CLASS_UNLOAD)) { - assert(thread->is_VM_thread(), "wrong thread"); - - // get JavaThread for whom we are proxy - Thread *calling_thread = ((VMThread *)thread)->vm_operation()->calling_thread(); - if (!calling_thread->is_Java_thread()) { - // cannot post an event to a non-JavaThread - return; - } - JavaThread *real_thread = (JavaThread *)calling_thread; JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { @@ -1370,33 +1377,14 @@ continue; } if (env->is_enabled((jvmtiEvent)EXT_EVENT_CLASS_UNLOAD)) { - EVT_TRACE(EXT_EVENT_CLASS_UNLOAD, ("[?] Evt Class Unload sent %s", - klass==NULL? "NULL" : klass->external_name() )); - - // do everything manually, since this is a proxy - needs special care - JNIEnv* jni_env = real_thread->jni_environment(); - jthread jt = (jthread)JNIHandles::make_local(real_thread, real_thread->threadObj()); - jclass jk = (jclass)JNIHandles::make_local(real_thread, klass->java_mirror()); - - // Before we call the JVMTI agent, we have to set the state in the - // thread for which we are proxying. - JavaThreadState prev_state = real_thread->thread_state(); - assert(((Thread *)real_thread)->is_ConcurrentGC_thread() || - (real_thread->is_Java_thread() && prev_state == _thread_blocked), - "should be ConcurrentGCThread or JavaThread at safepoint"); - real_thread->set_thread_state(_thread_in_native); + EVT_TRACE(EXT_EVENT_CLASS_UNLOAD, ("[?] Evt Class Unload sent %s", name)); + JvmtiEventMark jem(thread); + JvmtiJavaThreadEventTransition jet(thread); jvmtiExtensionEvent callback = env->ext_callbacks()->ClassUnload; if (callback != NULL) { - (*callback)(env->jvmti_external(), jni_env, jt, jk); + (*callback)(env->jvmti_external(), jem.jni_env(), name); } - - assert(real_thread->thread_state() == _thread_in_native, - "JavaThread should be in native"); - real_thread->set_thread_state(prev_state); - - JNIHandles::destroy_local(jk); - JNIHandles::destroy_local(jt); } } } --- old/src/hotspot/share/prims/jvmtiExport.hpp 2019-11-22 14:30:45.357677963 -0500 +++ new/src/hotspot/share/prims/jvmtiExport.hpp 2019-11-22 14:30:45.093671042 -0500 @@ -160,6 +160,7 @@ // internal implementation. Also called from JvmtiDeferredEvent::post() static void post_dynamic_code_generated_internal(const char *name, const void *code_begin, const void *code_end) NOT_JVMTI_RETURN; + static void post_class_unload_internal(const char *name) NOT_JVMTI_RETURN; private: // GenerateEvents support to allow posting of CompiledMethodLoad and --- old/src/hotspot/share/prims/jvmtiExtensions.cpp 2019-11-22 14:30:45.697686876 -0500 +++ new/src/hotspot/share/prims/jvmtiExtensions.cpp 2019-11-22 14:30:45.441680164 -0500 @@ -70,9 +70,8 @@ // register our extension event static jvmtiParamInfo event_params[] = { - { (char*)"JNI Environment", JVMTI_KIND_IN, JVMTI_TYPE_JNIENV, JNI_FALSE }, - { (char*)"Thread", JVMTI_KIND_IN, JVMTI_TYPE_JTHREAD, JNI_FALSE }, - { (char*)"Class", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, JNI_FALSE } + { (char*)"JNI Environment", JVMTI_KIND_IN_PTR, JVMTI_TYPE_JNIENV, JNI_FALSE }, + { (char*)"Class", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CCHAR, JNI_FALSE } }; static jvmtiExtensionEventInfo ext_event = { EXT_EVENT_CLASS_UNLOAD, --- old/src/hotspot/share/prims/jvmtiImpl.cpp 2019-11-22 14:30:46.057696313 -0500 +++ new/src/hotspot/share/prims/jvmtiImpl.cpp 2019-11-22 14:30:45.797689497 -0500 @@ -943,6 +943,16 @@ return event; } +JvmtiDeferredEvent JvmtiDeferredEvent::class_unload_event(const char* name) { + JvmtiDeferredEvent event = JvmtiDeferredEvent(TYPE_CLASS_UNLOADED); + // Need to make a copy of the name since we don't know how long + // the event poster will keep it around after we enqueue the + // deferred event and return. strdup() failure is handled in + // the post() routine below. + event._event_data.class_unloaded.name = os::strdup(name); + return event; +} + void JvmtiDeferredEvent::post() { assert(ServiceThread::is_service_thread(Thread::current()), "Service thread must post enqueued events"); @@ -976,6 +986,17 @@ } break; } + case TYPE_CLASS_UNLOADED: { + JvmtiExport::post_class_unload_internal( + // if strdup failed give the event a default name + (_event_data.class_unloaded.name == NULL) + ? "unknown_class" : _event_data.class_unloaded.name); + if (_event_data.class_unloaded.name != NULL) { + // release our copy + os::free((void *)_event_data.class_unloaded.name); + } + break; + } default: ShouldNotReachHere(); } --- old/src/hotspot/share/prims/jvmtiImpl.hpp 2019-11-22 14:30:46.401705331 -0500 +++ new/src/hotspot/share/prims/jvmtiImpl.hpp 2019-11-22 14:30:46.141698515 -0500 @@ -440,7 +440,8 @@ TYPE_NONE, TYPE_COMPILED_METHOD_LOAD, TYPE_COMPILED_METHOD_UNLOAD, - TYPE_DYNAMIC_CODE_GENERATED + TYPE_DYNAMIC_CODE_GENERATED, + TYPE_CLASS_UNLOADED } Type; Type _type; @@ -456,6 +457,9 @@ const void* code_begin; const void* code_end; } dynamic_code_generated; + struct { + const char* name; + } class_unloaded; } _event_data; JvmtiDeferredEvent(Type t) : _type(t) {} @@ -472,6 +476,8 @@ static JvmtiDeferredEvent dynamic_code_generated_event( const char* name, const void* begin, const void* end) NOT_JVMTI_RETURN_(JvmtiDeferredEvent()); + static JvmtiDeferredEvent class_unload_event( + const char* name) NOT_JVMTI_RETURN_(JvmtiDeferredEvent()); // Actually posts the event. void post() NOT_JVMTI_RETURN; --- old/src/hotspot/share/runtime/thread.cpp 2019-11-22 14:30:46.753714558 -0500 +++ new/src/hotspot/share/runtime/thread.cpp 2019-11-22 14:30:46.489707638 -0500 @@ -1650,7 +1650,7 @@ set_deopt_compiled_method(NULL); set_monitor_chunks(NULL); _on_thread_list = false; - set_thread_state(_thread_new); + _thread_state = _thread_new; _terminated = _not_terminated; _array_for_gc = NULL; _suspend_equivalent = false; --- old/src/hotspot/share/runtime/thread.inline.hpp 2019-11-22 14:30:47.189725988 -0500 +++ new/src/hotspot/share/runtime/thread.inline.hpp 2019-11-22 14:30:46.897718333 -0500 @@ -125,6 +125,8 @@ } inline void JavaThread::set_thread_state(JavaThreadState s) { + assert(current_or_null() == NULL || current_or_null() == this, + "state change should only be called by the current thread"); #if defined(PPC64) || defined (AARCH64) // Use membars when accessing volatile _thread_state. See // Threads::create_vm() for size checks. --- old/test/hotspot/jtreg/ProblemList.txt 2019-11-22 14:30:47.569735950 -0500 +++ new/test/hotspot/jtreg/ProblemList.txt 2019-11-22 14:30:47.289728609 -0500 @@ -178,7 +178,6 @@ vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted003/TestDescription.java 6606767 generic-all vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted004/TestDescription.java 6606767 generic-all -vmTestbase/nsk/jvmti/scenarios/extension/EX03/ex03t001/TestDescription.java 8173658 generic-all vmTestbase/nsk/jvmti/AttachOnDemand/attach045/TestDescription.java 8202971 generic-all vmTestbase/nsk/jvmti/scenarios/jni_interception/JI05/ji05t001/TestDescription.java 8219652 aix-ppc64 vmTestbase/nsk/jvmti/scenarios/jni_interception/JI06/ji06t001/TestDescription.java 8219652 aix-ppc64 --- old/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/extension/EX03/ex03t001/ex03t001.cpp 2019-11-22 14:30:47.925745282 -0500 +++ new/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/extension/EX03/ex03t001/ex03t001.cpp 2019-11-22 14:30:47.657738257 -0500 @@ -41,18 +41,15 @@ /* ============================================================================= */ static void JNICALL -ClassUnload(jvmtiEnv* jvmti_env, JNIEnv *jni_env, jthread thread, jclass klass, ...) { - /* - * With the CMS GC the event can be posted on - * a ConcurrentGC thread that is not a JavaThread. - * In this case the thread argument can be NULL, so that, - * we should not expect the thread argument to be non-NULL. - */ - if (klass == NULL) { +ClassUnload(jvmtiEnv* jvmti_env, JNIEnv* jni_env, const char* name, ...) { + // The name argument should never be null + if (name == NULL) { nsk_jvmti_setFailStatus(); - NSK_COMPLAIN0("ClassUnload: 'klass' input parameter is NULL.\n"); - + NSK_COMPLAIN0("ClassUnload: 'name' input parameter is NULL.\n"); + } else { + NSK_DISPLAY1("Class unloaded %s\n", name); } + NSK_DISPLAY0("Received ClassUnload event.\n"); if (eventEnabled == JNI_TRUE) { eventReceived1 = JNI_TRUE; @@ -107,6 +104,20 @@ return enabled; } +jboolean checkParams(jvmtiExtensionEventInfo event) { + // Check parameters are: + // JNIEnv *jni_env, const char* name + if (event.param_count != 2 || + event.params[0].kind != JVMTI_KIND_IN_PTR || + event.params[0].base_type != JVMTI_TYPE_JNIENV || + event.params[1].kind != JVMTI_KIND_IN_PTR || + event.params[1].base_type != JVMTI_TYPE_CCHAR) { + return JNI_FALSE; + } else { + return JNI_TRUE; + } +} + jboolean enableClassUnloadEvent (jboolean enable) { jint extCount, i; jvmtiExtensionEventInfo* extList; @@ -122,6 +133,14 @@ if (strcmp(extList[i].id, (char*)"com.sun.hotspot.events.ClassUnload") == 0) { found = JNI_TRUE; + NSK_DISPLAY1("%s", extList[i].short_description); + + if (!checkParams(extList[i])) { + NSK_COMPLAIN0("ClassUnload event has wrong parameters."); + nsk_jvmti_setFailStatus(); + return JNI_FALSE; + } + if (!NSK_JVMTI_VERIFY( jvmti->SetExtensionEventCallback(extList[i].extension_event_index, enable ? (jvmtiExtensionEvent)ClassUnload : NULL))) {