JVMTI RetransformClasses() is taking a lot of time

1.1k views Asked by At

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

Code:

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");
}
1

There are 1 answers

1
Saqib Ahmed On BEST ANSWER

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.

Class Load Time Instrumentation

JIT invocation during class load time

Execution Time: 18 seconds approx.

Instrumentation During JIT invocation

JIT invocation during instrumentation after JIT is invoked

Execution Time: 80 seconds approx.

Conclusion

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.