Printing out the number of transitions.
[jpf-core.git] / src / main / gov / nasa / jpf / listener / DPORStateReducer.java
index e613f09fb15609ebc3f45e9f337351dbc1a80e06..a35286454e3025d6311d0b50c72cb7ab0f538082 100644 (file)
@@ -74,10 +74,12 @@ public class DPORStateReducer extends ListenerAdapter {
   private ArrayList<BacktrackPoint> backtrackPointList;           // Record backtrack points (CG, state Id, and choice)
   private HashMap<Integer, HashSet<Integer>> conflictPairMap;     // Record conflicting events
   private HashSet<String> doneBacktrackSet;                       // Record state ID and trace already constructed
+  private HashMap<Integer,Integer> newStateEventMap;              // Record event producing a new state ID
   private HashMap<Integer, ReadWriteSet> readWriteFieldsMap;      // Record fields that are accessed
   private HashMap<Integer, RestorableVMState> restorableStateMap; // Maps state IDs to the restorable state object
 
   // Visible operation dependency graph implementation (SPIN paper) related fields
+  private int currChoiceValue;
   private int prevChoiceValue;
   private HashMap<Integer, HashSet<Integer>> vodGraphMap; // Visible operation dependency graph (VOD graph)
 
@@ -85,6 +87,9 @@ public class DPORStateReducer extends ListenerAdapter {
   private boolean isBooleanCGFlipped;
   private boolean isEndOfExecution;
 
+  // Statistics
+  private int numOfTransitions;        
+       
   public DPORStateReducer(Config config, JPF jpf) {
     verboseMode = config.getBoolean("printout_state_transition", false);
     stateReductionMode = config.getBoolean("activate_state_reduction", true);
@@ -94,6 +99,7 @@ public class DPORStateReducer extends ListenerAdapter {
       out = null;
     }
     isBooleanCGFlipped = false;
+               numOfTransitions = 0;
     restorableStateMap = new HashMap<>();
     initializeStatesVariables();
   }
@@ -160,6 +166,9 @@ public class DPORStateReducer extends ListenerAdapter {
   @Override
   public void searchFinished(Search search) {
     if (verboseMode) {
+      out.println("\n==> DEBUG: ----------------------------------- search finished");
+      out.println("\n==> DEBUG: State reduction mode  : " + stateReductionMode);
+      out.println("\n==> DEBUG: Number of transitions : " + numOfTransitions);
       out.println("\n==> DEBUG: ----------------------------------- search finished" + "\n");
     }
   }
@@ -216,15 +225,19 @@ public class DPORStateReducer extends ListenerAdapter {
         fairSchedulingAndBacktrackPoint(icsCG, vm);
         // 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()) {
+        // Explore the next backtrack point: 
+        // 1) if we have seen this state or this state contains cycles that involve all events, and
+        // 2) after the current CG is advanced at least once
+        if (terminateCurrentExecution() && choiceCounter > 0) {
           exploreNextBacktrackPoints(vm, icsCG);
+        } else {
+          numOfTransitions++;
         }
         justVisitedStates.clear();
         choiceCounter++;
       }
+    } else {
+      numOfTransitions++;
     }
   }
 
@@ -265,7 +278,7 @@ public class DPORStateReducer extends ListenerAdapter {
                   if (isConflictFound(nextInsn, eventCounter, currentChoice, 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)) {
+                    if (vm.isNewState() || isReachableInVODGraph(currentChoice, vm)) {
                       createBacktrackingPoint(currentChoice, eventCounter);
                     }
                   }
@@ -375,8 +388,14 @@ public class DPORStateReducer extends ListenerAdapter {
         icsCG.setChoice(currCGIndex, expectedChoice);
       }
     }
+    // Update current choice
+    currChoiceValue = refChoices[choiceIndex];
     // Record state ID and choice/event as backtrack point
-    backtrackPointList.add(new BacktrackPoint(icsCG, vm.getStateId(), refChoices[choiceIndex]));
+    int stateId = vm.getStateId();
+    backtrackPointList.add(new BacktrackPoint(icsCG, stateId, refChoices[choiceIndex]));
+    // Store restorable state object for this state (always store the latest)
+    RestorableVMState restorableState = vm.getRestorableState();
+    restorableStateMap.put(stateId, restorableState);
   }
 
   private Integer[] copyChoices(Integer[] choicesToCopy) {
@@ -428,8 +447,10 @@ public class DPORStateReducer extends ListenerAdapter {
     backtrackPointList = new ArrayList<>();
     conflictPairMap = new HashMap<>();
     doneBacktrackSet = new HashSet<>();
+    newStateEventMap = new HashMap<>();
     readWriteFieldsMap = new HashMap<>();
     // VOD graph
+    currChoiceValue = 0;
     prevChoiceValue = -1;
     vodGraphMap = new HashMap<>();
     // Booleans
@@ -468,9 +489,8 @@ public class DPORStateReducer extends ListenerAdapter {
       stateToEventMap.put(stateId, eventSet);
     }
     justVisitedStates.add(stateId);
-    // Store restorable state object for this state (always store the latest)
-    RestorableVMState restorableState = search.getVM().getRestorableState();
-    restorableStateMap.put(stateId, restorableState);
+    // Update the VOD graph when there is a new state
+    updateVODGraph(search.getVM());
   }
 
   // --- Functions related to Read/Write access analysis on shared fields
@@ -555,15 +575,20 @@ public class DPORStateReducer extends ListenerAdapter {
     // 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 = 0;
+    int currChoiceFromCG = currChoiceInd;
     ChoiceGenerator<?> currentCG = vm.getChoiceGenerator();
     // This is the main event CG
-    if (currentCG instanceof IntChoiceFromSet) {
-      currChoiceFromCG = currChoiceInd;
-    } else {
+    if (currentCG instanceof IntIntervalGenerator) {
       // This is the interval CG used in device handlers
       ChoiceGenerator<?> parentCG = ((IntIntervalGenerator) currentCG).getPreviousChoiceGenerator();
-      currChoiceFromCG = ((IntChoiceFromSet) parentCG).getNextChoiceIndex();
+      int actualEvtNum = ((IntChoiceFromSet) parentCG).getNextChoice();
+      // Find the index of the event/choice in refChoices
+      for (int i = 0; i<refChoices.length; i++) {
+        if (actualEvtNum == refChoices[i]) {
+          currChoiceFromCG = i;
+          break;
+        }
+      }
     }
     if (currChoiceInd != currChoiceFromCG) {
       currentChoice = (currentChoice - currChoiceInd) + currChoiceFromCG;
@@ -595,7 +620,6 @@ public class DPORStateReducer extends ListenerAdapter {
     if (isTraceAlreadyConstructed(newChoiceList, stateId)) {
       return;
     }
-    //BacktrackPoint backtrackPoint = new BacktrackPoint(backtrackCG, newChoiceList);
     addNewBacktrackPoint(stateId, newChoiceList);
   }
 
@@ -628,35 +652,32 @@ public class DPORStateReducer extends ListenerAdapter {
 
   private void exploreNextBacktrackPoints(VM vm, IntChoiceFromSet icsCG) {
 
-    // We can start exploring the next backtrack point after the current CG is advanced at least once
-    if (choiceCounter > 0) {
-      // Check if we are reaching the end of our execution: no more backtracking points to explore
-      // cgMap, backtrackMap, backtrackStateQ are updated simultaneously (checking backtrackStateQ is enough)
-      if (!backtrackStateQ.isEmpty()) {
-        // Set done all the other backtrack points
-        for (BacktrackPoint backtrackPoint : backtrackPointList) {
-          backtrackPoint.getBacktrackCG().setDone();
-        }
-        // Reset the next backtrack point with the latest state
-        int hiStateId = backtrackStateQ.peek();
-        // Restore the state first if necessary
-        if (vm.getStateId() != hiStateId) {
-          RestorableVMState restorableState = restorableStateMap.get(hiStateId);
-          vm.restoreState(restorableState);
-        }
-        // Set the backtrack CG
-        IntChoiceFromSet backtrackCG = (IntChoiceFromSet) vm.getChoiceGenerator();
-        setBacktrackCG(hiStateId, backtrackCG);
-      } else {
-        // Set done this last CG (we save a few rounds)
-        icsCG.setDone();
-      }
-      // Save all the visited states when starting a new execution of trace
-      prevVisitedStates.addAll(currVisitedStates);
-      currVisitedStates.clear();
-      // This marks a transitional period to the new CG
-      isEndOfExecution = true;
-    }
+               // Check if we are reaching the end of our execution: no more backtracking points to explore
+               // cgMap, backtrackMap, backtrackStateQ are updated simultaneously (checking backtrackStateQ is enough)
+               if (!backtrackStateQ.isEmpty()) {
+                       // Set done all the other backtrack points
+                       for (BacktrackPoint backtrackPoint : backtrackPointList) {
+                               backtrackPoint.getBacktrackCG().setDone();
+                       }
+                       // Reset the next backtrack point with the latest state
+                       int hiStateId = backtrackStateQ.peek();
+                       // Restore the state first if necessary
+                       if (vm.getStateId() != hiStateId) {
+                               RestorableVMState restorableState = restorableStateMap.get(hiStateId);
+                               vm.restoreState(restorableState);
+                       }
+                       // Set the backtrack CG
+                       IntChoiceFromSet backtrackCG = (IntChoiceFromSet) vm.getChoiceGenerator();
+                       setBacktrackCG(hiStateId, backtrackCG);
+               } else {
+                       // Set done this last CG (we save a few rounds)
+                       icsCG.setDone();
+               }
+               // Save all the visited states when starting a new execution of trace
+               prevVisitedStates.addAll(currVisitedStates);
+               currVisitedStates.clear();
+               // This marks a transitional period to the new CG
+               isEndOfExecution = true;
   }
 
   private ReadWriteSet getReadWriteSet(int currentChoice) {
@@ -769,29 +790,32 @@ public class DPORStateReducer extends ListenerAdapter {
 
   // --- 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
+  // This method checks whether a choice/event (transition) is reachable from the choice/event that produces
+  // the state right before this state in the VOD graph
+  // We use a BFS algorithm for this purpose
+  private boolean isReachableInVODGraph(int currentChoice, VM vm) {
+    // Current event
     int choiceIndex = currentChoice % refChoices.length;
-    int prevChoIndex = (currentChoice - 1) % refChoices.length;
     int currEvent = refChoices[choiceIndex];
-    int prevEvent = refChoices[prevChoIndex];
-    // 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.
+    // Previous event
+    int stateId = vm.getStateId();  // A state that has been seen
+    int prevEvent = newStateEventMap.get(stateId);
+    // Only start traversing the graph if prevEvent has an outgoing edge
     if (vodGraphMap.containsKey(prevEvent)) {
+      // Record visited choices as we search in the graph
+      HashSet<Integer> visitedChoice = new HashSet<>();
+      visitedChoice.add(prevEvent);
+      // Get the first nodes to visit (the neighbors of prevEvent)
+      LinkedList<Integer> nodesToVisit = new LinkedList<>();
       nodesToVisit.addAll(vodGraphMap.get(prevEvent));
-      while(!nodesToVisit.isEmpty()) {
-        int choice = nodesToVisit.getFirst();
+      // Traverse the graph using BFS
+      while (!nodesToVisit.isEmpty()) {
+        int choice = nodesToVisit.removeFirst();
         if (choice == currEvent) {
           return true;
         }
-        if (visitedChoice.contains(choice)) { // If there is a loop then we don't find it
-          return false;
+        if (visitedChoice.contains(choice)) { // If there is a loop then just continue the exploration
+          continue;
         }
         // Continue searching
         visitedChoice.add(choice);
@@ -811,18 +835,23 @@ public class DPORStateReducer extends ListenerAdapter {
     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);
+  private void updateVODGraph(VM vm) {
+    // Do this only if it is a new state
+    if (vm.isNewState()) {
+      // 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;
+      // Map this state ID to the event (transition) that produces it
+      newStateEventMap.put(vm.getStateId(), currChoiceValue);
     }
-    choiceSet.add(currChoiceValue);
-    prevChoiceValue = currChoiceValue;
   }
 }