使用ASM 5.0的字节码仪器。注入跟踪器来跟踪局部变量

时间:2015-06-21 23:20:02

标签: java bytecode java-bytecode-asm bytecode-manipulation

我正在进行Java字节码分析。我想继续跟踪局部变量的每个变化状态。这个想法很像一个调试器。例如,我有一个像

这样的Java源代码
# get a column named region, with the names of the regions
df = get_admin1_regions("dominican republic") 
df$value = 1:nrow(df) # add a column called "value"

# ?admin1_choropleth needs the name of the country and the data
admin1_choropleth(country.name = "dominican republic", df = df)

通过使用ASM 5.0,我可以访问行号,本地变量名,mehtod名,var指令等。访客看起来如下

public class Foo {

public void main() {
    printOne();
}

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

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

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

1 个答案:

答案 0 :(得分:2)

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

原则上,您可以访问代码,查找存储到您感兴趣的本地变量索引中的所有操作码,并插入检测代码以将新值传递给您自己的某些报告方法,但这将是一个重新实现调试器已经完成的工作很多。

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

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

可以知道的内容,是运行时局部变量值在堆栈上的位置的索引。这是传递给本地变量visitor的索引参数。您需要获取该索引参数并分析方法的字节码,以查找将值存储到该索引处的变量中的所有操作码。这些代表方法中可能改变局部变量值的点。然后,您可以插入其他字节码来调用您自己的报告方法,并在指定的索引处传递局部变量的值。然后,您必须运行已检测的代码。毋庸置疑,这一切都不适合胆小的人!