package soot.spl.ifds; import heros.IFDSTabulationProblem; import heros.solver.IFDSSolver; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.BitSet; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import net.sf.javabdd.BDDFactory; import org.eclipse.jdt.core.IJavaProject; import soot.Local; import soot.Scene; import soot.SootMethod; import soot.Unit; import soot.options.Options; import soot.util.NumberedString; import br.ufal.cideei.handlers.AnalysisArgs; import br.ufal.cideei.soot.instrument.FeatureModelInstrumentorTransformer; public class PerformanceTestDriver { private static boolean checkResults = false; private static boolean debug = false; private static boolean crossChecksPassed = true; private static PrintWriter log; @SuppressWarnings({ "unchecked", "rawtypes" }) protected static void perfTest(FeatureModelInstrumentorTransformer bodyTransformer, IFDSTabulationProblem<Unit, ?, SootMethod,?> problem, IJavaProject javaProject, AnalysisArgs args) { long splDuration; Scene.v().setEntryPoints(Collections.singletonList(Scene.v().getMainMethod())); args.print(); checkResults = args.checkResults; debug = args.debug; if(checkResults) System.err.println("Cross checks are enabled."); initNodeFactory(); FeatureModelContext fmContext = new FeatureModelContext(bodyTransformer, javaProject); setupLog(fmContext, args); try { log(""); log(""); log(""); log("New run for project;"+javaProject.getElementName()); log("Analysis class;"+problem.getClass().getName()); log("Main class;"+Scene.v().getMainClass().getName()); log("Start time;"+new Date()); log("JDK is "+ (Options.v().no_bodies_for_excluded() ? "not " : "") +"included in analysis"); long durationSoot = System.currentTimeMillis()-Main.beforeSootStart; log("Soot startup and cg took;"+durationSoot); if(args.wait) { System.err.println("Waiting 10 seconds for eclipse to reach steady state..."); try { Thread.sleep(10000); } catch (InterruptedException e) { } } int reachableFeatures = bodyTransformer.getFeatureNumberer().size(); log("Features reachable in code;"+reachableFeatures); Set<NumberedString> featuresReachableInCode = new HashSet<NumberedString>(); for (NumberedString ns : bodyTransformer.getFeatureNumberer()) { featuresReachableInCode.add(ns); } Constraint<String> fullFeatureModelConstraint = args.useFeatureModel ? FMConstraintFactory.v().computeFeatureConstraint(fmContext) : Constraint.<String>trueValue(); fmContext.setFullFMConstraint(fullFeatureModelConstraint); Set<NumberedString> allFeatures = new HashSet<NumberedString>(); for (NumberedString ns : bodyTransformer.getFeatureNumberer()) { allFeatures.add(ns); } Constraint<String> featureModelConstraintForAnalysis = fullFeatureModelConstraint; if(args.simplify) { featureModelConstraintForAnalysis = fullFeatureModelConstraint.simplify(allFeatures, featuresReachableInCode); } fmContext.setSimplifiedFMConstraint(featureModelConstraintForAnalysis); System.err.println("Full feature-model constraint: "+fullFeatureModelConstraint); System.err.println("Simplified feature-model constraint: "+featureModelConstraintForAnalysis); log("Full feature-model node count;"+fullFeatureModelConstraint.size()); log("Simplified feature-model node count;"+featureModelConstraintForAnalysis.size()); if(featureModelConstraintForAnalysis.equals(Constraint.falseValue())) { System.err.println("Feature model not satisfiable. Exiting..."); return; } int totalFeatures = bodyTransformer.getFeatureNumberer().size(); log("Total features;"+totalFeatures); { System.err.println("Performing dry run to fully construct the ICFG upfront..."); SPLIFDSSolver<Local> splSolver = new SPLIFDSSolver(problem,fmContext,args.eagerPruning); splSolver.solve(); } SPLIFDSSolver<Local> splSolver = new SPLIFDSSolver(problem,fmContext,args.eagerPruning); System.err.println("Starting SPL solver..."); long before = System.currentTimeMillis(); splSolver.solve(); splDuration = System.currentTimeMillis()-before; if(debug) printResults(bodyTransformer, splSolver); log("SPL solving took;"+splDuration); log("SPL flow functions constructed;"+splSolver.flowFunctionConstructionCount); log("SPL duration of flow function construction (phase 1);"+splSolver.durationFlowFunctionConstruction); log("SPL flow functions applied;"+splSolver.flowFunctionApplicationCount); log("SPL duration of flow function application (phase 2);"+splSolver.durationFlowFunctionApplication); log("SPL propagation count;"+splSolver.propagationCount); if(!checkResults) splSolver = null; //free memory if(!args.onlyLifted) { long totalDuration = 0L; long totalDurationCrossChecks = 0L; crossChecksPassed = true; final Iterable<Set<NumberedString>> powerSet = Powerset.powerset(featuresReachableInCode); int totalCombinations = (int) Math.pow(2, featuresReachableInCode.size()); Iterator<BitSet> configIter; int numConfigs; if(args.determineValidConfigsUpfront) { int n = 0; List<BitSet> validConfigs = new LinkedList<BitSet>(); System.err.println("Testing "+totalCombinations+" combinations for validity w.r.t. feature model."); long beforeCombinationTesting = System.currentTimeMillis(); for(Set<NumberedString> subset: powerSet) { BitSet bs = new BitSet(); for (NumberedString feature: subset) { bs.set(feature.getNumber()); } if(args.useFeatureModel) { Constraint<String> currentConfig = Constraint.make(bs, featuresReachableInCode); if(!featureModelConstraintForAnalysis.and(currentConfig).equals(Constraint.falseValue())) { validConfigs.add(bs); } } else { validConfigs.add(bs); } n++; if(n % 10000==0) System.err.println("So far tested "+n+" out of "+totalCombinations+"."); } long durationCombinationTesting = System.currentTimeMillis()-beforeCombinationTesting; System.err.println("The feature model determined that "+validConfigs.size()+" configurations out of "+totalCombinations+" configurations for "+reachableFeatures+" reachable features are valid."); log("Checking for valid configs took;"+durationCombinationTesting); log("Number of valid configurations;"+validConfigs.size()); configIter = validConfigs.iterator(); numConfigs = validConfigs.size(); System.err.println("Will now run separate analyses for "+numConfigs+" configurations."); } else { configIter = new Iterator<BitSet>() { Iterator<Set<NumberedString>> inner = powerSet.iterator(); public boolean hasNext() { return inner.hasNext(); } public BitSet next() { Set<NumberedString> next = inner.next(); BitSet bs = new BitSet(); for (NumberedString feature: next) { bs.set(feature.getNumber()); } return bs; } public void remove() { throw new UnsupportedOperationException(); } }; numConfigs = totalCombinations; System.err.println("Will now run separate analyses for up to "+numConfigs+" configurations."); } int i=0; int numValid=0; long startTime = System.currentTimeMillis(); long totalflowFunctionConstructionCount = 0; while(configIter.hasNext()) { BitSet bs = configIter.next(); i++; boolean isValidConfig; if(args.determineValidConfigsUpfront) { isValidConfig = true; } else { Constraint<String> currentConfig = Constraint.make(bs, featuresReachableInCode); isValidConfig = !featureModelConstraintForAnalysis.and(currentConfig).equals(Constraint.falseValue()); } if(isValidConfig) { System.err.print("Configuration "+i+", bitset:"+bs+", "+((numConfigs-i)-1)+" more to go. "); numValid++; IFDSSolver<Unit, ?, SootMethod,?> solver = new SPLSingleConfigIFDSSolver(problem,bs); if(debug) System.err.println("Starting standard IFDS solver for configuration "+bs+"..."); before = System.currentTimeMillis(); solver.solve(); long duration = System.currentTimeMillis()-before; log("Standard solving for configuration "+i+" took;"+duration); log("Standard flow functions constructed;"+solver.flowFunctionConstructionCount); totalDuration += duration; totalflowFunctionConstructionCount += solver.flowFunctionConstructionCount; log("Standard flow functions applied;"+solver.flowFunctionApplicationCount); if(debug) printIFDSResults(bodyTransformer, solver); if(checkResults) { long beforeCC = System.currentTimeMillis(); checkResults(splSolver, solver, bs, featuresReachableInCode); long durationCC = System.currentTimeMillis()-beforeCC; totalDurationCrossChecks += durationCC; } long delta = System.currentTimeMillis()-startTime; double estimatedDuration = (delta / (double)i) * numConfigs; long eta = startTime + (long) estimatedDuration; System.err.println("ETA: "+new Date(eta)+" est. duration: "+(long) estimatedDuration); } } log("Number of valid configurations;"+numValid); log("TOTAL SEPARATE Solving took;"+totalDuration); log("TOTAL flow functions applied;"+totalflowFunctionConstructionCount); log("TOTAL SAVINGS;"+(totalDuration-splDuration)); if(checkResults) { log("Cross checks took;"+totalDurationCrossChecks); if(crossChecksPassed) log("All cross checks passed."); else log("Some cross checks FAILED!!!"); } } log("End time;"+new Date()); } finally { log.close(); } } private static void log(String line) { log.println(line); log.flush(); if(debug) System.err.println(line); } private static void setupLog(FeatureModelContext fmContext, AnalysisArgs args) { String type = args.analysisClassName; if(!args.useFeatureModel) type += "-nofm"; else if(args.simplify) type += "-simplified"; else type += "-full"; try { String logFilePath = fmContext.getJavaProject().getResource().getLocation() + File.separator + "splAnalysis-"+type+".log"; System.err.println("Writing log to: "+logFilePath); log = new PrintWriter(new FileOutputStream(new File(logFilePath),true)); } catch (FileNotFoundException e) { e.printStackTrace(); } } private static void initNodeFactory() { Constraint.FACTORY = BDDFactory.init(100000, 100000); Constraint.FACTORY.setVarNum(100); //some number high enough to accommodate the max. number of features; ideally we should compute this number } private static void checkResults(SPLIFDSSolver<Local> splSolver, IFDSSolver<Unit, ?, SootMethod, ?> solver, BitSet enabledFeatures, Set<NumberedString> featuresReachableInCode) { Unit ret = Scene.v().getMainMethod().getActiveBody().getUnits().getLast(); Map<?, Constraint<String>> splResults = splSolver.resultsAt(ret); Set<?> ifdsResults = solver.ifdsResultsAt(ret); Constraint<String> ifdsConstraint = Constraint.<String>make(enabledFeatures, featuresReachableInCode); for(Map.Entry<?, Constraint<String>> entry: splResults.entrySet()) { Object analysisResult = entry.getKey(); Constraint<String> splConstraint = entry.getValue(); if(!splConstraint.and(ifdsConstraint).equals(Constraint.falseValue())) { if(!ifdsResults.contains(analysisResult)) { System.err.println("!!! ERROR !!! SPL result too weak! IFDS="+ifdsResults+", SPL/IDE="+analysisResult); crossChecksPassed = false; return; } } } for(Object res: ifdsResults) { Constraint<String> splConstraint = splResults.get(res); if(splConstraint==null) { System.err.println("!!! ERROR !!! SPL result too strong! IFDS="+ifdsResults+", SPL/IDE("+res+")={}"); crossChecksPassed = false; return; } if(splConstraint.and(ifdsConstraint).equals(Constraint.falseValue())) { System.err.println("!!! ERROR !!! SPL result too strong! IFDS="+ifdsResults+", SPL/IDE("+res+")="+splConstraint); crossChecksPassed = false; return; } } System.err.print("Cross checks passed. "); } protected static void printResults(FeatureModelInstrumentorTransformer bodyTransformer, SPLIFDSSolver<Local> splSolver) { for(SootMethod m: Scene.v().getMainClass().getMethods()) { if(!m.hasActiveBody()) continue; System.err.println(m.getActiveBody()); Unit ret = m.getActiveBody().getUnits().getLast(); for(Entry<Local, Constraint<String>> l: splSolver.resultsAt(ret).entrySet()) { System.err.print(l.getKey()); System.err.print("="); System.err.println(l.getValue().toString(bodyTransformer.getFeatureNumberer())); } System.err.println(); } } protected static void printIFDSResults(FeatureModelInstrumentorTransformer bodyTransformer, IFDSSolver<Unit, ?, SootMethod, ?> solver) { Unit ret = Scene.v().getMainMethod().getActiveBody().getUnits().getLast(); for(Object l: solver.ifdsResultsAt(ret)) { System.err.println(l); } System.err.println(); } }