hu.bme.mit.theta.cfa.cli.CfaCli Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of theta-cfa-cli Show documentation
Show all versions of theta-cfa-cli Show documentation
Cfa Cli subproject in the Theta model checking framework
/*
* Copyright 2024 Budapest University of Technology and Economics
*
* 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.
*/
package hu.bme.mit.theta.cfa.cli;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.google.common.base.Stopwatch;
import hu.bme.mit.theta.analysis.Trace;
import hu.bme.mit.theta.analysis.algorithm.SafetyResult;
import hu.bme.mit.theta.analysis.algorithm.arg.ARG;
import hu.bme.mit.theta.analysis.algorithm.bounded.BoundedChecker;
import hu.bme.mit.theta.analysis.algorithm.bounded.BoundedCheckerBuilderKt;
import hu.bme.mit.theta.analysis.algorithm.bounded.MonolithicExpr;
import hu.bme.mit.theta.analysis.algorithm.cegar.CegarStatistics;
import hu.bme.mit.theta.analysis.expl.ExplState;
import hu.bme.mit.theta.analysis.expr.ExprAction;
import hu.bme.mit.theta.analysis.expr.ExprState;
import hu.bme.mit.theta.analysis.expr.refinement.PruneStrategy;
import hu.bme.mit.theta.cfa.CFA;
import hu.bme.mit.theta.cfa.analysis.CfaAction;
import hu.bme.mit.theta.cfa.analysis.CfaState;
import hu.bme.mit.theta.cfa.analysis.CfaToMonolithicExprKt;
import hu.bme.mit.theta.cfa.analysis.CfaTraceConcretizer;
import hu.bme.mit.theta.cfa.analysis.config.CfaConfig;
import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder;
import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Algorithm;
import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Domain;
import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Encoding;
import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.InitPrec;
import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.PrecGranularity;
import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.PredSplit;
import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Refinement;
import hu.bme.mit.theta.cfa.analysis.config.CfaConfigBuilder.Search;
import hu.bme.mit.theta.cfa.analysis.utils.CfaVisualizer;
import hu.bme.mit.theta.cfa.dsl.CfaDslManager;
import hu.bme.mit.theta.common.CliUtils;
import hu.bme.mit.theta.common.OsHelper;
import hu.bme.mit.theta.common.logging.ConsoleLogger;
import hu.bme.mit.theta.common.logging.Logger;
import hu.bme.mit.theta.common.logging.Logger.Level;
import hu.bme.mit.theta.common.logging.NullLogger;
import hu.bme.mit.theta.common.table.BasicTableWriter;
import hu.bme.mit.theta.common.table.TableWriter;
import hu.bme.mit.theta.common.visualization.Graph;
import hu.bme.mit.theta.common.visualization.writer.GraphvizWriter;
import hu.bme.mit.theta.solver.SolverFactory;
import hu.bme.mit.theta.solver.SolverManager;
import hu.bme.mit.theta.solver.smtlib.SmtLibSolverManager;
import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory;
import hu.bme.mit.theta.solver.z3legacy.Z3SolverManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A command line interface for running a CEGAR configuration on a CFA.
*/
public class CfaCli {
private static final String JAR_NAME = "theta-cfa-cli.jar";
private final String[] args;
private final TableWriter writer;
@Parameter(names = {"--algorithm"}, description = "Algorithm")
Algorithm algorithm = Algorithm.CEGAR;
@Parameter(names = "--domain", description = "Abstract domain")
Domain domain = Domain.PRED_CART;
@Parameter(names = "--refinement", description = "Refinement strategy")
Refinement refinement = Refinement.SEQ_ITP;
@Parameter(names = "--search", description = "Search strategy")
Search search = Search.BFS;
@Parameter(names = "--predsplit", description = "Predicate splitting (for predicate abstraction)")
PredSplit predSplit = PredSplit.WHOLE;
@Parameter(names = "--solver", description = "Sets the underlying SMT solver to use for both the abstraction and the refinement process. Enter in format :, see theta-smtlib-cli.jar for more details. Enter \"Z3\" to use the legacy z3 solver.")
String solver = "Z3";
@Parameter(names = "--abstraction-solver", description = "Sets the underlying SMT solver to use for the abstraction process. Enter in format :, see theta-smtlib-cli.jar for more details. Enter \"Z3\" to use the legacy z3 solver.")
String abstractionSolver;
@Parameter(names = "--refinement-solver", description = "Sets the underlying SMT solver to use for the refinement process. Enter in format :, see theta-smtlib-cli.jar for more details. Enter \"Z3\" to use the legacy z3 solver.")
String refinementSolver;
@Parameter(names = "--home", description = "The path of the solver registry")
String home = SmtLibSolverManager.HOME.toAbsolutePath().toString();
@Parameter(names = "--model", description = "Path of the input CFA model", required = true)
String model;
@Parameter(names = "--errorloc", description = "Error (target) location")
String errorLoc = "";
@Parameter(names = "--precgranularity", description = "Precision granularity")
PrecGranularity precGranularity = PrecGranularity.GLOBAL;
@Parameter(names = "--encoding", description = "Block encoding")
Encoding encoding = Encoding.LBE;
@Parameter(names = "--maxenum", description = "Maximal number of explicitly enumerated successors (0: unlimited)")
Integer maxEnum = 10;
@Parameter(names = "--initprec", description = "Initial precision of abstraction")
InitPrec initPrec = InitPrec.EMPTY;
@Parameter(names = "--prunestrategy", description = "Strategy for pruning the ARG after refinement")
PruneStrategy pruneStrategy = PruneStrategy.LAZY;
@Parameter(names = "--loglevel", description = "Detailedness of logging")
Logger.Level logLevel = Level.SUBSTEP;
@Parameter(names = "--benchmark", description = "Benchmark mode (only print metrics)")
Boolean benchmarkMode = false;
@Parameter(names = "--cex", description = "Write concrete counterexample to a file")
String cexfile = null;
@Parameter(names = "--header", description = "Print only a header (for benchmarks)", help = true)
boolean headerOnly = false;
@Parameter(names = "--visualize", description = "Visualize CFA to this file without running the algorithm")
String visualize = null;
@Parameter(names = "--metrics", description = "Print metrics about the CFA without running the algorithm")
boolean metrics = false;
@Parameter(names = "--stacktrace", description = "Print full stack trace in case of exception")
boolean stacktrace = false;
@Parameter(names = "--version", description = "Display version", help = true)
boolean versionInfo = false;
private Logger logger;
public CfaCli(final String[] args) {
this.args = args;
writer = new BasicTableWriter(System.out, ",", "\"", "\"");
}
public static void main(final String[] args) {
final CfaCli mainApp = new CfaCli(args);
mainApp.run();
}
private void run() {
try {
JCommander.newBuilder().addObject(this).programName(JAR_NAME).build().parse(args);
logger = benchmarkMode ? NullLogger.getInstance() : new ConsoleLogger(logLevel);
} catch (final ParameterException ex) {
System.out.println("Invalid parameters, details:");
System.out.println(ex.getMessage());
ex.usage();
return;
}
if (headerOnly) {
printHeader();
return;
}
if (versionInfo) {
CliUtils.printVersion(System.out);
return;
}
try {
SolverManager.registerSolverManager(Z3SolverManager.create());
if (OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)) {
final var homePath = Path.of(home);
final var smtLibSolverManager = SmtLibSolverManager.create(homePath, logger);
SolverManager.registerSolverManager(smtLibSolverManager);
}
final Stopwatch sw = Stopwatch.createStarted();
final CFA cfa = loadModel();
if (visualize != null) {
final Graph graph = CfaVisualizer.visualize(cfa);
GraphvizWriter.getInstance().writeFileAutoConvert(graph, visualize);
return;
}
if (metrics) {
CfaMetrics.printMetrics(logger, cfa);
return;
}
CFA.Loc errLoc = null;
if (cfa.getErrorLoc().isPresent()) {
errLoc = cfa.getErrorLoc().get();
}
if (!errorLoc.isEmpty()) {
errLoc = null;
for (CFA.Loc running : cfa.getLocs()) {
if (running.getName().equals(errorLoc)) {
errLoc = running;
}
}
checkNotNull(errLoc, "Location '" + errorLoc + "' not found in CFA");
}
checkNotNull(errLoc, "Error location must be specified in CFA or as argument");
final SolverFactory abstractionSolverFactory;
if (abstractionSolver != null) {
abstractionSolverFactory = SolverManager.resolveSolverFactory(abstractionSolver);
} else {
abstractionSolverFactory = SolverManager.resolveSolverFactory(solver);
}
final SolverFactory refinementSolverFactory;
if (refinementSolver != null) {
refinementSolverFactory = SolverManager.resolveSolverFactory(refinementSolver);
} else {
refinementSolverFactory = SolverManager.resolveSolverFactory(solver);
}
final SafetyResult, ? extends Trace, ?>> status;
if (algorithm == Algorithm.CEGAR) {
final CfaConfig, ?, ?> configuration = buildConfiguration(cfa, errLoc, abstractionSolverFactory, refinementSolverFactory);
status = check(configuration);
} else if (algorithm == Algorithm.BMC || algorithm == Algorithm.KINDUCTION || algorithm == Algorithm.IMC) {
final BoundedChecker, ?> checker = buildBoundedChecker(cfa, abstractionSolverFactory);
status = checker.check(null);
} else {
throw new UnsupportedOperationException("Algorithm " + algorithm + " not supported");
}
sw.stop();
printResult(status, sw.elapsed(TimeUnit.MILLISECONDS));
if (status.isUnsafe() && cexfile != null) {
writeCex(status.asUnsafe());
}
} catch (final Throwable ex) {
printError(ex);
System.exit(1);
}
}
private void printHeader() {
Stream.of("Result", "TimeMs", "AlgoTimeMs", "AbsTimeMs", "RefTimeMs", "Iterations",
"ArgSize", "ArgDepth", "ArgMeanBranchFactor", "CexLen").forEach(writer::cell);
writer.newRow();
}
private CFA loadModel() throws Exception {
try (InputStream inputStream = new FileInputStream(model)) {
try {
return CfaDslManager.createCfa(inputStream);
} catch (final Exception ex) {
throw new Exception("Could not parse CFA: " + ex.getMessage(), ex);
}
}
}
private CfaConfig, ?, ?> buildConfiguration(final CFA cfa, final CFA.Loc errLoc,
final SolverFactory abstractionSolverFactory, final SolverFactory refinementSolverFactory)
throws Exception {
try {
return new CfaConfigBuilder(domain, refinement, abstractionSolverFactory,
refinementSolverFactory)
.precGranularity(precGranularity).search(search)
.predSplit(predSplit).encoding(encoding).maxEnum(maxEnum).initPrec(initPrec)
.pruneStrategy(pruneStrategy).logger(logger).build(cfa, errLoc);
} catch (final Exception ex) {
throw new Exception("Could not create configuration: " + ex.getMessage(), ex);
}
}
private BoundedChecker, ?> buildBoundedChecker(final CFA cfa, final SolverFactory abstractionSolverFactory) {
final MonolithicExpr monolithicExpr = CfaToMonolithicExprKt.toMonolithicExpr(cfa);
final BoundedChecker, ?> checker;
switch (algorithm) {
case BMC -> checker = BoundedCheckerBuilderKt.buildBMC(
monolithicExpr,
abstractionSolverFactory.createSolver(),
val -> CfaToMonolithicExprKt.valToState(cfa, val),
(val1, val2) -> CfaToMonolithicExprKt.valToAction(cfa, val1, val2),
logger
);
case KINDUCTION -> checker = BoundedCheckerBuilderKt.buildKIND(
monolithicExpr,
abstractionSolverFactory.createSolver(),
abstractionSolverFactory.createSolver(),
val -> CfaToMonolithicExprKt.valToState(cfa, val),
(val1, val2) -> CfaToMonolithicExprKt.valToAction(cfa, val1, val2),
logger
);
case IMC -> checker = BoundedCheckerBuilderKt.buildIMC(
monolithicExpr,
abstractionSolverFactory.createSolver(),
abstractionSolverFactory.createItpSolver(),
val -> CfaToMonolithicExprKt.valToState(cfa, val),
(val1, val2) -> CfaToMonolithicExprKt.valToAction(cfa, val1, val2),
logger
);
default ->
throw new UnsupportedOperationException("Algorithm " + algorithm + " not supported");
}
return checker;
}
private SafetyResult extends ARG, ?>, ? extends Trace, ?>> check(CfaConfig, ?, ?> configuration) throws Exception {
try {
return configuration.check();
} catch (final Exception ex) {
String message = ex.getMessage() == null ? "(no message)" : ex.getMessage();
throw new Exception(
"Error while running algorithm: " + ex.getClass().getSimpleName() + " " + message,
ex);
}
}
private void printResult(final SafetyResult, ? extends Trace, ?>> status, final long totalTimeMs) {
final CegarStatistics stats = (CegarStatistics)
status.getStats().orElse(new CegarStatistics(0, 0, 0, 0));
if (benchmarkMode) {
writer.cell(status.isSafe());
writer.cell(totalTimeMs);
writer.cell(stats.getAlgorithmTimeMs());
writer.cell(stats.getAbstractorTimeMs());
writer.cell(stats.getRefinerTimeMs());
writer.cell(stats.getIterations());
if (status.getWitness() instanceof ARG, ?> arg) {
writer.cell(arg.size());
writer.cell(arg.getDepth());
writer.cell(arg.getMeanBranchingFactor());
} else {
writer.cell("");
writer.cell("");
writer.cell("");
}
if (status.isUnsafe()) {
writer.cell(status.asUnsafe().getCex().length() + "");
} else {
writer.cell("");
}
writer.newRow();
}
}
private void printError(final Throwable ex) {
final String message = ex.getMessage() == null ? "" : ex.getMessage();
if (benchmarkMode) {
writer.cell("[EX] " + ex.getClass().getSimpleName() + ": " + message);
writer.newRow();
} else {
logger.write(Level.RESULT, "%s occurred, message: %s%n", ex.getClass().getSimpleName(),
message);
if (stacktrace) {
final StringWriter errors = new StringWriter();
ex.printStackTrace(new PrintWriter(errors));
logger.write(Level.RESULT, "Trace:%n%s%n", errors.toString());
} else {
logger.write(Level.RESULT, "Use --stacktrace for stack trace%n");
}
}
}
private void writeCex(final SafetyResult.Unsafe, ?> status) throws FileNotFoundException {
@SuppressWarnings("unchecked") final Trace, CfaAction> trace = (Trace, CfaAction>) status.getCex();
final Trace, CfaAction> concrTrace = CfaTraceConcretizer.concretize(
trace, Z3LegacySolverFactory.getInstance());
final File file = new File(cexfile);
PrintWriter printWriter = null;
try {
printWriter = new PrintWriter(file);
printWriter.write(concrTrace.toString());
} finally {
if (printWriter != null) {
printWriter.close();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy