org.sosy_lab.cpachecker.cpa.apron.refiner.ApronDelegatingRefiner.java Source code

Java tutorial

Introduction

Here is the source code for org.sosy_lab.cpachecker.cpa.apron.refiner.ApronDelegatingRefiner.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.cpa.apron.refiner;

import java.io.PrintStream;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;

import org.sosy_lab.common.Pair;
import org.sosy_lab.common.configuration.InvalidConfigurationException;
import org.sosy_lab.common.configuration.Option;
import org.sosy_lab.common.configuration.Options;
import org.sosy_lab.common.configuration.TimeSpanOption;
import org.sosy_lab.common.log.LogManager;
import org.sosy_lab.common.time.TimeSpan;
import org.sosy_lab.cpachecker.cfa.CFA;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.model.CFANode;
import org.sosy_lab.cpachecker.core.CPAcheckerResult.Result;
import org.sosy_lab.cpachecker.core.CounterexampleInfo;
import org.sosy_lab.cpachecker.core.ShutdownNotifier;
import org.sosy_lab.cpachecker.core.counterexample.Model;
import org.sosy_lab.cpachecker.core.defaults.VariableTrackingPrecision;
import org.sosy_lab.cpachecker.core.interfaces.ConfigurableProgramAnalysis;
import org.sosy_lab.cpachecker.core.interfaces.Precision;
import org.sosy_lab.cpachecker.core.interfaces.Statistics;
import org.sosy_lab.cpachecker.core.interfaces.StatisticsProvider;
import org.sosy_lab.cpachecker.core.interfaces.WrapperCPA;
import org.sosy_lab.cpachecker.core.reachedset.ReachedSet;
import org.sosy_lab.cpachecker.core.reachedset.UnmodifiableReachedSet;
import org.sosy_lab.cpachecker.cpa.apron.ApronCPA;
import org.sosy_lab.cpachecker.cpa.arg.ARGPath;
import org.sosy_lab.cpachecker.cpa.arg.ARGReachedSet;
import org.sosy_lab.cpachecker.cpa.arg.ARGState;
import org.sosy_lab.cpachecker.cpa.arg.AbstractARGBasedRefiner;
import org.sosy_lab.cpachecker.cpa.arg.MutableARGPath;
import org.sosy_lab.cpachecker.cpa.predicate.PredicateCPARefiner;
import org.sosy_lab.cpachecker.cpa.value.ValueAnalysisState.MemoryLocation;
import org.sosy_lab.cpachecker.cpa.value.refiner.ValueAnalysisPathInterpolator;
import org.sosy_lab.cpachecker.cpa.value.refiner.utils.ValueAnalysisFeasibilityChecker;
import org.sosy_lab.cpachecker.exceptions.CPAException;
import org.sosy_lab.cpachecker.util.Precisions;
import org.sosy_lab.cpachecker.util.resources.ResourceLimit;
import org.sosy_lab.cpachecker.util.resources.ResourceLimitChecker;
import org.sosy_lab.cpachecker.util.resources.WalltimeLimit;

import apron.ApronException;

import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;

/**
 * Refiner implementation that delegates to {@link ApronInterpolationBasedRefiner},
 * and if this fails, optionally delegates also to {@link PredicateCPARefiner}.
 */
@Options(prefix = "cpa.apron.refiner")
public class ApronDelegatingRefiner extends AbstractARGBasedRefiner implements Statistics, StatisticsProvider {

    /**
     * refiner used for value-analysis interpolation refinement
     */
    private ValueAnalysisPathInterpolator interpolatingRefiner;

    /**
     * the hash code of the previous error path
     */
    private int previousErrorPathID = -1;

    /**
     * the flag to determine whether or not to check for repeated refinements
     */
    @Option(secure = true, description = "whether or not to check for repeated refinements, to then reset the refinement root")
    private boolean checkForRepeatedRefinements = true;

    @Option(secure = true, description = "Timelimit for the backup feasibility check with the apron analysis."
            + "(use seconds or specify a unit; 0 for infinite)")
    @TimeSpanOption(codeUnit = TimeUnit.NANOSECONDS, defaultUserUnit = TimeUnit.SECONDS, min = 0)
    private TimeSpan timeForApronFeasibilityCheck = TimeSpan.ofNanos(0);

    // statistics
    private int numberOfValueAnalysisRefinements = 0;
    private int numberOfSuccessfulValueAnalysisRefinements = 0;

    /**
     * the identifier which is used to identify repeated refinements
     */
    private int previousRefinementId = 0;

    /** if this variable is toggled, only octagon refinements will be done as
     * value analysis refinements will make no sense any more because they are too
     * imprecise
     */
    private boolean existsExplicitApronRefinement = false;

    private final CFA cfa;
    private final ApronCPA apronCPA;

    private final LogManager logger;
    private final ShutdownNotifier shutdownNotifier;

