Making field exclusion checks more efficient.
[jpf-core.git] / src / main / gov / nasa / jpf / listener / ConflictTracker.java
index a61da9ce631738438ba055b3c7becee9936fe7b7..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;
@@ -49,15 +51,22 @@ public class ConflictTracker extends ListenerAdapter {
   private Node parentNode = new Node(-2);
   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
@@ -73,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;
@@ -129,10 +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 && !e.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));
+            //throw new RuntimeException(createErrorMessage(u, u2));
+            conflictFound = true;
+               errorMessage = createErrorMessage(u, u2);
           }
         }
       }
@@ -168,22 +173,22 @@ public class ConflictTracker extends ListenerAdapter {
   }
   
   String createErrorMessage(Update u, Update u2) {
-    String message = "Conflict found between the two apps. App"+u.getApp()+
+    String message = "Conflict found between the two apps. "+u.getApp()+
                     " has written the value: "+u.getValue()+
-      " to the attribute: "+u.getAttribute()+" while App"
+      " to the attribute: "+u.getAttribute()+" while "
       +u2.getApp()+" is writing the value: "
       +u2.getValue()+" to the same variable!";
     System.out.println(message);       
     return message;
   }
 
-  Edge createEdge(Node parent, Node current, Transition transition, boolean ismanual) {
+  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, ismanual);
+    Edge e = new Edge(parent, current, transition, evtNum, currUpdates);
     parent.addOutEdge(e);
 
     //Mark transition as explored
@@ -221,21 +226,17 @@ public class ConflictTracker extends ListenerAdapter {
   static class Edge {
     Node source, destination;
     Transition transition;
+    int eventNumber;
     ArrayList<Update> updates = new ArrayList<Update>();
-    boolean ismanual;
     
-    Edge(Node src, Node dst, Transition t, ArrayList<Update> _updates, boolean _ismanual) {
+    Edge(Node src, Node dst, Transition t, int evtNum, ArrayList<Update> _updates) {
       this.source = src;
       this.destination = dst;
       this.transition = t;
-      this.ismanual = _ismanual;
+      this.eventNumber = evtNum;
       this.updates.addAll(_updates);
     }
 
-    boolean isManual() {
-      return ismanual;
-    }
-
     Node getSrc() {
       return source;
     }
@@ -248,6 +249,8 @@ public class ConflictTracker extends ListenerAdapter {
       return transition;
     }
 
+    int getEventNumber() { return eventNumber; }
+
     ArrayList<Update> getUpdates() {
       return updates;
     }
@@ -258,14 +261,20 @@ public class ConflictTracker extends ListenerAdapter {
     String deviceId;
     String attribute;
     String value;
-
-    Update(String _appName, String _deviceId, String _attribute, String _value) {
+    boolean ismanual;
+    
+    Update(String _appName, String _deviceId, String _attribute, String _value, boolean _ismanual) {
       this.appName = _appName;
       this.deviceId = _deviceId;
       this.attribute = _attribute;
       this.value = _value;
+      this.ismanual = _ismanual;
     }
 
+    boolean isManual() {
+      return ismanual;
+    }
+    
     String getApp() {
       return appName;
     }
@@ -303,6 +312,10 @@ public class ConflictTracker extends ListenerAdapter {
         (getApp().hashCode() << 1) ^
         getValue().hashCode();
     }
+
+    public String toString() {
+      return "<"+getAttribute()+", "+getValue()+", "+getApp()+", "+ismanual+">";
+    }
   }
 
   static class IndexObject {
@@ -347,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;
@@ -359,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, manual);
+    Edge newEdge = createEdge(parentNode, currentNode, transition, currentEvent);
 
     // Reset the temporary variables and flags
     currUpdates.clear();
@@ -402,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) {
@@ -419,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));
     }
 
@@ -453,7 +512,7 @@ public class ConflictTracker extends ListenerAdapter {
 
         if (ci.getName().equals("java.lang.String"))
           return('"' + ei.asString() + '"');
-
+       
         return(ei.toString());
 
       default:
@@ -539,7 +598,7 @@ public class ConflictTracker extends ListenerAdapter {
   }
 
   private void writeWriterAndValue(String writer, String attribute, String value) {
-    Update u = new Update(writer, "DEVICE", attribute, value);
+    Update u = new Update(writer, "DEVICE", attribute, value, manual);
     currUpdates.add(u);
   }
 
@@ -554,43 +613,59 @@ public class ConflictTracker extends ListenerAdapter {
       }
     }
 
-    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);
-      }
+    if (conflictFound) {
+      StringBuilder sb = new StringBuilder();
+      sb.append(errorMessage);
+      Instruction nextIns = ti.createAndThrowException("java.lang.RuntimeException", sb.toString());
+      ti.setNextPC(nextIns);
     } 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;
         }
       }
-    }
+    }    
   }
 }