package soot.jimple.toolkits.thread.synchronization; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import soot.Hierarchy; import soot.Local; import soot.RefLikeType; import soot.RefType; import soot.Scene; import soot.SootClass; import soot.PointsToAnalysis; import soot.jimple.toolkits.callgraph.ReachableMethods; import soot.jimple.toolkits.pointer.CodeBlockRWSet; import soot.jimple.toolkits.thread.mhp.MhpTester; public class CriticalSectionInterferenceGraph { int nextGroup; List<CriticalSectionGroup> groups; List<CriticalSection> criticalSections; MhpTester mhp; PointsToAnalysis pta; boolean optionOneGlobalLock = false; boolean optionLeaveOriginalLocks = false; boolean optionIncludeEmptyPossibleEdges = false; public CriticalSectionInterferenceGraph(List<CriticalSection> criticalSections, MhpTester mhp, boolean optionOneGlobalLock, boolean optionLeaveOriginalLocks, boolean optionIncludeEmptyPossibleEdges) { this.criticalSections = criticalSections; this.mhp = mhp; this.pta = Scene.v().getPointsToAnalysis(); this.optionOneGlobalLock = optionOneGlobalLock; this.optionLeaveOriginalLocks = optionLeaveOriginalLocks; this.optionIncludeEmptyPossibleEdges = optionIncludeEmptyPossibleEdges; calculateGroups(); } public int groupCount() { return nextGroup; } public List<CriticalSectionGroup> groups() { return groups; } public void calculateGroups() { nextGroup = 1; groups = new ArrayList<CriticalSectionGroup>(); groups.add(new CriticalSectionGroup(0)); // dummy group if(optionOneGlobalLock) // use one group for all transactions { CriticalSectionGroup onlyGroup = new CriticalSectionGroup(nextGroup); Iterator<CriticalSection> tnIt1 = criticalSections.iterator(); while(tnIt1.hasNext()) { CriticalSection tn1 = tnIt1.next(); onlyGroup.add(tn1); } nextGroup++; groups.add(onlyGroup); } else // calculate separate groups for transactions { Iterator<CriticalSection> tnIt1 = criticalSections.iterator(); while(tnIt1.hasNext()) { CriticalSection tn1 = tnIt1.next(); // if this transaction has somehow already been marked for deletion if(tn1.setNumber == -1) continue; // if this transaction is empty if(tn1.read.size() == 0 && tn1.write.size() == 0 && !optionLeaveOriginalLocks) { // this transaction has no effect except on locals... we don't need it! tn1.setNumber = -1; // AKA delete the transactional region (but don't really so long as we are using // the synchronized keyword in our language... because java guarantees memory // barriers at certain points in synchronized blocks) } else { Iterator<CriticalSection> tnIt2 = criticalSections.iterator(); while(tnIt2.hasNext()) { CriticalSection tn2 = tnIt2.next(); // check if this transactional region is going to be deleted if(tn2.setNumber == -1) continue; // check if they're already marked as having an interference // NOTE: this results in a sound grouping, but a badly // incomplete dependency graph. If the dependency // graph is to be analyzed, we cannot do this // if(tn1.setNumber > 0 && tn1.setNumber == tn2.setNumber) // continue; // check if these two transactions can't ever be in parallel if(!mayHappenInParallel(tn1, tn2)) continue; // check for RW or WW data dependencies. // or, for optionLeaveOriginalLocks, check type compatibility SootClass classOne = null; SootClass classTwo = null; boolean typeCompatible = false; boolean emptyEdge = false; if(tn1.origLock != null && tn2.origLock != null) { // Check if edge is empty if(tn1.origLock == null || tn2.origLock == null) emptyEdge = true; else if(!(tn1.origLock instanceof Local) || !(tn2.origLock instanceof Local)) emptyEdge = !tn1.origLock.equals(tn2.origLock); else emptyEdge = !pta.reachingObjects((Local) tn1.origLock).hasNonEmptyIntersection(pta.reachingObjects((Local) tn2.origLock)); // Check if types are compatible RefLikeType typeOne = (RefLikeType) tn1.origLock.getType(); RefLikeType typeTwo = (RefLikeType) tn2.origLock.getType(); classOne = (typeOne instanceof RefType) ? ((RefType) typeOne).getSootClass() : null; classTwo = (typeTwo instanceof RefType) ? ((RefType) typeTwo).getSootClass() : null; if(classOne != null && classTwo != null) { Hierarchy h = Scene.v().getActiveHierarchy(); if(classOne.isInterface()) { if(classTwo.isInterface()) { typeCompatible = h.getSubinterfacesOfIncluding(classOne).contains(classTwo) || h.getSubinterfacesOfIncluding(classTwo).contains(classOne); } else { typeCompatible = h.getImplementersOf(classOne).contains(classTwo); } } else { if(classTwo.isInterface()) { typeCompatible = h.getImplementersOf(classTwo).contains(classOne); } else { typeCompatible = (classOne != null && Scene.v().getActiveHierarchy().getSubclassesOfIncluding(classOne).contains(classTwo) || classTwo != null && Scene.v().getActiveHierarchy().getSubclassesOfIncluding(classTwo).contains(classOne)); } } } } if((!optionLeaveOriginalLocks && (tn1.write.hasNonEmptyIntersection(tn2.write) || tn1.write.hasNonEmptyIntersection(tn2.read) || tn1.read.hasNonEmptyIntersection(tn2.write)) ) || ( optionLeaveOriginalLocks && typeCompatible && (optionIncludeEmptyPossibleEdges || !emptyEdge) )) { // Determine the size of the intersection for GraphViz output CodeBlockRWSet rw = null; int size; if(optionLeaveOriginalLocks) { rw = new CodeBlockRWSet(); size = emptyEdge ? 0 : 1; } else { rw = tn1.write.intersection(tn2.write); rw.union(tn1.write.intersection(tn2.read)); rw.union(tn1.read.intersection(tn2.write)); size = rw.size(); } // Record this tn1.edges.add(new CriticalSectionDataDependency(tn2, size, rw)); // Don't add opposite... all n^2 pairs will be visited separately if(size > 0) { // if tn1 already is in a group if(tn1.setNumber > 0) { // if tn2 is NOT already in a group if(tn2.setNumber == 0) { tn1.group.add(tn2); } // if tn2 is already in a group else if(tn2.setNumber > 0) { if(tn1.setNumber != tn2.setNumber) // if they are equal, then they are already in the same group! { tn1.group.mergeGroups(tn2.group); } } } // if tn1 is NOT already in a group else if(tn1.setNumber == 0) { // if tn2 is NOT already in a group if(tn2.setNumber == 0) { CriticalSectionGroup newGroup = new CriticalSectionGroup(nextGroup); newGroup.add(tn1); newGroup.add(tn2); groups.add(newGroup); nextGroup++; } // if tn2 is already in a group else if(tn2.setNumber > 0) { tn2.group.add(tn1); } } } } } // If, after comparing to all other transactions, we have no group: if(tn1.setNumber == 0) { tn1.setNumber = -1; // delete transactional region } } } } } public boolean mayHappenInParallel(CriticalSection tn1, CriticalSection tn2) { if(mhp == null) { if(optionLeaveOriginalLocks) return true; ReachableMethods rm = Scene.v().getReachableMethods(); if(!rm.contains(tn1.method) || !rm.contains(tn2.method)) return false; return true; } return mhp.mayHappenInParallel(tn1.method, tn2.method); } }