private Transition transition;
// DPOR-related fields
+ // Basic information
private Integer[] choices;
private Integer[] refChoices;
private int choiceCounter;
private int maxEventChoice;
- // Record CGs for backtracking points
- private List<IntChoiceFromSet> cgList;
// Data structure to track the events seen by each state to track cycles (containing all events) for termination
- private HashMap<Integer, HashSet<Integer>> stateToEventMap;
+ private HashSet<Integer> currVisitedStates; // States being visited in the current execution
private HashSet<Integer> justVisitedStates; // States just visited in the previous choice/event
private HashSet<Integer> prevVisitedStates; // States visited in the previous execution
- private HashSet<Integer> currVisitedStates; // States being visited in the current execution
- // Data structure to analyze field Read/Write accesses
- private HashMap<Integer, ReadWriteSet> readWriteFieldsMap;
- private HashMap<Integer, HashSet<Integer>> conflictPairMap;
+ private HashMap<Integer, HashSet<Integer>> stateToEventMap;
+ // Data structure to analyze field Read/Write accesses and conflicts
+ private HashMap<Integer, LinkedList<Integer[]>> backtrackMap; // Track created backtracking points
+ private Stack<BacktrackPoint> btrckPtsStack; // Stack that stores backtracking points
+ private List<IntChoiceFromSet> cgList; // Record CGs for backtracking points
+ private HashSet<IntChoiceFromSet> btrckCGSet; // Set that records all the backtrack CGs
+ private HashMap<Integer, HashSet<Integer>> conflictPairMap; // Record conflicting events
+ private HashMap<Integer, ReadWriteSet> readWriteFieldsMap; // Record fields that are accessed
+
+ // Visible operation dependency graph implementation (SPIN paper) related fields
+ private int prevChoiceValue;
+ private HashMap<Integer, HashSet<Integer>> vodGraphMap; // Visible operation dependency graph (VOD graph)
// Boolean states
private boolean isBooleanCGFlipped;
refChoices = null;
choiceCounter = 0;
maxEventChoice = 0;
- cgList = new ArrayList<>();
- stateToEventMap = new HashMap<>();
+ // Cycle tracking
+ currVisitedStates = new HashSet<>();
justVisitedStates = new HashSet<>();
prevVisitedStates = new HashSet<>();
- currVisitedStates = new HashSet<>();
- readWriteFieldsMap = new HashMap<>();
+ stateToEventMap = new HashMap<>();
+ // Backtracking
+ backtrackMap = new HashMap<>();
+ btrckPtsStack = new Stack<>();
+ btrckCGSet = new HashSet<>();
+ cgList = new ArrayList<>();
conflictPairMap = new HashMap<>();
+ readWriteFieldsMap = new HashMap<>();
+ // VOD graph
+ prevChoiceValue = -1;
+ vodGraphMap = new HashMap<>();
// Booleans
isBooleanCGFlipped = false;
}
// Record the max event choice (the last element of the choice array)
maxEventChoice = choices[choices.length - 1];
}
+ icsCG.setNewValues(choices);
+ icsCG.reset();
// Use a modulo since choiceCounter is going to keep increasing
int choiceIndex = choiceCounter % choices.length;
icsCG.advance(choices[choiceIndex]);
checkAndEnforceFairScheduling(icsCG);
// Map state to event
mapStateToEvent(icsCG.getNextChoice());
+ // Update the VOD graph always with the latest
+ updateVODGraph(icsCG.getNextChoice());
// Check if we have seen this state or this state contains cycles that involve all events
if (terminateCurrentExecution()) {
exploreNextBacktrackSets(icsCG);
ChoiceGenerator<?> cg = vm.getChoiceGenerator();
if (cg instanceof IntChoiceFromSet || cg instanceof IntIntervalGenerator) {
int currentChoice = choiceCounter - 1; // Accumulative choice w.r.t the current trace
- //if (getCurrentChoice(vm) < 0) { // If choice is -1 then skip
if (currentChoice < 0) { // If choice is -1 then skip
return;
}
+ currentChoice = checkAndAdjustChoice(currentChoice, vm);
// Record accesses from executed instructions
if (executedInsn instanceof JVMFieldInstruction) {
// Analyze only after being initialized
String fieldClass = ((JVMFieldInstruction) nextInsn).getFieldInfo().getFullName();
if (!isFieldExcluded(fieldClass)) {
// Check for conflict (go backward from current choice and get the first conflict)
- for (int evtCntr = currentChoice - 1; evtCntr >= 0; evtCntr--) {
- if (!readWriteFieldsMap.containsKey(evtCntr)) { // Skip if this event does not have any Read/Write set
- continue;
- }
- ReadWriteSet rwSet = readWriteFieldsMap.get(evtCntr);
- int currObjId = ((JVMFieldInstruction) nextInsn).getFieldInfo().getClassInfo().getClassObjectRef();
+ for (int eventCounter = currentChoice - 1; eventCounter >= 0; eventCounter--) {
// Check for conflicts with Write fields for both Read and Write instructions
- if (((nextInsn instanceof WriteInstruction || nextInsn instanceof ReadInstruction) &&
- rwSet.writeFieldExists(fieldClass) && rwSet.writeFieldObjectId(fieldClass) == currObjId) ||
- (nextInsn instanceof WriteInstruction && rwSet.readFieldExists(fieldClass) &&
- rwSet.readFieldObjectId(fieldClass) == currObjId)) {
- // Check and record a backtrack set for just once!
- if (successfullyRecordConflictPair(currentChoice, evtCntr)) {
- // Lines 4-8 of the algorithm in the paper page 11 (see the heading note above)
-// if (vm.isNewState() || isReachableInVODGraph(refChoices[currentChoice], refChoices[currentChoice-1])) {
-// createBacktrackChoiceList(currentChoice, eventNumber);
-// }
+ // Check and record a backtrack set for just once!
+ if (isConflictFound(nextInsn, eventCounter, fieldClass) && isNewConflict(currentChoice, eventCounter)) {
+ // Lines 4-8 of the algorithm in the paper page 11 (see the heading note above)
+ if (vm.isNewState() || isReachableInVODGraph(currentChoice)) {
+ createBacktrackingPoint(currentChoice, eventCounter);
}
}
}
// == HELPERS
- // -- INNER CLASS
+
+ // -- INNER CLASSES
+
// This class compactly stores Read and Write field sets
// We store the field name and its object ID
// Sharing the same field means the same field name and object ID
}
}
+ // This class compactly stores backtracking points: 1) backtracking ChoiceGenerator, and 2) backtracking choices
+ private class BacktrackPoint {
+ private IntChoiceFromSet backtrackCG; // CG to backtrack from
+ private Integer[] backtrackChoices; // Choices to set for this backtrack CG
+
+ public BacktrackPoint(IntChoiceFromSet cg, Integer[] choices) {
+ backtrackCG = cg;
+ backtrackChoices = choices;
+ }
+
+ public IntChoiceFromSet getBacktrackCG() {
+ return backtrackCG;
+ }
+
+ public Integer[] getBacktrackChoices() {
+ return backtrackChoices;
+ }
+ }
+
// -- CONSTANTS
private final static String DO_CALL_METHOD = "doCall";
// We exclude fields that come from libraries (Java and Groovy), and also the infrastructure
}
// Get the iterated object whose property is accessed
ElementInfo eiAccessObj = VM.getVM().getHeap().get(frameSlots[1]);
+ if (eiAccessObj == null) {
+ return;
+ }
// We exclude library classes (they start with java, org, etc.) and some more
String objClassName = eiAccessObj.getClassInfo().getName();
if (excludeThisForItStartsWith(EXCLUDED_FIELDS_STARTS_WITH_LIST, objClassName) ||
}
}
+ private int checkAndAdjustChoice(int currentChoice, VM vm) {
+ // If current choice is not the same, then this is caused by the firing of IntIntervalGenerator
+ // for certain method calls in the infrastructure, e.g., eventSince()
+ int currChoiceInd = currentChoice % refChoices.length;
+ int currChoiceFromCG = getCurrentChoice(vm);
+ if (currChoiceInd != currChoiceFromCG) {
+ currentChoice = (currentChoice - currChoiceInd) + currChoiceFromCG;
+ }
+ return currentChoice;
+ }
+
+ private void createBacktrackingPoint(int currentChoice, int confEvtNum) {
+
+ // Create a new list of choices for backtrack based on the current choice and conflicting event number
+ // E.g. if we have a conflict between 1 and 3, then we create the list {3, 1, 0, 2}
+ // for the original set {0, 1, 2, 3}
+ Integer[] newChoiceList = new Integer[refChoices.length];
+ // Put the conflicting event numbers first and reverse the order
+ int actualCurrCho = currentChoice % refChoices.length;
+ int actualConfEvtNum = confEvtNum % refChoices.length;
+ newChoiceList[0] = refChoices[actualCurrCho];
+ newChoiceList[1] = refChoices[actualConfEvtNum];
+ // Put the rest of the event numbers into the array starting from the minimum to the upper bound
+ for (int i = 0, j = 2; i < refChoices.length; i++) {
+ if (refChoices[i] != newChoiceList[0] && refChoices[i] != newChoiceList[1]) {
+ newChoiceList[j] = refChoices[i];
+ j++;
+ }
+ }
+ // Record the backtracking point in the stack as well
+ IntChoiceFromSet backtrackCG = cgList.get(confEvtNum);
+ BacktrackPoint backtrackPoint = new BacktrackPoint(backtrackCG, newChoiceList);
+ btrckPtsStack.push(backtrackPoint);
+ // Also record the CG in the set
+ btrckCGSet.add(backtrackCG);
+ }
+
private boolean excludeThisForItContains(String[] excludedStrings, String className) {
for (String excludedField : excludedStrings) {
if (className.contains(excludedField)) {
return false;
}
+ // TODO: THIS METHOD IS STILL UNTESTED AT THIS POINT
private void exploreNextBacktrackSets(IntChoiceFromSet icsCG) {
- // Save all the visited states when starting a new execution of trace
- prevVisitedStates.addAll(currVisitedStates);
- currVisitedStates.clear();
-
+ // We try to update the CG with a backtrack list if the state has been visited multiple times
+ if (icsCG.getNextChoiceIndex() > 0) {
+ if (btrckPtsStack.empty()) {
+ // TODO: PROBABLY NEED TO DO CONTEXT SWITCHING HERE
+ return;
+ }
+ BacktrackPoint backtrackPoint = btrckPtsStack.pop();
+ Integer[] choiceList = backtrackPoint.getBacktrackChoices();
+ IntChoiceFromSet backtrackCG = backtrackPoint.getBacktrackCG();
+ // Deploy the new choice list for this CG
+ backtrackCG.setNewValues(choiceList);
+ backtrackCG.reset();
+ // Clear unused CGs
+ for(IntChoiceFromSet cg : cgList) {
+ if (!btrckCGSet.contains(cg)) {
+ cg.setDone();
+ }
+ }
+ cgList.clear();
+ btrckCGSet.clear();
+ // Save all the visited states when starting a new execution of trace
+ prevVisitedStates.addAll(currVisitedStates);
+ currVisitedStates.clear();
+ }
}
private int getCurrentChoice(VM vm) {
return rwSet;
}
+ private boolean isConflictFound(Instruction nextInsn, int eventCounter, String fieldClass) {
+ // Skip if this event does not have any Read/Write set
+ if (!readWriteFieldsMap.containsKey(eventCounter)) {
+ return false;
+ }
+ ReadWriteSet rwSet = readWriteFieldsMap.get(eventCounter);
+ int currObjId = ((JVMFieldInstruction) nextInsn).getFieldInfo().getClassInfo().getClassObjectRef();
+ // Check for conflicts with Write fields for both Read and Write instructions
+ if (((nextInsn instanceof WriteInstruction || nextInsn instanceof ReadInstruction) &&
+ rwSet.writeFieldExists(fieldClass) && rwSet.writeFieldObjectId(fieldClass) == currObjId) ||
+ (nextInsn instanceof WriteInstruction && rwSet.readFieldExists(fieldClass) &&
+ rwSet.readFieldObjectId(fieldClass) == currObjId)) {
+ return true;
+ }
+ return false;
+ }
+
private boolean isFieldExcluded(String field) {
// Check against "starts-with", "ends-with", and "contains" list
if (excludeThisForItStartsWith(EXCLUDED_FIELDS_STARTS_WITH_LIST, field) ||
return false;
}
- private boolean successfullyRecordConflictPair(int currentEvent, int eventNumber) {
+ private boolean isNewConflict(int currentEvent, int eventNumber) {
HashSet<Integer> conflictSet;
if (!conflictPairMap.containsKey(currentEvent)) {
conflictSet = new HashSet<>();
conflictSet = conflictPairMap.get(currentEvent);
}
// If this conflict has been recorded before, we return false because
- // we don't want to service this backtrack point twice
+ // we don't want to save this backtrack point twice
if (conflictSet.contains(eventNumber)) {
return false;
}
conflictSet.add(eventNumber);
return true;
}
+
+ // --- Functions related to the visible operation dependency graph implementation discussed in the SPIN paper
+
+ // This method checks whether a choice is reachable in the VOD graph from a reference choice (BFS algorithm)
+ //private boolean isReachableInVODGraph(int checkedChoice, int referenceChoice) {
+ private boolean isReachableInVODGraph(int currentChoice) {
+ // Extract previous and current events
+ int choiceIndex = currentChoice % refChoices.length;
+ int currEvent = refChoices[choiceIndex];
+ int prevEvent = refChoices[choiceIndex - 1];
+ // Record visited choices as we search in the graph
+ HashSet<Integer> visitedChoice = new HashSet<>();
+ visitedChoice.add(prevEvent);
+ LinkedList<Integer> nodesToVisit = new LinkedList<>();
+ // If the state doesn't advance as the threads/sub-programs are executed (basically there is no new state),
+ // there is a chance that the graph doesn't have new nodes---thus this check will return a null.
+ if (vodGraphMap.containsKey(prevEvent)) {
+ nodesToVisit.addAll(vodGraphMap.get(prevEvent));
+ while(!nodesToVisit.isEmpty()) {
+ int choice = nodesToVisit.getFirst();
+ if (choice == currEvent) {
+ return true;
+ }
+ if (visitedChoice.contains(choice)) { // If there is a loop then we don't find it
+ return false;
+ }
+ // Continue searching
+ visitedChoice.add(choice);
+ HashSet<Integer> choiceNextNodes = vodGraphMap.get(choice);
+ if (choiceNextNodes != null) {
+ // Add only if there is a mapping for next nodes
+ for (Integer nextNode : choiceNextNodes) {
+ // Skip cycles
+ if (nextNode == choice) {
+ continue;
+ }
+ nodesToVisit.addLast(nextNode);
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private void updateVODGraph(int currChoiceValue) {
+ // Update the graph when we have the current choice value
+ HashSet<Integer> choiceSet;
+ if (vodGraphMap.containsKey(prevChoiceValue)) {
+ // If the key already exists, just retrieve it
+ choiceSet = vodGraphMap.get(prevChoiceValue);
+ } else {
+ // Create a new entry
+ choiceSet = new HashSet<>();
+ vodGraphMap.put(prevChoiceValue, choiceSet);
+ }
+ choiceSet.add(currChoiceValue);
+ prevChoiceValue = currChoiceValue;
+ }
}