From: rtrimana Date: Wed, 4 Dec 2019 21:20:00 +0000 (-0800) Subject: Fixing bug: completing missing/unexplored event combinations. X-Git-Url: http://plrg.eecs.uci.edu/git/?p=jpf-core.git;a=commitdiff_plain;h=2c58565baa61262d690c9683b1864f8f99bcba2a Fixing bug: completing missing/unexplored event combinations. --- diff --git a/src/main/gov/nasa/jpf/listener/StateReducer.java b/src/main/gov/nasa/jpf/listener/StateReducer.java index 4667668..f70c992 100644 --- a/src/main/gov/nasa/jpf/listener/StateReducer.java +++ b/src/main/gov/nasa/jpf/listener/StateReducer.java @@ -52,20 +52,23 @@ public class StateReducer extends ListenerAdapter { private IntChoiceFromSet currCG; private int choiceCounter; private Integer choiceUpperBound; + private Integer maxUpperBound; private boolean isInitialized; private boolean isResetAfterAnalysis; private boolean isBooleanCGFlipped; - private HashMap cgMap; + private HashMap cgMap; // Record the mapping between event number and field accesses (Read and Write) - private HashMap readWriteFieldsMap; + private HashMap readWriteFieldsMap; // The following is the backtrack map (set) that stores all the backtrack information // e.g., event number 1 can have two backtrack sequences: {3,1,2,4,...} and {2,1,3,4,...} - private HashMap> backtrackMap; - private HashMap> conflictPairMap; + private HashMap> backtrackMap; + // Stores explored backtrack lists in the form of HashSet of Strings + private HashSet backtrackSet; + private HashMap> conflictPairMap; // Map choicelist with start index - private HashMap choiceListStartIndexMap; +// private HashMap choiceListStartIndexMap; - public StateReducer (Config config, JPF jpf) { + public StateReducer(Config config, JPF jpf) { debugMode = config.getBoolean("debug_state_transition", false); stateReductionMode = config.getBoolean("activate_state_reduction", true); if (debugMode) { @@ -87,13 +90,14 @@ public class StateReducer extends ListenerAdapter { currCG = null; choiceCounter = 0; choiceUpperBound = 0; + maxUpperBound = 0; isInitialized = false; isResetAfterAnalysis = false; cgMap = new HashMap<>(); readWriteFieldsMap = new HashMap<>(); backtrackMap = new HashMap<>(); + backtrackSet = new HashSet<>(); conflictPairMap = new HashMap<>(); - choiceListStartIndexMap = new HashMap<>(); } } @@ -118,7 +122,7 @@ public class StateReducer extends ListenerAdapter { } @Override - public void choiceGeneratorRegistered (VM vm, ChoiceGenerator nextCG, ThreadInfo currentThread, Instruction executedInstruction) { + public void choiceGeneratorRegistered(VM vm, ChoiceGenerator nextCG, ThreadInfo currentThread, Instruction executedInstruction) { if (stateReductionMode) { // Initialize with necessary information from the CG if (nextCG instanceof IntChoiceFromSet) { @@ -134,10 +138,14 @@ public class StateReducer extends ListenerAdapter { if (!isResetAfterAnalysis && choiceCounter <= choiceUpperBound && !cgMap.containsValue(choiceCounter)) { // Update the choices of the first CG and add '-1' if (choices == null) { + // Initialize backtrack set that stores all the explored backtrack lists + maxUpperBound = cgChoices.length; // All the choices are always the same so we only need to update it once choices = new Integer[cgChoices.length + 1]; System.arraycopy(cgChoices, 0, choices, 0, cgChoices.length); choices[choices.length - 1] = -1; + String firstChoiceListString = buildStringFromChoiceList(choices); + backtrackSet.add(firstChoiceListString); } icsCG.setNewValues(choices); icsCG.reset(); @@ -163,7 +171,7 @@ public class StateReducer extends ListenerAdapter { return; } // Reset every CG with the first backtrack lists - for(IntChoiceFromSet cg : cgMap.keySet()) { + for (IntChoiceFromSet cg : cgMap.keySet()) { int event = cgMap.get(cg); LinkedList choiceLists = backtrackMap.get(event); if (choiceLists != null && choiceLists.peekFirst() != null) { @@ -178,9 +186,9 @@ public class StateReducer extends ListenerAdapter { } @Override - public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator currentCG) { + public void choiceGeneratorAdvanced(VM vm, ChoiceGenerator currentCG) { - if(stateReductionMode) { + if (stateReductionMode) { // Check the boolean CG and if it is flipped, we are resetting the analysis if (currentCG instanceof BooleanChoiceGenerator) { if (!isBooleanCGFlipped) { @@ -195,12 +203,11 @@ public class StateReducer extends ListenerAdapter { IntChoiceFromSet icsCG = (IntChoiceFromSet) currentCG; // Update the current pointer to the current set of choices if (choices == null || choices != icsCG.getAllChoices()) { - choiceListStartIndexMap.remove(choices); currCG = icsCG; choices = icsCG.getAllChoices(); // Reset a few things for the sub-graph - conflictPairMap = new HashMap<>(); - readWriteFieldsMap = new HashMap<>(); + conflictPairMap.clear(); + readWriteFieldsMap.clear(); choiceCounter = 0; } // Traverse the sub-graphs @@ -276,8 +283,8 @@ public class StateReducer extends ListenerAdapter { // We store the field name and its object ID // Sharing the same field means the same field name and object ID private class ReadWriteSet { - private HashMap readSet; - private HashMap writeSet; + private HashMap readSet; + private HashMap writeSet; public ReadWriteSet() { readSet = new HashMap<>(); @@ -323,7 +330,7 @@ public class StateReducer extends ListenerAdapter { // Record the field in the map if (executedInsn instanceof WriteInstruction) { // Exclude certain field writes because of infrastructure needs, e.g., Event class field writes - for(String str : EXCLUDED_FIELDS_WRITE_INSTRUCTIONS_STARTS_WITH_LIST) { + for (String str : EXCLUDED_FIELDS_WRITE_INSTRUCTIONS_STARTS_WITH_LIST) { if (fieldClass.startsWith(str)) { return; } @@ -352,6 +359,37 @@ public class StateReducer extends ListenerAdapter { return true; } + private String buildStringFromChoiceList(Integer[] newChoiceList) { + + // When we see a choice list shorter than the upper bound, e.g., [3,2] for choices 0,1,2, and 3, + // then we have to pad the beginning before we store it, because [3,2] actually means [0,1,3,2] + // First, calculate the difference between this choice list and the upper bound + // The actual list doesn't include '-1' at the end + int actualListLength = newChoiceList.length - 1; + int diff = maxUpperBound - actualListLength; + StringBuilder sb = new StringBuilder(); + // Pad the beginning if necessary + for (int i = 0; i < diff; i++) { + sb.append(i); + } + // Then continue with the actual choice list + // We don't include the '-1' at the end + for (int i = 0; i < newChoiceList.length - 1; i++) { + sb.append(newChoiceList[i]); + } + return sb.toString(); + } + + private void checkAndAddBacktrackList(LinkedList backtrackChoiceLists, Integer[] newChoiceList) { + + String newChoiceListString = buildStringFromChoiceList(newChoiceList); + // Add only if we haven't seen this combination before + if (!backtrackSet.contains(newChoiceListString)) { + backtrackSet.add(newChoiceListString); + backtrackChoiceLists.addLast(newChoiceList); + } + } + private void createBacktrackChoiceList(int currentChoice, int conflictEventNumber) { LinkedList backtrackChoiceLists; @@ -383,33 +421,33 @@ public class StateReducer extends ListenerAdapter { } // Set the last element to '-1' as the end of the sequence newChoiceList[newChoiceList.length - 1] = -1; - backtrackChoiceLists.addLast(newChoiceList); + checkAndAddBacktrackList(backtrackChoiceLists, newChoiceList); // The start index for the recursion is always 1 (from the main branch) - choiceListStartIndexMap.put(newChoiceList, 1); } else { // This is a sub-graph - int backtrackListIndex = cgMap.get(currCG); - backtrackChoiceLists = backtrackMap.get(backtrackListIndex); - int listLength = choices.length; - Integer[] newChoiceList = new Integer[listLength]; - // Copy everything before the conflict number - for(int i = 0; i < conflictEventNumber; i++) { - newChoiceList[i] = choices[i]; - } - // Put the conflicting events - newChoiceList[conflictEventNumber] = choices[currentChoice]; - newChoiceList[conflictEventNumber + 1] = choices[conflictEventNumber]; - // Copy the rest - for(int i = conflictEventNumber + 1, j = conflictEventNumber + 2; j < listLength - 1; i++) { - if (choices[i] != choices[currentChoice]) { - newChoiceList[j] = choices[i]; - j++; + // There is a case/bug that after a re-initialization, currCG is not yet initialized + if (currCG != null) { + int backtrackListIndex = cgMap.get(currCG); + backtrackChoiceLists = backtrackMap.get(backtrackListIndex); + int listLength = choices.length; + Integer[] newChoiceList = new Integer[listLength]; + // Copy everything before the conflict number + for (int i = 0; i < conflictEventNumber; i++) { + newChoiceList[i] = choices[i]; } + // Put the conflicting events + newChoiceList[conflictEventNumber] = choices[currentChoice]; + newChoiceList[conflictEventNumber + 1] = choices[conflictEventNumber]; + // Copy the rest + for (int i = conflictEventNumber + 1, j = conflictEventNumber + 2; j < listLength - 1; i++) { + if (choices[i] != choices[currentChoice]) { + newChoiceList[j] = choices[i]; + j++; + } + } + // Set the last element to '-1' as the end of the sequence + newChoiceList[newChoiceList.length - 1] = -1; + checkAndAddBacktrackList(backtrackChoiceLists, newChoiceList); } - // Set the last element to '-1' as the end of the sequence - newChoiceList[newChoiceList.length - 1] = -1; - backtrackChoiceLists.addLast(newChoiceList); - // For the sub-graph the start index depends on the conflicting event number - choiceListStartIndexMap.put(newChoiceList, conflictEventNumber + 1); } } @@ -478,11 +516,12 @@ public class StateReducer extends ListenerAdapter { if (!isFieldExcluded(fieldClass)) { // For the main graph we go down to 0, but for subgraph, we only go down to 1 since 0 contains // the reversed event - int end = !isResetAfterAnalysis ? 0 : choiceListStartIndexMap.get(choices); +// int end = !isResetAfterAnalysis ? 0 : choiceListStartIndexMap.get(choices); // Check for conflict (go backward from currentChoice and get the first conflict) // If the current event has conflicts with multiple events, then these will be detected // one by one as this recursively checks backward when backtrack set is revisited and executed. - for (int eventNumber = currentChoice - 1; eventNumber >= end; eventNumber--) { +// for (int eventNumber = currentChoice - 1; eventNumber >= end; eventNumber--) { + for (int eventNumber = currentChoice - 1; eventNumber >= 0; eventNumber--) { // Skip if this event number does not have any Read/Write set if (!readWriteFieldsMap.containsKey(choices[eventNumber])) { continue;