//===-- 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 "Common.h"
#include "Searcher.h"
#include "CoreStats.h"
#include "Executor.h"
#include "PTree.h"
#include "StatsTracker.h"
#include "klee/ExecutionState.h"
#include "klee/Statistics.h"
#include "klee/Internal/Module/InstructionInfoTable.h"
#include "klee/Internal/Module/KInstruction.h"
#include "klee/Internal/Module/KModule.h"
#include "klee/Internal/ADT/DiscretePDF.h"
#include "klee/Internal/ADT/RNG.h"
#include "klee/Internal/Support/ModuleUtil.h"
#include "klee/Internal/System/Time.h"
#include "llvm/Constants.h"
#include "llvm/Instructions.h"
#include "llvm/Module.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Support/CFG.h"
#include "llvm/Support/CommandLine.h"
#include <cassert>
#include <fstream>
#include <climits>
using namespace klee;
using namespace llvm;
namespace {
cl::opt<bool>
DebugLogMerge("debug-log-merge");
}
namespace klee {
extern RNG theRNG;
}
Searcher::~Searcher() {
}
///
ExecutionState &DFSSearcher::selectState() {
return *states.back();
}
void DFSSearcher::update(ExecutionState *current,
const std::set<ExecutionState*> &addedStates,
const std::set<ExecutionState*> &removedStates) {
states.insert(states.end(),
addedStates.begin(),
addedStates.end());
for (std::set<ExecutionState*>::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<ExecutionState*>::iterator it = states.begin(),
ie = states.end(); it != ie; ++it) {
if (es==*it) {
states.erase(it);
ok = true;
break;
}
}
assert(ok && "invalid state removed");
}
}
}
///
ExecutionState &RandomSearcher::selectState() {
return *states[theRNG.getInt32()%states.size()];
}
void RandomSearcher::update(ExecutionState *current,
const std::set<ExecutionState*> &addedStates,
const std::set<ExecutionState*> &removedStates) {
states.insert(states.end(),
addedStates.begin(),
addedStates.end());
for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(),
ie = removedStates.end(); it != ie; ++it) {
ExecutionState *es = *it;
bool ok = false;
for (std::vector<ExecutionState*>::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(Executor &_executor,
WeightType _type)
: executor(_executor),
states(new DiscretePDF<ExecutionState*>()),
type(_type) {
switch(type) {
case Depth:
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->weight;
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 < .1) ? 1. : 1./es->queryCost;
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::set<ExecutionState*> &addedStates,
const std::set<ExecutionState*> &removedStates) {
if (current && updateWeights && !removedStates.count(current))
states->update(current, getWeight(current));
for (std::set<ExecutionState*>::const_iterator it = addedStates.begin(),
ie = addedStates.end(); it != ie; ++it) {
ExecutionState *es = *it;
states->insert(es, getWeight(es));
}
for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(),
ie = removedStates.end(); it != ie; ++it) {
states->remove(*it);
}
}
bool WeightedRandomSearcher::empty() {
return states->empty();
}
///
RandomPathSearcher::RandomPathSearcher(Executor &_executor)
: executor(_executor) {
}
RandomPathSearcher::~RandomPathSearcher() {
}
ExecutionState &RandomPathSearcher::selectState() {
unsigned flips=0, bits=0;
PTree::Node *n = executor.processTree->root;
while (!n->data) {
if (!n->left) {
n = n->right;
} else if (!n->right) {
n = n->left;
} else {
if (bits==0) {
flips = theRNG.getInt32();
bits = 32;
}
--bits;
n = (flips&(1<<bits)) ? n->left : n->right;
}
}
return *n->data;
}
void RandomPathSearcher::update(ExecutionState *current,
const std::set<ExecutionState*> &addedStates,
const std::set<ExecutionState*> &removedStates) {
}
bool RandomPathSearcher::empty() {
return executor.states.empty();
}
///
BumpMergingSearcher::BumpMergingSearcher(Executor &_executor, Searcher *_baseSearcher)
: executor(_executor),
baseSearcher(_baseSearcher),
mergeFunction(executor.kmodule->kleeMergeFn) {
}
BumpMergingSearcher::~BumpMergingSearcher() {
delete baseSearcher;
}
///
Instruction *BumpMergingSearcher::getMergePoint(ExecutionState &es) {
if (mergeFunction) {
Instruction *i = es.pc->inst;
if (i->getOpcode()==Instruction::Call) {
CallSite cs(cast<CallInst>(i));
if (mergeFunction==cs.getCalledFunction())
return i;
}
}
return 0;
}
ExecutionState &BumpMergingSearcher::selectState() {
entry:
// out of base states, pick one to pop
if (baseSearcher->empty()) {
std::map<llvm::Instruction*, ExecutionState*>::iterator it =
statesAtMerge.begin();
ExecutionState *es = it->second;
statesAtMerge.erase(it);
++es->pc;
baseSearcher->addState(es);
}
ExecutionState &es = baseSearcher->selectState();
if (Instruction *mp = getMergePoint(es)) {
std::map<llvm::Instruction*, ExecutionState*>::iterator it =
statesAtMerge.find(mp);
baseSearcher->removeState(&es);
if (it==statesAtMerge.end()) {
statesAtMerge.insert(std::make_pair(mp, &es));
} else {
ExecutionState *mergeWith = it->second;
if (mergeWith->merge(es)) {
// hack, because we are terminating the state we need to let
// the baseSearcher know about it again
baseSearcher->addState(&es);
executor.terminateState(es);
} else {
it->second = &es; // the bump
++mergeWith->pc;
baseSearcher->addState(mergeWith);
}
}
goto entry;
} else {
return es;
}
}
void BumpMergingSearcher::update(ExecutionState *current,
const std::set<ExecutionState*> &addedStates,
const std::set<ExecutionState*> &removedStates) {
baseSearcher->update(current, addedStates, removedStates);
}
///
MergingSearcher::MergingSearcher(Executor &_executor, Searcher *_baseSearcher)
: executor(_executor),
baseSearcher(_baseSearcher),
mergeFunction(executor.kmodule->kleeMergeFn) {
}
MergingSearcher::~MergingSearcher() {
delete baseSearcher;
}
///
Instruction *MergingSearcher::getMergePoint(ExecutionState &es) {
if (mergeFunction) {
Instruction *i = es.pc->inst;
if (i->getOpcode()==Instruction::Call) {
CallSite cs(cast<CallInst>(i));
if (mergeFunction==cs.getCalledFunction())
return i;
}
}
return 0;
}
ExecutionState &MergingSearcher::selectState() {
while (!baseSearcher->empty()) {
ExecutionState &es = baseSearcher->selectState();
if (getMergePoint(es)) {
baseSearcher->removeState(&es, &es);
statesAtMerge.insert(&es);
} else {
return es;
}
}
// build map of merge point -> state list
std::map<Instruction*, std::vector<ExecutionState*> > merges;
for (std::set<ExecutionState*>::const_iterator it = statesAtMerge.begin(),
ie = statesAtMerge.end(); it != ie; ++it) {
ExecutionState &state = **it;
Instruction *mp = getMergePoint(state);
merges[mp].push_back(&state);
}
if (DebugLogMerge)
std::cerr << "-- all at merge --\n";
for (std::map<Instruction*, std::vector<ExecutionState*> >::iterator
it = merges.begin(), ie = merges.end(); it != ie; ++it) {
if (DebugLogMerge) {
std::cerr << "\tmerge: " << it->first << " [";
for (std::vector<ExecutionState*>::iterator it2 = it->second.begin(),
ie2 = it->second.end(); it2 != ie2; ++it2) {
ExecutionState *state = *it2;
std::cerr << state << ", ";
}
std::cerr << "]\n";
}
// merge states
std::set<ExecutionState*> toMerge(it->second.begin(), it->second.end());
while (!toMerge.empty()) {
ExecutionState *base = *toMerge.begin();
toMerge.erase(toMerge.begin());
std::set<ExecutionState*> toErase;
for (std::set<ExecutionState*>::iterator it = toMerge.begin(),
ie = toMerge.end(); it != ie; ++it) {
ExecutionState *mergeWith = *it;
if (base->merge(*mergeWith)) {
toErase.insert(mergeWith);
}
}
if (DebugLogMerge && !toErase.empty()) {
std::cerr << "\t\tmerged: " << base << " with [";
for (std::set<ExecutionState*>::iterator it = toErase.begin(),
ie = toErase.end(); it != ie; ++it) {
if (it!=toErase.begin()) std::cerr << ", ";
std::cerr << *it;
}
std::cerr << "]\n";
}
for (std::set<ExecutionState*>::iterator it = toErase.begin(),
ie = toErase.end(); it != ie; ++it) {
std::set<ExecutionState*>::iterator it2 = toMerge.find(*it);
assert(it2!=toMerge.end());
executor.terminateState(**it);
toMerge.erase(it2);
}
// step past merge and toss base back in pool
statesAtMerge.erase(statesAtMerge.find(base));
++base->pc;
baseSearcher->addState(base);
}
}
if (DebugLogMerge)
std::cerr << "-- merge complete, continuing --\n";
return selectState();
}
void MergingSearcher::update(ExecutionState *current,
const std::set<ExecutionState*> &addedStates,
const std::set<ExecutionState*> &removedStates) {
if (!removedStates.empty()) {
std::set<ExecutionState *> alt = removedStates;
for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(),
ie = removedStates.end(); it != ie; ++it) {
ExecutionState *es = *it;
std::set<ExecutionState*>::const_iterator it2 = statesAtMerge.find(es);
if (it2 != statesAtMerge.end()) {
statesAtMerge.erase(it2);
alt.erase(alt.find(es));
}
}
baseSearcher->update(current, addedStates, alt);
} else {
baseSearcher->update(current, addedStates, removedStates);
}
}
///
BatchingSearcher::BatchingSearcher(Searcher *_baseSearcher,
double _timeBudget,
unsigned _instructionBudget)
: baseSearcher(_baseSearcher),
timeBudget(_timeBudget),
instructionBudget(_instructionBudget),
lastState(0) {
}
BatchingSearcher::~BatchingSearcher() {
delete baseSearcher;
}
ExecutionState &BatchingSearcher::selectState() {
if (!lastState ||
(util::getWallTime()-lastStartTime)>timeBudget ||
(stats::instructions-lastStartInstructions)>instructionBudget) {
if (lastState) {
double delta = util::getWallTime()-lastStartTime;
if (delta>timeBudget*1.1) {
std::cerr << "KLEE: increased time budget from " << timeBudget << " to " << delta << "\n";
timeBudget = delta;
}
}
lastState = &baseSearcher->selectState();
lastStartTime = util::getWallTime();
lastStartInstructions = stats::instructions;
return *lastState;
} else {
return *lastState;
}
}
void BatchingSearcher::update(ExecutionState *current,
const std::set<ExecutionState*> &addedStates,
const std::set<ExecutionState*> &removedStates) {
if (removedStates.count(lastState))
lastState = 0;
baseSearcher->update(current, addedStates, removedStates);
}
/***/
IterativeDeepeningTimeSearcher::IterativeDeepeningTimeSearcher(Searcher *_baseSearcher)
: baseSearcher(_baseSearcher),
time(1.) {
}
IterativeDeepeningTimeSearcher::~IterativeDeepeningTimeSearcher() {
delete baseSearcher;
}
ExecutionState &IterativeDeepeningTimeSearcher::selectState() {
ExecutionState &res = baseSearcher->selectState();
startTime = util::getWallTime();
return res;
}
void IterativeDeepeningTimeSearcher::update(ExecutionState *current,
const std::set<ExecutionState*> &addedStates,
const std::set<ExecutionState*> &removedStates) {
double elapsed = util::getWallTime() - startTime;
if (!removedStates.empty()) {
std::set<ExecutionState *> alt = removedStates;
for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(),
ie = removedStates.end(); it != ie; ++it) {
ExecutionState *es = *it;
std::set<ExecutionState*>::const_iterator it2 = pausedStates.find(es);
if (it2 != pausedStates.end()) {
pausedStates.erase(it);
alt.erase(alt.find(es));
}
}
baseSearcher->update(current, addedStates, alt);
} else {
baseSearcher->update(current, addedStates, removedStates);
}
if (current && !removedStates.count(current) && elapsed>time) {
pausedStates.insert(current);
baseSearcher->removeState(current);
}
if (baseSearcher->empty()) {
time *= 2;
std::cerr << "KLEE: increasing time budget to: " << time << "\n";
baseSearcher->update(0, pausedStates, std::set<ExecutionState*>());
pausedStates.clear();
}
}
/***/
InterleavedSearcher::InterleavedSearcher(const std::vector<Searcher*> &_searchers)
: searchers(_searchers),
index(1) {
}
InterleavedSearcher::~InterleavedSearcher() {
for (std::vector<Searcher*>::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::set<ExecutionState*> &addedStates,
const std::set<ExecutionState*> &removedStates) {
for (std::vector<Searcher*>::const_iterator it = searchers.begin(),
ie = searchers.end(); it != ie; ++it)
(*it)->update(current, addedStates, removedStates);
}