8c6a1d69e5bb7fcdcbb96281b14c4d7f9e5f8639
[jpf-core.git] / src / main / gov / nasa / jpf / listener / DPORStateReducer.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0.
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package gov.nasa.jpf.listener;
19
20 import gov.nasa.jpf.Config;
21 import gov.nasa.jpf.JPF;
22 import gov.nasa.jpf.ListenerAdapter;
23 import gov.nasa.jpf.search.Search;
24 import gov.nasa.jpf.jvm.bytecode.*;
25 import gov.nasa.jpf.vm.*;
26 import gov.nasa.jpf.vm.bytecode.ReadInstruction;
27 import gov.nasa.jpf.vm.bytecode.WriteInstruction;
28 import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
29 import gov.nasa.jpf.vm.choice.IntIntervalGenerator;
30
31 import java.io.PrintWriter;
32 import java.util.*;
33
34 // TODO: Fix for Groovy's model-checking
35 // TODO: This is a setter to change the values of the ChoiceGenerator to implement POR
36 /**
37  * Simple tool to log state changes.
38  *
39  * This DPOR implementation is augmented by the algorithm presented in this SPIN paper:
40  * http://spinroot.com/spin/symposia/ws08/spin2008_submission_33.pdf
41  *
42  * The algorithm is presented on page 11 of the paper. Basically, we create a graph G
43  * (i.e., visible operation dependency graph)
44  * that maps inter-related threads/sub-programs that trigger state changes.
45  * The key to this approach is that we evaluate graph G in every iteration/recursion to
46  * only update the backtrack sets of the threads/sub-programs that are reachable in graph G
47  * from the currently running thread/sub-program.
48  */
49 public class DPORStateReducer extends ListenerAdapter {
50
51   // Information printout fields for verbose mode
52   private boolean verboseMode;
53   private boolean stateReductionMode;
54   private final PrintWriter out;
55   private String detail;
56   private int depth;
57   private int id;
58   private Transition transition;
59
60   // DPOR-related fields
61   // Basic information
62   private Integer[] choices;
63   private Integer[] refChoices;
64   private int choiceCounter;
65   private int maxEventChoice;
66   // Data structure to track the events seen by each state to track cycles (containing all events) for termination
67   private HashSet<Integer> currVisitedStates; // States being visited in the current execution
68   private HashSet<Integer> justVisitedStates; // States just visited in the previous choice/event
69   private HashSet<Integer> prevVisitedStates; // States visited in the previous execution
70   private HashMap<Integer, HashSet<Integer>> stateToEventMap;
71   // Data structure to analyze field Read/Write accesses and conflicts
72   private HashMap<Integer, LinkedList<Integer[]>> backtrackMap; // Track created backtracking points
73   private Stack<BacktrackPoint> btrckPtsStack;                  // Stack that stores backtracking points
74   private List<IntChoiceFromSet> cgList;                        // Record CGs for backtracking points
75   private HashSet<IntChoiceFromSet> btrckCGSet;                 // Set that records all the backtrack CGs
76   private HashMap<Integer, HashSet<Integer>> conflictPairMap;   // Record conflicting events
77   private HashMap<Integer, ReadWriteSet> readWriteFieldsMap;    // Record fields that are accessed
78
79   // Visible operation dependency graph implementation (SPIN paper) related fields
80   private int prevChoiceValue;
81   private HashMap<Integer, HashSet<Integer>> vodGraphMap; // Visible operation dependency graph (VOD graph)
82
83   // Boolean states
84   private boolean isBooleanCGFlipped;
85
86   public DPORStateReducer(Config config, JPF jpf) {
87     verboseMode = config.getBoolean("printout_state_transition", false);
88     stateReductionMode = config.getBoolean("activate_state_reduction", true);
89     if (verboseMode) {
90       out = new PrintWriter(System.out, true);
91     } else {
92       out = null;
93     }
94     // DPOR-related
95     choices = null;
96     refChoices = null;
97     choiceCounter = 0;
98     maxEventChoice = 0;
99     // Cycle tracking
100     currVisitedStates = new HashSet<>();
101     justVisitedStates = new HashSet<>();
102     prevVisitedStates = new HashSet<>();
103     stateToEventMap = new HashMap<>();
104     // Backtracking
105     backtrackMap = new HashMap<>();
106     btrckPtsStack = new Stack<>();
107     btrckCGSet = new HashSet<>();
108     cgList = new ArrayList<>();
109     conflictPairMap = new HashMap<>();
110     readWriteFieldsMap = new HashMap<>();
111     // VOD graph
112     prevChoiceValue = -1;
113     vodGraphMap = new HashMap<>();
114     // Booleans
115     isBooleanCGFlipped = false;
116   }
117
118   @Override
119   public void stateRestored(Search search) {
120     if (verboseMode) {
121       id = search.getStateId();
122       depth = search.getDepth();
123       transition = search.getTransition();
124       detail = null;
125       out.println("\n==> DEBUG: The state is restored to state with id: " + id + " -- Transition: " + transition +
126               " and depth: " + depth + "\n");
127     }
128   }
129
130   @Override
131   public void searchStarted(Search search) {
132     if (verboseMode) {
133       out.println("\n==> DEBUG: ----------------------------------- search started" + "\n");
134     }
135   }
136
137   @Override
138   public void stateAdvanced(Search search) {
139     if (verboseMode) {
140       id = search.getStateId();
141       depth = search.getDepth();
142       transition = search.getTransition();
143       if (search.isNewState()) {
144         detail = "new";
145       } else {
146         detail = "visited";
147       }
148
149       if (search.isEndState()) {
150         out.println("\n==> DEBUG: This is the last state!\n");
151         detail += " end";
152       }
153       out.println("\n==> DEBUG: The state is forwarded to state with id: " + id + " with depth: " + depth +
154               " which is " + detail + " Transition: " + transition + "\n");
155     }
156     if (stateReductionMode) {
157       updateStateInfo(search);
158     }
159   }
160
161   @Override
162   public void stateBacktracked(Search search) {
163     if (verboseMode) {
164       id = search.getStateId();
165       depth = search.getDepth();
166       transition = search.getTransition();
167       detail = null;
168
169       out.println("\n==> DEBUG: The state is backtracked to state with id: " + id + " -- Transition: " + transition +
170               " and depth: " + depth + "\n");
171     }
172     if (stateReductionMode) {
173       updateStateInfo(search);
174     }
175   }
176
177   @Override
178   public void searchFinished(Search search) {
179     if (verboseMode) {
180       out.println("\n==> DEBUG: ----------------------------------- search finished" + "\n");
181     }
182   }
183
184   @Override
185   public void choiceGeneratorRegistered(VM vm, ChoiceGenerator<?> nextCG, ThreadInfo currentThread, Instruction executedInstruction) {
186     if (stateReductionMode) {
187       // Initialize with necessary information from the CG
188       if (nextCG instanceof IntChoiceFromSet) {
189         IntChoiceFromSet icsCG = (IntChoiceFromSet) nextCG;
190         // Check if CG has been initialized, otherwise initialize it
191         Integer[] cgChoices = icsCG.getAllChoices();
192         // Record the events (from choices)
193         if (choices == null) {
194           choices = cgChoices;
195           // Make a copy of choices as reference
196           refChoices = copyChoices(choices);
197           // Record the max event choice (the last element of the choice array)
198           maxEventChoice = choices[choices.length - 1];
199         }
200         icsCG.setNewValues(choices);
201         icsCG.reset();
202         // Use a modulo since choiceCounter is going to keep increasing
203         int choiceIndex = choiceCounter % choices.length;
204         icsCG.advance(choices[choiceIndex]);
205         // Index the ChoiceGenerator to set backtracking points
206         cgList.add(icsCG);
207       }
208     }
209   }
210
211   @Override
212   public void choiceGeneratorAdvanced(VM vm, ChoiceGenerator<?> currentCG) {
213
214     if (stateReductionMode) {
215       // Check the boolean CG and if it is flipped, we are resetting the analysis
216 //      if (currentCG instanceof BooleanChoiceGenerator) {
217 //        if (!isBooleanCGFlipped) {
218 //          isBooleanCGFlipped = true;
219 //        } else {
220 //          initializeStateReduction();
221 //        }
222 //      }
223       // Check every choice generated and ensure fair scheduling!
224       if (currentCG instanceof IntChoiceFromSet) {
225         IntChoiceFromSet icsCG = (IntChoiceFromSet) currentCG;
226         // If we don't see a fair scheduling of events/choices then we have to enforce it
227         checkAndEnforceFairScheduling(icsCG);
228         // Map state to event
229         mapStateToEvent(icsCG.getNextChoice());
230         // Update the VOD graph always with the latest
231         updateVODGraph(icsCG.getNextChoice());
232         // Check if we have seen this state or this state contains cycles that involve all events
233         if (terminateCurrentExecution()) {
234           exploreNextBacktrackSets(icsCG);
235         }
236         justVisitedStates.clear();
237         choiceCounter++;
238       }
239     }
240   }
241
242   @Override
243   public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
244     if (stateReductionMode) {
245       // Has to be initialized and a integer CG
246       ChoiceGenerator<?> cg = vm.getChoiceGenerator();
247       if (cg instanceof IntChoiceFromSet || cg instanceof IntIntervalGenerator) {
248         int currentChoice = choiceCounter - 1;  // Accumulative choice w.r.t the current trace
249         if (currentChoice < 0) { // If choice is -1 then skip
250           return;
251         }
252         currentChoice = checkAndAdjustChoice(currentChoice, vm);
253         // Record accesses from executed instructions
254         if (executedInsn instanceof JVMFieldInstruction) {
255           // Analyze only after being initialized
256           String fieldClass = ((JVMFieldInstruction) executedInsn).getFieldInfo().getFullName();
257           // We don't care about libraries
258           if (!isFieldExcluded(fieldClass)) {
259             analyzeReadWriteAccesses(executedInsn, fieldClass, currentChoice);
260           }
261         } else if (executedInsn instanceof INVOKEINTERFACE) {
262           // Handle the read/write accesses that occur through iterators
263           analyzeReadWriteAccesses(executedInsn, ti, currentChoice);
264         }
265         // Analyze conflicts from next instructions
266         if (nextInsn instanceof JVMFieldInstruction) {
267           // Skip the constructor because it is called once and does not have shared access with other objects
268           if (!nextInsn.getMethodInfo().getName().equals("<init>")) {
269             String fieldClass = ((JVMFieldInstruction) nextInsn).getFieldInfo().getFullName();
270             if (!isFieldExcluded(fieldClass)) {
271               // Check for conflict (go backward from current choice and get the first conflict)
272               for (int eventCounter = currentChoice - 1; eventCounter >= 0; eventCounter--) {
273                 // Check for conflicts with Write fields for both Read and Write instructions
274                 // Check and record a backtrack set for just once!
275                 if (isConflictFound(nextInsn, eventCounter, fieldClass) && isNewConflict(currentChoice, eventCounter)) {
276                   // Lines 4-8 of the algorithm in the paper page 11 (see the heading note above)
277                   if (vm.isNewState() || isReachableInVODGraph(currentChoice)) {
278                     createBacktrackingPoint(currentChoice, eventCounter);
279                   }
280                 }
281               }
282             }
283           }
284         }
285       }
286     }
287   }
288
289
290   // == HELPERS
291
292   // -- INNER CLASSES
293
294   // This class compactly stores Read and Write field sets
295   // We store the field name and its object ID
296   // Sharing the same field means the same field name and object ID
297   private class ReadWriteSet {
298     private HashMap<String, Integer> readSet;
299     private HashMap<String, Integer> writeSet;
300
301     public ReadWriteSet() {
302       readSet = new HashMap<>();
303       writeSet = new HashMap<>();
304     }
305
306     public void addReadField(String field, int objectId) {
307       readSet.put(field, objectId);
308     }
309
310     public void addWriteField(String field, int objectId) {
311       writeSet.put(field, objectId);
312     }
313
314     public boolean readFieldExists(String field) {
315       return readSet.containsKey(field);
316     }
317
318     public boolean writeFieldExists(String field) {
319       return writeSet.containsKey(field);
320     }
321
322     public int readFieldObjectId(String field) {
323       return readSet.get(field);
324     }
325
326     public int writeFieldObjectId(String field) {
327       return writeSet.get(field);
328     }
329   }
330
331   // This class compactly stores backtracking points: 1) backtracking ChoiceGenerator, and 2) backtracking choices
332   private class BacktrackPoint {
333     private IntChoiceFromSet backtrackCG; // CG to backtrack from
334     private Integer[] backtrackChoices;   // Choices to set for this backtrack CG
335
336     public BacktrackPoint(IntChoiceFromSet cg, Integer[] choices) {
337       backtrackCG = cg;
338       backtrackChoices = choices;
339     }
340
341     public IntChoiceFromSet getBacktrackCG() {
342       return backtrackCG;
343     }
344
345     public Integer[] getBacktrackChoices() {
346       return backtrackChoices;
347     }
348   }
349
350   // -- CONSTANTS
351   private final static String DO_CALL_METHOD = "doCall";
352   // We exclude fields that come from libraries (Java and Groovy), and also the infrastructure
353   private final static String[] EXCLUDED_FIELDS_CONTAINS_LIST = {"_closure"};
354   private final static String[] EXCLUDED_FIELDS_ENDS_WITH_LIST =
355           // Groovy library created fields
356           {"stMC", "callSiteArray", "metaClass", "staticClassInfo", "__constructor__",
357           // Infrastructure
358           "sendEvent", "Object", "reference", "location", "app", "state", "log", "functionList", "objectList",
359           "eventList", "valueList", "settings", "printToConsole", "app1", "app2"};
360   private final static String[] EXCLUDED_FIELDS_STARTS_WITH_LIST =
361           // Java and Groovy libraries
362           { "java", "org", "sun", "com", "gov", "groovy"};
363   private final static String[] EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST = {"Event"};
364   private final static String GET_PROPERTY_METHOD =
365           "invokeinterface org.codehaus.groovy.runtime.callsite.CallSite.callGetProperty";
366   private final static String GROOVY_CALLSITE_LIB = "org.codehaus.groovy.runtime.callsite";
367   private final static String JAVA_INTEGER = "int";
368   private final static String JAVA_STRING_LIB = "java.lang.String";
369
370   // -- FUNCTIONS
371   private void checkAndEnforceFairScheduling(IntChoiceFromSet icsCG) {
372     // Check the next choice and if the value is not the same as the expected then force the expected value
373     int choiceIndex = choiceCounter % refChoices.length;
374     int nextChoice = icsCG.getNextChoice();
375     if (refChoices[choiceIndex] != nextChoice) {
376       int expectedChoice = refChoices[choiceIndex];
377       int currCGIndex = icsCG.getNextChoiceIndex();
378       if ((currCGIndex >= 0) && (currCGIndex < refChoices.length)) {
379         icsCG.setChoice(currCGIndex, expectedChoice);
380       }
381     }
382   }
383
384   private Integer[] copyChoices(Integer[] choicesToCopy) {
385
386     Integer[] copyOfChoices = new Integer[choicesToCopy.length];
387     System.arraycopy(choicesToCopy, 0, copyOfChoices, 0, choicesToCopy.length);
388     return copyOfChoices;
389   }
390
391   // --- Functions related to cycle detection
392
393   // Detect cycles in the current execution/trace
394   // We terminate the execution iff:
395   // (1) the state has been visited in the current execution
396   // (2) the state has one or more cycles that involve all the events
397   // With simple approach we only need to check for a re-visited state.
398   // Basically, we have to check that we have executed all events between two occurrences of such state.
399   private boolean containsCyclesWithAllEvents(int stId) {
400
401     // False if the state ID hasn't been recorded
402     if (!stateToEventMap.containsKey(stId)) {
403       return false;
404     }
405     HashSet<Integer> visitedEvents = stateToEventMap.get(stId);
406     // Check if this set contains all the event choices
407     // If not then this is not the terminating condition
408     for(int i=0; i<=maxEventChoice; i++) {
409       if (!visitedEvents.contains(i)) {
410         return false;
411       }
412     }
413     return true;
414   }
415
416   private void mapStateToEvent(int nextChoiceValue) {
417     // Update all states with this event/choice
418     // This means that all past states now see this transition
419     Set<Integer> stateSet = stateToEventMap.keySet();
420     for(Integer stateId : stateSet) {
421       HashSet<Integer> eventSet = stateToEventMap.get(stateId);
422       eventSet.add(nextChoiceValue);
423     }
424   }
425
426   private boolean terminateCurrentExecution() {
427     // We need to check all the states that have just been visited
428     // Often a transition (choice/event) can result into forwarding/backtracking to a number of states
429     for(Integer stateId : justVisitedStates) {
430       if (prevVisitedStates.contains(stateId) || containsCyclesWithAllEvents(stateId)) {
431         return true;
432       }
433     }
434     return false;
435   }
436
437   private void updateStateInfo(Search search) {
438     // Update the state variables
439     // Line 19 in the paper page 11 (see the heading note above)
440     int stateId = search.getStateId();
441     currVisitedStates.add(stateId);
442     // Insert state ID into the map if it is new
443     if (!stateToEventMap.containsKey(stateId)) {
444       HashSet<Integer> eventSet = new HashSet<>();
445       stateToEventMap.put(stateId, eventSet);
446     }
447     justVisitedStates.add(stateId);
448   }
449
450   // --- Functions related to Read/Write access analysis on shared fields
451
452   // Analyze Read/Write accesses that are directly invoked on fields
453   private void analyzeReadWriteAccesses(Instruction executedInsn, String fieldClass, int currentChoice) {
454     // Do the analysis to get Read and Write accesses to fields
455     ReadWriteSet rwSet = getReadWriteSet(currentChoice);
456     int objectId = ((JVMFieldInstruction) executedInsn).getFieldInfo().getClassInfo().getClassObjectRef();
457     // Record the field in the map
458     if (executedInsn instanceof WriteInstruction) {
459       // Exclude certain field writes because of infrastructure needs, e.g., Event class field writes
460       for (String str : EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST) {
461         if (fieldClass.startsWith(str)) {
462           return;
463         }
464       }
465       rwSet.addWriteField(fieldClass, objectId);
466     } else if (executedInsn instanceof ReadInstruction) {
467       rwSet.addReadField(fieldClass, objectId);
468     }
469   }
470
471   // Analyze Read accesses that are indirect (performed through iterators)
472   // These accesses are marked by certain bytecode instructions, e.g., INVOKEINTERFACE
473   private void analyzeReadWriteAccesses(Instruction instruction, ThreadInfo ti, int currentChoice) {
474     // Get method name
475     INVOKEINTERFACE insn = (INVOKEINTERFACE) instruction;
476     if (insn.toString().startsWith(GET_PROPERTY_METHOD) &&
477             insn.getMethodInfo().getName().equals(DO_CALL_METHOD)) {
478       // Extract info from the stack frame
479       StackFrame frame = ti.getTopFrame();
480       int[] frameSlots = frame.getSlots();
481       // Get the Groovy callsite library at index 0
482       ElementInfo eiCallsite = VM.getVM().getHeap().get(frameSlots[0]);
483       if (!eiCallsite.getClassInfo().getName().startsWith(GROOVY_CALLSITE_LIB)) {
484         return;
485       }
486       // Get the iterated object whose property is accessed
487       ElementInfo eiAccessObj = VM.getVM().getHeap().get(frameSlots[1]);
488       if (eiAccessObj == null) {
489         return;
490       }
491       // We exclude library classes (they start with java, org, etc.) and some more
492       String objClassName = eiAccessObj.getClassInfo().getName();
493       if (excludeThisForItStartsWith(EXCLUDED_FIELDS_STARTS_WITH_LIST, objClassName) ||
494           excludeThisForItStartsWith(EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST, objClassName)) {
495         return;
496       }
497       // Extract fields from this object and put them into the read write
498       int numOfFields = eiAccessObj.getNumberOfFields();
499       for(int i=0; i<numOfFields; i++) {
500         FieldInfo fieldInfo = eiAccessObj.getFieldInfo(i);
501         if (fieldInfo.getType().equals(JAVA_STRING_LIB) || fieldInfo.getType().equals(JAVA_INTEGER)) {
502           String fieldClass = fieldInfo.getFullName();
503           ReadWriteSet rwSet = getReadWriteSet(currentChoice);
504           int objectId = fieldInfo.getClassInfo().getClassObjectRef();
505           // Record the field in the map
506           rwSet.addReadField(fieldClass, objectId);
507         }
508       }
509     }
510   }
511
512   private int checkAndAdjustChoice(int currentChoice, VM vm) {
513     // If current choice is not the same, then this is caused by the firing of IntIntervalGenerator
514     // for certain method calls in the infrastructure, e.g., eventSince()
515     int currChoiceInd = currentChoice % refChoices.length;
516     int currChoiceFromCG = getCurrentChoice(vm);
517     if (currChoiceInd != currChoiceFromCG) {
518       currentChoice = (currentChoice - currChoiceInd) + currChoiceFromCG;
519     }
520     return currentChoice;
521   }
522
523   private void createBacktrackingPoint(int currentChoice, int confEvtNum) {
524
525     // Create a new list of choices for backtrack based on the current choice and conflicting event number
526     // E.g. if we have a conflict between 1 and 3, then we create the list {3, 1, 0, 2}
527     // for the original set {0, 1, 2, 3}
528     Integer[] newChoiceList = new Integer[refChoices.length];
529     // Put the conflicting event numbers first and reverse the order
530     int actualCurrCho = currentChoice % refChoices.length;
531     int actualConfEvtNum = confEvtNum % refChoices.length;
532     newChoiceList[0] = refChoices[actualCurrCho];
533     newChoiceList[1] = refChoices[actualConfEvtNum];
534     // Put the rest of the event numbers into the array starting from the minimum to the upper bound
535     for (int i = 0, j = 2; i < refChoices.length; i++) {
536       if (refChoices[i] != newChoiceList[0] && refChoices[i] != newChoiceList[1]) {
537         newChoiceList[j] = refChoices[i];
538         j++;
539       }
540     }
541     // Record the backtracking point in the stack as well
542     IntChoiceFromSet backtrackCG = cgList.get(confEvtNum);
543     BacktrackPoint backtrackPoint = new BacktrackPoint(backtrackCG, newChoiceList);
544     btrckPtsStack.push(backtrackPoint);
545     // Also record the CG in the set
546     btrckCGSet.add(backtrackCG);
547   }
548
549   private boolean excludeThisForItContains(String[] excludedStrings, String className) {
550     for (String excludedField : excludedStrings) {
551       if (className.contains(excludedField)) {
552         return true;
553       }
554     }
555     return false;
556   }
557
558   private boolean excludeThisForItEndsWith(String[] excludedStrings, String className) {
559     for (String excludedField : excludedStrings) {
560       if (className.endsWith(excludedField)) {
561         return true;
562       }
563     }
564     return false;
565   }
566
567   private boolean excludeThisForItStartsWith(String[] excludedStrings, String className) {
568     for (String excludedField : excludedStrings) {
569       if (className.startsWith(excludedField)) {
570         return true;
571       }
572     }
573     return false;
574   }
575
576   // TODO: THIS METHOD IS STILL UNTESTED AT THIS POINT
577   private void exploreNextBacktrackSets(IntChoiceFromSet icsCG) {
578     // We try to update the CG with a backtrack list if the state has been visited multiple times
579     if (icsCG.getNextChoiceIndex() > 0) {
580       if (btrckPtsStack.empty()) {
581         // TODO: PROBABLY NEED TO DO CONTEXT SWITCHING HERE
582         return;
583       }
584       BacktrackPoint backtrackPoint = btrckPtsStack.pop();
585       Integer[] choiceList = backtrackPoint.getBacktrackChoices();
586       IntChoiceFromSet backtrackCG = backtrackPoint.getBacktrackCG();
587       // Deploy the new choice list for this CG
588       backtrackCG.setNewValues(choiceList);
589       backtrackCG.reset();
590       // Clear unused CGs
591       for(IntChoiceFromSet cg : cgList) {
592         if (!btrckCGSet.contains(cg)) {
593           cg.setDone();
594         }
595       }
596       cgList.clear();
597       btrckCGSet.clear();
598       // Save all the visited states when starting a new execution of trace
599       prevVisitedStates.addAll(currVisitedStates);
600       currVisitedStates.clear();
601     }
602   }
603
604   private int getCurrentChoice(VM vm) {
605     ChoiceGenerator<?> currentCG = vm.getChoiceGenerator();
606     // This is the main event CG
607     if (currentCG instanceof IntChoiceFromSet) {
608       return ((IntChoiceFromSet) currentCG).getNextChoiceIndex();
609     } else {
610       // This is the interval CG used in device handlers
611       ChoiceGenerator<?> parentCG = ((IntIntervalGenerator) currentCG).getPreviousChoiceGenerator();
612       return ((IntChoiceFromSet) parentCG).getNextChoiceIndex();
613     }
614   }
615
616   private ReadWriteSet getReadWriteSet(int currentChoice) {
617     // Do the analysis to get Read and Write accesses to fields
618     ReadWriteSet rwSet;
619     // We already have an entry
620     if (readWriteFieldsMap.containsKey(currentChoice)) {
621       rwSet = readWriteFieldsMap.get(currentChoice);
622     } else { // We need to create a new entry
623       rwSet = new ReadWriteSet();
624       readWriteFieldsMap.put(currentChoice, rwSet);
625     }
626     return rwSet;
627   }
628
629   private boolean isConflictFound(Instruction nextInsn, int eventCounter, String fieldClass) {
630     // Skip if this event does not have any Read/Write set
631     if (!readWriteFieldsMap.containsKey(eventCounter)) {
632       return false;
633     }
634     ReadWriteSet rwSet = readWriteFieldsMap.get(eventCounter);
635     int currObjId = ((JVMFieldInstruction) nextInsn).getFieldInfo().getClassInfo().getClassObjectRef();
636     // Check for conflicts with Write fields for both Read and Write instructions
637     if (((nextInsn instanceof WriteInstruction || nextInsn instanceof ReadInstruction) &&
638           rwSet.writeFieldExists(fieldClass) && rwSet.writeFieldObjectId(fieldClass) == currObjId) ||
639          (nextInsn instanceof WriteInstruction && rwSet.readFieldExists(fieldClass) &&
640           rwSet.readFieldObjectId(fieldClass) == currObjId)) {
641       return true;
642     }
643     return false;
644   }
645
646   private boolean isFieldExcluded(String field) {
647     // Check against "starts-with", "ends-with", and "contains" list
648     if (excludeThisForItStartsWith(EXCLUDED_FIELDS_STARTS_WITH_LIST, field) ||
649             excludeThisForItEndsWith(EXCLUDED_FIELDS_ENDS_WITH_LIST, field) ||
650             excludeThisForItContains(EXCLUDED_FIELDS_CONTAINS_LIST, field)) {
651       return true;
652     }
653
654     return false;
655   }
656
657   private boolean isNewConflict(int currentEvent, int eventNumber) {
658     HashSet<Integer> conflictSet;
659     if (!conflictPairMap.containsKey(currentEvent)) {
660       conflictSet = new HashSet<>();
661       conflictPairMap.put(currentEvent, conflictSet);
662     } else {
663       conflictSet = conflictPairMap.get(currentEvent);
664     }
665     // If this conflict has been recorded before, we return false because
666     // we don't want to save this backtrack point twice
667     if (conflictSet.contains(eventNumber)) {
668       return false;
669     }
670     // If it hasn't been recorded, then do otherwise
671     conflictSet.add(eventNumber);
672     return true;
673   }
674
675   // --- Functions related to the visible operation dependency graph implementation discussed in the SPIN paper
676
677   // This method checks whether a choice is reachable in the VOD graph from a reference choice (BFS algorithm)
678   //private boolean isReachableInVODGraph(int checkedChoice, int referenceChoice) {
679   private boolean isReachableInVODGraph(int currentChoice) {
680     // Extract previous and current events
681     int choiceIndex = currentChoice % refChoices.length;
682     int currEvent = refChoices[choiceIndex];
683     int prevEvent = refChoices[choiceIndex - 1];
684     // Record visited choices as we search in the graph
685     HashSet<Integer> visitedChoice = new HashSet<>();
686     visitedChoice.add(prevEvent);
687     LinkedList<Integer> nodesToVisit = new LinkedList<>();
688     // If the state doesn't advance as the threads/sub-programs are executed (basically there is no new state),
689     // there is a chance that the graph doesn't have new nodes---thus this check will return a null.
690     if (vodGraphMap.containsKey(prevEvent)) {
691       nodesToVisit.addAll(vodGraphMap.get(prevEvent));
692       while(!nodesToVisit.isEmpty()) {
693         int choice = nodesToVisit.getFirst();
694         if (choice == currEvent) {
695           return true;
696         }
697         if (visitedChoice.contains(choice)) { // If there is a loop then we don't find it
698           return false;
699         }
700         // Continue searching
701         visitedChoice.add(choice);
702         HashSet<Integer> choiceNextNodes = vodGraphMap.get(choice);
703         if (choiceNextNodes != null) {
704           // Add only if there is a mapping for next nodes
705           for (Integer nextNode : choiceNextNodes) {
706             // Skip cycles
707             if (nextNode == choice) {
708               continue;
709             }
710             nodesToVisit.addLast(nextNode);
711           }
712         }
713       }
714     }
715     return false;
716   }
717
718   private void updateVODGraph(int currChoiceValue) {
719     // Update the graph when we have the current choice value
720     HashSet<Integer> choiceSet;
721     if (vodGraphMap.containsKey(prevChoiceValue)) {
722       // If the key already exists, just retrieve it
723       choiceSet = vodGraphMap.get(prevChoiceValue);
724     } else {
725       // Create a new entry
726       choiceSet = new HashSet<>();
727       vodGraphMap.put(prevChoiceValue, choiceSet);
728     }
729     choiceSet.add(currChoiceValue);
730     prevChoiceValue = currChoiceValue;
731   }
732 }