X-Git-Url: http://plrg.eecs.uci.edu/git/?p=jpf-core.git;a=blobdiff_plain;f=src%2Fmain%2Fgov%2Fnasa%2Fjpf%2Flistener%2FDPORStateReducer.java;h=9ce2c3dea6bffb7bdd0501604478e3b48ff12549;hp=f503dfa92f5ff9556f4b7e83b302acbf577594d7;hb=ff88f5a973e34aca737f95bbe85193447d69c134;hpb=9ec360a1ff7dab0435778fc07216add56ebb0c9e diff --git a/src/main/gov/nasa/jpf/listener/DPORStateReducer.java b/src/main/gov/nasa/jpf/listener/DPORStateReducer.java old mode 100644 new mode 100755 index f503dfa..9ce2c3d --- a/src/main/gov/nasa/jpf/listener/DPORStateReducer.java +++ b/src/main/gov/nasa/jpf/listener/DPORStateReducer.java @@ -20,8 +20,9 @@ package gov.nasa.jpf.listener; 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; @@ -29,10 +30,10 @@ import gov.nasa.jpf.vm.choice.IntChoiceFromSet; 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 @@ -59,27 +60,28 @@ public class DPORStateReducer extends ListenerAdapter { 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 currVisitedStates; // States being visited in the current execution - private HashSet justVisitedStates; // States just visited in the previous choice/event - private HashSet prevVisitedStates; // States visited in the previous execution + private HashSet currVisitedStates; // States being visited in the current execution + private HashSet justVisitedStates; // States just visited in the previous choice/event + private HashSet prevVisitedStates; // States visited in the previous execution + private HashSet nonRelevantClasses;// Class info objects of non-relevant classes + private HashSet nonRelevantFields; // Field info objects of non-relevant fields + private HashSet relevantFields; // Field info objects of relevant fields private HashMap> stateToEventMap; // Data structure to analyze field Read/Write accesses and conflicts private HashMap> backtrackMap; // Track created backtracking points private PriorityQueue backtrackStateQ; // Heap that returns the latest state private Execution currentExecution; // Holds the information about the current execution - private HashSet doneBacktrackSet; // Record state ID and trace already constructed + private HashMap> doneBacktrackMap; // Record state ID and trace already constructed private HashMap restorableStateMap; // Maps state IDs to the restorable state object - private HashMap stateToChoiceCounterMap; // Maps state IDs to the choice counter - private HashMap> rGraph; // Reachability graph for past executions + private RGraph rGraph; // R-Graph for past executions // Boolean states private boolean isBooleanCGFlipped; private boolean isEndOfExecution; // Statistics - private int numOfConflicts; private int numOfTransitions; - + public DPORStateReducer(Config config, JPF jpf) { verboseMode = config.getBoolean("printout_state_transition", false); stateReductionMode = config.getBoolean("activate_state_reduction", true); @@ -96,8 +98,10 @@ public class DPORStateReducer extends ListenerAdapter { } } isBooleanCGFlipped = false; - numOfConflicts = 0; numOfTransitions = 0; + nonRelevantClasses = new HashSet<>(); + nonRelevantFields = new HashSet<>(); + relevantFields = new HashSet<>(); restorableStateMap = new HashMap<>(); initializeStatesVariables(); } @@ -141,8 +145,6 @@ public class DPORStateReducer extends ListenerAdapter { " which is " + detail + " Transition: " + transition + "\n"); } if (stateReductionMode) { - // Only add a transition into R-Graph when it advances the state - addTransitionToRGRaph(); updateStateInfo(search); } } @@ -167,19 +169,13 @@ public class DPORStateReducer extends ListenerAdapter { @Override public void searchFinished(Search search) { - if (stateReductionMode) { - // Number of conflicts = first trace + subsequent backtrack points - numOfConflicts += 1 + doneBacktrackSet.size(); - } if (verboseMode) { out.println("\n==> DEBUG: ----------------------------------- search finished"); out.println("\n==> DEBUG: State reduction mode : " + stateReductionMode); - out.println("\n==> DEBUG: Number of conflicts : " + numOfConflicts); out.println("\n==> DEBUG: Number of transitions : " + numOfTransitions); out.println("\n==> DEBUG: ----------------------------------- search finished" + "\n"); fileWriter.println("==> DEBUG: State reduction mode : " + stateReductionMode); - fileWriter.println("==> DEBUG: Number of conflicts : " + numOfConflicts); fileWriter.println("==> DEBUG: Number of transitions : " + numOfTransitions); fileWriter.println(); fileWriter.close(); @@ -192,6 +188,8 @@ public class DPORStateReducer extends ListenerAdapter { // Initialize with necessary information from the CG if (nextCG instanceof IntChoiceFromSet) { IntChoiceFromSet icsCG = (IntChoiceFromSet) nextCG; + // Tell JPF that we are performing DPOR + icsCG.setDpor(); if (!isEndOfExecution) { // Check if CG has been initialized, otherwise initialize it Integer[] cgChoices = icsCG.getAllChoices(); @@ -218,15 +216,12 @@ public class DPORStateReducer extends ListenerAdapter { @Override public void choiceGeneratorAdvanced(VM vm, ChoiceGenerator currentCG) { - if (stateReductionMode) { // Check the boolean CG and if it is flipped, we are resetting the analysis if (currentCG instanceof BooleanChoiceGenerator) { if (!isBooleanCGFlipped) { isBooleanCGFlipped = true; } else { - // Number of conflicts = first trace + subsequent backtrack points - numOfConflicts = 1 + doneBacktrackSet.size(); // Allocate new objects for data structure when the boolean is flipped from "false" to "true" initializeStatesVariables(); } @@ -237,10 +232,10 @@ public class DPORStateReducer extends ListenerAdapter { // If this is a new CG then we need to update data structures resetStatesForNewExecution(icsCG, vm); // If we don't see a fair scheduling of events/choices then we have to enforce it - fairSchedulingAndTransition(icsCG, vm); + ensureFairSchedulingAndSetupTransition(icsCG, vm); // Update backtrack set of an executed event (transition): one transition before this one updateBacktrackSet(currentExecution, choiceCounter - 1); - // Explore the next backtrack point: + // 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) { @@ -272,11 +267,9 @@ public class DPORStateReducer extends ListenerAdapter { currentChoice = checkAndAdjustChoice(currentChoice, vm); // Record accesses from executed instructions if (executedInsn instanceof JVMFieldInstruction) { - // Analyze only after being initialized - String fieldClass = ((JVMFieldInstruction) executedInsn).getFieldInfo().getFullName(); // We don't care about libraries - if (!isFieldExcluded(fieldClass)) { - analyzeReadWriteAccesses(executedInsn, fieldClass, currentChoice); + if (!isFieldExcluded(executedInsn)) { + analyzeReadWriteAccesses(executedInsn, currentChoice); } } else if (executedInsn instanceof INVOKEINTERFACE) { // Handle the read/write accesses that occur through iterators @@ -313,22 +306,22 @@ public class DPORStateReducer extends ListenerAdapter { } } - // This class stores a representation of the execution graph node + // This class stores a representation of an execution // TODO: We can modify this class to implement some optimization (e.g., clock-vector) // TODO: We basically need to keep track of: // TODO: (1) last read/write access to each memory location - // TODO: (2) last state with two or more incoming events (transitions) + // TODO: (2) last state with two or more incoming events/transitions private class Execution { private HashMap cgToChoiceMap; // Map between CG to choice numbers for O(1) access - private ArrayList executionTrace; // The BacktrackPoint objects of this execution + private ArrayList executionTrace; // The BacktrackPoint objects of this execution + private boolean isNew; // Track if this is the first time it is accessed private HashMap readWriteFieldsMap; // Record fields that are accessed - private HashMap stateToTransitionMap; // For O(1) access to backtrack point public Execution() { cgToChoiceMap = new HashMap<>(); executionTrace = new ArrayList<>(); + isNew = true; readWriteFieldsMap = new HashMap<>(); - stateToTransitionMap = new HashMap<>(); } public void addTransition(TransitionEvent newBacktrackPoint) { @@ -339,13 +332,6 @@ public class DPORStateReducer extends ListenerAdapter { cgToChoiceMap = null; } - public TransitionEvent getTransitionFromState(int stateId) { - if (stateToTransitionMap.containsKey(stateId)) { - return stateToTransitionMap.get(stateId); - } - return null; - } - public int getChoiceFromCG(IntChoiceFromSet icsCG) { return cgToChoiceMap.get(icsCG); } @@ -354,10 +340,6 @@ public class DPORStateReducer extends ListenerAdapter { return executionTrace; } - public HashMap getReadWriteFieldsMap() { - return readWriteFieldsMap; - } - public TransitionEvent getFirstTransition() { return executionTrace.get(0); } @@ -366,106 +348,95 @@ public class DPORStateReducer extends ListenerAdapter { return executionTrace.get(executionTrace.size() - 1); } + public HashMap getReadWriteFieldsMap() { + return readWriteFieldsMap; + } + public boolean isNew() { - return executionTrace.size() == 1; + if (isNew) { + // Right after this is accessed, it is no longer new + isNew = false; + return true; + } + return false; } public void mapCGToChoice(IntChoiceFromSet icsCG, int choice) { cgToChoiceMap.put(icsCG, choice); } - - public void mapStateToTransition(int stateId, TransitionEvent backtrackPoint) { - stateToTransitionMap.put(stateId, backtrackPoint); - } } // This class compactly stores a predecessor // 1) a predecessor execution // 2) the predecessor choice in that predecessor execution private class Predecessor { - private int predecessorChoice; // Predecessor choice - private Execution predecessorExecution; // Predecessor execution + private int choice; // Predecessor choice + private Execution execution; // Predecessor execution public Predecessor(int predChoice, Execution predExec) { - predecessorChoice = predChoice; - predecessorExecution = predExec; - } - - public int getPredecessorChoice() { - return predecessorChoice; - } - - public Execution getPredecessorExecution() { - return predecessorExecution; - } - } - - // This class compactly stores backtrack points: - // 1) CG, - // 2) state ID, - // 3) choice, - // 4) predecessors (for backward DFS). - private class TransitionEvent { - private IntChoiceFromSet transitionCG; // CG at this transition - private int stateId; // State at this transition - private int choice; // Choice chosen at this transition - private Execution execution; // The execution where this transition belongs - private int choiceCounter; // Choice counter at this transition - private HashSet predecessors; // Maps incoming events/transitions (execution and choice) - - public TransitionEvent() { - transitionCG = null; - stateId = -1; - choice = -1; - execution = null; - choiceCounter = -1; - predecessors = new HashSet<>(); - } - - public void setTransitionCG(IntChoiceFromSet cg) { - transitionCG = cg; - } - - public void setStateId(int stId) { - stateId = stId; - } - - public void setChoice(int cho) { - choice = cho; - } - - public void setChoiceCounter(int choCounter) { - choiceCounter = choCounter; - } - - public IntChoiceFromSet getTransitionCG() { return transitionCG; } - - public int getStateId() { - return stateId; + choice = predChoice; + execution = predExec; } public int getChoice() { return choice; } - public int getChoiceCounter() { - return choiceCounter; + public Execution getExecution() { + return execution; } + } - public void setExecution(Execution exec) { - execution = exec; + // This class represents a R-Graph (in the paper it is a state transition graph R) + // This implementation stores reachable transitions from and connects with past executions + private class RGraph { + private int hiStateId; // Maximum state Id + private HashMap> graph; // Reachable transitions from past executions + + public RGraph() { + hiStateId = 0; + graph = new HashMap<>(); } - public Execution getExecution() { - return execution; + public void addReachableTransition(int stateId, TransitionEvent transition) { + HashSet transitionSet; + if (graph.containsKey(stateId)) { + transitionSet = graph.get(stateId); + } else { + transitionSet = new HashSet<>(); + graph.put(stateId, transitionSet); + } + // Insert into the set if it does not contain it yet + if (!transitionSet.contains(transition)) { + transitionSet.add(transition); + } + // Update highest state ID + if (hiStateId < stateId) { + hiStateId = stateId; + } } - public HashSet getPredecessors() { - return predecessors; + public HashSet getReachableTransitionsAtState(int stateId) { + if (!graph.containsKey(stateId)) { + // This is a loop from a transition to itself, so just return the current transition + HashSet transitionSet = new HashSet<>(); + transitionSet.add(currentExecution.getLastTransition()); + return transitionSet; + } + return graph.get(stateId); } - public void recordPredecessor(Execution execution, int choice) { - predecessors.add(new Predecessor(choice, execution)); + public HashSet getReachableTransitions(int stateId) { + HashSet 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; } } @@ -550,6 +521,102 @@ public class DPORStateReducer extends ListenerAdapter { } } + // This class compactly stores transitions: + // 1) CG, + // 2) state ID, + // 3) choice, + // 4) predecessors (for backward DFS). + private class TransitionEvent { + private int choice; // Choice chosen at this transition + private int choiceCounter; // Choice counter at this transition + private Execution execution; // The execution where this transition belongs + private HashSet predecessors; // Maps incoming events/transitions (execution and choice) + private HashMap> 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 + + public TransitionEvent() { + choice = 0; + choiceCounter = 0; + execution = null; + predecessors = new HashSet<>(); + recordedPredecessors = new HashMap<>(); + stateId = 0; + transitionCG = null; + } + + public int getChoice() { + return choice; + } + + public int getChoiceCounter() { + return choiceCounter; + } + + public Execution getExecution() { + return execution; + } + + public HashSet getPredecessors() { + return predecessors; + } + + public int getStateId() { + return stateId; + } + + public IntChoiceFromSet getTransitionCG() { return transitionCG; } + + private boolean isRecordedPredecessor(Execution execution, int choice) { + // See if we have recorded this predecessor earlier + HashSet 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) { + if (!isRecordedPredecessor(execution, choice)) { + predecessors.add(new Predecessor(choice, execution)); + } + } + + public void setChoice(int cho) { + choice = cho; + } + + public void setChoiceCounter(int choCounter) { + choiceCounter = choCounter; + } + + public void setExecution(Execution exec) { + execution = exec; + } + + public void setPredecessors(HashSet preds) { + predecessors = new HashSet<>(preds); + } + + public void setStateId(int stId) { + stateId = stId; + } + + public void setTransitionCG(IntChoiceFromSet cg) { + transitionCG = cg; + } + } + // -- CONSTANTS private final static String DO_CALL_METHOD = "doCall"; // We exclude fields that come from libraries (Java and Groovy), and also the infrastructure @@ -571,7 +638,14 @@ public class DPORStateReducer extends ListenerAdapter { private final static String JAVA_STRING_LIB = "java.lang.String"; // -- FUNCTIONS - private void fairSchedulingAndTransition(IntChoiceFromSet icsCG, VM vm) { + private Integer[] copyChoices(Integer[] choicesToCopy) { + + Integer[] copyOfChoices = new Integer[choicesToCopy.length]; + System.arraycopy(choicesToCopy, 0, copyOfChoices, 0, choicesToCopy.length); + return copyOfChoices; + } + + private void ensureFairSchedulingAndSetupTransition(IntChoiceFromSet icsCG, VM vm) { // Check the next choice and if the value is not the same as the expected then force the expected value int choiceIndex = choiceCounter % refChoices.length; int nextChoice = icsCG.getNextChoice(); @@ -584,6 +658,20 @@ public class DPORStateReducer extends ListenerAdapter { } // Get state ID and associate it with this transition int stateId = vm.getStateId(); + TransitionEvent transition = setupTransition(icsCG, stateId, choiceIndex); + // Add new transition to the current execution and map it in R-Graph + for (Integer stId : justVisitedStates) { // Map this transition to all the previously passed states + rGraph.addReachableTransition(stId, transition); + } + currentExecution.mapCGToChoice(icsCG, choiceCounter); + // Store restorable state object for this state (always store the latest) + if (!restorableStateMap.containsKey(stateId)) { + RestorableVMState restorableState = vm.getRestorableState(); + restorableStateMap.put(stateId, restorableState); + } + } + + private TransitionEvent setupTransition(IntChoiceFromSet icsCG, int stateId, int choiceIndex) { // Get a new transition TransitionEvent transition; if (currentExecution.isNew()) { @@ -591,6 +679,7 @@ public class DPORStateReducer extends ListenerAdapter { transition = currentExecution.getFirstTransition(); } else { transition = new TransitionEvent(); + currentExecution.addTransition(transition); transition.recordPredecessor(currentExecution, choiceCounter - 1); } transition.setExecution(currentExecution); @@ -598,20 +687,8 @@ public class DPORStateReducer extends ListenerAdapter { transition.setStateId(stateId); transition.setChoice(refChoices[choiceIndex]); transition.setChoiceCounter(choiceCounter); - // Add new transition to the current execution - currentExecution.mapStateToTransition(stateId, transition); - currentExecution.addTransition(transition); - currentExecution.mapCGToChoice(icsCG, choiceCounter); - // Store restorable state object for this state (always store the latest) - RestorableVMState restorableState = vm.getRestorableState(); - restorableStateMap.put(stateId, restorableState); - } - - private Integer[] copyChoices(Integer[] choicesToCopy) { - Integer[] copyOfChoices = new Integer[choicesToCopy.length]; - System.arraycopy(choicesToCopy, 0, copyOfChoices, 0, choicesToCopy.length); - return copyOfChoices; + return transition; } // --- Functions related to cycle detection and reachability graph @@ -623,7 +700,6 @@ public class DPORStateReducer extends ListenerAdapter { // With simple approach we only need to check for a re-visited state. // Basically, we have to check that we have executed all events between two occurrences of such state. private boolean completeFullCycle(int stId) { - // False if the state ID hasn't been recorded if (!stateToEventMap.containsKey(stId)) { return false; @@ -655,9 +731,8 @@ public class DPORStateReducer extends ListenerAdapter { backtrackStateQ = new PriorityQueue<>(Collections.reverseOrder()); currentExecution = new Execution(); currentExecution.addTransition(new TransitionEvent()); // Always start with 1 backtrack point - doneBacktrackSet = new HashSet<>(); - stateToChoiceCounterMap = new HashMap<>(); - rGraph = new HashMap<>(); + doneBacktrackMap = new HashMap<>(); + rGraph = new RGraph(); // Booleans isEndOfExecution = false; } @@ -672,26 +747,6 @@ public class DPORStateReducer extends ListenerAdapter { } } - // Save the current transition into R-Graph - // Basically the current transition is reachable from the final state of the previous transition in this execution - private void addTransitionToRGRaph() { - // Get the current transition - TransitionEvent currTrans = currentExecution.getLastTransition(); - // This transition is reachable from this source state when it has advanced the state - int stateId = currTrans.getStateId(); - // Add transition into R-Graph - HashSet transitionSet; - if (rGraph.containsKey(stateId)) { - transitionSet = rGraph.get(stateId); - } else { - transitionSet = new HashSet<>(); - } - // Insert into the set if it does not contain it yet - if (!transitionSet.contains(currTrans)) { - transitionSet.add(currTrans); - } - } - 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 @@ -705,7 +760,6 @@ public class DPORStateReducer extends ListenerAdapter { private void updateStateInfo(Search search) { // Update the state variables - // Line 19 in the paper page 11 (see the heading note above) int stateId = search.getStateId(); // Insert state ID into the map if it is new if (!stateToEventMap.containsKey(stateId)) { @@ -713,14 +767,16 @@ public class DPORStateReducer extends ListenerAdapter { stateToEventMap.put(stateId, eventSet); } analyzeReachabilityAndCreateBacktrackPoints(search.getVM(), stateId); - stateToChoiceCounterMap.put(stateId, choiceCounter); justVisitedStates.add(stateId); - currVisitedStates.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); + } } // --- Functions related to Read/Write access analysis on shared fields - private void addNewBacktrackPoint(int stateId, Integer[] newChoiceList, Execution parentExecution, int parentChoice) { + private void addNewBacktrackPoint(int stateId, Integer[] newChoiceList, TransitionEvent conflictTransition) { // Insert backtrack point to the right state ID LinkedList backtrackExecList; if (backtrackMap.containsKey(stateId)) { @@ -731,7 +787,7 @@ public class DPORStateReducer extends ListenerAdapter { } // Add the new backtrack execution object TransitionEvent backtrackTransition = new TransitionEvent(); - backtrackTransition.recordPredecessor(parentExecution, parentChoice); + backtrackTransition.setPredecessors(conflictTransition.getPredecessors()); backtrackExecList.addFirst(new BacktrackExecution(newChoiceList, backtrackTransition)); // Add to priority queue if (!backtrackStateQ.contains(stateId)) { @@ -740,17 +796,28 @@ public class DPORStateReducer extends ListenerAdapter { } // Analyze Read/Write accesses that are directly invoked on fields - private void analyzeReadWriteAccesses(Instruction executedInsn, String fieldClass, int currentChoice) { + private void analyzeReadWriteAccesses(Instruction executedInsn, int currentChoice) { + // Get the field info + FieldInfo fieldInfo = ((JVMFieldInstruction) executedInsn).getFieldInfo(); + // Analyze only after being initialized + String fieldClass = fieldInfo.getFullName(); // Do the analysis to get Read and Write accesses to fields ReadWriteSet rwSet = getReadWriteSet(currentChoice); - int objectId = ((JVMFieldInstruction) executedInsn).getFieldInfo().getClassInfo().getClassObjectRef(); + int objectId = fieldInfo.getClassInfo().getClassObjectRef(); // Record the field in the map if (executedInsn instanceof WriteInstruction) { - // Exclude certain field writes because of infrastructure needs, e.g., Event class field writes - for (String str : EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST) { - if (fieldClass.startsWith(str)) { - return; + // We first check the non-relevant fields set + if (!nonRelevantFields.contains(fieldInfo)) { + // Exclude certain field writes because of infrastructure needs, e.g., Event class field writes + for (String str : EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST) { + if (fieldClass.startsWith(str)) { + nonRelevantFields.add(fieldInfo); + return; + } } + } else { + // If we have this field in the non-relevant fields set then we return right away + return; } rwSet.addWriteField(fieldClass, objectId); } else if (executedInsn instanceof ReadInstruction) { @@ -779,9 +846,17 @@ public class DPORStateReducer extends ListenerAdapter { 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) || - excludeThisForItStartsWith(EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST, objClassName)) { + ClassInfo classInfo = eiAccessObj.getClassInfo(); + String objClassName = classInfo.getName(); + // Check if this class info is part of the non-relevant classes set already + if (!nonRelevantClasses.contains(classInfo)) { + if (excludeThisForItStartsWith(EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST, objClassName) || + excludeThisForItStartsWith(EXCLUDED_FIELDS_STARTS_WITH_LIST, objClassName)) { + nonRelevantClasses.add(classInfo); + return; + } + } else { + // If it is part of the non-relevant classes set then return immediately return; } // Extract fields from this object and put them into the read write @@ -817,17 +892,19 @@ public class DPORStateReducer extends ListenerAdapter { return currentChoice; } - private void createBacktrackingPoint(Execution execution, int currentChoice, int conflictChoice) { - + private void createBacktrackingPoint(Execution execution, int currentChoice, + Execution conflictExecution, int conflictChoice) { // 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]; - //int firstChoice = choices[actualChoice]; - ArrayList pastTrace = execution.getExecutionTrace(); - ArrayList currTrace = currentExecution.getExecutionTrace(); - int currChoice = currTrace.get(currentChoice).getChoice(); - int stateId = pastTrace.get(conflictChoice).getStateId(); + ArrayList currentTrace = execution.getExecutionTrace(); + ArrayList conflictTrace = conflictExecution.getExecutionTrace(); + int currChoice = currentTrace.get(currentChoice).getChoice(); + int stateId = conflictTrace.get(conflictChoice).getStateId(); // Check if this trace has been done from this state if (isTraceAlreadyConstructed(currChoice, stateId)) { return; @@ -841,8 +918,8 @@ public class DPORStateReducer extends ListenerAdapter { j++; } } - // Parent choice is conflict choice - 1 - addNewBacktrackPoint(stateId, newChoiceList, execution, conflictChoice - 1); + // Predecessor of the new backtrack point is the same as the conflict point's + addNewBacktrackPoint(stateId, newChoiceList, conflictTrace.get(conflictChoice)); } private boolean excludeThisForItContains(String[] excludedStrings, String className) { @@ -873,7 +950,6 @@ public class DPORStateReducer extends ListenerAdapter { } private void exploreNextBacktrackPoints(VM vm, IntChoiceFromSet icsCG) { - // 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()) { @@ -901,65 +977,27 @@ public class DPORStateReducer extends ListenerAdapter { isEndOfExecution = true; } - private boolean isConflictFound(Execution execution, int reachableChoice, int conflictChoice, + 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 executionTrace = execution.getExecutionTrace(); - HashMap execRWFieldsMap = execution.getReadWriteFieldsMap(); + ArrayList conflictTrace = conflictExecution.getExecutionTrace(); + HashMap confRWFieldsMap = conflictExecution.getReadWriteFieldsMap(); // Skip if this event does not have any Read/Write set or the two events are basically the same event (number) - if (!execRWFieldsMap.containsKey(conflictChoice) || - executionTrace.get(reachableChoice).getChoice() == executionTrace.get(conflictChoice).getChoice()) { + if (!confRWFieldsMap.containsKey(conflictChoice) || + executionTrace.get(reachableChoice).getChoice() == conflictTrace.get(conflictChoice).getChoice()) { return false; } // R/W set of choice/event that may have a potential conflict - ReadWriteSet evtRWSet = execRWFieldsMap.get(conflictChoice); + ReadWriteSet confRWSet = confRWFieldsMap.get(conflictChoice); // Check for conflicts with Read and Write fields for Write instructions Set currWriteSet = currRWSet.getWriteSet(); for(String writeField : currWriteSet) { int currObjId = currRWSet.writeFieldObjectId(writeField); - if (evtRWSet.readFieldExists(writeField) && evtRWSet.readFieldObjectId(writeField) == currObjId) { - // Remove this from the read set as we are tracking per memory location - evtRWSet.removeWriteField(writeField); - return true; - } else if (evtRWSet.writeFieldExists(writeField) && evtRWSet.writeFieldObjectId(writeField) == currObjId) { + if ((confRWSet.readFieldExists(writeField) && confRWSet.readFieldObjectId(writeField) == currObjId) || + (confRWSet.writeFieldExists(writeField) && confRWSet.writeFieldObjectId(writeField) == currObjId)) { // Remove this from the write set as we are tracking per memory location - evtRWSet.removeReadField(writeField); - return true; - } - } - // Check for conflicts with Write fields for Read instructions - Set currReadSet = currRWSet.getReadSet(); - for(String readField : currReadSet) { - int currObjId = currRWSet.readFieldObjectId(readField); - if (evtRWSet.writeFieldExists(readField) && evtRWSet.writeFieldObjectId(readField) == currObjId) { - // Remove this from the write set as we are tracking per memory location - evtRWSet.removeWriteField(readField); - return true; - } - } - // Return false if no conflict is found - return false; - } - - private boolean isConflictFound(Execution execution, int reachableChoice, int conflictChoice) { - - ArrayList executionTrace = execution.getExecutionTrace(); - HashMap execRWFieldsMap = execution.getReadWriteFieldsMap(); - // Skip if this event does not have any Read/Write set or the two events are basically the same event (number) - if (!execRWFieldsMap.containsKey(conflictChoice) || - executionTrace.get(reachableChoice).getChoice() == executionTrace.get(conflictChoice).getChoice()) { - return false; - } - // Current R/W set - ReadWriteSet currRWSet = execRWFieldsMap.get(reachableChoice); - // R/W set of choice/event that may have a potential conflict - ReadWriteSet evtRWSet = execRWFieldsMap.get(conflictChoice); - // Check for conflicts with Read and Write fields for Write instructions - Set currWriteSet = currRWSet.getWriteSet(); - for(String writeField : currWriteSet) { - int currObjId = currRWSet.writeFieldObjectId(writeField); - if ((evtRWSet.readFieldExists(writeField) && evtRWSet.readFieldObjectId(writeField) == currObjId) || - (evtRWSet.writeFieldExists(writeField) && evtRWSet.writeFieldObjectId(writeField) == currObjId)) { + currRWSet.removeWriteField(writeField); return true; } } @@ -967,7 +1005,9 @@ public class DPORStateReducer extends ListenerAdapter { Set currReadSet = currRWSet.getReadSet(); for(String readField : currReadSet) { int currObjId = currRWSet.readFieldObjectId(readField); - if (evtRWSet.writeFieldExists(readField) && evtRWSet.writeFieldObjectId(readField) == currObjId) { + if (confRWSet.writeFieldExists(readField) && confRWSet.writeFieldObjectId(readField) == currObjId) { + // Remove this from the read set as we are tracking per memory location + currRWSet.removeReadField(readField); return true; } } @@ -989,33 +1029,47 @@ public class DPORStateReducer extends ListenerAdapter { return rwSet; } - private boolean isFieldExcluded(String field) { + private boolean isFieldExcluded(Instruction executedInsn) { + // Get the field info + FieldInfo fieldInfo = ((JVMFieldInstruction) executedInsn).getFieldInfo(); + // Check if the non-relevant fields set already has it + if (nonRelevantFields.contains(fieldInfo)) { + return true; + } + // Check if the relevant fields set already has it + if (relevantFields.contains(fieldInfo)) { + return false; + } + // Analyze only after being initialized + String field = fieldInfo.getFullName(); // Check against "starts-with", "ends-with", and "contains" list if (excludeThisForItStartsWith(EXCLUDED_FIELDS_STARTS_WITH_LIST, field) || excludeThisForItEndsWith(EXCLUDED_FIELDS_ENDS_WITH_LIST, field) || excludeThisForItContains(EXCLUDED_FIELDS_CONTAINS_LIST, field)) { + nonRelevantFields.add(fieldInfo); return true; } - + relevantFields.add(fieldInfo); return false; } // Check if this trace is already constructed private boolean isTraceAlreadyConstructed(int firstChoice, int stateId) { // Concatenate state ID and only the first event in the string, e.g., "1:1 for the trace 10234 at state 1" - // TODO: THIS IS AN OPTIMIZATION! - // This is the optimized version because after we execute, e.g., the trace 1:10234, we don't need to try - // another trace that starts with event 1 at state 1, e.g., the trace 1:13024 - // The second time this event 1 is explored, it will generate the same state as the first one - StringBuilder sb = new StringBuilder(); - sb.append(stateId); - sb.append(':'); - sb.append(firstChoice); // Check if the trace has been constructed as a backtrack point for this state - if (doneBacktrackSet.contains(sb.toString())) { - return true; + // TODO: THIS IS AN OPTIMIZATION! + HashSet choiceSet; + if (doneBacktrackMap.containsKey(stateId)) { + choiceSet = doneBacktrackMap.get(stateId); + if (choiceSet.contains(firstChoice)) { + return true; + } + } else { + choiceSet = new HashSet<>(); + doneBacktrackMap.put(stateId, choiceSet); } - doneBacktrackSet.add(sb.toString()); + choiceSet.add(firstChoice); + return false; } @@ -1028,7 +1082,6 @@ public class DPORStateReducer extends ListenerAdapter { refChoices = copyChoices(choices); // Clear data structures currVisitedStates = new HashSet<>(); - stateToChoiceCounterMap = new HashMap<>(); stateToEventMap = new HashMap<>(); isEndOfExecution = false; } @@ -1060,40 +1113,49 @@ public class DPORStateReducer extends ListenerAdapter { // 1) recursively, and // 2) track accesses per memory location (per shared variable/field) private void updateBacktrackSet(Execution execution, int currentChoice) { - // Choice/event we want to check for conflict against (start from actual choice) - int conflictChoice = currentChoice; // Copy ReadWriteSet object HashMap currRWFieldsMap = execution.getReadWriteFieldsMap(); - ReadWriteSet currRWSet = currRWFieldsMap.get(currentChoice).getCopy(); + ReadWriteSet currRWSet = currRWFieldsMap.get(currentChoice); + if (currRWSet == null) { + return; + } + currRWSet = currRWSet.getCopy(); // Memorize visited TransitionEvent object while performing backward DFS to avoid getting caught up in a cycle HashSet visited = new HashSet<>(); // Update backtrack set recursively - updateBacktrackSetRecursive(execution, currentChoice, conflictChoice, currRWSet, visited); + updateBacktrackSetRecursive(execution, currentChoice, execution, currentChoice, currRWSet, visited); } - private void updateBacktrackSetRecursive(Execution execution, int currentChoice, int conflictChoice, + private void updateBacktrackSetRecursive(Execution execution, int currentChoice, + Execution conflictExecution, int conflictChoice, ReadWriteSet currRWSet, HashSet visited) { // Halt when we have found the first read/write conflicts for all memory locations if (currRWSet.isEmpty()) { return; } - TransitionEvent confTrans = execution.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.getPredecessorChoice(); - execution = predecessor.getPredecessorExecution(); + int predecessorChoice = predecessor.getChoice(); + Execution predecessorExecution = predecessor.getExecution(); + // Push up one happens-before transition + int newConflictChoice = conflictChoice; + Execution newConflictExecution = conflictExecution; // Check if a conflict is found - if (isConflictFound(execution, currentChoice, conflictChoice, currRWSet)) { - createBacktrackingPoint(execution, currentChoice, conflictChoice); + 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, conflictChoice, currRWSet, visited); + updateBacktrackSetRecursive(predecessorExecution, predecessorChoice, newConflictExecution, newConflictChoice, + currRWSet, visited); } } @@ -1101,32 +1163,25 @@ public class DPORStateReducer extends ListenerAdapter { private void analyzeReachabilityAndCreateBacktrackPoints(VM vm, int stateId) { // Perform this analysis only when: - // 1) there is a state match, - // 2) this is not during a switch to a new execution, - // 3) at least 2 choices/events have been explored (choiceCounter > 1), - // 4) state > 0 (state 0 is for boolean CG) - if (!vm.isNewState() && !isEndOfExecution && choiceCounter > 1 && (stateId > 0)) { - if (currVisitedStates.contains(stateId)) { - // Get the backtrack point from the current execution - TransitionEvent transition = currentExecution.getTransitionFromState(stateId); - transition.recordPredecessor(currentExecution, choiceCounter - 1); - updateBacktrackSetsFromPreviousExecution(stateId); - } else if (prevVisitedStates.contains(stateId)) { // We visit a state in a previous execution - // Update past executions with a predecessor - HashSet reachableTransitions = rGraph.get(stateId); + // 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)) { + // Update reachable transitions in the graph with a predecessor + HashSet reachableTransitions = rGraph.getReachableTransitionsAtState(stateId); for(TransitionEvent transition : reachableTransitions) { - Execution execution = transition.getExecution(); - transition.recordPredecessor(execution, choiceCounter - 1); + transition.recordPredecessor(currentExecution, choiceCounter - 1); } - updateBacktrackSetsFromPreviousExecution(stateId); + updateBacktrackSetsFromGraph(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 reachableTransitions = rGraph.get(stateId); + HashSet reachableTransitions = rGraph.getReachableTransitions(stateId); for(TransitionEvent transition : reachableTransitions) { Execution execution = transition.getExecution(); int currentChoice = transition.getChoiceCounter();