/** This method builds an actual tree from multiple path. */ private ARGState buildTreeFromMultiplePaths(final Collection<ARGPath> targetPaths) { ARGState itpTreeRoot = null; Deque<ARGState> todo = new ArrayDeque<>(extractTargets(targetPaths)); // build the tree, bottom-up, starting from the target states while (!todo.isEmpty()) { final ARGState currentState = todo.removeFirst(); if (currentState.getParents().iterator().hasNext()) { if (!predecessorRelation.containsKey(currentState)) { ARGState parentState = currentState.getParents().iterator().next(); predecessorRelation.put(currentState, parentState); successorRelation.put(parentState, currentState); todo.addFirst(parentState); } } else if (itpTreeRoot == null) { itpTreeRoot = currentState; } } return itpTreeRoot; }
private List<ARGState> chooseIfArbitrary( ARGState parent, List<ARGState> pRelevantChildrenOfElement) { if (pRelevantChildrenOfElement.size() <= 1) { return pRelevantChildrenOfElement; } List<ARGState> result = new ArrayList<>(2); for (ARGState candidate : pRelevantChildrenOfElement) { boolean valid = true; if (!result.isEmpty()) { Set<ARGState> candidateParents = ImmutableSet.copyOf(candidate.getParents()); Set<ARGState> candidateChildren = ImmutableSet.copyOf(candidate.getChildren()); for (ARGState chosen : result) { if (parent.getEdgesToChild(chosen).equals(parent.getEdgesToChild(candidate))) { Set<ARGState> chosenParents = ImmutableSet.copyOf(chosen.getParents()); Set<ARGState> chosenChildren = ImmutableSet.copyOf(chosen.getChildren()); if (candidateParents.equals(chosenParents) && candidateChildren.equals(chosenChildren)) { valid = false; break; } } } } if (valid) { result.add(candidate); } } return result; }
private List<BooleanFormula> computeBlockFormulas(final ARGState pRoot) throws CPATransferException, InterruptedException { final Map<ARGState, ARGState> callStacks = new HashMap<>(); // contains states and their next higher callstate final Map<ARGState, PathFormula> finishedFormulas = new HashMap<>(); final List<BooleanFormula> abstractionFormulas = new ArrayList<>(); final Deque<ARGState> waitlist = new ArrayDeque<>(); // initialize assert pRoot.getParents().isEmpty() : "rootState must be the first state of the program"; callStacks.put(pRoot, null); // main-start has no callstack finishedFormulas.put(pRoot, pfmgr.makeEmptyPathFormula()); waitlist.addAll(pRoot.getChildren()); // iterate over all elements in the ARG with BFS while (!waitlist.isEmpty()) { final ARGState currentState = waitlist.pollFirst(); if (finishedFormulas.containsKey(currentState)) { continue; // already handled } if (!finishedFormulas.keySet().containsAll(currentState.getParents())) { // parent not handled yet, re-queue current element and wait for all parents waitlist.addLast(currentState); continue; } // collect formulas for current location final List<PathFormula> currentFormulas = new ArrayList<>(currentState.getParents().size()); final List<ARGState> currentStacks = new ArrayList<>(currentState.getParents().size()); for (ARGState parentElement : currentState.getParents()) { PathFormula parentFormula = finishedFormulas.get(parentElement); final CFAEdge edge = parentElement.getEdgeToChild(currentState); assert edge != null : "ARG is invalid: parent has no edge to child"; final ARGState prevCallState; // we enter a function, so lets add the previous state to the stack if (edge.getEdgeType() == CFAEdgeType.FunctionCallEdge) { prevCallState = parentElement; } else if (edge.getEdgeType() == CFAEdgeType.FunctionReturnEdge) { // we leave a function, so rebuild return-state before assigning the return-value. // rebuild states with info from previous state assert callStacks.containsKey(parentElement); final ARGState callState = callStacks.get(parentElement); assert extractLocation(callState).getLeavingSummaryEdge().getSuccessor() == extractLocation(currentState) : "callstack does not match entry of current function-exit."; assert callState != null || currentState.getChildren().isEmpty() : "returning from empty callstack is only possible at program-exit"; prevCallState = callStacks.get(callState); parentFormula = rebuildStateAfterFunctionCall( parentFormula, finishedFormulas.get(callState), (FunctionExitNode) extractLocation(parentElement)); } else { assert callStacks.containsKey(parentElement); // check for null is not enough prevCallState = callStacks.get(parentElement); } final PathFormula currentFormula = pfmgr.makeAnd(parentFormula, edge); currentFormulas.add(currentFormula); currentStacks.add(prevCallState); } assert currentFormulas.size() >= 1 : "each state except root must have parents"; assert currentStacks.size() == currentFormulas.size() : "number of callstacks must match predecessors"; // merging after functioncall with different callstates is ugly. // this is also guaranteed by the abstraction-locations at function-entries // (--> no merge of states with different latest abstractions). assert Sets.newHashSet(currentStacks).size() <= 1 : "function with multiple entry-states not supported"; callStacks.put(currentState, currentStacks.get(0)); PathFormula currentFormula; final PredicateAbstractState predicateElement = extractStateByType(currentState, PredicateAbstractState.class); if (predicateElement.isAbstractionState()) { // abstraction element is the start of a new part of the ARG assert waitlist.isEmpty() : "todo should be empty, because of the special ARG structure"; assert currentState.getParents().size() == 1 : "there should be only one parent, because of the special ARG structure"; // finishedFormulas.clear(); // free some memory // TODO disabled, we need to keep callStates for later usage // start new block with empty formula currentFormula = getOnlyElement(currentFormulas); abstractionFormulas.add(currentFormula.getFormula()); currentFormula = pfmgr.makeEmptyPathFormula(currentFormula); } else { // merge the formulas Iterator<PathFormula> it = currentFormulas.iterator(); currentFormula = it.next(); while (it.hasNext()) { currentFormula = pfmgr.makeOr(currentFormula, it.next()); } } assert !finishedFormulas.containsKey(currentState) : "a state should only be finished once"; finishedFormulas.put(currentState, currentFormula); waitlist.addAll(currentState.getChildren()); } return abstractionFormulas; }
private Collection<Edge> handleEdge( Edge nextEdge, Map<Integer, MergeNode> mergeNodes, Set<ARGState> elementsOnPath, EdgeVisitor callback) { ARGState childElement = nextEdge.getChildState(); CFAEdge edge = nextEdge.getEdge(); Stack<FunctionBody> functionStack = nextEdge.getStack(); // clone stack to have a different representation of the function calls and conditions // for every element functionStack = cloneStack(functionStack); // we do not have a single edge, instead a dynamic multi-edge if (edge == null) { List<CFAEdge> edges = nextEdge.getParentState().getEdgesToChild(childElement); for (CFAEdge inner : edges) { callback.visit(childElement, inner, functionStack); } } else { callback.visit(childElement, edge, functionStack); } // how many parents does the child have? // ignore parents not on the error path int noOfParents = from(childElement.getParents()).filter(in(elementsOnPath)).size(); assert noOfParents >= 1; // handle merging if necessary if (noOfParents > 1) { assert !((edge instanceof CFunctionCallEdge) || (childElement.isTarget())); // this is the end of a condition, determine whether we should continue or backtrack int elemId = childElement.getStateId(); FunctionBody currentFunction = functionStack.peek(); currentFunction.write("goto label_" + elemId + ";"); // get the merge node for that node MergeNode mergeNode = mergeNodes.get(elemId); // if null create new and put in the map if (mergeNode == null) { mergeNode = new MergeNode(elemId); mergeNodes.put(elemId, mergeNode); } // this tells us the number of edges (entering that node) processed so far int noOfProcessedBranches = mergeNode.addBranch(currentFunction); // if all edges are processed if (noOfParents == noOfProcessedBranches) { // all branches are processed, now decide which nodes to remove from the stack List<FunctionBody> incomingStacks = mergeNode.getIncomingStates(); FunctionBody newFunction = processIncomingStacks(incomingStacks); // replace the current function body with the right one functionStack.pop(); functionStack.push(newFunction); newFunction.write("label_" + elemId + ": ;"); } else { return Collections.emptySet(); } } return getRelevantChildrenOfState(childElement, functionStack, elementsOnPath); }
private ARGState relocateRefinementRoot( final ARGState pRefinementRoot, final boolean predicatePrecisionIsAvailable) throws InterruptedException { // no relocation needed if only running value analysis, // because there, this does slightly degrade performance // when running VA+PA, merging/covering and refinements // of both CPAs could lead to the state, where in two // subsequent refinements, two identical error paths // were found, through different parts of the ARG // So now, when running VA+PA, the refinement root // is set to the lowest common ancestor of those states // that are covered by the states in the subtree of the // original refinement root if (!predicatePrecisionIsAvailable) { return pRefinementRoot; } // no relocation needed if restart at top if (restartStrategy == RestartStrategy.ROOT) { return pRefinementRoot; } Set<ARGState> descendants = pRefinementRoot.getSubgraph(); Set<ARGState> coveredStates = new HashSet<>(); shutdownNotifier.shutdownIfNecessary(); for (ARGState descendant : descendants) { coveredStates.addAll(descendant.getCoveredByThis()); } coveredStates.add(pRefinementRoot); // no relocation needed if set of descendants is closed under coverage if (descendants.containsAll(coveredStates)) { return pRefinementRoot; } Map<ARGState, ARGState> predecessorRelation = Maps.newHashMap(); SetMultimap<ARGState, ARGState> successorRelation = LinkedHashMultimap.create(); Deque<ARGState> todo = new ArrayDeque<>(coveredStates); ARGState coverageTreeRoot = null; // build the coverage tree, bottom-up, starting from the covered states while (!todo.isEmpty()) { shutdownNotifier.shutdownIfNecessary(); final ARGState currentState = todo.removeFirst(); if (currentState.getParents().iterator().hasNext()) { ARGState parentState = currentState.getParents().iterator().next(); todo.add(parentState); predecessorRelation.put(currentState, parentState); successorRelation.put(parentState, currentState); } else if (coverageTreeRoot == null) { coverageTreeRoot = currentState; } } // starting from the root of the coverage tree, // the new refinement root is either the first node // having two or more children, or the original // refinement root, what ever comes first shutdownNotifier.shutdownIfNecessary(); ARGState newRefinementRoot = coverageTreeRoot; while (successorRelation.get(newRefinementRoot).size() == 1 && newRefinementRoot != pRefinementRoot) { newRefinementRoot = Iterables.getOnlyElement(successorRelation.get(newRefinementRoot)); } rootRelocations.inc(); return newRefinementRoot; }