I deployed a simple JVMTI agent to test bytecode instrumentation. My strategy is to call RetransformClasses function in CompiledMethodLoad call back to invoke ClassFileLoadHook. I wrote following code to do so:
err = (*jvmti)->GetMethodDeclaringClass(jvmti, method, &klass);
check_jvmti_error(jvmti, err, "Get Declaring Class");
err = (*jvmti)->RetransformClasses(jvmti, 1, &klass);
check_jvmti_error(jvmti, err, "Retransform class");
This function works correctly by invoking ClassFileLoadHook event, but it takes a lot of time while I'm just passing the same class inside it. My ClassFileLoadHook callback function is empty. I'm counting time of a simple matrix multiplication algorithm. By commenting out RetransformClasses function I get the execution time of the order of 0.8 seconds. Whereas just writing this function elevates the execution time to around 15 seconds.
Is it supposed to take that much overhead or am I doing something wrong?
Regards
static int x = 1;
void JNICALL
compiled_method_load(jvmtiEnv *jvmti, jmethodID method, jint code_size,
const void* code_addr, jint map_length, const jvmtiAddrLocationMap* map,
const void* compile_info) {
jvmtiError err;
jclass klass;
char* name = NULL;
char* signature = NULL;
char* generic_ptr = NULL;
err = (*jvmti)->RawMonitorEnter(jvmti, lock);
check_jvmti_error(jvmti, err, "raw monitor enter");
err = (*jvmti)->GetMethodName(jvmti, method, &name, &signature,
&generic_ptr);
check_jvmti_error(jvmti, err, "Get Method Name");
printf("\nCompiled method load event\n");
printf("Method name %s %s %s\n\n", name, signature,
generic_ptr == NULL ? "" : generic_ptr);
if (strstr(name, "main") != NULL && x == 1) {
x++;
err = (*jvmti)->GetMethodDeclaringClass(jvmti, method, &klass);
check_jvmti_error(jvmti, err, "Get Declaring Class");
err = (*jvmti)->RetransformClasses(jvmti, 1, &klass);
check_jvmti_error(jvmti, err, "Retransform class");
}
if (name != NULL) {
err = (*jvmti)->Deallocate(jvmti, (unsigned char*) name);
check_jvmti_error(jvmti, err, "deallocate name");
}
if (signature != NULL) {
err = (*jvmti)->Deallocate(jvmti, (unsigned char*) signature);
check_jvmti_error(jvmti, err, "deallocate signature");
}
if (generic_ptr != NULL) {
err = (*jvmti)->Deallocate(jvmti, (unsigned char*) generic_ptr);
check_jvmti_error(jvmti, err, "deallocate generic_ptr");
}
err = (*jvmti)->RawMonitorExit(jvmti, lock);
check_jvmti_error(jvmti, err, "raw monitor exit");
}
To answer my question:
No. I wasn't doing anything wrong. It's supposed to take that much overhead.
And here's the proof:
I used Jitwatch to get some insight in the problem. I profiled both ClassLoad time instrumentation and instrumentation after JIT invocation. I'm using same application code in both cases.

Execution Time: 18 seconds approx.

Execution Time: 80 seconds approx.
We can clearly see here that when I try to instrument my code by calling RetransformClasses -> CLassFileLoadHook sequence in CompiledLoadEvent, JIT simply halts and then never gets invoked for the function I tried to instrument. It doesn't even do OSR compilations afterward. I summed up the reason for this behaviour of JIT in this answer. The follow up question is given here. Anybody who knows a workaround is most welcome to answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With