I want to get the current code line number when instrumenting the java bytecode. Instrumentation is achieved through ASM. Insert the bytecode corresponding to getLineNumber after the visitcode, the return value is -1, but the return value obtained by instrumentation in other locations is normal.
for example,the source code is as follows
public static int add(int a, int b){
int sum = a + b;
return sum;
}
According to the logic of ASM, the bytecode to obtain the line number information should be inserted after the add method. But when I call the function in the main method, the line number obtained is -1
At the same time, I also analyzed the assembly code before and after instrumentation, as follows
//this is before instrumentation
public static int add(int, int);
Code:
0: iload_0
1: iload_1
2: iadd
3: istore_2
4: iload_2
5: ireturn
//this is after instrumentation
public static int add(int, int);
Code:
0: new #33 // class java/lang/StringBuilder
3: dup
4: invokespecial #34 // Method java/lang/StringBuilder."<init>":()V
7: ldc #36 // String _
9: invokevirtual #40 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: invokestatic #46 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
15: invokevirtual #50 // Method java/lang/Thread.getStackTrace:()[Ljava/lang/StackTraceElement;
18: iconst_1
19: aaload
20: invokevirtual #56 // Method java/lang/StackTraceElement.getLineNumber:()I
23: invokevirtual #59 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #63 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: invokestatic #69 // Method afljava/logger/Logger.writeToLogger:(Ljava/lang/String;)V
32: iload_0
33: iload_1
34: iadd
35: istore_2
36: iload_2
37: ireturn
As you can see, I get not only the line number, but also the class name and method name. Among them, the class name and method name are obtained normally, and the line number is obtained as -1.
Additionally, Only inserting after the visitcode position will let the line number be -1, and inserting the same bytecode at other positions will not have this problem.
And this is one part of my instrumentation code
private void instrument(){
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;", false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Thread", "getName", "()Ljava/lang/String;", false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
mv.visitLdcInsn("_" + classAndMethodName + "_");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;", false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Thread", "getStackTrace", "()[Ljava/lang/StackTraceElement;", false);
mv.visitInsn(Opcodes.ICONST_1);
mv.visitInsn(Opcodes.AALOAD);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StackTraceElement", "getLineNumber", "()I", false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", false);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "afljava/logger/Logger", "writeToLogger", "(Ljava/lang/String;)V", false);
}
@Override
public void visitCode() {
super.visitCode();
instrument();
}
Like Holger's code,instead I insert code by using visitcode.