**/
public class ConflictTracker extends ListenerAdapter {
-
private final PrintWriter out;
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<NameValuePair> tempSetSet = new HashSet<NameValuePair>();
+ private HashSet<Transition> transitions = new HashSet<Transition>();
+ private ArrayList<Update> currUpdates = new ArrayList<Update>();
private long timeout;
private long startTime;
private Node parentNode = new Node(-2);
private String operation;
private String detail;
- private String errorMessage;
private int depth;
private int id;
- private boolean conflictFound = false;
- private boolean isSet = false;
private boolean manual = false;
-
-
+
+ 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);
startTime = System.currentTimeMillis();
}
- boolean propagateTheChange(Node currentNode) {
- HashSet<Node> changed = new HashSet<Node>(currentNode.getSuccessors());
-
- while(!changed.isEmpty()) {
- // Get the first element of HashSet and remove it from the changed set
- Node nodeToProcess = changed.iterator().next();
- changed.remove(nodeToProcess);
-
- // Update the sets, store the outSet to temp before its changes
- boolean isChanged = updateSets(nodeToProcess);
-
- // Check for a conflict
- if (checkForConflict(nodeToProcess))
- return true;
-
- // Checking if the out set has changed or not(Add its successors to the change list!)
- if (isChanged) {
- for (Node i : nodeToProcess.getSuccessors()) {
- if (!changed.contains(i))
- changed.add(i);
- }
- }
- }
-
- return false;
- }
-
- boolean setOutSet(Node currentNode) {
- Integer prevSize = currentNode.getOutSet().size();
+ void propagateChange(Edge newEdge) {
+ HashSet<Edge> changed = new HashSet<Edge>();
- // Update based on setSet
- for (NameValuePair i : currentNode.getSetSet()) {
- if (currentNode.getOutSet().contains(i))
- currentNode.getOutSet().remove(i);
- currentNode.getOutSet().add(i);
- }
+ // Add the current node to the changed set
+ changed.add(newEdge);
- // Add all the inSet
- currentNode.getOutSet().addAll(currentNode.getInSet());
+ while(!changed.isEmpty()) {
+ // Get the first element of the changed set and remove it
+ Edge edgeToProcess = changed.iterator().next();
+ changed.remove(edgeToProcess);
- // Check if the outSet is changed
- if (!prevSize.equals(currentNode.getOutSet().size()))
- return true;
-
- return false;
+ //If propagating change on edge causes change, enqueue all the target node's out edges
+ if (propagateEdge(edgeToProcess)) {
+ Node dst = edgeToProcess.getDst();
+ for (Edge e : dst.getOutEdges()) {
+ changed.add(e);
+ }
+ }
+ }
}
- void setInSet(Node currentNode) {
- for (Node i : currentNode.getPredecessors()) {
- currentNode.getInSet().addAll(i.getOutSet());
- }
+ boolean propagateEdge(Edge e) {
+ HashMap<IndexObject, HashSet<Update>> srcUpdates = e.getSrc().getLastUpdates();
+ HashMap<IndexObject, HashSet<Update>> dstUpdates = e.getDst().getLastUpdates();
+ ArrayList<Update> edgeUpdates = e.getUpdates();
+ HashMap<IndexObject, Update> lastupdate = new HashMap<IndexObject, Update>();
+ boolean changed = false;
+
+ for(int i=0; i<edgeUpdates.size(); i++) {
+ Update u = edgeUpdates.get(i);
+ IndexObject io = u.getIndex();
+ HashSet<Update> confupdates = null;
+ if (lastupdate.containsKey(io)) {
+ confupdates = new HashSet<Update>();
+ confupdates.add(lastupdate.get(io));
+ } else if (srcUpdates.containsKey(io)){
+ confupdates = srcUpdates.get(io);
+ }
+ //Check for conflict
+ if (confupdates != null && !e.isManual()) {
+ for(Update u2: confupdates) {
+ if (conflicts(u, u2)) {
+ throw new RuntimeException(createErrorMessage(u, u2));
+ }
+ }
+ }
+ lastupdate.put(io, u);
+ }
+ for(IndexObject io: srcUpdates.keySet()) {
+ if (!lastupdate.containsKey(io)) {
+ //Make sure destination has hashset in map
+ if (!dstUpdates.containsKey(io))
+ dstUpdates.put(io, new HashSet<Update>());
+
+ changed |= dstUpdates.get(io).addAll(srcUpdates.get(io));
+ }
+ }
+ for(IndexObject io: lastupdate.keySet()) {
+ //Make sure destination has hashset in map
+ if (!dstUpdates.containsKey(io))
+ dstUpdates.put(io, new HashSet<Update>());
+
+ changed |= dstUpdates.get(io).add(lastupdate.get(io));
+ }
+ return changed;
}
- boolean checkForConflict(Node currentNode) {
- HashMap<String, String> valueMap = new HashMap<String, String>(); // HashMap from varName to value
- HashMap<String, Integer> writerMap = new HashMap<String, Integer>(); // HashMap from varName to appNum
-
- for (NameValuePair i : currentNode.getSetSet()) {
- if (i.getIsManual()) // Manual input: we have no conflict
- continue;
-
- valueMap.put(i.getVarName(), i.getValue());
- if (writerMap.containsKey(i.getVarName()))
- writerMap.put(i.getVarName(), i.getAppNum()+writerMap.get(i.getVarName())); // We have two writers?
- else
- writerMap.put(i.getVarName(), i.getAppNum());
- }
-
- // Comparing the inSet and setSet to find the conflict
- for (NameValuePair i : currentNode.getInSet()) {
- if (valueMap.containsKey(i.getVarName())) {
- if (!(valueMap.get(i.getVarName()).equals(i.getValue()))) // We have different values
- if (!(writerMap.get(i.getVarName()).equals(i.getAppNum()))) {// We have different writers
- errorMessage = "Conflict found between the two apps. App"+i.getAppNum()+" has written the value: "+i.getValue()+
- " to the variable: "+i.getVarName()+" while App"+writerMap.get(i.getVarName())+" is overwriting the value: "
- +valueMap.get(i.getVarName())+" to the same variable!";
- return true;
- }
- }
- }
-
- return false;
+ //Have conflict if same device, same attribute, different app, different vaalue
+ boolean conflicts(Update u, Update u2) {
+ return (!u.getApp().equals(u2.getApp())) &&
+ u.getDeviceId().equals(u2.getDeviceId()) &&
+ u.getAttribute().equals(u2.getAttribute()) &&
+ (!u.getValue().equals(u2.getValue()));
+ }
+
+ String createErrorMessage(Update u, Update u2) {
+ String message = "Conflict found between the two apps. App"+u.getApp()+
+ " has written the value: "+u.getValue()+
+ " to the attribute: "+u.getAttribute()+" while App"
+ +u2.getApp()+" is writing the value: "
+ +u2.getValue()+" to the same variable!";
+ System.out.println(message);
+ return message;
}
- boolean updateSets(Node currentNode) {
- // Set input set according to output set of pred states of current state
- setInSet(currentNode);
+ Edge createEdge(Node parent, Node current, Transition transition, boolean ismanual) {
+ if (transitions.contains(transition))
+ return null;
+
+ //Create edge
+ Edge e = new Edge(parent, current, transition, currUpdates, ismanual);
+ parent.addOutEdge(e);
- // Set outSet according to inSet, and setSet of current node, check if there is a change
- return setOutSet(currentNode);
+ //Mark transition as explored
+ transitions.add(transition);
+ return e;
}
static class Node {
- Integer id;
- HashSet<Node> predecessors = new HashSet<Node>();
- HashSet<Node> successors = new HashSet<Node>();
- HashSet<NameValuePair> inSet = new HashSet<NameValuePair>();
- HashSet<NameValuePair> setSet = new HashSet<NameValuePair>();
- HashSet<NameValuePair> outSet = new HashSet<NameValuePair>();
-
- Node(Integer id) {
- this.id = id;
- }
-
- void addPredecessor(Node node) {
- predecessors.add(node);
- }
-
- void addSuccessor(Node node) {
- successors.add(node);
- }
-
- void setSetSet(HashSet<NameValuePair> setSet, boolean isManual) {
- if (isManual)
- this.setSet = new HashSet<NameValuePair>();
- for (NameValuePair i : setSet) {
- this.setSet.add(new NameValuePair(i.getAppNum(), i.getValue(), i.getVarName(), i.getIsManual()));
- }
- }
-
- Integer getId() {
- return id;
- }
-
- HashSet<Node> getPredecessors() {
- return predecessors;
- }
-
- HashSet<Node> getSuccessors() {
- return successors;
- }
-
- HashSet<NameValuePair> getInSet() {
- return inSet;
- }
-
- HashSet<NameValuePair> getSetSet() {
- return setSet;
- }
-
- HashSet<NameValuePair> getOutSet() {
- return outSet;
- }
+ Integer id;
+ Vector<Edge> outEdges = new Vector<Edge>();
+ HashMap<IndexObject, HashSet<Update>> lastUpdates = new HashMap<IndexObject, HashSet<Update>>();
+
+ Node(Integer id) {
+ this.id = id;
+ }
+
+ Integer getId() {
+ return id;
+ }
+
+ Vector<Edge> getOutEdges() {
+ return outEdges;
+ }
+
+ void addOutEdge(Edge e) {
+ outEdges.add(e);
+ }
+
+ HashMap<IndexObject, HashSet<Update>> getLastUpdates() {
+ return lastUpdates;
+ }
+ }
+
+ static class Edge {
+ Node source, destination;
+ Transition transition;
+ ArrayList<Update> updates = new ArrayList<Update>();
+ boolean ismanual;
+
+ Edge(Node src, Node dst, Transition t, ArrayList<Update> _updates, boolean _ismanual) {
+ this.source = src;
+ this.destination = dst;
+ this.transition = t;
+ this.ismanual = _ismanual;
+ this.updates.addAll(_updates);
+ }
+
+ boolean isManual() {
+ return ismanual;
+ }
+
+ Node getSrc() {
+ return source;
+ }
+
+ Node getDst() {
+ return destination;
+ }
+
+ Transition getTransition() {
+ return transition;
+ }
+
+ ArrayList<Update> getUpdates() {
+ return updates;
+ }
}
- static class NameValuePair {
- Integer appNum;
- String value;
- String varName;
- boolean isManual;
-
- NameValuePair(Integer appNum, String value, String varName, boolean isManual) {
- this.appNum = appNum;
- this.value = value;
- this.varName = varName;
- this.isManual = isManual;
- }
-
- void setAppNum(Integer appNum) {
- this.appNum = appNum;
- }
-
- void setValue(String value) {
- this.value = value;
- }
-
- void setVarName(String varName) {
- this.varName = varName;
- }
-
- void setIsManual(String varName) {
- this.isManual = isManual;
- }
-
- Integer getAppNum() {
- return appNum;
- }
-
- String getValue() {
- return value;
- }
-
- String getVarName() {
- return varName;
- }
-
- boolean getIsManual() {
- return isManual;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof NameValuePair) {
- NameValuePair other = (NameValuePair) o;
- if (varName.equals(other.getVarName()))
- return appNum.equals(other.getAppNum());
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return appNum.hashCode() * 31 + varName.hashCode();
- }
+ static class Update {
+ String appName;
+ String deviceId;
+ String attribute;
+ String value;
+
+ Update(String _appName, String _deviceId, String _attribute, String _value) {
+ this.appName = _appName;
+ this.deviceId = _deviceId;
+ this.attribute = _attribute;
+ this.value = _value;
+ }
+
+ String getApp() {
+ return appName;
+ }
+
+ String getDeviceId() {
+ return deviceId;
+ }
+
+ String getAttribute() {
+ return attribute;
+ }
+
+ String getValue() {
+ return value;
+ }
+
+ IndexObject getIndex() {
+ return new IndexObject(this);
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof Update))
+ return false;
+ Update u=(Update)o;
+ return (getDeviceId().equals(u.getDeviceId()) &&
+ getAttribute().equals(u.getAttribute()) &&
+ getApp().equals(u.getApp()) &&
+ getValue().equals(u.getValue()));
+ }
+
+ public int hashCode() {
+ return (getDeviceId().hashCode() << 3) ^
+ (getAttribute().hashCode() << 2) ^
+ (getApp().hashCode() << 1) ^
+ getValue().hashCode();
+ }
}
+ static class IndexObject {
+ Update u;
+ IndexObject(Update _u) {
+ this.u = _u;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof IndexObject))
+ return false;
+ IndexObject io=(IndexObject)o;
+ return (u.getDeviceId().equals(io.u.getDeviceId()) &&
+ u.getAttribute().equals(io.u.getAttribute()));
+ }
+ public int hashCode() {
+ return (u.getDeviceId().hashCode() << 1) ^ u.getAttribute().hashCode();
+ }
+ }
+
@Override
public void stateRestored(Search search) {
id = search.getStateId();
out.println("The state is restored to state with id: "+id+", depth: "+depth);
// Update the parent node
- if (nodes.containsKey(id)) {
- parentNode = nodes.get(id);
- } else {
- parentNode = new Node(id);
- }
+ parentNode = getNode(id);
}
@Override
public void searchStarted(Search search) {
out.println("----------------------------------- search started");
}
-
+
+ private Node getNode(Integer id) {
+ if (!nodes.containsKey(id))
+ nodes.put(id, new Node(id));
+ return nodes.get(id);
+ }
@Override
public void stateAdvanced(Search search) {
String theEnd = null;
+ Transition transition = search.getTransition();
id = search.getStateId();
depth = search.getDepth();
operation = "forward";
- // Add the node to the list of nodes
- nodes.put(id, new Node(id));
- Node currentNode = nodes.get(id);
+ // Add the node to the list of nodes
+ Node currentNode = getNode(id);
- // Update the setSet for this new node
- if (isSet) {
- currentNode.setSetSet(tempSetSet, manual);
- tempSetSet = new HashSet<NameValuePair>();
- isSet = false;
- manual = false;
- }
+ // Create an edge based on the current transition
+ Edge newEdge = createEdge(parentNode, currentNode, transition, manual);
+
+ // Reset the temporary variables and flags
+ currUpdates.clear();
+ manual = false;
+
+ // If we have a new Edge, check for conflicts
+ if (newEdge != null)
+ propagateChange(newEdge);
if (search.isNewState()) {
detail = "new";
out.println("The state is forwarded to state with id: "+id+", depth: "+depth+" which is "+detail+" state: "+"% "+theEnd);
- // Updating the predecessors for this node
- // Check if parent node is already in successors of the current node or not
- if (!(currentNode.getPredecessors().contains(parentNode)))
- currentNode.addPredecessor(parentNode);
-
- // Update the successors for this node
- // Check if current node is already in successors of the parent node or not
- if (!(parentNode.getSuccessors().contains(currentNode)))
- parentNode.addSuccessor(currentNode);
-
- // Update the sets, check if the outSet is changed or not
- boolean isChanged = updateSets(currentNode);
-
- // Check for a conflict
- conflictFound = checkForConflict(currentNode);
-
- // Check if the outSet of this state has changed, update all of its successors' sets if any
- if (isChanged)
- conflictFound = conflictFound || propagateTheChange(currentNode);
-
// Update the parent node
- if (nodes.containsKey(id)) {
- parentNode = nodes.get(id);
- } else {
- parentNode = new Node(id);
- }
+ parentNode = currentNode;
}
@Override
out.println("The state is backtracked to state with id: "+id+", depth: "+depth);
// Update the parent node
- if (nodes.containsKey(id)) {
- parentNode = nodes.get(id);
- } else {
- parentNode = new Node(id);
- }
+ parentNode = getNode(id);
}
@Override
if ((inst instanceof JVMLocalVariableInstruction) ||
(inst instanceof JVMFieldInstruction))
{
- if (frame.getTopPos() < 0)
- return(null);
+ if (frame.getTopPos() < 0)
+ return(null);
- lo = frame.peek();
- hi = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
+ lo = frame.peek();
+ hi = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
- return(decodeValue(type, lo, hi));
+ return(decodeValue(type, lo, hi));
}
if (inst instanceof JVMArrayElementInstruction)
return null;
}
+ private void writeWriterAndValue(String writer, String attribute, String value) {
+ // Update the temporary Set set.
+ Update u = new Update(writer, "DEVICE", attribute, value);
+ currUpdates.add(u);
+ }
+
@Override
public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
- // Instantiate timeoutTimer
if (timeout > 0) {
if (System.currentTimeMillis() - startTime > timeout) {
StringBuilder sbTimeOut = new StringBuilder();
}
}
- if (conflictFound) {
- StringBuilder sb = new StringBuilder();
- sb.append(errorMessage);
- Instruction nextIns = ti.createAndThrowException("java.lang.RuntimeException", sb.toString());
- ti.setNextPC(nextIns);
- } else 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 temporary Set set.
- if (writer.equals("App1"))
- tempSetSet.add(new NameValuePair(1, value, var, manual));
- else if (writer.equals("App2"))
- tempSetSet.add(new NameValuePair(2, value, var, manual));
- // Set isSet to true
- isSet = true;
-
- }
- }
-
-
+ 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 {
+ 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 temporary Set set.
+ writeWriterAndValue(writer, var, value);
+ }
+ }
+ }
+ }
}
-
- }
-
}