Soot 学习笔记 8:More on profiling

来源:互联网 发布:java工程师工作职责 编辑:程序博客网 时间:2024/05/18 12:39
本文参考 Using Soot to instrument a class file

目标

  1. 怎样用 soot  对一个 class 文件 inspect
  2. 怎样用为 class 文件插桩的方法对程序 profile

用例子说明

Task:为下面这个 TestInvoke.java 程序统计 InvokeStatic 指令的运行次数。

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

现在我新建一个包装类来为 Soot 添加一个 phase。目的是插入 profiling 的指令,然后调用 soot.Main.main()。这个driver 类的 main method 添加了一个命名为 “jtp.instrumenter” 的 transformation phase 到 soot 的 “jtp” pack。当MainDriversoot.Main.main 发起调用时,Soot 能从 PackManager 中得知一个新的 phase 被注册了,并且它的internalTransform 方法会被 soot 调用。以下是 MainDriver.java:

      /* Usage: java MainDriver [soot-options] appClass       */          /* import necessary soot packages */      import soot.*;          public class MainDriver {        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.instrumenter",                                new InvokeStaticInstrumenter()));              /* Give control to Soot to process all options,           * InvokeStaticInstrumenter.internalTransform will get called.           */          soot.Main.main(args);        }      }
插桩器真正的实现是继承自抽象类 BodyTransformer。它实现了 internalTransform 方法,它需要一个方法的 body 和一些 option。主要的操作都在这个方法内发生。根据你的命令行,soot 创建一个 class 的 list(同样也有 method 的 list),然后传递每个方法的 body 并调用InvokeStaticInstrumenter.internalTransform 方法

以下是 InvokeStaticInstrumenter.java :

     1  /*     2   * InvokeStaticInstrumenter inserts count instructions before     3   * INVOKESTATIC bytecode in a program. The instrumented program will     4   * report how many static invocations happen in a run.     5   *     6   * Goal:     7   *   Insert counter instruction before static invocation instruction.     8   *   Report counters before program's normal exit point.     9   *    10   * Approach:    11   *   1. Create a counter class which has a counter field, and    12   *      a reporting method.    13   *   2. Take each method body, go through each instruction, and    14   *      insert count instructions before INVOKESTATIC.    15   *   3. Make a call of reporting method of the counter class.    16   *    17   * Things to learn from this example:    18   *   1. How to use Soot to examine a Java class.    19   *   2. How to insert profiling instructions in a class.    20   */    21    22  /* InvokeStaticInstrumenter extends the abstract class BodyTransformer,    23   * and implements <pre>internalTransform</pre> method.    24   */    25  import soot.*;    26  import soot.jimple.*;    27  import soot.util.*;    28  import java.util.*;    29    30  public class InvokeStaticInstrumenter extends BodyTransformer{    31    32    /* some internal fields */    33    static SootClass counterClass;    34    static SootMethod increaseCounter, reportCounter;    35    36    static {    37      counterClass    = Scene.v().loadClassAndSupport("MyCounter");    38      increaseCounter = counterClass.getMethod("void increase(int)");    39      reportCounter   = counterClass.getMethod("void report()");    40    }    41    42    /* internalTransform goes through a method body and inserts    43     * counter instructions before an INVOKESTATIC instruction    44     */    45    protected void internalTransform(Body body, String phase, Map options) {    46      // body's method    47      SootMethod method = body.getMethod();    48    49      // debugging    50      System.out.println("instrumenting method : " + method.getSignature());    51    52      // get body's unit as a chain    53      Chain units = body.getUnits();    54    55      // get a snapshot iterator of the unit since we are going to    56      // mutate the chain when iterating over it.    57      //    58      Iterator stmtIt = units.snapshotIterator();    59    60      // typical while loop for iterating over each statement    61      while (stmtIt.hasNext()) {    62    63        // cast back to a statement.    64        Stmt stmt = (Stmt)stmtIt.next();    65    66        // there are many kinds of statements, here we are only    67        // interested in statements containing InvokeStatic    68        // NOTE: there are two kinds of statements may contain    69        //       invoke expression: InvokeStmt, and AssignStmt    70        if (!stmt.containsInvokeExpr()) {    71          continue;    72        }    73    74        // take out the invoke expression    75        InvokeExpr expr = (InvokeExpr)stmt.getInvokeExpr();    76    77        // now skip non-static invocations    78        if (! (expr instanceof StaticInvokeExpr)) {    79          continue;    80        }    81    82        // now we reach the real instruction    83        // call Chain.insertBefore() to insert instructions    84        //    85        // 1. first, make a new invoke expression    86        InvokeExpr incExpr= Jimple.v().newStaticInvokeExpr(increaseCounter.makeRef(),    87                                                    IntConstant.v(1));    88        // 2. then, make a invoke statement    89        Stmt incStmt = Jimple.v().newInvokeStmt(incExpr);    90    91        // 3. insert new statement into the chain    92        //    (we are mutating the unit chain).    93        units.insertBefore(incStmt, stmt);    94      }    95    96    97      // Do not forget to insert instructions to report the counter    98      // this only happens before the exit points of main method.    99   100      // 1. check if this is the main method by checking signature   101      String signature = method.getSubSignature();   102      boolean isMain = signature.equals("void main(java.lang.String[])");   103   104      // re-iterate the body to look for return statement   105      if (isMain) {   106        stmtIt = units.snapshotIterator();   107   108        while (stmtIt.hasNext()) {   109          Stmt stmt = (Stmt)stmtIt.next();   110   111          // check if the instruction is a return with/without value   112          if ((stmt instanceof ReturnStmt)   113              ||(stmt instanceof ReturnVoidStmt)) {   114            // 1. make invoke expression of MyCounter.report()   115            InvokeExpr reportExpr= Jimple.v().newStaticInvokeExpr(reportCounter.makeRef());   116   117            // 2. then, make a invoke statement   118            Stmt reportStmt = Jimple.v().newInvokeStmt(reportExpr);   119   120            // 3. insert new statement into the chain   121            //    (we are mutating the unit chain).   122            units.insertBefore(reportStmt, stmt);   123          }   124        }   125      }   126    }   127  }
然后测试插桩器,在插桩前,输入命令

java TestInvoke
运行被测类输出:

I made 20 static calls

运行插桩器:

java MainDriver TestInvoke
Soot started on Tue Feb 12 21:22:59 EST 2002Transforming TestInvoke... instrumenting method : <TestInvoke: void <init>()>instrumenting method : <TestInvoke: void main(java.lang.String[])>instrumenting method : <TestInvoke: void foo()>instrumenting method : <TestInvoke: void bar()>instrumenting method : <TestInvoke: void <clinit>()> Soot finished on Tue Feb 12 21:23:02 EST 2002Soot has run for 0 min. 3 sec.
然后再 ./sootOutput 文件中有一个转换好的类 TestInvoke.class,运行这个新类:

cd sootOutput
cp ../MyCounter.class .
java TestInvoke
输出:

I made 20 static callscounter : 20
对比一下插桩前后的 Jimple:

之前:

     1  class TestInvoke extends java.lang.Object     2  {            ......    14      public static void main(java.lang.String[] )    15      {             ......    26       label0:    27          staticinvoke <TestInvoke: void foo()>();    28          i0 = i0 + 1;                ......    42          return;    43      }    44    45      private static void foo()    46      {                ......    52          staticinvoke <TestInvoke: void bar()>();    53          return;    54      }    55    56      private static void bar()    57      {                ......    63          return;    64      }            ......    71  }

之后

     1  class TestInvoke extends java.lang.Object     2  {            ......    14      public static void main(java.lang.String[] )    15      {                ......    26       label0:    27          staticinvoke <MyCounter: void increase(int)>(1);    28          staticinvoke <TestInvoke: void foo()>();    29          i0 = i0 + 1;                ......    43          staticinvoke <MyCounter: void report()>();    44          return;    45      }    46    47      private static void foo()    48      {                ......    54          staticinvoke <MyCounter: void increase(int)>(1);    55          staticinvoke <TestInvoke: void bar()>();    56          return;    57      }    58    59      private static void bar()    60      {                ......    66          return;    67      }            ......    74  }

我们发现在每个 staticinvoke 指令前都插入了一个 MyCounter.increase(1),并且在 main 方法的 return 指令前插入了MyCounter.report()


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小孩经常流鼻子怎么办 5岁宝宝流鼻涕怎么办 小孩鼻涕一直流怎么办 二岁宝宝流鼻涕怎么办 小婴儿有点鼻塞怎么办 宝宝流鼻涕总不好怎么办 孩子鼻炎睡不好怎么办 鼻炎清鼻涕不止怎么办 宝宝持续低烧流鼻涕怎么办 孩子鼻塞不通气怎么办 2月婴儿感冒怎么办 长期流黄鼻涕怎么办 孩子流清水鼻涕怎么办 小孩有点流鼻子怎么办 初生婴儿堵鼻子怎么办? 小孩反复发烧了怎么办 小孩突然发烧了怎么办 40天宝宝鼻塞怎么办 宝宝伤风鼻子不通怎么办 鼻子伤风不通气怎么办 宝宝伤风流鼻子怎么办 十个月婴儿上火怎么办 一个多月宝宝鼻子有鼻屎怎么办 三个月婴儿感冒发烧怎么办 小孩感冒发烧流鼻涕怎么办 小孩感冒发烧反反复复怎么办 宝宝反复发烧39怎么办 一岁婴儿流鼻涕怎么办 四岁宝宝发烧怎么办 小孩流清鼻涕怎么办? 5宝宝光流清鼻涕怎么办 孩子一直流鼻子怎么办 10岁天天流鼻涕怎么办 喉咙痛又痒咳嗽怎么办 60天宝宝流鼻涕怎么办 宝宝流鼻子严重怎么办 鼻炎鼻涕多鼻塞怎么办 夏天老人感冒流鼻涕怎么办 鼻窦炎流清鼻涕怎么办 鼻子有脓鼻涕怎么办 宝宝有脓鼻涕怎么办