org.sosy_lab.cpachecker.util.StateToFormulaWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.sosy_lab.cpachecker.util.StateToFormulaWriter.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.util;

import static org.sosy_lab.cpachecker.util.AbstractStates.*;

import java.io.IOException;
import java.io.PrintStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;

import org.sosy_lab.common.ShutdownNotifier;
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.log.LogManager;
import org.sosy_lab.cpachecker.cfa.CFA;
import org.sosy_lab.cpachecker.cfa.model.CFANode;
import org.sosy_lab.cpachecker.cfa.model.FunctionEntryNode;
import org.sosy_lab.cpachecker.cfa.model.FunctionExitNode;
import org.sosy_lab.cpachecker.core.AnalysisDirection;
import org.sosy_lab.cpachecker.core.CPAcheckerResult.Result;
import org.sosy_lab.cpachecker.core.interfaces.AbstractState;
import org.sosy_lab.cpachecker.core.interfaces.FormulaReportingState;
import org.sosy_lab.cpachecker.core.interfaces.Statistics;
import org.sosy_lab.cpachecker.core.interfaces.StatisticsProvider;
import org.sosy_lab.cpachecker.core.reachedset.ReachedSet;
import org.sosy_lab.cpachecker.core.reachedset.UnmodifiableReachedSet;
import org.sosy_lab.cpachecker.util.predicates.Solver;
import org.sosy_lab.solver.api.BooleanFormula;
import org.sosy_lab.cpachecker.util.predicates.interfaces.PathFormulaManager;
import org.sosy_lab.cpachecker.util.predicates.interfaces.view.BooleanFormulaManagerView;
import org.sosy_lab.cpachecker.util.predicates.interfaces.view.FormulaManagerView;
import org.sosy_lab.cpachecker.util.predicates.pathformula.PathFormulaManagerImpl;

import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;

/**
 * This class allows to export the information of abstract states as SMT-formula.
 * Therefore we filter the abstract states for matching {@link FormulaReportingState}s
 * and retrieve the formula from there.
 * Then we export the formulas in a fixed line-based format,
 * which allows re-usage with a further predicate analysis.
 *
 * The solver used for the formula-creation is configured as usual
 * (including options like 'encodeBitVectorAs={Int,BV}').
 * Each abstract state is responsible for correct C-types in its own formula
 * and thus there can be wrong/imprecise bitvector-operations in the formula,
 * for example, every variable can be handled as 'signed integer'.
 */
@Options(prefix = "statesToFormulas")
public class StateToFormulaWriter implements StatisticsProvider {

    @Option(secure = true, description = "export abstract states as formula, e.g. for re-using them as PredicatePrecision.")
    @FileOption(FileOption.Type.OUTPUT_FILE)
    private Path exportFile = null; // default is null to disable unwanted output of big files

    @Option(secure = true, description = "instead of writing the exact state-representation as a single formula, "
            + "write its atoms as a list of formulas. Therefore we ignore operators for conjunction and disjunction.")
    private FormulaSplitter splitFormulas = FormulaSplitter.LOCATION;

    @Option(secure = true, description = "export formulas for all program locations or just the important locations,"
            + "which include loop-heads, funtion-calls and function-exits.")
    private boolean exportOnlyImporantLocations = false;

    private static final Splitter LINE_SPLITTER = Splitter.on('\n').omitEmptyStrings();
    private static final Joiner LINE_JOINER = Joiner.on('\n');

    private final Solver solver;
    private final FormulaManagerView fmgr;
    private final PathFormulaManager pfmgr;
    private final LogManager logger;
    private final CFA cfa;

    public enum FormulaSplitter {
        LOCATION, // one formula per location: "(a=2&b=3)|(a=2&b=4)"
        STATE, // one formula per state: "a=2&b=3", "a=2&b=4"
        ATOM // really split into atoms: "a=2", "b=3", "b=4"
    }

    public StateToFormulaWriter(Configuration config, LogManager pLogger, ShutdownNotifier shutdownNotifier,
            CFA pCfa) throws InvalidConfigurationException {
        config.inject(this);
        solver = Solver.create(config, pLogger, shutdownNotifier);
        logger = pLogger;
        fmgr = solver.getFormulaManager();
        pfmgr = new PathFormulaManagerImpl(fmgr, config, logger, shutdownNotifier, pCfa, AnalysisDirection.FORWARD);
        cfa = pCfa;
    }

    @Override
    public void collectStatistics(Collection<Statistics> pStatsCollection) {
        pStatsCollection.add(new Statistics() {

            @Override
            public void printStatistics(PrintStream pOut, Result pResult, ReachedSet pReached) {
                if (exportFile != null) {
                    try (Writer w = Files.openOutputFile(exportFile)) {
                        write(pReached, w);
                    } catch (IOException e) {
                        logger.logUserException(Level.WARNING, e, "Could not write formulas to file");
                    }
                }
            }

            @Override
            public String getName() {
                return null;
            }
        });
    }

    /** write all formulas for the whole reached-set. */
    public void write(UnmodifiableReachedSet pReachedSet, Appendable pAppendable) throws IOException {
        SetMultimap<CFANode, FormulaReportingState> locationPredicates = HashMultimap.create();
        for (AbstractState state : pReachedSet) {
            CFANode location = extractLocation(state);
            if (location != null && isImportantNode(location)) {
                FluentIterable<FormulaReportingState> formulaState = asIterable(state)
                        .filter(FormulaReportingState.class);
                locationPredicates.putAll(location, formulaState);
            }

        }
        write(locationPredicates, pAppendable);
    }

