Adding reachability analysis when state matching occurs.
[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 have a graph G
43  * (i.e., visible operation dependency graph).
44  * This DPOR implementation actually fixes the algorithm in the SPIN paper that does not
45  * consider cases where a state could be matched early. In this new algorithm/implementation,
46  * each run is terminated iff:
47  * - we find a state that matches a state in a previous run, or
48  * - we have a matched state in the current run that consists of cycles that contain all choices/events.
49  */
50 public class DPORStateReducer extends ListenerAdapter {
51
52   // Information printout fields for verbose mode
53   private boolean verboseMode;
54   private boolean stateReductionMode;
55   private final PrintWriter out;
56   private String detail;
57   private int depth;
58   private int id;
59   private Transition transition;
60
61   // DPOR-related fields
62   // Basic information
63   private Integer[] choices;
64   private Integer[] refChoices; // Second reference to a copy of choices (choices may be modified for fair scheduling)
65   private int choiceCounter;
66   private int maxEventChoice;
67   // Data structure to track the events seen by each state to track cycles (containing all events) for termination
68   private HashSet<Integer> currVisitedStates; // States being visited in the current execution
69   private HashSet<Integer> justVisitedStates; // States just visited in the previous choice/event
70   private HashSet<Integer> prevVisitedStates; // States visited in the previous execution
71   private HashMap<Integer, HashSet<Integer>> stateToEventMap;
72   // Data structure to analyze field Read/Write accesses and conflicts
73   private HashMap<Integer, LinkedList<Integer[]>> backtrackMap;   // Track created backtracking points
74   private PriorityQueue<Integer> backtrackStateQ;                 // Heap that returns the latest state
75   private ArrayList<BacktrackPoint> backtrackPointList;           // Record backtrack points (CG, state Id, and choice)
76   private HashMap<Integer, HashSet<Integer>> conflictPairMap;     // Record conflicting events
77   private HashSet<String> doneBacktrackSet;                       // Record state ID and trace already constructed
78   private HashMap<Integer, ReadWriteSet> readWriteFieldsMap;      // Record fields that are accessed
79   private HashMap<Integer, RestorableState> restorableStateMap;   // Maps state IDs to the restorable state object
80
81   // Boolean states
82   private boolean isBooleanCGFlipped;
83   private boolean isEndOfExecution;
84
85   // Statistics
86   private int numOfConflicts;
87   private int numOfTransitions;
88         
89   public DPORStateReducer(Config config, JPF jpf) {
90     verboseMode = config.getBoolean("printout_state_transition", false);
91     stateReductionMode = config.getBoolean("activate_state_reduction", true);
92     if (verboseMode) {
93       out = new PrintWriter(System.out, true);
94     } else {
95       out = null;
96     }
97     isBooleanCGFlipped = false;
98                 numOfConflicts = 0;
99                 numOfTransitions = 0;
100     restorableStateMap = new HashMap<>();
101     initializeStatesVariables();
102   }
103
104   @Override
105   public void stateRestored(Search search) {
106     if (verboseMode) {
107       id = search.getStateId();
108       depth = search.getDepth();
109       transition = search.getTransition();
110       detail = null;
111       out.println("\n==> DEBUG: The state is restored to state with id: " + id + " -- Transition: " + transition +
112               " and depth: " + depth + "\n");
113     }
114   }
115
116   @Override
117   public void searchStarted(Search search) {
118     if (verboseMode) {
119       out.println("\n==> DEBUG: ----------------------------------- search started" + "\n");
120     }
121   }
122
123   @Override
124   public void stateAdvanced(Search search) {
125     if (verboseMode) {
126       id = search.getStateId();
127       depth = search.getDepth();
128       transition = search.getTransition();
129       if (search.isNewState()) {
130         detail = "new";
131       } else {
132         detail = "visited";
133       }
134
135       if (search.isEndState()) {
136         out.println("\n==> DEBUG: This is the last state!\n");
137         detail += " end";
138       }
139       out.println("\n==> DEBUG: The state is forwarded to state with id: " + id + " with depth: " + depth +
140               " which is " + detail + " Transition: " + transition + "\n");
141     }
142     if (stateReductionMode) {
143       updateStateInfo(search);
144     }
145   }
146
147   @Override
148   public void stateBacktracked(Search search) {
149     if (verboseMode) {
150       id = search.getStateId();
151       depth = search.getDepth();
152       transition = search.getTransition();
153       detail = null;
154
155       out.println("\n==> DEBUG: The state is backtracked to state with id: " + id + " -- Transition: " + transition +
156               " and depth: " + depth + "\n");
157     }
158     if (stateReductionMode) {
159       updateStateInfo(search);
160     }
161   }
162
163   @Override
164   public void searchFinished(Search search) {
165     if (stateReductionMode) {
166       // Number of conflicts = first trace + subsequent backtrack points
167       numOfConflicts += 1 + doneBacktrackSet.size();
168     }
169     if (verboseMode) {
170       out.println("\n==> DEBUG: ----------------------------------- search finished");
171       out.println("\n==> DEBUG: State reduction mode  : " + stateReductionMode);
172       out.println("\n==> DEBUG: Number of conflicts   : " + numOfConflicts);
173       out.println("\n==> DEBUG: Number of transitions : " + numOfTransitions);
174       out.println("\n==> DEBUG: ----------------------------------- search finished" + "\n");
175     }
176   }
177
178   @Override
179   public void choiceGeneratorRegistered(VM vm, ChoiceGenerator<?> nextCG, ThreadInfo currentThread, Instruction executedInstruction) {
180     if (stateReductionMode) {
181       // Initialize with necessary information from the CG
182       if (nextCG instanceof IntChoiceFromSet) {
183         IntChoiceFromSet icsCG = (IntChoiceFromSet) nextCG;
184         if (!isEndOfExecution) {
185           // Check if CG has been initialized, otherwise initialize it
186           Integer[] cgChoices = icsCG.getAllChoices();
187           // Record the events (from choices)
188           if (choices == null) {
189             choices = cgChoices;
190             // Make a copy of choices as reference
191             refChoices = copyChoices(choices);
192             // Record the max event choice (the last element of the choice array)
193             maxEventChoice = choices[choices.length - 1];
194           }
195           icsCG.setNewValues(choices);
196           icsCG.reset();
197           // Use a modulo since choiceCounter is going to keep increasing
198           int choiceIndex = choiceCounter % choices.length;
199           icsCG.advance(choices[choiceIndex]);
200         } else {
201           // Set done all CGs while transitioning to a new execution
202           icsCG.setDone();
203         }
204       }
205     }
206   }
207
208   @Override
209   public void choiceGeneratorAdvanced(VM vm, ChoiceGenerator<?> currentCG) {
210
211     if (stateReductionMode) {
212       // Check the boolean CG and if it is flipped, we are resetting the analysis
213       if (currentCG instanceof BooleanChoiceGenerator) {
214         if (!isBooleanCGFlipped) {
215           isBooleanCGFlipped = true;
216         } else {
217           // Number of conflicts = first trace + subsequent backtrack points
218           numOfConflicts = 1 + doneBacktrackSet.size();
219           // Allocate new objects for data structure when the boolean is flipped from "false" to "true"
220           initializeStatesVariables();
221         }
222       }
223       // Check every choice generated and ensure fair scheduling!
224       if (currentCG instanceof IntChoiceFromSet) {
225         IntChoiceFromSet icsCG = (IntChoiceFromSet) currentCG;
226         // If this is a new CG then we need to update data structures
227         resetStatesForNewExecution(icsCG, vm);
228         // If we don't see a fair scheduling of events/choices then we have to enforce it
229         fairSchedulingAndBacktrackPoint(icsCG, vm);
230         // Map state to event
231         mapStateToEvent(icsCG.getNextChoice());
232         // Explore the next backtrack point: 
233         // 1) if we have seen this state or this state contains cycles that involve all events, and
234         // 2) after the current CG is advanced at least once
235         if (terminateCurrentExecution() && choiceCounter > 0) {
236           exploreNextBacktrackPoints(vm, icsCG);
237         } else {
238           numOfTransitions++;
239         }
240         justVisitedStates.clear();
241         choiceCounter++;
242       }
243     } else {
244       numOfTransitions++;
245     }
246   }
247
248   @Override
249   public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
250     if (stateReductionMode) {
251       if (!isEndOfExecution) {
252         // Has to be initialized and a integer CG
253         ChoiceGenerator<?> cg = vm.getChoiceGenerator();
254         if (cg instanceof IntChoiceFromSet || cg instanceof IntIntervalGenerator) {
255           int currentChoice = choiceCounter - 1;  // Accumulative choice w.r.t the current trace
256           if (currentChoice < 0) { // If choice is -1 then skip
257             return;
258           }
259           currentChoice = checkAndAdjustChoice(currentChoice, vm);
260           // Record accesses from executed instructions
261           if (executedInsn instanceof JVMFieldInstruction) {
262             // Analyze only after being initialized
263             String fieldClass = ((JVMFieldInstruction) executedInsn).getFieldInfo().getFullName();
264             // We don't care about libraries
265             if (!isFieldExcluded(fieldClass)) {
266               analyzeReadWriteAccesses(executedInsn, fieldClass, currentChoice);
267             }
268           } else if (executedInsn instanceof INVOKEINTERFACE) {
269             // Handle the read/write accesses that occur through iterators
270             analyzeReadWriteAccesses(executedInsn, ti, currentChoice);
271           }
272           // Analyze conflicts from next instructions
273           if (nextInsn instanceof JVMFieldInstruction) {
274             // Skip the constructor because it is called once and does not have shared access with other objects
275             if (!nextInsn.getMethodInfo().getName().equals("<init>")) {
276               String fieldClass = ((JVMFieldInstruction) nextInsn).getFieldInfo().getFullName();
277               if (!isFieldExcluded(fieldClass)) {
278                 // Check for conflict (go backward from current choice and get the first conflict)
279                 for (int eventCounter = currentChoice - 1; eventCounter >= 0; eventCounter--) {
280                   // Check for conflicts with Write fields for both Read and Write instructions
281                   // Check and record a backtrack set for just once!
282                   if (isConflictFound(nextInsn, eventCounter, currentChoice, fieldClass) &&
283                       isNewConflict(currentChoice, eventCounter)) {
284                     createBacktrackingPoint(currentChoice, eventCounter);
285                   }
286                 }
287               }
288             }
289           }
290         }
291       }
292     }
293   }
294
295
296   // == HELPERS
297
298   // -- INNER CLASSES
299
300   // This class compactly stores Read and Write field sets
301   // We store the field name and its object ID
302   // Sharing the same field means the same field name and object ID
303   private class ReadWriteSet {
304     private HashMap<String, Integer> readSet;
305     private HashMap<String, Integer> writeSet;
306
307     public ReadWriteSet() {
308       readSet = new HashMap<>();
309       writeSet = new HashMap<>();
310     }
311
312     public void addReadField(String field, int objectId) {
313       readSet.put(field, objectId);
314     }
315
316     public void addWriteField(String field, int objectId) {
317       writeSet.put(field, objectId);
318     }
319
320     public Set<String> getReadSet() {
321       return readSet.keySet();
322     }
323
324     public Set<String> getWriteSet() {
325       return writeSet.keySet();
326     }
327
328     public boolean readFieldExists(String field) {
329       return readSet.containsKey(field);
330     }
331
332     public boolean writeFieldExists(String field) {
333       return writeSet.containsKey(field);
334     }
335
336     public int readFieldObjectId(String field) {
337       return readSet.get(field);
338     }
339
340     public int writeFieldObjectId(String field) {
341       return writeSet.get(field);
342     }
343   }
344
345   // This class compactly stores backtrack points: 1) backtrack state ID, and 2) backtracking choices
346   private class BacktrackPoint {
347     private IntChoiceFromSet backtrackCG; // CG at this backtrack point
348     private int stateId;                  // State at this backtrack point
349     private int choice;                   // Choice chosen at this backtrack point
350
351     public BacktrackPoint(IntChoiceFromSet cg, int stId, int cho) {
352       backtrackCG = cg;
353       stateId = stId;
354       choice = cho;
355     }
356
357     public IntChoiceFromSet getBacktrackCG() { return backtrackCG; }
358
359     public int getStateId() {
360       return stateId;
361     }
362
363     public int getChoice() {
364       return choice;
365     }
366   }
367
368   // This class compactly stores: 1) restorable VM state, and 2) global choice counter
369   private class RestorableState {
370     private RestorableVMState restorableState;
371     private int choiceCounter;
372
373     public RestorableState (RestorableVMState restState, int choCtr) {
374       restorableState = restState;
375       choiceCounter = choCtr;
376     }
377
378     public RestorableVMState getRestorableState() {
379       return restorableState;
380     }
381
382     public int getChoiceCounter() {
383       return choiceCounter;
384     }
385   }
386
387   // -- CONSTANTS
388   private final static String DO_CALL_METHOD = "doCall";
389   // We exclude fields that come from libraries (Java and Groovy), and also the infrastructure
390   private final static String[] EXCLUDED_FIELDS_CONTAINS_LIST = {"_closure"};
391   private final static String[] EXCLUDED_FIELDS_ENDS_WITH_LIST =
392           // Groovy library created fields
393           {"stMC", "callSiteArray", "metaClass", "staticClassInfo", "__constructor__",
394           // Infrastructure
395           "sendEvent", "Object", "reference", "location", "app", "state", "log", "functionList", "objectList",
396           "eventList", "valueList", "settings", "printToConsole", "app1", "app2"};
397   private final static String[] EXCLUDED_FIELDS_STARTS_WITH_LIST =
398           // Java and Groovy libraries
399           { "java", "org", "sun", "com", "gov", "groovy"};
400   private final static String[] EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST = {"Event"};
401   private final static String GET_PROPERTY_METHOD =
402           "invokeinterface org.codehaus.groovy.runtime.callsite.CallSite.callGetProperty";
403   private final static String GROOVY_CALLSITE_LIB = "org.codehaus.groovy.runtime.callsite";
404   private final static String JAVA_INTEGER = "int";
405   private final static String JAVA_STRING_LIB = "java.lang.String";
406
407   // -- FUNCTIONS
408   private void fairSchedulingAndBacktrackPoint(IntChoiceFromSet icsCG, VM vm) {
409     // Check the next choice and if the value is not the same as the expected then force the expected value
410     int choiceIndex = choiceCounter % refChoices.length;
411     int nextChoice = icsCG.getNextChoice();
412     if (refChoices[choiceIndex] != nextChoice) {
413       int expectedChoice = refChoices[choiceIndex];
414       int currCGIndex = icsCG.getNextChoiceIndex();
415       if ((currCGIndex >= 0) && (currCGIndex < refChoices.length)) {
416         icsCG.setChoice(currCGIndex, expectedChoice);
417       }
418     }
419     // Record state ID and choice/event as backtrack point
420     int stateId = vm.getStateId();
421     backtrackPointList.add(new BacktrackPoint(icsCG, stateId, refChoices[choiceIndex]));
422     // Store restorable state object for this state (always store the latest)
423     RestorableVMState restorableState = vm.getRestorableState();
424     restorableStateMap.put(stateId, new RestorableState(restorableState, choiceCounter));
425   }
426
427   private Integer[] copyChoices(Integer[] choicesToCopy) {
428
429     Integer[] copyOfChoices = new Integer[choicesToCopy.length];
430     System.arraycopy(choicesToCopy, 0, copyOfChoices, 0, choicesToCopy.length);
431     return copyOfChoices;
432   }
433
434   // --- Functions related to cycle detection
435
436   // Detect cycles in the current execution/trace
437   // We terminate the execution iff:
438   // (1) the state has been visited in the current execution
439   // (2) the state has one or more cycles that involve all the events
440   // With simple approach we only need to check for a re-visited state.
441   // Basically, we have to check that we have executed all events between two occurrences of such state.
442   private boolean containsCyclesWithAllEvents(int stId) {
443
444     // False if the state ID hasn't been recorded
445     if (!stateToEventMap.containsKey(stId)) {
446       return false;
447     }
448     HashSet<Integer> visitedEvents = stateToEventMap.get(stId);
449     // Check if this set contains all the event choices
450     // If not then this is not the terminating condition
451     for(int i=0; i<=maxEventChoice; i++) {
452       if (!visitedEvents.contains(i)) {
453         return false;
454       }
455     }
456     return true;
457   }
458
459   private void initializeStatesVariables() {
460     // DPOR-related
461     choices = null;
462     refChoices = null;
463     choiceCounter = 0;
464     maxEventChoice = 0;
465     // Cycle tracking
466     currVisitedStates = new HashSet<>();
467     justVisitedStates = new HashSet<>();
468     prevVisitedStates = new HashSet<>();
469     stateToEventMap = new HashMap<>();
470     // Backtracking
471     backtrackMap = new HashMap<>();
472     backtrackStateQ = new PriorityQueue<>(Collections.reverseOrder());
473     backtrackPointList = new ArrayList<>();
474     conflictPairMap = new HashMap<>();
475     doneBacktrackSet = new HashSet<>();
476     readWriteFieldsMap = new HashMap<>();
477     // Booleans
478     isEndOfExecution = false;
479   }
480
481   private void mapStateToEvent(int nextChoiceValue) {
482     // Update all states with this event/choice
483     // This means that all past states now see this transition
484     Set<Integer> stateSet = stateToEventMap.keySet();
485     for(Integer stateId : stateSet) {
486       HashSet<Integer> eventSet = stateToEventMap.get(stateId);
487       eventSet.add(nextChoiceValue);
488     }
489   }
490
491   private boolean terminateCurrentExecution() {
492     // We need to check all the states that have just been visited
493     // Often a transition (choice/event) can result into forwarding/backtracking to a number of states
494     for(Integer stateId : justVisitedStates) {
495       if (prevVisitedStates.contains(stateId) || containsCyclesWithAllEvents(stateId)) {
496         return true;
497       }
498     }
499     return false;
500   }
501
502   private void updateStateInfo(Search search) {
503     // Update the state variables
504     // Line 19 in the paper page 11 (see the heading note above)
505     int stateId = search.getStateId();
506     currVisitedStates.add(stateId);
507     // Insert state ID into the map if it is new
508     if (!stateToEventMap.containsKey(stateId)) {
509       HashSet<Integer> eventSet = new HashSet<>();
510       stateToEventMap.put(stateId, eventSet);
511     }
512     justVisitedStates.add(stateId);
513     analyzeReachabilityAndCreateBacktrackPoints(search.getVM(), stateId);
514   }
515
516   // --- Functions related to Read/Write access analysis on shared fields
517
518   private void addNewBacktrackPoint(int stateId, Integer[] newChoiceList) {
519     // Insert backtrack point to the right state ID
520     LinkedList<Integer[]> backtrackList;
521     if (backtrackMap.containsKey(stateId)) {
522       backtrackList = backtrackMap.get(stateId);
523     } else {
524       backtrackList = new LinkedList<>();
525       backtrackMap.put(stateId, backtrackList);
526     }
527     backtrackList.addFirst(newChoiceList);
528     // Add to priority queue
529     if (!backtrackStateQ.contains(stateId)) {
530       backtrackStateQ.add(stateId);
531     }
532   }
533
534   // Analyze Read/Write accesses that are directly invoked on fields
535   private void analyzeReadWriteAccesses(Instruction executedInsn, String fieldClass, int currentChoice) {
536     // Do the analysis to get Read and Write accesses to fields
537     ReadWriteSet rwSet = getReadWriteSet(currentChoice);
538     int objectId = ((JVMFieldInstruction) executedInsn).getFieldInfo().getClassInfo().getClassObjectRef();
539     // Record the field in the map
540     if (executedInsn instanceof WriteInstruction) {
541       // Exclude certain field writes because of infrastructure needs, e.g., Event class field writes
542       for (String str : EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST) {
543         if (fieldClass.startsWith(str)) {
544           return;
545         }
546       }
547       rwSet.addWriteField(fieldClass, objectId);
548     } else if (executedInsn instanceof ReadInstruction) {
549       rwSet.addReadField(fieldClass, objectId);
550     }
551   }
552
553   // Analyze Read accesses that are indirect (performed through iterators)
554   // These accesses are marked by certain bytecode instructions, e.g., INVOKEINTERFACE
555   private void analyzeReadWriteAccesses(Instruction instruction, ThreadInfo ti, int currentChoice) {
556     // Get method name
557     INVOKEINTERFACE insn = (INVOKEINTERFACE) instruction;
558     if (insn.toString().startsWith(GET_PROPERTY_METHOD) &&
559             insn.getMethodInfo().getName().equals(DO_CALL_METHOD)) {
560       // Extract info from the stack frame
561       StackFrame frame = ti.getTopFrame();
562       int[] frameSlots = frame.getSlots();
563       // Get the Groovy callsite library at index 0
564       ElementInfo eiCallsite = VM.getVM().getHeap().get(frameSlots[0]);
565       if (!eiCallsite.getClassInfo().getName().startsWith(GROOVY_CALLSITE_LIB)) {
566         return;
567       }
568       // Get the iterated object whose property is accessed
569       ElementInfo eiAccessObj = VM.getVM().getHeap().get(frameSlots[1]);
570       if (eiAccessObj == null) {
571         return;
572       }
573       // We exclude library classes (they start with java, org, etc.) and some more
574       String objClassName = eiAccessObj.getClassInfo().getName();
575       if (excludeThisForItStartsWith(EXCLUDED_FIELDS_STARTS_WITH_LIST, objClassName) ||
576           excludeThisForItStartsWith(EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST, objClassName)) {
577         return;
578       }
579       // Extract fields from this object and put them into the read write
580       int numOfFields = eiAccessObj.getNumberOfFields();
581       for(int i=0; i<numOfFields; i++) {
582         FieldInfo fieldInfo = eiAccessObj.getFieldInfo(i);
583         if (fieldInfo.getType().equals(JAVA_STRING_LIB) || fieldInfo.getType().equals(JAVA_INTEGER)) {
584           String fieldClass = fieldInfo.getFullName();
585           ReadWriteSet rwSet = getReadWriteSet(currentChoice);
586           int objectId = fieldInfo.getClassInfo().getClassObjectRef();
587           // Record the field in the map
588           rwSet.addReadField(fieldClass, objectId);
589         }
590       }
591     }
592   }
593
594   private int checkAndAdjustChoice(int currentChoice, VM vm) {
595     // If current choice is not the same, then this is caused by the firing of IntIntervalGenerator
596     // for certain method calls in the infrastructure, e.g., eventSince()
597     int currChoiceInd = currentChoice % refChoices.length;
598     int currChoiceFromCG = currChoiceInd;
599     ChoiceGenerator<?> currentCG = vm.getChoiceGenerator();
600     // This is the main event CG
601     if (currentCG instanceof IntIntervalGenerator) {
602       // This is the interval CG used in device handlers
603       ChoiceGenerator<?> parentCG = ((IntIntervalGenerator) currentCG).getPreviousChoiceGenerator();
604       int actualEvtNum = ((IntChoiceFromSet) parentCG).getNextChoice();
605       // Find the index of the event/choice in refChoices
606       for (int i = 0; i<refChoices.length; i++) {
607         if (actualEvtNum == refChoices[i]) {
608           currChoiceFromCG = i;
609           break;
610         }
611       }
612     }
613     if (currChoiceInd != currChoiceFromCG) {
614       currentChoice = (currentChoice - currChoiceInd) + currChoiceFromCG;
615     }
616     return currentChoice;
617   }
618
619   private void createBacktrackingPoint(int currentChoice, int confEvtNum) {
620
621     // Create a new list of choices for backtrack based on the current choice and conflicting event number
622     // E.g. if we have a conflict between 1 and 3, then we create the list {3, 1, 0, 2}
623     // for the original set {0, 1, 2, 3}
624     Integer[] newChoiceList = new Integer[refChoices.length];
625     // Put the conflicting event numbers first and reverse the order
626     int actualCurrCho = currentChoice % refChoices.length;
627     // We use the actual choices here in case they have been modified/adjusted by the fair scheduling method
628     newChoiceList[0] = choices[actualCurrCho];
629     newChoiceList[1] = backtrackPointList.get(confEvtNum).getChoice();
630     // Put the rest of the event numbers into the array starting from the minimum to the upper bound
631     for (int i = 0, j = 2; i < refChoices.length; i++) {
632       if (refChoices[i] != newChoiceList[0] && refChoices[i] != newChoiceList[1]) {
633         newChoiceList[j] = refChoices[i];
634         j++;
635       }
636     }
637     // Get the backtrack CG for this backtrack point
638     int stateId = backtrackPointList.get(confEvtNum).getStateId();
639     // Check if this trace has been done starting from this state
640     if (isTraceAlreadyConstructed(newChoiceList, stateId)) {
641       return;
642     }
643     addNewBacktrackPoint(stateId, newChoiceList);
644   }
645
646   private boolean excludeThisForItContains(String[] excludedStrings, String className) {
647     for (String excludedField : excludedStrings) {
648       if (className.contains(excludedField)) {
649         return true;
650       }
651     }
652     return false;
653   }
654
655   private boolean excludeThisForItEndsWith(String[] excludedStrings, String className) {
656     for (String excludedField : excludedStrings) {
657       if (className.endsWith(excludedField)) {
658         return true;
659       }
660     }
661     return false;
662   }
663
664   private boolean excludeThisForItStartsWith(String[] excludedStrings, String className) {
665     for (String excludedField : excludedStrings) {
666       if (className.startsWith(excludedField)) {
667         return true;
668       }
669     }
670     return false;
671   }
672
673   private void exploreNextBacktrackPoints(VM vm, IntChoiceFromSet icsCG) {
674
675                 // Check if we are reaching the end of our execution: no more backtracking points to explore
676                 // cgMap, backtrackMap, backtrackStateQ are updated simultaneously (checking backtrackStateQ is enough)
677                 if (!backtrackStateQ.isEmpty()) {
678                         // Set done all the other backtrack points
679                         for (BacktrackPoint backtrackPoint : backtrackPointList) {
680                                 backtrackPoint.getBacktrackCG().setDone();
681                         }
682                         // Reset the next backtrack point with the latest state
683                         int hiStateId = backtrackStateQ.peek();
684                         // Restore the state first if necessary
685                         if (vm.getStateId() != hiStateId) {
686                                 RestorableVMState restorableState = restorableStateMap.get(hiStateId).getRestorableState();
687                                 vm.restoreState(restorableState);
688                         }
689                         // Set the backtrack CG
690                         IntChoiceFromSet backtrackCG = (IntChoiceFromSet) vm.getChoiceGenerator();
691                         setBacktrackCG(hiStateId, backtrackCG);
692                 } else {
693                         // Set done this last CG (we save a few rounds)
694                         icsCG.setDone();
695                 }
696                 // Save all the visited states when starting a new execution of trace
697                 prevVisitedStates.addAll(currVisitedStates);
698                 currVisitedStates.clear();
699                 // This marks a transitional period to the new CG
700                 isEndOfExecution = true;
701   }
702
703   private ReadWriteSet getReadWriteSet(int currentChoice) {
704     // Do the analysis to get Read and Write accesses to fields
705     ReadWriteSet rwSet;
706     // We already have an entry
707     if (readWriteFieldsMap.containsKey(currentChoice)) {
708       rwSet = readWriteFieldsMap.get(currentChoice);
709     } else { // We need to create a new entry
710       rwSet = new ReadWriteSet();
711       readWriteFieldsMap.put(currentChoice, rwSet);
712     }
713     return rwSet;
714   }
715
716   private boolean isConflictFound(int eventCounter, int currentChoice) {
717
718     int actualCurrCho = currentChoice % refChoices.length;
719     // Skip if this event does not have any Read/Write set or the two events are basically the same event (number)
720     if (!readWriteFieldsMap.containsKey(eventCounter) ||
721             choices[actualCurrCho] == backtrackPointList.get(eventCounter).getChoice()) {
722       return false;
723     }
724     // Current R/W set
725     ReadWriteSet currRWSet = readWriteFieldsMap.get(currentChoice);
726     // R/W set of choice/event that may have a potential conflict
727     ReadWriteSet evtRWSet = readWriteFieldsMap.get(eventCounter);
728     // Check for conflicts with Read and Write fields for Write instructions
729     Set<String> currWriteSet = currRWSet.getWriteSet();
730     for(String writeField : currWriteSet) {
731       int currObjId = currRWSet.writeFieldObjectId(writeField);
732       if ((evtRWSet.readFieldExists(writeField) && evtRWSet.readFieldObjectId(writeField) == currObjId) ||
733           (evtRWSet.writeFieldExists(writeField) && evtRWSet.writeFieldObjectId(writeField) == currObjId)) {
734         return true;
735       }
736     }
737     // Check for conflicts with Write fields for Read instructions
738     Set<String> currReadSet = currRWSet.getReadSet();
739     for(String readField : currReadSet) {
740       int currObjId = currRWSet.readFieldObjectId(readField);
741       if (evtRWSet.writeFieldExists(readField) && evtRWSet.writeFieldObjectId(readField) == currObjId) {
742         return true;
743       }
744     }
745     // Return false if no conflict is found
746     return false;
747   }
748
749   private boolean isConflictFound(Instruction nextInsn, int eventCounter, int currentChoice, String fieldClass) {
750
751     int actualCurrCho = currentChoice % refChoices.length;
752     // Skip if this event does not have any Read/Write set or the two events are basically the same event (number)
753     if (!readWriteFieldsMap.containsKey(eventCounter) ||
754          choices[actualCurrCho] == backtrackPointList.get(eventCounter).getChoice()) {
755       return false;
756     }
757     ReadWriteSet rwSet = readWriteFieldsMap.get(eventCounter);
758     int currObjId = ((JVMFieldInstruction) nextInsn).getFieldInfo().getClassInfo().getClassObjectRef();
759     // Check for conflicts with Write fields for both Read and Write instructions
760     if (((nextInsn instanceof WriteInstruction || nextInsn instanceof ReadInstruction) &&
761           rwSet.writeFieldExists(fieldClass) && rwSet.writeFieldObjectId(fieldClass) == currObjId) ||
762          (nextInsn instanceof WriteInstruction && rwSet.readFieldExists(fieldClass) &&
763           rwSet.readFieldObjectId(fieldClass) == currObjId)) {
764       return true;
765     }
766     return false;
767   }
768
769   private boolean isFieldExcluded(String field) {
770     // Check against "starts-with", "ends-with", and "contains" list
771     if (excludeThisForItStartsWith(EXCLUDED_FIELDS_STARTS_WITH_LIST, field) ||
772             excludeThisForItEndsWith(EXCLUDED_FIELDS_ENDS_WITH_LIST, field) ||
773             excludeThisForItContains(EXCLUDED_FIELDS_CONTAINS_LIST, field)) {
774       return true;
775     }
776
777     return false;
778   }
779
780   private boolean isNewConflict(int currentEvent, int eventNumber) {
781     HashSet<Integer> conflictSet;
782     if (!conflictPairMap.containsKey(currentEvent)) {
783       conflictSet = new HashSet<>();
784       conflictPairMap.put(currentEvent, conflictSet);
785     } else {
786       conflictSet = conflictPairMap.get(currentEvent);
787     }
788     // If this conflict has been recorded before, we return false because
789     // we don't want to save this backtrack point twice
790     if (conflictSet.contains(eventNumber)) {
791       return false;
792     }
793     // If it hasn't been recorded, then do otherwise
794     conflictSet.add(eventNumber);
795     return true;
796   }
797
798   private boolean isTraceAlreadyConstructed(Integer[] choiceList, int stateId) {
799     // Concatenate state ID and only the first event in the string, e.g., "1:1 for the trace 10234 at state 1"
800     // TODO: THIS IS AN OPTIMIZATION!
801     // This is the optimized version because after we execute, e.g., the trace 1:10234, we don't need to try
802     // another trace that starts with event 1 at state 1, e.g., the trace 1:13024
803     // The second time this event 1 is explored, it will generate the same state as the first one
804     StringBuilder sb = new StringBuilder();
805     sb.append(stateId);
806     sb.append(':');
807     sb.append(choiceList[0]);
808     // Check if the trace has been constructed as a backtrack point for this state
809     if (doneBacktrackSet.contains(sb.toString())) {
810       return true;
811     }
812     doneBacktrackSet.add(sb.toString());
813     return false;
814   }
815
816   private void resetStatesForNewExecution(IntChoiceFromSet icsCG, VM vm) {
817     if (choices == null || choices != icsCG.getAllChoices()) {
818       // Reset state variables
819       choiceCounter = 0;
820       choices = icsCG.getAllChoices();
821       refChoices = copyChoices(choices);
822       // Clearing data structures
823       conflictPairMap.clear();
824       readWriteFieldsMap.clear();
825       stateToEventMap.clear();
826       isEndOfExecution = false;
827       backtrackPointList.clear();
828     }
829   }
830
831   private void setBacktrackCG(int stateId, IntChoiceFromSet backtrackCG) {
832     // Set a backtrack CG based on a state ID
833     LinkedList<Integer[]> backtrackChoices = backtrackMap.get(stateId);
834     backtrackCG.setNewValues(backtrackChoices.removeLast());  // Get the last from the queue
835     backtrackCG.setStateId(stateId);
836     backtrackCG.reset();
837     // Remove from the queue if we don't have more backtrack points for that state
838     if (backtrackChoices.isEmpty()) {
839       backtrackMap.remove(stateId);
840       backtrackStateQ.remove(stateId);
841     }
842   }
843
844   // --- Functions related to the reachability analysis when there is a state match
845
846   // We use backtrackPointsList to analyze the reachable states/events when there is a state match:
847   // 1) Whenever there is state match, there is a cycle of events
848   // 2) We need to analyze and find conflicts for the reachable choices/events in the cycle
849   // 3) Then we create a new backtrack point for every new conflict
850   private void analyzeReachabilityAndCreateBacktrackPoints(VM vm, int stateId) {
851     // Perform this analysis only when there is a state match
852     if (!vm.isNewState()) {
853       if (restorableStateMap.containsKey(stateId)) {
854         // Find the choice/event that marks the start of this cycle: first choice we explore for conflicts
855         int conflictChoice = restorableStateMap.get(stateId).getChoiceCounter();
856         int currentChoice = choiceCounter - 1;
857         // Find conflicts between choices/events in this cycle (we scan forward in the cycle, not backward)
858         while (conflictChoice < currentChoice) {
859           for (int eventCounter = conflictChoice + 1; eventCounter <= currentChoice; eventCounter++) {
860             if (isConflictFound(eventCounter, conflictChoice) && isNewConflict(conflictChoice, eventCounter)) {
861               createBacktrackingPoint(conflictChoice, eventCounter);
862             }
863           }
864           conflictChoice++;
865         }
866       }
867     }
868   }
869 }