Java tutorial
/* * 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; import static com.google.common.collect.FluentIterable.from; import static edu.buaa.satla.analysis.core.ShutdownNotifier.interruptCurrentThreadOnShutdown; import static edu.buaa.satla.analysis.util.AbstractStates.IS_TARGET_STATE; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.Set; import java.util.logging.Level; import javax.annotation.Nullable; import org.sosy_lab.common.AbstractMBean; 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.io.Files; import org.sosy_lab.common.io.Path; import org.sosy_lab.common.io.Paths; import org.sosy_lab.common.log.LogManager; import org.sosy_lab.cpachecker.cfa.CFA; import org.sosy_lab.cpachecker.cfa.CFACreator; import org.sosy_lab.cpachecker.cfa.Language; import org.sosy_lab.cpachecker.cfa.model.CFANode; import org.sosy_lab.cpachecker.cfa.model.FunctionEntryNode; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.base.StandardSystemProperty; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import com.google.common.io.Resources; import edu.buaa.satla.analysis.core.CPAcheckerResult.Result; import edu.buaa.satla.analysis.core.ShutdownNotifier.ShutdownRequestListener; import edu.buaa.satla.analysis.core.algorithm.Algorithm; import edu.buaa.satla.analysis.core.algorithm.ExternalCBMCAlgorithm; import edu.buaa.satla.analysis.core.algorithm.impact.ImpactAlgorithm; import edu.buaa.satla.analysis.core.interfaces.AbstractState; import edu.buaa.satla.analysis.core.interfaces.ConfigurableProgramAnalysis; import edu.buaa.satla.analysis.core.interfaces.Precision; import edu.buaa.satla.analysis.core.interfaces.Targetable; import edu.buaa.satla.analysis.core.reachedset.ReachedSet; import edu.buaa.satla.analysis.exceptions.CPAException; import edu.buaa.satla.analysis.exceptions.ParserException; import edu.buaa.satla.analysis.util.automaton.TargetLocationProvider; import edu.buaa.satla.analysis.util.globalinfo.GlobalInfo; @Options(prefix = "analysis") public class CPAchecker { public static interface CPAcheckerMXBean { public int getReachedSetSize(); public void stop(); } private static class CPAcheckerBean extends AbstractMBean implements CPAcheckerMXBean { private final ReachedSet reached; private final ShutdownNotifier shutdownNotifier; public CPAcheckerBean(ReachedSet pReached, LogManager logger, ShutdownNotifier pShutdownNotifier) { super("org.sosy_lab.cpachecker:type=CPAchecker", logger); reached = pReached; shutdownNotifier = pShutdownNotifier; register(); } @Override public int getReachedSetSize() { return reached.size(); } @Override public void stop() { shutdownNotifier.requestShutdown("A stop request was received via the JMX interface."); } } @Option(secure = true, description = "stop after the first error has been found") private boolean stopAfterError = true; @Option(secure = true, name = "disable", description = "stop CPAchecker after startup (internal option, not intended for users)") private boolean disableAnalysis = false; public static enum InitialStatesFor { ENTRY, TARGET, EXIT } @Option(secure = true, name = "initialStatesFor", description = "What CFA nodes should be the starting point of the analysis?") private InitialStatesFor initialStatesFor = InitialStatesFor.ENTRY; @Option(secure = true, name = "algorithm.CBMC", description = "use CBMC as an external tool from CPAchecker") private boolean runCBMCasExternalTool = false; @Option(secure = true, description = "Do not report unknown if analysis terminated, report true (UNSOUND!).") private boolean unknownAsTrue = false; private final LogManager logger; private final Configuration config; private final ShutdownNotifier shutdownNotifier; private final CoreComponentsFactory factory; // The content of this String is read from a file that is created by the // ant task "init". // To change the version, update the property in build.xml. private static final String version; static { String v = "(unknown version)"; try { URL url = CPAchecker.class.getClassLoader().getResource("org/sosy_lab/cpachecker/VERSION.txt"); if (url != null) { String content = Resources.toString(url, StandardCharsets.US_ASCII).trim(); if (content.matches("[a-zA-Z0-9 ._+:-]+")) { v = content; } } } catch (IOException e) { // Ignore exception, no better idea what to do here. } version = v; } public static String getVersion() { return getCPAcheckerVersion() + " (" + StandardSystemProperty.JAVA_VM_NAME.value() + " " + StandardSystemProperty.JAVA_VERSION.value() + ")"; } public static String getCPAcheckerVersion() { return version; } public CPAchecker(Configuration pConfiguration, LogManager pLogManager, ShutdownNotifier pShutdownNotifier) throws InvalidConfigurationException { config = pConfiguration; logger = pLogManager; shutdownNotifier = pShutdownNotifier; config.inject(this); factory = new CoreComponentsFactory(pConfiguration, pLogManager, shutdownNotifier); } public CPAcheckerResult run(String programDenotation) { logger.log(Level.INFO, "CPAchecker", getVersion(), "started"); MainCPAStatistics stats = null; ReachedSet reached = null; Result result = Result.NOT_YET_STARTED; String violatedPropertyDescription = ""; final ShutdownRequestListener interruptThreadOnShutdown = interruptCurrentThreadOnShutdown(); shutdownNotifier.register(interruptThreadOnShutdown); try { stats = new MainCPAStatistics(config, logger); // create reached set, cpa, algorithm stats.creationTime.start(); reached = factory.createReachedSet(); Algorithm algorithm; if (runCBMCasExternalTool) { checkIfOneValidFile(programDenotation); algorithm = new ExternalCBMCAlgorithm(programDenotation, config, logger); } else { CFA cfa = parse(programDenotation, stats); GlobalInfo.getInstance().storeCFA(cfa); shutdownNotifier.shutdownIfNecessary(); ConfigurableProgramAnalysis cpa = factory.createCPA(cfa, stats, initialStatesFor != InitialStatesFor.TARGET); GlobalInfo.getInstance().storeCPA(cpa); algorithm = factory.createAlgorithm(cpa, programDenotation, cfa, stats); if (algorithm instanceof ImpactAlgorithm) { ImpactAlgorithm mcmillan = (ImpactAlgorithm) algorithm; reached.add(mcmillan.getInitialState(cfa.getMainFunction()), mcmillan.getInitialPrecision(cfa.getMainFunction())); } else { initializeReachedSet(reached, cpa, cfa.getMainFunction(), cfa); } } printConfigurationWarnings(); stats.creationTime.stop(); shutdownNotifier.shutdownIfNecessary(); // now everything necessary has been instantiated if (disableAnalysis) { return new CPAcheckerResult(Result.NOT_YET_STARTED, violatedPropertyDescription, null, stats); } // run analysis result = Result.UNKNOWN; // set to unknown so that the result is correct in case of exception boolean isComplete = runAlgorithm(algorithm, reached, stats); violatedPropertyDescription = findViolatedProperties(reached); if (violatedPropertyDescription != null) { result = Result.FALSE; } else { violatedPropertyDescription = ""; result = analyzeResult(reached, isComplete); if (unknownAsTrue && result == Result.UNKNOWN) { result = Result.TRUE; } } } catch (IOException e) { logger.logUserException(Level.SEVERE, e, "Could not read file"); } catch (ParserException e) { logger.logUserException(Level.SEVERE, e, "Parsing failed"); StringBuilder msg = new StringBuilder(); msg.append("Please make sure that the code can be compiled by a compiler.\n"); if (e.getLanguage() == Language.C) { msg.append( "If the code was not preprocessed, please use a C preprocessor\nor specify the -preprocess command-line argument.\n"); } msg.append( "If the error still occurs, please send this error message\ntogether with the input file to cpachecker-users@googlegroups.com.\n"); logger.log(Level.INFO, msg); } catch (InvalidConfigurationException e) { logger.logUserException(Level.SEVERE, e, "Invalid configuration"); } catch (InterruptedException e) { // CPAchecker must exit because it was asked to // we return normally instead of propagating the exception // so we can return the partial result we have so far if (!Strings.isNullOrEmpty(e.getMessage())) { logger.logUserException(Level.WARNING, e, "Analysis stopped"); } } catch (CPAException e) { logger.logUserException(Level.SEVERE, e, null); } finally { shutdownNotifier.unregister(interruptThreadOnShutdown); } return new CPAcheckerResult(result, violatedPropertyDescription, reached, stats); } private void checkIfOneValidFile(String fileDenotation) throws InvalidConfigurationException { if (!denotesOneFile(fileDenotation)) { throw new InvalidConfigurationException("Exactly one code file has to be given."); } Path file = Paths.get(fileDenotation); try { Files.checkReadableFile(file); } catch (FileNotFoundException e) { throw new InvalidConfigurationException(e.getMessage()); } } private boolean denotesOneFile(String programDenotation) { return !programDenotation.contains(","); } private CFA parse(String fileNamesCommaSeparated, MainCPAStatistics stats) throws InvalidConfigurationException, IOException, ParserException, InterruptedException { // parse file and create CFA CFACreator cfaCreator = new CFACreator(config, logger, shutdownNotifier); stats.setCFACreator(cfaCreator); Splitter commaSplitter = Splitter.on(',').omitEmptyStrings().trimResults(); CFA cfa = cfaCreator.parseFileAndCreateCFA(commaSplitter.splitToList(fileNamesCommaSeparated)); stats.setCFA(cfa); return cfa; } private void printConfigurationWarnings() { Set<String> unusedProperties = config.getUnusedProperties(); if (!unusedProperties.isEmpty()) { logger.log(Level.WARNING, "The following configuration options were specified but are not used:\n", Joiner.on("\n ").join(unusedProperties), "\n"); } Set<String> deprecatedProperties = config.getDeprecatedProperties(); if (!deprecatedProperties.isEmpty()) { logger.log(Level.WARNING, "The following options are deprecated and will be removed in the future:\n", Joiner.on("\n ").join(deprecatedProperties), "\n"); } } private boolean runAlgorithm(final Algorithm algorithm, final ReachedSet reached, final MainCPAStatistics stats) throws CPAException, InterruptedException { logger.log(Level.INFO, "Starting analysis ..."); boolean isComplete = true; // register management interface for CPAchecker CPAcheckerBean mxbean = new CPAcheckerBean(reached, logger, shutdownNotifier); stats.startAnalysisTimer(); try { do { isComplete &= algorithm.run(reached); // either run only once (if stopAfterError == true) // or until the waitlist is empty } while (!stopAfterError && reached.hasWaitingState()); logger.log(Level.INFO, "Stopping analysis ..."); return isComplete; } finally { stats.stopAnalysisTimer(); // unregister management interface for CPAchecker mxbean.unregister(); } } private @Nullable String findViolatedProperties(final ReachedSet reached) { Set<String> descriptions = from(reached).filter(IS_TARGET_STATE) .transform(new Function<AbstractState, String>() { @Override public String apply(AbstractState s) { return ((Targetable) s).getViolatedPropertyDescription(); } }).copyInto(new HashSet<String>()); if (descriptions.isEmpty()) { // signal no target state -> result safe return null; } descriptions.remove(""); return Joiner.on(", ").join(descriptions); } private Result analyzeResult(final ReachedSet reached, boolean isComplete) { if (reached.hasWaitingState()) { logger.log(Level.WARNING, "Analysis not completed: there are still states to be processed."); return Result.UNKNOWN; } if (!isComplete) { logger.log(Level.WARNING, "Analysis incomplete: no errors found, but not everything could be checked."); return Result.UNKNOWN; } return Result.TRUE; } private void initializeReachedSet(final ReachedSet reached, final ConfigurableProgramAnalysis cpa, final FunctionEntryNode analysisEntryFunction, final CFA cfa) throws InvalidConfigurationException { logger.log(Level.FINE, "Creating initial reached set"); ImmutableSet<? extends CFANode> initialLocations = null; if (initialStatesFor == InitialStatesFor.TARGET) { TargetLocationProvider tlp = new TargetLocationProvider(factory.getReachedSetFactory(), shutdownNotifier, logger, config, cfa); initialLocations = tlp.tryGetAutomatonTargetLocations(analysisEntryFunction); } else if (initialStatesFor == InitialStatesFor.EXIT) { initialLocations = ImmutableSet.of(analysisEntryFunction.getExitNode()); } else { initialLocations = ImmutableSet.of(analysisEntryFunction); } if (initialLocations == null) { throw new InvalidConfigurationException( "Initialization of the set of initial states failed: No analysis target found!"); } for (CFANode loc : initialLocations) { AbstractState initialState = cpa.getInitialState(loc); Precision initialPrecision = cpa.getInitialPrecision(loc); reached.add(initialState, initialPrecision); } } }