JVMTI ExceptionCatch event clarification

David Holmes david.holmes at oracle.com
Tue May 14 05:34:56 UTC 2019


Hi Chris,

On 14/05/2019 7:38 am, Chris wrote:
> Hi,
> 
> I hope this is the correct mailing list, if not, please guide me to the 
> one I should use.

This is absolutely the right list.

> If I understand the JVMTI ExceptionCatch event correctly, an event 
> should be generated if a native method catches an exception. The docs say:
> 
> "If the exception is caught in a native method, the event is generated 
> as soon as control is returned to a Java programming language method."
> 
> I am not able to see this behavior, if a JNI method catches a Java 
> exception using the "ExceptionClear" JNI function (or is there another 
> way to catch an exception in native code?). My test scenario is as follows:
> 
> Java method *main* calls the native method *native0*, which calls the 
> Java method *throwing*. *throwing* throws an exception, and *native0* 
> clears the exception (by calling "ExceptionClear") before returning. 
> According to the documentation, a JVMTI event should be generated, after 
>   *native0* returned, but this is not the case. The exception catch 
> event is never generated. Please see the reproducing example at the end 
> of this mail.
> 
> My assumption is, that the example produces an output like:
> 
> MAIN
> THROW!
> CATCH!
> THROW!
> CATCH!
>
> But the actual output, with an OpenJDK 8, and OpenJDK 11 HotSpot JVM, is:
> 
> MAIN
> THROW!
> THROW!
> CATCH!

I can see the code in jni_ExceptionClear clearly setting that the 
exception has been caught. So the question is whether the reporting 
aspect of this is buggy ... I'm looking at:

JvmtiExport::notice_unwind_due_to_exception

and it looks wrong to me. It only does the callback if we're in the 
exception handler frame - which handles the "caught by Java" case. 
AFAICS it should also be doing the callback if we're not in the handler 
frame and the exception is already marked caught.

So yes I would say this is a bug. I filed:

https://bugs.openjdk.java.net/browse/JDK-8223812

"JVM TI ExceptionCatch event is not posted when an exception is caught 
by a native method"

Fix seems simple enough.

Cheers,
David
-----

> Java versions (download from adoptopenjdk.net):
> 
> openjdk version "1.8.0_212"
> OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_212-b03)
> OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.212-b03, mixed mode)
> 
> openjdk version "11.0.3" 2019-04-16
> OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.3+7)
> OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.3+7, mixed mode)
> 
> running on Arch Linux:
> 
> $ uname -srvmo
> Linux 5.0.13-arch1-1-ARCH #1 SMP PREEMPT Sun May 5 18:05:41 UTC 2019 
> x86_64 GNU/Linux
> 
> Could you please elaborate if my assumption about the behavior is correct?
> If not, is there a way to get a callback if the "ExceptionClear" 
> function catches an Exception in native code (beside using the JNI 
> Function Interception feature)?
> If my assumption is correct and this is an actual bug, could you please 
> point me to the correct place to submit a bug report?
> 
> Thanks, Christoph
> 
> ========================================================================
> // Test.java
> class Test {
>      public static void main(String... args) throws Exception {
>          System.out.println("MAIN");
>          try {
>              native0(true);
>          } catch (UnsupportedOperationException t) {}
>          try {
>              native0(false);
>          } catch (UnsupportedOperationException t) {}
>      }
> 
>      public static void throwing() {
>          throw new UnsupportedOperationException();
>      }
> 
>      public static native void native0(boolean doCatch);
> }
> ========================================================================
> 
> ========================================================================
> // test.cpp
> #include <cstdio>
> #include <jvmti.h>
> 
> void JNICALL
> on_exception(jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation, jobject,
>               jmethodID, jlocation) {
>      printf("THROW!\n");
> }
> 
> void JNICALL
> on_exception_catch(jvmtiEnv*, JNIEnv*, jthread, jmethodID, jlocation,
>                     jobject) {
>      printf("CATCH!\n");
> }
> 
> JNIEXPORT jint JNICALL
> Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
>      jvmtiEnv* jvmti;
>      jvm->GetEnv((void**) &jvmti, JVMTI_VERSION);
> 
>      jvmtiCapabilities requiredCaps = {};
>      requiredCaps.can_generate_exception_events = 1;
>      jvmti->AddCapabilities(&requiredCaps);
> 
>      jvmti->SetEventNotificationMode(JVMTI_ENABLE,
>                                      JVMTI_EVENT_EXCEPTION, NULL);
>      jvmti->SetEventNotificationMode(JVMTI_ENABLE,
>                                      JVMTI_EVENT_EXCEPTION_CATCH, NULL);
> 
>      jvmtiEventCallbacks eventCallbacks = {};
>      eventCallbacks.Exception = on_exception;
>      eventCallbacks.ExceptionCatch = on_exception_catch;
>      jvmti->SetEventCallbacks(&eventCallbacks, sizeof(eventCallbacks));
> 
>      return 0;
> }
> 
> extern "C" JNIEXPORT void JNICALL
> Java_Test_native0(JNIEnv* jni, jclass klass, jboolean do_catch) {
>      jmethodID throwingMethod;
>      throwingMethod = jni->GetStaticMethodID(klass,"throwing","()V");
>      jni->CallStaticVoidMethod(klass, throwingMethod);
>      if (jni->ExceptionCheck() == JNI_TRUE && do_catch == JNI_TRUE)
>          jni->ExceptionClear();
> }
> ========================================================================


More information about the serviceability-dev mailing list