Java 使用ASM在方法出口处读取异常

Java 使用ASM在方法出口处读取异常,java,instrumentation,java-bytecode-asm,Java,Instrumentation,Java Bytecode Asm,我正在编写一个小java工具,它使用ASM5.2记录所有方法的入口和出口。下面是运行良好的程序。这个程序基本上使用try-and-finally(无catch)块。onMethodEnter()记录方法项。当整个方法放在INSDE try/FINAL中时,它在finally块中记录方法退出 import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes

我正在编写一个小java工具,它使用ASM5.2记录所有方法的入口和出口。下面是运行良好的程序。这个程序基本上使用try-and-finally(无catch)块。onMethodEnter()记录方法项。当整个方法放在INSDE try/FINAL中时,它在finally块中记录方法退出

import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AdviceAdapter;

public class SGMethodAdapter extends AdviceAdapter {

    private String entry;
    private String exit;
    private Label startFinally = new Label();

    public SGMethodAdapter(int api, MethodVisitor mv, int access, String name, String desc, String className) {
        super(api, mv, access, name, desc);
        entry = ">" + className + ";" + name;
        exit = "<" + className + ";" + name;
    }

    public void visitCode() {
        super.visitCode();
        mv.visitLabel(startFinally);
    }

    public void visitMaxs(int maxStack, int maxLocals) {
        Label endFinally = new Label();
        mv.visitTryCatchBlock(startFinally, endFinally, endFinally, null);
        mv.visitLabel(endFinally);
        onFinally(ATHROW);
        mv.visitInsn(ATHROW);
        mv.visitMaxs(maxStack, maxLocals);
    }

    private void onFinally(int opcode) {
        try {
            mv.visitLdcInsn(exit);
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/test/MethodEntryExitRecorder", "methodExit",
                    "(Ljava/lang/String;)V", false);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    protected void onMethodEnter() {
        try {
            super.visitLdcInsn(entry);
            super.visitMethodInsn(Opcodes.INVOKESTATIC, "com/test/MethodEntryExitRecorder", "methodEntry",
                    "(Ljava/lang/String;)V", false);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    protected void onMethodExit(int opcode) {
        if (opcode != ATHROW) {
            onFinally(opcode);
        }
    }
}
更新:更好地解释问题 看来我对这个问题的解释不清楚

给你

1) 我正在开发一个记录所有java方法入口和出口的java工具(基本上在应用程序运行时生成一个动态调用图)

2) 为此,我不想更改应用程序代码。这个工具应该在不改变应用程序行为的情况下工作

3) 为了实现这一点,我使用ASM进行字节码插装

4) 该工具需要在方法体开始和结束时将字节码注入java应用程序的每个方法

5) 因为一个方法中可能有多个退出/返回点,所以在方法体结尾注入记录代码是不够的,因为如果该方法返回到方法体的一半,则不会记录方法退出

6) 为了克服这个问题,我将整个应用程序方法体放在try/finally(使用工具)中,不使用catch(因为应用程序需要捕捉异常,而不是这个工具)

7) 上面的第一个程序是ASM的AdviceAdapter,它将在类加载时向应用程序的所有方法中注入字节码

8) onMethodEnter()将注入记录代码,记录在运行时调用时输入的应用程序方法

9) onMethodExit()将在新注入的finally块中注入记录代码,以记录运行时退出的应用程序方法(处于finally中确保始终执行此代码块)

到目前为止,这一切都很顺利。它正确地生成了动态调用图

现在,对于上面的第一个程序,我想注入额外的代码,这些代码也将读取应用程序/方法抛出的异常(type和stacktrace)。再次只看不懂。 为此,我在上面的第二个程序中添加了这个附加代码。问题是,第二个程序只读取少数异常,而不是全部异常。不确定代码中的错误在哪里。非常感谢您的帮助

谢谢


Srinivas

您的需求反映了我的需求&我正在使用下面的代码记录特定java方法引发的异常-它仅在使用try…catch…block无法捕获这些异常时记录异常

package com.abc.agent.adapter;

import java.util.Map;
import com.abc.tm.Constants;
import com.abc.org.objectweb.asm.Label;
import com.abc.org.objectweb.asm.MethodVisitor;
import com.abc.org.objectweb.asm.Opcodes;
import com.abc.org.objectweb.asm.Type;
import com.abc.org.objectweb.asm.commons.AdviceAdapter;

