Soot学习笔记(2)

来源:互联网 发布:node.js用什么软件 编辑:程序博客网 时间:2024/05/18 17:00

通过soot来分析一个java classfile 对于Sable官网的介绍的文档,我们将使用soot flow analysis framework

学习这个例子的目地在于:

1. how to inspect a class file by using Soot, and   // 怎么样去用soot去检测一个class文件
2. how to profile a program by instrumenting the class file. //通过instrumenting class file to 剖析你的程序

任务:计算我们到底进行了多少次静态调用

1.Testinvoke 类 ,该类是我们要进行分析的类的sourcecode。

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++;}}

为了计算我们到底进行了多少次静态调用,我们需要一个辅助类MyCounter

/* The counter class */public class MyCounter {/* the counter, initialize to zero */private static int c = 0;/** * increases the counter by *  * <pre> * howmany * </pre> *  * @param howmany *            , the increment of the counter. */public static synchronized void increase(int howmany) {c += howmany;}/** * reports the counter content. */public static synchronized void report() {System.err.println("counter : " + c);}}

接下来我们要创建一个wrapper class(封装类)通过加入一个phrase 进入soot来完成我们的任务:剖析TestInvoke需要进行多少staticInvoke?然后在再执行运行soot(通过soot.Main.main()).

wrapper class: MainDriver:

import soot.*;/* import necessary soot packages */public class MainDriver {/** * 这里放入: TestInvoke.java * @param args */    public static void main(String[] args) {         /* check the arguments */        if (args.length == 0){            System.err.println("Usage: java MainDriver [options] classname");            System.exit(0);        }                /* add a phase to transformer pack by call Pack.add */         Pack jtp = PackManager.v().getPack("jtp");                jtp.add(new Transform("jtp.instrumanter",                 new InvokeStaticInstrumenter()));        /* Give control to Soot to process all options,         * InvokeStaticInstrumenter.internalTransform will get called.         */        soot.Main.main(args);    }}
 我们要如何去统计TestInvoke中staticInvoke调用了多少次呢,我们就要在它每次staticInvoke之前先放入一个类似计数器的语句来统计staticInvoke的运行次数,这个任务就需要InvokeStaticInstrumanters

import soot.*;import soot.jimple.*;import soot.toolkits.graph.ExceptionalUnitGraph;import soot.toolkits.graph.UnitGraph;import soot.util.*;import java.util.*;/** * InvokeStaticInstrumenter inserts count instructions before INVOKESTATIC * bytecode in a program. The instrumented program will report how many static * invocations happen in a run. *  * Goal: Insert counter instruction before static invocation instruction. Report * counters before program’s normal exit point. Approach: 1. Create a counter * class which has a counter field, and a reporting method. 2. Take each method * body, go through each instruction, and insert count instructions before * INVOKESTATIC. 3. Make a call of reporting method of the counter class. *  * Things to learn from this example: 1. How to use Soot to examine a Java * class. 2. How to insert profiling instructions in a class. * InvokeStaticInstrumenter extends the abstract class BodyTransformer, and * implements *  * <pre> * internalTransform * </pre> *  * method. */public class InvokeStaticInstrumenter extends BodyTransformer {/* some internal fields */static SootClass counterClass;static SootMethod increaseCounter, reportCounter;   //在soot中注册我们的辅助类static {counterClass = Scene.v().loadClassAndSupport("MyCounter");increaseCounter = counterClass.getMethod("void increase(int)");reportCounter = counterClass.getMethod("void report()");}/** internalTransform goes through a method body and inserts * counter instructions before an INVOKESTATIC instruction */@Overrideprotected void internalTransform(Body body, String arg1, Map arg2) { // 得到该方法SootMethod method = body.getMethod();// 调试System.out.println("instrumenting method : " + method.getSignature());System.out.println("MethodName: " + method.getName());// 得到该方法的UnitChainChain units = body.getUnits();//当遍历它的时候,改变它的语句链Iterator stmtIt = units.snapshotIterator(); // 遍历每一条语句while (stmtIt.hasNext()) {// 得到statementStmt stmt = (Stmt) stmtIt.next();if (!stmt.containsInvokeExpr()) {continue;}// take out the invoke expressionInvokeExpr expr = (InvokeExpr) stmt.getInvokeExpr();// 跳过 non-static invocationsif (!(expr instanceof StaticInvokeExpr)) {continue;}// now we reach the real instruction// call Chain.insertBefore() to insert instructions//插入一条Expr,调用我们构造的辅助类的方法,参数为1(显然)InvokeExpr incExpr = Jimple.v().newStaticInvokeExpr(increaseCounter.makeRef(), IntConstant.v(1));Stmt incStmt = Jimple.v().newInvokeStmt(incExpr);// 3. insert new statement into the chain    //we are mutating the unit chain)units.insertBefore(incStmt, stmt);}//最后,我们要插入一条语句来输出最后的结果//判断当前是不是main方法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(); // check if the instruction is a return with/without valueif ((stmt instanceof ReturnStmt)|| (stmt instanceof ReturnVoidStmt)) {// 2. then, make a invoke statementInvokeExpr reportExpr = Jimple.v().newStaticInvokeExpr(reportCounter.makeRef());// 3. insert new statement into the chainStmt reportStmt = Jimple.v().newInvokeStmt(reportExpr);units.insertBefore(reportStmt, stmt);}}}}}

下面是

改变后的TestInvoke.class改变后的截图:


   6  invokestatic MyCounter.increase(int) : void [36]

  50  invokestatic MyCounter.report() : void [17]

可以看到 6,50行的变化~我们插入了我们需要的expr

                                             
0 0