Making field exclusion checks more efficient.
[jpf-core.git] / src / main / gov / nasa / jpf / listener / ConflictTracker.java
index fd7311715a21d1636c5d67972a8b47f2a4fef422..620d1be410597f8ef34963696a895ca7c3e1592b 100644 (file)
@@ -27,6 +27,7 @@ import gov.nasa.jpf.vm.bytecode.LocalVariableInstruction;
 import gov.nasa.jpf.vm.bytecode.ReadInstruction;
 import gov.nasa.jpf.vm.bytecode.StoreInstruction;
 import gov.nasa.jpf.vm.bytecode.WriteInstruction;
+import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
 
 import java.io.PrintWriter;
 
@@ -37,11 +38,12 @@ import java.util.*;
  **/
 
 public class ConflictTracker extends ListenerAdapter {
+
   private final PrintWriter out;
+  private final HashMap<Integer, Node> nodes = new HashMap<Integer, Node>(); // Nodes of a graph
   private final HashSet<String> conflictSet = new HashSet<String>(); // Variables we want to track
   private final HashSet<String> appSet = new HashSet<String>(); // Apps we want to find their conflicts
   private final HashSet<String> manualSet = new HashSet<String>(); // Writer classes with manual inputs to detect direct-direct(No Conflict) interactions
-  private final HashMap<Integer, Node> nodes = new HashMap<Integer, Node>(); // Nodes of a graph
   private HashSet<Transition> transitions = new HashSet<Transition>();
   private ArrayList<Update> currUpdates = new ArrayList<Update>();
   private long timeout;
@@ -50,16 +52,21 @@ public class ConflictTracker extends ListenerAdapter {
   private String operation;
   private String detail;
   private String errorMessage;
+  private String varName;
   private int depth;
   private int id;
   private boolean manual = false;
   private boolean conflictFound = false;
+  private int currentEvent = -1;
+  private boolean debugMode = false;
+  private int popRef = 0;
 
   private final String SET_LOCATION_METHOD = "setLocationMode";
   private final String LOCATION_VAR = "locationMode";
 
   public ConflictTracker(Config config, JPF jpf) {
     out = new PrintWriter(System.out, true);
+    debugMode = config.getBoolean("debug_mode", false);
 
     String[] conflictVars = config.getStringArray("variables");
     // We are not tracking anything if it is null
@@ -75,13 +82,6 @@ public class ConflictTracker extends ListenerAdapter {
         appSet.add(var);
       }
     }
-    String[] manualClasses = config.getStringArray("manualClasses");
-    // We are not tracking anything if it is null
-    if (manualClasses != null) {
-      for (String var : manualClasses) {
-        manualSet.add(var);
-      }
-    }
 
     // Timeout input from config is in minutes, so we need to convert into millis
     timeout = config.getInt("timeout", 0) * 60 * 1000;
@@ -131,12 +131,13 @@ public class ConflictTracker extends ListenerAdapter {
       }
 
       //Check for conflict with the appropriate update set if we are not a manual transition
-      if (confupdates != null && !u.isManual()) {
+      //If this is debug mode, then we do not report any conflicts
+      if (!debugMode && confupdates != null && !u.isManual()) {
         for(Update u2: confupdates) {
           if (conflicts(u, u2)) {
             //throw new RuntimeException(createErrorMessage(u, u2));
             conflictFound = true;
-           errorMessage = createErrorMessage(u, u2);
+               errorMessage = createErrorMessage(u, u2);
           }
         }
       }
@@ -181,13 +182,13 @@ public class ConflictTracker extends ListenerAdapter {
     return message;
   }
 
-  Edge createEdge(Node parent, Node current, Transition transition) {
+  Edge createEdge(Node parent, Node current, Transition transition, int evtNum) {
     //Check if this transition is explored.  If so, just skip everything
     if (transitions.contains(transition))
       return null;
 
     //Create edge
-    Edge e = new Edge(parent, current, transition, currUpdates);
+    Edge e = new Edge(parent, current, transition, evtNum, currUpdates);
     parent.addOutEdge(e);
 
     //Mark transition as explored
@@ -225,12 +226,14 @@ public class ConflictTracker extends ListenerAdapter {
   static class Edge {
     Node source, destination;
     Transition transition;
+    int eventNumber;
     ArrayList<Update> updates = new ArrayList<Update>();
     
-    Edge(Node src, Node dst, Transition t, ArrayList<Update> _updates) {
+    Edge(Node src, Node dst, Transition t, int evtNum, ArrayList<Update> _updates) {
       this.source = src;
       this.destination = dst;
       this.transition = t;
+      this.eventNumber = evtNum;
       this.updates.addAll(_updates);
     }
 
@@ -246,6 +249,8 @@ public class ConflictTracker extends ListenerAdapter {
       return transition;
     }
 
+    int getEventNumber() { return eventNumber; }
+
     ArrayList<Update> getUpdates() {
       return updates;
     }
@@ -307,6 +312,10 @@ public class ConflictTracker extends ListenerAdapter {
         (getApp().hashCode() << 1) ^
         getValue().hashCode();
     }
+
+    public String toString() {
+      return "<"+getAttribute()+", "+getValue()+", "+getApp()+", "+ismanual+">";
+    }
   }
 
   static class IndexObject {
@@ -351,6 +360,39 @@ public class ConflictTracker extends ListenerAdapter {
     return nodes.get(id);
   }
 
+  public void printGraph() {
+    System.out.println("digraph testgraph {");
+    for(Integer i : nodes.keySet()) {
+      Node n = nodes.get(i);
+      System.out.print("N"+i+"[label=\"");
+
+      for(IndexObject io:n.lastUpdates.keySet()) {
+        for(Update u:n.lastUpdates.get(io)) {
+          System.out.print(u.toString().replace("\"", "\\\"")+", ");
+        }
+      }
+      System.out.println("\"];");
+      for(Edge e:n.outEdges) {
+        System.out.print("N"+e.getSrc().getId()+"->N"+e.getDst().getId()+"[label=\"");
+        for(Update u:e.getUpdates()) {
+          System.out.print(u.toString().replace("\"", "\\\"")+", ");
+        }
+        System.out.println("\"];");
+      }
+    }
+
+    System.out.println("}");
+  }
+
+  @Override
+  public void choiceGeneratorAdvanced(VM vm, ChoiceGenerator<?> currentCG) {
+
+    if (currentCG instanceof IntChoiceFromSet) {
+      IntChoiceFromSet icsCG = (IntChoiceFromSet) currentCG;
+      currentEvent = icsCG.getNextChoice();
+    }
+  }
+  
   @Override
   public void stateAdvanced(Search search) {
     String theEnd = null;
@@ -363,7 +405,7 @@ public class ConflictTracker extends ListenerAdapter {
     Node currentNode = getNode(id);
 
     // Create an edge based on the current transition
-    Edge newEdge = createEdge(parentNode, currentNode, transition);
+    Edge newEdge = createEdge(parentNode, currentNode, transition, currentEvent);
 
     // Reset the temporary variables and flags
     currUpdates.clear();
@@ -406,6 +448,9 @@ public class ConflictTracker extends ListenerAdapter {
   @Override
   public void searchFinished(Search search) {
     out.println("----------------------------------- search finished");
+
+    //Comment out the following line to print the explored graph
+    printGraph();
   }
 
   private String getValue(ThreadInfo ti, Instruction inst, byte type) {
@@ -423,6 +468,16 @@ public class ConflictTracker extends ListenerAdapter {
       lo = frame.peek();
       hi = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
 
+      // TODO: Fix for integer values (need to dig deeper into the stack frame to find the right value other than 0)
+      // TODO: Seems to be a problem since this is Groovy (not Java)
+      if (type == Types.T_INT || type == Types.T_LONG || type == Types.T_SHORT) {
+        int offset = 0;
+        while (lo == 0) {
+          lo = frame.peek(offset);
+          offset++;
+        }
+      }
+
       return(decodeValue(type, lo, hi));
     }
 
@@ -457,7 +512,7 @@ public class ConflictTracker extends ListenerAdapter {
 
         if (ci.getName().equals("java.lang.String"))
           return('"' + ei.asString() + '"');
-
+       
         return(ei.toString());
 
       default:
@@ -557,50 +612,60 @@ public class ConflictTracker extends ListenerAdapter {
         ti.setNextPC(nextIns);
       }
     }
-    
+
     if (conflictFound) {
       StringBuilder sb = new StringBuilder();
       sb.append(errorMessage);
       Instruction nextIns = ti.createAndThrowException("java.lang.RuntimeException", sb.toString());
       ti.setNextPC(nextIns);
-    } else if (conflictSet.contains(LOCATION_VAR)) {
-      MethodInfo mi = executedInsn.getMethodInfo();
-      // Find the last load before return and get the value here
-      if (mi.getName().equals(SET_LOCATION_METHOD) &&
-          executedInsn instanceof ALOAD && nextInsn instanceof ARETURN) {
-        byte type  = getType(ti, executedInsn);
-        String value = getValue(ti, executedInsn, type);
-        
-        // Extract the writer app name
-        ClassInfo ci = mi.getClassInfo();
-        String writer = ci.getName();
-        
-        // Update the temporary Set set.
-        writeWriterAndValue(writer, LOCATION_VAR, value);
-      }
     } else {
+      // Check if we are ready to pop the values
+      if (popRef == 2) {
+       byte type = getType(ti, executedInsn);
+        varName = getValue(ti, executedInsn, type);
+       String writer = getWriter(ti.getStack(), appSet);
+
+       popRef = popRef-1;
+      } else if (popRef == 1) {
+       byte type = getType(ti, executedInsn);
+       String value = getValue(ti, executedInsn, type);
+       String writer = getWriter(ti.getStack(), appSet);       
+
+       for (String var: conflictSet) {
+               if (varName.contains(var)) {
+                       if (writer != null)
+                               writeWriterAndValue(writer, varName, value);
+               }
+       }
+
+       popRef = popRef-1;
+      }
+
+
+      if (executedInsn.getMnemonic().equals("getfield")) {
+       if (executedInsn.toString().contains("deviceValueSmartThing") || 
+           executedInsn.toString().contains("deviceIntValueSmartThing")) {
+               if (executedInsn.getNext() != null) {
+                       if (executedInsn.getNext().getMnemonic().contains("load")) {
+
+                               if (executedInsn.getNext().getNext() != null)
+                                       if (executedInsn.getNext().getNext().getMnemonic().contains("load"))
+                                               popRef = 2;
+                       }
+               }
+       }
+      }
+
       if (executedInsn instanceof WriteInstruction) {
         String varId = ((WriteInstruction) executedInsn).getFieldInfo().getFullName();
-         
-        for (String var : conflictSet) {
-          if (varId.contains(var)) {
-            // Get variable info
-            byte type = getType(ti, executedInsn);
-            String value = getValue(ti, executedInsn, type);
-            String writer = getWriter(ti.getStack(), appSet);
-
-            // Just return if the writer is not one of the listed apps in the .jpf file
-            if (writer == null)
-              return;
-            
-            //if (getWriter(ti.getStack(), manualSet) != null)
-            //  manual = true;
-            
-            // Update the current updates
-            writeWriterAndValue(writer, var, value);
-          }
+        // Check if we have an update to isManualTransaction to update manual field
+        if (varId.contains("isManualTransaction")) {
+          byte type = getType(ti, executedInsn);
+          String value = getValue(ti, executedInsn, type);
+
+          manual = (value.equals("true"))?true:false;
         }
       }
-    }
+    }    
   }
 }