12月中旬的时候又按照页面:
http://www.sable.mcgill.ca/soot/tutorial/index.html
中的教程More on profiling,继续对Soot的Instrumentation功能学习。上面这个文档中已经把基本内容解释得很清楚了,我这里就简单总结一下。首先,假设要分析的字节码文件对应的源代码文件是:TestInvoke.java
class TestInvoke{
private static int calls=0;
public static void main (String[] args){
for (int i=0; i<10; i++){
foo();
}
System.out.println("I made "+calls+" static calls");
}
private static void foo(){
calls++;
bar();
}
private static void bar(){
calls++;
}
}
为了记录TestInvoke中调用了多少次静态方法bar,我们再生成一个很简单的MyCounter类(MyCounter.java):
public class MyCounter {
private static int c = 0;
public static synchronized void increase(int howmany){
c +=howmany;
}
public static synchronized void report(){
System.err.println("counter : " + c);
}
}
之后,需要把我们自己的分析代码插入到Soot自己分析的phase中,和我之前一篇博客介绍的内容类似,我们生成一个MainDriver.java:
import soot.*;
public class MainDriver {
public static void main(String[] args) {
if (args.length == 0){
System.err.println("Usage: java MainDriver [options] classname");
System.exit(0);
}
Pack jtp = PackManager.v().getPack("jtp");
jtp.add(new Transform("jtp.instrumanter",
new InvokeStaticInstrumenter()));
soot.Main.main(args);
}
}
最后,我们需要实现上面的InvokeStaticInstrumenter类,文档中对这个类的实现原理已经写得很清楚,这里就只把代码拷贝过来:
import soot.*;
import soot.jimple.*;
import soot.util.*;
import java.util.*;
public class InvokeStaticInstrumenter extends BodyTransformer{
static SootClass counterClass;
static SootMethod increaseCounter , reportCounter;
static {
counterClass = Scene.v().loadClassAndSupport("MyCounter");
increaseCounter = counterClass.getMethod("void increase(int)");
reportCounter = counterClass.getMethod("void report()");
}
protected void internalTransform(Body body, String phase, Map options) {
SootMethod method = body.getMethod();
System.out.println("instrumenting method : " + method.getSignature());
Chain units = body.getUnits();
Iterator stmtIt = units.snapshotIterator();
while (stmtIt.hasNext()) {
Stmt stmt = (Stmt)stmtIt.next();
if (!stmt.containsInvokeExpr()){
continue;
}
InvokeExpr expr = (InvokeExpr)stmt.getInvokeExpr();
if (!(expr instanceof StaticInvokeExpr)) {
continue;
}
InvokeExpr incExpr = Jimple.v().newStaticInvokeExpr(increaseCounter.makeRef(), IntConstant.v(1));
Stmt incStmt = Jimple.v().newInvokeStmt(incExpr);
units.insertBefore(incStmt, stmt);
}
String signature = method.getSubSignature();
boolean isMain = signature.equals("void main(java.lang.String[])");
if (isMain) {
stmtIt = units.snapshotIterator();
while (stmtIt.hasNext()) {
Stmt stmt = (Stmt)stmtIt.next();
if ((stmt instanceof ReturnStmt)
||(stmt instanceof ReturnVoidStmt)){
InvokeExpr reportExpr = Jimple.v().newStaticInvokeExpr(reportCounter.makeRef());
Stmt reportStmt = Jimple.v().newInvokeStmt(reportExpr);
units.insertBefore(reportStmt, stmt);
}
}
}
}
}
之后,我们在已经配置好Soot开发环境的IDE(如Eclipse)中,以"TestInvoke"作为参数运行MainDriver.java,可以看到在工程文件夹下生成了一个"sootOutput"文件夹,里面有经过处理后的TestInvoke.class。为了方便起见,我们将MyCounter.class、MainDriver.class、InvokeStaticInstrumenter.class也拷贝到这个目录下,再次通过命令行运行TestInvoke,得到的结果如下图所示:
和没有处理之前的TestInvoke相比:
可以很明显看到两者的差别。这个思路是很清楚,但是个人觉得,过程太过繁琐,完全以这种思路做实验会很辛苦。