From: Brian Demsky Date: Tue, 17 Dec 2019 06:35:45 +0000 (-0800) Subject: Rewrite of Conflict Tracker X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;ds=sidebyside;h=2e13195f3855e28e7cc7afd34858e15724ad6b3f;p=jpf-core.git Rewrite of Conflict Tracker --- diff --git a/src/main/gov/nasa/jpf/listener/ConflictTracker.java b/src/main/gov/nasa/jpf/listener/ConflictTracker.java index 2ba4eca..56d4d67 100644 --- a/src/main/gov/nasa/jpf/listener/ConflictTracker.java +++ b/src/main/gov/nasa/jpf/listener/ConflictTracker.java @@ -42,21 +42,20 @@ public class ConflictTracker extends ListenerAdapter { private final HashSet appSet = new HashSet(); // Apps we want to find their conflicts private final HashSet manualSet = new HashSet(); // Writer classes with manual inputs to detect direct-direct(No Conflict) interactions private final HashMap nodes = new HashMap(); // Nodes of a graph - private ArrayList tempSetSet = new ArrayList(); + private HashSet transitions = new HashSet(); + private ArrayList currUpdates = new ArrayList(); 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 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); @@ -87,161 +86,109 @@ public class ConflictTracker extends ListenerAdapter { startTime = System.currentTimeMillis(); } - boolean propagateTheChange(Node currentNode) { - HashSet changed = new HashSet(); - boolean isChanged = false; + void propagateChange(Edge newEdge) { + HashSet changed = new HashSet(); // Add the current node to the changed set - changed.add(currentNode); + changed.add(newEdge); while(!changed.isEmpty()) { // Get the first element of the changed set and remove it - Node nodeToProcess = changed.iterator().next(); - changed.remove(nodeToProcess); - - // Update all the successors of the node - for (Node node : nodeToProcess.getSuccessors()) { - isChanged = false; - isChanged = updateTheOutSet(nodeToProcess, node); - if (isChanged) { - changed.add(node); - if (checkAllSuccForConflict(node)) - return true; - } + Edge edgeToProcess = changed.iterator().next(); + changed.remove(edgeToProcess); + + //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); + } } } - return false; - } - - String createErrorMessage(NameValuePair pair, HashMap valueMap, HashMap writerMap) { - String message = "Conflict found between the two apps. App"+pair.getAppNum()+ - " has written the value: "+pair.getValue()+ - " to the variable: "+pair.getVarName()+" while App" - +writerMap.get(pair.getVarName())+" is overwriting the value: " - +valueMap.get(pair.getVarName())+" to the same variable!"; - System.out.println(message); - return message; } - boolean checkAllSuccForConflict(Node currentNode) { - for (Node node : currentNode.getSuccessors()) { - HashMap> setSets = currentNode.getOutgoingEdges().get(node).getSetSetMap(); - for (Map.Entry mapElement : setSets.entrySet()) { - Transition transition = (Transition)mapElement.getKey(); - if (checkForConflict(currentNode, node, transition)) - return true; + boolean propagateEdge(Edge e) { + HashMap> srcUpdates = e.getSrc().getLastUpdates(); + HashMap> dstUpdates = e.getDst().getLastUpdates(); + ArrayList edgeUpdates = e.getUpdates(); + HashMap lastupdate = new HashMap(); + boolean changed = false; + + for(int i=0; i confupdates = null; + if (lastupdate.containsKey(io)) { + confupdates = new HashSet(); + confupdates.add(lastupdate.get(io)); + } else if (srcUpdates.containsKey(io)){ + confupdates = srcUpdates.get(io); } - } - return false; - } - - boolean checkForConflict(Node parentNode, Node currentNode, Transition currentTransition) { - ArrayList setSet = parentNode.getOutgoingEdges().get(currentNode).getSetSetMap().get(currentTransition); - HashMap valueMap = new HashMap(); // HashMap from varName to value - HashMap writerMap = new HashMap(); // HashMap from varName to appNum - HashMap firstValueMap = new HashMap(); // HashMap from varName to value - first instruction in transition - HashMap firstWriterMap = new HashMap(); // HashMap from varName to appNum - first instruction in transition - - // Update the valueMap and writerMap + check for conflict between the elements of setSet - for (int i = 0;i < setSet.size();i++) { - NameValuePair nameValuePair = setSet.get(i); - String varName = nameValuePair.getVarName(); - String value = nameValuePair.getValue(); - Integer appNum = nameValuePair.getAppNum(); - Boolean isManual = nameValuePair.getIsManual(); - - if (valueMap.containsKey(varName)) { - // Check if we have a same writer - if (!writerMap.get(varName).equals(appNum)) { - // Check if we have a conflict or not - if (!valueMap.get(varName).equals(value)) { - errorMessage = createErrorMessage(nameValuePair, valueMap, writerMap); - return true; - } - } - valueMap.put(varName, value); - writerMap.put(varName, appNum); - } else { - valueMap.put(varName, value); - writerMap.put(varName, appNum); - if (!isManual) { - firstValueMap.put(varName, value); - firstWriterMap.put(varName, appNum); - } + //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()); - // Check for conflict between outSet and this transition setSet - for (NameValuePair i : parentNode.getOutSet()) { - if (firstValueMap.containsKey(i.getVarName())) { - String value = firstValueMap.get(i.getVarName()); - Integer writer = firstWriterMap.get(i.getVarName()); - if ((value != null)&&(writer != null)) { - if (!value.equals(i.getValue())&&!writer.equals(i.getAppNum())) { - // We have different values and different writers - errorMessage = createErrorMessage(i, firstValueMap, firstWriterMap); - return true; - } - } + changed |= dstUpdates.get(io).addAll(srcUpdates.get(io)); } } - return false; + for(IndexObject io: lastupdate.keySet()) { + //Make sure destination has hashset in map + if (!dstUpdates.containsKey(io)) + dstUpdates.put(io, new HashSet()); + + changed |= dstUpdates.get(io).add(lastupdate.get(io)); + } + return changed; } - boolean updateTheOutSet(Node parentNode, Node currentNode) { - Edge edge = parentNode.getOutgoingEdges().get(currentNode); - HashMap> setSets = edge.getSetSetMap(); - HashSet updatedVarNames = new HashSet(); - boolean isChanged = false; - - for (Map.Entry mapElement : setSets.entrySet()) { - ArrayList setSet = (ArrayList)mapElement.getValue(); + //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())); + } - for (int i = 0;i < setSet.size();i++) { - updatedVarNames.add(setSet.get(i).getVarName()); - } - } + 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; + } - for (NameValuePair i : parentNode.getOutSet()) { - if (!updatedVarNames.contains(i.getVarName())) - isChanged |= currentNode.getOutSet().add(i); - } + Edge createEdge(Node parent, Node current, Transition transition, boolean ismanual) { + if (transitions.contains(transition)) + return null; - ArrayList lastSetSet = setSets.get(edge.getFinalTransition()); + //Create edge + Edge e = new Edge(parent, current, transition, currUpdates, ismanual); + parent.addOutEdge(e); - for (int i = 0;i < lastSetSet.size();i++) { - isChanged |= currentNode.getOutSet().add(lastSetSet.get(i)); - } - return isChanged; - } - - void updateTheEdge(Node currentNode, Transition transition) { - if (parentNode.getOutgoingEdges().containsKey(currentNode)) { - Edge currentEdge = parentNode.getOutgoingEdges().get(currentNode); - if (currentEdge.getSetSetMap().containsKey(transition)) { // Update the transition - if (manual) - currentEdge.getSetSetMap().put(transition, tempSetSet); - else - currentEdge.getSetSetMap().get(transition).addAll(tempSetSet); - } else { // Add a new transition - currentEdge.getSetSetMap().put(transition, tempSetSet); - } - currentEdge.setFinalTransition(transition); - } else { - parentNode.getOutgoingEdges().put(currentNode, new Edge(parentNode, currentNode)); - Edge currentEdge = parentNode.getOutgoingEdges().get(currentNode); - currentEdge.getSetSetMap().put(transition, tempSetSet); - currentEdge.setFinalTransition(transition); - } + //Mark transition as explored + transitions.add(transition); + return e; } static class Node { Integer id; - HashSet predecessors = new HashSet(); - HashSet successors = new HashSet(); - HashSet outSet = new HashSet(); - HashMap outgoingEdges = new HashMap(); - + Vector outEdges = new Vector(); + HashMap> lastUpdates = new HashMap>(); + Node(Integer id) { this.id = id; } @@ -250,125 +197,123 @@ public class ConflictTracker extends ListenerAdapter { return id; } - HashSet getPredecessors() { - return predecessors; - } - - HashSet getSuccessors() { - return successors; + Vector getOutEdges() { + return outEdges; } - HashSet getOutSet() { - return outSet; + void addOutEdge(Edge e) { + outEdges.add(e); } - - HashMap getOutgoingEdges() { - return outgoingEdges; + + HashMap> getLastUpdates() { + return lastUpdates; } } static class Edge { Node source, destination; - Transition finalTransition; - HashMap lastWriter = new HashMap(); - HashMap lastValue = new HashMap(); - HashMap> setSetMap = new HashMap>(); + Transition transition; + ArrayList updates = new ArrayList(); + boolean ismanual; + + Edge(Node src, Node dst, Transition t, ArrayList _updates, boolean _ismanual) { + this.source = src; + this.destination = dst; + this.transition = t; + this.ismanual = _ismanual; + this.updates.addAll(_updates); + } - Edge(Node source, Node destination) { - this.source = source; - this.destination = destination; + boolean isManual() { + return ismanual; } - Node getSource() { + Node getSrc() { return source; } - Node getDestination() { + Node getDst() { return destination; } - Transition getFinalTransition() { - return finalTransition; + Transition getTransition() { + return transition; } - void setFinalTransition(Transition transition) { - finalTransition = transition; - } - - HashMap> getSetSetMap() { - return setSetMap; - } - - HashMap getLastWriter() { - return lastWriter; - } - - HashMap getLastValue() { - return lastValue; + ArrayList getUpdates() { + return updates; } } - static class NameValuePair { - Integer appNum; + static class Update { + String appName; + String deviceId; + String attribute; 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; + Update(String _appName, String _deviceId, String _attribute, String _value) { + this.appName = _appName; + this.deviceId = _deviceId; + this.attribute = _attribute; + this.value = _value; } - void setAppNum(Integer appNum) { - this.appNum = appNum; + String getApp() { + return appName; } - void setValue(String value) { - this.value = value; + String getDeviceId() { + return deviceId; } - void setVarName(String varName) { - this.varName = varName; + String getAttribute() { + return attribute; } - - void setIsManual(String varName) { - this.isManual = isManual; + + String getValue() { + return value; } - Integer getAppNum() { - return appNum; + IndexObject getIndex() { + return new IndexObject(this); } - String getValue() { - return value; + 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())); } - String getVarName() { - return varName; + public int hashCode() { + return (getDeviceId().hashCode() << 3) ^ + (getAttribute().hashCode() << 2) ^ + (getApp().hashCode() << 1) ^ + getValue().hashCode(); } + } - boolean getIsManual() { - return isManual; + static class IndexObject { + Update u; + IndexObject(Update _u) { + this.u = _u; } - - @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; + if (!(o instanceof IndexObject)) + return false; + IndexObject io=(IndexObject)o; + return (u.getDeviceId().equals(io.u.getDeviceId()) && + u.getAttribute().equals(io.u.getAttribute())); } - - @Override public int hashCode() { - return appNum.hashCode() * 31 + varName.hashCode(); + return (u.getDeviceId().hashCode() << 1) ^ u.getAttribute().hashCode(); } } - + @Override public void stateRestored(Search search) { id = search.getStateId(); @@ -379,18 +324,19 @@ public class ConflictTracker extends ListenerAdapter { 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) { @@ -401,20 +347,18 @@ public class ConflictTracker extends ListenerAdapter { operation = "forward"; // Add the node to the list of nodes - if (nodes.get(id) == null) - nodes.put(id, new Node(id)); + Node currentNode = getNode(id); - Node currentNode = nodes.get(id); - - // Update the edge based on the current transition - updateTheEdge(currentNode, transition); + // Create an edge based on the current transition + Edge newEdge = createEdge(parentNode, currentNode, transition, manual); // Reset the temporary variables and flags - tempSetSet = new ArrayList(); + currUpdates.clear(); manual = false; - // Check for the conflict in this transition - conflictFound = checkForConflict(parentNode, currentNode, transition); + // If we have a new Edge, check for conflicts + if (newEdge != null) + propagateChange(newEdge); if (search.isNewState()) { detail = "new"; @@ -429,29 +373,8 @@ public class ConflictTracker extends ListenerAdapter { 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.getPredecessors().add(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.getSuccessors().add(currentNode); - - // Update the outset of the current node and check if it is changed or not to propagate the change - boolean isChanged = updateTheOutSet(parentNode, currentNode); - - // Check if the outSet of this state has changed, update all of its successors' sets if any - if (isChanged) - conflictFound = conflictFound || checkAllSuccForConflict(currentNode) || propagateTheChange(currentNode); - // Update the parent node - if (nodes.containsKey(id)) { - parentNode = nodes.get(id); - } else { - parentNode = new Node(id); - } + parentNode = currentNode; } @Override @@ -464,11 +387,7 @@ public class ConflictTracker extends ListenerAdapter { 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 @@ -610,13 +529,10 @@ public class ConflictTracker extends ListenerAdapter { return null; } - private void writeWriterAndValue(String writer, String value, String var) { + private void writeWriterAndValue(String writer, String attribute, String value) { // Update the temporary Set set. - NameValuePair temp = new NameValuePair(1, value, var, manual); - if (writer.equals("App2")) - temp = new NameValuePair(2, value, var, manual); - - tempSetSet.add(temp); + Update u = new Update(writer, "DEVICE", attribute, value); + currUpdates.add(u); } @Override @@ -630,47 +546,40 @@ public class ConflictTracker extends ListenerAdapter { } } - if (conflictFound) { - StringBuilder sb = new StringBuilder(); - sb.append(errorMessage); - Instruction nextIns = ti.createAndThrowException("java.lang.RuntimeException", sb.toString()); - ti.setNextPC(nextIns); + 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 (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, value, LOCATION_VAR); - } - } 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, value, var); - } + 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); } } }