package jqian.sootex.dependency.pdg; import java.util.*; import jqian.Global; import jqian.sootex.location.InvokeInfo; import jqian.sootex.location.Location; import jqian.sootex.location.MethodRet; import jqian.sootex.util.SootUtils; import jqian.sootex.util.callgraph.CallGraphHelper; import jqian.sootex.util.callgraph.CallGraphWorklist; import jqian.sootex.util.callgraph.Callees; import jqian.sootex.util.graph.PathTable; import soot.*; import soot.jimple.Stmt; import soot.jimple.toolkits.callgraph.*; import soot.toolkits.graph.DirectedGraph; import soot.toolkits.graph.PseudoTopologicalOrderer; /** * SDG connected by PDGs * Summary edges are also supported */ public class SDG implements DependenceGraph{ private Map<DependenceNode,Collection<DependenceEdge>> _node2edgesIn = new HashMap<DependenceNode,Collection<DependenceEdge>>(); private Map<SootMethod,Collection<SummaryEdge>> _mc2summaries = new HashMap<SootMethod,Collection<SummaryEdge>>(); private Map<MethodOrMethodContext,PDG> _mc2pdg = new HashMap<MethodOrMethodContext,PDG>(); /** Get a procedure's dependence graph */ public PDG getPDG(MethodOrMethodContext mc){ return (PDG)_mc2pdg.get(mc); } public Collection<PDG> getPDGs(){ return _mc2pdg.values(); } public boolean addPDG(MethodOrMethodContext mc,PDG pdg){ if(_mc2pdg.get(mc)!=null) return false; _mc2pdg.put(mc,pdg); return true; } public int countEdge(int type){ return 0; } public Collection<DependenceEdge> edgesInto(DependenceNode n){ MethodOrMethodContext mc = n.getMethodOrMethodContext(); PDG pdg = getPDG(mc); if(n instanceof FormalIn || n instanceof ActualOut || n instanceof EntryNode){ Collection<DependenceEdge> extras = _node2edgesIn.get(n); if(extras!=null){ Collection<DependenceEdge> edges = new LinkedList<DependenceEdge>(); edges.addAll(pdg.edgesInto(n)); edges.addAll(extras); return edges; } } return pdg.edgesInto(n); } //support the traversing public Collection<DependenceNode> getNodes(){ throw new RuntimeException("Not yet implemented"); } public int getEdgeCount(){ throw new RuntimeException("Not yet implemented"); } public Collection<DependenceEdge> edgesOutOf(DependenceNode node){ throw new RuntimeException("Not yet implemented"); } /**Get a graph compatible with the DirectedGraph interface*/ public DirectedGraph<DependenceNode> toDirectedGraph(){ throw new RuntimeException("Not yet implemented"); } /** building connection between actuals and formals. */ public void connectPDGs(){ Global.v().out.print("\n[SDG connect methods]"); //connect actuals and formals Collection<ActualNode> actuals = new LinkedList<ActualNode>(); for(PDG pdg: _mc2pdg.values()){ pdg.getActualNodes(actuals); } for(ActualNode actual: actuals){ FormalNode formal = getFormal(actual); if(formal==null) continue; DependenceEdge edge; if(actual instanceof ActualIn){ Object binding = formal.getBinding(); edge = new DataDependenceEdge(actual,formal,binding); addExtraEdge(formal,edge); } else{ Object binding = actual.getBinding(); edge = new DataDependenceEdge(formal,actual,binding); addExtraEdge(actual,edge); } } //connect entry nodes and call nodes for(PDG pdg: _mc2pdg.values()){ for(DependenceNode n: pdg.getNodes()){ if(n instanceof CallNode){ CallNode call = (CallNode)n; SootMethod callee = (SootMethod)call.getBinding(); if(callee.isConcrete()){ PDG calleePdg = getPDG(callee); DependenceNode entry = calleePdg.entry(); DependenceEdge edge = new CtrlDependenceEdge(call,entry); addExtraEdge(entry,edge); } } } } Global.v().out.print(" done."); } /** * Build summary edges for all methods reachable from the entry method. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public void buildSummaryEdges(SootMethod entryMethod){ Global.v().out.print("\n[SDG summary]"); CallGraph cg = Scene.v().getCallGraph(); Collection<SootMethod> entries = new LinkedList<SootMethod>(); entries.add(entryMethod); DirectedGraph graph = SootUtils.getSCCGraph(cg, entries); PseudoTopologicalOrderer pto = new PseudoTopologicalOrderer(); List order = pto.newList(graph, true); int total = 0; for(Iterator it=order.iterator();it.hasNext();){ Collection node = (Collection) it.next(); total += node.size(); } // bottom-up phase int methodNum = 0; int lastPercent = 0; for(Iterator it=order.iterator();it.hasNext();){ Collection node = (Collection) it.next(); if(node.size()==1){ SootMethod m = (SootMethod)node.iterator().next(); findSummary(m); } else{ // need iterative computation inside each strongly-connected-component Set component = new HashSet(node); CallGraph subCg = CallGraphHelper.getSubCallGraph(cg, component); CallGraphWorklist worklist = new CallGraphWorklist(subCg, new MyVisitor()); worklist.process(); } // compact summary edge representation for(Object o: node){ SootMethod m = (SootMethod)o; compactSummaries(m); PDG pdg = (PDG)_mc2pdg.get(m); if(pdg!=null){ pdg.finalizeEdgeSets(); } } methodNum += node.size(); int newPercent = (100*methodNum)/total; if(newPercent-lastPercent>5){ Global.v().out.print(" " + newPercent + "%"); lastPercent = newPercent; } } Global.v().out.print(" <done>\n"); } /** Compact SDG representation. */ public void compact(){ _mc2summaries = null; } private class MyVisitor implements jqian.sootex.util.callgraph.CallGraphWorklist.MethodVisitor{ public boolean visit(SootMethod m,Set<SootMethod> changeSources){ Collection<SummaryEdge> summary = getSummaries(m); int oldSummaries = summary.size(); findSummary(m); int newSummaries = summary.size(); return (newSummaries!=oldSummaries); } } /** * Summarize the dependence between FormalIn(s) and FormalOut(s) */ @SuppressWarnings("unused") private void findSummary(SootMethod m){ //FIXME: Here do not build summary for native methods if(!m.isConcrete()){ return; } PDG pdg = (PDG)_mc2pdg.get(m); // No PDG for the method (We may not construct PDG for every method, especially for the library methods) if(pdg==null){ return; } if(pdg.containsMethodBody()){ Body body = m.getActiveBody(); //add summary for callees CallGraph cg = Scene.v().getCallGraph(); for(Iterator<?> it=body.getUnits().iterator();it.hasNext();){ Stmt s = (Stmt)it.next(); if(!s.containsInvokeExpr()){ continue; } // no binding node, the statement may be ignored in the CFG if(pdg.getStmtBindingNode(s)==null){ continue; } Callees callees = new Callees(cg,s); for(SootMethod tgt: callees.explicits()){ addSummaryForCall(pdg,s,tgt); } } } else{ pdg.hashCode(); } Collection<SummaryEdge> summaries = getSummaries(m); //using PathTable to find the reachablity relations in the PDG //The same objective also can be implemented by LocalSlicer if(false){ findSummaryEdgeByPathTable(pdg,summaries); } else{ findSummaryEdgeByGraphTraverse(pdg,summaries); } } /**XXX The path table based approach seems to time-consuming. A PDG may have 10000 nodes. */ protected void findSummaryEdgeByPathTable(PDG pdg,Collection<SummaryEdge> summaries){ Collection<FormalNode> fIns = pdg.getFormalIns(); Collection<FormalNode> fOuts = pdg.getFormalOuts(); PathTable tbl = new PathTable(pdg.toDirectedGraph()); for(FormalNode in: fIns){ for(FormalNode out: fOuts){ if(tbl.hasPath(in, out)){ Object binding = out.getBinding(); SummaryEdge edge = new SummaryEdge(in,out,binding); summaries.add(edge); } } } } protected void findSummaryEdgeByGraphTraverse(PDG pdg,Collection<SummaryEdge> summaries){ Collection<FormalNode> fOuts = pdg.getFormalOuts(); for(FormalNode out: fOuts){ Set<DependenceNode> fIns = getReachableFormals(pdg, out); for(DependenceNode in: fIns){ Object binding = out.getBinding(); SummaryEdge edge = new SummaryEdge(in,out,binding); summaries.add(edge); } } } // FIXME: Here to use BitVector to speed up analysis private Set<DependenceNode> getReachableFormals(PDG pdg, DependenceNode dest) { Set<DependenceNode> reach = new HashSet<DependenceNode>(); Set<DependenceNode> formals = new HashSet<DependenceNode>(); Stack<DependenceNode> stack = new Stack<DependenceNode>(); DependenceNode entry = pdg.entry(); Collection<DependenceEdge> edges = pdg.edgesInto(dest); if (edges != null){ for (DependenceEdge e: edges) { stack.add(e.getFrom()); } } while (!stack.isEmpty()) { DependenceNode top = stack.pop(); if (!reach.add(top)) { continue; } if(top instanceof FormalNode){ formals.add(top); continue; } if(top==entry){ continue; } edges = pdg.edgesInto(top); if (edges == null) continue; for (DependenceEdge e: edges) { DependenceNode from = e.getFrom(); if (!reach.contains(from)) { stack.add(from); } } } return formals; } private void addSummaryForCall(PDG pdg,Unit callsite,SootMethod callee){ InvokeInfo invoke = new InvokeInfo((Stmt)callsite); if(!callee.isConcrete()){ //just assume the return value depends on each formal-in Location ret = invoke.getRetLoc(); if(ret!=null){ ActualNode out = pdg.getBindingActual(callsite, callee, ret, false); Location[] args = invoke.getArgLocs(); int count = args.length; for(int i=0;i<count;i++){ Location arg = args[i]; ActualNode in = pdg.getBindingActual(callsite, callee, arg, true); SummaryEdge edge = new SummaryEdge(in,out,ret); pdg.addEdge(edge); } if(!callee.isStatic()){ Location thiz = invoke.receiver(); ActualNode in = pdg.getBindingActual(callsite, callee, thiz, true); SummaryEdge edge = new SummaryEdge(in,out,ret); pdg.addEdge(edge); } } // TODO: need to use native helper to find dependence between in parameter } else{ Collection<SummaryEdge> summaries = getSummaries(callee); Location[] args = invoke.getArgLocs(); for(SummaryEdge edge: summaries){ FormalIn from = (FormalIn)edge.getFrom(); DependenceNode to = edge.getTo(); Object inBinding = null; Object outBinding = null; { int paramIdx = from.getParamIndex(); if (paramIdx >= 0) { inBinding = args[paramIdx]; } else if (paramIdx == FormalIn.THIS_INDEX) { inBinding = invoke.receiver(); } else{ inBinding = from.getBinding(); } Object obj = to.getBinding(); if(obj instanceof MethodRet){ outBinding = invoke.getRetLoc(); //if method return is not used, then just continue; if(outBinding==null) continue; } else{ outBinding = obj; } } if(inBinding==null || outBinding==null){ throw new RuntimeException("Strange Error"); } ActualNode actualIn = pdg.getBindingActual(callsite, callee, inBinding, true); ActualNode actualOut = pdg.getBindingActual(callsite, callee, outBinding, false); edge = new SummaryEdge(actualIn,actualOut,outBinding); pdg.addEdge(edge); } } } /** Get the corresponding FormalNode of a ActualNode. * @return The result can be null when the callee is not concrete. */ public FormalNode getFormal(ActualNode actual){ Object binding = actual.getFormalBinding(); if(binding==null) return null; SootMethod callee = actual.getCallee(); if(!callee.isConcrete()){ return null; } PDG pdg = getPDG(callee); FormalNode formal; if(actual instanceof ActualIn){ formal = pdg.getBindingFormal(binding, true); } else{ formal = pdg.getBindingFormal(binding, false); } return formal; } private Collection<SummaryEdge> getSummaries(SootMethod m){ Collection<SummaryEdge> summaries = _mc2summaries.get(m); if(summaries==null){ summaries = new HashSet<SummaryEdge>(); _mc2summaries.put(m, summaries); } return summaries; } private void compactSummaries(SootMethod m){ Collection<SummaryEdge> summaries = _mc2summaries.get(m); if(summaries!=null){ summaries = new ArrayList<SummaryEdge>(summaries); _mc2summaries.put(m, summaries); } } /*public Collection getBindingActuals(FormalNode fm){ Collection actuals = (Collection)_formal2actuals.get(fm); if(actuals==null){ actuals = new LinkedList(); _formal2actuals.put(fm, actuals); CallGraph cg = Scene.v().getCallGraph(); MethodOrMethodContext mc = fm.getMethodOrMethodContext(); for(Iterator it=cg.edgesInto(mc);it.hasNext();){ Edge edge = (Edge)it.next(); SootMethod m = edge.src(); PDG pdg = getPDG(m); boolean isIn = (fm instanceof FormalIn); ActualNode an = pdg.getBindingActual(edge.srcUnit(),(SootMethod)mc,(Location)fm.getBinding(),isIn); actuals.add(an); } } return actuals; }*/ private void addExtraEdge(DependenceNode node,DependenceEdge e){ Collection<DependenceEdge> edges = _node2edgesIn.get(node); if(edges==null){ edges = new ArrayList<DependenceEdge>(); _node2edgesIn.put(node, edges); } edges.add(e); } /**Change to the PDG with .java line as node*/ public DependenceGraph toJavaStmtDepGraph(){ SDG sdg = new SDG(); Map<DependenceNode,DependenceNode> old2New = new HashMap<DependenceNode,DependenceNode>(); for(Map.Entry<MethodOrMethodContext, PDG> entry: _mc2pdg.entrySet()){ MethodOrMethodContext mc = entry.getKey(); PDG pdg = entry.getValue(); PDG javaPDG = pdg.toJavaStmtDepGraph(old2New); sdg.addPDG(mc, javaPDG); } //clone entra edges for(Map.Entry<DependenceNode,Collection<DependenceEdge>> entry:_node2edgesIn.entrySet()){ DependenceNode node = entry.getKey(); Collection<DependenceEdge> edges = entry.getValue(); DependenceNode newTgt = (DependenceNode)old2New.get(node); Collection<DependenceEdge> newEdges= new LinkedList<DependenceEdge>(); for(DependenceEdge e: edges){ DependenceNode newFrom = (DependenceNode)old2New.get(e.getFrom()); if(e instanceof CtrlDependenceEdge){ e = new CtrlDependenceEdge(newFrom, newTgt); } else if(e instanceof DataDependenceEdge){ DataDependenceEdge dd = (DataDependenceEdge)e; e = new DataDependenceEdge(newFrom, newTgt,dd.getReason()); } newEdges.add(e); } addInEdges(sdg, newTgt, newEdges); } return sdg; } private static void addInEdges(SDG sdg, DependenceNode node, Collection<DependenceEdge> inEdges){ Collection<DependenceEdge> edges = sdg._node2edgesIn.get(node); if(edges==null){ edges = new LinkedList<DependenceEdge>(); sdg._node2edgesIn.put(node,edges); } edges.addAll(inEdges); } Set<Map.Entry<DependenceNode,Collection<DependenceEdge>>> getExtraEdges(){ return _node2edgesIn.entrySet(); } }