public class PojoMethodAdviceAdapter extends AdviceAdapter  {

private String methodName;
private String className;
private String description;
private int okFlag =  newLocal(Type.BOOLEAN_TYPE); 

Label startFinally = new Label();

public PojoMethodAdviceAdapter(int access , MethodVisitor mv , String methodName, String description, String className , int classFileVersion, boolean learning, int lineNumber, Map pendingMethod){
    super(Opcodes.ASM5 , mv, access, methodName, description);
    this.className = className;
    this.methodName = methodName;
    this.description = description;
}

public void visitCode() {
    super.visitCode(); 
    mv.visitLabel(startFinally);
}

protected void onMethodEnter(){
    mv.visitInsn(Opcodes.ICONST_0);
    mv.visitVarInsn(ISTORE, okFlag);
    mv.visitLdcInsn(className);
    mv.visitLdcInsn(methodName);
    mv.visitLdcInsn(description);
    mv.visitMethodInsn(Opcodes.INVOKESTATIC, Constants.RootTracer, Constants.pojoMethodBegin, Constants.pojoMethodBeginDesc, false);
    mv.visitVarInsn(ISTORE, okFlag);
}

protected void onMethodExit(int opcode){
    if(opcode!=ATHROW) {
        onFinally(opcode);
    }
}

public void visitMaxs(int maxStack, int maxLocals){
    Label endFinally = new Label();
    mv.visitTryCatchBlock(startFinally, endFinally, endFinally, null);
    mv.visitLabel(endFinally);
    onFinally(ATHROW);
    mv.visitInsn(ATHROW);
    mv.visitMaxs(maxStack+4, maxLocals);
}

private void onFinally(int opcode){
    if(opcode == ATHROW){
        mv.visitInsn(Opcodes.DUP);
        mv.visitLdcInsn(className);
        mv.visitLdcInsn(methodName);
        mv.visitLdcInsn(description);
        mv.visitVarInsn(ILOAD, okFlag);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, Constants.RootTracer, Constants.recordPOJOException, Constants.recordPOJOExceptionDesc, false);
    }
    mv.visitLdcInsn(className);
    mv.visitLdcInsn(methodName);
    mv.visitLdcInsn(description);
    mv.visitVarInsn(ILOAD, okFlag);
    mv.visitLdcInsn(opcode);
    mv.visitMethodInsn(Opcodes.INVOKESTATIC, Constants.RootTracer, Constants.pojoMethodEnd, Constants.pojoMethodEndDesc, false);
}

}

ASM->assembly这个问题需要对java的指令集和操作码有一定程度的了解,比如DUP、ATHROW、INVOKESTATIC、SWAP、ASTORE等,因此我也标记了assembly。如果听起来不对,我将删除它。首先,你应该停止用
try{…}catch(Throwable t){t.printStackTrace();}
包装所有代码的习惯。当您已经知道某个应用程序出错(因此,生成的代码将被破坏)时,继续处理该应用程序是没有意义的。它会使你的代码变得杂乱无章,难以阅读。此外,您是否有一个示例代码,其插入指令的版本会引发无法处理的异常?@Holger在更新中对问题提供了更好的解释。我感谢您的努力,但您所解释的内容已被理解。我要求的是“当我插入以下方法时,不会报告在代码位置xyz抛出的xyz/类型的异常”类型的一些示例代码。
package com.abc.agent.adapter;

import java.util.Map;
import com.abc.tm.Constants;
import com.abc.org.objectweb.asm.Label;
import com.abc.org.objectweb.asm.MethodVisitor;
import com.abc.org.objectweb.asm.Opcodes;
import com.abc.org.objectweb.asm.Type;
import com.abc.org.objectweb.asm.commons.AdviceAdapter;

public class PojoMethodAdviceAdapter extends AdviceAdapter  {

private String methodName;
private String className;
private String description;
private int okFlag =  newLocal(Type.BOOLEAN_TYPE); 

Label startFinally = new Label();

public PojoMethodAdviceAdapter(int access , MethodVisitor mv , String methodName, String description, String className , int classFileVersion, boolean learning, int lineNumber, Map pendingMethod){
    super(Opcodes.ASM5 , mv, access, methodName, description);
    this.className = className;
    this.methodName = methodName;
    this.description = description;
}

public void visitCode() {
    super.visitCode(); 
    mv.visitLabel(startFinally);
}

protected void onMethodEnter(){
    mv.visitInsn(Opcodes.ICONST_0);
    mv.visitVarInsn(ISTORE, okFlag);
    mv.visitLdcInsn(className);
    mv.visitLdcInsn(methodName);
    mv.visitLdcInsn(description);
    mv.visitMethodInsn(Opcodes.INVOKESTATIC, Constants.RootTracer, Constants.pojoMethodBegin, Constants.pojoMethodBeginDesc, false);
    mv.visitVarInsn(ISTORE, okFlag);
}

protected void onMethodExit(int opcode){
    if(opcode!=ATHROW) {
        onFinally(opcode);
    }
}

public void visitMaxs(int maxStack, int maxLocals){
    Label endFinally = new Label();
    mv.visitTryCatchBlock(startFinally, endFinally, endFinally, null);
    mv.visitLabel(endFinally);
    onFinally(ATHROW);
    mv.visitInsn(ATHROW);
    mv.visitMaxs(maxStack+4, maxLocals);
}

private void onFinally(int opcode){
    if(opcode == ATHROW){
        mv.visitInsn(Opcodes.DUP);
        mv.visitLdcInsn(className);
        mv.visitLdcInsn(methodName);
        mv.visitLdcInsn(description);
        mv.visitVarInsn(ILOAD, okFlag);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, Constants.RootTracer, Constants.recordPOJOException, Constants.recordPOJOExceptionDesc, false);
    }
    mv.visitLdcInsn(className);
    mv.visitLdcInsn(methodName);
    mv.visitLdcInsn(description);
    mv.visitVarInsn(ILOAD, okFlag);
    mv.visitLdcInsn(opcode);
    mv.visitMethodInsn(Opcodes.INVOKESTATIC, Constants.RootTracer, Constants.pojoMethodEnd, Constants.pojoMethodEndDesc, false);
}