Fixing bug due to refactoring/moving of code in the previous commit to handle apps...
[jpf-core.git] / src / main / gov / nasa / jpf / listener / DPORStateReducerEfficient.java
index 876d34fd1ae45479ebfe36db0eb66dda2ecdc62f..4867d0bdad33ba82610b0cac32a71596528fce63 100644 (file)
@@ -373,7 +373,7 @@ public class DPORStateReducerEfficient extends ListenerAdapter {
     private int choice;           // Predecessor choice
     private Execution execution;  // Predecessor execution
 
-    public Predecessor(int predChoice, Execution predExec) {
+    public Predecessor(Execution predExec, int predChoice) {
       choice = predChoice;
       execution = predExec;
     }
@@ -399,6 +399,7 @@ public class DPORStateReducerEfficient extends ListenerAdapter {
     }
 
     public void addReachableTransition(int stateId, TransitionEvent transition) {
+      // Record transition into graph
       HashSet<TransitionEvent> transitionSet;
       if (graph.containsKey(stateId)) {
         transitionSet = graph.get(stateId);
@@ -425,19 +426,6 @@ public class DPORStateReducerEfficient extends ListenerAdapter {
       }
       return graph.get(stateId);
     }
-
-    public HashSet<TransitionEvent> getReachableTransitions(int stateId) {
-      HashSet<TransitionEvent> reachableTransitions = new HashSet<>();
-      // All transitions from states higher than the given state ID (until the highest state ID) are reachable
-      for(int stId = stateId; stId <= hiStateId; stId++) {
-        // We might encounter state IDs from the first round of Boolean CG
-        // The second round of Boolean CG should consider these new states
-        if (graph.containsKey(stId)) {
-          reachableTransitions.addAll(graph.get(stId));
-        }
-      }
-      return reachableTransitions;
-    }
   }
 
   // This class compactly stores Read and Write field sets
@@ -521,6 +509,25 @@ public class DPORStateReducerEfficient extends ListenerAdapter {
     }
   }
 
+  // This class provides a data structure to store TransitionEvent and ReadWriteSet for a summary
+  private class SummaryNode {
+    private TransitionEvent transitionEvent;
+    private ReadWriteSet readWriteSet;
+
+    public SummaryNode(TransitionEvent transEvent, ReadWriteSet rwSet) {
+      transitionEvent = transEvent;
+      readWriteSet = rwSet;
+    }
+
+    public TransitionEvent getTransitionEvent() {
+      return transitionEvent;
+    }
+
+    public ReadWriteSet getReadWriteSet() {
+      return readWriteSet;
+    }
+  }
+
   // This class compactly stores transitions:
   // 1) CG,
   // 2) state ID,
@@ -531,6 +538,10 @@ public class DPORStateReducerEfficient extends ListenerAdapter {
     private int choiceCounter;                 // Choice counter at this transition
     private Execution execution;               // The execution where this transition belongs
     private HashSet<Predecessor> predecessors; // Maps incoming events/transitions (execution and choice)
+    private HashMap<Integer, SummaryNode> transitionSummary;
+                                               // Summary of pushed transitions at the current transition
+    private HashMap<Execution, HashSet<Integer>> recordedPredecessors;
+                                               // Memorize event and choice number to not record them twice
     private int stateId;                       // State at this transition
     private IntChoiceFromSet transitionCG;     // CG at this transition
 
@@ -539,6 +550,8 @@ public class DPORStateReducerEfficient extends ListenerAdapter {
       choiceCounter = 0;
       execution = null;
       predecessors = new HashSet<>();
+      transitionSummary = new HashMap<>();
+      recordedPredecessors = new HashMap<>();
       stateId = 0;
       transitionCG = null;
     }
@@ -563,10 +576,82 @@ public class DPORStateReducerEfficient extends ListenerAdapter {
       return stateId;
     }
 
+    public HashMap<Integer, SummaryNode> getTransitionSummary() {
+      return transitionSummary;
+    }
+
     public IntChoiceFromSet getTransitionCG() { return transitionCG; }
 
+    private boolean isRecordedPredecessor(Execution execution, int choice) {
+      // See if we have recorded this predecessor earlier
+      HashSet<Integer> recordedChoices;
+      if (recordedPredecessors.containsKey(execution)) {
+        recordedChoices = recordedPredecessors.get(execution);
+        if (recordedChoices.contains(choice)) {
+          return true;
+        }
+      } else {
+        recordedChoices = new HashSet<>();
+        recordedPredecessors.put(execution, recordedChoices);
+      }
+      // Record the choice if we haven't seen it
+      recordedChoices.add(choice);
+
+      return false;
+    }
+
+    private ReadWriteSet performUnion(ReadWriteSet recordedRWSet, ReadWriteSet rwSet) {
+      // Combine the same write accesses and record in the recordedRWSet
+      HashMap<String, Integer> recordedWriteMap = recordedRWSet.getWriteMap();
+      HashMap<String, Integer> writeMap = rwSet.getWriteMap();
+      for(Map.Entry<String, Integer> entry : recordedWriteMap.entrySet()) {
+        String writeField = entry.getKey();
+        // Remove the entry from rwSet if both field and object ID are the same
+        if (writeMap.containsKey(writeField) &&
+                (writeMap.get(writeField).equals(recordedWriteMap.get(writeField)))) {
+          writeMap.remove(writeField);
+        }
+      }
+      // Then add everything into the recorded map because these will be traversed
+      recordedWriteMap.putAll(writeMap);
+      // Combine the same read accesses and record in the recordedRWSet
+      HashMap<String, Integer> recordedReadMap = recordedRWSet.getReadMap();
+      HashMap<String, Integer> readMap = rwSet.getReadMap();
+      for(Map.Entry<String, Integer> entry : recordedReadMap.entrySet()) {
+        String readField = entry.getKey();
+        // Remove the entry from rwSet if both field and object ID are the same
+        if (readMap.containsKey(readField) &&
+                (readMap.get(readField).equals(recordedReadMap.get(readField)))) {
+          readMap.remove(readField);
+        }
+      }
+      // Then add everything into the recorded map because these will be traversed
+      recordedReadMap.putAll(readMap);
+
+      return rwSet;
+    }
+
     public void recordPredecessor(Execution execution, int choice) {
-      predecessors.add(new Predecessor(choice, execution));
+      if (!isRecordedPredecessor(execution, choice)) {
+        predecessors.add(new Predecessor(execution, choice));
+      }
+    }
+
+    public ReadWriteSet recordTransitionSummary(TransitionEvent transition, ReadWriteSet rwSet, boolean refresh) {
+      // Record transition into reachability summary
+      // TransitionMap maps event (choice) number to a R/W set
+      int choice = transition.getChoice();
+      SummaryNode summaryNode;
+      // Insert transition into the map if we haven't had this event number recorded
+      if (!transitionSummary.containsKey(choice) || refresh) {
+        summaryNode = new SummaryNode(transition, rwSet.getCopy());
+        transitionSummary.put(choice, summaryNode);
+      } else {
+        summaryNode = transitionSummary.get(choice);
+        // Perform union and subtraction between the recorded and the given R/W sets
+        rwSet = performUnion(summaryNode.getReadWriteSet(), rwSet);
+      }
+      return rwSet;
     }
 
     public void setChoice(int cho) {
@@ -1100,54 +1185,26 @@ public class DPORStateReducerEfficient extends ListenerAdapter {
     // Memorize visited TransitionEvent object while performing backward DFS to avoid getting caught up in a cycle
     HashSet<TransitionEvent> visited = new HashSet<>();
     // Update backtrack set recursively
-    // TODO: The following is the call to the original version of the method
-//    updateBacktrackSetRecursive(execution, currentChoice, execution, currentChoice, currRWSet, visited);
-    // TODO: The following is the call to the version of the method with pushing up happens-before transitions
-    updateBacktrackSetRecursive(execution, currentChoice, execution, currentChoice, currRWSet, visited);
+    updateBacktrackSetRecursive(execution, currentChoice, execution, currentChoice, currRWSet, visited, false);
   }
 
-//  TODO: This is the original version of the recursive method
-//  private void updateBacktrackSetRecursive(Execution execution, int currentChoice,
-//                                           Execution conflictExecution, int conflictChoice,
-//                                           ReadWriteSet currRWSet, HashSet<TransitionEvent> visited) {
-//    // Halt when we have found the first read/write conflicts for all memory locations
-//    if (currRWSet.isEmpty()) {
-//      return;
-//    }
-//    TransitionEvent confTrans = conflictExecution.getExecutionTrace().get(conflictChoice);
-//    // Halt when we have visited this transition (in a cycle)
-//    if (visited.contains(confTrans)) {
-//      return;
-//    }
-//    visited.add(confTrans);
-//    // Explore all predecessors
-//    for (Predecessor predecessor : confTrans.getPredecessors()) {
-//      // Get the predecessor (previous conflict choice)
-//      conflictChoice = predecessor.getChoice();
-//      conflictExecution = predecessor.getExecution();
-//      // Check if a conflict is found
-//      if (isConflictFound(execution, currentChoice, conflictExecution, conflictChoice, currRWSet)) {
-//        createBacktrackingPoint(execution, currentChoice, conflictExecution, conflictChoice);
-//      }
-//      // Continue performing DFS if conflict is not found
-//      updateBacktrackSetRecursive(execution, currentChoice, conflictExecution, conflictChoice, currRWSet, visited);
-//    }
-//  }
-
-  // TODO: This is the version of the method with pushing up happens-before transitions
+  // Recursive method to perform backward DFS to traverse the graph
   private void updateBacktrackSetRecursive(Execution execution, int currentChoice,
                                            Execution conflictExecution, int conflictChoice,
-                                           ReadWriteSet currRWSet, HashSet<TransitionEvent> visited) {
-    // Halt when we have found the first read/write conflicts for all memory locations
-    if (currRWSet.isEmpty()) {
-      return;
-    }
+                                           ReadWriteSet currRWSet, HashSet<TransitionEvent> visited,
+                                           boolean refresh) {
     TransitionEvent currTrans = execution.getExecutionTrace().get(currentChoice);
-    // Halt when we have visited this transition (in a cycle)
     if (visited.contains(currTrans)) {
       return;
     }
     visited.add(currTrans);
+    TransitionEvent confTrans = conflictExecution.getExecutionTrace().get(conflictChoice);
+    // Record this transition into rGraph summary
+    currRWSet = currTrans.recordTransitionSummary(confTrans, currRWSet, refresh);
+    // Halt when we have found the first read/write conflicts for all memory locations
+    if (currRWSet.isEmpty()) {
+      return;
+    }
     // Explore all predecessors
     for (Predecessor predecessor : currTrans.getPredecessors()) {
       // Get the predecessor (previous conflict choice)
@@ -1159,16 +1216,13 @@ public class DPORStateReducerEfficient extends ListenerAdapter {
       // Check if a conflict is found
       if (isConflictFound(conflictExecution, conflictChoice, predecessorExecution, predecessorChoice, currRWSet)) {
         createBacktrackingPoint(conflictExecution, conflictChoice, predecessorExecution, predecessorChoice);
-        newConflictChoice = conflictChoice;
-        newConflictExecution = conflictExecution;
+        newConflictChoice = predecessorChoice;
+        newConflictExecution = predecessorExecution;
       }
       // Continue performing DFS if conflict is not found
       updateBacktrackSetRecursive(predecessorExecution, predecessorChoice, newConflictExecution, newConflictChoice,
-              currRWSet, visited);
+              currRWSet, visited, refresh);
     }
-    // Remove the transition after being explored
-    // TODO: Seems to cause a lot of loops---commented out for now
-    //visited.remove(confTrans);
   }
 
   // --- Functions related to the reachability analysis when there is a state match
@@ -1190,14 +1244,28 @@ public class DPORStateReducerEfficient extends ListenerAdapter {
     }
   }
 
-  // Update the backtrack sets from previous executions
   private void updateBacktrackSetsFromPreviousExecution(int stateId) {
     // Collect all the reachable transitions from R-Graph
-    HashSet<TransitionEvent> reachableTransitions = rGraph.getReachableTransitions(stateId);
+    HashSet<TransitionEvent> reachableTransitions = rGraph.getReachableTransitionsAtState(stateId);
     for(TransitionEvent transition : reachableTransitions) {
-      Execution execution = transition.getExecution();
+      // Current transition that stems from this state ID
+      Execution currentExecution = transition.getExecution();
       int currentChoice = transition.getChoiceCounter();
-      updateBacktrackSet(execution, currentChoice);
+      // Iterate over the stored conflict transitions in the summary
+      for(Map.Entry<Integer, SummaryNode> conflictTransition : transition.getTransitionSummary().entrySet()) {
+        SummaryNode summaryNode = conflictTransition.getValue();
+        // Conflict transition in the summary node
+        TransitionEvent confTrans = summaryNode.getTransitionEvent();
+        Execution conflictExecution = confTrans.getExecution();
+        int conflictChoice = confTrans.getChoiceCounter();
+        // Copy ReadWriteSet object
+        ReadWriteSet currRWSet = summaryNode.getReadWriteSet();
+        currRWSet = currRWSet.getCopy();
+        // Memorize visited TransitionEvent object while performing backward DFS to avoid getting caught up in a cycle
+        HashSet<TransitionEvent> visited = new HashSet<>();
+        updateBacktrackSetRecursive(currentExecution, currentChoice, conflictExecution,
+                conflictChoice, currRWSet, visited, true);
+      }
     }
   }
 }