Question

I am using java Instrumentation and ASM ByteCode Library for developing a Javaagent. How to get Runtime exception thrown by a Method?

Attached the Code. Here am getting whether the method is terminated normally or throw an exception. But not able to retrieve the exception. How to Retrieve the Exception ?

 package com.abc.agent.servlet;

 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;

 import com.abc.agent.matcher.HttpServletMethodMatcher;

public class AbcServletMethodVisitorAdapter extends MethodVisitor {
    private String methodName;
    private String className;
    private String description;
    private boolean doMethodMatch;
    private int opcode = -1;

public AbcServletMethodVisitorAdapter(MethodVisitor mv , String  methodName , String description , String className) {
    super(Opcodes.ASM4, mv);
    this.methodName = methodName;
    this.className = className;
    this.description = description;
    this.doMethodMatch = false;
}

public void visitCode() {
    super.visitCode();
    if(methodName.equals("<clinit>") || methodName.equals("<init>"))
        return;
    HttpServletMethodMatcher httpServletMethodMatcher = new HttpServletMethodMatcher(className , methodName , description);
    this.doMethodMatch =  httpServletMethodMatcher.isHttpServletMatch();
    if(this.doMethodMatch){
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitVarInsn(Opcodes.ALOAD, 1);
        mv.visitLdcInsn(this.className);
        mv.visitLdcInsn(this.methodName);
        mv.visitLdcInsn(this.description);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/abc/agent/trace/RootTracer", "httpServletDoMethodBegin", "(Ljava/lang/Object;Ljavax/servlet/http/HttpServletRequest;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
        mv.visitCode();
    }
    else // Other Methods defined in the HttpServlet...
    {
        mv.visitLdcInsn(this.className);
        mv.visitLdcInsn(this.methodName);
        mv.visitLdcInsn(this.description);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/abc/agent/trace/RootTracer", "httpServletOtherMethodBegin", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
        mv.visitCode();
    }
}

public void visitMaxs(int maxStack, int maxLocals) {
    super.visitMaxs(maxStack + 4, maxLocals);
    }

public void visitInsn(int opcode) {
    if(methodName.equals("<clinit>") || methodName.equals("<init>")){
        // Do nothing....
    }
    else{
        if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) {
            this.opcode = opcode;
            mv.visitLdcInsn(this.className);
            mv.visitLdcInsn(this.methodName);
            mv.visitLdcInsn(this.description);
            mv.visitLdcInsn(this.opcode);
            if(this.doMethodMatch) {
                mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/abc/agent/trace/RootTracer", "httpServletDoMethodEnd", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
            }
            else{
                mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/abc/agent/trace/RootTracer", "httpServletOtherMethodEnd", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
            }
        }
    }
    mv.visitInsn(opcode);
}
}
Was it helpful?

Solution

Here is a simple example. First we create a bunch of labels for the variaous points in the code we need to refer to. Then we set up a try-catch block around the method execution. Then we call the code in the "try" block, and if an exception is thrown in the try block, execution will jump to the lCatchBlockStart label with the exception on the stack. The example catch block calls e.printStackTrace().

I used local variable number 1 in this example because it happened to be available. You might have to choose some other variable - of course you don't have to store the exception in a variable, but it is quite common to do so.

Unless you use the COMPUTE_FRAMES flag when constructing your ClassWriter you will have to insert visitFrame() calls as well.

Label lTryBlockStart = new Label();
Label lTryBlockEnd = new Label();
Label lCatchBlockStart = new Label();
Label lCatchBlockEnd = new Label();
// set up try-catch block for RuntimeException
mv.visitTryCatchBlock(lTryBlockStart, lTryBlockEnd, lCatchBlockStart, "java/lang/RuntimeException");
mv.visitLabel(lTryBlockStart);
// code to call the method goes here
mv.visitLabel(lTryBlockEnd);
mv.visitJumpInsn(GOTO, lCatchBlockEnd); // when here, no exception was thrown, so skip exception handler

// exception handler starts here, with RuntimeException stored on the stack
mv.visitLabel(lCatchBlockStart);
mv.visitVarInsn(ASTORE, 1); // store the RuntimeException in local variable 1
// here we could for example do e.printStackTrace()
mv.visitVarInsn(ALOAD, 1); // load it
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/RuntimeException", "printStackTrace", "()V");

// exception handler ends here:
mv.visitLabel(lCatchBlockEnd);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top