    public static ApronDelegatingRefiner create(ConfigurableProgramAnalysis cpa)
            throws CPAException, InvalidConfigurationException {
        if (!(cpa instanceof WrapperCPA)) {
            throw new InvalidConfigurationException(
                    ApronDelegatingRefiner.class.getSimpleName() + " could not find the ApronCPA");
        }

        ApronCPA apronCPA = ((WrapperCPA) cpa).retrieveWrappedCpa(ApronCPA.class);
        if (apronCPA == null) {
            throw new InvalidConfigurationException(
                    ApronDelegatingRefiner.class.getSimpleName() + " needs an ApronCPA");
        }

        ApronDelegatingRefiner refiner = new ApronDelegatingRefiner(cpa, apronCPA);

        return refiner;
    }

    private ApronDelegatingRefiner(final ConfigurableProgramAnalysis pCpa, final ApronCPA pApronCPA)
            throws CPAException, InvalidConfigurationException {
        super(pCpa);
        pApronCPA.getConfiguration().inject(this);

        cfa = pApronCPA.getCFA();
        logger = pApronCPA.getLogger();
        shutdownNotifier = pApronCPA.getShutdownNotifier();
        apronCPA = pApronCPA;
        interpolatingRefiner = new ValueAnalysisPathInterpolator(pApronCPA.getConfiguration(), logger,
                shutdownNotifier, cfa);
    }

    @Override
    protected CounterexampleInfo performRefinement(final ARGReachedSet reached, final ARGPath pErrorPath)
            throws CPAException, InterruptedException {

        MutableARGPath errorPath = pErrorPath.mutableCopy();

        // if path is infeasible, try to refine the precision
        if (!isPathFeasable(pErrorPath) && !existsExplicitApronRefinement) {
            if (performValueAnalysisRefinement(reached, errorPath)) {
                return CounterexampleInfo.spurious();
            }
        }

        // if the path is infeasible, try to refine the precision, this time
        // only with apron states, this is more precise than only using the value analysis
        // refinement
        ApronAnalysisFeasabilityChecker apronChecker;
        try {
            apronChecker = createApronFeasibilityChecker(errorPath);
        } catch (ApronException e) {
            throw new RuntimeException("An error occured while operating with the apron library", e);
        }
        if (!apronChecker.isFeasible()) {
            if (performApronAnalysisRefinement(reached, apronChecker)) {
                existsExplicitApronRefinement = true;
                return CounterexampleInfo.spurious();
            }
        }

        return CounterexampleInfo.feasible(pErrorPath, Model.empty());
    }

    /**
     * This method performs an value-analysis refinement.
     *
     * @param reached the current reached set
     * @param errorPath the current error path
     * @returns true, if the value-analysis refinement was successful, else false
     * @throws CPAException when value-analysis interpolation fails
     * @throws InvalidConfigurationException
     */
    private boolean performValueAnalysisRefinement(final ARGReachedSet reached, final MutableARGPath errorPath)
            throws CPAException, InterruptedException {
        numberOfValueAnalysisRefinements++;

        UnmodifiableReachedSet reachedSet = reached.asReachedSet();
        Precision precision = reachedSet.getPrecision(reachedSet.getLastState());
        VariableTrackingPrecision apronPrecision = (VariableTrackingPrecision) Precisions.asIterable(precision)
                .filter(VariableTrackingPrecision.isMatchingCPAClass(ApronCPA.class)).get(0);

        VariableTrackingPrecision refinedApronPrecision;
        Pair<ARGState, CFAEdge> refinementRoot;

        Multimap<CFANode, MemoryLocation> increment = interpolatingRefiner.determinePrecisionIncrement(errorPath);
        refinementRoot = interpolatingRefiner.determineRefinementRoot(errorPath, increment, false);

        // no increment - value-analysis refinement was not successful
        if (increment.isEmpty()) {
            return false;
        }

        // if two subsequent refinements are similar (based on some fancy heuristic), choose a different refinement root
        if (checkForRepeatedRefinements && isRepeatedRefinement(increment, refinementRoot)) {
            refinementRoot = interpolatingRefiner.determineRefinementRoot(errorPath, increment, true);
        }

        refinedApronPrecision = apronPrecision.withIncrement(increment);

        if (valueAnalysisRefinementWasSuccessful(errorPath, apronPrecision, refinedApronPrecision)) {
            numberOfSuccessfulValueAnalysisRefinements++;
            reached.removeSubtree(refinementRoot.getFirst(), refinedApronPrecision,
                    VariableTrackingPrecision.isMatchingCPAClass(ApronCPA.class));
            return true;

        } else {
            return false;
        }
    }

