Чтение исключения при выходе из метода с использованием ASM - PullRequest
0 голосов
/ 17 ноября 2018

Я пишу небольшой инструмент Java, который записывает все записи метода и выходы с использованием ASM 5.2. Ниже приведена программа, которая работает хорошо. Эта программа в основном использует блок try и finally (без перехвата). 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 / finally (используя инструментарий) без перехвата (поскольку приложение должно перехватывать исключение, а не этот инструмент)

7) Над первой программой находится AdviceAdapter от ASM, который вставляет байт-код во все методы приложения во время загрузки класса

8) onMethodEnter () внедрит код записи, который будет записывать метод приложения, введенный при вызове во время выполнения

9) onMethodExit () будет вставлять код записи во вновь введенный блок finally, чтобы записать метод приложения как выход во время выполнения (будучи в finally гарантирует, что этот блок кода всегда выполняется)

До этого все работает хорошо. Правильно генерирует динамические графы вызовов.

Теперь этой первой программе, приведенной выше, я хочу добавить дополнительный код, который также будет считывать исключение (тип и трассировку стека), создаваемое приложением / методом. Опять только чтение не ловит. Для этого я добавил этот дополнительный код во второй программе выше. Проблема в том, что эта вторая программа читает только несколько исключений, но не все. Не уверен, где ошибка в коде. Любая помощь с благодарностью.

Спасибо

Шринивас

1 Ответ

0 голосов
/ 13 декабря 2018

Ваше требование отражает мои требования, и я использую приведенный ниже код, который записывает исключение, генерируемое конкретным java-методом. Он записывает исключение (я), только когда они не перехвачены с помощью блока try ... catch ....

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

}

...