Soot 学习笔记 8:More on profiling
来源:互联网 发布:java工程师工作职责 编辑:程序博客网 时间:2024/05/18 12:39
本文参考 Using Soot to instrument a class file
之后
我们发现在每个 staticinvoke 指令前都插入了一个 MyCounter.increase(1),并且在 main 方法的 return 指令前插入了MyCounter.report()
目标
- 怎样用 soot 对一个 class 文件 inspect
- 怎样用为 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。当MainDriver 对 soot.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
- Soot 学习笔记 8:More on profiling
- [Soot学习笔记][7]Soot Instrument教程学习(More on profiling)
- Soot学习笔记(1)-Adding profiling instructions to applications
- Soot 学习笔记 7:使用 Soot 为应用进行 profiling 插桩
- Soot学习笔记(1)
- Soot学习笔记(2)
- soot 学习笔记 一
- soot 学习笔记 三
- soot 学习笔记 二
- soot 学习笔记 四
- soot学习笔记(一)
- [Soot学习笔记][5]Soot依赖的两个框架
- Soot 学习笔记 1:First Step 命令行执行 soot
- Soot 学习笔记 4:Soot 的 pack 和 phase
- Soot 学习笔记 5:使用 Soot 创建 Java class
- Soot 学习笔记 6:一些 Soot 重要的类
- soot学习笔记-2.使用soot解析Android apk.
- Soot学习笔记3.使用soot解析java文件
- linux下TCP连接占用的资源
- 可编辑的下拉选择框,既可以输入又可以选择
- 4-20mA电流环设计
- 深搜-素数环-nyoj
- Oracle 数据泵使用详解
- Soot 学习笔记 8:More on profiling
- Ubuntu 12.04上PPPoe服务的搭建和使用
- 套接字编程API
- 关于opencv2.4.9配置vS2010(win7 32)
- makefile.new(7117) : error U1087: cannot have : and :: dependents for same target
- sql 创建异库链接
- Decode Ways
- CoffeeScript的缩进
- NGUI技能冷却CD特效制作