klee
MetaSMTSolver.cpp
Go to the documentation of this file.
1//===-- MetaSMTSolver.cpp -------------------------------------------------===//
2//
3// The KLEE Symbolic Virtual Machine
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include "klee/Config/config.h"
10
11#ifdef ENABLE_METASMT
12#include "MetaSMTSolver.h"
13#include "MetaSMTBuilder.h"
14
17#include "klee/Expr/ExprUtil.h"
19#include "klee/Solver/Solver.h"
21
22#include "llvm/Support/ErrorHandling.h"
23
24#include <metaSMT/DirectSolver_Context.hpp>
25
26#ifdef METASMT_HAVE_Z3
27#include <metaSMT/backend/Z3_Backend.hpp>
28#endif
29
30#ifdef METASMT_HAVE_BTOR
31#include <metaSMT/backend/Boolector.hpp>
32#endif
33
34#ifdef METASMT_HAVE_CVC4
35#include <metaSMT/backend/CVC4.hpp>
36#endif
37
38#ifdef METASMT_HAVE_YICES2
39#include <metaSMT/backend/Yices2.hpp>
40#endif
41
42#ifdef METASMT_HAVE_STP
43#define Expr VCExpr
44#define Type VCType
45#define STP STP_Backend
46#define type_t STP_type_t
47#include <metaSMT/backend/STP.hpp>
48#undef Expr
49#undef Type
50#undef STP
51#undef type_t
52#endif
53
54#include <errno.h>
55#include <signal.h>
56#include <sys/ipc.h>
57#include <sys/shm.h>
58#include <sys/wait.h>
59#include <unistd.h>
60
61static unsigned char *shared_memory_ptr;
62static int shared_memory_id = 0;
63// Darwin by default has a very small limit on the maximum amount of shared
64// memory, which will quickly be exhausted by KLEE running its tests in
65// parallel. For now, we work around this by just requesting a smaller size --
66// in practice users hitting this limit on counterexample sizes probably already
67// are hitting more serious scalability issues.
68#ifdef __APPLE__
69static const unsigned shared_memory_size = 1 << 16;
70#else
71static const unsigned shared_memory_size = 1 << 20;
72#endif
73
74namespace klee {
75
76template <typename SolverContext> class MetaSMTSolverImpl : public SolverImpl {
77private:
78 SolverContext _meta_solver;
79 MetaSMTSolver<SolverContext> *_solver;
80 MetaSMTBuilder<SolverContext> *_builder;
81 time::Span _timeout;
82 bool _useForked;
83 SolverRunStatus _runStatusCode;
84
85public:
86 MetaSMTSolverImpl(MetaSMTSolver<SolverContext> *solver, bool useForked,
87 bool optimizeDivides);
88 virtual ~MetaSMTSolverImpl();
89
90 char *getConstraintLog(const Query &);
91 void setCoreSolverTimeout(time::Span timeout) { _timeout = timeout; }
92
93 bool computeTruth(const Query &, bool &isValid);
94 bool computeValue(const Query &, ref<Expr> &result);
95
96 bool computeInitialValues(const Query &query,
97 const std::vector<const Array *> &objects,
98 std::vector<std::vector<unsigned char> > &values,
99 bool &hasSolution);
100
102 runAndGetCex(const Query &query, const std::vector<const Array *> &objects,
103 std::vector<std::vector<unsigned char> > &values,
104 bool &hasSolution);
105
107 runAndGetCexForked(const Query &query,
108 const std::vector<const Array *> &objects,
109 std::vector<std::vector<unsigned char> > &values,
110 bool &hasSolution, time::Span timeout);
111
112 SolverRunStatus getOperationStatusCode();
113
114 SolverContext &get_meta_solver() { return _meta_solver; };
115};
116
117template <typename SolverContext>
118MetaSMTSolverImpl<SolverContext>::MetaSMTSolverImpl(
119 MetaSMTSolver<SolverContext> *solver, bool useForked, bool optimizeDivides)
120 : _solver(solver), _builder(new MetaSMTBuilder<SolverContext>(
121 _meta_solver, optimizeDivides)),
122 _useForked(useForked) {
123 assert(_solver && "unable to create MetaSMTSolver");
124 assert(_builder && "unable to create MetaSMTBuilder");
125
126 if (_useForked) {
127 shared_memory_id =
128 shmget(IPC_PRIVATE, shared_memory_size, IPC_CREAT | 0700);
129 assert(shared_memory_id >= 0 && "shmget failed");
130 shared_memory_ptr = (unsigned char *)shmat(shared_memory_id, NULL, 0);
131 assert(shared_memory_ptr != (void *)-1 && "shmat failed");
132 shmctl(shared_memory_id, IPC_RMID, NULL);
133 }
134}
135
136template <typename SolverContext>
137MetaSMTSolverImpl<SolverContext>::~MetaSMTSolverImpl() {}
138
139template <typename SolverContext>
140char *MetaSMTSolverImpl<SolverContext>::getConstraintLog(const Query &) {
141 const char *msg = "Not supported";
142 char *buf = new char[strlen(msg) + 1];
143 strcpy(buf, msg);
144 return buf;
145}
146
147template <typename SolverContext>
148bool MetaSMTSolverImpl<SolverContext>::computeTruth(const Query &query,
149 bool &isValid) {
150
151 bool success = false;
152 std::vector<const Array *> objects;
153 std::vector<std::vector<unsigned char> > values;
154 bool hasSolution;
155
156 if (computeInitialValues(query, objects, values, hasSolution)) {
157 // query.expr is valid iff !query.expr is not satisfiable
158 isValid = !hasSolution;
159 success = true;
160 }
161
162 return success;
163}
164
165template <typename SolverContext>
166bool MetaSMTSolverImpl<SolverContext>::computeValue(const Query &query,
167 ref<Expr> &result) {
168
169 bool success = false;
170 std::vector<const Array *> objects;
171 std::vector<std::vector<unsigned char> > values;
172 bool hasSolution;
173
174 // Find the object used in the expression, and compute an assignment for them.
175 findSymbolicObjects(query.expr, objects);
176 if (computeInitialValues(query.withFalse(), objects, values, hasSolution)) {
177 assert(hasSolution && "state has invalid constraint set");
178 // Evaluate the expression with the computed assignment.
179 Assignment a(objects, values);
180 result = a.evaluate(query.expr);
181 success = true;
182 }
183
184 return success;
185}
186
187template <typename SolverContext>
188bool MetaSMTSolverImpl<SolverContext>::computeInitialValues(
189 const Query &query, const std::vector<const Array *> &objects,
190 std::vector<std::vector<unsigned char> > &values, bool &hasSolution) {
191
192 _runStatusCode = SOLVER_RUN_STATUS_FAILURE;
193
194 TimerStatIncrementer t(stats::queryTime);
195 assert(_builder);
196
199
200 bool success = true;
201 if (_useForked) {
202 _runStatusCode =
203 runAndGetCexForked(query, objects, values, hasSolution, _timeout);
204 success = ((SOLVER_RUN_STATUS_SUCCESS_SOLVABLE == _runStatusCode) ||
205 (SOLVER_RUN_STATUS_SUCCESS_UNSOLVABLE == _runStatusCode));
206 } else {
207 _runStatusCode = runAndGetCex(query, objects, values, hasSolution);
208 success = true;
209 }
210
211 if (success) {
212 if (hasSolution) {
214 } else {
216 }
217 }
218
219 return success;
220}
221
222template <typename SolverContext>
223SolverImpl::SolverRunStatus MetaSMTSolverImpl<SolverContext>::runAndGetCex(
224 const Query &query, const std::vector<const Array *> &objects,
225 std::vector<std::vector<unsigned char> > &values, bool &hasSolution) {
226
227 // assume the constraints of the query
228 for (auto &constraint : query.constraints)
229 assumption(_meta_solver, _builder->construct(constraint));
230
231 // assume the negation of the query
232 assumption(_meta_solver, _builder->construct(Expr::createIsZero(query.expr)));
233 hasSolution = solve(_meta_solver);
234
235 if (hasSolution) {
236 values.reserve(objects.size());
237 for (std::vector<const Array *>::const_iterator it = objects.begin(),
238 ie = objects.end();
239 it != ie; ++it) {
240
241 const Array *array = *it;
242 assert(array);
243 typename SolverContext::result_type array_exp =
244 _builder->getInitialArray(array);
245
246 std::vector<unsigned char> data;
247 data.reserve(array->size);
248
249 for (unsigned offset = 0; offset < array->size; offset++) {
250 typename SolverContext::result_type elem_exp = evaluate(
251 _meta_solver, metaSMT::logic::Array::select(
252 array_exp, bvuint(offset, array->getDomain())));
253 unsigned char elem_value = metaSMT::read_value(_meta_solver, elem_exp);
254 data.push_back(elem_value);
255 }
256
257 values.push_back(data);
258 }
259 }
260
261 if (true == hasSolution) {
262 return SolverImpl::SOLVER_RUN_STATUS_SUCCESS_SOLVABLE;
263 } else {
264 return SolverImpl::SOLVER_RUN_STATUS_SUCCESS_UNSOLVABLE;
265 }
266}
267
268static void metaSMTTimeoutHandler(int x) { _exit(52); }
269
270template <typename SolverContext>
271SolverImpl::SolverRunStatus
272MetaSMTSolverImpl<SolverContext>::runAndGetCexForked(
273 const Query &query, const std::vector<const Array *> &objects,
274 std::vector<std::vector<unsigned char> > &values, bool &hasSolution,
275 time::Span timeout) {
276 unsigned char *pos = shared_memory_ptr;
277 unsigned sum = 0;
278 for (std::vector<const Array *>::const_iterator it = objects.begin(),
279 ie = objects.end();
280 it != ie; ++it) {
281 sum += (*it)->size;
282 }
283 // sum += sizeof(uint64_t);
284 sum += sizeof(stats::queryConstructs);
285 assert(sum < shared_memory_size &&
286 "not enough shared memory for counterexample");
287
288 fflush(stdout);
289 fflush(stderr);
290 int pid = fork();
291 if (pid == -1) {
292 klee_warning("fork failed (for metaSMT)");
293 return SolverImpl::SOLVER_RUN_STATUS_FORK_FAILED;
294 }
295
296 if (pid == 0) {
297 if (timeout) {
298 ::alarm(0); /* Turn off alarm so we can safely set signal handler */
299 ::signal(SIGALRM, metaSMTTimeoutHandler);
300 ::alarm(std::max(1u, static_cast<unsigned>(timeout.toSeconds())));
301 }
302
303 // assert constraints as we are in a child process
304 for (const auto &constraint : query.constraints) {
305 assertion(_meta_solver, _builder->construct(constraint));
306 // assumption(_meta_solver, _builder->construct(*it));
307 }
308
309 // asssert the negation of the query as we are in a child process
310 assertion(_meta_solver,
311 _builder->construct(Expr::createIsZero(query.expr)));
312 unsigned res = solve(_meta_solver);
313
314 if (res) {
315 for (std::vector<const Array *>::const_iterator it = objects.begin(),
316 ie = objects.end();
317 it != ie; ++it) {
318
319 const Array *array = *it;
320 assert(array);
321 typename SolverContext::result_type array_exp =
322 _builder->getInitialArray(array);
323
324 for (unsigned offset = 0; offset < array->size; offset++) {
325
326 typename SolverContext::result_type elem_exp = evaluate(
327 _meta_solver, metaSMT::logic::Array::select(
328 array_exp, bvuint(offset, array->getDomain())));
329 unsigned char elem_value =
330 metaSMT::read_value(_meta_solver, elem_exp);
331 *pos++ = elem_value;
332 }
333 }
334 }
335 assert((uint64_t *)pos);
336 *((uint64_t *)pos) = stats::queryConstructs;
337
338 _exit(!res);
339 } else {
340 int status;
341 pid_t res;
342
343 do {
344 res = waitpid(pid, &status, 0);
345 } while (res < 0 && errno == EINTR);
346
347 if (res < 0) {
348 klee_warning("waitpid() for metaSMT failed");
349 return SolverImpl::SOLVER_RUN_STATUS_WAITPID_FAILED;
350 }
351
352 // From timed_run.py: It appears that linux at least will on
353 // "occasion" return a status when the process was terminated by a
354 // signal, so test signal first.
355 if (WIFSIGNALED(status) || !WIFEXITED(status)) {
357 "error: metaSMT did not return successfully (status = %d) \n",
358 WTERMSIG(status));
359 return SolverImpl::SOLVER_RUN_STATUS_INTERRUPTED;
360 }
361
362 int exitcode = WEXITSTATUS(status);
363 if (exitcode == 0) {
364 hasSolution = true;
365 } else if (exitcode == 1) {
366 hasSolution = false;
367 } else if (exitcode == 52) {
368 klee_warning("metaSMT timed out");
369 return SolverImpl::SOLVER_RUN_STATUS_TIMEOUT;
370 } else {
371 klee_warning("metaSMT did not return a recognized code");
372 return SolverImpl::SOLVER_RUN_STATUS_UNEXPECTED_EXIT_CODE;
373 }
374
375 if (hasSolution) {
376 values = std::vector<std::vector<unsigned char> >(objects.size());
377 unsigned i = 0;
378 for (std::vector<const Array *>::const_iterator it = objects.begin(),
379 ie = objects.end();
380 it != ie; ++it) {
381 const Array *array = *it;
382 assert(array);
383 std::vector<unsigned char> &data = values[i++];
384 data.insert(data.begin(), pos, pos + array->size);
385 pos += array->size;
386 }
387 }
388 stats::queryConstructs += (*((uint64_t *)pos) - stats::queryConstructs);
389
390 if (true == hasSolution) {
391 return SolverImpl::SOLVER_RUN_STATUS_SUCCESS_SOLVABLE;
392 } else {
393 return SolverImpl::SOLVER_RUN_STATUS_SUCCESS_UNSOLVABLE;
394 }
395 }
396}
397
398template <typename SolverContext>
399SolverImpl::SolverRunStatus
400MetaSMTSolverImpl<SolverContext>::getOperationStatusCode() {
401 return _runStatusCode;
402}
403
404template <typename SolverContext>
405MetaSMTSolver<SolverContext>::MetaSMTSolver(bool useForked,
406 bool optimizeDivides)
407 : Solver(new MetaSMTSolverImpl<SolverContext>(this, useForked,
408 optimizeDivides)) {}
409
410template <typename SolverContext>
411MetaSMTSolver<SolverContext>::~MetaSMTSolver() {}
412
413template <typename SolverContext>
414char *MetaSMTSolver<SolverContext>::getConstraintLog(const Query &query) {
415 return impl->getConstraintLog(query);
416}
417
418template <typename SolverContext>
419void MetaSMTSolver<SolverContext>::setCoreSolverTimeout(time::Span timeout) {
420 impl->setCoreSolverTimeout(timeout);
421}
422
423Solver *createMetaSMTSolver() {
424 using namespace metaSMT;
425
426 Solver *coreSolver = NULL;
427 std::string backend;
428 switch (MetaSMTBackend) {
429#ifdef METASMT_HAVE_STP
430 case METASMT_BACKEND_STP:
431 backend = "STP";
432 coreSolver = new MetaSMTSolver<DirectSolver_Context<solver::STP_Backend> >(
434 break;
435#endif
436#ifdef METASMT_HAVE_Z3
437 case METASMT_BACKEND_Z3:
438 backend = "Z3";
439 coreSolver = new MetaSMTSolver<DirectSolver_Context<solver::Z3_Backend> >(
441 break;
442#endif
443#ifdef METASMT_HAVE_BTOR
444 case METASMT_BACKEND_BOOLECTOR:
445 backend = "Boolector";
446 coreSolver = new MetaSMTSolver<DirectSolver_Context<solver::Boolector> >(
448 break;
449#endif
450#ifdef METASMT_HAVE_CVC4
451 case METASMT_BACKEND_CVC4:
452 backend = "CVC4";
453 coreSolver = new MetaSMTSolver<DirectSolver_Context<solver::CVC4> >(
455 break;
456#endif
457#ifdef METASMT_HAVE_YICES2
458 case METASMT_BACKEND_YICES2:
459 backend = "Yices2";
460 coreSolver = new MetaSMTSolver<DirectSolver_Context<solver::Yices2> >(
462 break;
463#endif
464 default:
465 llvm_unreachable("Unrecognised MetaSMT backend");
466 break;
467 };
468 klee_message("Starting MetaSMTSolver(%s)", backend.c_str());
469 return coreSolver;
470}
471
472}
473#endif // ENABLE_METASMT
void klee_warning(char *name)
Definition: klee-replay.c:432
Statistic queryConstructs
Statistic queryTime
Statistic queriesInvalid
Statistic queriesValid
Statistic queryCounterexamples
Statistic queries
Definition: main.cpp:291
void findSymbolicObjects(ref< Expr > e, std::vector< const Array * > &results)
Definition: ExprUtil.cpp:132
Solver * createMetaSMTSolver()
void klee_message(const char *msg,...) __attribute__((format(printf
llvm::cl::opt< bool > UseForkedCoreSolver
llvm::cl::opt< bool > CoreSolverOptimizeDivides