Я работаю над исследовательским проектом, который включает в себя обратную связь профилировщика Hotspot. В настоящее время я работаю над агентом JVMTI, который должен иметь следующие функции:Как получить файл класса (в формате спецификации) во время выполнения с использованием JVMTI?
- прослушать любое скомпилированное событие нагрузки.
- Извлечь и проанализировать полный файл класса, который имеет метод «горячей точки».
- Изменить/переопределить байт-коды класса.
У меня есть много функций 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);
}
Мои вопросы:
- можно ли получить полный файл класса через агента?
- Если нет, то как я могу заполнить структуру
ClassFile
, используяJVMTI
илиJNI
API? - Любая другая стратегия для достижения моей цели?
Ограничение:
- Я должен изучить и манипулировать байткоды во время выполнения долгого времени после загрузки класса. Таким образом, агенты Java AFAIK, использующие библиотеки, такие как
ASM
иJAVASSIST
, не помогли бы.
Любая помощь будет высоко оценена.
Вы смешиваете термины. ASM и JAVASSIST - это просто библиотеки, а не агенты. Как вы их используете, т. Е. Во время загрузки класса или позже, зависит от вас. Но поскольку вы пишете собственный код, использование библиотек, написанных на Java, было бы довольно сложным. Что касается вашего фактического вопроса, 'ClassFileLoadHook' получит байтовый код в формате файла класса. Тот факт, что вы хотите получить его для уже загруженных классов, не так важен, поскольку, если вы вызываете «RetransformClasses», крючки снова будут вызваны для указанных классов. – Holger
Благодарим вас за исправление. Однако я не мог понять, что вы сказали о моем вопросе. Моя проблема заключается не в том, чтобы получить только байт-код. Я уже делал это, и это в формате файла класса: '(* JVMTI) -> GetBytecodes (JVMTI, метод & code_size, & bytecodes_ptr);' Я хочу, чтобы весь файл класса, который имеет постоянный пул, интерфейсы, атрибуты и т. д., как указано в спецификации. Кроме того, я не хочу слушать «ClassFileLoadHook». У меня есть вызов для события 'CompiledMethodLoad', который должен получить всю информацию о классе. Надеюсь, это очистит помещение моей проблемы. –
'GetBytecodes' возвращает байтовый код одного метода. Так что это не «в формате файла класса», ну иначе, в чем вопрос? Если вы хотите получить весь файл класса, «ClassFileLoadHook» - это путь. Как было сказано выше, когда вы вызываете «RetransformClasses», крючок будет вызываться с указанным классом немедленно. Это способ получить файл класса правильно, когда вам это нужно. Это немного сложно, но это единственный способ узнать. – Holger