JDK-8234085 : Change parameters to JVMTI Extension event ClassUnload
  • Type: CSR
  • Component: hotspot
  • Sub-Component: jvmti
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 14
  • Submitted: 2019-11-13
  • Updated: 2019-12-02
  • Resolved: 2019-11-26
Related Reports
CSR :  
Description
Summary
-------

The parameters to the JVMTI Extension event `ClassUnload` are `JNIEnv*`, `java.lang.Thread`, and `java.lang.Class` of the class being unloaded.  The last two arguments are buggy, are replaced with `char*`, which is the name of the class.

Problem
-------

The `java.lang.Thread` argument is not the thread that requested the event.  In stop-the-world GCs, at least the target thread is stopped while the event unsafely modifies the state, in order to send the event to the Java thread that can call JNI.  The user can't access the `java.lang.Thread` object without going into JNI, which will not continue while a safepoint is in progress, so will hang.
The `java.lang.Class` argument is the Java Object that is not marked during GC, so not live.  This parameter returns a dead object to the caller.
This doesn't work at all for GCs like ZGC.

Solution
--------

Replacing the last two arguments with C `char*` argument, which gives the name of the class being unloaded.  So it's a more useful event.  The purpose for the extension event is primarily to test the extension event mechanism.  There are better ways in JVMTI to find out if your class is unloaded.

There used to be a JVMTI ClassUnload event that was removed in 2003 by https://bugs.openjdk.java.net/browse/JDK-4898113 and the JPDA release in 1.5 never included it.
The ClassUnload event was changed to the extension event as a demonstration of the extension event mechanism but it looks like the arguments and code were kept.

IBM implements several different extension events which are documented in some obsolete references online, but ClassUnload appears not to be one of them.

Specification
-------------

I could not find anything online that specifies the format of the JVMTI `ClassUnload` Extension event.   The arguments came from the `ClassUnload` event that was removed from the specification because it was buggy.

       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,
            (char*)"com.sun.hotspot.events.ClassUnload",
            (char*)"CLASS_UNLOAD event",
            sizeof(event_params)/sizeof(event_params[0]),
           event_params
         };


http://cr.openjdk.java.net/~coleenp/2019/8173658.01/webrev/index.html

Comments
[~darcy] I added a bit more to the description about ClassUnload event. I don't know why the old ClassUnload code was kept when they removed the event from the JVMTI spec. To clarify, I don't know why they kept the broken code.
02-12-2019

Thanks all for the additional context; moving to Approved.
26-11-2019

As noted above the ClassUnload event was kept purely to provide an example extension event to allow testing of the extension event mechanism.
25-11-2019

[~darcy] The ClassUnload extension event is not documented. It is not intended for actual use but exists purely to allow us to test the extension mechanism.
24-11-2019

Where are the documented JVM TI events documented? Moving to the intermediate Provisional state.
23-11-2019

Thanks [~dcubed]!
19-11-2019

[~coleenp] - Thanks for adding the sample code. Also, forgot to add myself as a reviewer yesterday. Sorry about that.
14-11-2019

[~sspitsyn] Thanks for looking into this and asking around if you can find anyone using it.
14-11-2019

Yes, it seems this extension event was introduced to test the extension mechanism. It is not used by the JDWP agent (it generates its own synthetic ClassUnload events). And it should not be used by IDE's as they normally use JDI. For instance, Egor Ushakov confirmed the JetBrains debugger does not use it. I agree that the compatibility risk is minimal. So, I think this CSR is good. But I see, David has already added himself as a reviewer and the CSR has been finalized.
14-11-2019

The `ClassUnload` event appears to have been modeled on the `ClassLoad` event which reports the thread that initiated the class load, and the class that got loaded. These parameters make absolutely no sense for `ClassUnload`. Class unloading is not initiated by any given thread, but is a consequence of the defining class loader becoming unreachable and the GC becoming active. The implementation for this part actually just returns the thread that requested the current VM operation that led to the safepoint in which class unloading occurs. That thread need have no relationship to the class being unloaded at all! Further, it may not even exist in which case the implementation logic is completely broken. Passing the class that was unloaded as a parameter also makes no sense - the class has been unloaded so by definition you cannot pass a reference to this class as it no longer exists! In short this event is completely unusable other than for counting the number of unloads - for which no parameters are needed. This extension event has also never been documented as it isn't really intended as an extension event to be used by JVM TI agents, but exists purely for the purpose of testing (in a very minimal sense) the extension mechanism itself. Consequently, we do not expect any JVM TI agents to actually use this event. If they do then they cannot be using the parameters (which are broken and can lead to crashes) and so they would be unaffected by the changes to the parameters. If by chance an agent is using the JVM TI introspection API's to understand the "shape" of this event, then as Coleen indicated they will now see a change to the number and type of parameters, as reported by those APIs.
14-11-2019

The agent could do something like this: +jboolean paramIsOk(jvmtiExtensionEventInfo event) { + if (event.param_count == 2 && + event.params[1].base_type == JVMTI_TYPE_CCHAR) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } +}
13-11-2019

Please describe how a JVM/TI agent can detect which version of the event a particular VM generates. I don't think you can/should change the JVM/TI version number since the spec doesn't actually specify this extension event. In general, most JVM/TI agent authors try to create a single agent binary that adapts to the VM the agent is attached to. How can that be accomplished?
13-11-2019