import gov.nasa.jpf.Config;
import gov.nasa.jpf.JPF;
import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.INVOKEINTERFACE;
+import gov.nasa.jpf.jvm.bytecode.JVMFieldInstruction;
import gov.nasa.jpf.search.Search;
-import gov.nasa.jpf.jvm.bytecode.*;
import gov.nasa.jpf.vm.*;
import gov.nasa.jpf.vm.bytecode.ReadInstruction;
import gov.nasa.jpf.vm.bytecode.WriteInstruction;
import gov.nasa.jpf.vm.choice.IntIntervalGenerator;
import java.io.FileWriter;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.logging.Logger;
-import java.io.IOException;
/**
* This a DPOR implementation for event-driven applications with loops that create cycles of state matching
private int choiceCounter;
private int maxEventChoice;
// Data structure to track the events seen by each state to track cycles (containing all events) for termination
- private HashSet<Integer> currVisitedStates; // States being visited in the current execution
+ private HashMap<Integer,Integer> currVisitedStates; // States visited in the current execution (maps to frequency)
private HashSet<Integer> justVisitedStates; // States just visited in the previous choice/event
private HashSet<Integer> prevVisitedStates; // States visited in the previous execution
private HashSet<ClassInfo> nonRelevantClasses;// Class info objects of non-relevant classes
// Statistics
private int numOfTransitions;
-
+
public DPORStateReducer(Config config, JPF jpf) {
verboseMode = config.getBoolean("printout_state_transition", false);
stateReductionMode = config.getBoolean("activate_state_reduction", true);
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<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
choiceCounter = 0;
execution = null;
predecessors = new HashSet<>();
+ recordedPredecessors = new HashMap<>();
stateId = 0;
transitionCG = null;
}
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;
+ }
+
public void recordPredecessor(Execution execution, int choice) {
- predecessors.add(new Predecessor(choice, execution));
+ if (!isRecordedPredecessor(execution, choice)) {
+ predecessors.add(new Predecessor(choice, execution));
+ }
}
public void setChoice(int cho) {
choiceCounter = 0;
maxEventChoice = 0;
// Cycle tracking
- currVisitedStates = new HashSet<>();
+ currVisitedStates = new HashMap<>();
justVisitedStates = new HashSet<>();
prevVisitedStates = new HashSet<>();
stateToEventMap = new HashMap<>();
private boolean terminateCurrentExecution() {
// We need to check all the states that have just been visited
// Often a transition (choice/event) can result into forwarding/backtracking to a number of states
+ boolean terminate = false;
for(Integer stateId : justVisitedStates) {
- if (prevVisitedStates.contains(stateId) || completeFullCycle(stateId)) {
- return true;
+ // We only flip the value of terminate once ...
+ if (!terminate && prevVisitedStates.contains(stateId) || completeFullCycle(stateId)) {
+ updateBacktrackSetsFromGraph(stateId);
+ terminate = true;
+ }
+ // If frequency > 1 then this means we have visited this stateId more than once
+ if (currVisitedStates.containsKey(stateId) && currVisitedStates.get(stateId) > 1) {
+ updateBacktrackSetsFromGraph(stateId);
}
}
- return false;
+ return terminate;
}
private void updateStateInfo(Search search) {
HashSet<Integer> eventSet = new HashSet<>();
stateToEventMap.put(stateId, eventSet);
}
- analyzeReachabilityAndCreateBacktrackPoints(search.getVM(), stateId);
+ addPredecessorToRevisitedState(search.getVM(), stateId);
justVisitedStates.add(stateId);
if (!prevVisitedStates.contains(stateId)) {
// It is a currently visited states if the state has not been seen in previous executions
- currVisitedStates.add(stateId);
+ int frequency = 0;
+ if (currVisitedStates.containsKey(stateId)) {
+ frequency = currVisitedStates.get(stateId);
+ }
+ currVisitedStates.put(stateId, frequency + 1); // Increment frequency counter
}
}
// 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}
+
+ // execution/currentChoice represent the event/transaction that will be put into the backtracking set of
+ // conflictExecution/conflictChoice
Integer[] newChoiceList = new Integer[refChoices.length];
ArrayList<TransitionEvent> currentTrace = execution.getExecutionTrace();
ArrayList<TransitionEvent> conflictTrace = conflictExecution.getExecutionTrace();
icsCG.setDone();
}
// Save all the visited states when starting a new execution of trace
- prevVisitedStates.addAll(currVisitedStates);
+ prevVisitedStates.addAll(currVisitedStates.keySet());
// This marks a transitional period to the new CG
isEndOfExecution = true;
}
private boolean isConflictFound(Execution execution, int reachableChoice, Execution conflictExecution, int conflictChoice,
ReadWriteSet currRWSet) {
+ // conflictExecution/conflictChoice represent a predecessor event/transaction that can potentially have a conflict
ArrayList<TransitionEvent> executionTrace = execution.getExecutionTrace();
ArrayList<TransitionEvent> conflictTrace = conflictExecution.getExecutionTrace();
HashMap<Integer, ReadWriteSet> confRWFieldsMap = conflictExecution.getReadWriteFieldsMap();
choices = icsCG.getAllChoices();
refChoices = copyChoices(choices);
// Clear data structures
- currVisitedStates = new HashSet<>();
+ currVisitedStates = new HashMap<>();
stateToEventMap = new HashMap<>();
isEndOfExecution = false;
}
// 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, execution, currentChoice, currRWSet, visited);
+ updateBacktrackSetRecursive(execution, currentChoice, execution, currentChoice, currRWSet, visited);
}
-// 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
private void updateBacktrackSetRecursive(Execution execution, int currentChoice,
Execution conflictExecution, int conflictChoice,
- Execution hbExecution, int hbChoice,
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);
+ TransitionEvent currTrans = execution.getExecutionTrace().get(currentChoice);
// Halt when we have visited this transition (in a cycle)
- if (visited.contains(confTrans)) {
+ if (visited.contains(currTrans)) {
return;
}
- visited.add(confTrans);
+ visited.add(currTrans);
// Explore all predecessors
- for (Predecessor predecessor : confTrans.getPredecessors()) {
+ for (Predecessor predecessor : currTrans.getPredecessors()) {
// Get the predecessor (previous conflict choice)
- conflictChoice = predecessor.getChoice();
- conflictExecution = predecessor.getExecution();
+ int predecessorChoice = predecessor.getChoice();
+ Execution predecessorExecution = predecessor.getExecution();
// Push up one happens-before transition
- int pushedChoice = hbChoice;
- Execution pushedExecution = hbExecution;
+ int newConflictChoice = conflictChoice;
+ Execution newConflictExecution = conflictExecution;
// Check if a conflict is found
- if (isConflictFound(execution, currentChoice, conflictExecution, conflictChoice, currRWSet)) {
- createBacktrackingPoint(pushedExecution, pushedChoice, conflictExecution, conflictChoice);
- pushedChoice = conflictChoice;
- pushedExecution = conflictExecution;
+ if (isConflictFound(conflictExecution, conflictChoice, predecessorExecution, predecessorChoice, currRWSet)) {
+ createBacktrackingPoint(conflictExecution, conflictChoice, predecessorExecution, predecessorChoice);
+ newConflictChoice = predecessorChoice;
+ newConflictExecution = predecessorExecution;
}
// Continue performing DFS if conflict is not found
- updateBacktrackSetRecursive(execution, currentChoice, conflictExecution, conflictChoice,
- pushedExecution, pushedChoice, currRWSet, visited);
+ updateBacktrackSetRecursive(predecessorExecution, predecessorChoice, newConflictExecution, newConflictChoice,
+ currRWSet, visited);
}
- // 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
- private void analyzeReachabilityAndCreateBacktrackPoints(VM vm, int stateId) {
+ private void addPredecessorToRevisitedState(VM vm, int stateId) {
// Perform this analysis only when:
// 1) this is not during a switch to a new execution,
// 2) at least 2 choices/events have been explored (choiceCounter > 1),
// 3) state > 0 (state 0 is for boolean CG)
if (!isEndOfExecution && choiceCounter > 1 && stateId > 0) {
- if (currVisitedStates.contains(stateId) || prevVisitedStates.contains(stateId)) {
+ if ((currVisitedStates.containsKey(stateId) && currVisitedStates.get(stateId) > 1) ||
+ prevVisitedStates.contains(stateId)) {
// Update reachable transitions in the graph with a predecessor
HashSet<TransitionEvent> reachableTransitions = rGraph.getReachableTransitionsAtState(stateId);
- for(TransitionEvent transition : reachableTransitions) {
+ for (TransitionEvent transition : reachableTransitions) {
transition.recordPredecessor(currentExecution, choiceCounter - 1);
}
- updateBacktrackSetsFromPreviousExecution(stateId);
}
}
}
// Update the backtrack sets from previous executions
- private void updateBacktrackSetsFromPreviousExecution(int stateId) {
+ private void updateBacktrackSetsFromGraph(int stateId) {
// Collect all the reachable transitions from R-Graph
HashSet<TransitionEvent> reachableTransitions = rGraph.getReachableTransitions(stateId);
for(TransitionEvent transition : reachableTransitions) {