package soot.jimple.toolkits.thread.synchronization; import java.util.*; import soot.*; import soot.util.Chain; import soot.jimple.*; import soot.jimple.toolkits.pointer.*; import soot.jimple.toolkits.thread.ThreadLocalObjectsAnalysis; import soot.jimple.toolkits.thread.mhp.MhpTester; import soot.jimple.toolkits.thread.mhp.SynchObliviousMhpAnalysis; import soot.jimple.toolkits.callgraph.*; import soot.jimple.toolkits.infoflow.*; import soot.jimple.spark.pag.*; import soot.jimple.spark.sets.*; import soot.toolkits.scalar.*; import soot.toolkits.graph.*; public class LockAllocator extends SceneTransformer { public LockAllocator(Singletons.Global g){} public static LockAllocator v() { return G.v().soot_jimple_toolkits_thread_synchronization_LockAllocator(); } List<CriticalSection> criticalSections = null; CriticalSectionInterferenceGraph interferenceGraph = null; DirectedGraph deadlockGraph = null; // Lock options boolean optionOneGlobalLock = false; boolean optionStaticLocks = false; boolean optionUseLocksets = false; boolean optionLeaveOriginalLocks = false; boolean optionIncludeEmptyPossibleEdges = false; // Semantic options boolean optionAvoidDeadlock = true; boolean optionOpenNesting = true; // Analysis options boolean optionDoMHP = false; boolean optionDoTLO = false; boolean optionOnFlyTLO = false; // not a CLI option yet // on-fly is more efficient, but harder to measure in time // Output options boolean optionPrintMhpSummary = true; // not a CLI option yet boolean optionPrintGraph = false; boolean optionPrintTable = false; boolean optionPrintDebug = false; protected void internalTransform(String phaseName, Map options) { // Get phase options String lockingScheme = PhaseOptions.getString( options, "locking-scheme" ); if(lockingScheme.equals("fine-grained")) { optionOneGlobalLock = false; optionStaticLocks = false; optionUseLocksets = true; optionLeaveOriginalLocks = false; } // if(lockingScheme.equals("fine-static")) // { // optionOneGlobalLock = false; // optionStaticLocks = true; // optionUseLocksets = true; // optionLeaveOriginalLocks = false; // } if(lockingScheme.equals("medium-grained")) // rename to coarse-grained { optionOneGlobalLock = false; optionStaticLocks = false; optionUseLocksets = false; optionLeaveOriginalLocks = false; } if(lockingScheme.equals("coarse-grained")) // rename to coarse-static { optionOneGlobalLock = false; optionStaticLocks = true; optionUseLocksets = false; optionLeaveOriginalLocks = false; } if(lockingScheme.equals("single-static")) { optionOneGlobalLock = true; optionStaticLocks = true; optionUseLocksets = false; optionLeaveOriginalLocks = false; } if(lockingScheme.equals("leave-original")) { optionOneGlobalLock = false; optionStaticLocks = false; optionUseLocksets = false; optionLeaveOriginalLocks = true; } optionAvoidDeadlock = PhaseOptions.getBoolean( options, "avoid-deadlock" ); optionOpenNesting = PhaseOptions.getBoolean( options, "open-nesting" ); optionDoMHP = PhaseOptions.getBoolean( options, "do-mhp" ); optionDoTLO = PhaseOptions.getBoolean( options, "do-tlo" ); // optionOnFlyTLO = PhaseOptions.getBoolean( options, "on-fly-tlo" ); // not a real option yet // optionPrintMhpSummary = PhaseOptions.getBoolean( options, "print-mhp" ); // not a real option yet optionPrintGraph = PhaseOptions.getBoolean( options, "print-graph" ); optionPrintTable = PhaseOptions.getBoolean( options, "print-table" ); optionPrintDebug = PhaseOptions.getBoolean( options, "print-debug" ); // optionIncludeEmptyPossibleEdges = PhaseOptions.getBoolean( options, "include-empty-edges" ); // not a real option yet // *** Build May Happen In Parallel Info *** MhpTester mhp = null; if(optionDoMHP && Scene.v().getPointsToAnalysis() instanceof PAG) { G.v().out.println("[wjtp.tn] *** Build May-Happen-in-Parallel Info *** " + (new Date())); mhp = new SynchObliviousMhpAnalysis(); if(optionPrintMhpSummary) { mhp.printMhpSummary(); } } // *** Find Thread-Local Objects *** ThreadLocalObjectsAnalysis tlo = null; if(optionDoTLO) { G.v().out.println("[wjtp.tn] *** Find Thread-Local Objects *** " + (new Date())); if(mhp != null) tlo = new ThreadLocalObjectsAnalysis(mhp); else tlo = new ThreadLocalObjectsAnalysis(new SynchObliviousMhpAnalysis()); if(!optionOnFlyTLO) { tlo.precompute(); G.v().out.println("[wjtp.tn] TLO totals (#analyzed/#encountered): " + SmartMethodInfoFlowAnalysis.counter + "/" + ClassInfoFlowAnalysis.methodCount); } else G.v().out.println("[wjtp.tn] TLO so far (#analyzed/#encountered): " + SmartMethodInfoFlowAnalysis.counter + "/" + ClassInfoFlowAnalysis.methodCount); } // *** Find and Name Transactions *** // The transaction finder finds the start, end, and preparatory statements // for each transaction. It also calculates the non-transitive read/write // sets for each transaction. // For all methods, run the intraprocedural analysis (TransactionAnalysis) Date start = new Date(); G.v().out.println("[wjtp.tn] *** Find and Name Transactions *** " + start); Map<SootMethod, FlowSet> methodToFlowSet = new HashMap<SootMethod, FlowSet>(); Map<SootMethod, ExceptionalUnitGraph> methodToExcUnitGraph = new HashMap<SootMethod, ExceptionalUnitGraph>(); Iterator runAnalysisClassesIt = Scene.v().getApplicationClasses().iterator(); while (runAnalysisClassesIt.hasNext()) { SootClass appClass = (SootClass) runAnalysisClassesIt.next(); Iterator methodsIt = appClass.getMethods().iterator(); while (methodsIt.hasNext()) { SootMethod method = (SootMethod) methodsIt.next(); if(method.isConcrete()) { Body b = method.retrieveActiveBody(); ExceptionalUnitGraph eug = new ExceptionalUnitGraph(b); methodToExcUnitGraph.put(method, eug); // run the intraprocedural analysis SynchronizedRegionFinder ta = new SynchronizedRegionFinder(eug, b, optionPrintDebug, optionOpenNesting, tlo); Chain units = b.getUnits(); Unit lastUnit = (Unit) units.getLast(); FlowSet fs = (FlowSet) ta.getFlowBefore(lastUnit); // add the results to the list of results methodToFlowSet.put(method, fs); } } } // Create a composite list of all transactions criticalSections = new Vector<CriticalSection>(); for(FlowSet fs : methodToFlowSet.values()) { List fList = fs.toList(); for(int i = 0; i < fList.size(); i++) criticalSections.add(((SynchronizedRegionFlowPair) fList.get(i)).tn); } // Assign Names To Transactions assignNamesToTransactions(criticalSections); if(optionOnFlyTLO) { G.v().out.println("[wjtp.tn] TLO so far (#analyzed/#encountered): " + SmartMethodInfoFlowAnalysis.counter + "/" + ClassInfoFlowAnalysis.methodCount); } // *** Find Transitive Read/Write Sets *** // Finds the transitive read/write set for each transaction using a given // nesting model. G.v().out.println("[wjtp.tn] *** Find Transitive Read/Write Sets *** " + (new Date())); PointsToAnalysis pta = Scene.v().getPointsToAnalysis(); CriticalSectionAwareSideEffectAnalysis tasea = null; tasea = new CriticalSectionAwareSideEffectAnalysis( pta, Scene.v().getCallGraph(), (optionOpenNesting ? criticalSections : null), tlo); Iterator<CriticalSection> tnIt = criticalSections.iterator(); while(tnIt.hasNext()) { CriticalSection tn = tnIt.next(); for(Unit unit : tn.invokes) { Stmt stmt = (Stmt) unit; HashSet uses = new HashSet(); RWSet stmtRead = tasea.readSet(tn.method, stmt, tn, uses); if(stmtRead != null) tn.read.union(stmtRead); RWSet stmtWrite = tasea.writeSet(tn.method, stmt, tn, uses); if(stmtWrite != null) tn.write.union(stmtWrite); } } long longTime = ((new Date()).getTime() - start.getTime()) / 100; float time = ((float) longTime) / 10.0f; if(optionOnFlyTLO) { G.v().out.println("[wjtp.tn] TLO totals (#analyzed/#encountered): " + SmartMethodInfoFlowAnalysis.counter + "/" + ClassInfoFlowAnalysis.methodCount); G.v().out.println("[wjtp.tn] Time for stages utilizing on-fly TLO: " + time + "s"); } // *** Find Stray Reads/Writes *** (DISABLED) // add external data races as one-line transactions // note that finding them isn't that hard (though it is time consuming) // For all methods, run the intraprocedural analysis (transaction finder) // Note that these will only be transformed if they are either added to // methodToFlowSet or if a loop and new body transformer are used for methodToStrayRWSet /* Map methodToStrayRWSet = new HashMap(); Iterator runRWFinderClassesIt = Scene.v().getApplicationClasses().iterator(); while (runRWFinderClassesIt.hasNext()) { SootClass appClass = (SootClass) runRWFinderClassesIt.next(); Iterator methodsIt = appClass.getMethods().iterator(); while (methodsIt.hasNext()) { SootMethod method = (SootMethod) methodsIt.next(); Body b = method.retrieveActiveBody(); UnitGraph g = (UnitGraph) methodToExcUnitGraph.get(method); // run the interprocedural analysis // PTFindStrayRW ptfrw = new PTFindStrayRW(new ExceptionalUnitGraph(b), b, AllTransactions); PTFindStrayRW ptfrw = new PTFindStrayRW(g, b, AllTransactions); Chain units = b.getUnits(); Unit firstUnit = (Unit) units.iterator().next(); FlowSet fs = (FlowSet) ptfrw.getFlowBefore(firstUnit); // add the results to the list of results methodToStrayRWSet.put(method, fs); } } //*/ // *** Calculate Locking Groups *** // Search for data dependencies between transactions, and split them into disjoint sets G.v().out.println("[wjtp.tn] *** Calculate Locking Groups *** " + (new Date())); CriticalSectionInterferenceGraph ig = new CriticalSectionInterferenceGraph( criticalSections, mhp, optionOneGlobalLock, optionLeaveOriginalLocks, optionIncludeEmptyPossibleEdges); interferenceGraph = ig; // save in field for later retrieval // *** Detect the Possibility of Deadlock *** G.v().out.println("[wjtp.tn] *** Detect the Possibility of Deadlock *** " + (new Date())); DeadlockDetector dd = new DeadlockDetector(optionPrintDebug, optionAvoidDeadlock, true, criticalSections); if(!optionUseLocksets) // deadlock detection for all single-lock-per-region allocations deadlockGraph = dd.detectComponentBasedDeadlock(); // *** Calculate Locking Objects *** // Get a list of all dependencies for each group G.v().out.println("[wjtp.tn] *** Calculate Locking Objects *** " + (new Date())); if(!optionStaticLocks) { // Calculate per-group contributing RWSet // (Might be preferable to use per-transaction contributing RWSet) for(CriticalSection tn : criticalSections) { if(tn.setNumber <= 0) continue; for(CriticalSectionDataDependency tdd : tn.edges) tn.group.rwSet.union(tdd.rw); } } // Inspect each group's RW dependencies to determine if there's a possibility // of a shared lock object (if all dependencies are fields/localobjs of the same object) Map<Value, Integer> lockToLockNum = null; List<PointsToSetInternal> lockPTSets = null; if(optionLeaveOriginalLocks) { analyzeExistingLocks(criticalSections, ig); } else if(optionStaticLocks) { setFlagsForStaticAllocations(ig); } else // for locksets and dynamic locks { setFlagsForDynamicAllocations(ig); // Data structures for determining lock numbers lockPTSets = new ArrayList<PointsToSetInternal>(); lockToLockNum = new HashMap<Value, Integer>(); findLockableReferences(criticalSections, pta, tasea, lockToLockNum,lockPTSets); // print out locksets if(optionUseLocksets) { for( CriticalSection tn : criticalSections ) { if( tn.group != null ) { G.v().out.println("[wjtp.tn] " + tn.name + " lockset: " + locksetToLockNumString(tn.lockset, lockToLockNum) + (tn.group.useLocksets ? "" : " (placeholder)")); } } } } // *** Detect the Possibility of Deadlock for Locksets *** if(optionUseLocksets) // deadlock detection and lock ordering for lockset allocations { G.v().out.println("[wjtp.tn] *** Detect " + (optionAvoidDeadlock ? "and Correct " : "") + "the Possibility of Deadlock for Locksets *** " + (new Date())); deadlockGraph = dd.detectLocksetDeadlock(lockToLockNum, lockPTSets); if(optionPrintDebug) ((HashMutableEdgeLabelledDirectedGraph) deadlockGraph).printGraph(); G.v().out.println("[wjtp.tn] *** Reorder Locksets to Avoid Deadlock *** " + (new Date())); dd.reorderLocksets(lockToLockNum, (HashMutableEdgeLabelledDirectedGraph) deadlockGraph); } // *** Print Output and Transform Program *** G.v().out.println("[wjtp.tn] *** Print Output and Transform Program *** " + (new Date())); // Print topological graph in graphviz format if(optionPrintGraph) printGraph(criticalSections, ig, lockToLockNum); // Print table of transaction information if(optionPrintTable) { printTable(criticalSections, mhp); printGroups(criticalSections, ig); } // For all methods, run the lock transformer if(!optionLeaveOriginalLocks) { // Create an array of booleans to keep track of which global locks have been inserted into the program boolean[] insertedGlobalLock = new boolean[ig.groupCount()]; insertedGlobalLock[0] = false; for(int i = 1; i < ig.groupCount(); i++) { CriticalSectionGroup tnGroup = ig.groups().get(i); insertedGlobalLock[i] = (!optionOneGlobalLock) && (tnGroup.useDynamicLock || tnGroup.useLocksets); } for(SootClass appClass : Scene.v().getApplicationClasses()) { for(SootMethod method : appClass.getMethods()) { if(method.isConcrete()) { FlowSet fs = methodToFlowSet.get(method); if(fs != null) // (newly added methods need not be transformed) LockAllocationBodyTransformer.v().internalTransform(method.getActiveBody(), fs, ig.groups(), insertedGlobalLock); } } } } } protected void findLockableReferences(List<CriticalSection> AllTransactions, PointsToAnalysis pta, CriticalSectionAwareSideEffectAnalysis tasea, Map<Value, Integer> lockToLockNum, List<PointsToSetInternal> lockPTSets) { // For each transaction, if the group's R/Ws may be fields of the same object, // then check for the transaction if they must be fields of the same RUNTIME OBJECT Iterator<CriticalSection> tnIt9 = AllTransactions.iterator(); while(tnIt9.hasNext()) { CriticalSection tn = tnIt9.next(); int group = tn.setNumber - 1; if(group < 0) continue; if(tn.group.useDynamicLock || tn.group.useLocksets) // if attempting to use a dynamic lock or locksets { // Get list of objects (FieldRef or Local) to be locked (lockset analysis) G.v().out.println("[wjtp.tn] * " + tn.name + " *"); LockableReferenceAnalysis la = new LockableReferenceAnalysis(new BriefUnitGraph(tn.method.retrieveActiveBody())); tn.lockset = la.getLocksetOf(tasea, tn.group.rwSet, tn); // Determine if list is suitable for the selected locking scheme // TODO check for nullness if(optionUseLocksets) { // Post-process the locksets if(tn.lockset == null || tn.lockset.size() <= 0) { // If the lockset is invalid, revert the entire group to static locks: tn.group.useLocksets = false; // Create a lockset containing a single placeholder static lock for each tn in the group Value newStaticLock = new NewStaticLock(tn.method.getDeclaringClass()); EquivalentValue newStaticLockEqVal = new EquivalentValue(newStaticLock); for(CriticalSection groupTn : tn.group) { groupTn.lockset = new ArrayList<EquivalentValue>(); groupTn.lockset.add(newStaticLockEqVal); } // Assign a lock number to the placeholder Integer lockNum = new Integer(-lockPTSets.size()); // negative indicates a static lock G.v().out.println("[wjtp.tn] Lock: num " + lockNum + " type " + newStaticLock.getType() + " obj " + newStaticLock); lockToLockNum.put(newStaticLockEqVal, lockNum); lockToLockNum.put(newStaticLock, lockNum); PointsToSetInternal dummyLockPT = new HashPointsToSet(newStaticLock.getType(), (PAG) pta); // KILLS CHA-BASED ANALYSIS (pointer exception) lockPTSets.add(dummyLockPT); } else { // If the lockset is valid // Assign a lock number for each lock in the lockset for( EquivalentValue lockEqVal : tn.lockset ) { Value lock = lockEqVal.getValue(); // Get reaching objects for this lock PointsToSetInternal lockPT; if(lock instanceof Local) lockPT = (PointsToSetInternal) pta.reachingObjects((Local) lock); else if(lock instanceof StaticFieldRef) // needs special treatment: could be primitive lockPT = null; else if(lock instanceof InstanceFieldRef) { Local base = (Local) ((InstanceFieldRef) lock).getBase(); if(base instanceof FakeJimpleLocal) lockPT = (PointsToSetInternal) pta.reachingObjects(((FakeJimpleLocal)base).getRealLocal(), ((FieldRef) lock).getField()); else lockPT = (PointsToSetInternal) pta.reachingObjects(base, ((FieldRef) lock).getField()); } else if(lock instanceof NewStaticLock) // placeholder for anything that needs a static lock lockPT = null; else lockPT = null; if( lockPT != null ) { // Assign an existing lock number if possible boolean foundLock = false; for(int i = 0; i < lockPTSets.size(); i++) { PointsToSetInternal otherLockPT = lockPTSets.get(i); if(lockPT.hasNonEmptyIntersection(otherLockPT)) // will never happen for empty, negative numbered sets { G.v().out.println("[wjtp.tn] Lock: num " + i + " type " + lock.getType() + " obj " + lock); lockToLockNum.put(lock, new Integer(i)); otherLockPT.addAll(lockPT, null); foundLock = true; break; } } // Assign a brand new lock number otherwise if(!foundLock) { G.v().out.println("[wjtp.tn] Lock: num " + lockPTSets.size() + " type " + lock.getType() + " obj " + lock); lockToLockNum.put(lock, new Integer(lockPTSets.size())); PointsToSetInternal otherLockPT = new HashPointsToSet(lockPT.getType(), (PAG) pta); lockPTSets.add(otherLockPT); otherLockPT.addAll(lockPT, null); } } else // static field locks and pathological cases... { // Assign an existing lock number if possible if( lockToLockNum.get(lockEqVal) != null ) { Integer lockNum = lockToLockNum.get(lockEqVal); G.v().out.println("[wjtp.tn] Lock: num " + lockNum + " type " + lock.getType() + " obj " + lock); lockToLockNum.put(lock, lockNum); } else { Integer lockNum = new Integer(-lockPTSets.size()); // negative indicates a static lock G.v().out.println("[wjtp.tn] Lock: num " + lockNum + " type " + lock.getType() + " obj " + lock); lockToLockNum.put(lockEqVal, lockNum); lockToLockNum.put(lock, lockNum); PointsToSetInternal dummyLockPT = new HashPointsToSet(lock.getType(), (PAG) pta); lockPTSets.add(dummyLockPT); } } } } } else { if(tn.lockset == null || tn.lockset.size() != 1) {// Found too few or too many locks // So use a static lock instead tn.lockObject = null; tn.group.useDynamicLock = false; tn.group.lockObject = null; } else {// Found exactly one lock // Use it! tn.lockObject = (Value) tn.lockset.get(0); // If it's the best lock we've found in the group yet, use it for display if(tn.group.lockObject == null || tn.lockObject instanceof Ref) tn.group.lockObject = tn.lockObject; } } } } if(optionUseLocksets) { // If any lock has only a singleton reaching object, treat it like a static lock for(int i = 0; i < lockPTSets.size(); i++) { PointsToSetInternal pts = lockPTSets.get(i); if(pts.size() == 1 && false) // isSingleton(pts)) // It's NOT easy to find a singleton: single alloc node must be run just once { for(Object e : lockToLockNum.entrySet()) { Map.Entry entry = (Map.Entry) e; Integer value = (Integer) entry.getValue(); if(value == i) { entry.setValue(new Integer(-i)); } } } } } } public void setFlagsForDynamicAllocations(CriticalSectionInterferenceGraph ig) { for(int group = 0; group < ig.groupCount() - 1; group++) { CriticalSectionGroup tnGroup = ig.groups().get(group + 1); if(optionUseLocksets) { tnGroup.useLocksets = true; // initially, guess that this is true } else { tnGroup.isDynamicLock = (tnGroup.rwSet.getGlobals().size() == 0); tnGroup.useDynamicLock = true; tnGroup.lockObject = null; } // empty groups don't get locks if(tnGroup.rwSet.size() <= 0) // There are no edges in this group { if(optionUseLocksets) { tnGroup.useLocksets = false; } else { tnGroup.isDynamicLock = false; tnGroup.useDynamicLock = false; } continue; } } } public void setFlagsForStaticAllocations(CriticalSectionInterferenceGraph ig) { // Allocate one new static lock for each group. for(int group = 0; group < ig.groupCount() - 1; group++) { CriticalSectionGroup tnGroup = ig.groups().get(group + 1); tnGroup.isDynamicLock = false; tnGroup.useDynamicLock = false; tnGroup.lockObject = null; } } private void analyzeExistingLocks(List<CriticalSection> AllTransactions, CriticalSectionInterferenceGraph ig) { setFlagsForStaticAllocations(ig); // if for any lock there is any def to anything other than a static field, then it's a local lock. // for each transaction, check every def of the lock Iterator<CriticalSection> tnAIt = AllTransactions.iterator(); while(tnAIt.hasNext()) { CriticalSection tn = tnAIt.next(); if(tn.setNumber <= 0) continue; ExceptionalUnitGraph egraph = new ExceptionalUnitGraph(tn.method.retrieveActiveBody()); SmartLocalDefs sld = new SmartLocalDefs(egraph, new SimpleLiveLocals(egraph)); if(tn.origLock == null || !(tn.origLock instanceof Local)) // || tn.begin == null) continue; List<Unit> rDefs = sld.getDefsOfAt( (Local) tn.origLock , tn.entermonitor ); if(rDefs == null) continue; Iterator<Unit> rDefsIt = rDefs.iterator(); while (rDefsIt.hasNext()) { Stmt next = (Stmt) rDefsIt.next(); if(next instanceof DefinitionStmt) { Value rightOp = ((DefinitionStmt) next).getRightOp(); if(rightOp instanceof FieldRef) { if(((FieldRef) rightOp).getField().isStatic()) { // lock may be static tn.group.lockObject = rightOp; } else { // this lock must be dynamic tn.group.isDynamicLock = true; tn.group.useDynamicLock = true; tn.group.lockObject = tn.origLock; } } else { // this lock is probably dynamic (but it's hard to tell for sure) tn.group.isDynamicLock = true; tn.group.useDynamicLock = true; tn.group.lockObject = tn.origLock; } } else { // this lock is probably dynamic (but it's hard to tell for sure) tn.group.isDynamicLock = true; tn.group.useDynamicLock = true; tn.group.lockObject = tn.origLock; } } } } public static String locksetToLockNumString(List<EquivalentValue> lockset, Map<Value, Integer> lockToLockNum) { if( lockset == null ) return "null"; String ret = "["; boolean first = true; for( EquivalentValue lockEqVal : lockset ) { if(!first) ret = ret + " "; first = false; ret = ret + lockToLockNum.get(lockEqVal.getValue()); } return ret + "]"; } public void assignNamesToTransactions(List<CriticalSection> AllTransactions) { // Give each method a unique, deterministic identifier // Sort transactions into bins... one for each method name // Get list of method names List<String> methodNamesTemp = new ArrayList<String>(); Iterator<CriticalSection> tnIt5 = AllTransactions.iterator(); while (tnIt5.hasNext()) { CriticalSection tn1 = tnIt5.next(); String mname = tn1.method.getSignature(); //tn1.method.getSignature() + "." + tn1.method.getName(); if(!methodNamesTemp.contains(mname)) methodNamesTemp.add(mname); } String methodNames[] = new String[1]; methodNames = methodNamesTemp.toArray(methodNames); Arrays.sort(methodNames); // Initialize method-named bins // this matrix is <# method names> wide and <max txns possible in one method> + 1 tall int identMatrix[][] = new int[methodNames.length][CriticalSection.nextIDNum - methodNames.length + 2]; for(int i = 0; i < methodNames.length; i++) { identMatrix[i][0] = 0; for(int j = 1; j < CriticalSection.nextIDNum - methodNames.length + 1; j++) { identMatrix[i][j] = 50000; } } // Put transactions into bins Iterator<CriticalSection> tnIt0 = AllTransactions.iterator(); while(tnIt0.hasNext()) { CriticalSection tn1 = tnIt0.next(); int methodNum = Arrays.binarySearch(methodNames, tn1.method.getSignature()); identMatrix[methodNum][0]++; identMatrix[methodNum][identMatrix[methodNum][0]] = tn1.IDNum; } // Sort bins by transaction IDNum // IDNums vary, but always increase in code-order within a method for(int j = 0; j < methodNames.length; j++) { identMatrix[j][0] = 0; // set the counter to 0 so it sorts out (into slot 0). Arrays.sort(identMatrix[j]); // sort this subarray } // Generate a name based on the bin number and location within the bin Iterator<CriticalSection> tnIt4 = AllTransactions.iterator(); while(tnIt4.hasNext()) { CriticalSection tn1 = tnIt4.next(); int methodNum = Arrays.binarySearch(methodNames, tn1.method.getSignature()); int tnNum = Arrays.binarySearch(identMatrix[methodNum], tn1.IDNum) - 1; tn1.name = "m" + (methodNum < 10? "00" : (methodNum < 100? "0" : "")) + methodNum + "n" + (tnNum < 10? "0" : "") + tnNum; } } public void printGraph(Collection<CriticalSection> AllTransactions, CriticalSectionInterferenceGraph ig, Map<Value, Integer> lockToLockNum) { final String[] colors = {"black", "blue", "blueviolet", "chartreuse", "crimson", "darkgoldenrod1", "darkseagreen", "darkslategray", "deeppink", "deepskyblue1", "firebrick1", "forestgreen", "gold", "gray80", "navy", "pink", "red", "sienna", "turquoise1", "yellow"}; Map<Integer, String> lockColors = new HashMap<Integer, String>(); int colorNum = 0; HashSet<CriticalSection> visited = new HashSet<CriticalSection>(); G.v().out.println("[transaction-graph]" + (optionUseLocksets ? "" : " strict") + " graph transactions {"); // "\n[transaction-graph] start=1;"); for(int group = 0; group < ig.groups().size(); group++) { boolean printedHeading = false; Iterator<CriticalSection> tnIt = AllTransactions.iterator(); while(tnIt.hasNext()) { CriticalSection tn = tnIt.next(); if(tn.setNumber == group + 1) { if(!printedHeading) { // if(localLock[group] && lockObject[group] != null) if(tn.group.useDynamicLock && tn.group.lockObject != null) { String typeString = ""; // if(lockObject[group].getType() instanceof RefType) // typeString = ((RefType) lockObject[group].getType()).getSootClass().getShortName(); // else // typeString = lockObject[group].getType().toString(); if(tn.group.lockObject.getType() instanceof RefType) typeString = ((RefType) tn.group.lockObject.getType()).getSootClass().getShortName(); else typeString = tn.group.lockObject.getType().toString(); G.v().out.println("[transaction-graph] subgraph cluster_" + (group + 1) + " {\n[transaction-graph] color=blue;\n[transaction-graph] label=\"Lock: a \\n" + typeString + " object\";"); } else if(tn.group.useLocksets) { G.v().out.println("[transaction-graph] subgraph cluster_" + (group + 1) + " {\n[transaction-graph] color=blue;\n[transaction-graph] label=\"Locksets\";"); } else { String objString = ""; // if(lockObject[group] == null) if(tn.group.lockObject == null) { objString = "lockObj" + (group + 1); } // else if(lockObject[group] instanceof FieldRef) else if(tn.group.lockObject instanceof FieldRef) { // SootField field = ((FieldRef) lockObject[group]).getField(); SootField field = ((FieldRef) tn.group.lockObject).getField(); objString = field.getDeclaringClass().getShortName() + "." + field.getName(); } else objString = tn.group.lockObject.toString(); // objString = lockObject[group].toString(); G.v().out.println("[transaction-graph] subgraph cluster_" + (group + 1) + " {\n[transaction-graph] color=blue;\n[transaction-graph] label=\"Lock: \\n" + objString + "\";"); } printedHeading = true; } if(Scene.v().getReachableMethods().contains(tn.method)) G.v().out.println("[transaction-graph] " + tn.name + " [name=\"" + tn.method.toString() + "\" style=\"setlinewidth(3)\"];"); else G.v().out.println("[transaction-graph] " + tn.name + " [name=\"" + tn.method.toString() + "\" color=cadetblue1 style=\"setlinewidth(1)\"];"); if(tn.group.useLocksets) // print locks instead of dependence edges { for(EquivalentValue lockEqVal : tn.lockset) { Integer lockNum = lockToLockNum.get(lockEqVal.getValue()); for(CriticalSection tn2 : tn.group) { if(!visited.contains(tn2) && ig.mayHappenInParallel(tn, tn2)) { for(EquivalentValue lock2EqVal : tn2.lockset) { Integer lock2Num = lockToLockNum.get(lock2EqVal.getValue()); if(lockNum.intValue() == lock2Num.intValue()) { // Get the color for this lock if(!lockColors.containsKey(lockNum)) { lockColors.put(lockNum, colors[colorNum % colors.length]); colorNum++; } String color = lockColors.get(lockNum); // Draw an edge for this lock G.v().out.println("[transaction-graph] " + tn.name + " -- " + tn2.name + " [color=" + color + " style=" + (lockNum >= 0 ? "dashed" : "solid") + " exactsize=1 style=\"setlinewidth(3)\"];"); } } } } visited.add(tn); } } else { Iterator<CriticalSectionDataDependency> tnedgeit = tn.edges.iterator(); while(tnedgeit.hasNext()) { CriticalSectionDataDependency edge = tnedgeit.next(); CriticalSection tnedge = edge.other; if(tnedge.setNumber == group + 1) G.v().out.println("[transaction-graph] " + tn.name + " -- " + tnedge.name + " [color=" + (edge.size > 0 ? "black" : "cadetblue1") + " style=" + (tn.setNumber > 0 && tn.group.useDynamicLock ? "dashed" : "solid") + " exactsize=" + edge.size + " style=\"setlinewidth(3)\"];"); } } } } if(printedHeading) G.v().out.println("[transaction-graph] }"); } // Print nodes with no group { boolean printedHeading = false; Iterator<CriticalSection> tnIt = AllTransactions.iterator(); while(tnIt.hasNext()) { CriticalSection tn = tnIt.next(); if(tn.setNumber == -1) { if(!printedHeading) { // putting these nodes in a "source" ranked subgraph makes them appear above all the clusters G.v().out.println("[transaction-graph] subgraph lone {\n[transaction-graph] rank=source;"); printedHeading = true; } if(Scene.v().getReachableMethods().contains(tn.method)) G.v().out.println("[transaction-graph] " + tn.name + " [name=\"" + tn.method.toString() + "\" style=\"setlinewidth(3)\"];"); else G.v().out.println("[transaction-graph] " + tn.name + " [name=\"" + tn.method.toString() + "\" color=cadetblue1 style=\"setlinewidth(1)\"];"); Iterator<CriticalSectionDataDependency> tnedgeit = tn.edges.iterator(); while(tnedgeit.hasNext()) { CriticalSectionDataDependency edge = tnedgeit.next(); CriticalSection tnedge = edge.other; if(tnedge.setNumber != tn.setNumber || tnedge.setNumber == -1) G.v().out.println("[transaction-graph] " + tn.name + " -- " + tnedge.name + " [color=" + (edge.size > 0 ? "black" : "cadetblue1") + " style=" + (tn.setNumber > 0 && tn.group.useDynamicLock ? "dashed" : "solid") + " exactsize=" + edge.size + " style=\"setlinewidth(1)\"];"); } } } if(printedHeading) G.v().out.println("[transaction-graph] }"); } G.v().out.println("[transaction-graph] }"); } public void printTable(Collection<CriticalSection> AllTransactions, MhpTester mhp) { G.v().out.println("[transaction-table] "); Iterator<CriticalSection> tnIt7 = AllTransactions.iterator(); while(tnIt7.hasNext()) { CriticalSection tn = tnIt7.next(); // Figure out if it's reachable, and if it MHP itself boolean reachable = false; boolean mhpself = false; { ReachableMethods rm = Scene.v().getReachableMethods(); reachable = rm.contains(tn.method); if(mhp != null) mhpself = mhp.mayHappenInParallel(tn.method, tn.method); } G.v().out.println("[transaction-table] Transaction " + tn.name + (reachable ? " reachable" : " dead") + (mhpself ? " [called from >= 2 threads]" : " [called from <= 1 thread]")); G.v().out.println("[transaction-table] Where: " + tn.method.getDeclaringClass().toString() + ":" + tn.method.toString() + ": "); G.v().out.println("[transaction-table] Orig : " + tn.origLock); G.v().out.println("[transaction-table] Prep : " + tn.prepStmt); G.v().out.println("[transaction-table] Begin: " + tn.entermonitor); G.v().out.print("[transaction-table] End : early:" + tn.earlyEnds.toString() + " exc:" + tn.exceptionalEnd + " through:" + tn.end + " \n"); G.v().out.println("[transaction-table] Size : " + tn.units.size()); if(tn.read.size() < 100) G.v().out.print("[transaction-table] Read : " + tn.read.size() + "\n[transaction-table] " + tn.read.toString().replaceAll("\\[", " : [").replaceAll("\n", "\n[transaction-table] ")); else G.v().out.print("[transaction-table] Read : " + tn.read.size() + " \n[transaction-table] "); if(tn.write.size() < 100) G.v().out.print("Write: " + tn.write.size() + "\n[transaction-table] " + tn.write.toString().replaceAll("\\[", " : [").replaceAll("\n", "\n[transaction-table] ")); // label provided by previous print statement else G.v().out.print("Write: " + tn.write.size() + "\n[transaction-table] "); // label provided by previous print statement G.v().out.print("Edges: (" + tn.edges.size() + ") "); // label provided by previous print statement Iterator<CriticalSectionDataDependency> tnedgeit = tn.edges.iterator(); while(tnedgeit.hasNext()) G.v().out.print(tnedgeit.next().other.name + " "); if(tn.group != null && tn.group.useLocksets) { G.v().out.println("\n[transaction-table] Locks: " + tn.lockset); } else G.v().out.println("\n[transaction-table] Lock : " + (tn.setNumber == -1 ? "-" : (tn.lockObject == null ? "Global" : (tn.lockObject.toString() + (tn.lockObjectArrayIndex == null ? "" : "[" + tn.lockObjectArrayIndex + "]")) ))); G.v().out.println("[transaction-table] Group: " + tn.setNumber + "\n[transaction-table] "); } } public void printGroups(Collection<CriticalSection> AllTransactions, CriticalSectionInterferenceGraph ig) { G.v().out.print("[transaction-groups] Group Summaries\n[transaction-groups] "); for(int group = 0; group < ig.groupCount() - 1; group++) { CriticalSectionGroup tnGroup = ig.groups().get(group + 1); if(tnGroup.size() > 0) { G.v().out.print("Group " + (group + 1) + " "); G.v().out.print("Locking: " + (tnGroup.useLocksets ? "using " : (tnGroup.isDynamicLock && tnGroup.useDynamicLock ? "Dynamic on " : "Static on ")) + (tnGroup.useLocksets ? "locksets" : (tnGroup.lockObject == null ? "null" : tnGroup.lockObject.toString())) ); G.v().out.print("\n[transaction-groups] : "); Iterator<CriticalSection> tnIt = AllTransactions.iterator(); while(tnIt.hasNext()) { CriticalSection tn = tnIt.next(); if(tn.setNumber == group + 1) G.v().out.print(tn.name + " "); } G.v().out.print("\n[transaction-groups] " + tnGroup.rwSet.toString().replaceAll("\\[", " : [").replaceAll("\n", "\n[transaction-groups] ")); } } G.v().out.print("Erasing \n[transaction-groups] : "); Iterator<CriticalSection> tnIt = AllTransactions.iterator(); while(tnIt.hasNext()) { CriticalSection tn = tnIt.next(); if(tn.setNumber == -1) G.v().out.print(tn.name + " "); } G.v().out.println("\n[transaction-groups] "); } public CriticalSectionInterferenceGraph getInterferenceGraph() { return interferenceGraph; } public DirectedGraph getDeadlockGraph() { return deadlockGraph; } public List<CriticalSection> getCriticalSections() { return criticalSections; } }