2016-12-13 4 views
2

Я работаю над исследовательским проектом, который включает в себя обратную связь профилировщика Hotspot. В настоящее время я работаю над агентом JVMTI, который должен иметь следующие функции:Как получить файл класса (в формате спецификации) во время выполнения с использованием JVMTI?

  1. прослушать любое скомпилированное событие нагрузки.
  2. Извлечь и проанализировать полный файл класса, который имеет метод «горячей точки».
  3. Изменить/переопределить байт-коды класса.

У меня есть много функций API, доступных в JVMTI, чтобы получить информацию о файле класса, имеющем метод, который скомпилирован JIT. Тем не менее, я хочу полный файл класса метода, как описано в спецификации виртуальной машины java. Если это не представляется возможным получить целый файл класса, я бы по крайней мере, хочу файл класса в следующем формате:

typedef struct { 
    unsigned int    magic; 
    unsigned short    minor_version; 
    unsigned short    major_version; 
    unsigned short    constant_pool_count; 
    unsigned char    *constant_pool; 
    unsigned short    access_flags; 
    unsigned short    this_class; 
    unsigned short    super_class; 
    unsigned short    interfaces_count; 
    unsigned char    *interfaces; 
    unsigned short    fields_count; 
    unsigned char    *fields; 
    unsigned short    methods_count; 
    unsigned char    *methods; 
    unsigned short    attributes_count; 
    unsigned char    *attributes; 

}ClassFile; 

Я следующий код до сих пор, который служит цели частично:

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) 
    { 
    static ClassFile *clazz; 
    jvmtiError err; 
    jclass klass; 
    jint constant_pool_count_pointer; 
    jint constant_pool_byte_count_pointer; 
    jint local_entry_count_ptr; 
    jint minor, major; 
    jint modifier_ptr; 
    jvmtiLocalVariableEntry* table_ptr; 

    unsigned char* constant_pool_bytes_ptr; 

    char* name = NULL; 
    char* signature = NULL; 
    char* generic_ptr = NULL; 
    unsigned char* bytecodes_ptr = NULL; 

    err = (*jvmti)->RawMonitorEnter(jvmti,lock); 
    check_jvmti_error(jvmti, err, "raw monitor enter"); 

     clazz->magic = 0xCAFEBABE; 

     err = (*jvmti)->GetMethodDeclaringClass(jvmti,method, &klass); 
     check_jvmti_error(jvmti, err, "Get Declaring Class"); 

     err = (*jvmti)->GetClassVersionNumbers(jvmti, klass, &minor, &major); 
     check_jvmti_error(jvmti, err, "Get Class Version Number"); 

     clazz->minor_version = (u2_int)minor; 
     clazz->major_version = (u2_int)major; 

     err = (*jvmti)->GetConstantPool(jvmti, klass, &constant_pool_count_pointer, 
       &constant_pool_byte_count_pointer, &constant_pool_bytes_ptr); 
     check_jvmti_error(jvmti, err, "Get Constant Pool"); 

     clazz->constant_pool_count = constant_pool_count_pointer; 
     clazz->constant_pool = constant_pool_bytes_ptr; 

     err = (*jvmti)->GetClassModifiers(jvmti,klass, &modifier_ptr); 
     check_jvmti_error(jvmti, err, "Get Access Flags"); 

     clazz->access_flags = (u2_int)modifier_ptr; 


     err = (*jvmti)->GetBytecodes(jvmti,method, &code_size, &bytecodes_ptr); 
     check_jvmti_error(jvmti, err, "Get Bytecodes"); 

     err = (*jvmti)->GetLocalVariableTable(jvmti,method, &local_entry_count_ptr, &table_ptr); 
     check_jvmti_error(jvmti, err, "Get Local Variable table"); 



    if (constant_pool_bytes_ptr != NULL) { 
     err = (*jvmti)->Deallocate(jvmti,(unsigned char*)constant_pool_bytes_ptr); 
     check_jvmti_error(jvmti, err, "deallocate bytecodes pointer"); 
    } 
    if (bytecodes_ptr != NULL) { 
     err = (*jvmti)->Deallocate(jvmti,(unsigned char*)bytecodes_ptr); 
     check_jvmti_error(jvmti, err, "deallocate bytecodes pointer"); 
    } 
    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); 
} 

