2013-03-05 243 views
2

我正在使用Java Instrumentation和ASM ByteCode库来开发Javaagent。 如何获得由方法抛出的运行时异常?ASM ByteCode - 异常的堆栈跟踪

附代码。这里得到方法是否正常终止或抛出 例外。但无法检索异常。如何检索异常?

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); 
} 
} 

回答

2

这是一个简单的例子。首先,我们为需要参考的代码中的变量点创建一堆标签。然后我们围绕方法执行建立一个try-catch块。然后我们调用“try”块中的代码,并且如果在try块中引发异常,则执行将跳转到lCatchBlockStart标签,但堆栈中的例外。示例catch块调用e.printStackTrace()。

我在本例中使用了局部变量编号1,因为它碰巧可用。您可能需要选择一些其他的变量 - 当然你不存储在一个变量的例外,但它是相当普遍这样做。

除非在构建ClassWriter时使用COMPUTE_FRAMES标志,否则还必须插入visitFrame()调用。

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); 
+0

嗨,你的帮助..我已附上代码,在此代码,其中插入你所指定的行感谢... – 2013-03-06 05:03:41

+0

因此,可以说,我有启动手柄/任何结束方法,我可以添加一些东西。但是我想打印由当前方法或方法调用的当前方法抛出的任何异常的堆栈跟踪。我将如何获得这个方法抛出的异常对象? – AKS 2016-02-02 12:52:04

+0

在我的示例代码中,使用'ASTORE,1'的行将异常存储在局部变量1中。然后,您可以根据需要多次检索该异常。在我的例子中,我加载了'ALOAD,1'并且在其上调用'printStackTrace'。 – 2016-02-02 16:14:03