[Soot学习笔记][7]Soot Instrument教程学习(More on profiling)

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相比:

可以很明显看到两者的差别。这个思路是很清楚,但是个人觉得,过程太过繁琐,完全以这种思路做实验会很辛苦。

posted on 2012-01-04 15:43  qysh123  阅读(2225)  评论(0编辑  收藏  举报

导航