Solver.cpp

Go to the documentation of this file.
00001 //===-- Solver.cpp --------------------------------------------------------===//
00002 //
00003 //                     The KLEE Symbolic Virtual Machine
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 
00010 #include "klee/Solver.h"
00011 #include "klee/SolverImpl.h"
00012 
00013 #include "SolverStats.h"
00014 #include "STPBuilder.h"
00015 
00016 #include "klee/Constraints.h"
00017 #include "klee/Expr.h"
00018 #include "klee/TimerStatIncrementer.h"
00019 #include "klee/util/Assignment.h"
00020 #include "klee/util/ExprPPrinter.h"
00021 #include "klee/util/ExprUtil.h"
00022 #include "klee/Internal/Support/Timer.h"
00023 
00024 #define vc_bvBoolExtract IAMTHESPAWNOFSATAN
00025 
00026 #include <cassert>
00027 #include <cstdio>
00028 #include <map>
00029 #include <vector>
00030 
00031 #include <signal.h>
00032 #include <sys/wait.h>
00033 #include <sys/ipc.h>
00034 #include <sys/shm.h>
00035 
00036 using namespace klee;
00037 
00038 /***/
00039 
00040 const char *Solver::validity_to_str(Validity v) {
00041   switch (v) {
00042   default:    return "Unknown";
00043   case True:  return "True";
00044   case False: return "False";
00045   }
00046 }
00047 
00048 Solver::~Solver() { 
00049   delete impl; 
00050 }
00051 
00052 SolverImpl::~SolverImpl() {
00053 }
00054 
00055 bool Solver::evaluate(const Query& query, Validity &result) {
00056   assert(query.expr->getWidth() == Expr::Bool && "Invalid expression type!");
00057 
00058   // Maintain invariants implementations expect.
00059   if (ConstantExpr *CE = dyn_cast<ConstantExpr>(query.expr)) {
00060     result = CE->getConstantValue() ? True : False;
00061     return true;
00062   }
00063 
00064   return impl->computeValidity(query, result);
00065 }
00066 
00067 bool SolverImpl::computeValidity(const Query& query, Solver::Validity &result) {
00068   bool isTrue, isFalse;
00069   if (!computeTruth(query, isTrue))
00070     return false;
00071   if (isTrue) {
00072     result = Solver::True;
00073   } else {
00074     if (!computeTruth(query.negateExpr(), isFalse))
00075       return false;
00076     result = isFalse ? Solver::False : Solver::Unknown;
00077   }
00078   return true;
00079 }
00080 
00081 bool Solver::mustBeTrue(const Query& query, bool &result) {
00082   assert(query.expr->getWidth() == Expr::Bool && "Invalid expression type!");
00083 
00084   // Maintain invariants implementations expect.
00085   if (ConstantExpr *CE = dyn_cast<ConstantExpr>(query.expr)) {
00086     result = CE->getConstantValue() ? true : false;
00087     return true;
00088   }
00089 
00090   return impl->computeTruth(query, result);
00091 }
00092 
00093 bool Solver::mustBeFalse(const Query& query, bool &result) {
00094   return mustBeTrue(query.negateExpr(), result);
00095 }
00096 
00097 bool Solver::mayBeTrue(const Query& query, bool &result) {
00098   bool res;
00099   if (!mustBeFalse(query, res))
00100     return false;
00101   result = !res;
00102   return true;
00103 }
00104 
00105 bool Solver::mayBeFalse(const Query& query, bool &result) {
00106   bool res;
00107   if (!mustBeTrue(query, res))
00108     return false;
00109   result = !res;
00110   return true;
00111 }
00112 
00113 bool Solver::getValue(const Query& query, ref<ConstantExpr> &result) {
00114   // Maintain invariants implementation expect.
00115   if (ConstantExpr *CE = dyn_cast<ConstantExpr>(query.expr)) {
00116     result = CE;
00117     return true;
00118   }
00119 
00120   // FIXME: Push ConstantExpr requirement down.
00121   ref<Expr> tmp;
00122   if (!impl->computeValue(query, tmp))
00123     return false;
00124   
00125   result = cast<ConstantExpr>(tmp);
00126   return true;
00127 }
00128 
00129 bool 
00130 Solver::getInitialValues(const Query& query,
00131                          const std::vector<const Array*> &objects,
00132                          std::vector< std::vector<unsigned char> > &values) {
00133   bool hasSolution;
00134   bool success =
00135     impl->computeInitialValues(query, objects, values, hasSolution);
00136   // FIXME: Propogate this out.
00137   if (!hasSolution)
00138     return false;
00139     
00140   return success;
00141 }
00142 
00143 std::pair< ref<Expr>, ref<Expr> > Solver::getRange(const Query& query) {
00144   ref<Expr> e = query.expr;
00145   Expr::Width width = e->getWidth();
00146   uint64_t min, max;
00147 
00148   if (width==1) {
00149     Solver::Validity result;
00150     if (!evaluate(query, result))
00151       assert(0 && "computeValidity failed");
00152     switch (result) {
00153     case Solver::True: 
00154       min = max = 1; break;
00155     case Solver::False: 
00156       min = max = 0; break;
00157     default:
00158       min = 0, max = 1; break;
00159     }
00160   } else if (ConstantExpr *CE = dyn_cast<ConstantExpr>(e)) {
00161     min = max = CE->getConstantValue();
00162   } else {
00163     // binary search for # of useful bits
00164     uint64_t lo=0, hi=width, mid, bits=0;
00165     while (lo<hi) {
00166       mid = (lo+hi)/2;
00167       bool res;
00168       bool success = 
00169         mustBeTrue(query.withExpr(
00170                      EqExpr::create(LShrExpr::create(e,
00171                                                      ConstantExpr::create(mid, 
00172                                                                           width)),
00173                                     ConstantExpr::create(0, width))),
00174                    res);
00175 
00176       assert(success && "FIXME: Unhandled solver failure");
00177       (void) success;
00178 
00179       if (res) {
00180         hi = mid;
00181       } else {
00182         lo = mid+1;
00183       }
00184 
00185       bits = lo;
00186     }
00187     
00188     // could binary search for training zeros and offset
00189     // min max but unlikely to be very useful
00190 
00191     // check common case
00192     bool res = false;
00193     bool success = 
00194       mayBeTrue(query.withExpr(EqExpr::create(e, ConstantExpr::create(0, 
00195                                                                       width))), 
00196                 res);
00197 
00198     assert(success && "FIXME: Unhandled solver failure");      
00199     (void) success;
00200 
00201     if (res) {
00202       min = 0;
00203     } else {
00204       // binary search for min
00205       lo=0, hi=bits64::maxValueOfNBits(bits);
00206       while (lo<hi) {
00207         mid = (lo+hi)/2;
00208         bool res = false;
00209         bool success = 
00210           mayBeTrue(query.withExpr(UleExpr::create(e, 
00211                                                    ConstantExpr::create(mid, 
00212                                                                         width))),
00213                     res);
00214 
00215         assert(success && "FIXME: Unhandled solver failure");      
00216         (void) success;
00217 
00218         if (res) {
00219           hi = mid;
00220         } else {
00221           lo = mid+1;
00222         }
00223       }
00224 
00225       min = lo;
00226     }
00227 
00228     // binary search for max
00229     lo=min, hi=bits64::maxValueOfNBits(bits);
00230     while (lo<hi) {
00231       mid = (lo+hi)/2;
00232       bool res;
00233       bool success = 
00234         mustBeTrue(query.withExpr(UleExpr::create(e, 
00235                                                   ConstantExpr::create(mid, 
00236                                                                        width))),
00237                    res);
00238 
00239       assert(success && "FIXME: Unhandled solver failure");      
00240       (void) success;
00241 
00242       if (res) {
00243         hi = mid;
00244       } else {
00245         lo = mid+1;
00246       }
00247     }
00248 
00249     max = lo;
00250   }
00251 
00252   return std::make_pair(ConstantExpr::create(min, width),
00253                         ConstantExpr::create(max, width));
00254 }
00255 
00256 /***/
00257 
00258 class ValidatingSolver : public SolverImpl {
00259 private:
00260   Solver *solver, *oracle;
00261 
00262 public: 
00263   ValidatingSolver(Solver *_solver, Solver *_oracle) 
00264     : solver(_solver), oracle(_oracle) {}
00265   ~ValidatingSolver() { delete solver; }
00266   
00267   bool computeValidity(const Query&, Solver::Validity &result);
00268   bool computeTruth(const Query&, bool &isValid);
00269   bool computeValue(const Query&, ref<Expr> &result);
00270   bool computeInitialValues(const Query&,
00271                             const std::vector<const Array*> &objects,
00272                             std::vector< std::vector<unsigned char> > &values,
00273                             bool &hasSolution);
00274 };
00275 
00276 bool ValidatingSolver::computeTruth(const Query& query,
00277                                     bool &isValid) {
00278   bool answer;
00279   
00280   if (!solver->impl->computeTruth(query, isValid))
00281     return false;
00282   if (!oracle->impl->computeTruth(query, answer))
00283     return false;
00284   
00285   if (isValid != answer)
00286     assert(0 && "invalid solver result (computeTruth)");
00287   
00288   return true;
00289 }
00290 
00291 bool ValidatingSolver::computeValidity(const Query& query,
00292                                        Solver::Validity &result) {
00293   Solver::Validity answer;
00294   
00295   if (!solver->impl->computeValidity(query, result))
00296     return false;
00297   if (!oracle->impl->computeValidity(query, answer))
00298     return false;
00299   
00300   if (result != answer)
00301     assert(0 && "invalid solver result (computeValidity)");
00302   
00303   return true;
00304 }
00305 
00306 bool ValidatingSolver::computeValue(const Query& query,
00307                                     ref<Expr> &result) {  
00308   bool answer;
00309 
00310   if (!solver->impl->computeValue(query, result))
00311     return false;
00312   // We don't want to compare, but just make sure this is a legal
00313   // solution.
00314   if (!oracle->impl->computeTruth(query.withExpr(NeExpr::create(query.expr, 
00315                                                                 result)),
00316                                   answer))
00317     return false;
00318 
00319   if (answer)
00320     assert(0 && "invalid solver result (computeValue)");
00321 
00322   return true;
00323 }
00324 
00325 bool 
00326 ValidatingSolver::computeInitialValues(const Query& query,
00327                                        const std::vector<const Array*>
00328                                          &objects,
00329                                        std::vector< std::vector<unsigned char> >
00330                                          &values,
00331                                        bool &hasSolution) {
00332   bool answer;
00333 
00334   if (!solver->impl->computeInitialValues(query, objects, values, 
00335                                           hasSolution))
00336     return false;
00337 
00338   if (hasSolution) {
00339     // Assert the bindings as constraints, and verify that the
00340     // conjunction of the actual constraints is satisfiable.
00341     std::vector< ref<Expr> > bindings;
00342     for (unsigned i = 0; i != values.size(); ++i) {
00343       const Array *array = objects[i];
00344       for (unsigned j=0; j<array->size; j++) {
00345         unsigned char value = values[i][j];
00346         bindings.push_back(EqExpr::create(ReadExpr::create(UpdateList(array,
00347                                                                       true, 0),
00348                                                            ConstantExpr::alloc(j, Expr::Int32)),
00349                                           ConstantExpr::alloc(value, Expr::Int8)));
00350       }
00351     }
00352     ConstraintManager tmp(bindings);
00353     ref<Expr> constraints = Expr::createNot(query.expr);
00354     for (ConstraintManager::const_iterator it = query.constraints.begin(), 
00355            ie = query.constraints.end(); it != ie; ++it)
00356       constraints = AndExpr::create(constraints, *it);
00357     
00358     if (!oracle->impl->computeTruth(Query(tmp, constraints), answer))
00359       return false;
00360     if (!answer)
00361       assert(0 && "invalid solver result (computeInitialValues)");
00362   } else {
00363     if (!oracle->impl->computeTruth(query, answer))
00364       return false;
00365     if (!answer)
00366       assert(0 && "invalid solver result (computeInitialValues)");    
00367   }
00368 
00369   return true;
00370 }
00371 
00372 Solver *klee::createValidatingSolver(Solver *s, Solver *oracle) {
00373   return new Solver(new ValidatingSolver(s, oracle));
00374 }
00375 
00376 /***/
00377 
00378 class STPSolverImpl : public SolverImpl {
00379 private:
00381   STPSolver *solver;
00382   VC vc;
00383   STPBuilder *builder;
00384   double timeout;
00385   bool useForkedSTP;
00386 
00387 public:
00388   STPSolverImpl(STPSolver *_solver, bool _useForkedSTP);
00389   ~STPSolverImpl();
00390 
00391   char *getConstraintLog(const Query&);
00392   void setTimeout(double _timeout) { timeout = _timeout; }
00393 
00394   bool computeTruth(const Query&, bool &isValid);
00395   bool computeValue(const Query&, ref<Expr> &result);
00396   bool computeInitialValues(const Query&,
00397                             const std::vector<const Array*> &objects,
00398                             std::vector< std::vector<unsigned char> > &values,
00399                             bool &hasSolution);
00400 };
00401 
00402 static unsigned char *shared_memory_ptr;
00403 static const unsigned shared_memory_size = 1<<20;
00404 static int shared_memory_id;
00405 
00406 static void stp_error_handler(const char* err_msg) {
00407   fprintf(stderr, "error: STP Error: %s\n", err_msg);
00408   abort();
00409 }
00410 
00411 STPSolverImpl::STPSolverImpl(STPSolver *_solver, bool _useForkedSTP) 
00412   : solver(_solver),
00413     vc(vc_createValidityChecker()),
00414     builder(new STPBuilder(vc)),
00415     timeout(0.0),
00416     useForkedSTP(_useForkedSTP)
00417 {
00418   assert(vc && "unable to create validity checker");
00419   assert(builder && "unable to create STPBuilder");
00420 
00421   vc_registerErrorHandler(::stp_error_handler);
00422 
00423   if (useForkedSTP) {
00424     shared_memory_id = shmget(IPC_PRIVATE, shared_memory_size, IPC_CREAT | 0700);
00425     assert(shared_memory_id>=0 && "shmget failed");
00426     shared_memory_ptr = (unsigned char*) shmat(shared_memory_id, NULL, 0);
00427     assert(shared_memory_ptr!=(void*)-1 && "shmat failed");
00428     shmctl(shared_memory_id, IPC_RMID, NULL);
00429   }
00430 }
00431 
00432 STPSolverImpl::~STPSolverImpl() {
00433   delete builder;
00434 
00435   vc_Destroy(vc);
00436 }
00437 
00438 /***/
00439 
00440 STPSolver::STPSolver(bool useForkedSTP) 
00441   : Solver(new STPSolverImpl(this, useForkedSTP))
00442 {
00443 }
00444 
00445 char *STPSolver::getConstraintLog(const Query &query) {
00446   return static_cast<STPSolverImpl*>(impl)->getConstraintLog(query);  
00447 }
00448 
00449 void STPSolver::setTimeout(double timeout) {
00450   static_cast<STPSolverImpl*>(impl)->setTimeout(timeout);
00451 }
00452 
00453 /***/
00454 
00455 char *STPSolverImpl::getConstraintLog(const Query &query) {
00456   vc_push(vc);
00457   for (std::vector< ref<Expr> >::const_iterator it = query.constraints.begin(), 
00458          ie = query.constraints.end(); it != ie; ++it)
00459     vc_assertFormula(vc, builder->construct(*it));
00460   assert(query.expr == ConstantExpr::alloc(0, Expr::Bool) &&
00461          "Unexpected expression in query!");
00462 
00463   char *buffer;
00464   unsigned long length;
00465   vc_printQueryStateToBuffer(vc, builder->getFalse(), 
00466                              &buffer, &length, false);
00467   vc_pop(vc);
00468 
00469   return buffer;
00470 }
00471 
00472 bool STPSolverImpl::computeTruth(const Query& query,
00473                                  bool &isValid) {
00474   std::vector<const Array*> objects;
00475   std::vector< std::vector<unsigned char> > values;
00476   bool hasSolution;
00477 
00478   if (!computeInitialValues(query, objects, values, hasSolution))
00479     return false;
00480 
00481   isValid = !hasSolution;
00482   return true;
00483 }
00484 
00485 bool STPSolverImpl::computeValue(const Query& query,
00486                                  ref<Expr> &result) {
00487   std::vector<const Array*> objects;
00488   std::vector< std::vector<unsigned char> > values;
00489   bool hasSolution;
00490 
00491   // Find the object used in the expression, and compute an assignment
00492   // for them.
00493   findSymbolicObjects(query.expr, objects);
00494   if (!computeInitialValues(query.withFalse(), objects, values, hasSolution))
00495     return false;
00496   assert(hasSolution && "state has invalid constraint set");
00497   
00498   // Evaluate the expression with the computed assignment.
00499   Assignment a(objects, values);
00500   result = a.evaluate(query.expr);
00501 
00502   return true;
00503 }
00504 
00505 static void runAndGetCex(::VC vc, STPBuilder *builder, ::VCExpr q,
00506                    const std::vector<const Array*> &objects,
00507                    std::vector< std::vector<unsigned char> > &values,
00508                    bool &hasSolution) {
00509   // XXX I want to be able to timeout here, safely
00510   hasSolution = !vc_query(vc, q);
00511 
00512   if (hasSolution) {
00513     values.reserve(objects.size());
00514     for (std::vector<const Array*>::const_iterator
00515            it = objects.begin(), ie = objects.end(); it != ie; ++it) {
00516       const Array *array = *it;
00517       std::vector<unsigned char> data;
00518       
00519       data.reserve(array->size);
00520       for (unsigned offset = 0; offset < array->size; offset++) {
00521         ExprHandle counter = 
00522           vc_getCounterExample(vc, builder->getInitialRead(array, offset));
00523         unsigned char val = getBVUnsigned(counter);
00524         data.push_back(val);
00525       }
00526       
00527       values.push_back(data);
00528     }
00529   }
00530 }
00531 
00532 static void stpTimeoutHandler(int x) {
00533   _exit(52);
00534 }
00535 
00536 static bool runAndGetCexForked(::VC vc, 
00537                                STPBuilder *builder,
00538                                ::VCExpr q,
00539                                const std::vector<const Array*> &objects,
00540                                std::vector< std::vector<unsigned char> >
00541                                  &values,
00542                                bool &hasSolution,
00543                                double timeout) {
00544   unsigned char *pos = shared_memory_ptr;
00545   unsigned sum = 0;
00546   for (std::vector<const Array*>::const_iterator
00547          it = objects.begin(), ie = objects.end(); it != ie; ++it)
00548     sum += (*it)->size;
00549   assert(sum<shared_memory_size && "not enough shared memory for counterexample");
00550 
00551   fflush(stdout);
00552   fflush(stderr);
00553   int pid = fork();
00554   if (pid==-1) {
00555     fprintf(stderr, "error: fork failed (for STP)");
00556     return false;
00557   }
00558 
00559   if (pid == 0) {
00560     if (timeout) {
00561       ::alarm(0); /* Turn off alarm so we can safely set signal handler */
00562       ::signal(SIGALRM, stpTimeoutHandler);
00563       ::alarm(std::max(1, (int)timeout));
00564     }    
00565     unsigned res = vc_query(vc, q);
00566     if (!res) {
00567       for (std::vector<const Array*>::const_iterator
00568              it = objects.begin(), ie = objects.end(); it != ie; ++it) {
00569         const Array *array = *it;
00570         for (unsigned offset = 0; offset < array->size; offset++) {
00571           ExprHandle counter = 
00572             vc_getCounterExample(vc, builder->getInitialRead(array, offset));
00573           *pos++ = getBVUnsigned(counter);
00574         }
00575       }
00576     }
00577     _exit(res);
00578   } else {
00579     int status;
00580     int res = waitpid(pid, &status, 0);
00581     
00582     if (res<0) {
00583       fprintf(stderr, "error: waitpid() for STP failed");
00584       return false;
00585     }
00586     
00587     // From timed_run.py: It appears that linux at least will on
00588     // "occasion" return a status when the process was terminated by a
00589     // signal, so test signal first.
00590     if (WIFSIGNALED(status) || !WIFEXITED(status)) {
00591       fprintf(stderr, "error: STP did not return successfully");
00592       return false;
00593     }
00594 
00595     int exitcode = WEXITSTATUS(status);
00596     if (exitcode==0) {
00597       hasSolution = true;
00598     } else if (exitcode==1) {
00599       hasSolution = false;
00600     } else if (exitcode==52) {
00601       fprintf(stderr, "error: STP timed out");
00602       return false;
00603     } else {
00604       fprintf(stderr, "error: STP did not return a recognized code");
00605       return false;
00606     }
00607     
00608     if (hasSolution) {
00609       values = std::vector< std::vector<unsigned char> >(objects.size());
00610       unsigned i=0;
00611       for (std::vector<const Array*>::const_iterator
00612              it = objects.begin(), ie = objects.end(); it != ie; ++it) {
00613         const Array *array = *it;
00614         std::vector<unsigned char> &data = values[i++];
00615         data.insert(data.begin(), pos, pos + array->size);
00616         pos += array->size;
00617       }
00618     }
00619 
00620     return true;
00621   }
00622 }
00623 
00624 bool
00625 STPSolverImpl::computeInitialValues(const Query &query,
00626                                     const std::vector<const Array*> 
00627                                       &objects,
00628                                     std::vector< std::vector<unsigned char> > 
00629                                       &values,
00630                                     bool &hasSolution) {
00631   TimerStatIncrementer t(stats::queryTime);
00632 
00633   vc_push(vc);
00634 
00635   for (ConstraintManager::const_iterator it = query.constraints.begin(), 
00636          ie = query.constraints.end(); it != ie; ++it)
00637     vc_assertFormula(vc, builder->construct(*it));
00638   
00639   ++stats::queries;
00640   ++stats::queryCounterexamples;
00641 
00642   ExprHandle stp_e = builder->construct(query.expr);
00643      
00644   bool success;
00645   if (useForkedSTP) {
00646     success = runAndGetCexForked(vc, builder, stp_e, objects, values, 
00647                                  hasSolution, timeout);
00648   } else {
00649     runAndGetCex(vc, builder, stp_e, objects, values, hasSolution);
00650     success = true;
00651   }
00652   
00653   if (success) {
00654     if (hasSolution)
00655       ++stats::queriesInvalid;
00656     else
00657       ++stats::queriesValid;
00658   }
00659   
00660   vc_pop(vc);
00661   
00662   return success;
00663 }

Generated on Fri Jun 5 03:31:32 2009 for klee by  doxygen 1.5.8