10#include "klee/Config/config.h"
27#include "llvm/Support/CommandLine.h"
28#include "llvm/Support/raw_ostream.h"
33llvm::cl::opt<std::string> Z3LogInteractionFile(
34 "debug-z3-log-api-interaction", llvm::cl::init(
""),
35 llvm::cl::desc(
"Log API interaction with Z3 to the specified path"),
38llvm::cl::opt<std::string> Z3QueryDumpFile(
39 "debug-z3-dump-queries", llvm::cl::init(
""),
40 llvm::cl::desc(
"Dump Z3's representation of the query to the specified path"),
43llvm::cl::opt<bool> Z3ValidateModels(
44 "debug-z3-validate-models", llvm::cl::init(
false),
45 llvm::cl::desc(
"When generating Z3 models validate these against the query"),
48llvm::cl::opt<unsigned>
49 Z3VerbosityLevel(
"debug-z3-verbosity", llvm::cl::init(0),
50 llvm::cl::desc(
"Z3 verbosity level (default=0)"),
54#include "llvm/Support/ErrorHandling.h"
58class Z3SolverImpl :
public SolverImpl {
62 SolverRunStatus runStatusCode;
63 std::unique_ptr<llvm::raw_fd_ostream> dumpedQueriesFile;
64 ::Z3_params solverParameters;
66 ::Z3_symbol timeoutParamStrSymbol;
68 bool internalRunSolver(
const Query &,
69 const std::vector<const Array *> *objects,
70 std::vector<std::vector<unsigned char> > *values,
72 bool validateZ3Model(::Z3_solver &theSolver, ::Z3_model &theModel);
78 char *getConstraintLog(
const Query &);
79 void setCoreSolverTimeout(time::Span _timeout) {
82 auto timeoutInMilliSeconds =
static_cast<unsigned>((timeout.toMicroseconds() / 1000));
83 if (!timeoutInMilliSeconds)
84 timeoutInMilliSeconds = UINT_MAX;
85 Z3_params_set_uint(builder->ctx, solverParameters, timeoutParamStrSymbol,
86 timeoutInMilliSeconds);
89 bool computeTruth(
const Query &,
bool &isValid);
90 bool computeValue(
const Query &, ref<Expr> &result);
91 bool computeInitialValues(
const Query &,
92 const std::vector<const Array *> &objects,
93 std::vector<std::vector<unsigned char> > &values,
96 handleSolverResponse(::Z3_solver theSolver, ::Z3_lbool satisfiable,
97 const std::vector<const Array *> *objects,
98 std::vector<std::vector<unsigned char> > *values,
100 SolverRunStatus getOperationStatusCode();
103Z3SolverImpl::Z3SolverImpl()
104 : builder(new Z3Builder(
106 Z3LogInteractionFile.size() > 0
107 ? Z3LogInteractionFile.c_str()
109 runStatusCode(SOLVER_RUN_STATUS_FAILURE) {
110 assert(builder &&
"unable to create Z3Builder");
111 solverParameters = Z3_mk_params(builder->ctx);
112 Z3_params_inc_ref(builder->ctx, solverParameters);
113 timeoutParamStrSymbol = Z3_mk_string_symbol(builder->ctx,
"timeout");
114 setCoreSolverTimeout(timeout);
116 if (!Z3QueryDumpFile.empty()) {
119 if (!dumpedQueriesFile) {
120 klee_error(
"Error creating file for dumping Z3 queries: %s",
123 klee_message(
"Dumping Z3 queries to \"%s\"", Z3QueryDumpFile.c_str());
127 if (Z3VerbosityLevel > 0) {
128 std::string underlyingString;
129 llvm::raw_string_ostream ss(underlyingString);
130 ss << Z3VerbosityLevel;
132 Z3_global_param_set(
"verbose", underlyingString.c_str());
136Z3SolverImpl::~Z3SolverImpl() {
137 Z3_params_dec_ref(builder->ctx, solverParameters);
141Z3Solver::Z3Solver() : Solver(new Z3SolverImpl()) {}
143char *Z3Solver::getConstraintLog(
const Query &query) {
144 return impl->getConstraintLog(query);
147void Z3Solver::setCoreSolverTimeout(time::Span timeout) {
148 impl->setCoreSolverTimeout(timeout);
151char *Z3SolverImpl::getConstraintLog(
const Query &query) {
152 std::vector<Z3ASTHandle> assumptions;
158 Z3Builder temp_builder(
false,
160 ConstantArrayFinder constant_arrays_in_query;
161 for (
auto const &constraint : query.constraints) {
162 assumptions.push_back(temp_builder.construct(constraint));
163 constant_arrays_in_query.visit(constraint);
172 Z3_mk_not(temp_builder.ctx, temp_builder.construct(query.expr)),
174 constant_arrays_in_query.visit(query.expr);
176 for (
auto const &constant_array : constant_arrays_in_query.results) {
177 assert(temp_builder.constant_array_assertions.count(constant_array) == 1 &&
178 "Constant array found in query, but not handled by Z3Builder");
179 for (
auto const &arrayIndexValueExpr :
180 temp_builder.constant_array_assertions[constant_array]) {
181 assumptions.push_back(arrayIndexValueExpr);
185 ::Z3_ast *assumptionsArray = NULL;
186 int numAssumptions = assumptions.size();
187 if (numAssumptions) {
188 assumptionsArray = (::Z3_ast *)malloc(
sizeof(::Z3_ast) * numAssumptions);
189 for (
int index = 0; index < numAssumptions; ++index) {
190 assumptionsArray[index] = (::Z3_ast)assumptions[index];
194 ::Z3_string result = Z3_benchmark_to_smtlib_string(
196 "Emited by klee::Z3SolverImpl::getConstraintLog()",
205 free(assumptionsArray);
213 return strdup(result);
216bool Z3SolverImpl::computeTruth(
const Query &query,
bool &isValid) {
217 bool hasSolution =
false;
219 internalRunSolver(query, NULL, NULL, hasSolution);
220 isValid = !hasSolution;
224bool Z3SolverImpl::computeValue(
const Query &query, ref<Expr> &result) {
225 std::vector<const Array *> objects;
226 std::vector<std::vector<unsigned char> > values;
232 if (!computeInitialValues(query.withFalse(), objects, values, hasSolution))
234 assert(hasSolution &&
"state has invalid constraint set");
237 Assignment a(objects, values);
238 result = a.evaluate(query.expr);
243bool Z3SolverImpl::computeInitialValues(
244 const Query &query,
const std::vector<const Array *> &objects,
245 std::vector<std::vector<unsigned char> > &values,
bool &hasSolution) {
246 return internalRunSolver(query, &objects, &values, hasSolution);
249bool Z3SolverImpl::internalRunSolver(
250 const Query &query,
const std::vector<const Array *> *objects,
251 std::vector<std::vector<unsigned char> > *values,
bool &hasSolution) {
260 Z3_solver theSolver = Z3_mk_solver(builder->ctx);
261 Z3_solver_inc_ref(builder->ctx, theSolver);
262 Z3_solver_set_params(builder->ctx, theSolver, solverParameters);
264 runStatusCode = SOLVER_RUN_STATUS_FAILURE;
266 ConstantArrayFinder constant_arrays_in_query;
267 for (
auto const &constraint : query.constraints) {
268 Z3_solver_assert(builder->ctx, theSolver, builder->construct(constraint));
269 constant_arrays_in_query.visit(constraint);
276 Z3ASTHandle(builder->construct(query.expr), builder->ctx);
277 constant_arrays_in_query.visit(query.expr);
279 for (
auto const &constant_array : constant_arrays_in_query.results) {
280 assert(builder->constant_array_assertions.count(constant_array) == 1 &&
281 "Constant array found in query, but not handled by Z3Builder");
282 for (
auto const &arrayIndexValueExpr :
283 builder->constant_array_assertions[constant_array]) {
284 Z3_solver_assert(builder->ctx, theSolver, arrayIndexValueExpr);
294 builder->ctx, theSolver,
295 Z3ASTHandle(Z3_mk_not(builder->ctx, z3QueryExpr), builder->ctx));
297 if (dumpedQueriesFile) {
298 *dumpedQueriesFile <<
"; start Z3 query\n";
299 *dumpedQueriesFile << Z3_solver_to_string(builder->ctx, theSolver);
300 *dumpedQueriesFile <<
"(check-sat)\n";
301 *dumpedQueriesFile <<
"(reset)\n";
302 *dumpedQueriesFile <<
"; end Z3 query\n\n";
303 dumpedQueriesFile->flush();
306 ::Z3_lbool satisfiable = Z3_solver_check(builder->ctx, theSolver);
307 runStatusCode = handleSolverResponse(theSolver, satisfiable, objects, values,
310 Z3_solver_dec_ref(builder->ctx, theSolver);
316 builder->clearConstructCache();
318 if (runStatusCode == SolverImpl::SOLVER_RUN_STATUS_SUCCESS_SOLVABLE ||
319 runStatusCode == SolverImpl::SOLVER_RUN_STATUS_SUCCESS_UNSOLVABLE) {
327 if (runStatusCode == SolverImpl::SOLVER_RUN_STATUS_INTERRUPTED) {
333SolverImpl::SolverRunStatus Z3SolverImpl::handleSolverResponse(
334 ::Z3_solver theSolver, ::Z3_lbool satisfiable,
335 const std::vector<const Array *> *objects,
336 std::vector<std::vector<unsigned char> > *values,
bool &hasSolution) {
337 switch (satisfiable) {
342 assert(values == NULL);
343 return SolverImpl::SOLVER_RUN_STATUS_SUCCESS_SOLVABLE;
345 assert(values &&
"values cannot be nullptr");
346 ::Z3_model theModel = Z3_solver_get_model(builder->ctx, theSolver);
347 assert(theModel &&
"Failed to retrieve model");
348 Z3_model_inc_ref(builder->ctx, theModel);
349 values->reserve(objects->size());
350 for (std::vector<const Array *>::const_iterator it = objects->begin(),
353 const Array *array = *it;
354 std::vector<unsigned char> data;
356 data.reserve(array->size);
357 for (
unsigned offset = 0; offset < array->size; offset++) {
359 ::Z3_ast arrayElementExpr;
360 Z3ASTHandle initial_read = builder->getInitialRead(array, offset);
363 bool successfulEval =
364 Z3_model_eval(builder->ctx, theModel, initial_read,
365 Z3_TRUE, &arrayElementExpr);
366 assert(successfulEval &&
"Failed to evaluate model");
367 Z3_inc_ref(builder->ctx, arrayElementExpr);
368 assert(Z3_get_ast_kind(builder->ctx, arrayElementExpr) ==
370 "Evaluated expression has wrong sort");
372 int arrayElementValue = 0;
374 bool successGet = Z3_get_numeral_int(builder->ctx, arrayElementExpr,
376 assert(successGet &&
"failed to get value back");
377 assert(arrayElementValue >= 0 && arrayElementValue <= 255 &&
378 "Integer from model is out of range");
379 data.push_back(arrayElementValue);
380 Z3_dec_ref(builder->ctx, arrayElementExpr);
382 values->push_back(data);
386 if (Z3ValidateModels) {
387 bool success = validateZ3Model(theSolver, theModel);
392 Z3_model_dec_ref(builder->ctx, theModel);
393 return SolverImpl::SOLVER_RUN_STATUS_SUCCESS_SOLVABLE;
397 return SolverImpl::SOLVER_RUN_STATUS_SUCCESS_UNSOLVABLE;
400 ::Z3_solver_get_reason_unknown(builder->ctx, theSolver);
401 if (strcmp(reason,
"timeout") == 0 || strcmp(reason,
"canceled") == 0 ||
402 strcmp(reason,
"(resource limits reached)") == 0) {
403 return SolverImpl::SOLVER_RUN_STATUS_TIMEOUT;
405 if (strcmp(reason,
"unknown") == 0) {
406 return SolverImpl::SOLVER_RUN_STATUS_FAILURE;
408 if (strcmp(reason,
"interrupted from keyboard") == 0) {
409 return SolverImpl::SOLVER_RUN_STATUS_INTERRUPTED;
411 klee_warning(
"Unexpected solver failure. Reason is \"%s,\"\n", reason);
415 llvm_unreachable(
"unhandled Z3 result");
419bool Z3SolverImpl::validateZ3Model(::Z3_solver &theSolver, ::Z3_model &theModel) {
421 ::Z3_ast_vector constraints =
422 Z3_solver_get_assertions(builder->ctx, theSolver);
423 Z3_ast_vector_inc_ref(builder->ctx, constraints);
425 unsigned size = Z3_ast_vector_size(builder->ctx, constraints);
427 for (
unsigned index = 0; index < size; ++index) {
429 Z3_ast_vector_get(builder->ctx, constraints, index), builder->ctx);
431 ::Z3_ast rawEvaluatedExpr;
433 bool successfulEval =
434 Z3_model_eval(builder->ctx, theModel, constraint,
435 Z3_TRUE, &rawEvaluatedExpr);
436 assert(successfulEval &&
"Failed to evaluate model");
439 Z3ASTHandle evaluatedExpr(rawEvaluatedExpr, builder->ctx);
442 Z3SortHandle(Z3_get_sort(builder->ctx, evaluatedExpr), builder->ctx);
443 assert(Z3_get_sort_kind(builder->ctx, sort) == Z3_BOOL_SORT &&
444 "Evaluated expression has wrong sort");
446 Z3_lbool evaluatedValue =
447 Z3_get_bool_value(builder->ctx, evaluatedExpr);
448 if (evaluatedValue != Z3_L_TRUE) {
449 llvm::errs() <<
"Validating model failed:\n"
450 <<
"The expression:\n";
452 llvm::errs() <<
"evaluated to \n";
453 evaluatedExpr.dump();
454 llvm::errs() <<
"But should be true\n";
460 llvm::errs() <<
"Solver state:\n" << Z3_solver_to_string(builder->ctx, theSolver) <<
"\n";
461 llvm::errs() <<
"Model:\n" << Z3_model_to_string(builder->ctx, theModel) <<
"\n";
464 Z3_ast_vector_dec_ref(builder->ctx, constraints);
468SolverImpl::SolverRunStatus Z3SolverImpl::getOperationStatusCode() {
469 return runStatusCode;
void *__dso_handle __attribute__((__weak__))
void klee_warning(char *name)
Statistic queryCounterexamples
void findSymbolicObjects(ref< Expr > e, std::vector< const Array * > &results)
std::unique_ptr< llvm::raw_fd_ostream > klee_open_output_file(const std::string &path, std::string &error)
void klee_message(const char *msg,...) __attribute__((format(printf
Z3NodeHandle< Z3_ast > Z3ASTHandle
void klee_error(const char *msg,...) __attribute__((format(printf
llvm::cl::OptionCategory SolvingCat
Z3NodeHandle< Z3_sort > Z3SortHandle