org.sosy_lab.cpachecker.cpa.bam.BAMCPAStatistics.java Source code

Java tutorial

Introduction

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

import static com.google.common.base.Preconditions.checkState;
import static org.sosy_lab.cpachecker.util.statistics.StatisticsUtils.toPercent;

import java.io.IOException;
import java.io.PrintStream;
import java.io.Writer;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;

import org.sosy_lab.common.Pair;
import org.sosy_lab.common.configuration.Configuration;
import org.sosy_lab.common.configuration.FileOption;
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.io.Files;
import org.sosy_lab.common.io.Path;
import org.sosy_lab.common.io.PathTemplate;
import org.sosy_lab.common.io.Paths;
import org.sosy_lab.common.log.LogManager;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.model.FunctionSummaryEdge;
import org.sosy_lab.cpachecker.core.CPAcheckerResult.Result;
import org.sosy_lab.cpachecker.core.interfaces.AbstractState;
import org.sosy_lab.cpachecker.core.interfaces.Statistics;
import org.sosy_lab.cpachecker.core.reachedset.ReachedSet;
import org.sosy_lab.cpachecker.core.reachedset.UnmodifiableReachedSet;
import org.sosy_lab.cpachecker.cpa.arg.ARGState;
import org.sosy_lab.cpachecker.cpa.arg.ARGToDotWriter;
import org.sosy_lab.cpachecker.cpa.arg.ARGUtils;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

/**
 * Prints some BAM related statistics
 */
@Options(prefix = "cpa.bam")
class BAMCPAStatistics implements Statistics {

    @Option(secure = true, description = "export blocked ARG as .dot file")
    @FileOption(FileOption.Type.OUTPUT_FILE)
    private Path argFile = Paths.get("BlockedARG.dot");

    @Option(secure = true, description = "export single blocked ARG as .dot files, should contain '%d'")
    @FileOption(FileOption.Type.OUTPUT_FILE)
    private PathTemplate indexedArgFile = PathTemplate.ofFormatString("ARGs/ARG_%d.dot");

    @Option(secure = true, description = "export used parts of blocked ARG as .dot file")
    @FileOption(FileOption.Type.OUTPUT_FILE)
    private Path simplifiedArgFile = Paths.get("BlockedARGSimplified.dot");

    private final Predicate<Pair<ARGState, ARGState>> highlightSummaryEdge = new Predicate<Pair<ARGState, ARGState>>() {
        @Override
        public boolean apply(Pair<ARGState, ARGState> input) {
            final CFAEdge edge = input.getFirst().getEdgeToChild(input.getSecond());
            return edge instanceof FunctionSummaryEdge;
        }
    };

    private final BAMCPA cpa;
    private final BAMCache cache;
    private AbstractBAMBasedRefiner refiner = null;
    private final LogManager logger;

    public BAMCPAStatistics(BAMCPA cpa, BAMCache cache, Configuration config, LogManager logger)
            throws InvalidConfigurationException {
        config.inject(this);

        this.cpa = cpa;
        this.cache = cache;
        this.logger = logger;
    }

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

    public void addRefiner(AbstractBAMBasedRefiner pRefiner) {
        checkState(refiner == null);
        refiner = pRefiner;
    }

