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.
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 eventoClassPrepare
, 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:En el código anterior, creamos un método ficticio en cada clase usando
GetStaticMethodID
y luego forzamos su ejecución usandoRunAgentThread
. Esto asegurará que cada clase cargada esté preparada antes de llamar aGetClassMethods
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.