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;
**/
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;
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
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;
}
//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);
}
}
}
}
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
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;
}
return transition;
}
+ int getEventNumber() { return eventNumber; }
+
ArrayList<Update> getUpdates() {
return updates;
}
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;
}
(getApp().hashCode() << 1) ^
getValue().hashCode();
}
+
+ public String toString() {
+ return "<"+getAttribute()+", "+getValue()+", "+getApp()+", "+ismanual+">";
+ }
}
static class IndexObject {
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;
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();
@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) {
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));
}
if (ci.getName().equals("java.lang.String"))
return('"' + ei.asString() + '"');
-
+
return(ei.toString());
default:
}
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);
}
}
}
- 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;
}
}
- }
+ }
}
}