    @Override
    public void printStatistics(PrintStream out, Result result, ReachedSet reached) {

        BAMTransferRelation transferRelation = cpa.getTransferRelation();
        TimedReducer reducer = cpa.getReducer();

        int sumCalls = cache.cacheMisses + cache.partialCacheHits + cache.fullCacheHits;

        int sumARTElemets = 0;
        for (ReachedSet subreached : BAMARGUtils.gatherReachedSets(cpa, reached).values()) {
            sumARTElemets += subreached.size();
        }

        out.println("Total size of all ARGs:                                         " + sumARTElemets);
        out.println("Maximum block depth:                                            "
                + transferRelation.maxRecursiveDepth);
        out.println("Total number of recursive CPA calls:                            " + sumCalls);
        out.println("  Number of cache misses:                                       " + cache.cacheMisses + " ("
                + toPercent(cache.cacheMisses, sumCalls) + " of all calls)");
        out.println("  Number of partial cache hits:                                 " + cache.partialCacheHits
                + " (" + toPercent(cache.partialCacheHits, sumCalls) + " of all calls)");
        out.println("  Number of full cache hits:                                    " + cache.fullCacheHits + " ("
                + toPercent(cache.fullCacheHits, sumCalls) + " of all calls)");
        if (cache.gatherCacheMissStatistics) {
            out.println("Cause for cache misses:                                         ");
            out.println("  Number of abstraction caused misses:                          "
                    + cache.abstractionCausedMisses + " ("
                    + toPercent(cache.abstractionCausedMisses, cache.cacheMisses) + " of all misses)");
            out.println(
                    "  Number of precision caused misses:                            " + cache.precisionCausedMisses
                            + " (" + toPercent(cache.precisionCausedMisses, cache.cacheMisses) + " of all misses)");
            out.println(
                    "  Number of misses with no similar elements:                    " + cache.noSimilarCausedMisses
                            + " (" + toPercent(cache.noSimilarCausedMisses, cache.cacheMisses) + " of all misses)");
        }
        out.println("Time for reducing abstract states:                            " + reducer.reduceTime
                + " (Calls: " + reducer.reduceTime.getNumberOfIntervals() + ")");
        out.println("Time for expanding abstract states:                           " + reducer.expandTime
                + " (Calls: " + reducer.expandTime.getNumberOfIntervals() + ")");
        out.println("Time for checking equality of abstract states:                " + cache.equalsTimer
                + " (Calls: " + cache.equalsTimer.getNumberOfIntervals() + ")");
        out.println("Time for computing the hashCode of abstract states:           " + cache.hashingTimer
                + " (Calls: " + cache.hashingTimer.getNumberOfIntervals() + ")");
        out.println("Time for searching for similar cache entries:                   " + cache.searchingTimer
                + " (Calls: " + cache.searchingTimer.getNumberOfIntervals() + ")");
        out.println("Time for reducing precisions:                                   " + reducer.reducePrecisionTime
                + " (Calls: " + reducer.reducePrecisionTime.getNumberOfIntervals() + ")");
        out.println("Time for expanding precisions:                                  " + reducer.expandPrecisionTime
                + " (Calls: " + reducer.expandPrecisionTime.getNumberOfIntervals() + ")");

        out.println("Time for removing cached subtrees for refinement:               "
                + transferRelation.removeCachedSubtreeTimer);
        out.println("Time for recomputing ARGs during counterexample analysis:       "
                + transferRelation.recomputeARTTimer);
        if (refiner != null) {
            out.println(
                    "Compute path for refinement:                                    " + refiner.computePathTimer);
            out.println("  Constructing flat ARG:                                        "
                    + refiner.computeSubtreeTimer);
            out.println("  Searching path to error location:                             "
                    + refiner.computeCounterexampleTimer);
        }

        //Add to reached set all states from BAM cache
        Collection<ReachedSet> cachedStates = cache.getAllCachedReachedStates();
        for (ReachedSet set : cachedStates) {
            for (AbstractState state : set.asCollection()) {
                /* Method 'add' add state not only in list of reached states, but also in waitlist,
                 * so we should delete it.
                 */
                reached.add(state, set.getPrecision(state));
                reached.removeOnlyFromWaitlist(state);
            }
        }

        exportAllReachedSets(argFile, indexedArgFile, reached);
        exportUsedReachedSets(simplifiedArgFile, reached);
    }

