//===-- Searcher.cpp ------------------------------------------------------===// // // The KLEE Symbolic Virtual Machine // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "Searcher.h" #include "CoreStats.h" #include "ExecutionState.h" #include "Executor.h" #include "MergeHandler.h" #include "PTree.h" #include "StatsTracker.h" #include "klee/ADT/DiscretePDF.h" #include "klee/ADT/RNG.h" #include "klee/Statistics/Statistics.h" #include "klee/Module/InstructionInfoTable.h" #include "klee/Module/KInstruction.h" #include "klee/Module/KModule.h" #include "klee/Support/ErrorHandling.h" #include "klee/Support/ModuleUtil.h" #include "klee/System/Time.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/Support/CommandLine.h" #include #include #include #include using namespace klee; using namespace llvm; namespace klee { extern RNG theRNG; } Searcher::~Searcher() { } /// ExecutionState &DFSSearcher::selectState() { return *states.back(); } void DFSSearcher::update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { states.insert(states.end(), addedStates.begin(), addedStates.end()); for (std::vector::const_iterator it = removedStates.begin(), ie = removedStates.end(); it != ie; ++it) { ExecutionState *es = *it; if (es == states.back()) { states.pop_back(); } else { bool ok = false; for (std::vector::iterator it = states.begin(), ie = states.end(); it != ie; ++it) { if (es==*it) { states.erase(it); ok = true; break; } } (void) ok; assert(ok && "invalid state removed"); } } } /// ExecutionState &BFSSearcher::selectState() { return *states.front(); } void BFSSearcher::update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { // Assumption: If new states were added KLEE forked, therefore states evolved. // constraints were added to the current state, it evolved. if (!addedStates.empty() && current && std::find(removedStates.begin(), removedStates.end(), current) == removedStates.end()) { auto pos = std::find(states.begin(), states.end(), current); assert(pos != states.end()); states.erase(pos); states.push_back(current); } states.insert(states.end(), addedStates.begin(), addedStates.end()); for (std::vector::const_iterator it = removedStates.begin(), ie = removedStates.end(); it != ie; ++it) { ExecutionState *es = *it; if (es == states.front()) { states.pop_front(); } else { bool ok = false; for (std::deque::iterator it = states.begin(), ie = states.end(); it != ie; ++it) { if (es==*it) { states.erase(it); ok = true; break; } } (void) ok; assert(ok && "invalid state removed"); } } } /// ExecutionState &RandomSearcher::selectState() { return *states[theRNG.getInt32()%states.size()]; } void RandomSearcher::update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { states.insert(states.end(), addedStates.begin(), addedStates.end()); for (std::vector::const_iterator it = removedStates.begin(), ie = removedStates.end(); it != ie; ++it) { ExecutionState *es = *it; __attribute__((unused)) bool ok = false; for (std::vector::iterator it = states.begin(), ie = states.end(); it != ie; ++it) { if (es==*it) { states.erase(it); ok = true; break; } } assert(ok && "invalid state removed"); } } /// WeightedRandomSearcher::WeightedRandomSearcher(WeightType _type) : states(new DiscretePDF()), type(_type) { switch(type) { case Depth: case RP: updateWeights = false; break; case InstCount: case CPInstCount: case QueryCost: case MinDistToUncovered: case CoveringNew: updateWeights = true; break; default: assert(0 && "invalid weight type"); } } WeightedRandomSearcher::~WeightedRandomSearcher() { delete states; } ExecutionState &WeightedRandomSearcher::selectState() { return *states->choose(theRNG.getDoubleL()); } double WeightedRandomSearcher::getWeight(ExecutionState *es) { switch(type) { default: case Depth: return es->depth; case RP: return std::pow(0.5, es->depth); case InstCount: { uint64_t count = theStatisticManager->getIndexedValue(stats::instructions, es->pc->info->id); double inv = 1. / std::max((uint64_t) 1, count); return inv * inv; } case CPInstCount: { StackFrame &sf = es->stack.back(); uint64_t count = sf.callPathNode->statistics.getValue(stats::instructions); double inv = 1. / std::max((uint64_t) 1, count); return inv; } case QueryCost: return (es->queryCost.toSeconds() < .1) ? 1. : 1./ es->queryCost.toSeconds(); case CoveringNew: case MinDistToUncovered: { uint64_t md2u = computeMinDistToUncovered(es->pc, es->stack.back().minDistToUncoveredOnReturn); double invMD2U = 1. / (md2u ? md2u : 10000); if (type==CoveringNew) { double invCovNew = 0.; if (es->instsSinceCovNew) invCovNew = 1. / std::max(1, (int) es->instsSinceCovNew - 1000); return (invCovNew * invCovNew + invMD2U * invMD2U); } else { return invMD2U * invMD2U; } } } } void WeightedRandomSearcher::update( ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { if (current && updateWeights && std::find(removedStates.begin(), removedStates.end(), current) == removedStates.end()) states->update(current, getWeight(current)); for (std::vector::const_iterator it = addedStates.begin(), ie = addedStates.end(); it != ie; ++it) { ExecutionState *es = *it; states->insert(es, getWeight(es)); } for (std::vector::const_iterator it = removedStates.begin(), ie = removedStates.end(); it != ie; ++it) { states->remove(*it); } } bool WeightedRandomSearcher::empty() { return states->empty(); } RandomPathSearcher::RandomPathSearcher(PTree &_processTree) : processTree(_processTree), idBitMask(_processTree.getNextId()) {} RandomPathSearcher::~RandomPathSearcher() { } // Check if n is a valid pointer and a node belonging to us #define IS_OUR_NODE_VALID(n) \ (((n).getPointer() != nullptr) && (((n).getInt() & idBitMask) != 0)) ExecutionState &RandomPathSearcher::selectState() { unsigned flips=0, bits=0; assert(processTree.root.getInt() & idBitMask && "Root should belong to the searcher"); PTreeNode *n = processTree.root.getPointer(); while (!n->state) { if (!IS_OUR_NODE_VALID(n->left)) { assert(IS_OUR_NODE_VALID(n->right) && "Both left and right nodes invalid"); assert(n != n->right.getPointer()); n = n->right.getPointer(); } else if (!IS_OUR_NODE_VALID(n->right)) { assert(IS_OUR_NODE_VALID(n->left) && "Both right and left nodes invalid"); assert(n != n->left.getPointer()); n = n->left.getPointer(); } else { if (bits==0) { flips = theRNG.getInt32(); bits = 32; } --bits; n = ((flips & (1 << bits)) ? n->left : n->right).getPointer(); } } return *n->state; } void RandomPathSearcher::update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { for (auto es : addedStates) { PTreeNode *pnode = es->ptreeNode, *parent = pnode->parent; PTreeNodePtr *childPtr; childPtr = parent ? ((parent->left.getPointer() == pnode) ? &parent->left : &parent->right) : &processTree.root; while (pnode && !IS_OUR_NODE_VALID(*childPtr)) { childPtr->setInt(childPtr->getInt() | idBitMask); pnode = parent; if (pnode) parent = pnode->parent; childPtr = parent ? ((parent->left.getPointer() == pnode) ? &parent->left : &parent->right) : &processTree.root; } } for (auto es : removedStates) { PTreeNode *pnode = es->ptreeNode, *parent = pnode->parent; while (pnode && !IS_OUR_NODE_VALID(pnode->left) && !IS_OUR_NODE_VALID(pnode->right)) { auto childPtr = parent ? ((parent->left.getPointer() == pnode) ? &parent->left : &parent->right) : &processTree.root; assert(IS_OUR_NODE_VALID(*childPtr) && "Removing pTree child not ours"); childPtr->setInt(childPtr->getInt() & ~idBitMask); pnode = parent; if (pnode) parent = pnode->parent; } } } bool RandomPathSearcher::empty() { return !IS_OUR_NODE_VALID(processTree.root); } /// MergingSearcher::MergingSearcher(Searcher *_baseSearcher) : baseSearcher(_baseSearcher){} MergingSearcher::~MergingSearcher() { delete baseSearcher; } ExecutionState& MergingSearcher::selectState() { assert(!baseSearcher->empty() && "base searcher is empty"); if (!UseIncompleteMerge) return baseSearcher->selectState(); // Iterate through all MergeHandlers for (auto cur_mergehandler: mergeGroups) { // Find one that has states that could be released if (!cur_mergehandler->hasMergedStates()) { continue; } // Find a state that can be prioritized ExecutionState *es = cur_mergehandler->getPrioritizeState(); if (es) { return *es; } else { if (DebugLogIncompleteMerge){ llvm::errs() << "Preemptively releasing states\n"; } // If no state can be prioritized, they all exceeded the amount of time we // are willing to wait for them. Release the states that already arrived at close_merge. cur_mergehandler->releaseStates(); } } // If we were not able to prioritize a merging state, just return some state return baseSearcher->selectState(); } /// BatchingSearcher::BatchingSearcher(Searcher *_baseSearcher, time::Span _timeBudget, unsigned _instructionBudget) : baseSearcher(_baseSearcher), timeBudget(_timeBudget), instructionBudget(_instructionBudget), lastState(0) { } BatchingSearcher::~BatchingSearcher() { delete baseSearcher; } ExecutionState &BatchingSearcher::selectState() { if (!lastState || (((timeBudget.toSeconds() > 0) && (time::getWallTime() - lastStartTime) > timeBudget)) || ((instructionBudget > 0) && (stats::instructions - lastStartInstructions) > instructionBudget)) { if (lastState) { time::Span delta = time::getWallTime() - lastStartTime; auto t = timeBudget; t *= 1.1; if (delta > t) { klee_message("increased time budget from %f to %f\n", timeBudget.toSeconds(), delta.toSeconds()); timeBudget = delta; } } lastState = &baseSearcher->selectState(); lastStartTime = time::getWallTime(); lastStartInstructions = stats::instructions; return *lastState; } else { return *lastState; } } void BatchingSearcher::update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { if (std::find(removedStates.begin(), removedStates.end(), lastState) != removedStates.end()) lastState = 0; baseSearcher->update(current, addedStates, removedStates); } /***/ IterativeDeepeningTimeSearcher::IterativeDeepeningTimeSearcher(Searcher *_baseSearcher) : baseSearcher(_baseSearcher), time(time::seconds(1)) { } IterativeDeepeningTimeSearcher::~IterativeDeepeningTimeSearcher() { delete baseSearcher; } ExecutionState &IterativeDeepeningTimeSearcher::selectState() { ExecutionState &res = baseSearcher->selectState(); startTime = time::getWallTime(); return res; } void IterativeDeepeningTimeSearcher::update( ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { const auto elapsed = time::getWallTime() - startTime; if (!removedStates.empty()) { std::vector alt = removedStates; for (std::vector::const_iterator it = removedStates.begin(), ie = removedStates.end(); it != ie; ++it) { ExecutionState *es = *it; std::set::const_iterator it2 = pausedStates.find(es); if (it2 != pausedStates.end()) { pausedStates.erase(it2); alt.erase(std::remove(alt.begin(), alt.end(), es), alt.end()); } } baseSearcher->update(current, addedStates, alt); } else { baseSearcher->update(current, addedStates, removedStates); } if (current && std::find(removedStates.begin(), removedStates.end(), current) == removedStates.end() && elapsed > time) { pausedStates.insert(current); baseSearcher->removeState(current); } if (baseSearcher->empty()) { time *= 2U; klee_message("increased time budget to %f\n", time.toSeconds()); std::vector ps(pausedStates.begin(), pausedStates.end()); baseSearcher->update(0, ps, std::vector()); pausedStates.clear(); } } /***/ InterleavedSearcher::InterleavedSearcher(const std::vector &_searchers) : searchers(_searchers), index(1) { } InterleavedSearcher::~InterleavedSearcher() { for (std::vector::const_iterator it = searchers.begin(), ie = searchers.end(); it != ie; ++it) delete *it; } ExecutionState &InterleavedSearcher::selectState() { Searcher *s = searchers[--index]; if (index==0) index = searchers.size(); return s->selectState(); } void InterleavedSearcher::update( ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { for (std::vector::const_iterator it = searchers.begin(), ie = searchers.end(); it != ie; ++it) (*it)->update(current, addedStates, removedStates); }