edu.buaa.satla.analysis.core.predicate.PredicateStaticRefiner.java Source code

Java tutorial

Introduction

Here is the source code for edu.buaa.satla.analysis.core.predicate.PredicateStaticRefiner.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 edu.buaa.satla.analysis.core.predicate;

import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
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.log.LogManager;
import org.sosy_lab.cpachecker.cfa.CFA;
import org.sosy_lab.cpachecker.cfa.ast.c.CAssignment;
import org.sosy_lab.cpachecker.cfa.ast.c.CExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CIdExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CParameterDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.c.CSimpleDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.c.CVariableDeclaration;
import org.sosy_lab.cpachecker.cfa.model.AStatementEdge;
import org.sosy_lab.cpachecker.cfa.model.AssumeEdge;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.model.CFANode;
import org.sosy_lab.cpachecker.cfa.model.MultiEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CStatementEdge;
import org.sosy_lab.cpachecker.core.interfaces.AbstractState;
import org.sosy_lab.cpachecker.core.reachedset.UnmodifiableReachedSet;
import org.sosy_lab.cpachecker.cpa.arg.ARGState;
import org.sosy_lab.cpachecker.cpa.arg.ARGUtils;

import com.google.common.base.Optional;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Queues;

import edu.buaa.satla.analysis.exceptions.CPATransferException;
import edu.buaa.satla.analysis.exceptions.SolverException;
import edu.buaa.satla.analysis.util.AbstractStates;
import edu.buaa.satla.analysis.util.CFAUtils;
import edu.buaa.satla.analysis.util.LoopStructure;
import edu.buaa.satla.analysis.util.StaticRefiner;
import edu.buaa.satla.analysis.util.VariableClassification;
import edu.buaa.satla.analysis.util.predicates.AbstractionPredicate;
import edu.buaa.satla.analysis.util.predicates.Solver;
import edu.buaa.satla.analysis.util.predicates.interfaces.BooleanFormula;
import edu.buaa.satla.analysis.util.predicates.interfaces.BooleanFormulaManager;
import edu.buaa.satla.analysis.util.predicates.interfaces.PathFormulaManager;
import edu.buaa.satla.analysis.util.predicates.interfaces.view.FormulaManagerView;

@Options(prefix = "staticRefiner")
public class PredicateStaticRefiner extends StaticRefiner {

    @Option(secure = true, description = "Apply mined predicates on the corresponding scope. false = add them to the global precision.")
    private boolean applyScoped = true;

    @Option(secure = true, description = "Add all assumtions along a error trace to the precision.")
    private boolean addAllErrorTraceAssumes = false;

    @Option(secure = true, description = "Add all assumtions from the control flow automaton to the precision.")
    private boolean addAllControlFlowAssumes = false;

    @Option(secure = true, description = "Add all assumtions along a error trace to the precision.")
    private boolean addAssumesByBoundedBackscan = true;

    @Option(secure = true, description = "Dump CFA assume edges as SMTLIB2 formulas to a file.")
    @FileOption(FileOption.Type.OUTPUT_FILE)
    private Path assumePredicatesFile = null;

    private final PathFormulaManager pathFormulaManager;
    private final FormulaManagerView formulaManagerView;
    private final BooleanFormulaManager booleanManager;
    private final PredicateAbstractionManager predAbsManager;
    private final Optional<LoopStructure> loopStructure;
    private final Solver solver;
    private final CFA cfa;

    private Multimap<String, AStatementEdge> directlyAffectingStatements;

    public PredicateStaticRefiner(Configuration pConfig, LogManager pLogger, Solver pSolver,
            PathFormulaManager pPathFormulaManager, FormulaManagerView pFormulaManagerView,
            PredicateAbstractionManager pPredAbsManager, CFA pCfa) throws InvalidConfigurationException {
        super(pConfig, pLogger);

        pConfig.inject(this);

        this.cfa = pCfa;
        this.loopStructure = cfa.getLoopStructure();

        this.pathFormulaManager = pPathFormulaManager;
        this.predAbsManager = pPredAbsManager;
        this.solver = pSolver;
        this.formulaManagerView = pFormulaManagerView;
        this.booleanManager = formulaManagerView.getBooleanFormulaManager();

        if (assumePredicatesFile != null) {
            dumpAssumePredicate(assumePredicatesFile);
        }
    }

    private boolean isAssumeOnLoopVariable(AssumeEdge e) {
        if (!loopStructure.isPresent()) {
            return false;
        }
        Collection<String> referenced = VariableClassification
                .getVariablesOfExpression((CExpression) e.getExpression());

        for (String var : referenced) {
            if (loopStructure.get().getLoopExitConditionVariables().contains(var)) {
                return true;
            }
        }

        return false;
    }

