Commit 262eb925 authored by amadionix's avatar amadionix
Browse files

Using Soot to instrument a Java class; example from soot repo.

parents
Pipeline #5955 failed with stages
in 0 seconds
target/
*.class
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4" />
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>instrument-from-tutorial</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>soot</artifactId>
<version>4.2.1</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
/*
* 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.
*/
import soot.*;
import soot.jimple.*;
import soot.options.Options;
import soot.util.*;
import java.io.File;
import java.util.*;
public class InvokeStaticInstrumenter extends BodyTransformer{
/* some internal fields */
static SootClass counterClass;
static SootMethod increaseCounter, reportCounter;
static {
// I added this trying to fix 'is your soot-class-path set properly?'
String sourceDirectory = System.getProperty("user.dir") + File.separator + "target"
+ File.separator + "classes";
Options.v().set_soot_classpath(sourceDirectory);
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
*/
protected void internalTransform(Body body, String phase, Map options) {
// body's method
SootMethod method = body.getMethod();
// debugging
System.out.println("instrumenting method : " + method.getSignature());
// get body's unit as a chain
Chain units = body.getUnits();
// get a snapshot iterator of the unit since we are going to
// mutate the chain when iterating over it.
//
Iterator stmtIt = units.snapshotIterator();
// typical while loop for iterating over each statement
while (stmtIt.hasNext()) {
// cast back to a statement.
Stmt stmt = (Stmt)stmtIt.next();
// there are many kinds of statements, here we are only
// interested in statements containing InvokeStatic
// NOTE: there are two kinds of statements may contain
// invoke expression: InvokeStmt, and AssignStmt
if (!stmt.containsInvokeExpr()) {
continue;
}
// take out the invoke expression
InvokeExpr expr = (InvokeExpr)stmt.getInvokeExpr();
// now skip non-static invocations
if (! (expr instanceof StaticInvokeExpr)) {
continue;
}
// now we reach the real instruction
// call Chain.insertBefore() to insert instructions
//
// 1. first, make a new invoke expression
InvokeExpr incExpr= Jimple.v().newStaticInvokeExpr(increaseCounter.makeRef(),
IntConstant.v(1));
// 2. then, make a invoke statement
Stmt incStmt = Jimple.v().newInvokeStmt(incExpr);
// 3. insert new statement into the chain
// (we are mutating the unit chain).
units.insertBefore(incStmt, stmt);
}
// Do not forget to insert instructions to report the counter
// this only happens before the exit points of main method.
// 1. check if this is the main method by checking signature
String signature = method.getSubSignature();
boolean isMain = signature.equals("void main(java.lang.String[])");
// re-iterate the body to look for return statement
if (isMain) {
stmtIt = units.snapshotIterator();
while (stmtIt.hasNext()) {
Stmt stmt = (Stmt)stmtIt.next();
// check if the instruction is a return with/without value
if ((stmt instanceof ReturnStmt)
||(stmt instanceof ReturnVoidStmt)) {
// 1. make invoke expression of MyCounter.report()
InvokeExpr reportExpr= Jimple.v().newStaticInvokeExpr(reportCounter.makeRef());
// 2. then, make a invoke statement
Stmt reportStmt = Jimple.v().newInvokeStmt(reportExpr);
// 3. insert new statement into the chain
// (we are mutating the unit chain).
units.insertBefore(reportStmt, stmt);
}
}
}
}
}
/* 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);
}
}
\ No newline at end of file
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);
}
}
\ No newline at end of file
public 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++;
}
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment