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

Java tutorial

Introduction

Here is the source code for org.sosy_lab.cpachecker.cpa.apron.refiner.ApronARGBasedDelegatingRefiner.java

Source

/*
 *  CPAchecker is a tool for configurable software verification.
 *  This file is part of CPAchecker.
 *
 *  Copyright (C) 2007-2016  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 apron.ApronException;

import com.google.common.collect.Multimap;

import org.sosy_lab.common.ShutdownManager;
import org.sosy_lab.common.ShutdownNotifier;
import org.sosy_lab.common.configuration.Configuration;
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.counterexample.CounterexampleInfo;
import org.sosy_lab.cpachecker.core.defaults.precision.VariableTrackingPrecision;
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.TransferRelation;
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.apron.ApronState;
import org.sosy_lab.cpachecker.cpa.arg.ARGBasedRefiner;
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.octagon.refiner.OctagonAnalysisFeasabilityChecker;
import org.sosy_lab.cpachecker.cpa.predicate.PredicateCPARefiner;
import org.sosy_lab.cpachecker.cpa.value.ValueAnalysisState;
import org.sosy_lab.cpachecker.cpa.value.refiner.ValueAnalysisPathInterpolator;
import org.sosy_lab.cpachecker.exceptions.CPAException;
import org.sosy_lab.cpachecker.util.ApronManager;
import org.sosy_lab.cpachecker.util.Pair;
import org.sosy_lab.cpachecker.util.Precisions;
import org.sosy_lab.cpachecker.util.refinement.FeasibilityChecker;
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 org.sosy_lab.cpachecker.util.states.MemoryLocation;

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

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

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

    private final FeasibilityChecker<ValueAnalysisState> valueAnalysisChecker;

    /**
     * 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 LogManager logger;
    private final ShutdownNotifier shutdownNotifier;
    private final Configuration config;

    private final ApronManager apronManager;
    private final TransferRelation apronTransfer;

    ApronARGBasedDelegatingRefiner(final Configuration pConfig, final LogManager pLogger,
            final ShutdownNotifier pShutdownNotifier, final CFA pCfa, final ApronManager pApronManager,
            final TransferRelation pApronTransfer,
            final FeasibilityChecker<ValueAnalysisState> pValueAnalysisFeasibilityChecker,
            final ValueAnalysisPathInterpolator pValueAnalysisPathInterpolator)
            throws InvalidConfigurationException {

        pConfig.inject(this);
        config = pConfig;
        logger = pLogger;
        shutdownNotifier = pShutdownNotifier;
        cfa = pCfa;

        apronManager = pApronManager;
        apronTransfer = pApronTransfer;
        valueAnalysisChecker = pValueAnalysisFeasibilityChecker;
        interpolatingRefiner = pValueAnalysisPathInterpolator;
    }

    @Override
    public CounterexampleInfo performRefinementForPath(final ARGReachedSet reached, final ARGPath pErrorPath)
            throws CPAException, InterruptedException {

        // if path is infeasible, try to refine the precision
        if (!isPathFeasable(pErrorPath) && !existsExplicitApronRefinement) {
            if (performValueAnalysisRefinement(reached, pErrorPath)) {
                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
        OctagonAnalysisFeasabilityChecker apronChecker;
        try {
            apronChecker = createApronFeasibilityChecker(pErrorPath);
        } 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();
            }
        }

        // we use the imprecise version of the CounterexampleInfo, due to the possible
        // merges which are done in the ApronCPA
        return CounterexampleInfo.feasibleImprecise(pErrorPath);
    }

    /**
     * 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
     */
    private boolean performValueAnalysisRefinement(final ARGReachedSet reached, final ARGPath 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 OctagonAnalysisFeasabilityChecker checker) throws InterruptedException {
        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();
        // 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"
                        + new TreeSet<>(increment.values()));

        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(ARGPath 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, InterruptedException {
        return valueAnalysisChecker.isFeasible(path);
    }

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

            // no specific timelimit set for octagon feasibility check
            if (timeForApronFeasibilityCheck.isEmpty()) {
                checker = new OctagonAnalysisFeasabilityChecker(config, shutdownNotifier, path, ApronCPA.class,
                        cfa.getVarClassification(), apronTransfer, new ApronState(logger, apronManager));

            } else {
                ShutdownManager shutdown = ShutdownManager.createWithParent(shutdownNotifier);
                WalltimeLimit l = WalltimeLimit.fromNowOn(timeForApronFeasibilityCheck);
                ResourceLimitChecker limits = new ResourceLimitChecker(shutdown,
                        Collections.<ResourceLimit>singletonList(l));

                limits.start();
                checker = new OctagonAnalysisFeasabilityChecker(config, shutdown.getNotifier(), path,
                        ApronCPA.class, cfa.getVarClassification(), apronTransfer,
                        new ApronState(logger, apronManager));
                limits.cancel();
            }

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

}