    private boolean performApronAnalysisRefinement(final ARGReachedSet reached,
            final ApronAnalysisFeasabilityChecker checker) {
        UnmodifiableReachedSet reachedSet = reached.asReachedSet();
        Precision precision = reachedSet.getPrecision(reachedSet.getLastState());
        VariableTrackingPrecision apronPrecision = (VariableTrackingPrecision) Precisions.asIterable(precision)
                .filter(VariableTrackingPrecision.isMatchingCPAClass(ApronCPA.class)).get(0);

        Multimap<CFANode, MemoryLocation> increment = checker.getPrecisionIncrement(apronPrecision);
        // no newly tracked variables, so the refinement was not successful // TODO why is this commented out
        if (increment.isEmpty()) {
            //  return false;
        }

        reached.removeSubtree(((ARGState) reachedSet.getFirstState()).getChildren().iterator().next(),
                apronPrecision.withIncrement(increment),
                VariableTrackingPrecision.isMatchingCPAClass(ApronCPA.class));

        logger.log(Level.INFO,
                "Refinement successful, precision incremented, following variables are now tracked additionally:\n"
                        + increment);

        return true;
    }

    /**
     * The not-so-fancy heuristic to determine if two subsequent refinements are similar
     *
     * @param increment the precision increment
     * @param refinementRoot the current refinement root
     * @return true, if the current refinement is found to be similar to the previous one, else false
     */
    private boolean isRepeatedRefinement(Multimap<CFANode, MemoryLocation> increment,
            Pair<ARGState, CFAEdge> refinementRoot) {
        int currentRefinementId = refinementRoot.getSecond().getSuccessor().getNodeNumber();
        boolean result = (previousRefinementId == currentRefinementId);
        previousRefinementId = currentRefinementId;

        return result;
    }

    /**
     * This helper method checks if the refinement was successful, i.e.,
     * that either the counterexample is not a repeated counterexample, or that the precision did grow.
     *
     * Repeated counterexamples might occur when combining the analysis with thresholding,
     * or when ignoring variable classes, i.e. when combined with BDD analysis (i.e. cpa.value.precision.ignoreBoolean).
     *
     * @param errorPath the current error path
     * @param valueAnalysisPrecision the previous precision
     * @param refinedValueAnalysisPrecision the refined precision
     */
    private boolean valueAnalysisRefinementWasSuccessful(MutableARGPath errorPath,
            VariableTrackingPrecision valueAnalysisPrecision,
            VariableTrackingPrecision refinedValueAnalysisPrecision) {
        // new error path or precision refined -> success
        boolean success = (errorPath.toString().hashCode() != previousErrorPathID)
                || (refinedValueAnalysisPrecision.getSize() > valueAnalysisPrecision.getSize());

        previousErrorPathID = errorPath.toString().hashCode();

        return success;
    }

    @Override
    public void collectStatistics(Collection<Statistics> pStatsCollection) {
        pStatsCollection.add(this);
        pStatsCollection.add(interpolatingRefiner);
    }

    @Override
    public String getName() {
        return "ApronAnalysisDelegatingRefiner";
    }

    @Override
    public void printStatistics(PrintStream out, Result result, ReachedSet reached) {
        out.println("  number of value analysis refinements:                " + numberOfValueAnalysisRefinements);
        out.println("  number of successful valueAnalysis refinements:      "
                + numberOfSuccessfulValueAnalysisRefinements);
    }

    /**
     * This method checks if the given path is feasible, when doing a full-precision check.
     *
     * @param path the path to check
     * @return true, if the path is feasible, else false
     * @throws CPAException if the path check gets interrupted
     */
    boolean isPathFeasable(ARGPath path) throws CPAException {
        try {
            // create a new ValueAnalysisPathChecker, which does check the given path at full precision
            ValueAnalysisFeasibilityChecker checker = new ValueAnalysisFeasibilityChecker(logger, cfa,
                    apronCPA.getConfiguration());

            return checker.isFeasible(path);
        } catch (InterruptedException | InvalidConfigurationException e) {
            throw new CPAException("counterexample-check failed: ", e);
        }
    }

    /**
     * Creates a new OctagonAnalysisPathChecker, which checks the given path at full precision.
     * @throws ApronException
     */
    private ApronAnalysisFeasabilityChecker createApronFeasibilityChecker(MutableARGPath path)
            throws CPAException, ApronException {
        try {
            ApronAnalysisFeasabilityChecker checker;

            // no specific timelimit set for octagon feasibility check
            if (timeForApronFeasibilityCheck.isEmpty()) {
                checker = new ApronAnalysisFeasabilityChecker(cfa, logger, shutdownNotifier, path, apronCPA);

            } else {
                ShutdownNotifier notifier = ShutdownNotifier.createWithParent(shutdownNotifier);
                WalltimeLimit l = WalltimeLimit.fromNowOn(timeForApronFeasibilityCheck);
                ResourceLimitChecker limits = new ResourceLimitChecker(notifier,
                        Lists.newArrayList((ResourceLimit) l));

                limits.start();
                checker = new ApronAnalysisFeasabilityChecker(cfa, logger, notifier, path, apronCPA);
                limits.cancel();
            }

            return checker;
        } catch (InterruptedException | InvalidConfigurationException e) {
            throw new CPAException("counterexample-check failed: ", e);
        }
    }

}