Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block Level Search #146

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
9 changes: 9 additions & 0 deletions include/klee/Module/KModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ struct KFunction : public KCallable {
/// Unique index for KFunction and KInstruction inside KModule
/// from 0 to [KFunction + KInstruction]
[[nodiscard]] inline unsigned getGlobalIndex() const { return globalIndex; }

bool operator<(const KFunction &rhs) const { return id < rhs.id; }
bool operator<(const KFunction *rhs) const { return id < rhs->id; }
};

struct KBlockCompare {
Expand All @@ -197,6 +200,12 @@ struct KBlockCompare {
}
};

struct KFunctionCompare {
bool operator()(const KFunction *a, const KFunction *b) const {
return a->id < b->id;
}
};

class KConstant {
public:
/// Actual LLVM constant this represents.
Expand Down
35 changes: 7 additions & 28 deletions include/klee/Support/ModuleUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,16 @@ bool linkModules(llvm::Module *composite,
std::vector<std::unique_ptr<llvm::Module>> &modules,
const unsigned Flags, std::string &errorMsg);

#if defined(__x86_64__) || defined(__i386__)
#define addFunctionReplacement(from, to) \
{#from "f", #to "f"}, {#from "", #to ""}, { "" #from "l", #to "l" }
void replaceOrRenameFunction(llvm::Module *module, const char *old_name,
const char *new_name);

#if defined(__x86_64__) || defined(__i386__)
#define addIntrinsicReplacement(from, to) \
{"llvm." #from ".f32", #to "f"}, {"llvm." #from ".f64", #to}, { \
"llvm." #from ".f80", #to "l" \
}

#else
#define addFunctionReplacement(from, to) \
{#from "f", #to "f"}, { "" #from, "" #to }

#define addIntrinsicReplacement(from, to) \
{"llvm." #from ".f32", #to "f"}, { "llvm." #from ".f64", #to }

Expand All @@ -62,33 +59,15 @@ bool linkModules(llvm::Module *composite,
/// implementations in runtime/klee-fp, but not explicitly replaced here. Should
/// we rename them and complete the list?
const std::vector<std::pair<std::string, std::string>> floatReplacements = {
addFunctionReplacement(__isnan, klee_internal_isnan),
addFunctionReplacement(isnan, klee_internal_isnan),
addFunctionReplacement(__isinf, klee_internal_isinf),
addFunctionReplacement(isinf, klee_internal_isinf),
addFunctionReplacement(__fpclassify, klee_internal_fpclassify),
addFunctionReplacement(fpclassify, klee_internal_fpclassify),
addFunctionReplacement(__finite, klee_internal_finite),
addFunctionReplacement(finite, klee_internal_finite),
addFunctionReplacement(sqrt, klee_internal_sqrt),
addFunctionReplacement(fabs, klee_internal_fabs),
addFunctionReplacement(rint, klee_internal_rint),
addFunctionReplacement(round, klee_internal_rint),
addFunctionReplacement(__signbit, klee_internal_signbit),
addIntrinsicReplacement(sqrt, klee_internal_sqrt),
addIntrinsicReplacement(rint, klee_internal_rint),
addIntrinsicReplacement(round, klee_internal_rint),
addIntrinsicReplacement(sqrt, sqrt),
addIntrinsicReplacement(rint, rint),
addIntrinsicReplacement(round, rint),
addIntrinsicReplacement(nearbyint, nearbyint),
addIntrinsicReplacement(copysign, copysign),
addIntrinsicReplacement(floor, klee_floor),
addIntrinsicReplacement(floor, floor),
addIntrinsicReplacement(ceil, ceil)};
#undef addFunctionReplacement
#undef addIntrinsicReplacement

const std::vector<std::pair<std::string, std::string>> feRoundReplacements{
{"fegetround", "klee_internal_fegetround"},
{"fesetround", "klee_internal_fesetround"}};

/// Return the Function* target of a Call or Invoke instruction, or
/// null if it cannot be determined (should be only for indirect
/// calls, although complicated constant expressions might be
Expand Down
3 changes: 3 additions & 0 deletions lib/Core/ExecutionState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,10 @@ void ExecutionState::increaseLevel() {
if (prevPC->inst->isTerminator() && kmodule->inMainModule(*kf->function)) {
auto srcLevel = stack.infoStack().back().multilevel[srcbb].second;
stack.infoStack().back().multilevel.replace({srcbb, srcLevel + 1});
stack.infoStack().back().maxMultilevel =
std::max(stack.infoStack().back().maxMultilevel, srcLevel + 1);
level.insert(prevPC->parent);
stack.infoStack().back().level.insert(prevPC->parent);
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/Core/ExecutionState.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ struct InfoStackFrame {
KFunction *kf;
CallPathNode *callPathNode = nullptr;
PersistentMap<llvm::BasicBlock *, unsigned long long> multilevel;
unsigned long long maxMultilevel = 0;
std::set<KBlock *, KBlockCompare> level;

/// Minimum distance to an uncovered instruction once the function
/// returns. This is not a good place for this but is used to
Expand Down
52 changes: 30 additions & 22 deletions lib/Core/Executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ llvm::Module *Executor::setModule(
kmodule = std::make_unique<KModule>();

// 1.) Link the modules together && 2.) Apply different instrumentation
kmodule->link(userModules, 0);
kmodule->link(userModules, 1);
kmodule->instrument(opts);

kmodule->link(libsModules, 2);
Expand Down Expand Up @@ -603,21 +603,28 @@ llvm::Module *Executor::setModule(
specialFunctionHandler = new SpecialFunctionHandler(*this);
specialFunctionHandler->prepare(preservedFunctions);

preservedFunctions.push_back(opts.EntryPoint.c_str());

// Preserve the free-standing library calls
preservedFunctions.push_back("memset");
preservedFunctions.push_back("memcpy");
preservedFunctions.push_back("memcmp");
preservedFunctions.push_back("memmove");

if (FunctionCallReproduce != "") {
// prevent elimination of the function
auto f = kmodule->module->getFunction(FunctionCallReproduce);
if (f)
preservedFunctions.push_back(FunctionCallReproduce.c_str());
}

// prevent elimination of the preservedFunctions functions
for (auto pf : preservedFunctions) {
auto f = kmodule->module->getFunction(pf);
if (f) {
f->addFnAttr(Attribute::OptimizeNone);
f->addFnAttr(Attribute::NoInline);
}
}

// except the entry point
preservedFunctions.push_back(opts.EntryPoint.c_str());

kmodule->optimiseAndPrepare(opts, preservedFunctions);
kmodule->checkModule();

Expand Down Expand Up @@ -2942,18 +2949,19 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
} else {
ref<Expr> v = eval(ki, 0, state).value;

if (!isa<ConstantExpr>(v) && MockExternalCalls) {
if (ki->inst->getType()->isSized()) {
prepareMockValue(state, "mockExternResult", ki);
}
} else {
ExecutionState *free = &state;
bool hasInvalid = false, first = true;

/* XXX This is wasteful, no need to do a full evaluate since we
have already got a value. But in the end the caches should
handle it for us, albeit with some overhead. */
do {
ExecutionState *free = &state;
bool hasInvalid = false, first = true;

/* XXX This is wasteful, no need to do a full evaluate since we
have already got a value. But in the end the caches should
handle it for us, albeit with some overhead. */
do {
if (!first && MockExternalCalls) {
free = nullptr;
if (ki->inst->getType()->isSized()) {
prepareMockValue(state, "mockExternResult", ki);
}
} else {
v = optimizer.optimizeExpr(v, true);
ref<ConstantExpr> value;
bool success = solver->getValue(free->constraints.cs(), v, value,
Expand Down Expand Up @@ -2986,8 +2994,8 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
first = false;
free = res.second;
timers.invoke();
} while (free && !haltExecution);
}
}
} while (free && !haltExecution);
}
break;
}
Expand Down Expand Up @@ -4142,7 +4150,7 @@ bool Executor::checkMemoryUsage() {
const auto mmapUsage = memory->getUsedDeterministicSize() >> 20U;
const auto totalUsage = mallocUsage + mmapUsage;

if (MemoryTriggerCoverOnTheFly && 3 * totalUsage <= 2 * MaxMemory) {
if (MemoryTriggerCoverOnTheFly && totalUsage > MaxMemory * 0.75) {
klee_warning_once(0,
"enabling cover-on-the-fly (close to memory cap: %luMB)",
totalUsage);
Expand Down Expand Up @@ -6755,7 +6763,7 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv,

if (guidanceKind == GuidanceKind::ErrorGuidance) {
std::map<klee::KFunction *, klee::ref<klee::TargetForest>,
klee::TargetedExecutionManager::KFunctionLess>
klee::KFunctionCompare>
prepTargets;
if (FunctionCallReproduce == "") {
auto &paths = interpreterOpts.Paths.value();
Expand Down
132 changes: 126 additions & 6 deletions lib/Core/Searcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,7 @@ weight_type TargetedSearcher::getWeight(ExecutionState *es) {
return weight;
}
auto distRes = distanceCalculator.getDistance(*es, target->getBlock());
weight = klee::util::ulog2(distRes.weight + es->steppedMemoryInstructions +
1); // [0, 32)
weight = klee::util::ulog2(distRes.weight + 1); // [0, 32)
if (!distRes.isInsideFunction) {
weight += 32; // [32, 64)
}
Expand All @@ -193,12 +192,12 @@ weight_type TargetedSearcher::getWeight(ExecutionState *es) {

ExecutionState &GuidedSearcher::selectState() {
unsigned size = historiesAndTargets.size();
index = theRNG.getInt32() % (size + 1);
interleave ^= 1;
ExecutionState *state = nullptr;
if (index == size) {
if (interleave || !size) {
state = &baseSearcher->selectState();
} else {
index = index % size;
index = theRNG.getInt32() % size;
auto &historyTargetPair = historiesAndTargets[index];
ref<const TargetsHistory> history = historyTargetPair.first;
ref<Target> target = historyTargetPair.second;
Expand Down Expand Up @@ -657,7 +656,7 @@ class MaxCyclesMetric final : public IterativeDeepeningSearcher::Metric {
return state.isCycled(maxCycles);
}
void increaseLimit() final {
maxCycles *= 2ULL;
maxCycles *= 4ULL;
klee_message("increased max-cycles to %llu", maxCycles);
}
};
Expand Down Expand Up @@ -763,3 +762,124 @@ void InterleavedSearcher::printName(llvm::raw_ostream &os) {
searcher->printName(os);
os << "</InterleavedSearcher>\n";
}

///

BlockLevelSearcher::BlockLevelSearcher(RNG &rng) : theRNG{rng} {}

ExecutionState &BlockLevelSearcher::selectState() {
unsigned rnd = 0;
unsigned index = 0;
unsigned mod = 10;
unsigned border = 9;

auto kfi = data.begin();
index = theRNG.getInt32() % data.size();
std::advance(kfi, index);
auto &sizesTo = kfi->second;

for (auto &sizesSize : sizesTo) {
rnd = theRNG.getInt32();
if (rnd % mod < border) {
for (auto &size : sizesSize.second) {
rnd = theRNG.getInt32();
if (rnd % mod < border) {
auto lbi = size.second.begin();
index = theRNG.getInt32() % size.second.size();
std::advance(lbi, index);
auto &level = *lbi;
auto si = level.second.begin();
index = theRNG.getInt32() % level.second.size();
std::advance(si, index);
auto &state = *si;
return *state;
}
}
}
}

return **(sizesTo.begin()->second.begin()->second.begin()->second.begin());
}

void BlockLevelSearcher::clear(ExecutionState &state) {
KFunction *kf = state.initPC->parent->parent;
BlockLevel &bl = stateToBlockLevel[&state];
auto &sizeTo = data[kf];
auto &sizesTo = sizeTo[bl.sizeOfLevel];
auto &levelTo = sizesTo[bl.sizesOfFrameLevels];
auto &states = levelTo[bl.maxMultilevel];

states.erase(&state);
if (states.size() == 0) {
levelTo.erase(bl.maxMultilevel);
}
if (levelTo.size() == 0) {
sizesTo.erase(bl.sizesOfFrameLevels);
}
if (sizesTo.size() == 0) {
sizeTo.erase(bl.sizeOfLevel);
}
if (sizeTo.size() == 0) {
data.erase(kf);
}
}

void BlockLevelSearcher::update(ExecutionState *current,
const StateIterable &addedStates,
const StateIterable &removedStates) {
if (current && std::find(removedStates.begin(), removedStates.end(),
current) == removedStates.end()) {
KFunction *kf = current->initPC->parent->parent;
BlockLevel &bl = stateToBlockLevel[current];
sizes.clear();
unsigned long long maxMultilevel = 0u;
for (auto &infoFrame : current->stack.infoStack()) {
sizes.push_back(infoFrame.level.size());
maxMultilevel = std::max(maxMultilevel, infoFrame.maxMultilevel);
}
for (auto &kfLevel : current->stack.multilevel) {
maxMultilevel = std::max(maxMultilevel, kfLevel.second);
}
if (sizes != bl.sizesOfFrameLevels ||
current->level.size() != bl.sizeOfLevel ||
maxMultilevel != bl.maxMultilevel) {
clear(*current);

data[kf][current->level.size()][sizes][maxMultilevel].insert(current);

stateToBlockLevel[current] =
BlockLevel(kf, current->level.size(), sizes, maxMultilevel);
}
}

for (const auto state : addedStates) {
KFunction *kf = state->initPC->parent->parent;

sizes.clear();
unsigned long long maxMultilevel = 0u;
for (auto &infoFrame : state->stack.infoStack()) {
sizes.push_back(infoFrame.level.size());
maxMultilevel = std::max(maxMultilevel, infoFrame.maxMultilevel);
}
for (auto &kfLevel : state->stack.multilevel) {
maxMultilevel = std::max(maxMultilevel, kfLevel.second);
}

data[kf][state->level.size()][sizes][maxMultilevel].insert(state);

stateToBlockLevel[state] =
BlockLevel(kf, state->level.size(), sizes, maxMultilevel);
}

// remove states
for (const auto state : removedStates) {
clear(*state);
stateToBlockLevel.erase(state);
}
}

bool BlockLevelSearcher::empty() { return stateToBlockLevel.empty(); }

void BlockLevelSearcher::printName(llvm::raw_ostream &os) {
os << "BlockLevelSearcher\n";
}
Loading
Loading