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