Мои вопросы:

  1. можно ли получить полный файл класса через агента?
  2. Если нет, то как я могу заполнить структуру ClassFile, используя JVMTI или JNI API?
  3. Любая другая стратегия для достижения моей цели?

Ограничение:

  1. Я должен изучить и манипулировать байткоды во время выполнения долгого времени после загрузки класса. Таким образом, агенты Java AFAIK, использующие библиотеки, такие как ASM и JAVASSIST, не помогли бы.

Любая помощь будет высоко оценена.

+0

Вы смешиваете термины. ASM и JAVASSIST - это просто библиотеки, а не агенты. Как вы их используете, т. Е. Во время загрузки класса или позже, зависит от вас. Но поскольку вы пишете собственный код, использование библиотек, написанных на Java, было бы довольно сложным. Что касается вашего фактического вопроса, 'ClassFileLoadHook' получит байтовый код в формате файла класса. Тот факт, что вы хотите получить его для уже загруженных классов, не так важен, поскольку, если вы вызываете «RetransformClasses», крючки снова будут вызваны для указанных классов. – Holger

+0

Благодарим вас за исправление. Однако я не мог понять, что вы сказали о моем вопросе. Моя проблема заключается не в том, чтобы получить только байт-код. Я уже делал это, и это в формате файла класса: '(* JVMTI) -> GetBytecodes (JVMTI, метод & code_size, & bytecodes_ptr);' Я хочу, чтобы весь файл класса, который имеет постоянный пул, интерфейсы, атрибуты и т. д., как указано в спецификации. Кроме того, я не хочу слушать «ClassFileLoadHook». У меня есть вызов для события 'CompiledMethodLoad', который должен получить всю информацию о классе. Надеюсь, это очистит помещение моей проблемы. –

+0

'GetBytecodes' возвращает байтовый код одного метода. Так что это не «в формате файла класса», ну иначе, в чем вопрос? Если вы хотите получить весь файл класса, «ClassFileLoadHook» - это путь. Как было сказано выше, когда вы вызываете «RetransformClasses», крючок будет вызываться с указанным классом немедленно. Это способ получить файл класса правильно, когда вам это нужно. Это немного сложно, но это единственный способ узнать. – Holger

ответ

2

Так что, наконец, я получил его. Идеи Хольгера в комментариях являются основными источниками этого ответа. Я не думаю, что кому-то это понадобится, но просто чтобы ответить на этот вопрос.

Проще говоря, чтобы получить весь файл класса, есть только одна возможность в API JVMTI, и это событие ClassFileLoadHook. Это событие запускается всякий раз, когда новый класс загружается в JVM или когда вызываются функции Retransformclasses или RedefineClasses. Поэтому я вызвал функцию Retransformclasses как фиктивный вызов, чтобы вызвать ClassFileLoadHookEvent, а затем, наконец, получил весь класс.

Добавлено следующие функции в моем исходном коде, главным образом, помимо добавления возможности и обратного вызова настроек:

void JNICALL 
Class_File_Load_Hook(jvmtiEnv *jvmti_env, 
      JNIEnv* jni_env, 
      jclass class_being_redefined, 
      jobject loader, 
      const char* name, 
      jobject protection_domain, 
      jint class_data_len, 
      const unsigned char* class_data, 
      jint* new_class_data_len, 
      unsigned char** new_class_data) 
{ 
    jvmtiError err; 
    unsigned char* jvmti_space = NULL; 
    char* args = "vop"; 

    javab_main(3, args, class_data, class_data_len); 

    err = (*jvmti_env)->Allocate(jvmti_env, (jlong)global_pos, &jvmti_space); 
    check_jvmti_error(jvmti_env, err, "Allocate new class Buffer."); 

    (void)memcpy((void*)jvmti_space, (void*)new_class_ptr, (int)global_pos); 

    *new_class_data_len = (jint)global_pos; 
    *new_class_data = jvmti_space; 
} 

Переменная class_data здесь содержит полный файл класса, на котором вызвавшей ClassFileLoadHook событие.Я проанализировал этот файл класса и измерил его в новом массиве char* с использованием метода javab_main и, наконец, указал новый массив на переменную new_class_data. new_class_ptr - глобальная переменная, которая содержит измененное определение класса.

Смежные вопросы