Я работаю над исследовательским проектом, который включает в себя отзывы профилировщика Hotspot. В настоящее время я работаю над агентом 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
, не помогут.
Любая помощь будет высоко ценится.
ClassFileLoadHook
s получит байтовый код в формате файла класса. Тот факт, что вы хотите получить его для уже загруженных классов, не столь важен, так как, если вы вызоветеRetransformClasses
, хуки вызовутся снова для указанных классов. - person Holger   schedule 14.12.2016(*jvmti)->GetBytecodes(jvmti,method, &code_size, &bytecodes_ptr);
Я хочу получить весь файл класса, который имеет постоянный пул, интерфейсы, атрибуты и т. д., как указано в спецификации. Более того, я не хочу слушатьClassFileLoadHook
. У меня есть обратный вызов для событияCompiledMethodLoad
, которому нужно получить всю информацию о классе. Я надеюсь, что это очищает предпосылки моей проблемы. - person Saqib Ahmed   schedule 15.12.2016GetBytecodes
возвращает байт-код одного метода. Так это не "в формате class file", ну а иначе в чем был вопрос? Если вы хотите получить весь файл класса, вам подойдетClassFileLoadHook
. Как сказано выше, когда вы вызываетеRetransformClasses
, хук будет вызываться с указанным классом немедленно. Это способ получить файл класса правильно, когда он вам нужен. Это немного сложно, но это единственный способ, который я знаю. - person Holger   schedule 15.12.2016struct
, который вы определили, не является фактическим файлом класса, он имеет только структуру, которая отдаленно напоминает его. Настоящий файл класса не имеет указателей и использует четко определенные типы данных с обратным порядком байтов, которые не обязательно соответствуют представлениюshort
иint
во время выполнения на вашем компьютере. - person Holger   schedule 15.12.2016RetransformClasses
, чтобы я мог получитьClassFileLoadHook
, которая предоставит мне весь файл класса. Было бы здорово, если бы вы могли написать небольшую демонстрацию в качестве ответа, чтобы я мог ее протестировать. Проблема в том, что я не знаю, как передать аргументconst jclass* classes
в функциюRetransformClasses
. Второй комментарий Я знаю, что этоstruct
не файл класса. Я просто хотел поработать, так как думал, что невозможно получить файл класса. - person Saqib Ahmed   schedule 15.12.2016jclass*
— это просто указатель наjclass
, который может быть массивом, если вас интересует несколько классов. Так как вас интересует только один класс, вы можете передать указатель на переменнуюklass
, которая уже содержит нужныйjclass
(после того, как вы вызвалиGetMethodDeclaringClass
). Таким образом, вы передаете&klass
какclasses
иclass_count
из1
(один). Вот и все. В последний раз я писал код на C так давно, что вам потребуется больше времени, чтобы исправить мои ошибки, чем писать самому. - person Holger   schedule 15.12.2016ClassFileLoadHook
должен вызыватьRetransformClasses
? Это уже метод, отвечающий за преобразование кода, если это предусмотрено. Все, что ему нужно сделать, это сохранить ссылку на новый (модифицированный) байт-код в указателе, переданном как параметрnew_class_data
. Вы читали документацию? - person Holger   schedule 15.12.2016