Я хочу напечатать смещение байт-кода, где исключение произошло во время выполнения. У меня есть патч JVMTI, чтобы сделать это. Но когда я применяю код с помощью ASM, смещение напечатанного байтового кода для исключения отличается из-за инструментария. Есть ли способ получить старое смещение байт-кода инструкции после завершения инструментирования? Или есть какой-нибудь альтернативный способ сделать это?
Вот пример класса:
class Test {
public static void main(String args[]) {
System.out.println("Before exception!!!");
System.out.println(5/0);
System.out.println("After exception!!!");
}
}
Патч JVMTI выглядит следующим образом:
#include <jvmti.h>
#include <stdio.h>
static jclass Exception;
static jfieldID detailMessage;
void JNICALL VMInit(jvmtiEnv* jvmti, JNIEnv* env, jthread thread) {
jclass localE = env->FindClass("java/lang/Exception");
Exception = (jclass) env->NewGlobalRef(localE);
jclass Throwable = env->FindClass("java/lang/Throwable");
detailMessage = env->GetFieldID(Throwable, "detailMessage", "Ljava/lang/String;");
}
void JNICALL ExceptionCallback(jvmtiEnv* jvmti, JNIEnv* env, jthread thread,
jmethodID method, jlocation location, jobject exception,
jmethodID catch_method, jlocation catch_location) {
if (env->IsInstanceOf(exception, Exception)) {
char buf[32];
sprintf(buf, "location=%ld", (long)location);
env->SetObjectField(exception, detailMessage, env->NewStringUTF(buf));
}
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
jvmtiEnv* jvmti;
vm->GetEnv((void**)&jvmti, JVMTI_VERSION_1_0);
jvmtiCapabilities capabilities = {0};
capabilities.can_generate_exception_events = 1;
jvmti->AddCapabilities(&capabilities);
jvmtiEventCallbacks callbacks = {0};
callbacks.VMInit = VMInit;
callbacks.Exception = ExceptionCallback;
jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, NULL);
return 0;
}
Чтобы скомпилировать patch используйте команду:
g++ -shared -fPIC -I/path/to/java/jdk1.7.0_80/include -I/path/to/java/jdk1.7.0_80/include/linux -o libJVMTIPatch.so JVMTIPatch.cpp
Для запуска используйте опцию -agentpath:
java -agentpath:/path/to/libJVMTIPatch.so Test
Вывод будет:
Before exception!!!
Exception in thread "main" java.lang.ArithmeticException: location=13
at Test.main(Test.java:6)
Где "местоположение = 13 "- это смещение байт-кода, где произошло исключение.
class Test {
Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Before exception!!!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iconst_5
12: iconst_0
13: idiv
14: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
17: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
20: ldc #6 // String After exception!!!
22: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
25: return
}
Но когда ASM для какой-то цели оснащается ASM, смещение байт-кода инструкции idiv изменяется и, следовательно, выходные данные отличаются, как и ожидалось:
public class Test {
Test();
Code:
0: invokestatic #39 // Method rr/state/ShadowThread.getCurrentShadowThread:()Lrr/state/ShadowThread;
3: astore_1
4: aload_1
5: astore_1
6: ldc #41 // String Test.<init>()V 0
8: aload_1
9: invokestatic #47 // Method rr/tool/RREventGenerator.bbEnter:(Ljava/lang/String;Lrr/state/ShadowThread;)V
12: aload_0
13: invokespecial #1 // Method java/lang/Object."<init>":()V
16: aload_0
17: ldc #49 // String ALOAD 0
19: aload_1
20: invokestatic #53 // Method rr/tool/RREventGenerator.objIdPrint:(Ljava/lang/Object;Ljava/lang/String;Lrr/state/ShadowThread;)V
23: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #39 // Method rr/state/ShadowThread.getCurrentShadowThread:()Lrr/state/ShadowThread;
3: astore_1
4: aload_1
5: astore_1
6: ldc #55 // String Test.main([Ljava/lang/String;)V 0
8: aload_1
9: invokestatic #47 // Method rr/tool/RREventGenerator.bbEnter:(Ljava/lang/String;Lrr/state/ShadowThread;)V
12: aload_0
13: ldc #49 // String ALOAD 0
15: aload_1
16: invokestatic #53 // Method rr/tool/RREventGenerator.objIdPrint:(Ljava/lang/Object;Ljava/lang/String;Lrr/state/ShadowThread;)V
19: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
22: ldc #56 // int 0
24: invokestatic #61 // Method rr/instrument/classes/ClassInitNotifier.__$rr_static_access:(I)V
27: ldc #3 // String Before exception!!!
29: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
32: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
35: ldc #56 // int 0
37: invokestatic #61 // Method rr/instrument/classes/ClassInitNotifier.__$rr_static_access:(I)V
40: iconst_5
41: iconst_0
42: idiv
43: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
46: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
49: ldc #56 // int 0
51: invokestatic #61 // Method rr/instrument/classes/ClassInitNotifier.__$rr_static_access:(I)V
54: ldc #6 // String After exception!!!
56: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
59: return
}
Отсюда вывод:
Before exception!!!
Exception in thread "main" java.lang.ArithmeticException: location=42
at Test.main(Test.java:6)
Я хочу получить смещение до того, как контрольно-измерительные приборы были сделаны.