private Iterable<AbstractState> getWrappedStates(ARGState wrapper) { if (wrapper.getWrappedState() instanceof AbstractWrapperState) { return ((AbstractWrapperState) wrapper.getWrappedState()).getWrappedStates(); } else { return Collections.singleton(wrapper.getWrappedState()); } }
/** 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; }
@Override public ARGPath getNextPathForInterpolation() { ARGState current = sources.remove(0); assert current.isTarget() : "current element is not a target"; ARGPathBuilder errorPathBuilder = ARGPath.reverseBuilder(); errorPathBuilder.add( current, FluentIterable.from(AbstractStates.getOutgoingEdges(current)).first().orNull()); while (predecessorRelation.get(current) != null) { ARGState parent = predecessorRelation.get(current); if (stateHasFalseInterpolant(parent)) { logger.log( Level.FINEST, "interpolant on path, namely for state ", parent.getStateId(), " is already false, so return empty path"); return EMPTY_PATH; } if (predecessorRelation.get(parent) != null) { errorPathBuilder.add(parent, parent.getEdgeToChild(current)); } current = parent; } return errorPathBuilder.build(current); }
private Collection<Edge> getRelevantChildrenOfState( ARGState currentElement, Stack<FunctionBody> functionStack, Set<ARGState> elementsOnPath) { // find the next elements to add to the waitlist List<ARGState> relevantChildrenOfElement = from(currentElement.getChildren()).filter(in(elementsOnPath)).toList(); relevantChildrenOfElement = chooseIfArbitrary(currentElement, relevantChildrenOfElement); // if there is only one child on the path if (relevantChildrenOfElement.size() == 1) { // get the next ARG state, create a new edge using the same stack and add it to the waitlist ARGState elem = Iterables.getOnlyElement(relevantChildrenOfElement); CFAEdge e = currentElement.getEdgeToChild(elem); Edge newEdge = new Edge(elem, currentElement, e, functionStack); return Collections.singleton(newEdge); } else if (relevantChildrenOfElement.size() > 1) { // if there are more than one relevant child, then this is a condition // we need to update the stack assert relevantChildrenOfElement.size() == 2; Collection<Edge> result = new ArrayList<>(2); int ind = 0; for (ARGState elem : relevantChildrenOfElement) { Stack<FunctionBody> newStack = cloneStack(functionStack); CFAEdge e = currentElement.getEdgeToChild(elem); FunctionBody currentFunction = newStack.peek(); assert e instanceof CAssumeEdge; CAssumeEdge assumeEdge = (CAssumeEdge) e; boolean truthAssumption = assumeEdge.getTruthAssumption(); String cond = ""; if (ind == 0) { cond = "if "; } else if (ind == 1) { cond = "else if "; } else { throw new AssertionError(); } ind++; if (truthAssumption) { cond += "(" + assumeEdge.getExpression().toASTString() + ")"; } else { cond += "(!(" + assumeEdge.getExpression().toASTString() + "))"; } // create a new block starting with this condition currentFunction.enterBlock(currentElement.getStateId(), assumeEdge, cond); Edge newEdge = new Edge(elem, currentElement, e, newStack); result.add(newEdge); } return result; } return Collections.emptyList(); }
private Collection<ARGState> getNonCoveredStatesInSubgraph(ARGState pRoot) { Collection<ARGState> subgraph = new HashSet<>(); for (ARGState state : pRoot.getSubgraph()) { if (!state.isCovered()) { subgraph.add(state); } } return subgraph; }
protected boolean checkCertificate( ReachedSet pReachedSet, ARGState pRoot, @Nullable List<ARGState> incompleteStates) throws CPAException, InterruptedException { // TODO does not account for strengthen yet (proof check will fail if strengthen is needed to // explain successor states) initChecking(pRoot); logger.log(Level.INFO, "Proof check algorithm started"); ARGState initialState = (ARGState) pReachedSet.popFromWaitlist(); Precision initialPrecision = pReachedSet.getPrecision(initialState); logger.log(Level.FINE, "Checking root state"); if (!checkCovering(initialState, pRoot, initialPrecision)) { return false; } pReachedSet.add(pRoot, initialPrecision); do { if (!prepareNextWaitlistIteration(pReachedSet)) { return false; } while (pReachedSet.hasWaitingState()) { shutdownNotifier.shutdownIfNecessary(); stats.increaseIteration(); ARGState state = (ARGState) pReachedSet.popFromWaitlist(); logger.log(Level.FINE, "Looking at state", state); if (!checkForStatePropertyAndOtherStateActions(state)) { logger.log(Level.INFO, "Property violation at state", state); return false; } if (state.isCovered()) { if (!checkCoveredStates(state, pReachedSet, initialPrecision)) { return false; } } else { if (!checkAndAddSuccessors(state, pReachedSet, initialPrecision, incompleteStates)) { return false; } } } } while (!isCheckComplete()); stats.increaseProofSize(pReachedSet.size() - 1); return isCheckSuccessful(); }
private boolean isCoveringCycleFree(ARGState pState) { HashSet<ARGState> seen = new HashSet<>(); seen.add(pState); while (pState.isCovered()) { pState = pState.getCoveringState(); boolean isNew = seen.add(pState); if (!isNew) { return false; } } return true; }
@Override public ARGPath getNextPathForInterpolation() { ARGPathBuilder errorPathBuilder = ARGPath.builder(); ARGState current = sources.pop(); if (!isValidInterpolationRoot(predecessorRelation.get(current))) { logger.log( Level.FINEST, "interpolant of predecessor of ", current.getStateId(), " is already false, so return empty path"); return EMPTY_PATH; } // if the current state is not the root, it is a child of a branch , however, the path should // not start with the // child, but with the branching node (children are stored on the stack because this needs // less book-keeping) if (current != root) { errorPathBuilder.add( predecessorRelation.get(current), predecessorRelation.get(current).getEdgeToChild(current)); } while (successorRelation.get(current).iterator().hasNext()) { Iterator<ARGState> children = successorRelation.get(current).iterator(); ARGState child = children.next(); errorPathBuilder.add(current, current.getEdgeToChild(child)); // push all other children of the current state, if any, onto the stack for later // interpolations int size = 1; while (children.hasNext()) { size++; ARGState sibling = children.next(); logger.log( Level.FINEST, "\tpush new root ", sibling.getStateId(), " onto stack for parent ", predecessorRelation.get(sibling).getStateId()); sources.push(sibling); } assert (size <= 2); current = child; } return errorPathBuilder.build(current); }
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; }
/** * Collect all precisions in the subgraph below refinementRoot and merge their predicates. * * @return a new precision with all these predicates. */ private PredicatePrecision findAllPredicatesFromSubgraph( ARGState refinementRoot, UnmodifiableReachedSet reached) { return PredicatePrecision.unionOf( from(refinementRoot.getSubgraph()) .filter(not(ARGState::isCovered)) .transform(reached::getPrecision)); }
private boolean checkAndAddSuccessors( final ARGState pPredecessor, final ReachedSet pReachedSet, final Precision pPrecision, @Nullable List<ARGState> pIncompleteStates) throws InterruptedException, CPAException { stats.getTransferTimer().start(); Collection<ARGState> successors = pPredecessor.getChildren(); logger.log(Level.FINER, "Checking abstract successors", successors); if (!checkSuccessors(pPredecessor, successors, pPrecision)) { stats.getTransferTimer().stop(); if (pIncompleteStates != null) { pIncompleteStates.add(pPredecessor); logger.log( Level.FINER, "State", pPredecessor, "is explored incompletely, will be recorded in the assumption automaton."); return true; } logger.log(Level.WARNING, "State", pPredecessor, "has other successors than", successors); return false; } stats.getTransferTimer().stop(); if (!addSuccessors(successors, pReachedSet, pPrecision)) { return false; } return true; }
/** * Processes an edge of the CFA and will write code to the output function body. * * @param childElement the state after the given edge * @param edge the edge to process * @param functionStack the current callstack */ void processEdge(ARGState childElement, CFAEdge edge, Stack<FunctionBody> functionStack) { FunctionBody currentFunction = functionStack.peek(); if (childElement.isTarget()) { currentFunction.write(getTargetState()); } // handle the edge if (edge instanceof CFunctionCallEdge) { // if this is a function call edge we need to create a new state and push // it to the topmost stack to represent the function // create function and put in onto stack String freshFunctionName = startFunction(childElement, functionStack, edge.getPredecessor()); // write summary edge to the caller site (with the new unique function name) currentFunction.write(processFunctionCall(edge, freshFunctionName)); } else if (edge instanceof CFunctionReturnEdge) { functionStack.pop(); } else { currentFunction.write(processSimpleEdge(edge, currentFunction.getCurrentBlock())); } }
private boolean checkCoveredStates( final ARGState pCovered, final ReachedSet pReachedSet, final Precision pPrecision) throws CPAException, InterruptedException { logger.log(Level.FINER, "State is covered by another abstract state; checking coverage"); ARGState coveringState = pCovered.getCoveringState(); if (!pReachedSet.contains(coveringState)) { if (treatStateIfCoveredByUnkownState(pCovered, coveringState, pReachedSet, pPrecision)) { return true; } } stats.getStopTimer().start(); if (!isCoveringCycleFree(pCovered)) { stats.getStopTimer().stop(); logger.log(Level.WARNING, "Found cycle in covering relation for state", pCovered); return false; } if (!checkCovering(pCovered, coveringState, pPrecision)) { stats.getStopTimer().stop(); logger.log(Level.WARNING, "State", pCovered, "is not covered by", coveringState); return false; } stats.getStopTimer().stop(); return true; }
/** * Merge all predicate precisions in the subgraph below the refinement root into a new predicate * precision * * @return a new predicate precision containing all predicate precision from the subgraph below * the refinement root. */ private PredicatePrecision mergePredicatePrecisionsForSubgraph( final ARGState pRefinementRoot, final ARGReachedSet pReached) { UnmodifiableReachedSet reached = pReached.asReachedSet(); return PredicatePrecision.unionOf( from(pRefinementRoot.getSubgraph()) .filter(not(ARGState::isCovered)) .transform(reached::getPrecision)); }
/** * This method returns the target states in the subtree of the given state. * * @param state the state for which to collect the target states in its subtree. * @return target states in the subtree of the given state */ public Collection<ARGState> getTargetsInSubtree(ARGState state) { Collection<ARGState> targetStates = new HashSet<>(); Deque<ARGState> todo = new ArrayDeque<>(Collections.singleton(state)); while (!todo.isEmpty()) { final ARGState currentState = todo.removeFirst(); if (currentState.isTarget()) { targetStates.add(currentState); continue; } Collection<ARGState> successors = successorRelation.get(currentState); todo.addAll(successors); } return targetStates; }
/** * This method extracts the precision increment for the given refinement root. It does so by * collection all non-trivial interpolants in the subtree of the given refinement root. * * @return the precision increment for the given refinement root */ public Multimap<CFANode, MemoryLocation> extractPrecisionIncrement(ARGState pRefinementRoot) { Multimap<CFANode, MemoryLocation> increment = HashMultimap.create(); Deque<ARGState> todo = new ArrayDeque<>(Collections.singleton(predecessorRelation.get(pRefinementRoot))); while (!todo.isEmpty()) { final ARGState currentState = todo.removeFirst(); if (stateHasNonTrivialInterpolant(currentState) && !currentState.isTarget()) { I itp = interpolants.get(currentState); for (MemoryLocation memoryLocation : itp.getMemoryLocations()) { increment.put(AbstractStates.extractLocation(currentState), memoryLocation); } } if (!stateHasFalseInterpolant(currentState)) { todo.addAll(successorRelation.get(currentState)); } } return increment; }
private VariableTrackingPrecision mergeAllValuePrecisionsFromSubgraph( ARGState refinementRoot, UnmodifiableReachedSet reached) { VariableTrackingPrecision rootPrecision = Precisions.extractPrecisionByType( reached.getPrecision(refinementRoot), VariableTrackingPrecision.class); // find all distinct precisions to merge them Set<Precision> precisions = Sets.newIdentityHashSet(); for (ARGState state : refinementRoot.getSubgraph()) { if (!state.isCovered()) { // covered states are not in reached set precisions.add(reached.getPrecision(state)); } } for (Precision prec : precisions) { rootPrecision = rootPrecision.join( Precisions.extractPrecisionByType(prec, VariableTrackingPrecision.class)); } return rootPrecision; }
/** * Start the function, puts another body on the function stack. * * @param firstFunctionElement the first state inside the function * @param functionStack the current callstack * @param predecessor the previous node */ protected String startFunction( ARGState firstFunctionElement, Stack<FunctionBody> functionStack, CFANode predecessor) { // create the first stack element using the first element of the function CFunctionEntryNode functionStartNode = extractFunctionCallLocation(firstFunctionElement); String freshFunctionName = getFreshFunctionName(functionStartNode); String lFunctionHeader = functionStartNode.getFunctionDefinition().getType().toASTString(freshFunctionName); // lFunctionHeader is for example "void foo_99(int a)" // create a new function FunctionBody newFunction = new FunctionBody(firstFunctionElement.getStateId(), lFunctionHeader); // register function mFunctionDecls.add(lFunctionHeader + ";"); mFunctionBodies.add(newFunction); functionStack.push(newFunction); // add function to current stack return freshFunctionName; }
@Override public boolean apply(ARGState pChild) { return pChild != null && predecessor.getEdgeToChild(pChild) == edge; }
private ARGState getUncoveredSuccessor(ARGState pMaybeCovered) { while (pMaybeCovered.isCovered()) { pMaybeCovered = pMaybeCovered.getCoveringState(); } return pMaybeCovered; }
private Pair<Map<String, Integer>, List<AbstractState>> identifyCompositeStateTypesAndTheirInitialInstances(Collection<ARGState> rootNodes) throws InterruptedException, CPAException { logger.log(Level.FINE, "Derive composite state structure of combined ARG"); List<AbstractState> initialState = new ArrayList<>(); Map<String, Integer> stateToPos = new HashMap<>(); List<String> automataStateNames = new ArrayList<>(); String name; int nextId = 0; Iterable<AbstractState> wrapped; logger.log(Level.FINE, "Add non-automaton states"); for (ARGState root : rootNodes) { shutdown.shutdownIfNecessary(); wrapped = getWrappedStates(root); for (AbstractState innerWrapped : wrapped) { shutdown.shutdownIfNecessary(); if (innerWrapped instanceof AssumptionStorageState) { continue; } name = getName(innerWrapped); if (stateToPos.containsKey(name)) { if (!initialState.get(stateToPos.get(name)).equals(innerWrapped)) { logger.log( Level.WARNING, "Abstract state ", innerWrapped.getClass(), " is used by multiple configurations, but cannot check that always start in the same initial state as it is assumed"); } } else { assert (initialState.size() == nextId); if (innerWrapped instanceof AutomatonState) { automataStateNames.add(name); } else { stateToPos.put(name, nextId); initialState.add(innerWrapped); nextId++; } } } } logger.log(Level.FINE, "Add automaton states related to specification"); Collections.sort(automataStateNames); int numRootStates = rootNodes.size(); Set<String> commonAutomataStates = new TreeSet<>(); for (int i = 1, j = 0; i < automataStateNames.size(); i++) { assert (j < i && j >= 0); if (automataStateNames.get(j).equals(automataStateNames.get(i))) { if (j + numRootStates - 1 == i) { // automaton states commonly used commonAutomataStates.add(automataStateNames.get(j)); } } else { j = i; } } // assume root is the root node of the first ARG constructed ARGState root = rootNodes.iterator().next(); if (root.getWrappedState() instanceof AbstractWrapperState) { wrapped = ((AbstractWrapperState) root.getWrappedState()).getWrappedStates(); } else { wrapped = Collections.singleton(root.getWrappedState()); } for (AbstractState innerWrapped : wrapped) { shutdown.shutdownIfNecessary(); name = getName(innerWrapped); if (commonAutomataStates.contains(name)) { assert (initialState.size() == nextId); stateToPos.put(name, nextId); if (!automatonARGBuilderSupport.registerAutomaton((AutomatonState) innerWrapped)) { logger.log( Level.SEVERE, "Property specification, given by automata specification, is ambigous."); throw new CPAException( "Ambigious property specification, automata specification contains automata with same name or same state names"); } initialState.add( automatonARGBuilderSupport.replaceStateByStateInAutomatonOfSameInstance( (AutomatonState) innerWrapped)); nextId++; } } return Pair.of(stateToPos, initialState); }
private boolean combineARGs( List<ARGState> roots, ForwardingReachedSet pReceivedReachedSet, HistoryForwardingReachedSet pForwaredReachedSet) throws InterruptedException, CPAException { Pair<Map<String, Integer>, List<AbstractState>> initStates = identifyCompositeStateTypesAndTheirInitialInstances(roots); Map<String, Integer> stateToPos = initStates.getFirst(); List<AbstractState> initialStates = initStates.getSecond(); try { pReceivedReachedSet.setDelegate(new ReachedSetFactory(config, logger).create()); } catch (InvalidConfigurationException e) { logger.log(Level.SEVERE, "Creating reached set which should contain combined ARG fails."); return false; } shutdown.shutdownIfNecessary(); // combined root ARGState combinedRoot = new ARGState(new CompositeState(initialStates), null); CFANode locPred; ARGState composedState, composedSuccessor; Collection<ARGState> components; List<List<ARGState>> successorsForEdge = new ArrayList<>(initialStates.size()); EdgeSuccessor edgeSuccessorIdentifier = new EdgeSuccessor(); Map<Pair<List<AbstractState>, List<ARGState>>, ARGState> constructedCombinedStates = Maps.newHashMap(); Deque<Pair<List<ARGState>, ARGState>> toVisit = new ArrayDeque<>(); toVisit.add(Pair.of(roots, combinedRoot)); // traverse through ARGs and construct combined ARG // assume that states in initial states are most general, represent top state (except for // automaton CPAs) while (!toVisit.isEmpty()) { shutdown.shutdownIfNecessary(); components = toVisit.peek().getFirst(); composedState = toVisit.poll().getSecond(); // add composed state to reached set pReceivedReachedSet.add(composedState, SingletonPrecision.getInstance()); pReceivedReachedSet.removeOnlyFromWaitlist(composedState); // identify possible successor edges locPred = AbstractStates.extractLocation(composedState); nextEdge: for (CFAEdge succEdge : CFAUtils.allLeavingEdges(locPred)) { shutdown.shutdownIfNecessary(); successorsForEdge.clear(); edgeSuccessorIdentifier.setCFAEdge(succEdge); for (ARGState component : components) { // get the successors of ARG state for this edge succEdge edgeSuccessorIdentifier.setPredecessor(component); successorsForEdge.add( Lists.newArrayList( Iterables.filter(component.getChildren(), edgeSuccessorIdentifier))); // check if stopped because no concrete successors exists, then do not if (successorsForEdge.get(successorsForEdge.size() - 1).isEmpty() && noConcreteSuccessorExist(component, succEdge, pForwaredReachedSet)) { continue nextEdge; } } // construct successors for each identified combination for (Pair<List<AbstractState>, List<ARGState>> combinedSuccessor : computeCartesianProduct(successorsForEdge, stateToPos, initialStates)) { if (constructedCombinedStates.containsKey(combinedSuccessor)) { // handle coverage constructedCombinedStates.get(combinedSuccessor).addParent(composedState); } else { // construct and register composed successor composedSuccessor = new ARGState(new CompositeState(combinedSuccessor.getFirst()), composedState); constructedCombinedStates.put(combinedSuccessor, composedSuccessor); // add successor for further exploration toVisit.add(Pair.of(combinedSuccessor.getSecond(), composedSuccessor)); } } } } return true; }
@Override public AlgorithmStatus run(ReachedSet pReachedSet) throws CPAException, InterruptedException, CPAEnabledAnalysisPropertyViolationException { checkArgument( pReachedSet instanceof ForwardingReachedSet, "PartialARGsCombiner needs ForwardingReachedSet"); HistoryForwardingReachedSet reached = new HistoryForwardingReachedSet(pReachedSet); logger.log(Level.INFO, "Start inner algorithm to analyze program(s)"); AlgorithmStatus status = AlgorithmStatus.UNSOUND_AND_PRECISE; stats.analysisTime.start(); try { status = restartAlgorithm.run(reached); } finally { stats.analysisTime.stop(); } if (status.isSound()) { shutdown.shutdownIfNecessary(); logger.log(Level.INFO, "Program(s) soundly analyzed, start combining ARGs."); stats.argCombineTime.start(); try { Collection<ReachedSet> usedReachedSets = reached.getAllReachedSetsUsedAsDelegates(); if (usedReachedSets.size() <= 1) { logger.log(Level.INFO, "Only a single ARG is considered. Do not need to combine ARGs"); if (usedReachedSets.size() == 1) { ((ForwardingReachedSet) pReachedSet).setDelegate(reached.getDelegate()); } return status; } if (from(reached.getDelegate()).anyMatch((IS_TARGET_STATE))) { logger.log(Level.INFO, "Error found, do not combine ARGs."); ((ForwardingReachedSet) pReachedSet).setDelegate(reached.getDelegate()); return status; } logger.log(Level.FINE, "Extract root nodes of ARGs"); List<ARGState> rootNodes = new ArrayList<>(usedReachedSets.size()); for (ReachedSet usedReached : usedReachedSets) { checkArgument( usedReached.getFirstState() instanceof ARGState, "Require that all restart configurations use ARGCPA as top level CPA."); checkArgument( AbstractStates.extractLocation(usedReached.getFirstState()) != null, "Require that all restart configurations consider a location aware state"); for (AbstractState errorState : from(usedReached).filter((IS_TARGET_STATE))) { logger.log( Level.INFO, "Error state found in reached set ", usedReached, "but not by last configuration. Error state must be infeasible."); logger.log(Level.FINE, "Remove infeasible error state", errorState); ((ARGState) errorState).removeFromARG(); } rootNodes.add((ARGState) usedReached.getFirstState()); } shutdown.shutdownIfNecessary(); if (!combineARGs(rootNodes, (ForwardingReachedSet) pReachedSet, reached)) { logger.log(Level.SEVERE, "Combination of ARGs failed."); return status.withSound(false); } } finally { stats.argCombineTime.stop(); } logger.log(Level.INFO, "Finished combination of ARGS"); } else { logger.log( Level.INFO, "Program analysis is already unsound.", "Do not continue with combination of unsound results"); // set reached set to last used by restart algorithm if (reached.getDelegate() != pReachedSet) { ((ForwardingReachedSet) pReachedSet).setDelegate(reached.getDelegate()); } return status.withSound(false); } return status.withSound(true); }
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; }
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); }
public void writeCoverageReport( final PrintStream pStatisticsOutput, final ReachedSet pReached, final CFA pCfa) { if (!enabled) { return; } Multiset<FunctionEntryNode> reachedLocations = getFunctionEntriesFromReached(pReached); Map<String, FileCoverageInformation> infosPerFile = new HashMap<>(); // Add information about existing functions for (FunctionEntryNode entryNode : pCfa.getAllFunctionHeads()) { final FileLocation loc = entryNode.getFileLocation(); if (loc.getStartingLineNumber() == 0) { // dummy location continue; } final String functionName = entryNode.getFunctionName(); final FileCoverageInformation infos = getFileInfoTarget(loc, infosPerFile); final int startingLine = loc.getStartingLineInOrigin(); final int endingLine = loc.getEndingLineInOrigin(); infos.addExistingFunction(functionName, startingLine, endingLine); if (reachedLocations.contains(entryNode)) { infos.addVisitedFunction(entryNode.getFunctionName(), reachedLocations.count(entryNode)); } } // Add information about existing locations for (CFANode node : pCfa.getAllNodes()) { for (int i = 0; i < node.getNumLeavingEdges(); i++) { handleExistedEdge(node.getLeavingEdge(i), infosPerFile); } } Set<CFANode> reachedNodes = from(pReached).transform(EXTRACT_LOCATION).filter(notNull()).toSet(); // Add information about visited locations for (AbstractState state : pReached) { ARGState argState = AbstractStates.extractStateByType(state, ARGState.class); if (argState != null) { for (ARGState child : argState.getChildren()) { if (!child.isCovered()) { List<CFAEdge> edges = argState.getEdgesToChild(child); if (edges.size() > 1) { for (CFAEdge innerEdge : edges) { handleCoveredEdge(innerEdge, infosPerFile); } // BAM produces paths with no edge connection thus the list will be empty } else if (!edges.isEmpty()) { handleCoveredEdge(Iterables.getOnlyElement(edges), infosPerFile); } } } } else { // Simple kind of analysis // Cover all edges from reached nodes // It is less precise, but without ARG it is impossible to know what path we chose CFANode node = AbstractStates.extractLocation(state); for (int i = 0; i < node.getNumLeavingEdges(); i++) { CFAEdge edge = node.getLeavingEdge(i); if (reachedNodes.contains(edge.getSuccessor())) { handleCoveredEdge(edge, infosPerFile); } } } } for (CoverageWriter w : reportWriters) { w.write(infosPerFile, pStatisticsOutput); } }