Search code examples
javajvmjavaagentsjvmti

JVMTI class not prepared


I'm writing a native Java agent using JVMTI that goes over all the methods of all the loaded classes. Unfortunately many classes seem not yet prepared and therefore GetClassMethods returns JVMTI_ERROR_CLASS_NOT_PREPARED. I am registering a ClassPrepare event callback but that seem to be called only for very few classes. Simplified (minus all the error handling and deallocation) my code looks like this

JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* jvm, char *options, void *reserved) {

     jvmtiEnv *jvmti;
     jint class_count;
     jclass* classes;
     jint method_count;
     jmethodID* methods;

     (*jvm)->GetEnv(jvm, (void**) &jvmti, JVMTI_VERSION_11);
     (*jvmti)->GetLoadedClasses(jvmti, &class_count, &classes);
     for (int i = 0; i < class_count; i++) {
       jclass klass = classes[i];
       // here a lot of time JVMTI_ERROR_CLASS_NOT_PREPARED is returned
       jvmtiError err = (*jvmti)->GetClassMethods(jvmti, klass, &method_count, &methods);
     }

The agent is attached dynamically to a running JVM using JCMD and the JVMTI.agent_load command. I did try to register a class prepare callback using:

jvmtiEventCallbacks callbacks;
(void)memset(&callbacks, 0, sizeof(callbacks));
callbacks.ClassPrepare = &callbackClassPrepare;
(*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint) sizeof(callbacks));
(*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, (jthread) NULL);

But this ended only being called for very few classes.

How can I get the JVM to prepare the loaded classes so that GetClassMethods returns JVMTI_ERROR_NONE?

So far I have only tested with JDK 17.0.1 with Shenandoah GC.


Solution

  • This is a normal situation when some classes are loaded but not linked. You don't need to do anything to prepare classes manually - JVM does this automatically when needed. JVM Specification guarantees the classes is completely prepared before it is initialized. As soon as it happens, JVM TI ClassPrepare event is fired.

    So in order to get all available jmethodIDs you'll need:

    1. Iterate over all loaded classes, ignoring possible JVMTI_ERROR_CLASS_NOT_PREPARED.
    2. Set ClassPrepare event callback and call GetClassMethods in it.