Adding a handler in the listener to check if the benchmark has no event (in this...
[jpf-core.git] / src / main / gov / nasa / jpf / listener / DPORStateReducerWithSummary.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.jvm.bytecode.INVOKEINTERFACE;
24 import gov.nasa.jpf.jvm.bytecode.JVMFieldInstruction;
25 import gov.nasa.jpf.search.Search;
26 import gov.nasa.jpf.vm.*;
27 import gov.nasa.jpf.vm.bytecode.ReadInstruction;
28 import gov.nasa.jpf.vm.bytecode.WriteInstruction;
29 import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
30 import gov.nasa.jpf.vm.choice.IntIntervalGenerator;
31
32 import java.io.FileWriter;
33 import java.io.IOException;
34 import java.io.PrintWriter;
35 import java.util.*;
36 import java.util.logging.Logger;
37
38 /**
39  * This a DPOR implementation for event-driven applications with loops that create cycles of state matching
40  * In this new DPOR algorithm/implementation, each run is terminated iff:
41  * - we find a state that matches a state in a previous run, or
42  * - we have a matched state in the current run that consists of cycles that contain all choices/events.
43  */
44 public class DPORStateReducerWithSummary extends ListenerAdapter {
45
46   // Information printout fields for verbose mode
47   private boolean verboseMode;
48   private boolean stateReductionMode;
49   private final PrintWriter out;
50   private PrintWriter fileWriter;
51   private String detail;
52   private int depth;
53   private int id;
54   private Transition transition;
55
56   // DPOR-related fields
57   // Basic information
58   private Integer[] choices;
59   private Integer[] refChoices; // Second reference to a copy of choices (choices may be modified for fair scheduling)
60   private int choiceCounter;
61   private int maxEventChoice;
62   // Data structure to track the events seen by each state to track cycles (containing all events) for termination
63   private HashMap<Integer,Integer> currVisitedStates; // States visited in the current execution (maps to frequency)
64   private HashSet<Integer> justVisitedStates;   // States just visited in the previous choice/event
65   private HashSet<Integer> prevVisitedStates;   // States visited in the previous execution
66   private HashSet<ClassInfo> nonRelevantClasses;// Class info objects of non-relevant classes
67   private HashSet<FieldInfo> nonRelevantFields; // Field info objects of non-relevant fields
68   private HashSet<FieldInfo> relevantFields;    // Field info objects of relevant fields
69   private HashMap<Integer, HashSet<Integer>> stateToEventMap;
70   // Data structure to analyze field Read/Write accesses and conflicts
71   private HashMap<Integer, LinkedList<BacktrackExecution>> backtrackMap;  // Track created backtracking points
72   private PriorityQueue<Integer> backtrackStateQ;                 // Heap that returns the latest state
73   private Execution currentExecution;                             // Holds the information about the current execution
74   private HashMap<Integer, HashSet<Integer>> doneBacktrackMap;    // Record state ID and trace already constructed
75   private MainSummary mainSummary;                                // Main summary (M) for state ID, event, and R/W set
76   private HashMap<Integer, PredecessorInfo> stateToPredInfo;      // Predecessor info indexed by state ID
77   private HashMap<Integer, RestorableVMState> restorableStateMap; // Maps state IDs to the restorable state object
78   private RGraph rGraph;                                          // R-Graph for past executions
79
80   // Boolean states
81   private boolean isBooleanCGFlipped;
82   private boolean isEndOfExecution;
83   private boolean isNotCheckedForEventsYet;
84
85   // Statistics
86   private int numOfTransitions;
87
88   public DPORStateReducerWithSummary(Config config, JPF jpf) {
89     verboseMode = config.getBoolean("printout_state_transition", false);
90     stateReductionMode = config.getBoolean("activate_state_reduction", true);
91     if (verboseMode) {
92       out = new PrintWriter(System.out, true);
93     } else {
94       out = null;
95     }
96     String outputFile = config.getString("file_output");
97     if (!outputFile.isEmpty()) {
98       try {
99         fileWriter = new PrintWriter(new FileWriter(outputFile, true), true);
100       } catch (IOException e) {
101       }
102     }
103     isBooleanCGFlipped = false;
104     isNotCheckedForEventsYet = true;
105     mainSummary = new MainSummary();
106     numOfTransitions = 0;
107     nonRelevantClasses = new HashSet<>();
108     nonRelevantFields = new HashSet<>();
109     relevantFields = new HashSet<>();
110     restorableStateMap = new HashMap<>();
111     stateToPredInfo = new HashMap<>();
112     initializeStatesVariables();
113   }
114
115   @Override
116   public void stateRestored(Search search) {
117     if (verboseMode) {
118       id = search.getStateId();
119       depth = search.getDepth();
120       transition = search.getTransition();
121       detail = null;
122       out.println("\n==> DEBUG: The state is restored to state with id: " + id + " -- Transition: " + transition +
123               " and depth: " + depth + "\n");
124     }
125   }
126
127   @Override
128   public void searchStarted(Search search) {
129     if (verboseMode) {
130       out.println("\n==> DEBUG: ----------------------------------- search started" + "\n");
131     }
132   }
133
134   @Override
135   public void stateAdvanced(Search search) {
136     if (verboseMode) {
137       id = search.getStateId();
138       depth = search.getDepth();
139       transition = search.getTransition();
140       if (search.isNewState()) {
141         detail = "new";
142       } else {
143         detail = "visited";
144       }
145
146       if (search.isEndState()) {
147         out.println("\n==> DEBUG: This is the last state!\n");
148         detail += " end";
149       }
150       out.println("\n==> DEBUG: The state is forwarded to state with id: " + id + " with depth: " + depth +
151               " which is " + detail + " Transition: " + transition + "\n");
152     }
153     if (stateReductionMode) {
154       updateStateInfo(search);
155     }
156   }
157
158   @Override
159   public void stateBacktracked(Search search) {
160     if (verboseMode) {
161       id = search.getStateId();
162       depth = search.getDepth();
163       transition = search.getTransition();
164       detail = null;
165
166       out.println("\n==> DEBUG: The state is backtracked to state with id: " + id + " -- Transition: " + transition +
167               " and depth: " + depth + "\n");
168     }
169     if (stateReductionMode) {
170       updateStateInfo(search);
171     }
172   }
173
174   static Logger log = JPF.getLogger("report");
175
176   @Override
177   public void searchFinished(Search search) {
178     if (verboseMode) {
179       out.println("\n==> DEBUG: ----------------------------------- search finished");
180       out.println("\n==> DEBUG: State reduction mode  : " + stateReductionMode);
181       out.println("\n==> DEBUG: Number of transitions : " + numOfTransitions);
182       out.println("\n==> DEBUG: ----------------------------------- search finished" + "\n");
183
184       fileWriter.println("==> DEBUG: State reduction mode  : " + stateReductionMode);
185       fileWriter.println("==> DEBUG: Number of transitions : " + numOfTransitions);
186       fileWriter.println();
187       fileWriter.close();
188     }
189   }
190
191   @Override
192   public void choiceGeneratorRegistered(VM vm, ChoiceGenerator<?> nextCG, ThreadInfo currentThread, Instruction executedInstruction) {
193     if (isNotCheckedForEventsYet) {
194       // Check if this benchmark has no events
195       if (nextCG instanceof IntChoiceFromSet) {
196         IntChoiceFromSet icsCG = (IntChoiceFromSet) nextCG;
197         Integer[] cgChoices = icsCG.getAllChoices();
198         if (cgChoices.length == 2 && cgChoices[0] == 0 && cgChoices[1] == -1) {
199           stateReductionMode = false;
200         }
201         isNotCheckedForEventsYet = false;
202       }
203     }
204     if (stateReductionMode) {
205       // Initialize with necessary information from the CG
206       if (nextCG instanceof IntChoiceFromSet) {
207         IntChoiceFromSet icsCG = (IntChoiceFromSet) nextCG;
208         // Tell JPF that we are performing DPOR
209         icsCG.setDpor();
210         if (!isEndOfExecution) {
211           // Check if CG has been initialized, otherwise initialize it
212           Integer[] cgChoices = icsCG.getAllChoices();
213           // Record the events (from choices)
214           if (choices == null) {
215             choices = cgChoices;
216             // Make a copy of choices as reference
217             refChoices = copyChoices(choices);
218             // Record the max event choice (the last element of the choice array)
219             maxEventChoice = choices[choices.length - 1];
220           }
221           icsCG.setNewValues(choices);
222           icsCG.reset();
223           // Use a modulo since choiceCounter is going to keep increasing
224           int choiceIndex = choiceCounter % choices.length;
225           icsCG.advance(choices[choiceIndex]);
226         } else {
227           // Set done all CGs while transitioning to a new execution
228           icsCG.setDone();
229         }
230       }
231     }
232   }
233
234   @Override
235   public void choiceGeneratorAdvanced(VM vm, ChoiceGenerator<?> currentCG) {
236     if (stateReductionMode) {
237       // Check the boolean CG and if it is flipped, we are resetting the analysis
238       if (currentCG instanceof BooleanChoiceGenerator) {
239         if (!isBooleanCGFlipped) {
240           isBooleanCGFlipped = true;
241         } else {
242           // Allocate new objects for data structure when the boolean is flipped from "false" to "true"
243           initializeStatesVariables();
244         }
245       }
246       // Check every choice generated and ensure fair scheduling!
247       if (currentCG instanceof IntChoiceFromSet) {
248         IntChoiceFromSet icsCG = (IntChoiceFromSet) currentCG;
249         // If this is a new CG then we need to update data structures
250         resetStatesForNewExecution(icsCG, vm);
251         // If we don't see a fair scheduling of events/choices then we have to enforce it
252         ensureFairSchedulingAndSetupTransition(icsCG, vm);
253         // Update backtrack set of an executed event (transition): one transition before this one
254         updateBacktrackSet(currentExecution, choiceCounter - 1);
255         // Explore the next backtrack point:
256         // 1) if we have seen this state or this state contains cycles that involve all events, and
257         // 2) after the current CG is advanced at least once
258         if (choiceCounter > 0 && terminateCurrentExecution()) {
259           exploreNextBacktrackPoints(vm, icsCG);
260         } else {
261           numOfTransitions++;
262         }
263         // Map state to event
264         mapStateToEvent(icsCG.getNextChoice());
265         justVisitedStates.clear();
266         choiceCounter++;
267       }
268     } else {
269       numOfTransitions++;
270     }
271   }
272
273   @Override
274   public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
275     if (stateReductionMode) {
276       if (!isEndOfExecution) {
277         // Has to be initialized and it is a integer CG
278         ChoiceGenerator<?> cg = vm.getChoiceGenerator();
279         if (cg instanceof IntChoiceFromSet || cg instanceof IntIntervalGenerator) {
280           int currentChoice = choiceCounter - 1;  // Accumulative choice w.r.t the current trace
281           if (currentChoice < 0) { // If choice is -1 then skip
282             return;
283           }
284           currentChoice = checkAndAdjustChoice(currentChoice, vm);
285           // Record accesses from executed instructions
286           if (executedInsn instanceof JVMFieldInstruction) {
287             // We don't care about libraries
288             if (!isFieldExcluded(executedInsn)) {
289               analyzeReadWriteAccesses(executedInsn, currentChoice);
290             }
291           } else if (executedInsn instanceof INVOKEINTERFACE) {
292             // Handle the read/write accesses that occur through iterators
293             analyzeReadWriteAccesses(executedInsn, ti, currentChoice);
294           }
295         }
296       }
297     }
298   }
299
300
301   // == HELPERS
302
303   // -- INNER CLASSES
304
305   // This class compactly stores backtrack execution:
306   // 1) backtrack choice list, and
307   // 2) first backtrack point (linking with predecessor execution)
308   private class BacktrackExecution {
309     private Integer[] choiceList;
310     private TransitionEvent firstTransition;
311
312     public BacktrackExecution(Integer[] choList, TransitionEvent fTransition) {
313       choiceList = choList;
314       firstTransition = fTransition;
315     }
316
317     public Integer[] getChoiceList() {
318       return choiceList;
319     }
320
321     public TransitionEvent getFirstTransition() {
322       return firstTransition;
323     }
324   }
325
326   // This class stores a representation of an execution
327   // TODO: We can modify this class to implement some optimization (e.g., clock-vector)
328   // TODO: We basically need to keep track of:
329   // TODO:    (1) last read/write access to each memory location
330   // TODO:    (2) last state with two or more incoming events/transitions
331   private class Execution {
332     private HashMap<IntChoiceFromSet, Integer> cgToChoiceMap;   // Map between CG to choice numbers for O(1) access
333     private ArrayList<TransitionEvent> executionTrace;          // The BacktrackPoint objects of this execution
334     private boolean isNew;                                      // Track if this is the first time it is accessed
335     private HashMap<Integer, ReadWriteSet> readWriteFieldsMap;  // Record fields that are accessed
336
337     public Execution() {
338       cgToChoiceMap = new HashMap<>();
339       executionTrace = new ArrayList<>();
340       isNew = true;
341       readWriteFieldsMap = new HashMap<>();
342     }
343
344     public void addTransition(TransitionEvent newBacktrackPoint) {
345       executionTrace.add(newBacktrackPoint);
346     }
347
348     public void clearCGToChoiceMap() {
349       cgToChoiceMap = null;
350     }
351
352     public int getChoiceFromCG(IntChoiceFromSet icsCG) {
353       return cgToChoiceMap.get(icsCG);
354     }
355
356     public ArrayList<TransitionEvent> getExecutionTrace() {
357       return executionTrace;
358     }
359
360     public TransitionEvent getFirstTransition() {
361       return executionTrace.get(0);
362     }
363
364     public TransitionEvent getLastTransition() {
365       return executionTrace.get(executionTrace.size() - 1);
366     }
367
368     public HashMap<Integer, ReadWriteSet> getReadWriteFieldsMap() {
369       return readWriteFieldsMap;
370     }
371
372     public boolean isNew() {
373       if (isNew) {
374         // Right after this is accessed, it is no longer new
375         isNew = false;
376         return true;
377       }
378       return false;
379     }
380
381     public void mapCGToChoice(IntChoiceFromSet icsCG, int choice) {
382       cgToChoiceMap.put(icsCG, choice);
383     }
384   }
385
386   // This class compactly stores a predecessor
387   // 1) a predecessor execution
388   // 2) the predecessor choice in that predecessor execution
389   private class Predecessor {
390     private int choice;           // Predecessor choice
391     private Execution execution;  // Predecessor execution
392
393     public Predecessor(int predChoice, Execution predExec) {
394       choice = predChoice;
395       execution = predExec;
396     }
397
398     public int getChoice() {
399       return choice;
400     }
401
402     public Execution getExecution() {
403       return execution;
404     }
405   }
406
407   // This class represents a R-Graph (in the paper it is a state transition graph R)
408   // This implementation stores reachable transitions from and connects with past executions
409   private class RGraph {
410     private int hiStateId;                                     // Maximum state Id
411     private HashMap<Integer, HashSet<TransitionEvent>> graph;  // Reachable transitions from past executions
412
413     public RGraph() {
414       hiStateId = 0;
415       graph = new HashMap<>();
416     }
417
418     public void addReachableTransition(int stateId, TransitionEvent transition) {
419       HashSet<TransitionEvent> transitionSet;
420       if (graph.containsKey(stateId)) {
421         transitionSet = graph.get(stateId);
422       } else {
423         transitionSet = new HashSet<>();
424         graph.put(stateId, transitionSet);
425       }
426       // Insert into the set if it does not contain it yet
427       if (!transitionSet.contains(transition)) {
428         transitionSet.add(transition);
429       }
430       // Update highest state ID
431       if (hiStateId < stateId) {
432         hiStateId = stateId;
433       }
434     }
435
436     public HashSet<TransitionEvent> getReachableTransitionsAtState(int stateId) {
437       if (!graph.containsKey(stateId)) {
438         // This is a loop from a transition to itself, so just return the current transition
439         HashSet<TransitionEvent> transitionSet = new HashSet<>();
440         transitionSet.add(currentExecution.getLastTransition());
441         return transitionSet;
442       }
443       return graph.get(stateId);
444     }
445
446     public HashSet<TransitionEvent> getReachableTransitions(int stateId) {
447       HashSet<TransitionEvent> reachableTransitions = new HashSet<>();
448       // All transitions from states higher than the given state ID (until the highest state ID) are reachable
449       for(int stId = stateId; stId <= hiStateId; stId++) {
450         // We might encounter state IDs from the first round of Boolean CG
451         // The second round of Boolean CG should consider these new states
452         if (graph.containsKey(stId)) {
453           reachableTransitions.addAll(graph.get(stId));
454         }
455       }
456       return reachableTransitions;
457     }
458   }
459
460   // This class compactly stores Read and Write field sets
461   // We store the field name and its object ID
462   // Sharing the same field means the same field name and object ID
463   private class ReadWriteSet {
464     private HashMap<String, Integer> readMap;
465     private HashMap<String, Integer> writeMap;
466
467     public ReadWriteSet() {
468       readMap = new HashMap<>();
469       writeMap = new HashMap<>();
470     }
471
472     public void addReadField(String field, int objectId) {
473       readMap.put(field, objectId);
474     }
475
476     public void addWriteField(String field, int objectId) {
477       writeMap.put(field, objectId);
478     }
479
480     public void removeReadField(String field) {
481       readMap.remove(field);
482     }
483
484     public void removeWriteField(String field) {
485       writeMap.remove(field);
486     }
487
488     public boolean isEmpty() {
489       return readMap.isEmpty() && writeMap.isEmpty();
490     }
491
492     public ReadWriteSet getCopy() {
493       ReadWriteSet copyRWSet = new ReadWriteSet();
494       // Copy the maps in the set into the new object copy
495       copyRWSet.setReadMap(new HashMap<>(this.getReadMap()));
496       copyRWSet.setWriteMap(new HashMap<>(this.getWriteMap()));
497       return copyRWSet;
498     }
499
500     public Set<String> getReadSet() {
501       return readMap.keySet();
502     }
503
504     public Set<String> getWriteSet() {
505       return writeMap.keySet();
506     }
507
508     public boolean readFieldExists(String field) {
509       return readMap.containsKey(field);
510     }
511
512     public boolean writeFieldExists(String field) {
513       return writeMap.containsKey(field);
514     }
515
516     public int readFieldObjectId(String field) {
517       return readMap.get(field);
518     }
519
520     public int writeFieldObjectId(String field) {
521       return writeMap.get(field);
522     }
523
524     private HashMap<String, Integer> getReadMap() {
525       return readMap;
526     }
527
528     private HashMap<String, Integer> getWriteMap() {
529       return writeMap;
530     }
531
532     private void setReadMap(HashMap<String, Integer> rMap) {
533       readMap = rMap;
534     }
535
536     private void setWriteMap(HashMap<String, Integer> wMap) {
537       writeMap = wMap;
538     }
539   }
540
541   // This class is a representation of a state.
542   // It stores the predecessors to a state.
543   // TODO: We also have stateToEventMap, restorableStateMap, and doneBacktrackMap that has state Id as HashMap key.
544   private class PredecessorInfo {
545     private HashSet<Predecessor> predecessors;  // Maps incoming events/transitions (execution and choice)
546     private HashMap<Execution, HashSet<Integer>> recordedPredecessors;
547                                                 // Memorize event and choice number to not record them twice
548
549     public PredecessorInfo() {
550       predecessors = new HashSet<>();
551       recordedPredecessors = new HashMap<>();
552     }
553
554     public HashSet<Predecessor> getPredecessors() {
555       return predecessors;
556     }
557
558     private boolean isRecordedPredecessor(Execution execution, int choice) {
559       // See if we have recorded this predecessor earlier
560       HashSet<Integer> recordedChoices;
561       if (recordedPredecessors.containsKey(execution)) {
562         recordedChoices = recordedPredecessors.get(execution);
563         if (recordedChoices.contains(choice)) {
564           return true;
565         }
566       } else {
567         recordedChoices = new HashSet<>();
568         recordedPredecessors.put(execution, recordedChoices);
569       }
570       // Record the choice if we haven't seen it
571       recordedChoices.add(choice);
572
573       return false;
574     }
575
576     public void recordPredecessor(Execution execution, int choice) {
577       if (!isRecordedPredecessor(execution, choice)) {
578         predecessors.add(new Predecessor(choice, execution));
579       }
580     }
581   }
582
583   // This class compactly stores transitions:
584   // 1) CG,
585   // 2) state ID,
586   // 3) choice,
587   // 4) predecessors (for backward DFS).
588   private class TransitionEvent {
589     private int choice;                        // Choice chosen at this transition
590     private int choiceCounter;                 // Choice counter at this transition
591     private Execution execution;               // The execution where this transition belongs
592     private int stateId;                       // State at this transition
593     private IntChoiceFromSet transitionCG;     // CG at this transition
594
595     public TransitionEvent() {
596       choice = 0;
597       choiceCounter = 0;
598       execution = null;
599       stateId = 0;
600       transitionCG = null;
601     }
602
603     public int getChoice() {
604       return choice;
605     }
606
607     public int getChoiceCounter() {
608       return choiceCounter;
609     }
610
611     public Execution getExecution() {
612       return execution;
613     }
614
615     public int getStateId() {
616       return stateId;
617     }
618
619     public IntChoiceFromSet getTransitionCG() { return transitionCG; }
620
621     public void setChoice(int cho) {
622       choice = cho;
623     }
624
625     public void setChoiceCounter(int choCounter) {
626       choiceCounter = choCounter;
627     }
628
629     public void setExecution(Execution exec) {
630       execution = exec;
631     }
632
633     public void setStateId(int stId) {
634       stateId = stId;
635     }
636
637     public void setTransitionCG(IntChoiceFromSet cg) {
638       transitionCG = cg;
639     }
640   }
641
642   // -- PRIVATE CLASSES RELATED TO SUMMARY
643   // This class stores the main summary of states
644   // 1) Main mapping between state ID and state summary
645   // 2) State summary is a mapping between events (i.e., event choices) and their respective R/W sets
646   private class MainSummary {
647     private HashMap<Integer, HashMap<Integer, ReadWriteSet>> mainSummary;
648
649     public MainSummary() {
650       mainSummary = new HashMap<>();
651     }
652
653     public Set<Integer> getEventChoicesAtStateId(int stateId) {
654       HashMap<Integer, ReadWriteSet> stateSummary = mainSummary.get(stateId);
655       // Return a new set since this might get updated concurrently
656       return new HashSet<>(stateSummary.keySet());
657     }
658
659     public ReadWriteSet getRWSetForEventChoiceAtState(int eventChoice, int stateId) {
660       HashMap<Integer, ReadWriteSet> stateSummary = mainSummary.get(stateId);
661       return stateSummary.get(eventChoice);
662     }
663
664     public Set<Integer> getStateIds() {
665       return mainSummary.keySet();
666     }
667
668     private ReadWriteSet performUnion(ReadWriteSet recordedRWSet, ReadWriteSet rwSet) {
669       // Combine the same write accesses and record in the recordedRWSet
670       HashMap<String, Integer> recordedWriteMap = recordedRWSet.getWriteMap();
671       HashMap<String, Integer> writeMap = rwSet.getWriteMap();
672       for(Map.Entry<String, Integer> entry : recordedWriteMap.entrySet()) {
673         String writeField = entry.getKey();
674         // Remove the entry from rwSet if both field and object ID are the same
675         if (writeMap.containsKey(writeField) &&
676                 (writeMap.get(writeField).equals(recordedWriteMap.get(writeField)))) {
677           writeMap.remove(writeField);
678         }
679       }
680       // Then add the rest (fields in rwSet but not in recordedRWSet)
681       // into the recorded map because these will be traversed
682       recordedWriteMap.putAll(writeMap);
683       // Combine the same read accesses and record in the recordedRWSet
684       HashMap<String, Integer> recordedReadMap = recordedRWSet.getReadMap();
685       HashMap<String, Integer> readMap = rwSet.getReadMap();
686       for(Map.Entry<String, Integer> entry : recordedReadMap.entrySet()) {
687         String readField = entry.getKey();
688         // Remove the entry from rwSet if both field and object ID are the same
689         if (readMap.containsKey(readField) &&
690                 (readMap.get(readField).equals(recordedReadMap.get(readField)))) {
691           readMap.remove(readField);
692         }
693       }
694       // Then add the rest (fields in rwSet but not in recordedRWSet)
695       // into the recorded map because these will be traversed
696       recordedReadMap.putAll(readMap);
697
698       return rwSet;
699     }
700
701     public ReadWriteSet updateStateSummary(int stateId, int eventChoice, ReadWriteSet rwSet) {
702       // If the state Id has not existed, insert the StateSummary object
703       // If the state Id has existed, find the event choice:
704       // 1) If the event choice has not existed, insert the ReadWriteSet object
705       // 2) If the event choice has existed, perform union between the two ReadWriteSet objects
706       if (!rwSet.isEmpty()) {
707         HashMap<Integer, ReadWriteSet> stateSummary;
708         if (!mainSummary.containsKey(stateId)) {
709           stateSummary = new HashMap<>();
710           stateSummary.put(eventChoice, rwSet.getCopy());
711           mainSummary.put(stateId, stateSummary);
712         } else {
713           stateSummary = mainSummary.get(stateId);
714           if (!stateSummary.containsKey(eventChoice)) {
715             stateSummary.put(eventChoice, rwSet.getCopy());
716           } else {
717             rwSet = performUnion(stateSummary.get(eventChoice), rwSet);
718           }
719         }
720       }
721       return rwSet;
722     }
723   }
724
725   // -- CONSTANTS
726   private final static String DO_CALL_METHOD = "doCall";
727   // We exclude fields that come from libraries (Java and Groovy), and also the infrastructure
728   private final static String[] EXCLUDED_FIELDS_CONTAINS_LIST = {"_closure"};
729   private final static String[] EXCLUDED_FIELDS_ENDS_WITH_LIST =
730           // Groovy library created fields
731           {"stMC", "callSiteArray", "metaClass", "staticClassInfo", "__constructor__",
732           // Infrastructure
733           "sendEvent", "Object", "reference", "location", "app", "state", "log", "functionList", "objectList",
734           "eventList", "valueList", "settings", "printToConsole", "app1", "app2"};
735   private final static String[] EXCLUDED_FIELDS_STARTS_WITH_LIST =
736           // Java and Groovy libraries
737           { "java", "org", "sun", "com", "gov", "groovy"};
738   private final static String[] EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST = {"Event"};
739   private final static String GET_PROPERTY_METHOD =
740           "invokeinterface org.codehaus.groovy.runtime.callsite.CallSite.callGetProperty";
741   private final static String GROOVY_CALLSITE_LIB = "org.codehaus.groovy.runtime.callsite";
742   private final static String JAVA_INTEGER = "int";
743   private final static String JAVA_STRING_LIB = "java.lang.String";
744
745   // -- FUNCTIONS
746   private Integer[] copyChoices(Integer[] choicesToCopy) {
747
748     Integer[] copyOfChoices = new Integer[choicesToCopy.length];
749     System.arraycopy(choicesToCopy, 0, copyOfChoices, 0, choicesToCopy.length);
750     return copyOfChoices;
751   }
752
753   private void ensureFairSchedulingAndSetupTransition(IntChoiceFromSet icsCG, VM vm) {
754     // Check the next choice and if the value is not the same as the expected then force the expected value
755     int choiceIndex = choiceCounter % refChoices.length;
756     int nextChoice = icsCG.getNextChoice();
757     if (refChoices[choiceIndex] != nextChoice) {
758       int expectedChoice = refChoices[choiceIndex];
759       int currCGIndex = icsCG.getNextChoiceIndex();
760       if ((currCGIndex >= 0) && (currCGIndex < refChoices.length)) {
761         icsCG.setChoice(currCGIndex, expectedChoice);
762       }
763     }
764     // Get state ID and associate it with this transition
765     int stateId = vm.getStateId();
766     TransitionEvent transition = setupTransition(icsCG, stateId, choiceIndex);
767     // Add new transition to the current execution and map it in R-Graph
768     for (Integer stId : justVisitedStates) {  // Map this transition to all the previously passed states
769       rGraph.addReachableTransition(stId, transition);
770     }
771     currentExecution.mapCGToChoice(icsCG, choiceCounter);
772     // Store restorable state object for this state (always store the latest)
773     if (!restorableStateMap.containsKey(stateId)) {
774       RestorableVMState restorableState = vm.getRestorableState();
775       restorableStateMap.put(stateId, restorableState);
776     }
777   }
778
779   private TransitionEvent setupTransition(IntChoiceFromSet icsCG, int stateId, int choiceIndex) {
780     // Get a new transition
781     TransitionEvent transition;
782     if (currentExecution.isNew()) {
783       // We need to handle the first transition differently because this has a predecessor execution
784       transition = currentExecution.getFirstTransition();
785     } else {
786       transition = new TransitionEvent();
787       currentExecution.addTransition(transition);
788       addPredecessors(stateId);
789     }
790     transition.setExecution(currentExecution);
791     transition.setTransitionCG(icsCG);
792     transition.setStateId(stateId);
793     transition.setChoice(refChoices[choiceIndex]);
794     transition.setChoiceCounter(choiceCounter);
795
796     return transition;
797   }
798
799   // --- Functions related to cycle detection and reachability graph
800
801   // Detect cycles in the current execution/trace
802   // We terminate the execution iff:
803   // (1) the state has been visited in the current execution
804   // (2) the state has one or more cycles that involve all the events
805   // With simple approach we only need to check for a re-visited state.
806   // Basically, we have to check that we have executed all events between two occurrences of such state.
807   private boolean completeFullCycle(int stId) {
808     // False if the state ID hasn't been recorded
809     if (!stateToEventMap.containsKey(stId)) {
810       return false;
811     }
812     HashSet<Integer> visitedEvents = stateToEventMap.get(stId);
813     // Check if this set contains all the event choices
814     // If not then this is not the terminating condition
815     for(int i=0; i<=maxEventChoice; i++) {
816       if (!visitedEvents.contains(i)) {
817         return false;
818       }
819     }
820     return true;
821   }
822
823   private void initializeStatesVariables() {
824     // DPOR-related
825     choices = null;
826     refChoices = null;
827     choiceCounter = 0;
828     maxEventChoice = 0;
829     // Cycle tracking
830     if (!isBooleanCGFlipped) {
831       currVisitedStates = new HashMap<>();
832       justVisitedStates = new HashSet<>();
833       prevVisitedStates = new HashSet<>();
834       stateToEventMap = new HashMap<>();
835     } else {
836       currVisitedStates.clear();
837       justVisitedStates.clear();
838       prevVisitedStates.clear();
839       stateToEventMap.clear();
840     }
841     // Backtracking
842     if (!isBooleanCGFlipped) {
843       backtrackMap = new HashMap<>();
844     } else {
845       backtrackMap.clear();
846     }
847     backtrackStateQ = new PriorityQueue<>(Collections.reverseOrder());
848     currentExecution = new Execution();
849     currentExecution.addTransition(new TransitionEvent()); // Always start with 1 backtrack point
850     if (!isBooleanCGFlipped) {
851       doneBacktrackMap = new HashMap<>();
852     } else {
853       doneBacktrackMap.clear();
854     }
855     rGraph = new RGraph();
856     // Booleans
857     isEndOfExecution = false;
858   }
859
860   private void mapStateToEvent(int nextChoiceValue) {
861     // Update all states with this event/choice
862     // This means that all past states now see this transition
863     Set<Integer> stateSet = stateToEventMap.keySet();
864     for(Integer stateId : stateSet) {
865       HashSet<Integer> eventSet = stateToEventMap.get(stateId);
866       eventSet.add(nextChoiceValue);
867     }
868   }
869
870   private boolean terminateCurrentExecution() {
871     // We need to check all the states that have just been visited
872     // Often a transition (choice/event) can result into forwarding/backtracking to a number of states
873     boolean terminate = false;
874     Set<Integer> mainStateIds = mainSummary.getStateIds();
875     for(Integer stateId : justVisitedStates) {
876       // We exclude states that are produced by other CGs that are not integer CG
877       // When we encounter these states, then we should also encounter the corresponding integer CG state ID
878       if (mainStateIds.contains(stateId)) {
879         // We perform updates on backtrack sets for every
880         if (prevVisitedStates.contains(stateId) || completeFullCycle(stateId)) {
881           updateBacktrackSetsFromGraph(stateId, currentExecution, choiceCounter - 1);
882           terminate = true;
883         }
884         // If frequency > 1 then this means we have visited this stateId more than once in the current execution
885         if (currVisitedStates.containsKey(stateId) && currVisitedStates.get(stateId) > 1) {
886           updateBacktrackSetsFromGraph(stateId, currentExecution, choiceCounter - 1);
887         }
888       }
889     }
890     return terminate;
891   }
892
893   private void updateStateInfo(Search search) {
894     // Update the state variables
895     int stateId = search.getStateId();
896     // Insert state ID into the map if it is new
897     if (!stateToEventMap.containsKey(stateId)) {
898       HashSet<Integer> eventSet = new HashSet<>();
899       stateToEventMap.put(stateId, eventSet);
900     }
901     addPredecessorToRevisitedState(stateId);
902     justVisitedStates.add(stateId);
903     if (!prevVisitedStates.contains(stateId)) {
904       // It is a currently visited states if the state has not been seen in previous executions
905       int frequency = 0;
906       if (currVisitedStates.containsKey(stateId)) {
907         frequency = currVisitedStates.get(stateId);
908       }
909       currVisitedStates.put(stateId, frequency + 1);  // Increment frequency counter
910     }
911   }
912
913   // --- Functions related to Read/Write access analysis on shared fields
914
915   private void addNewBacktrackPoint(int stateId, Integer[] newChoiceList, TransitionEvent conflictTransition) {
916     // Insert backtrack point to the right state ID
917     LinkedList<BacktrackExecution> backtrackExecList;
918     if (backtrackMap.containsKey(stateId)) {
919       backtrackExecList = backtrackMap.get(stateId);
920     } else {
921       backtrackExecList = new LinkedList<>();
922       backtrackMap.put(stateId, backtrackExecList);
923     }
924     // Add the new backtrack execution object
925     TransitionEvent backtrackTransition = new TransitionEvent();
926     backtrackExecList.addFirst(new BacktrackExecution(newChoiceList, backtrackTransition));
927     // Add to priority queue
928     if (!backtrackStateQ.contains(stateId)) {
929       backtrackStateQ.add(stateId);
930     }
931   }
932
933   private void addPredecessors(int stateId) {
934     PredecessorInfo predecessorInfo;
935     if (!stateToPredInfo.containsKey(stateId)) {
936       predecessorInfo = new PredecessorInfo();
937       stateToPredInfo.put(stateId, predecessorInfo);
938     } else {  // This is a new state Id
939       predecessorInfo = stateToPredInfo.get(stateId);
940     }
941     predecessorInfo.recordPredecessor(currentExecution, choiceCounter - 1);
942   }
943
944   // Analyze Read/Write accesses that are directly invoked on fields
945   private void analyzeReadWriteAccesses(Instruction executedInsn, int currentChoice) {
946     // Get the field info
947     FieldInfo fieldInfo = ((JVMFieldInstruction) executedInsn).getFieldInfo();
948     // Analyze only after being initialized
949     String fieldClass = fieldInfo.getFullName();
950     // Do the analysis to get Read and Write accesses to fields
951     ReadWriteSet rwSet = getReadWriteSet(currentChoice);
952     int objectId = fieldInfo.getClassInfo().getClassObjectRef();
953     // Record the field in the map
954     if (executedInsn instanceof WriteInstruction) {
955       // We first check the non-relevant fields set
956       if (!nonRelevantFields.contains(fieldInfo)) {
957         // Exclude certain field writes because of infrastructure needs, e.g., Event class field writes
958         for (String str : EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST) {
959           if (fieldClass.startsWith(str)) {
960             nonRelevantFields.add(fieldInfo);
961             return;
962           }
963         }
964       } else {
965         // If we have this field in the non-relevant fields set then we return right away
966         return;
967       }
968       rwSet.addWriteField(fieldClass, objectId);
969     } else if (executedInsn instanceof ReadInstruction) {
970       rwSet.addReadField(fieldClass, objectId);
971     }
972   }
973
974   // Analyze Read accesses that are indirect (performed through iterators)
975   // These accesses are marked by certain bytecode instructions, e.g., INVOKEINTERFACE
976   private void analyzeReadWriteAccesses(Instruction instruction, ThreadInfo ti, int currentChoice) {
977     // Get method name
978     INVOKEINTERFACE insn = (INVOKEINTERFACE) instruction;
979     if (insn.toString().startsWith(GET_PROPERTY_METHOD) &&
980             insn.getMethodInfo().getName().equals(DO_CALL_METHOD)) {
981       // Extract info from the stack frame
982       StackFrame frame = ti.getTopFrame();
983       int[] frameSlots = frame.getSlots();
984       // Get the Groovy callsite library at index 0
985       ElementInfo eiCallsite = VM.getVM().getHeap().get(frameSlots[0]);
986       if (!eiCallsite.getClassInfo().getName().startsWith(GROOVY_CALLSITE_LIB)) {
987         return;
988       }
989       // Get the iterated object whose property is accessed
990       ElementInfo eiAccessObj = VM.getVM().getHeap().get(frameSlots[1]);
991       if (eiAccessObj == null) {
992         return;
993       }
994       // We exclude library classes (they start with java, org, etc.) and some more
995       ClassInfo classInfo = eiAccessObj.getClassInfo();
996       String objClassName = classInfo.getName();
997       // Check if this class info is part of the non-relevant classes set already
998       if (!nonRelevantClasses.contains(classInfo)) {
999         if (excludeThisForItStartsWith(EXCLUDED_FIELDS_READ_WRITE_INSTRUCTIONS_STARTS_WITH_LIST, objClassName) ||
1000                 excludeThisForItStartsWith(EXCLUDED_FIELDS_STARTS_WITH_LIST, objClassName)) {
1001           nonRelevantClasses.add(classInfo);
1002           return;
1003         }
1004       } else {
1005         // If it is part of the non-relevant classes set then return immediately
1006         return;
1007       }
1008       // Extract fields from this object and put them into the read write
1009       int numOfFields = eiAccessObj.getNumberOfFields();
1010       for(int i=0; i<numOfFields; i++) {
1011         FieldInfo fieldInfo = eiAccessObj.getFieldInfo(i);
1012         if (fieldInfo.getType().equals(JAVA_STRING_LIB) || fieldInfo.getType().equals(JAVA_INTEGER)) {
1013           String fieldClass = fieldInfo.getFullName();
1014           ReadWriteSet rwSet = getReadWriteSet(currentChoice);
1015           int objectId = fieldInfo.getClassInfo().getClassObjectRef();
1016           // Record the field in the map
1017           rwSet.addReadField(fieldClass, objectId);
1018         }
1019       }
1020     }
1021   }
1022
1023   private int checkAndAdjustChoice(int currentChoice, VM vm) {
1024     // If current choice is not the same, then this is caused by the firing of IntIntervalGenerator
1025     // for certain method calls in the infrastructure, e.g., eventSince()
1026     ChoiceGenerator<?> currentCG = vm.getChoiceGenerator();
1027     // This is the main event CG
1028     if (currentCG instanceof IntIntervalGenerator) {
1029       // This is the interval CG used in device handlers
1030       ChoiceGenerator<?> parentCG = ((IntIntervalGenerator) currentCG).getPreviousChoiceGenerator();
1031       // Iterate until we find the IntChoiceFromSet CG
1032       while (!(parentCG instanceof IntChoiceFromSet)) {
1033         parentCG = ((IntIntervalGenerator) parentCG).getPreviousChoiceGenerator();
1034       }
1035       // Find the choice related to the IntIntervalGenerator CG from the map
1036       currentChoice = currentExecution.getChoiceFromCG((IntChoiceFromSet) parentCG);
1037     }
1038     return currentChoice;
1039   }
1040
1041   private void createBacktrackingPoint(int eventChoice, Execution conflictExecution, int conflictChoice) {
1042     // Create a new list of choices for backtrack based on the current choice and conflicting event number
1043     // E.g. if we have a conflict between 1 and 3, then we create the list {3, 1, 0, 2}
1044     // for the original set {0, 1, 2, 3}
1045     
1046     // eventChoice represents the event/transaction that will be put into the backtracking set of
1047     // conflictExecution/conflictChoice
1048     Integer[] newChoiceList = new Integer[refChoices.length];
1049     ArrayList<TransitionEvent> conflictTrace = conflictExecution.getExecutionTrace();
1050     int stateId = conflictTrace.get(conflictChoice).getStateId();
1051     // Check if this trace has been done from this state
1052     if (isTraceAlreadyConstructed(eventChoice, stateId)) {
1053       return;
1054     }
1055     // Put the conflicting event numbers first and reverse the order
1056     newChoiceList[0] = eventChoice;
1057     // Put the rest of the event numbers into the array starting from the minimum to the upper bound
1058     for (int i = 0, j = 1; i < refChoices.length; i++) {
1059       if (refChoices[i] != newChoiceList[0]) {
1060         newChoiceList[j] = refChoices[i];
1061         j++;
1062       }
1063     }
1064     // Predecessor of the new backtrack point is the same as the conflict point's
1065     addNewBacktrackPoint(stateId, newChoiceList, conflictTrace.get(conflictChoice));
1066   }
1067
1068   private boolean excludeThisForItContains(String[] excludedStrings, String className) {
1069     for (String excludedField : excludedStrings) {
1070       if (className.contains(excludedField)) {
1071         return true;
1072       }
1073     }
1074     return false;
1075   }
1076
1077   private boolean excludeThisForItEndsWith(String[] excludedStrings, String className) {
1078     for (String excludedField : excludedStrings) {
1079       if (className.endsWith(excludedField)) {
1080         return true;
1081       }
1082     }
1083     return false;
1084   }
1085
1086   private boolean excludeThisForItStartsWith(String[] excludedStrings, String className) {
1087     for (String excludedField : excludedStrings) {
1088       if (className.startsWith(excludedField)) {
1089         return true;
1090       }
1091     }
1092     return false;
1093   }
1094
1095   private void exploreNextBacktrackPoints(VM vm, IntChoiceFromSet icsCG) {
1096     // Check if we are reaching the end of our execution: no more backtracking points to explore
1097     // cgMap, backtrackMap, backtrackStateQ are updated simultaneously (checking backtrackStateQ is enough)
1098     if (!backtrackStateQ.isEmpty()) {
1099       // Set done all the other backtrack points
1100       for (TransitionEvent backtrackTransition : currentExecution.getExecutionTrace()) {
1101         backtrackTransition.getTransitionCG().setDone();
1102       }
1103       // Reset the next backtrack point with the latest state
1104       int hiStateId = backtrackStateQ.peek();
1105       // Restore the state first if necessary
1106       if (vm.getStateId() != hiStateId) {
1107         RestorableVMState restorableState = restorableStateMap.get(hiStateId);
1108         vm.restoreState(restorableState);
1109       }
1110       // Set the backtrack CG
1111       IntChoiceFromSet backtrackCG = (IntChoiceFromSet) vm.getChoiceGenerator();
1112       setBacktrackCG(hiStateId, backtrackCG);
1113     } else {
1114       // Set done this last CG (we save a few rounds)
1115       icsCG.setDone();
1116     }
1117     // Save all the visited states when starting a new execution of trace
1118     prevVisitedStates.addAll(currVisitedStates.keySet());
1119     // This marks a transitional period to the new CG
1120     isEndOfExecution = true;
1121   }
1122
1123   private boolean isConflictFound(int eventChoice, Execution conflictExecution, int conflictChoice,
1124                                   ReadWriteSet currRWSet) {
1125     // conflictExecution/conflictChoice represent a predecessor event/transaction that can potentially have a conflict
1126     ArrayList<TransitionEvent> conflictTrace = conflictExecution.getExecutionTrace();
1127     HashMap<Integer, ReadWriteSet> confRWFieldsMap = conflictExecution.getReadWriteFieldsMap();
1128     // Skip if this event does not have any Read/Write set or the two events are basically the same event (number)
1129     if (!confRWFieldsMap.containsKey(conflictChoice) || eventChoice == conflictTrace.get(conflictChoice).getChoice()) {
1130       return false;
1131     }
1132     // R/W set of choice/event that may have a potential conflict
1133     ReadWriteSet confRWSet = confRWFieldsMap.get(conflictChoice);
1134     // Check for conflicts with Read and Write fields for Write instructions
1135     Set<String> currWriteSet = currRWSet.getWriteSet();
1136     for(String writeField : currWriteSet) {
1137       int currObjId = currRWSet.writeFieldObjectId(writeField);
1138       if ((confRWSet.readFieldExists(writeField) && confRWSet.readFieldObjectId(writeField) == currObjId) ||
1139           (confRWSet.writeFieldExists(writeField) && confRWSet.writeFieldObjectId(writeField) == currObjId)) {
1140         // Remove this from the write set as we are tracking per memory location
1141         currRWSet.removeWriteField(writeField);
1142         return true;
1143       }
1144     }
1145     // Check for conflicts with Write fields for Read instructions
1146     Set<String> currReadSet = currRWSet.getReadSet();
1147     for(String readField : currReadSet) {
1148       int currObjId = currRWSet.readFieldObjectId(readField);
1149       if (confRWSet.writeFieldExists(readField) && confRWSet.writeFieldObjectId(readField) == currObjId) {
1150         // Remove this from the read set as we are tracking per memory location
1151         currRWSet.removeReadField(readField);
1152         return true;
1153       }
1154     }
1155     // Return false if no conflict is found
1156     return false;
1157   }
1158
1159   private boolean isFieldExcluded(Instruction executedInsn) {
1160     // Get the field info
1161     FieldInfo fieldInfo = ((JVMFieldInstruction) executedInsn).getFieldInfo();
1162     // Check if the non-relevant fields set already has it
1163     if (nonRelevantFields.contains(fieldInfo)) {
1164       return true;
1165     }
1166     // Check if the relevant fields set already has it
1167     if (relevantFields.contains(fieldInfo)) {
1168       return false;
1169     }
1170     // Analyze only after being initialized
1171     String field = fieldInfo.getFullName();
1172     // Check against "starts-with", "ends-with", and "contains" list
1173     if (excludeThisForItStartsWith(EXCLUDED_FIELDS_STARTS_WITH_LIST, field) ||
1174             excludeThisForItEndsWith(EXCLUDED_FIELDS_ENDS_WITH_LIST, field) ||
1175             excludeThisForItContains(EXCLUDED_FIELDS_CONTAINS_LIST, field)) {
1176       nonRelevantFields.add(fieldInfo);
1177       return true;
1178     }
1179     relevantFields.add(fieldInfo);
1180     return false;
1181   }
1182
1183   // Check if this trace is already constructed
1184   private boolean isTraceAlreadyConstructed(int firstChoice, int stateId) {
1185     // Concatenate state ID and only the first event in the string, e.g., "1:1 for the trace 10234 at state 1"
1186     // Check if the trace has been constructed as a backtrack point for this state
1187     // TODO: THIS IS AN OPTIMIZATION!
1188     HashSet<Integer> choiceSet;
1189     if (doneBacktrackMap.containsKey(stateId)) {
1190       choiceSet = doneBacktrackMap.get(stateId);
1191       if (choiceSet.contains(firstChoice)) {
1192         return true;
1193       }
1194     } else {
1195       choiceSet = new HashSet<>();
1196       doneBacktrackMap.put(stateId, choiceSet);
1197     }
1198     choiceSet.add(firstChoice);
1199
1200     return false;
1201   }
1202
1203   private HashSet<Predecessor> getPredecessors(int stateId) {
1204     // Get a set of predecessors for this state ID
1205     HashSet<Predecessor> predecessors;
1206     if (stateToPredInfo.containsKey(stateId)) {
1207       PredecessorInfo predecessorInfo = stateToPredInfo.get(stateId);
1208       predecessors = predecessorInfo.getPredecessors();
1209     } else {
1210       predecessors = new HashSet<>();
1211     }
1212
1213     return predecessors;
1214   }
1215
1216   private ReadWriteSet getReadWriteSet(int currentChoice) {
1217     // Do the analysis to get Read and Write accesses to fields
1218     ReadWriteSet rwSet;
1219     // We already have an entry
1220     HashMap<Integer, ReadWriteSet> currReadWriteFieldsMap = currentExecution.getReadWriteFieldsMap();
1221     if (currReadWriteFieldsMap.containsKey(currentChoice)) {
1222       rwSet = currReadWriteFieldsMap.get(currentChoice);
1223     } else { // We need to create a new entry
1224       rwSet = new ReadWriteSet();
1225       currReadWriteFieldsMap.put(currentChoice, rwSet);
1226     }
1227     return rwSet;
1228   }
1229
1230   // Reset data structure for each new execution
1231   private void resetStatesForNewExecution(IntChoiceFromSet icsCG, VM vm) {
1232     if (choices == null || choices != icsCG.getAllChoices()) {
1233       // Reset state variables
1234       choiceCounter = 0;
1235       choices = icsCG.getAllChoices();
1236       refChoices = copyChoices(choices);
1237       // Clear data structures
1238       currVisitedStates.clear();
1239       stateToEventMap.clear();
1240       isEndOfExecution = false;
1241     }
1242   }
1243
1244   // Set a backtrack point for a particular state
1245   private void setBacktrackCG(int stateId, IntChoiceFromSet backtrackCG) {
1246     // Set a backtrack CG based on a state ID
1247     LinkedList<BacktrackExecution> backtrackExecutions = backtrackMap.get(stateId);
1248     BacktrackExecution backtrackExecution = backtrackExecutions.removeLast();
1249     backtrackCG.setNewValues(backtrackExecution.getChoiceList());  // Get the last from the queue
1250     backtrackCG.setStateId(stateId);
1251     backtrackCG.reset();
1252     // Update current execution with this new execution
1253     Execution newExecution = new Execution();
1254     TransitionEvent firstTransition = backtrackExecution.getFirstTransition();
1255     newExecution.addTransition(firstTransition);
1256     // Try to free some memory since this map is only used for the current execution
1257     currentExecution.clearCGToChoiceMap();
1258     currentExecution = newExecution;
1259     // Remove from the queue if we don't have more backtrack points for that state
1260     if (backtrackExecutions.isEmpty()) {
1261       backtrackMap.remove(stateId);
1262       backtrackStateQ.remove(stateId);
1263     }
1264   }
1265
1266   // Update backtrack sets
1267   // 1) recursively, and
1268   // 2) track accesses per memory location (per shared variable/field)
1269   private void updateBacktrackSet(Execution execution, int currentChoice) {
1270     // Copy ReadWriteSet object
1271     HashMap<Integer, ReadWriteSet> currRWFieldsMap = execution.getReadWriteFieldsMap();
1272     ReadWriteSet currRWSet = currRWFieldsMap.get(currentChoice);
1273     if (currRWSet == null) {
1274       return;
1275     }
1276     currRWSet = currRWSet.getCopy();
1277     // Memorize visited TransitionEvent object while performing backward DFS to avoid getting caught up in a cycle
1278     HashSet<TransitionEvent> visited = new HashSet<>();
1279     // Conflict TransitionEvent is essentially the current TransitionEvent
1280     TransitionEvent confTrans = execution.getExecutionTrace().get(currentChoice);
1281     // Update backtrack set recursively
1282     updateBacktrackSetDFS(execution, currentChoice, confTrans.getChoice(), currRWSet, visited);
1283   }
1284
1285   private void updateBacktrackSetDFS(Execution execution, int currentChoice, int conflictEventChoice,
1286                                      ReadWriteSet currRWSet, HashSet<TransitionEvent> visited) {
1287     TransitionEvent currTrans = execution.getExecutionTrace().get(currentChoice);
1288     // Record this transition into the state summary of main summary
1289     currRWSet = mainSummary.updateStateSummary(currTrans.getStateId(), conflictEventChoice, currRWSet);
1290     // Halt when we have visited this transition (in a cycle)
1291     if (visited.contains(currTrans)) {
1292       return;
1293     }
1294     visited.add(currTrans);
1295     // Check the predecessors only if the set is not empty
1296     if (!currRWSet.isEmpty()) {
1297       // Explore all predecessors
1298       for (Predecessor predecessor : getPredecessors(currTrans.getStateId())) {
1299         // Get the predecessor (previous conflict choice)
1300         int predecessorChoice = predecessor.getChoice();
1301         Execution predecessorExecution = predecessor.getExecution();
1302         // Push up one happens-before transition
1303         int newConflictEventChoice = conflictEventChoice;
1304         // Check if a conflict is found
1305         ReadWriteSet newCurrRWSet = currRWSet.getCopy();
1306         if (isConflictFound(conflictEventChoice, predecessorExecution, predecessorChoice, newCurrRWSet)) {
1307           createBacktrackingPoint(conflictEventChoice, predecessorExecution, predecessorChoice);
1308           // We need to extract the pushed happens-before event choice from the predecessor execution and choice
1309           newConflictEventChoice = predecessorExecution.getExecutionTrace().get(predecessorChoice).getChoice();
1310         }
1311         // Continue performing DFS if conflict is not found
1312         updateBacktrackSetDFS(predecessorExecution, predecessorChoice, newConflictEventChoice,
1313                 newCurrRWSet, visited);
1314       }
1315     }
1316   }
1317
1318   // --- Functions related to the reachability analysis when there is a state match
1319
1320   private void addPredecessorToRevisitedState(int stateId) {
1321     // Perform this analysis only when:
1322     // 1) this is not during a switch to a new execution,
1323     // 2) at least 2 choices/events have been explored (choiceCounter > 1),
1324     // 3) state > 0 (state 0 is for boolean CG)
1325     if (!isEndOfExecution && choiceCounter > 1 && stateId > 0) {
1326       if ((currVisitedStates.containsKey(stateId) && currVisitedStates.get(stateId) > 1) ||
1327               prevVisitedStates.contains(stateId)) {
1328         // Record a new predecessor for a revisited state
1329         addPredecessors(stateId);
1330       }
1331     }
1332   }
1333
1334   // Update the backtrack sets from previous executions
1335   private void updateBacktrackSetsFromGraph(int stateId, Execution currExecution, int currChoice) {
1336     // Get events/choices at this state ID
1337     Set<Integer> eventChoicesAtStateId = mainSummary.getEventChoicesAtStateId(stateId);
1338     for (Integer eventChoice : eventChoicesAtStateId) {
1339       // Get the ReadWriteSet object for this event at state ID
1340       ReadWriteSet rwSet = mainSummary.getRWSetForEventChoiceAtState(eventChoice, stateId);
1341       // Memorize visited TransitionEvent object while performing backward DFS to avoid getting caught up in a cycle
1342       HashSet<TransitionEvent> visited = new HashSet<>();
1343       // Update the backtrack sets recursively
1344       updateBacktrackSetDFS(currExecution, currChoice, eventChoice, rwSet.getCopy(), visited);
1345     }
1346   }
1347 }