org.sosy_lab.cpachecker.cfa.postprocessing.global.CFAReduction.java Source code

Java tutorial

Introduction

Here is the source code for org.sosy_lab.cpachecker.cfa.postprocessing.global.CFAReduction.java

Source

/*
 *  CPAchecker is a tool for configurable software verification.
 *  This file is part of CPAchecker.
 *
 *  Copyright (C) 2007-2014  Dirk Beyer
 *  All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *
 *  CPAchecker web page:
 *    http://cpachecker.sosy-lab.org
 */
package org.sosy_lab.cpachecker.cfa.postprocessing.global;

import static com.google.common.collect.FluentIterable.from;
import static org.sosy_lab.cpachecker.util.AbstractStates.*;

import java.util.Collection;
import java.util.Set;
import java.util.logging.Level;

import org.sosy_lab.common.configuration.Configuration;
import org.sosy_lab.common.configuration.InvalidConfigurationException;
import org.sosy_lab.common.log.LogManager;
import org.sosy_lab.cpachecker.cfa.CFACreationUtils;
import org.sosy_lab.cpachecker.cfa.MutableCFA;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.model.CFANode;
import org.sosy_lab.cpachecker.core.CPABuilder;
import org.sosy_lab.cpachecker.core.ShutdownNotifier;
import org.sosy_lab.cpachecker.core.algorithm.Algorithm;
import org.sosy_lab.cpachecker.core.algorithm.CPAAlgorithm;
import org.sosy_lab.cpachecker.core.interfaces.ConfigurableProgramAnalysis;
import org.sosy_lab.cpachecker.core.reachedset.ReachedSet;
import org.sosy_lab.cpachecker.core.reachedset.ReachedSetFactory;
import org.sosy_lab.cpachecker.exceptions.CPAException;
import org.sosy_lab.cpachecker.util.CFATraversal;
import org.sosy_lab.cpachecker.util.CPAs;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;

/**
 * Perform a (very) simple cone-of-influence reduction on the given CFA.
 * That is, get rid of all the nodes/edges that are not reachable from the
 * potential error states (according to the specification).
 *
 * In fact, this should probably *not* be called ConeOfInfluenceCFAReduction,
 * since it is *much* more trivial (and less powerful) than that.
 */
public class CFAReduction {

    private final Configuration config;
    private final LogManager logger;
    private final ShutdownNotifier shutdownNotifier;

    public CFAReduction(Configuration config, LogManager logger, ShutdownNotifier pShutdownNotifier)
            throws InvalidConfigurationException {
        if (config.getProperty("specification") == null) {
            throw new InvalidConfigurationException(
                    "Option cfa.removeIrrelevantForSpecification is only valid if a specification is given!");
        }

        this.config = config;
        this.logger = logger;
        this.shutdownNotifier = pShutdownNotifier;
    }

    public void removeIrrelevantForSpecification(final MutableCFA cfa) throws InterruptedException {
        Collection<CFANode> errorNodes = getErrorNodesWithCPA(cfa);

        if (errorNodes.isEmpty()) {
            // shortcut, all nodes are irrelevant
            cfa.clear();
            return;
        }

        Collection<CFANode> allNodes = cfa.getAllNodes();

        if (errorNodes.size() == allNodes.size()) {
            // shortcut, no node is irrelevant
            return;
        }

        CFATraversal.NodeCollectingCFAVisitor cfaVisitor = new CFATraversal.NodeCollectingCFAVisitor();
        CFATraversal traversal = CFATraversal.dfs().backwards();
        // backwards search to determine all relevant nodes
        for (CFANode n : errorNodes) {
            traversal.traverse(n, cfaVisitor);
        }
        Set<CFANode> relevantNodes = cfaVisitor.getVisitedNodes();

        assert allNodes.containsAll(relevantNodes) : "Inconsistent CFA";

        int numIrrelevantNodes = allNodes.size() - relevantNodes.size();

        logger.log(Level.INFO, "Detected", numIrrelevantNodes, "irrelevant CFA nodes.");

        if (numIrrelevantNodes == 0) {
            // shortcut, no node is irrelevant
            return;
        }

        Predicate<CFANode> irrelevantNode = Predicates.not(Predicates.in(relevantNodes));
        Collection<CFANode> removedNodes = ImmutableList.copyOf(Collections2.filter(allNodes, irrelevantNode));

        // now detach all the nodes not visited
        pruneIrrelevantNodes(cfa, removedNodes, errorNodes);
    }

    private Collection<CFANode> getErrorNodesWithCPA(MutableCFA cfa) throws InterruptedException {
        try {
            LogManager lLogger = logger.withComponentName("CFAReduction");
            ReachedSetFactory lReachedSetFactory = new ReachedSetFactory(Configuration.defaultConfiguration(),
                    lLogger);

            // create new configuration based on existing config but with default set of CPAs
            Configuration lConfig = Configuration.builder().copyFrom(config).setOption("output.disable", "true")
                    .clearOption("cpa").clearOption("cpas").clearOption("CompositeCPA.cpas").build();

            CPABuilder lBuilder = new CPABuilder(lConfig, lLogger, shutdownNotifier, lReachedSetFactory);
            ConfigurableProgramAnalysis lCpas = lBuilder.buildCPAs(cfa);
            Algorithm lAlgorithm = CPAAlgorithm.create(lCpas, lLogger, lConfig, shutdownNotifier);
            ReachedSet lReached = lReachedSetFactory.create();
            lReached.add(lCpas.getInitialState(cfa.getMainFunction()),
                    lCpas.getInitialPrecision(cfa.getMainFunction()));

            lAlgorithm.run(lReached);

            CPAs.closeCpaIfPossible(lCpas, lLogger);
            CPAs.closeIfPossible(lAlgorithm, lLogger);

            return from(lReached).filter(IS_TARGET_STATE).transform(EXTRACT_LOCATION).toSet();

        } catch (CPAException e) {
            logger.logUserException(Level.WARNING, e, "Error during CFA reduction, using full CFA");
        } catch (InvalidConfigurationException e) {
            logger.logUserException(Level.WARNING, e,
                    "Invalid configuration used for CFA reduction, using full CFA");
        }
        return cfa.getAllNodes();
    }

    private void pruneIrrelevantNodes(MutableCFA cfa, Collection<CFANode> irrelevantNodes,
            Collection<CFANode> errorNodes) {

        for (CFANode n : irrelevantNodes) {
            cfa.removeNode(n);

            // check if node is successor of error node and remove incoming edges
            for (int edgeIndex = n.getNumEnteringEdges() - 1; edgeIndex >= 0; edgeIndex--) {
                CFAEdge removedEdge = n.getEnteringEdge(edgeIndex);
                CFANode prevNode = removedEdge.getPredecessor();

                if (!errorNodes.contains(prevNode)) {
                    // do not remove the direct successors of error nodes

                    CFACreationUtils.removeEdgeFromNodes(removedEdge);
                }
            }

            // remove all outgoing edges
            while (n.getNumLeavingEdges() > 0) {
                CFACreationUtils.removeEdgeFromNodes(n.getLeavingEdge(0));
            }

            // remove all summary edges
            if (n.getEnteringSummaryEdge() != null) {
                CFACreationUtils.removeSummaryEdgeFromNodes(n.getEnteringSummaryEdge());
            }
            if (n.getLeavingSummaryEdge() != null) {
                CFACreationUtils.removeSummaryEdgeFromNodes(n.getLeavingSummaryEdge());
            }
        }
    }
}