使用ASM

时间:2018-11-17 06:05:34

标签: java instrumentation java-bytecode-asm

我正在编写一个小的Java工具,该工具使用ASM 5.2记录所有方法的进入和退出。下面是运行良好的程序。该程序基本上使用try and finally(无catch)块。 onMethodEnter()记录方法输入。当整个方法坚持尝试/最后尝试时,最后在它阻止时记录方法退出。

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

现在,我希望该程序在退出之前还读取任何方法抛出的异常(只读取而不捕获)。如果没有抛出异常,我只希望它记录正常方法的退出。如果方法抛出异常,则JVM保证在方法退出时异常对象在堆栈的顶部可用。下面是我经过调整以添加此行为的上述相同程序。问题是,这仅读取了少数例外,但不是全部。无法理解读取了哪些异常以及未读取哪些异常。不确定代码中的错误在哪里...非常感谢您的帮助...

private void onFinally(int opcode) {
    if(opcode == ATHROW) {
        try {
            mv.visitInsn(DUP);
            mv.visitLdcInsn(exit);
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/test/MethodEntryExitRecorder", "methodExitWithException",
                    "(Ljava/lang/Throwable;Ljava/lang/String;)V", false);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    } else {
        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) {
    onFinally(opcode);
}

更新:对该问题的更好解释

看起来我对问题的解释不清楚。

在这里。

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

2)为此,我不想更改应用程序代码。该工具可以正常工作,而无需更改应用程序行为

3)为此,我正在使用ASM进行字节代码检测

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

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

6)为了克服该问题,我将整个应用程序主体放在try / final(使用工具)中,没有捕获(因为应用程序需要捕获异常,而不是此工具)

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

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

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

到目前为止,它运行良好。它可以正确生成动态调用图。

现在,在上面的第一个程序中,我想注入其他代码,这些代码还将读取应用程序/方法抛出的异常(类型和堆栈跟踪)。再次,只有阅读没有抓住。 为此,我在上面的第二个程序中添加了此附加代码。问题在于第二个程序仅读取少数异常,而不是全部。不确定代码中的错误在哪里。任何帮助将不胜感激。

谢谢

Srinivas

1 个答案:

答案 0 :(得分:1)

您的需求反映了我的需求,我正在使用下面的代码来记录由特定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);
}

}