    private void buildDirectlyAffectingStatements() {
        if (directlyAffectingStatements != null) {
            return;
        }

        directlyAffectingStatements = LinkedHashMultimap.create();

        for (CFANode u : cfa.getAllNodes()) {
            Deque<CFAEdge> edgesToHandle = Queues.newArrayDeque(CFAUtils.leavingEdges(u));
            while (!edgesToHandle.isEmpty()) {
                CFAEdge e = edgesToHandle.pop();
                if (e instanceof MultiEdge) {
                    edgesToHandle.addAll(((MultiEdge) e).getEdges());
                } else if (e instanceof CStatementEdge) {
                    CStatementEdge stmtEdge = (CStatementEdge) e;
                    if (stmtEdge.getStatement() instanceof CAssignment) {
                        CAssignment assign = (CAssignment) stmtEdge.getStatement();

                        if (assign.getLeftHandSide() instanceof CIdExpression) {
                            String variable = ((CIdExpression) assign.getLeftHandSide()).getDeclaration()
                                    .getQualifiedName();
                            directlyAffectingStatements.put(variable, stmtEdge);
                        }
                    }
                }
            }
        }
    }

    private boolean isContradicting(AssumeEdge assume, AStatementEdge stmt)
            throws SolverException, CPATransferException, InterruptedException {
        // Check stmt ==> assume?

        BooleanFormula stmtFormula = pathFormulaManager.makeAnd(pathFormulaManager.makeEmptyPathFormula(), stmt)
                .getFormula();

        BooleanFormula assumeFormula = pathFormulaManager.makeAnd(pathFormulaManager.makeEmptyPathFormula(), assume)
                .getFormula();

        BooleanFormula query = formulaManagerView.uninstantiate(booleanManager.and(stmtFormula, assumeFormula));
        boolean contra = solver.isUnsat(query);

        if (contra) {
            logger.log(Level.INFO, "Contradiction found ", query);
        }

        return contra;

        /* [a == 1]
         *  a = <literal>, where <literal> != 1
         *  a = a +-* <literal>
         *
         *  Variable classification can be used!!
         *
         *  if a IN the set Eq
         *
         */
    }

    private boolean hasContradictingOperationInFlow(AssumeEdge e)
            throws SolverException, CPATransferException, InterruptedException {
        buildDirectlyAffectingStatements();

        Collection<String> referenced = VariableClassification
                .getVariablesOfExpression((CExpression) e.getExpression());
        for (String varName : referenced) {
            Collection<AStatementEdge> affectedByStmts = directlyAffectingStatements.get(varName);
            for (AStatementEdge stmtEdge : affectedByStmts) {
                if (isContradicting(e, stmtEdge)) {
                    return true;
                }
            }
        }
        return false;
    }

    private Set<AssumeEdge> getAllNonLoopControlFlowAssumes()
            throws SolverException, CPATransferException, InterruptedException {
        Set<AssumeEdge> result = new HashSet<>();

        for (CFANode u : cfa.getAllNodes()) {
            for (CFAEdge e : CFAUtils.leavingEdges(u)) {
                if (e instanceof AssumeEdge) {
                    AssumeEdge assume = (AssumeEdge) e;
                    if (!isAssumeOnLoopVariable(assume)) {
                        if (hasContradictingOperationInFlow(assume)) {
                            result.add(assume);
                        }
                    }
                }
            }
        }

        return result;
    }

    private Set<AssumeEdge> getAssumeEdgesAlongPath(UnmodifiableReachedSet reached, ARGState targetState)
            throws SolverException, CPATransferException, InterruptedException {
        Set<AssumeEdge> result = new HashSet<>();

        Set<ARGState> allStatesOnPath = ARGUtils.getAllStatesOnPathsTo(targetState);
        for (ARGState s : allStatesOnPath) {
            CFANode u = AbstractStates.extractLocation(s);
            for (CFAEdge e : CFAUtils.leavingEdges(u)) {
                CFANode v = e.getSuccessor();
                Collection<AbstractState> reachedOnV = reached.getReached(v);

                boolean edgeOnTrace = false;
                for (AbstractState ve : reachedOnV) {
                    if (allStatesOnPath.contains(ve)) {
                        edgeOnTrace = true;
                        break;
                    }
                }

                if (edgeOnTrace) {
                    if (e instanceof AssumeEdge) {
                        AssumeEdge assume = (AssumeEdge) e;
                        if (!isAssumeOnLoopVariable(assume)) {
                            if (hasContradictingOperationInFlow(assume)) {
                                result.add(assume);
                            }
                        }
                    }
                }
            }
        }

        return result;
    }

