es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Clase JVMTI no preparada.

Estoy escribiendo un agente Java nativo que utiliza JVMTI y recorre todos los métodos de todas las clases cargadas. Desafortunadamente, muchas clases parecen no estar preparadas todavía, por lo que GetClassMethods devuelve JVMTI_ERROR_CLASS_NOT_PREPARED. Estoy registrando un evento de retorno de llamada ClassPrepare, pero parece que solo se llama para muy pocas clases. Simplificando (sin tener en cuenta todo el manejo de errores y la liberación de memoria), mi código se ve así:

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];
       // aquí muchas veces se devuelve JVMTI_ERROR_CLASS_NOT_PREPARED
       jvmtiError err = (*jvmti)->GetClassMethods(jvmti, klass, &method_count, &methods);
     }
}

El agente se adjunta dinámicamente a una JVM en ejecución utilizando JCMD y el comando JVMTI.agent_load. Intenté registrar una devolución de llamada de preparación de clase usando:

jvmtiEventCallbacks callbacks;
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);

Pero esto solo se llamó para muy pocas clases.

¿Cómo puedo hacer que la JVM prepare las clases cargadas para que GetClassMethods devuelva JVMTI_ERROR_NONE?

Hasta ahora solo he probado con JDK 17.0.1 con Shenandoah GC.

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Parece que el problema que estás enfrentando es que algunas clases no están preparadas al llamar a GetClassMethods y ya has intentado registrar una devolución de llamada de evento ClassPrepare, pero no se está llamando para todas las clases.

    Una posible solución es iterar sobre todas las clases cargadas y forzar que se preparen llamando a ForceEarlyReturnFromMethod en un método de cada clase. Esto obligará a que la clase se prepare antes de que el método regrese. Aquí hay un ejemplo de código:

    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];
          jmethodID mid = (*jvmti)->GetStaticMethodID(jvmti, klass, "dummyMethod", "()V");
          if (mid != NULL) {
             (*jvmti)->RunAgentThread(jvmti, mid, NULL, 0);
          }
          jvmtiError err = (*jvmti)->GetClassMethods(jvmti, klass, &method_count, &methods);
          // process methods as before
       }
    
       return JNI_OK;
    }
    
    // método ficticio para forzar la preparación de la clase
    JNIEXPORT void JNICALL dummyMethod(JNIEnv* env, jclass klass) {
       // no hacer nada
    }
    

    En el código anterior, creamos un método ficticio en cada clase usando GetStaticMethodID y luego forzamos su ejecución usando RunAgentThread. Esto asegurará que cada clase cargada esté preparada antes de llamar a GetClassMethods en ella.

    Además de esta solución, puede haber otros factores que afecten a que la devolución de llamada del evento ClassPrepare no se llame para todas las clases, como los mecanismos de carga de clases utilizados por la JVM o la versión y configuración específica de la JVM. Por lo tanto, puede valer la pena investigar estos factores más a fondo si la solución anterior no resuelve el problema por completo.

Comments are closed.