package jqian.sootex.util.callgraph; import soot.*; import soot.jimple.toolkits.callgraph.*; import soot.toolkits.graph.*; import soot.util.queue.*; import java.util.*; /** */ @SuppressWarnings({ "unchecked", "rawtypes" }) public class CallGraphHelper{ public static ReachableMethods getReachableMethod(CallGraph cg, Collection entries){ ReachableMethods rm; Scene scene = Scene.v(); if(cg==scene.getCallGraph() && entries.equals(scene.getEntryPoints())){ rm = scene.getReachableMethods(); } else{ rm = new ReachableMethods(cg,entries); rm.update(); } return rm; } static class CGNodeFilter extends CallGraphNodeFilter{ ReachableMethods _rm; public CGNodeFilter(ReachableMethods rm){ this._rm = rm; } public boolean isIngored(MethodOrMethodContext m){ return !_rm.contains(m); } } public static CallGraphNodeFilter getCallGraphNodeFilter(ReachableMethods rm){ CGNodeFilter nodeFilter = new CGNodeFilter(rm); return nodeFilter; } public DirectedGraph<MethodOrMethodContext> toDirectedGraph(CallGraph cg,Collection<MethodOrMethodContext> entries){ ReachableMethods rm = getReachableMethod(cg, entries); CGNodeFilter nodeFilter = new CGNodeFilter(rm); DirectedCallGraph dcg = new DirectedCallGraph(cg, entries, nodeFilter, new CallGraphEdgeFilter()); return dcg; } private static class SubCallGraph extends CallGraph { Set<MethodOrMethodContext> _nodes; CallGraph _cg; CallGraphEdgeFilter _filter; public SubCallGraph(CallGraph cg, Set<MethodOrMethodContext> nodes, CallGraphEdgeFilter filter) { _nodes = nodes; _cg = cg; this._filter = filter; } public Iterator<Edge> edgesInto(MethodOrMethodContext m) { List<Edge> edgesIn = new LinkedList<Edge>(); for (Iterator<Edge> edges = _cg.edgesInto(m); edges.hasNext();) { Edge e = edges.next(); if (_nodes.contains(e.src()) && !_filter.isIgnored(e)) { edgesIn.add(e); } } return edgesIn.iterator(); } public Iterator<Edge> edgesOutOf(MethodOrMethodContext m) { List<Edge> edgesOut = new LinkedList<Edge>(); for (Iterator<Edge> edges = _cg.edgesOutOf(m); edges.hasNext();) { Edge e = edges.next(); if (_nodes.contains(e.tgt()) && !_filter.isIgnored(e)) { edgesOut.add(e); } } return edgesOut.iterator(); } public Iterator<Edge> edgesOutOf(Unit u) { throw new RuntimeException("Not implemented"); } public QueueReader<Edge> listener() { throw new RuntimeException("Not implemented"); } public boolean removeEdge(Edge e) { throw new RuntimeException("Not implemented"); } // Returns the number of edges in the call graph. public int size() { throw new RuntimeException("Not implemented"); } public Iterator<MethodOrMethodContext> sourceMethods() { return _nodes.iterator(); } public String toString() { throw new RuntimeException("Not implemented"); } } public static CallGraph getSubCallGraph(CallGraph cg,Set nodes,CallGraphEdgeFilter filter){ return new SubCallGraph(cg,nodes,filter); } public static CallGraph getSubCallGraph(CallGraph cg,Set nodes){ return new SubCallGraph(cg,nodes,new CallGraphEdgeFilter()); } public static boolean hasDirectRecurion(CallGraph cg,SootMethod m){ for(Iterator<Edge> it = cg.edgesOutOf(m);it.hasNext();){ Edge e = it.next(); SootMethod tgt = e.tgt(); if(tgt==m){ return true; } } return false; } /** Make sure the call information of method <code>m</code> occur in the call graph. */ public static void assureCallGraph(CallGraph cg, SootMethod m){ } private static boolean isLibMethod(SootMethod m){ SootClass cls = m.getDeclaringClass(); String className = cls.getName(); int size = JAVA_LIB_PACKAGES.length; for(int i=0;i<size;i++){ String pkg = JAVA_LIB_PACKAGES[i]; if(className.startsWith(pkg)){ return true; } } return false; } private static String[] JAVA_LIB_PACKAGES = { "sun.", "sunw.", "com.sun.", "java.", "javax." }; public static Collection<SootMethod> widthFirstTraverse(CallGraph cg, SootMethod entry, int depth){ Queue<SootMethod> queue = new LinkedList<SootMethod>(); queue.add(entry); Set<SootMethod> processed = new HashSet<SootMethod>(); for(int i=0; i<depth && !queue.isEmpty(); i++){ // process elements generated by last round int size = queue.size(); for(int k=0;k<size;k++){ SootMethod m = (SootMethod)queue.poll(); if(!processed.add(m)){ continue; } for(Iterator<Edge> it = cg.edgesOutOf(m);it.hasNext();){ Edge e = it.next(); SootMethod tgt = e.tgt(); if(!processed.contains(tgt)){ queue.offer(tgt); } } } } return processed; } /** * Tailor call graph. All methods called in a library method more than depth * <code>libDepth</code> will be tailored off. */ public static CallGraph tailorLibMethods(CallGraph cg, Collection<SootMethod> entries, int libDepth){ libDepth--; CallGraph newCg = new CallGraph(); Stack<SootMethod> stack = new Stack<SootMethod>(); Map<SootMethod,Integer> m2depth = new HashMap<SootMethod,Integer>(); Set<SootMethod> processed = new HashSet<SootMethod>(); stack.addAll(entries); while(!stack.isEmpty()){ SootMethod m = (SootMethod)stack.pop(); boolean firstTime = processed.add(m); Integer depth = m2depth.get(m); if(depth==null){ depth = 0; m2depth.put(m, 0); } depth = depth + 1; for(Iterator<Edge> it = cg.edgesOutOf(m);it.hasNext();){ Edge e = it.next(); SootMethod tgt = e.tgt(); if(firstTime){ newCg.addEdge(e); } if(isLibMethod(tgt)){ if(depth <= libDepth){ Integer lastDepth = m2depth.get(tgt); // non passed, or this time depth is smaller if(lastDepth==null || depth<lastDepth){ m2depth.put(tgt, depth); stack.push(tgt); } } } else{ if(!processed.contains(tgt)){ m2depth.put(tgt, 0); stack.push(tgt); } } } } return newCg; } }