提问者:小点点

字节码插桩使用ASM5.0.注入跟踪器跟踪局部变量


我正在做Java字节码分析。我想不断跟踪局部变量的每个变化状态。这个想法很像调试器。例如,我有一个Java源代码,如

public class Foo {

public void main() {
    printOne();
}

public void printOne() {    
     int i=11110;
     String hello="hello";
}

通过使用ASM5.0,我可以访问行号、局部变量名、mehtod名称、var指令等。访问者如下所示

@Override
    public void visitLineNumber(int line, Label start) {
        System.out.println("line: "+line+" ");
        mv.visitLineNumber(line, start);

    }

    @Override
    public void visitLocalVariable(String name, String desc,String signature, Label start, Label end, int index) {
        System.out.println("local variable:"+name);
        mv.visitLocalVariable(name, desc, signature, start, end, index);

    }

    @Override
    public void visitVarInsn(int opcode, int var) {
        System.out.println("var instruction"+Integer.toHexString(opcode));
        mv.visitVarInsn(opcode, var);
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
        System.out.println("method");
        mv.visitMethodInsn(opcode, owner, name, desc, itf);
    }

问题是在哪里注入跟踪器(只是一个静态方法调用),并在堆栈上查找变量值并作为参数传递给跟踪器?

mv.visitMethodInsn(Opcodes.INVOKESTATIC, "path/to/Tracer", "trace", "(Ljava/lang/String;)V",false);

共1个答案

匿名用户

你不能对访问者做你想做的事情。访问者只访问局部变量的声明,这些声明存储在类文件Code属性的可选LocalVariableTable属性中(请参阅此处的规范)。这为您提供了局部变量的签名,但没有告诉您它们的值在运行时会发生什么。为此,您需要分析字节码中的loadstore指令(等等),正如您所说,这将有效地编写调试器。

原则上,您可以访问代码,查找存储到您感兴趣的局部变量索引中的所有操作码,并插入插桩代码以将新值传递给您自己的某个报告方法,但要重新实现调试器已经做的事情,这将是大量工作。

重申:LocalVariableTable不包含值。它是描述局部变量的元数据——它们的名称、类型等——存储在类文件中,以便于调试器使用。它是一个可选属性,只有在编译时包含调试信息时才会创建。在运行时,正在执行的代码不使用LocalVariableTable。局部变量值本身存在于堆栈中,在运行时分配。我认为你需要退后一步,想想你正在努力实现什么。你想知道局部变量在运行时采用了什么值。直到运行时才能知道。

您不能将调试器指向类文件并询问“局部变量x的值是多少?”。该信息在类文件中不存在。这甚至不是一个有意义的问题。您必须运行程序才能在其上使用调试器。调试器暂停执行并查看特定时间点执行堆栈上的值。您的asm访问者类正在查看类文件,而不是正在运行的代码或执行堆栈。

您可以知道的是运行时局部变量值在堆栈上的位置的索引。这是传递给局部变量访问者的索引参数。您需要获取该索引参数并分析方法的字节码,以找到在该索引处将值存储到变量中的所有操作码。这些表示方法中局部变量的值可能发生变化的点。然后,您可以插入额外的字节码来调用您自己的报告方法,在指定的索引处传递局部变量的值。然后您必须运行已检测的代码。不用说,这些都不适合胆小的人!

相关问题