//===-- Searcher.h ----------------------------------------------*- C++ -*-===// // // The KLEE Symbolic Virtual Machine // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef KLEE_SEARCHER_H #define KLEE_SEARCHER_H #include "PTree.h" #include "klee/System/Time.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include namespace llvm { class BasicBlock; class Function; class Instruction; class raw_ostream; } namespace klee { template class DiscretePDF; class ExecutionState; class Executor; class Searcher { public: virtual ~Searcher(); virtual ExecutionState &selectState() = 0; virtual void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) = 0; virtual bool empty() = 0; // prints name of searcher as a klee_message() // TODO: could probably make prettier or more flexible virtual void printName(llvm::raw_ostream &os) { os << "\n"; } // pgbovine - to be called when a searcher gets activated and // deactivated, say, by a higher-level searcher; most searchers // don't need this functionality, so don't have to override. virtual void activate() {} virtual void deactivate() {} // utility functions void addState(ExecutionState *es, ExecutionState *current = 0) { std::vector tmp; tmp.push_back(es); update(current, tmp, std::vector()); } void removeState(ExecutionState *es, ExecutionState *current = 0) { std::vector tmp; tmp.push_back(es); update(current, std::vector(), tmp); } enum CoreSearchType { DFS, BFS, RandomState, RandomPath, NURS_CovNew, NURS_MD2U, NURS_Depth, NURS_RP, NURS_ICnt, NURS_CPICnt, NURS_QC }; }; class DFSSearcher : public Searcher { std::vector states; public: ExecutionState &selectState(); void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates); bool empty() { return states.empty(); } void printName(llvm::raw_ostream &os) { os << "DFSSearcher\n"; } }; class BFSSearcher : public Searcher { std::deque states; public: ExecutionState &selectState(); void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates); bool empty() { return states.empty(); } void printName(llvm::raw_ostream &os) { os << "BFSSearcher\n"; } }; class RandomSearcher : public Searcher { std::vector states; public: ExecutionState &selectState(); void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates); bool empty() { return states.empty(); } void printName(llvm::raw_ostream &os) { os << "RandomSearcher\n"; } }; class WeightedRandomSearcher : public Searcher { public: enum WeightType { Depth, RP, QueryCost, InstCount, CPInstCount, MinDistToUncovered, CoveringNew }; private: DiscretePDF *states; WeightType type; bool updateWeights; double getWeight(ExecutionState*); public: WeightedRandomSearcher(WeightType type); ~WeightedRandomSearcher(); ExecutionState &selectState(); void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates); bool empty(); void printName(llvm::raw_ostream &os) { os << "WeightedRandomSearcher::"; switch(type) { case Depth : os << "Depth\n"; return; case RP : os << "RandomPath\n"; return; case QueryCost : os << "QueryCost\n"; return; case InstCount : os << "InstCount\n"; return; case CPInstCount : os << "CPInstCount\n"; return; case MinDistToUncovered : os << "MinDistToUncovered\n"; return; case CoveringNew : os << "CoveringNew\n"; return; default : os << "\n"; return; } } }; /* RandomPathSearcher performs a random walk of the PTree to select a state. PTree is a global data structure, however a searcher can sometimes only select from a subset of all states (depending on the update calls). To support this RandomPathSearcher has a subgraph view of PTree, in that it only walks the PTreeNodes that it "owns". Ownership is stored in the getInt method of PTreeNodePtr class (which hides it in the pointer itself). The current implementation of PTreeNodePtr supports only 3 instances of the RandomPathSearcher. This is because current PTreeNodePtr implementation conforms to C++ and only steals the last 3 alligment bits. This restriction could be relaxed slightly by an architecture specific implementation of PTreeNodePtr, that also steals the top bits of the pointer. The ownership bits are maintained in the update method. */ class RandomPathSearcher : public Searcher { PTree &processTree; // Unique bitmask of this searcher const uint8_t idBitMask; public: RandomPathSearcher(PTree &processTree); ~RandomPathSearcher(); ExecutionState &selectState(); void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates); bool empty(); void printName(llvm::raw_ostream &os) { os << "RandomPathSearcher\n"; } }; extern llvm::cl::opt UseIncompleteMerge; class MergeHandler; class MergingSearcher : public Searcher { friend class MergeHandler; private: Searcher *baseSearcher; /// States that have been paused by the 'pauseState' function std::vector pausedStates; public: MergingSearcher(Searcher *baseSearcher); ~MergingSearcher(); /// ExecutionStates currently paused from scheduling because they are /// waiting to be merged in a klee_close_merge instruction std::set inCloseMerge; /// Keeps track of all currently ongoing merges. /// An ongoing merge is a set of states (stored in a MergeHandler object) /// which branched from a single state which ran into a klee_open_merge(), /// and not all states in the set have reached the corresponding /// klee_close_merge() yet. std::vector mergeGroups; /// Remove state from the searcher chain, while keeping it in the executor. /// This is used here to 'freeze' a state while it is waiting for other /// states in its merge group to reach the same instruction. void pauseState(ExecutionState &state){ assert(std::find(pausedStates.begin(), pausedStates.end(), &state) == pausedStates.end()); pausedStates.push_back(&state); baseSearcher->removeState(&state); } /// Continue a paused state void continueState(ExecutionState &state){ auto it = std::find(pausedStates.begin(), pausedStates.end(), &state); assert( it != pausedStates.end()); pausedStates.erase(it); baseSearcher->addState(&state); } ExecutionState &selectState(); void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { // We have to check if the current execution state was just deleted, as to // not confuse the nurs searchers if (std::find(pausedStates.begin(), pausedStates.end(), current) == pausedStates.end()) { baseSearcher->update(current, addedStates, removedStates); } } bool empty() { return baseSearcher->empty(); } void printName(llvm::raw_ostream &os) { os << "MergingSearcher\n"; } }; class BatchingSearcher : public Searcher { Searcher *baseSearcher; time::Span timeBudget; unsigned instructionBudget; ExecutionState *lastState; time::Point lastStartTime; unsigned lastStartInstructions; public: BatchingSearcher(Searcher *baseSearcher, time::Span _timeBudget, unsigned _instructionBudget); ~BatchingSearcher(); ExecutionState &selectState(); void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates); bool empty() { return baseSearcher->empty(); } void printName(llvm::raw_ostream &os) { os << " timeBudget: " << timeBudget << ", instructionBudget: " << instructionBudget << ", baseSearcher:\n"; baseSearcher->printName(os); os << "\n"; } }; class IterativeDeepeningTimeSearcher : public Searcher { Searcher *baseSearcher; time::Point startTime; time::Span time; std::set pausedStates; public: IterativeDeepeningTimeSearcher(Searcher *baseSearcher); ~IterativeDeepeningTimeSearcher(); ExecutionState &selectState(); void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates); bool empty() { return baseSearcher->empty() && pausedStates.empty(); } void printName(llvm::raw_ostream &os) { os << "IterativeDeepeningTimeSearcher\n"; } }; class InterleavedSearcher : public Searcher { typedef std::vector searchers_ty; searchers_ty searchers; unsigned index; public: explicit InterleavedSearcher(const searchers_ty &_searchers); ~InterleavedSearcher(); ExecutionState &selectState(); void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates); bool empty() { return searchers[0]->empty(); } void printName(llvm::raw_ostream &os) { os << " containing " << searchers.size() << " searchers:\n"; for (searchers_ty::iterator it = searchers.begin(), ie = searchers.end(); it != ie; ++it) (*it)->printName(os); os << "\n"; } }; } #endif /* KLEE_SEARCHER_H */