package jqian.slicer.core; import java.util.*; import java.io.*; import org.eclipse.swt.widgets.*; import org.eclipse.jdt.core.*; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.core.resources.*; import org.eclipse.ui.*; import org.eclipse.ui.texteditor.ITextEditor; import soot.*; import soot.jimple.*; import soot.toolkits.graph.BriefUnitGraph; import soot.toolkits.graph.DirectedGraph; import jqian.Global; import jqian.util.*; import jqian.util.dot.DotViewer; import jqian.util.dot.GrappaGraph; import jqian.util.eclipse.ConsoleUtil; import jqian.util.eclipse.JDTUtils; import jqian.util.graph.Graph; import jqian.util.jgraphx.GraphViewer; import jqian.slicer.view.global.*; import jqian.slicer.util.*; import jqian.slicer.plugin.view.*; import jqian.slicer.plugin.*; import jqian.sootex.dependency.pdg.ActualNode; import jqian.sootex.dependency.pdg.DependenceEdge; import jqian.sootex.dependency.pdg.DependenceGraphHelper; import jqian.sootex.dependency.pdg.DependenceNode; import jqian.sootex.dependency.pdg.EntryNode; import jqian.sootex.dependency.pdg.JavaStmtNode; import jqian.sootex.dependency.pdg.JimpleStmtNode; import jqian.sootex.dependency.pdg.PDG; import jqian.sootex.dependency.pdg.SDG; import jqian.sootex.dependency.slicing.GlobalSlicer; import jqian.sootex.dependency.slicing.JavaSlicingCriterion; import jqian.sootex.dependency.slicing.JimpleSlicingCriterion; import jqian.sootex.dependency.slicing.LocalSlicer; import jqian.sootex.ptsto.IPtsToQuery; import jqian.sootex.util.CFGViewer; import jqian.sootex.util.SootUtils; import jqian.sootex.util.callgraph.CallGraphDotter; /** * Core slicer engine * */ public class SlithiceSlicer { private static SlithiceSlicer _instance; public static SlithiceSlicer v(){ if(_instance==null) _instance = new SlithiceSlicer(); return _instance; } private String _entrySignature; private IJavaProject _project; private SlicerOptions _options; private volatile SDG _sdg; private IPtsToQuery _ptsto; private volatile boolean _inSlicing; private volatile ISliceDistribution _currentDistribution; private SlithiceSlicer(){ loadConfig(); } public boolean inCurrentProject(IFile file){ return true; } public boolean isConfigurated(){ return true; } public SlicerOptions getConfiguration(){ return _options; } public boolean useDependenceNavigator(){ return _options.useDepNavigator; } public ISliceDistribution getCurrentSliceDistribution(){ return _currentDistribution; } public void reconfig(SlicerOptions opt){ //update configuration this._options = new SlicerOptions(opt); //dump the new configuration to file String path = getConfigFilePath(); Map<String,String> optMap = opt.toOptionMap(); String content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Config>\n\n"; for(Map.Entry<String,String> entry : optMap.entrySet()){ String key = entry.getKey(); content += "<"+key+">"+entry.getValue()+"</"+key+">\n"; } content += "\n</Config>\n"; try{ File file = new File(path); if(!file.exists()){ File parent = file.getParentFile(); if(!parent.exists()) parent.mkdir(); file.createNewFile(); } PrintStream out = new PrintStream(new FileOutputStream(path)); out.print(content); out.close(); } catch(IOException e){ e.printStackTrace(); } } private String getConfigFilePath(){ String path = PathUtil.getPyxisConfigurationPath(); path += File.separator + "config.xml"; return path; } private String getDefaultDotPath(){ String prjroot = PathUtil.getPluginPath(); return prjroot + File.separator + "lib" + File.separator +"dot"+ File.separator + "dot.exe"; } private final void loadConfig(){ try{ String path = getConfigFilePath(); Properties props = null; File file = new File(path); if(file.exists()){ ConsoleUtil.println(ID.CONSOLE, "Load configuration from: "+path); Configurator conf = new Configurator(); props = conf.parse(path); if(props!=null){ Map<String, String> optMap = new HashMap<String,String>(); for(Iterator<?> it = props.entrySet().iterator();it.hasNext();){ Map.Entry<?,?> entry = (Map.Entry<?,?>)it.next(); String key = (String)entry.getKey(); String val = (String)entry.getValue(); optMap.put(key, val); } _options = new SlicerOptions(optMap); //append default configuration if(_options.dotpath==null || _options.dotpath.equals("")){ _options.dotpath = getDefaultDotPath(); } } } if(props==null){ _options = defaultConfig(); } } catch(Exception e){ ConsoleUtil.printError(ID.CONSOLE, "Load config: " + e.getMessage()); _options = defaultConfig(); } //InputStream stream = Path.getFileByName(getClass(),_options.getFileOfSpecials()); //BufferedReader in = new BufferedReader(new InputStreamReader(stream)); } public SlicerOptions defaultConfig(){ SlicerOptions options = new SlicerOptions(); options.verbose = true; options.simplifyCallGraph = true; options.ignoreJreClinits = true; //options.showSliceInSDG = true; options.useDepNavigator = true; options.dotpath = getDefaultDotPath(); return options; } public void setProject(IJavaProject project,String entry){ if(!project.equals(this._project) || !entry.equals(this._entrySignature)){ ConsoleUtil.print(ID.CONSOLE,"\n======================== Project Reset ============================"); ConsoleUtil.print(ID.CONSOLE,"\nProject Entry: " + entry); this._project = project; this._entrySignature = entry; //clear original SDG this._sdg = null; //construct SDG in a new thread //TODO how to handle hot code modification String classpath = PathUtil.getClassPath(_project); String temppath = getCurPrjTemporalPath(); SDGConstructor constructor = new SDGConstructor(this,_entrySignature,null,classpath,temppath,_options); Thread t= new Thread(constructor); t.start(); } } public void doSlicing(Shell shell,String startMethod,int line,Collection<String> vars, boolean postExecution,boolean sliceInGlobal){ if(_sdg==null){ MessageDialog.openInformation(shell,ID.DISPLAY_NAME, "No dependence graph avaliable" +"Right click on a project, and select a project entry from menu \"Program" +" Slicing\" -> \"Set project entry\" to force dependence graph construction"); return; } if(_inSlicing){ MessageDialog.openInformation(shell,ID.DISPLAY_NAME, "A slicing operation is already" +"in process, please wait for its finishing and then try again."); return; } //do slicing in a new thread JavaSlicingCriterion javaCriterion = new JavaSlicingCriterion(vars,startMethod,line,postExecution); Collection<JimpleSlicingCriterion> criteria = javaCriterion.toJimpleCriterion(_ptsto, _options.heapAbstraction); if(criteria==null || criteria.isEmpty()){ MessageDialog.openInformation(shell,"Invalid Slicing Criterion", "Make sure the specified line, the specified variables and the specified method exists. \n" +"Also make sure the specified point is reachable from your project entry.\n" +"\n(Compiler optimization may also cause problems for the tool. Please \n" +" carefully check whether the line number and variable name information\n" +" is kept in the classfile, and check whether the code could be eliminated\n" +" during optimization.)"); return; } SlicingThread slicing = new SlicingThread(criteria,sliceInGlobal); Thread t= new Thread(slicing); t.start(); } class SlicingThread implements Runnable{ Collection<JimpleSlicingCriterion> _criteria; boolean _sliceInGlobal; public SlicingThread(Collection<JimpleSlicingCriterion> criteria,boolean sliceInGlobal){ this._criteria = criteria; this._sliceInGlobal = sliceInGlobal; } public void run(){ _inSlicing = true; Collection<DependenceNode> result = null; if(_sliceInGlobal){ GlobalSlicer slicer = new GlobalSlicer(_sdg); result = slicer.slice(_criteria); } else{ JimpleSlicingCriterion c = _criteria.iterator().next(); MethodOrMethodContext mc = c.context(); PDG pdg = _sdg.getPDG(mc); if(pdg==null){ ErrorPrinter.printError("No dependence graph found for: "+mc); return; } LocalSlicer slicer = new LocalSlicer(pdg); result = slicer.slice(_criteria); } _currentDistribution = new SliceDistribution(_project.getProject(),_criteria,result); class ShowView implements Runnable{ ISliceDistribution _distribution; public ShowView(ISliceDistribution distribution){ this._distribution = distribution; } public void run(){ PluginSliceViewer view = (PluginSliceViewer)WorkbenchHelper.openView(ID.GLOBAL_VIEW_ID); view.setProject(_project.getProject(),_distribution); //highlight current editor PluginSliceViewer.showSliceInCurrentEditor(_distribution); } } //show global slice view IWorkbench workbench = PlatformUI.getWorkbench(); workbench.getDisplay().syncExec(new ShowView(_currentDistribution)); boolean showSlices = _options.showSliceInSDG; if(showSlices){ GrappaGraph sliceGraph = DependenceGraphHelper.toGrappaGraph(_sdg, result); String filename = getCurPrjTemporalPath() + "/slice.dot"; sliceGraph.saveToDot(filename); ConsoleUtil.print(ID.CONSOLE,"\nDoting Slice file: "+filename+" ... ..."); DotViewer dotView = new DotViewer(_options.dotpath,filename, "jpg"); dotView.dotIt(); ConsoleUtil.print(ID.CONSOLE,"OK\n"); //dotView.view(); ImageView.showImage(filename+".jpg"); } ConsoleUtil.println(ID.CONSOLE, "\nSlicing result:"); ConsoleUtil.println(ID.CONSOLE, CollectionUtils.toString(result.iterator(),"\n")); _inSlicing = false; } } public String getCurPrjTemporalPath(){ String path = PathUtil.getProjectPath(_project); if(path==null){ path = "/slice_temp"; } else path += "/slice_temp"; File file = new File(path); if(!file.exists()) file.mkdir(); return path; } public String getEntryClass(){ return _entrySignature; } public void reset(){ if(_project!=null){ //clear data in temp directory String tmppath = getCurPrjTemporalPath(); File file = new File(tmppath); File[] contents = file.listFiles(); for(int i=0;i<contents.length;i++){ contents[i].delete(); } } Global.v().reset(); SootUtils.resetSoot(); _entrySignature = null; _project = null; _sdg = null; _currentDistribution = null; _inSlicing = false; } static class PDGLabelFetcher extends DependenceGraphHelper.LabelProvider{ ITextEditor _editor; public PDGLabelFetcher(ITextEditor editor){ this._editor = editor; } public String getLabel(DependenceNode node){ if(node instanceof JavaStmtNode){ int line = ((JavaStmtNode)node).getLine(); String text = JDTUtils.getLineText(_editor, line-1,true,false); if(text.length()>40){ text = text.substring(0,40)+"..."; } return text; } else{ return super.getLabel(node); } } } public boolean dotJavaPDG(final ITextEditor editor,String methodSignature,String dotfile){ if(_sdg==null) return false; try{ SootMethod m = Scene.v().getMethod(methodSignature); final PDG pdg = _sdg.getPDG(m); if(pdg==null){ ErrorPrinter.printError("No dependence graph found for method: "+methodSignature); return false; } final String dotpath = _options.dotpath; String tmppath = getCurPrjTemporalPath(); if(tmppath==null) tmppath = ""; final String filepath = tmppath + '/' +dotfile; class DotThread extends Thread{ public void run(){ try{ PDGLabelFetcher fetcher = new PDGLabelFetcher(editor); PDG jpdg = pdg.toJavaStmtDepGraph(); DependenceGraphHelper.toGrappaGraph(jpdg, Collections.EMPTY_LIST, fetcher).saveToDot(filepath); ConsoleUtil.print(ID.CONSOLE,"\nDoting Java level PDG file: "+filepath+" ... ..."); DotViewer dotView = new DotViewer(dotpath,filepath, "jpg"); dotView.dotIt(); ConsoleUtil.print(ID.CONSOLE,"OK\n"); ImageView.showImage(filepath+".jpg"); } catch(Exception e){ ConsoleUtil.printError(ID.CONSOLE, e.getMessage()); } } } DotThread thread = new DotThread(); thread.start(); return true; } catch(Exception e){ ConsoleUtil.printError(ID.CONSOLE,e.getMessage()); } return false; } public boolean dotJimplePDG(final ITextEditor editor,String methodSignature,String dotfile){ if(_sdg==null) return false; try{ SootMethod m = Scene.v().getMethod(methodSignature); final PDG pdg = _sdg.getPDG(m); if(pdg==null){ ErrorPrinter.printError("No dependence graph found for method: "+methodSignature); return false; } final String dotpath = _options.dotpath; String tmppath = getCurPrjTemporalPath(); if(tmppath==null) tmppath = ""; final String filepath = tmppath + '/' +dotfile; class DotThread extends Thread{ public void run(){ try{ PDGLabelFetcher fetcher = new PDGLabelFetcher(editor); DependenceGraphHelper.toGrappaGraph(pdg, Collections.EMPTY_LIST, fetcher).saveToDot(filepath); ConsoleUtil.print(ID.CONSOLE,"\nDoting Java level PDG file: "+filepath+" ... ..."); DotViewer dotView = new DotViewer(dotpath,filepath, "jpg"); dotView.dotIt(); ConsoleUtil.print(ID.CONSOLE,"OK\n"); ImageView.showImage(filepath+".jpg"); } catch(Exception e){ ConsoleUtil.printError(ID.CONSOLE, e.getMessage()); } } } DotThread thread = new DotThread(); thread.start(); return true; } catch(Exception e){ ConsoleUtil.printError(ID.CONSOLE,e.getMessage()); } return false; } public boolean showJimpleCFG(String methodSignature){ try{ SootMethod m = Scene.v().getMethod(methodSignature); Body body = m.getActiveBody(); DirectedGraph<Unit> ucfg = new BriefUnitGraph(body); CFGViewer viewer = new CFGViewer(m, ucfg); Graph cfg = viewer.makeJimpleCFG(); cfg.setTitle("jimple_" + m.getName()); GraphViewer frame = new GraphViewer(cfg); frame.setSize(1000, 700); frame.setVisible(true); return true; } catch(Exception e){ ConsoleUtil.printError(ID.CONSOLE,e.getMessage()); } return false; } public boolean dotCallGraph(final String method,final int depth,final String filepath){ class DotThread extends Thread{ public void run(){ try{ SootMethod entry = Scene.v().getMethod(method); ConsoleUtil.print(ID.CONSOLE,"\nDoting Call Graph file: "+filepath+" ... ..."); CallGraphDotter.dot(Scene.v().getCallGraph(), entry, depth, _options.dotpath,filepath); ConsoleUtil.print(ID.CONSOLE,"OK\n"); ImageView.showImage(filepath+".jpg"); } catch(Exception e){ ErrorPrinter.printError(e); } } } DotThread thread = new DotThread(); thread.start(); return true; } public boolean dotSDG(){ class DotThread extends Thread{ public void run(){ try{ String tmppath = getCurPrjTemporalPath(); if(tmppath==null) tmppath = ""; String filepath = tmppath + "/sdg.dot"; SDG sdg = getSDG(); ConsoleUtil.print(ID.CONSOLE,"\nDoting SDG (can be very slow): "+filepath+" ... ..."); DependenceGraphHelper.dotDependenceGraph(sdg,_options.dotpath, filepath, false); ConsoleUtil.print(ID.CONSOLE,"OK\n"); ImageView.showImage(filepath+".jpg"); } catch(Exception e){ ErrorPrinter.printError(e); } } } DotThread thread = new DotThread(); thread.start(); return true; } public boolean showSDG(){ class DotThread extends Thread{ public void run(){ try{ String tmppath = getCurPrjTemporalPath(); if(tmppath==null) tmppath = ""; String filepath = tmppath + "/sdg.dot"; File f = new File(filepath); if(f.exists()){ ImageView.showImage(filepath+".jpg"); } else{ ErrorPrinter.printError("Can not find SDG image in: " + filepath); } } catch(Exception e){ ErrorPrinter.printError(e); } } } DotThread thread = new DotThread(); thread.start(); return true; } public SDG getSDG(){ return _sdg; } void setSDG(SDG sdg){ this._sdg = sdg; } public IPtsToQuery getPtsToQuery() { return _ptsto; } public void setPtsToQuery(IPtsToQuery ptsto) { this._ptsto = ptsto; } public Collection<Integer> getDependedLines(String method,int methodStartLine,int line){ Set<Integer> lines = new HashSet<Integer>(); if(_sdg==null) return lines; SootMethod m; try{ m = Scene.v().getMethod(method); if(m==null) return lines; }catch(Exception e){ return lines; } Collection<Unit> units = new LinkedList<Unit>(); JavaSlicingCriterion.unitsInLine(m, line, units); PDG pdg = _sdg.getPDG(m); for(Unit u: units){ boolean hasCall = false; if(u instanceof Stmt){ hasCall = ((Stmt)u).containsInvokeExpr(); } DependenceNode node = pdg.getStmtBindingNode(u); if(node==null) continue; collectDependedLine(methodStartLine,node,lines); //collect depended lines from the actual ins. if(hasCall){ LinkedList<ActualNode> actuals = new LinkedList<ActualNode>(); pdg.getActualIns(actuals, u); for(ActualNode an: actuals){ collectDependedLine(methodStartLine,an,lines); } } } return lines; } private void collectDependedLine(int methodStartLine,DependenceNode node,Collection<Integer> lines){ Collection<?> edges = _sdg.edgesInto(node); for(Iterator<?> eIt=edges.iterator();eIt.hasNext();){ DependenceEdge e = (DependenceEdge)eIt.next(); DependenceNode depended = e.getFrom(); if(depended instanceof JimpleStmtNode){ Unit d = (Unit)depended.getBinding(); lines.add(SootUtils.getLine(d)); } else if(depended instanceof ActualNode){ ActualNode actual = (ActualNode)depended; Unit d = actual.getCallSite(); lines.add(SootUtils.getLine(d)); } else if(depended instanceof EntryNode){ lines.add(methodStartLine+1); } } } }