使用javaagents在Java中进行基本块记录

时间:2013-04-27 05:12:30

标签: java instrumentation javassist

我的目标是在某些Java字节码的每个基本块的开头处检测少量代码。目的是通过基本块记录执行路径。目前,我正在使用Javassist在方法的开头和结尾处检测一些代码,但是我已经遇到了使用Javassist API编写更精细字节码位置的问题。

Javassist为我提供了一个Block [],它代表了方法中的所有基本块。基本块可以报告它们的字节码位置,因此我知道我的仪器需要去哪里。我想要使​​用的Javassist的工具是CtMethod.insertAt(int sourceCodeLineNumber,String newSourceCode),但是它使用源代码行号而不是字节码行号,这会导致this问题中说明的问题(我很确定到目前为止还没有解决这个问题)。

问题:当变量本身来自Javassist instrumentation 时,如何使用检测为方法中的变量赋值。我最好不必使用其他工具,但是我现在可以得到任何帮助。

使用Javassist声明变量:

//m is a CtMethod           
try { m.addLocalVariable("someVar", ClassPool.getDefault().get("SomePackage.SomeClass")); } 
catch (NotFoundException e) { e.printStackTrace(); }

我最糟糕的情况是以某种方式推断javassist工具的堆栈变量,并使用遍历整个方法/类的迭代器插入字节码,但那真的很讨厌。我的方法只有一个整数输入(块ID)和void输出,所以Java字节码在每个基本块的开头都是这样的:

ALOAD 6 //6 is my new Javassist variable ID, however I don't think I can get Javassist to actually tell it to me
ICONST_1 //My parameters, which is an int.  I'd have to switch statement between ICONST, SIPUSH, and ALOAD depending on basic block's index size
INVOKEVIRTUAL SomePackage/SomeClass.SomeMethod(I)V

1 个答案:

答案 0 :(得分:1)

我发现从ConstPool表中获取变量ID的最佳方法是在插入变量后测试最大大小

    try { m.addLocalVariable(mse, ClassPool.getDefault().get("org.javadynamicanalyzer.MethodStackEntry")); } 
    catch (NotFoundException e) { e.printStackTrace(); }

    int mseCSindex=m.getMethodInfo().getCodeAttribute().getMaxLocals()-1;

接下来,我需要表中的invokevirtual索引。这有点令人讨厌。下面的代码在ConstPool表中搜索我正在寻找的函数。我正在搜索的功能在org.javadyanmicanalyzer.MethodStackEntry.setBlockID(int)

        int virtFunIndex=-1;
        boolean found=false;
        while(found==false){
            ++virtFunIndex;
            try{ 
                int id=cp.isMember("org.javadynamicanalyzer.MethodStackEntry", "setBlockIndex", virtFunIndex);
                if(id!=0){
                    found=true;
                }
            }
            catch(NullPointerException | ClassCastException e){}
        }

最后,我需要检测每个块的开头:

        int len=new ControlFlow(m).basicBlocks().length;
        for(int i=0; i<len; ++i){
            Block thisbb=new ControlFlow(m).basicBlocks()[i]; //we have to re-evaluate the control flow every time we add new code
            CodeIterator itr=m.getMethodInfo().getCodeAttribute().iterator();

            int pos=thisbb.position();
            byte[] newCode=new byte[]{Bytecode.ALOAD, //loads the mse class
                                      mseCSindex, //my mse local variable
                                      Bytecode.ICONST_0, //the input to the virtual function
                                      Bytecode.INVOKEVIRTUAL, //execute the virtual function
                                      (byte) virtFunIndex>>8, //virtual function's address
                                      (byte) virtFunIndex && 0xFF};

            int n = itr.insertAt(pos, newCode);
        }

它很乱,有可能彻底毁灭自己,但它有效!