Skip to content

Instantly share code, notes, and snippets.

@monperrus
Created November 10, 2011 11:22
Show Gist options
  • Save monperrus/1354641 to your computer and use it in GitHub Desktop.
Save monperrus/1354641 to your computer and use it in GitHub Desktop.
Static analysis to extract method calls
/**
* This program makes a static analysis in
* order to extract method calls of a directory containing Java bytecode .
*
* It uses the Soot library.
*
* Usage:
* 1. Change the classpath and the directoryToAnalyze variable
* 2. run java -cp bin:soot.jar mm.soot.MethodCallCollector
* 3. look at /tmp/data.dat
*
* For instance, running MethodCallCollector on itself outputs:
*
* <pre>
* Analyzing bin
* [-cp, .:/opt/jdk1.6.0_23/jre/lib/rt.jar:/opt/jdk1.6.0_23/lib/tools.jar:/opt/jdk1.6.0_23/jre/lib/jsse.jar:bin:bin:data-mining-for-software-engineering/soot-2.4.0.jar, -process-dir, bin, -include, org.apache.]
* Transforming mm.soot.MethodCallCollector$1...
* 0 java.lang.StringBuilder <init> append
* 1 java.util.Iterator next hasNext
* 2 soot.jimple.AssignStmt getLeftOp getRightOp
* 3 soot.Body getLocals getUnits getMethod
* 4 java.lang.StringBuilder <init> append
* 5 java.util.Iterator next hasNext
* 6 soot.jimple.InstanceInvokeExpr getBase getMethod
* 7 java.util.Iterator next hasNext
* 8 soot.PatchingChain iterator iterator
* 9 java.util.Iterator next hasNext
* 10 soot.Local getType getName
* 11 java.lang.StringBuilder <init> append
* 12 java.util.Iterator next hasNext
* 13 soot.jimple.Stmt containsInvokeExpr getInvokeExpr getTag
* 14 java.util.HashMap put get get get values <init>
* Transforming mm.soot.Variable...
* 15 java.lang.StringBuilder <init> append
* 16 java.lang.StringBuilder <init> append
* 17 java.lang.StringBuilder <init> append
* 18 java.util.Iterator next hasNext hasNext
* Transforming mm.soot.MethodCallCollector...
* 19 java.util.StringTokenizer nextToken hasMoreTokens <init>
* 20 java.lang.StringBuilder <init> append
* 21 java.io.File <init> exists
* 22 java.util.Iterator next hasNext
* 23 java.lang.StringBuilder <init> append
* 24 java.lang.StringBuilder <init> append
* 25 java.util.Iterator next hasNext
* </pre>
*
* @author Eric Bodden
* @author Martin Monperrus
* GNU General Public License
*
*/
package mm.soot;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import soot.Body;
import soot.BodyTransformer;
import soot.Local;
import soot.PackManager;
import soot.PatchingChain;
import soot.Scene;
import soot.Transform;
import soot.Unit;
import soot.Value;
import soot.jimple.AssignStmt;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.Stmt;
import soot.options.Options;
import soot.tagkit.SourceLnPosTag;
import soot.util.Chain;
public class MethodCallCollector {
static String directoryToAnalyze = "/path/to/bin";
/** the classpath given to soot, it is system dependent
* On Windows, use a semi-column as separator
* Note that you may use rt.jar, tools.jar and jsse.jar from a Linux installation even on Windows or Mac,
* because these jars are used by Soot (and not by the JVM)
*/
static List<String> lSootClasspath = new ArrayList<String>();
static void init() {
lSootClasspath.add("/opt/jdk1.6.0_23/jre/lib/rt.jar");
lSootClasspath.add("/opt/jdk1.6.0_23/lib/tools.jar");
lSootClasspath.add("/opt/jdk1.6.0_23/jre/lib/jsse.jar");
lSootClasspath.add(directoryToAnalyze);
StringTokenizer st = new StringTokenizer(System.getProperty("java.class.path"), classpathDelimiter);
while (st.hasMoreTokens()) {lSootClasspath.add(st.nextToken());}
}
/** The file in which the traces are put */
static String datasetFileName = "/tmp/data.dat";
public static void main(String[] args) throws Exception{
if (args.length == 1) {
directoryToAnalyze = args[0];
}
init();
for(String file : lSootClasspath) {
if (!new File(file).exists()) {
throw new IllegalArgumentException(file + " does not exist");
}
}
System.err.println("Analyzing "+directoryToAnalyze);
MethodCallCollector analyzer = new MethodCallCollector();
analyzer.analyze(directoryToAnalyze);
}
public void analyze(String toBeAnalyzed) throws Exception{
appOut = new BufferedWriter(new FileWriter(datasetFileName));
PackManager.v().getPack("jtp").add(
new Transform("jtp.myTransform", new BodyTransformer() {
protected void internalTransform(Body body, String phase, Map options) {
//System.out.println("IN METHOD: " + body.getMethod().getName());
Chain<Local> locals = body.getLocals();
HashMap<String, Variable> variables = new HashMap<String, Variable>();
// first getting Locals
for(Local l:locals) { // for each variable of the method
Variable aVariable = new Variable();
aVariable.location="!";
//System.out.println(l);
aVariable.type = l.getType().toString();
variables.put(l.getName(),aVariable);
}
PatchingChain<Unit> units = body.getUnits();
for (Unit u: units) { // for each statement
Stmt s = (Stmt)u;
//System.out.println(s+"-"+s.getClass());
if (s.containsInvokeExpr()) {
InvokeExpr invokeExpr = s.getInvokeExpr();
//System.out.println(invokeExpr);
if (invokeExpr instanceof InstanceInvokeExpr) {
InstanceInvokeExpr instanceInvokeExpr = (InstanceInvokeExpr) invokeExpr;
Value v = instanceInvokeExpr.getBase();
Variable aVariable = variables.get(((Local)v).getName());
if (aVariable.location.equals("!")) {
SourceLnPosTag tag = (SourceLnPosTag) s.getTag("SourceLnPosTag");
if(tag!=null) {
aVariable.location = body.getMethod().getDeclaringClass()+":"+tag.startLn();
} else{
aVariable.location = "Unknown";
}
}
// we can add this method call
aVariable.methodCalls.add(instanceInvokeExpr.getMethod().getName());
}
}
}
// simple resolving
for (Unit u: units) { // for each statement
//System.out.println(u+" /// "+u.getClass());
Stmt s = (Stmt)u;
if (s instanceof AssignStmt) {
AssignStmt ass = (AssignStmt)s;
//System.out.println(ass);
Value left = ass.getLeftOp();
Value right = ass.getRightOp();
if (left instanceof Local && right instanceof Local) {
Variable vleft = variables.get(((Local)left).getName());
Variable vright = variables.get(((Local)right).getName());
for (String mc: vright.methodCalls) {
vleft.methodCalls.add(mc);
}
}
}
}
// output the variables
for (Variable aVariable: variables.values()) {
if ((aVariable.methodCalls.size()>1) ) {
System.out.println((nbTraces++)+" "+aVariable);
try {
appOut.write(aVariable+"\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
} // end internalTransform
} /* end new Transform */));
String sootClasspath = ".";
for (String jar : lSootClasspath) { sootClasspath+=classpathDelimiter+jar;}
String[] myArgs =
{
"-cp", sootClasspath,
"-process-dir", toBeAnalyzed,
"-include","org.apache." /* by default, org.apache. is discarded by soot */
};
Options.v().parse( myArgs);
Options.v().set_keep_line_number(true);
Options.v().set_output_format(Options.output_format_none);
System.err.println(Arrays.toString(myArgs));
Scene.v().loadNecessaryClasses();
PackManager.v().runPacks();
PackManager.v().writeOutput();
appOut.close();
}
/** The number of collected traces */
int nbTraces;
static String classpathDelimiter =":";
Writer appOut ;
}
/** used to store traces */
class Variable {
String location = "!";
String type = "!";
Local l;
List<String> methodCalls = new ArrayList<String>();
public String toString() {
String callString = "";
for (Iterator<String> iterator = methodCalls.iterator(); iterator.hasNext();) {
String call = iterator.next();
callString += call;
if(iterator.hasNext()) {
callString += " ";
}
}
//return location + " " + type + " " + callString;
return type + " " + callString;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment