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.base.Preconditions.*; import static com.google.common.base.Predicates.notNull; import static com.google.common.collect.FluentIterable.from; import static edu.buaa.satla.analysis.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.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.management.JMException; import org.sosy_lab.common.concurrency.Threads; 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.Paths; import org.sosy_lab.common.log.LogManager; import org.sosy_lab.common.time.TimeSpan; import org.sosy_lab.common.time.Timer; import org.sosy_lab.cpachecker.cfa.CFA; import org.sosy_lab.cpachecker.cfa.CFACreator; import org.sosy_lab.cpachecker.cfa.export.DOTBuilder; import org.sosy_lab.cpachecker.cfa.model.CFANode; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.HashMultiset; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimaps; import com.google.common.collect.Multiset; import com.google.common.collect.Ordering; import edu.buaa.satla.analysis.core.CPAcheckerResult.Result; import edu.buaa.satla.analysis.core.algorithm.Algorithm; import edu.buaa.satla.analysis.core.interfaces.AbstractState; import edu.buaa.satla.analysis.core.interfaces.AlgorithmIterationListener; import edu.buaa.satla.analysis.core.interfaces.Graphable; import edu.buaa.satla.analysis.core.interfaces.IterationStatistics; import edu.buaa.satla.analysis.core.interfaces.Statistics; import edu.buaa.satla.analysis.core.reachedset.ForwardingReachedSet; import edu.buaa.satla.analysis.core.reachedset.LocationMappedReachedSet; import edu.buaa.satla.analysis.core.reachedset.PartitionedReachedSet; import edu.buaa.satla.analysis.core.reachedset.ReachedSet; import edu.buaa.satla.analysis.util.CFAUtils; import edu.buaa.satla.analysis.util.coverage.CoverageInformation; import edu.buaa.satla.analysis.util.resources.MemoryStatistics; import edu.buaa.satla.analysis.util.resources.ProcessCpuTime; import edu.buaa.satla.analysis.util.statistics.StatisticsUtils; @Options class MainCPAStatistics implements Statistics, AlgorithmIterationListener { // Beyond this many states, we omit some statistics because they are costly. private static final int MAX_SIZE_FOR_REACHED_STATISTICS = 1000000; @Option(secure = true, name = "reachedSet.export", description = "print reached set to text file") private boolean exportReachedSet = false; @Option(secure = true, name = "reachedSet.file", description = "print reached set to text file") @FileOption(FileOption.Type.OUTPUT_FILE) private Path reachedSetFile = Paths.get("reached.txt"); @Option(secure = true, name = "reachedSet.dot", description = "print reached set to graph file") @FileOption(FileOption.Type.OUTPUT_FILE) private Path reachedSetGraphDumpPath = Paths.get("reached.dot"); @Option(secure = true, name = "coverage.export", description = "print coverage info to file") private boolean exportCoverage = true; @Option(secure = true, name = "coverage.file", description = "print coverage info to file") @FileOption(FileOption.Type.OUTPUT_FILE) private Path outputCoverageFile = Paths.get("coverage.info"); @Option(secure = true, name = "statistics.memory", description = "track memory usage of JVM during runtime") private boolean monitorMemoryUsage = true; private final LogManager logger; private final Collection<Statistics> subStats; private final MemoryStatistics memStats; private Thread memStatsThread; private Collection<IterationStatistics> iterationStats; private final Timer programTime = new Timer(); final Timer creationTime = new Timer(); final Timer cpaCreationTime = new Timer(); private final Timer analysisTime = new Timer(); private long programCpuTime; private long analysisCpuTime = 0; private Statistics cfaCreatorStatistics; private CFA cfa; public MainCPAStatistics(Configuration config, LogManager pLogger) throws InvalidConfigurationException { logger = pLogger; config.inject(this); subStats = new ArrayList<>(); if (monitorMemoryUsage) { memStats = new MemoryStatistics(pLogger); memStatsThread = Threads.newThread(memStats, "CPAchecker memory statistics collector", true); memStatsThread.start(); } else { memStats = null; } programTime.start(); try { programCpuTime = ProcessCpuTime.read(); } catch (JMException e) { logger.logDebugException(e, "Querying cpu time failed"); logger.log(Level.WARNING, "Your Java VM does not support measuring the cpu time, some statistics will be missing."); programCpuTime = -1; } /* * Google App Engine does not allow to use classes from the package java.lang.management. * Therefore it throws a NoClassDefFoundError if this is attempted regardless. To prevent * CPAChecker from crashing in this case we catch the error and log the event. */ catch (NoClassDefFoundError e) { logger.logDebugException(e, "Querying cpu time failed"); logger.log(Level.WARNING, "Google App Engine does not support measuring the cpu time."); programCpuTime = -1; } } public Collection<Statistics> getSubStatistics() { return subStats; } @Override public String getName() { return "CPAchecker"; } void startAnalysisTimer() { analysisTime.start(); try { analysisCpuTime = ProcessCpuTime.read(); } catch (JMException e) { logger.logDebugException(e, "Querying cpu time failed"); // user was already warned analysisCpuTime = -1; } /* * Google App Engine does not allow to use classes from the package java.lang.management. * Therefore it throws a NoClassDefFoundError if this is attempted regardless. To prevent * CPAChecker from crashing in this case we catch the error and log the event. */ catch (NoClassDefFoundError e) { logger.logDebugException(e, "Querying cpu time failed"); logger.log(Level.WARNING, "Google App Engine does not support measuring the cpu time."); analysisCpuTime = -1; } } void stopAnalysisTimer() { analysisTime.stop(); programTime.stop(); try { long stopCpuTime = ProcessCpuTime.read(); if (programCpuTime >= 0) { programCpuTime = stopCpuTime - programCpuTime; } if (analysisCpuTime >= 0) { analysisCpuTime = stopCpuTime - analysisCpuTime; } } catch (JMException e) { logger.logDebugException(e, "Querying cpu time failed"); // user was already warned } /* * Google App Engine does not allow to use classes from the package java.lang.management. * Therefore it throws a NoClassDefFoundError if this is attempted regardless. To prevent * CPAChecker from crashing in this case we catch the error and log the event. */ catch (NoClassDefFoundError e) { logger.logDebugException(e, "Querying cpu time failed"); logger.log(Level.WARNING, "Google App Engine does not support measuring the cpu time."); } } @Override public void printStatistics(PrintStream out, Result result, ReachedSet reached) { checkNotNull(out); checkNotNull(result); checkArgument(result == Result.NOT_YET_STARTED || reached != null); // call stop again in case CPAchecker was terminated abnormally if (analysisTime.isRunning()) { analysisTime.stop(); } if (programTime.isRunning()) { programTime.stop(); } if (memStats != null) { memStatsThread.interrupt(); // stop memory statistics collection } if (result != Result.NOT_YET_STARTED) { dumpReachedSet(reached); printSubStatistics(out, result, reached); if (exportCoverage && outputCoverageFile != null && cfa != null) { CoverageInformation.writeCoverageInfo(outputCoverageFile, reached, cfa, logger); } } out.println("CPAchecker general statistics"); out.println("-----------------------------"); printCfaStatistics(out); if (result != Result.NOT_YET_STARTED) { try { printReachedSetStatistics(reached, out); } catch (OutOfMemoryError e) { logger.logUserException(Level.WARNING, e, "Out of memory while generating statistics about final reached set"); } } out.println(); printTimeStatistics(out, result, reached); out.println(); printMemoryStatistics(out); } private void dumpReachedSet(ReachedSet reached) { dumpReachedSet(reached, reachedSetFile, false); dumpReachedSet(reached, reachedSetGraphDumpPath, true); } private void dumpReachedSet(ReachedSet reached, Path pOutputFile, boolean writeDotFormat) { assert reached != null : "ReachedSet may be null only if analysis not yet started"; if (exportReachedSet && pOutputFile != null) { try (Writer w = Files.openOutputFile(pOutputFile)) { if (writeDotFormat) { // Location-map specific dump. dumpLocationMappedReachedSet(reached, cfa, w); } else { // Default dump. Joiner.on('\n').appendTo(w, reached); } } catch (IOException e) { logger.logUserException(Level.WARNING, e, "Could not write reached set to file"); } catch (OutOfMemoryError e) { logger.logUserException(Level.WARNING, e, "Could not write reached set to file due to memory problems"); } } } private void dumpLocationMappedReachedSet(final ReachedSet pReachedSet, CFA cfa, Appendable sb) throws IOException { final ListMultimap<CFANode, AbstractState> locationIndex = Multimaps.index(pReachedSet, EXTRACT_LOCATION); Function<CFANode, String> nodeLabelFormatter = new Function<CFANode, String>() { @Override public String apply(CFANode node) { StringBuilder buf = new StringBuilder(); buf.append(node.getNodeNumber()).append("\n"); for (AbstractState state : locationIndex.get(node)) { if (state instanceof Graphable) { buf.append(((Graphable) state).toDOTLabel()); } } return buf.toString(); } }; DOTBuilder.generateDOT(sb, cfa, nodeLabelFormatter); } private void printSubStatistics(PrintStream out, Result result, ReachedSet reached) { assert reached != null : "ReachedSet may be null only if analysis not yet started"; for (Statistics s : subStats) { String name = s.getName(); if (!Strings.isNullOrEmpty(name)) { name = name + " statistics"; out.println(name); out.println(Strings.repeat("-", name.length())); } try { s.printStatistics(out, result, reached); } catch (OutOfMemoryError e) { logger.logUserException(Level.WARNING, e, "Out of memory while generating statistics and writing output files"); } if (!Strings.isNullOrEmpty(name)) { out.println(); } } } private void printReachedSetStatistics(ReachedSet reached, PrintStream out) { assert reached != null : "ReachedSet may be null only if analysis not yet started"; if (reached instanceof ForwardingReachedSet) { reached = ((ForwardingReachedSet) reached).getDelegate(); } int reachedSize = reached.size(); out.println("Size of reached set: " + reachedSize); if (!reached.isEmpty()) { if (reachedSize < MAX_SIZE_FOR_REACHED_STATISTICS) { printReachedSetStatisticsDetails(reached, out); } if (reached.hasWaitingState()) { out.println(" Size of final wait list " + reached.getWaitlistSize()); } } } private void printReachedSetStatisticsDetails(ReachedSet reached, PrintStream out) { int reachedSize = reached.size(); Set<CFANode> locations; CFANode mostFrequentLocation = null; int mostFrequentLocationCount = 0; if (reached instanceof LocationMappedReachedSet) { LocationMappedReachedSet l = (LocationMappedReachedSet) reached; locations = l.getLocations(); Map.Entry<Object, Collection<AbstractState>> maxPartition = l.getMaxPartition(); mostFrequentLocation = (CFANode) maxPartition.getKey(); mostFrequentLocationCount = maxPartition.getValue().size(); } else { HashMultiset<CFANode> allLocations = HashMultiset .create(from(reached).transform(EXTRACT_LOCATION).filter(notNull())); locations = allLocations.elementSet(); for (Multiset.Entry<CFANode> location : allLocations.entrySet()) { int size = location.getCount(); if (size > mostFrequentLocationCount) { mostFrequentLocationCount = size; mostFrequentLocation = location.getElement(); } else if (size == mostFrequentLocationCount) { // use node with smallest number to have deterministic output mostFrequentLocation = Ordering.natural().min(mostFrequentLocation, location.getElement()); } } } if (!locations.isEmpty()) { int locs = locations.size(); out.println(" Number of reached locations: " + locs + " (" + StatisticsUtils.toPercent(locs, cfa.getAllNodes().size()) + ")"); out.println(" Avg states per location: " + reachedSize / locs); out.println(" Max states per location: " + mostFrequentLocationCount + " (at node " + mostFrequentLocation + ")"); Set<String> functions = from(locations).transform(CFAUtils.GET_FUNCTION).toSet(); out.println(" Number of reached functions: " + functions.size() + " (" + StatisticsUtils.toPercent(functions.size(), cfa.getNumberOfFunctions()) + ")"); } if (reached instanceof PartitionedReachedSet) { PartitionedReachedSet p = (PartitionedReachedSet) reached; int partitions = p.getNumberOfPartitions(); out.println(" Number of partitions: " + partitions); out.println(" Avg size of partitions: " + reachedSize / partitions); Map.Entry<Object, Collection<AbstractState>> maxPartition = p.getMaxPartition(); out.print(" Max size of partitions: " + maxPartition.getValue().size()); if (maxPartition.getValue().size() > 1) { out.println(" (with key " + maxPartition.getKey() + ")"); } else { out.println(); } } out.println(" Number of target states: " + from(reached).filter(IS_TARGET_STATE).size()); } private void printCfaStatistics(PrintStream out) { if (cfa != null) { out.println("Number of program locations: " + cfa.getAllNodes().size()); out.println("Number of functions: " + cfa.getNumberOfFunctions()); if (cfa.getLoopStructure().isPresent()) { int loops = cfa.getLoopStructure().get().getCount(); out.println("Number of loops: " + loops); } } } private void printTimeStatistics(PrintStream out, Result result, ReachedSet reached) { out.println("Time for analysis setup: " + creationTime); out.println(" Time for loading CPAs: " + cpaCreationTime); if (cfaCreatorStatistics != null) { cfaCreatorStatistics.printStatistics(out, result, reached); } out.println("Time for Analysis: " + analysisTime); out.println( "CPU time for analysis: " + TimeSpan.ofNanos(analysisCpuTime).formatAs(TimeUnit.SECONDS)); out.println("Total time for CPAchecker: " + programTime); out.println("Total CPU time for CPAchecker:" + TimeSpan.ofNanos(programCpuTime).formatAs(TimeUnit.SECONDS)); } private void printMemoryStatistics(PrintStream out) { if (monitorMemoryUsage) { MemoryStatistics.printGcStatistics(out); if (memStats != null) { try { memStatsThread.join(); // thread should have terminated already, // but wait for it to ensure memory visibility } catch (InterruptedException e) { Thread.currentThread().interrupt(); } if (!memStatsThread.isAlive()) { memStats.printStatistics(out); } } } } public void setCFACreator(CFACreator pCfaCreator) { Preconditions.checkState(cfaCreatorStatistics == null); cfaCreatorStatistics = pCfaCreator.getStatistics(); } public void setCFA(CFA pCfa) { Preconditions.checkState(cfa == null); cfa = pCfa; } @Override public void afterAlgorithmIteration(Algorithm pAlg, ReachedSet pReached) { if (iterationStats == null) { iterationStats = Lists.newArrayList(); for (Statistics s : subStats) { if (s instanceof IterationStatistics) { iterationStats.add((IterationStatistics) s); } } } for (IterationStatistics s : iterationStats) { s.printIterationStatistics(System.out, pReached); } } }