    /**
     * This method extracts a precision based only on static information derived from the CFA.
     *
     * @return a precision for the predicate CPA
     * @throws CPATransferException
     * @throws InterruptedException
     */
    public PredicatePrecision extractPrecisionFromCfa(UnmodifiableReachedSet pReached,
            List<ARGState> abstractionStatesTrace, boolean atomicPredicates)
            throws SolverException, CPATransferException, InterruptedException {
        logger.log(Level.FINER, "Extracting precision from CFA...");

        // Predicates that should be tracked on function scope
        Multimap<String, AbstractionPredicate> functionPredicates = ArrayListMultimap.create();

        // Predicates that should be tracked globally
        Collection<AbstractionPredicate> globalPredicates = Lists.newArrayList();

        // Determine the ERROR location of the path (last node)
        ARGState targetState = abstractionStatesTrace.get(abstractionStatesTrace.size() - 1);
        CFANode targetLocation = AbstractStates.extractLocation(targetState);

        // Determine the assume edges that should be considered for predicate extraction
        Set<AssumeEdge> assumeEdges = new HashSet<>();

        if (addAllControlFlowAssumes) {
            assumeEdges.addAll(getAllNonLoopControlFlowAssumes());
        } else {
            if (addAllErrorTraceAssumes) {
                assumeEdges.addAll(getAssumeEdgesAlongPath(pReached, targetState));
            }
            if (addAssumesByBoundedBackscan) {
                assumeEdges.addAll(getTargetLocationAssumes(Lists.newArrayList(targetLocation)).values());
            }
        }

        // Create predicates for the assume edges and add them to the precision
        for (AssumeEdge assume : assumeEdges) {
            // Create a boolean formula from the assume
            Collection<AbstractionPredicate> preds = assumeEdgeToPredicates(atomicPredicates, assume);

            // Check whether the predicate should be used global or only local
            boolean applyGlobal = true;
            if (applyScoped) {
                for (CIdExpression idExpr : getVariablesOfAssume(assume)) {
                    CSimpleDeclaration decl = idExpr.getDeclaration();
                    if (decl instanceof CVariableDeclaration) {
                        if (!((CVariableDeclaration) decl).isGlobal()) {
                            applyGlobal = false;
                        }
                    } else if (decl instanceof CParameterDeclaration) {
                        applyGlobal = false;
                    }
                }
            }

            // Add the predicate to the resulting precision
            if (applyGlobal) {
                logger.log(Level.FINEST, "Global predicates mined", preds);
                globalPredicates.addAll(preds);
            } else {
                logger.log(Level.FINEST, "Function predicates mined", preds);
                String function = assume.getPredecessor().getFunctionName();
                functionPredicates.putAll(function, preds);
            }
        }

        logger.log(Level.FINER, "Extracting finished.");

        return new PredicatePrecision(ImmutableSetMultimap.<Pair<CFANode, Integer>, AbstractionPredicate>of(),
                ArrayListMultimap.<CFANode, AbstractionPredicate>create(), functionPredicates, globalPredicates);
    }

    private Collection<AbstractionPredicate> assumeEdgeToPredicates(boolean atomicPredicates, AssumeEdge assume)
            throws CPATransferException, InterruptedException {
        BooleanFormula relevantAssumesFormula = pathFormulaManager
                .makeAnd(pathFormulaManager.makeEmptyPathFormula(), assume).getFormula();

        Collection<AbstractionPredicate> preds;
        if (atomicPredicates) {
            preds = predAbsManager.extractPredicates(relevantAssumesFormula);
        } else {
            preds = ImmutableList.of(
                    predAbsManager.createPredicateFor(formulaManagerView.uninstantiate(relevantAssumesFormula)));
        }

        return preds;
    }

    protected void dumpAssumePredicate(Path target) {
        try (Writer w = Files.openOutputFile(target)) {
            for (CFANode u : cfa.getAllNodes()) {
                for (CFAEdge e : CFAUtils.leavingEdges(u)) {
                    if (e instanceof AssumeEdge) {
                        Collection<AbstractionPredicate> preds = assumeEdgeToPredicates(false, (AssumeEdge) e);
                        for (AbstractionPredicate p : preds) {
                            w.append(p.getSymbolicAtom().toString());
                            w.append("\n");
                        }
                    }
                }
            }
        } catch (InterruptedException e) {
            logger.logUserException(Level.WARNING, e, "Interrupted, could not write assume predicates to file!");
            Thread.currentThread().interrupt();
        } catch (IOException e) {
            logger.logUserException(Level.WARNING, e, "IO exception! Could not write assume predicates to file!");
        } catch (CPATransferException e) {
            logger.logUserException(Level.WARNING, e,
                    "Transfer exception! Could not write assume predicates to file!");
        }
    }

}