I've been fiddling with JVMTI API, trying to write something useful. Pasting code from other SO questions together, I've attempted to create a simple tool which would dump the heap when some particular kind of exception occurs, possibly useful in environments where creating jdb listener would be unwieldy. Mentioned code:
#include <jni.h>
#include <jvmti.h>
#include <string.h>
#include <stdio.h>
#include "jmm.h"
JNIEXPORT void* JNICALL JVM_GetManagement(jint version);
jvmtiEnv* jvmti;
void JNICALL
Exception(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
jlocation location,
jobject exception,
jmethodID catch_method,
jlocation catch_location) {
char *exception_signature;
/* Obtain signature of the exception and compare type name with FNFE */
jclass class = (*jni_env)->GetObjectClass(jni_env, exception);
(*jvmti)->GetClassSignature(jvmti_env, class, &exception_signature, NULL);
if (strcmp("Ljava/io/FileNotFoundException;", exception_signature)==0) {
JmmInterface* jmm = (JmmInterface*) JVM_GetManagement(JMM_VERSION_1_0);
if (jmm == NULL) {
printf("Sorry, JMM is not supported\n");
} else {
jstring path = (*jni)->NewStringUTF(jni, "dump.hprof");
jmm->DumpHeap0(jni, path, JNI_TRUE);
printf("Heap dumped\n");
}
}
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
(*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_0);
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.Exception = Exception;
(*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
(*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, NULL)
return 0;
}
The process as such is working; I get a heap dump while trying this code on a simple test program (which prints a few lines and generates exception between them). The problem is, thread stack analysis in Eclipse MAT shows me stack trace with DestroyJavaVM instead of main method, unlike heap dump generated with jmap. I expected that heap dump to be different from the jmap one, as it's a JNI call 'interrupting' the normal execution of the instrumented thread, but not disrupting the whole trace, just with native calls lying atop the stack.
Why is that so, and can I somehow prevent the issue, and dump the heap the way I'd like it to? Or is my entire reasoning wrong and it's not possible?