    /**
     * Filter important program locations.
     * In some cases we re-use only states at abstraction locations,
     * which include loop-starts and function calls.
     * We use a simple filter-mechanism to export only them!
     */
    private boolean isImportantNode(CFANode location) {
        if (exportOnlyImporantLocations) {
            return (cfa.getAllLoopHeads().isPresent() && cfa.getAllLoopHeads().get().contains(location))
                    || location instanceof FunctionEntryNode || location instanceof FunctionExitNode
                    || location.getLeavingSummaryEdge() != null || location.getEnteringSummaryEdge() != null;
        } else {
            // all locations are important
            return true;
        }
    }

    /** write all formulas for a single program location. */
    public void write(final CFANode pCfaNode, UnmodifiableReachedSet pReachedSet, Appendable pAppendable)
            throws IOException {
        SetMultimap<CFANode, FormulaReportingState> statesToNode = HashMultimap.create();
        statesToNode.putAll(pCfaNode,
                projectToType(filterLocation(pReachedSet, pCfaNode), FormulaReportingState.class));
        write(statesToNode, pAppendable);
    }

    private void write(SetMultimap<CFANode, FormulaReportingState> pStates, Appendable pAppendable)
            throws IOException {

        // (global) definitions used for predicates
        final Set<String> definitions = Sets.newLinkedHashSet();

        // in this set, we collect the string representing each predicate
        // (potentially making use of the above definitions)
        final Multimap<CFANode, String> cfaNodeToPredicate = HashMultimap.create();

        // fill the above set and map
        for (CFANode cfaNode : pStates.keySet()) {
            List<BooleanFormula> formulas = getFormulasForNode(pStates.get(cfaNode), cfaNode);
            extractPredicatesAndDefinitions(cfaNode, definitions, cfaNodeToPredicate, formulas);
        }

        writeFormulas(pAppendable, definitions, cfaNodeToPredicate);
    }

    /** get formulas representing the abstract states at the cfaNode. */
    private List<BooleanFormula> getFormulasForNode(Set<FormulaReportingState> states, CFANode cfaNode) {
        final List<BooleanFormula> formulas = new ArrayList<>();
        final BooleanFormulaManagerView bfmgr = fmgr.getBooleanFormulaManager();

        List<BooleanFormula> stateFormulas = new ArrayList<>();
        for (FormulaReportingState state : states) {
            stateFormulas.add(state.getFormulaApproximation(fmgr, pfmgr));
        }

        switch (splitFormulas) {
        case LOCATION:
            // create the disjunction of the found states for the current location
            formulas.add(bfmgr.or(stateFormulas));
            break;
        case STATE:
            // do not merge different location-formulas
            formulas.addAll(stateFormulas);
            break;
        case ATOM:
            // atomize formulas
            for (BooleanFormula f : stateFormulas) {
                formulas.addAll(fmgr.extractAtoms(f, false));
            }
            break;
        default:
            throw new AssertionError("unknown option");
        }

        // filter out formulas with no information
        final List<BooleanFormula> filtered = new ArrayList<>();
        for (BooleanFormula f : formulas) {
            if (!bfmgr.isTrue(f) && !bfmgr.isFalse(f)) {
                filtered.add(f);
            }
        }

        return filtered;
    }

    /** dump each formula and split it into the predicate and some utility-stuff (named definition)
     *  that consists of symbol-declarations and solver-specific queries. */
    private void extractPredicatesAndDefinitions(CFANode cfaNode, Set<String> definitions,
            Multimap<CFANode, String> cfaNodeToPredicate, List<BooleanFormula> predicates) throws IOException {

        for (BooleanFormula formula : predicates) {
            String s = fmgr.dumpFormula(formula).toString();
            List<String> lines = Lists.newArrayList(LINE_SPLITTER.split(s));
            assert !lines.isEmpty();

            // Get the predicate from the last line
            String predString = lines.get(lines.size() - 1);

            // Remove the predicate from the dump
            lines.remove(lines.size() - 1);

            // Check that the dump format is correct
            if (!(predString.startsWith("(assert ") && predString.endsWith(")"))) {
                throw new AssertionError("Writing formulas is only supported for solvers "
                        + "that support the Smtlib2 format, please try using Mathsat5.");
            }

            // Add the definition part of the dump to the set of definitions
            definitions.addAll(lines);

            // Record the predicate to write it later at t
            cfaNodeToPredicate.put(cfaNode, predString);
        }
    }

    /** write the definitions and predicates in the commonly used precision-format
     *  (that is defined somewhere else...)*/
    private void writeFormulas(Appendable pAppendable, Set<String> definitions,
            Multimap<CFANode, String> cfaNodeToPredicate) throws IOException {

        // write definitions to file
        LINE_JOINER.appendTo(pAppendable, definitions);
        pAppendable.append("\n\n");

        // write states to file
        for (CFANode cfaNode : cfaNodeToPredicate.keySet()) {
            pAppendable.append(toKey(cfaNode));
            pAppendable.append(":\n");
            LINE_JOINER.appendTo(pAppendable, cfaNodeToPredicate.get(cfaNode));
            pAppendable.append("\n\n");
        }
    }

    private static String toKey(CFANode pCfaNode) {
        return pCfaNode.getFunctionName() + " " + pCfaNode.toString();
    }
}