bool Z3SolverImpl::internalRunSolver( const Query &query, const std::vector<const Array *> *objects, std::vector<std::vector<unsigned char> > *values, bool &hasSolution) { TimerStatIncrementer t(stats::queryTime); // TODO: Does making a new solver for each query have a performance // impact vs making one global solver and using push and pop? // TODO: is the "simple_solver" the right solver to use for // best performance? Z3_solver theSolver = Z3_mk_simple_solver(builder->ctx); Z3_solver_inc_ref(builder->ctx, theSolver); Z3_solver_set_params(builder->ctx, theSolver, solverParameters); runStatusCode = SOLVER_RUN_STATUS_FAILURE; for (ConstraintManager::const_iterator it = query.constraints.begin(), ie = query.constraints.end(); it != ie; ++it) { Z3_solver_assert(builder->ctx, theSolver, builder->construct(*it)); } ++stats::queries; if (objects) ++stats::queryCounterexamples; Z3ASTHandle z3QueryExpr = Z3ASTHandle(builder->construct(query.expr), builder->ctx); // KLEE Queries are validity queries i.e. // ∀ X Constraints(X) → query(X) // but Z3 works in terms of satisfiability so instead we ask the // negation of the equivalent i.e. // ∃ X Constraints(X) ∧ ¬ query(X) Z3_solver_assert( builder->ctx, theSolver, Z3ASTHandle(Z3_mk_not(builder->ctx, z3QueryExpr), builder->ctx)); ::Z3_lbool satisfiable = Z3_solver_check(builder->ctx, theSolver); runStatusCode = handleSolverResponse(theSolver, satisfiable, objects, values, hasSolution); Z3_solver_dec_ref(builder->ctx, theSolver); // Clear the builder's cache to prevent memory usage exploding. // By using ``autoClearConstructCache=false`` and clearning now // we allow Z3_ast expressions to be shared from an entire // ``Query`` rather than only sharing within a single call to // ``builder->construct()``. builder->clearConstructCache(); if (runStatusCode == SolverImpl::SOLVER_RUN_STATUS_SUCCESS_SOLVABLE || runStatusCode == SolverImpl::SOLVER_RUN_STATUS_SUCCESS_UNSOLVABLE) { if (hasSolution) { ++stats::queriesInvalid; } else { ++stats::queriesValid; } return true; // success } return false; // failed }
char *Z3SolverImpl::getConstraintLog(const Query &query) { std::vector<Z3ASTHandle> assumptions; for (std::vector<ref<Expr> >::const_iterator it = query.constraints.begin(), ie = query.constraints.end(); it != ie; ++it) { assumptions.push_back(builder->construct(*it)); } ::Z3_ast *assumptionsArray = NULL; int numAssumptions = query.constraints.size(); if (numAssumptions) { assumptionsArray = (::Z3_ast *)malloc(sizeof(::Z3_ast) * numAssumptions); for (int index = 0; index < numAssumptions; ++index) { assumptionsArray[index] = (::Z3_ast)assumptions[index]; } } // KLEE Queries are validity queries i.e. // ∀ X Constraints(X) → query(X) // but Z3 works in terms of satisfiability so instead we ask the // the negation of the equivalent i.e. // ∃ X Constraints(X) ∧ ¬ query(X) Z3ASTHandle formula = Z3ASTHandle( Z3_mk_not(builder->ctx, builder->construct(query.expr)), builder->ctx); ::Z3_string result = Z3_benchmark_to_smtlib_string( builder->ctx, /*name=*/"Emited by klee::Z3SolverImpl::getConstraintLog()", /*logic=*/"", /*status=*/"unknown", /*attributes=*/"", /*num_assumptions=*/numAssumptions, /*assumptions=*/assumptionsArray, /*formula=*/formula); if (numAssumptions) free(assumptionsArray); // Client is responsible for freeing the returned C-string return strdup(result); }
SolverImpl::SolverRunStatus Z3SolverImpl::handleSolverResponse( ::Z3_solver theSolver, ::Z3_lbool satisfiable, const std::vector<const Array *> *objects, std::vector<std::vector<unsigned char> > *values, bool &hasSolution) { switch (satisfiable) { case Z3_L_TRUE: { hasSolution = true; if (!objects) { // No assignment is needed assert(values == NULL); return SolverImpl::SOLVER_RUN_STATUS_SUCCESS_SOLVABLE; } assert(values && "values cannot be nullptr"); ::Z3_model theModel = Z3_solver_get_model(builder->ctx, theSolver); assert(theModel && "Failed to retrieve model"); Z3_model_inc_ref(builder->ctx, theModel); values->reserve(objects->size()); for (std::vector<const Array *>::const_iterator it = objects->begin(), ie = objects->end(); it != ie; ++it) { const Array *array = *it; std::vector<unsigned char> data; data.reserve(array->size); for (unsigned offset = 0; offset < array->size; offset++) { // We can't use Z3ASTHandle here so have to do ref counting manually ::Z3_ast arrayElementExpr; Z3ASTHandle initial_read = builder->getInitialRead(array, offset); bool successfulEval = Z3_model_eval(builder->ctx, theModel, initial_read, /*model_completion=*/Z3_TRUE, &arrayElementExpr); assert(successfulEval && "Failed to evaluate model"); Z3_inc_ref(builder->ctx, arrayElementExpr); assert(Z3_get_ast_kind(builder->ctx, arrayElementExpr) == Z3_NUMERAL_AST && "Evaluated expression has wrong sort"); int arrayElementValue = 0; bool successGet = Z3_get_numeral_int(builder->ctx, arrayElementExpr, &arrayElementValue); assert(successGet && "failed to get value back"); assert(arrayElementValue >= 0 && arrayElementValue <= 255 && "Integer from model is out of range"); data.push_back(arrayElementValue); Z3_dec_ref(builder->ctx, arrayElementExpr); } values->push_back(data); } Z3_model_dec_ref(builder->ctx, theModel); return SolverImpl::SOLVER_RUN_STATUS_SUCCESS_SOLVABLE; } case Z3_L_FALSE: hasSolution = false; return SolverImpl::SOLVER_RUN_STATUS_SUCCESS_UNSOLVABLE; case Z3_L_UNDEF: { ::Z3_string reason = ::Z3_solver_get_reason_unknown(builder->ctx, theSolver); if (strcmp(reason, "timeout") == 0 || strcmp(reason, "canceled") == 0) { return SolverImpl::SOLVER_RUN_STATUS_TIMEOUT; } if (strcmp(reason, "unknown") == 0) { return SolverImpl::SOLVER_RUN_STATUS_FAILURE; } llvm::errs() << "Unexpected solver failure. Reason is \"" << reason << "\"\n"; abort(); } default: llvm_unreachable("unhandled Z3 result"); } }
bool Z3SolverImpl::internalRunSolver( const Query &query, const std::vector<const Array *> *objects, std::vector<std::vector<unsigned char> > *values, bool &hasSolution) { TimerStatIncrementer t(stats::queryTime); // NOTE: Z3 will switch to using a slower solver internally if push/pop are // used so for now it is likely that creating a new solver each time is the // right way to go until Z3 changes its behaviour. // // TODO: Investigate using a custom tactic as described in // https://github.com/klee/klee/issues/653 Z3_solver theSolver = Z3_mk_solver(builder->ctx); Z3_solver_inc_ref(builder->ctx, theSolver); Z3_solver_set_params(builder->ctx, theSolver, solverParameters); runStatusCode = SOLVER_RUN_STATUS_FAILURE; for (ConstraintManager::const_iterator it = query.constraints.begin(), ie = query.constraints.end(); it != ie; ++it) { Z3_solver_assert(builder->ctx, theSolver, builder->construct(*it)); } ++stats::queries; if (objects) ++stats::queryCounterexamples; Z3ASTHandle z3QueryExpr = Z3ASTHandle(builder->construct(query.expr), builder->ctx); // KLEE Queries are validity queries i.e. // ∀ X Constraints(X) → query(X) // but Z3 works in terms of satisfiability so instead we ask the // negation of the equivalent i.e. // ∃ X Constraints(X) ∧ ¬ query(X) Z3_solver_assert( builder->ctx, theSolver, Z3ASTHandle(Z3_mk_not(builder->ctx, z3QueryExpr), builder->ctx)); if (dumpedQueriesFile) { *dumpedQueriesFile << "; start Z3 query\n"; *dumpedQueriesFile << Z3_solver_to_string(builder->ctx, theSolver); *dumpedQueriesFile << "(check-sat)\n"; *dumpedQueriesFile << "(reset)\n"; *dumpedQueriesFile << "; end Z3 query\n\n"; dumpedQueriesFile->flush(); } ::Z3_lbool satisfiable = Z3_solver_check(builder->ctx, theSolver); runStatusCode = handleSolverResponse(theSolver, satisfiable, objects, values, hasSolution); Z3_solver_dec_ref(builder->ctx, theSolver); // Clear the builder's cache to prevent memory usage exploding. // By using ``autoClearConstructCache=false`` and clearning now // we allow Z3_ast expressions to be shared from an entire // ``Query`` rather than only sharing within a single call to // ``builder->construct()``. builder->clearConstructCache(); if (runStatusCode == SolverImpl::SOLVER_RUN_STATUS_SUCCESS_SOLVABLE || runStatusCode == SolverImpl::SOLVER_RUN_STATUS_SUCCESS_UNSOLVABLE) { if (hasSolution) { ++stats::queriesInvalid; } else { ++stats::queriesValid; } return true; // success } return false; // failed }