Skip to content

Commit f1e8720

Browse files
Columpiomisonijnik
authored andcommitted
[feat] Improved Iterative Deepening Searcher
1 parent c9c1419 commit f1e8720

9 files changed

Lines changed: 5303 additions & 87 deletions

File tree

lib/Core/ExecutionState.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ struct ExecutionStack {
127127
inline value_stack_ty &valueStack() { return valueStack_; }
128128
inline const value_stack_ty &valueStack() const { return valueStack_; }
129129
inline const call_stack_ty &callStack() const { return callStack_; }
130+
inline const info_stack_ty &infoStack() const { return infoStack_; }
130131
inline info_stack_ty &infoStack() { return infoStack_; }
131132
inline const call_stack_ty &uniqueFrames() const { return uniqueFrames_; }
132133

@@ -491,16 +492,18 @@ class ExecutionState {
491492
bool reachedTarget(ref<ReachBlockTarget> target) const;
492493
static std::uint32_t getLastID() { return nextID - 1; };
493494

494-
inline bool isStuck(unsigned long long bound) {
495-
KInstruction *prevKI = prevPC;
496-
if (prevKI->inst->isTerminator() && stack.size() > 0) {
497-
auto level = stack.infoStack().back().multilevel[getPCBlock()].second;
498-
return bound && level > bound;
495+
inline bool isStuck(unsigned long long bound) const {
496+
if (bound == 0)
497+
return false;
498+
if (prevPC->inst->isTerminator() && stack.size() > 0) {
499+
auto &ml = stack.infoStack().back().multilevel;
500+
auto level = ml.find(getPCBlock());
501+
return level != ml.end() && level->second > bound;
499502
}
500503
if (pc == pc->parent->getFirstInstruction() &&
501504
pc->parent == pc->parent->parent->entryKBlock) {
502-
auto level = stack.multilevel[stack.callStack().back().kf].second;
503-
return bound && level > bound;
505+
auto level = stack.multilevel.at(stack.callStack().back().kf);
506+
return level > bound;
504507
}
505508
return false;
506509
}

lib/Core/Executor.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4295,8 +4295,6 @@ void Executor::run(std::vector<ExecutionState *> initialStates) {
42954295
while (!states.empty() && !haltExecution) {
42964296
while (!searcher->empty() && !haltExecution) {
42974297
ExecutionState &state = searcher->selectState();
4298-
KInstruction *prevKI = state.prevPC;
4299-
KFunction *kf = prevKI->parent->parent;
43004298

43014299
executeStep(state);
43024300
}
@@ -4375,8 +4373,6 @@ static std::string terminationTypeFileExtension(StateTerminationType type) {
43754373
};
43764374

43774375
void Executor::executeStep(ExecutionState &state) {
4378-
KInstruction *prevKI = state.prevPC;
4379-
43804376
if (CoverOnTheFly && guidanceKind != GuidanceKind::ErrorGuidance &&
43814377
stats::instructions > DelayCoverOnTheFly && shouldWriteTest(state)) {
43824378
state.clearCoveredNew();
@@ -4389,13 +4385,7 @@ void Executor::executeStep(ExecutionState &state) {
43894385
if (targetManager->isTargeted(state) && state.targets().empty()) {
43904386
terminateStateEarlyAlgorithm(state, "State missed all it's targets.",
43914387
StateTerminationType::MissedAllTargets);
4392-
} else if (prevKI->inst->isTerminator() && MaxCycles &&
4393-
(state.stack.infoStack()
4394-
.back()
4395-
.multilevel[state.getPCBlock()]
4396-
.second > MaxCycles ||
4397-
state.stack.multilevel[state.stack.callStack().back().kf].second >
4398-
MaxCycles)) {
4388+
} else if (state.isStuck(MaxCycles)) {
43994389
terminateStateEarly(state, "max-cycles exceeded.",
44004390
StateTerminationType::MaxCycles);
44014391
} else {

lib/Core/Searcher.cpp

Lines changed: 85 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -640,33 +640,94 @@ void BatchingSearcher::printName(llvm::raw_ostream &os) {
640640

641641
///
642642

643-
IterativeDeepeningTimeSearcher::IterativeDeepeningTimeSearcher(
644-
Searcher *baseSearcher)
645-
: baseSearcher{baseSearcher} {};
643+
class TimeMetric final : public IterativeDeepeningSearcher::Metric {
644+
time::Point startTime;
645+
time::Span time{time::seconds(1)};
646646

647-
ExecutionState &IterativeDeepeningTimeSearcher::selectState() {
647+
public:
648+
void selectState() final { startTime = time::getWallTime(); }
649+
bool exceeds(const ExecutionState &state) const final {
650+
return time::getWallTime() - startTime > time;
651+
}
652+
void increaseLimit() final {
653+
time *= 2U;
654+
klee_message("increased time budget to %f seconds", time.toSeconds());
655+
}
656+
};
657+
658+
class MaxCyclesMetric final : public IterativeDeepeningSearcher::Metric {
659+
public:
660+
using ty = unsigned long long;
661+
662+
private:
663+
ty maxCycles;
664+
665+
public:
666+
explicit MaxCyclesMetric(ty maxCycles) : maxCycles(maxCycles){};
667+
explicit MaxCyclesMetric() : MaxCyclesMetric(1ULL){};
668+
669+
bool exceeds(const ExecutionState &state) const final {
670+
return state.isStuck(maxCycles);
671+
}
672+
void increaseLimit() final {
673+
maxCycles *= 2ULL;
674+
klee_message("increased max-cycles to %llu", maxCycles);
675+
}
676+
};
677+
678+
IterativeDeepeningSearcher::IterativeDeepeningSearcher(
679+
Searcher *baseSearcher, TargetManagerSubscriber *tms,
680+
HaltExecution::Reason m)
681+
: baseSearcher{baseSearcher}, tms{tms} {
682+
switch (m) {
683+
case HaltExecution::Reason::MaxTime:
684+
metric = std::make_unique<TimeMetric>();
685+
return;
686+
case HaltExecution::Reason::MaxCycles:
687+
metric = std::make_unique<MaxCyclesMetric>();
688+
return;
689+
default:
690+
klee_error("Illegal metric for iterative deepening searcher: %d", m);
691+
}
692+
}
693+
694+
ExecutionState &IterativeDeepeningSearcher::selectState() {
648695
ExecutionState &res = baseSearcher->selectState();
649-
startTime = time::getWallTime();
696+
metric->selectState();
650697
return res;
651698
}
652699

653-
void IterativeDeepeningTimeSearcher::update(
654-
ExecutionState *current, const std::vector<ExecutionState *> &addedStates,
655-
const std::vector<ExecutionState *> &removedStates) {
700+
void IterativeDeepeningSearcher::filter(const StatesVector &states,
701+
StatesVector &result) const {
702+
StatesVector states1(states);
703+
std::sort(states1.begin(), states1.end());
704+
std::set_difference(states1.begin(), states1.end(), pausedStates.begin(),
705+
pausedStates.end(), std::back_inserter(result));
706+
}
656707

657-
const auto elapsed = time::getWallTime() - startTime;
708+
void IterativeDeepeningSearcher::update(
709+
const TargetHistoryTargetPairToStatesMap &added,
710+
const TargetHistoryTargetPairToStatesMap &removed) {
711+
if (!tms)
712+
return;
713+
TargetHistoryTargetPairToStatesMap removedRefined(removed.size());
714+
for (const auto &pair : removed) {
715+
StatesVector refined;
716+
IterativeDeepeningSearcher::filter(pair.second, refined);
717+
removedRefined.emplace(pair.first, std::move(refined));
718+
}
719+
tms->update(added, removedRefined);
720+
}
721+
722+
void IterativeDeepeningSearcher::update(ExecutionState *current,
723+
const StatesVector &addedStates,
724+
const StatesVector &removedStates) {
658725

659726
// update underlying searcher (filter paused states unknown to underlying
660727
// searcher)
661728
if (!removedStates.empty()) {
662-
std::vector<ExecutionState *> alt = removedStates;
663-
for (const auto state : removedStates) {
664-
auto it = pausedStates.find(state);
665-
if (it != pausedStates.end()) {
666-
pausedStates.erase(it);
667-
alt.erase(std::remove(alt.begin(), alt.end(), state), alt.end());
668-
}
669-
}
729+
StatesVector alt;
730+
IterativeDeepeningSearcher::filter(removedStates, alt);
670731
baseSearcher->update(current, addedStates, alt);
671732
} else {
672733
baseSearcher->update(current, addedStates, removedStates);
@@ -676,27 +737,26 @@ void IterativeDeepeningTimeSearcher::update(
676737
if (current &&
677738
std::find(removedStates.begin(), removedStates.end(), current) ==
678739
removedStates.end() &&
679-
elapsed > time) {
740+
metric->exceeds(*current)) {
680741
pausedStates.insert(current);
681742
baseSearcher->update(nullptr, {}, {current});
682743
}
683744

684745
// no states left in underlying searcher: fill with paused states
685746
if (baseSearcher->empty()) {
686-
time *= 2U;
687-
klee_message("increased time budget to %f\n", time.toSeconds());
688-
std::vector<ExecutionState *> ps(pausedStates.begin(), pausedStates.end());
689-
baseSearcher->update(nullptr, ps, std::vector<ExecutionState *>());
747+
metric->increaseLimit();
748+
StatesVector ps(pausedStates.begin(), pausedStates.end());
749+
baseSearcher->update(nullptr, ps, {});
690750
pausedStates.clear();
691751
}
692752
}
693753

694-
bool IterativeDeepeningTimeSearcher::empty() {
754+
bool IterativeDeepeningSearcher::empty() {
695755
return baseSearcher->empty() && pausedStates.empty();
696756
}
697757

698-
void IterativeDeepeningTimeSearcher::printName(llvm::raw_ostream &os) {
699-
os << "IterativeDeepeningTimeSearcher\n";
758+
void IterativeDeepeningSearcher::printName(llvm::raw_ostream &os) {
759+
os << "IterativeDeepeningSearcher\n";
700760
}
701761

702762
///

lib/Core/Searcher.h

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -160,17 +160,10 @@ class GuidedSearcher final : public Searcher, public TargetManagerSubscriber {
160160
using TargetHistoryTargetPairHashMap =
161161
std::unordered_map<TargetHistoryTargetPair, T, TargetHistoryTargetHash,
162162
TargetHistoryTargetCmp>;
163-
164-
using TargetHistoryTargetPair =
165-
std::pair<ref<const TargetsHistory>, ref<Target>>;
166163
using TargetHistoryTargetPairToSearcherMap =
167164
std::unordered_map<TargetHistoryTargetPair,
168165
std::unique_ptr<TargetedSearcher>,
169166
TargetHistoryTargetHash, TargetHistoryTargetCmp>;
170-
using StatesVector = std::vector<ExecutionState *>;
171-
using TargetHistoryTargetPairToStatesMap =
172-
std::unordered_map<TargetHistoryTargetPair, StatesVector,
173-
TargetHistoryTargetHash, TargetHistoryTargetCmp>;
174167
using TargetForestHisoryTargetVector = std::vector<TargetHistoryTargetPair>;
175168
using TargetForestHistoryTargetSet =
176169
std::unordered_set<TargetHistoryTargetPair, TargetHistoryTargetHash,
@@ -323,26 +316,43 @@ class BatchingSearcher final : public Searcher {
323316
void printName(llvm::raw_ostream &os) override;
324317
};
325318

326-
/// IterativeDeepeningTimeSearcher implements time-based deepening. States
327-
/// are selected from an underlying searcher. When a state reaches its time
328-
/// limit it is paused (removed from underlying searcher). When the underlying
329-
/// searcher runs out of states, the time budget is increased and all paused
319+
/// IterativeDeepeningSearcher implements a metric-based deepening. States
320+
/// are selected from an underlying searcher. When a state exceeds its metric
321+
/// limit, it is paused (removed from underlying searcher). When the underlying
322+
/// searcher runs out of states, the metric limit is increased and all paused
330323
/// states are revived (added to underlying searcher).
331-
class IterativeDeepeningTimeSearcher final : public Searcher {
324+
class IterativeDeepeningSearcher final : public Searcher,
325+
public TargetManagerSubscriber {
326+
public:
327+
struct Metric {
328+
virtual ~Metric() = default;
329+
virtual void selectState(){};
330+
virtual bool exceeds(const ExecutionState &state) const = 0;
331+
virtual void increaseLimit() = 0;
332+
};
333+
334+
private:
332335
std::unique_ptr<Searcher> baseSearcher;
333-
time::Point startTime;
334-
time::Span time{time::seconds(1)};
336+
TargetManagerSubscriber *tms;
337+
std::unique_ptr<Metric> metric;
335338
std::set<ExecutionState *> pausedStates;
336339

340+
void filter(const std::vector<ExecutionState *> &states,
341+
std::vector<ExecutionState *> &result) const;
342+
337343
public:
338344
/// \param baseSearcher The underlying searcher (takes ownership).
339-
explicit IterativeDeepeningTimeSearcher(Searcher *baseSearcher);
340-
~IterativeDeepeningTimeSearcher() override = default;
345+
explicit IterativeDeepeningSearcher(Searcher *baseSearcher,
346+
TargetManagerSubscriber *tms,
347+
HaltExecution::Reason metric);
348+
~IterativeDeepeningSearcher() override = default;
341349

342350
ExecutionState &selectState() override;
343351
void update(ExecutionState *current,
344352
const std::vector<ExecutionState *> &addedStates,
345353
const std::vector<ExecutionState *> &removedStates) override;
354+
void update(const TargetHistoryTargetPairToStatesMap &added,
355+
const TargetHistoryTargetPairToStatesMap &removed) override;
346356
bool empty() override;
347357
void printName(llvm::raw_ostream &os) override;
348358
};

lib/Core/StatsTracker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ void StatsTracker::stepInstruction(ExecutionState &es) {
392392

393393
Instruction *inst = es.pc->inst;
394394
const KInstruction *ki = es.pc;
395-
InfoStackFrame &sf = es.stack.infoStack().back();
395+
auto &sf = es.stack.infoStack().back();
396396
theStatisticManager->setIndex(ki->getGlobalIndex());
397397
if (UseCallPaths)
398398
theStatisticManager->setContext(&sf.callPathNode->statistics);

lib/Core/TargetManager.h

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@
2525
namespace klee {
2626
class TargetCalculator;
2727

28+
using TargetHistoryTargetPair =
29+
std::pair<ref<const TargetsHistory>, ref<Target>>;
30+
using StatesVector = std::vector<ExecutionState *>;
31+
using TargetHistoryTargetPairToStatesMap =
32+
std::unordered_map<TargetHistoryTargetPair, StatesVector,
33+
TargetHistoryTargetHash, TargetHistoryTargetCmp>;
34+
2835
class TargetManagerSubscriber {
2936
public:
30-
using TargetHistoryTargetPair =
31-
std::pair<ref<const TargetsHistory>, ref<Target>>;
32-
using StatesVector = std::vector<ExecutionState *>;
33-
using TargetHistoryTargetPairToStatesMap =
34-
std::unordered_map<TargetHistoryTargetPair, StatesVector,
35-
TargetHistoryTargetHash, TargetHistoryTargetCmp>;
36-
3737
virtual ~TargetManagerSubscriber() = default;
3838

3939
/// Selects a state for further exploration.
@@ -47,12 +47,6 @@ class TargetManager {
4747
using StatesSet = std::unordered_set<ExecutionState *>;
4848
using StateToDistanceMap =
4949
std::unordered_map<const ExecutionState *, TargetHashMap<DistanceResult>>;
50-
using TargetHistoryTargetPair =
51-
std::pair<ref<const TargetsHistory>, ref<Target>>;
52-
using StatesVector = std::vector<ExecutionState *>;
53-
using TargetHistoryTargetPairToStatesMap =
54-
std::unordered_map<TargetHistoryTargetPair, StatesVector,
55-
TargetHistoryTargetHash, TargetHistoryTargetCmp>;
5650
using TargetForestHistoryTargetSet =
5751
std::unordered_set<TargetHistoryTargetPair, TargetHistoryTargetHash,
5852
TargetHistoryTargetCmp>;

lib/Core/UserSearcher.cpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,17 @@ cl::list<Searcher::CoreSearchType> CoreSearch(
5555
clEnumValN(Searcher::NURS_QC, "nurs:qc", "use NURS with Query-Cost")),
5656
cl::cat(SearchCat));
5757

58-
cl::opt<bool> UseIterativeDeepeningTimeSearch(
59-
"use-iterative-deepening-time-search",
60-
cl::desc(
61-
"Use iterative deepening time search (experimental) (default=false)"),
62-
cl::init(false), cl::cat(SearchCat));
58+
cl::opt<HaltExecution::Reason> UseIterativeDeepeningSearch(
59+
"use-iterative-deepening-search",
60+
cl::desc("Use iterative deepening search based on metric (experimental) "
61+
"(default=unspecified)"),
62+
cl::values(clEnumValN(HaltExecution::Reason::Unspecified, "unspecified",
63+
"Do not use iterative deepening search (default)"),
64+
clEnumValN(HaltExecution::Reason::MaxTime, "max-time",
65+
"metric is maximum time"),
66+
clEnumValN(HaltExecution::Reason::MaxCycles, "max-cycles",
67+
"metric is maximum cycles")),
68+
cl::init(HaltExecution::Reason::Unspecified), cl::cat(SearchCat));
6369

6470
cl::opt<bool> UseBatchingSearch(
6571
"use-batching-search",
@@ -172,16 +178,22 @@ Searcher *klee::constructUserSearcher(Executor &executor,
172178
BatchInstructions);
173179
}
174180

175-
if (UseIterativeDeepeningTimeSearch) {
176-
searcher = new IterativeDeepeningTimeSearcher(searcher);
177-
}
178-
181+
TargetManagerSubscriber *tms = nullptr;
179182
if (executor.guidanceKind != Interpreter::GuidanceKind::NoGuidance) {
180183
searcher = new GuidedSearcher(searcher, *executor.distanceCalculator,
181184
executor.theRNG);
182-
executor.targetManager->subscribe(*static_cast<GuidedSearcher *>(searcher));
185+
tms = static_cast<GuidedSearcher *>(searcher);
183186
}
184187

188+
if (UseIterativeDeepeningSearch != HaltExecution::Reason::Unspecified) {
189+
searcher = new IterativeDeepeningSearcher(searcher, tms,
190+
UseIterativeDeepeningSearch);
191+
tms = static_cast<IterativeDeepeningSearcher *>(searcher);
192+
}
193+
194+
if (tms)
195+
executor.targetManager->subscribe(*tms);
196+
185197
llvm::raw_ostream &os = executor.getHandler().getInfoStream();
186198

187199
os << "BEGIN searcher description\n";

test/Feature/Searchers.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
// RUN: rm -rf %t.klee-out
1919
// RUN: %klee --output-dir=%t.klee-out --search=random-path --search=nurs:qc %t2.bc
2020
// RUN: rm -rf %t.klee-out
21-
// RUN: %klee --output-dir=%t.klee-out --use-iterative-deepening-time-search --use-batching-search %t2.bc
21+
// RUN: %klee --output-dir=%t.klee-out --use-iterative-deepening-search=max-time --use-batching-search %t2.bc
2222
// RUN: rm -rf %t.klee-out
23-
// RUN: %klee --output-dir=%t.klee-out --use-iterative-deepening-time-search --use-batching-search --search=random-state %t2.bc
23+
// RUN: %klee --output-dir=%t.klee-out --use-iterative-deepening-search=max-time --use-batching-search --search=random-state %t2.bc
2424
// RUN: rm -rf %t.klee-out
25-
// RUN: %klee --output-dir=%t.klee-out --use-iterative-deepening-time-search --use-batching-search --search=nurs:depth %t2.bc
25+
// RUN: %klee --output-dir=%t.klee-out --use-iterative-deepening-search=max-time --use-batching-search --search=nurs:depth %t2.bc
2626
// RUN: rm -rf %t.klee-out
27-
// RUN: %klee --output-dir=%t.klee-out --use-iterative-deepening-time-search --use-batching-search --search=nurs:qc %t2.bc
27+
// RUN: %klee --output-dir=%t.klee-out --use-iterative-deepening-search=max-time --use-batching-search --search=nurs:qc %t2.bc
2828

2929
/* this test is basically just for coverage and doesn't really do any
3030
correctness check (aside from testing that the various combinations

0 commit comments

Comments
 (0)