    private void exportAllReachedSets(final Path superArgFile, final PathTemplate indexedFile,
            final UnmodifiableReachedSet mainReachedSet) {

        if (superArgFile != null) {

            final Set<UnmodifiableReachedSet> allReachedSets = new HashSet<>();
            allReachedSets.addAll(cache.getAllCachedReachedStates());
            allReachedSets.add(mainReachedSet);

            final Set<ARGState> rootStates = new HashSet<>();
            final Multimap<ARGState, ARGState> connections = HashMultimap.create();

            for (final UnmodifiableReachedSet reachedSet : allReachedSets) {
                ARGState rootState = (ARGState) reachedSet.getFirstState();
                rootStates.add(rootState);
                Multimap<ARGState, ARGState> localConnections = HashMultimap.create();
                getConnections(rootState, localConnections);
                connections.putAll(localConnections);

                // dump small graph
                writeArg(indexedFile.getPath(((ARGState) reachedSet.getFirstState()).getStateId()),
                        localConnections, Collections.singleton((ARGState) reachedSet.getFirstState()));
            }

            // dump super-graph
            writeArg(superArgFile, connections, rootStates);
        }
    }

    /** dump only those ReachedSets, that are reachable from mainReachedSet. */
    private void exportUsedReachedSets(final Path superArgFile, final UnmodifiableReachedSet mainReachedSet) {

        if (superArgFile != null) {

            final Multimap<ARGState, ARGState> connections = HashMultimap.create();
            final Set<ARGState> rootStates = getUsedRootStates(mainReachedSet, connections);
            writeArg(superArgFile, connections, rootStates);
        }
    }

    private void writeArg(final Path file, final Multimap<ARGState, ARGState> connections,
            final Set<ARGState> rootStates) {
        try (Writer w = Files.openOutputFile(file)) {
            ARGToDotWriter.write(w, rootStates, connections, ARGUtils.CHILDREN_OF_STATE, Predicates.alwaysTrue(),
                    highlightSummaryEdge);
        } catch (IOException e) {
            logger.logUserException(Level.WARNING, e, String.format("Could not write ARG to file: %s", file));
        }
    }

    private Set<ARGState> getUsedRootStates(final UnmodifiableReachedSet mainReachedSet,
            final Multimap<ARGState, ARGState> connections) {
        final Set<UnmodifiableReachedSet> finished = new HashSet<>();
        final Deque<UnmodifiableReachedSet> waitlist = new ArrayDeque<>();
        waitlist.add(mainReachedSet);
        while (!waitlist.isEmpty()) {
            final UnmodifiableReachedSet reachedSet = waitlist.pop();
            if (!finished.add(reachedSet)) {
                continue;
            }
            final ARGState rootState = (ARGState) reachedSet.getFirstState();
            final Set<ReachedSet> referencedReachedSets = getConnections(rootState, connections);
            waitlist.addAll(referencedReachedSets);
        }

        final Set<ARGState> rootStates = new HashSet<>();
        for (UnmodifiableReachedSet reachedSet : finished) {
            rootStates.add((ARGState) reachedSet.getFirstState());
        }
        return rootStates;
    }

    /**
     * This method iterates over all reachable states from rootState
     * and searches for connections to other reachedSets (a set of all those other reachedSets is returned).
     * As side-effect we collect a Multimap of all connections:
     * - from a state (in current reachedSet) to its reduced state (in other rechedSet) and
     * - from a foreign state (in other reachedSet) to its expanded state(s) (in current reachedSet).
     */
    private Set<ReachedSet> getConnections(final ARGState rootState,
            final Multimap<ARGState, ARGState> connections) {
        final Set<ReachedSet> referencedReachedSets = new HashSet<>();
        final Set<ARGState> finished = new HashSet<>();
        final Deque<ARGState> waitlist = new ArrayDeque<>();
        waitlist.add(rootState);
        while (!waitlist.isEmpty()) {
            ARGState state = waitlist.pop();
            if (!finished.add(state)) {
                continue;
            }
            if (cpa.getTransferRelation().abstractStateToReachedSet.containsKey(state)) {
                ReachedSet target = cpa.getTransferRelation().abstractStateToReachedSet.get(state);
                referencedReachedSets.add(target);
                ARGState targetState = (ARGState) target.getFirstState();
                connections.put(state, targetState);
            }
            if (cpa.getTransferRelation().expandedToReducedCache.containsKey(state)) {
                AbstractState sourceState = cpa.getTransferRelation().expandedToReducedCache.get(state);
                connections.put((ARGState) sourceState, state);
            }
            waitlist.addAll(state.getChildren());
        }
        return referencedReachedSets;
    }
}