Skip to content

Commit f81b257

Browse files
committed
[feat] Add BlockLevelSearcher
1 parent 52efb47 commit f81b257

15 files changed

Lines changed: 225 additions & 28 deletions

include/klee/Module/KModule.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ struct KFunction : public KCallable {
187187
/// Unique index for KFunction and KInstruction inside KModule
188188
/// from 0 to [KFunction + KInstruction]
189189
[[nodiscard]] inline unsigned getGlobalIndex() const { return globalIndex; }
190+
191+
bool operator<(const KFunction &rhs) const { return id < rhs.id; }
192+
bool operator<(const KFunction *rhs) const { return id < rhs->id; }
190193
};
191194

192195
struct KBlockCompare {
@@ -197,6 +200,12 @@ struct KBlockCompare {
197200
}
198201
};
199202

203+
struct KFunctionCompare {
204+
bool operator()(const KFunction *a, const KFunction *b) const {
205+
return a->id < b->id;
206+
}
207+
};
208+
200209
class KConstant {
201210
public:
202211
/// Actual LLVM constant this represents.

lib/Core/ExecutionState.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,10 @@ void ExecutionState::increaseLevel() {
446446
if (prevPC->inst->isTerminator() && kmodule->inMainModule(*kf->function)) {
447447
auto srcLevel = stack.infoStack().back().multilevel[srcbb].second;
448448
stack.infoStack().back().multilevel.replace({srcbb, srcLevel + 1});
449+
stack.infoStack().back().maxMultilevel =
450+
std::max(stack.infoStack().back().maxMultilevel, srcLevel + 1);
449451
level.insert(prevPC->parent);
452+
stack.infoStack().back().level.insert(prevPC->parent);
450453
}
451454
}
452455

lib/Core/ExecutionState.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ struct InfoStackFrame {
9696
KFunction *kf;
9797
CallPathNode *callPathNode = nullptr;
9898
PersistentMap<llvm::BasicBlock *, unsigned long long> multilevel;
99+
unsigned long long maxMultilevel = 0;
100+
std::set<KBlock *, KBlockCompare> level;
99101

100102
/// Minimum distance to an uncovered instruction once the function
101103
/// returns. This is not a good place for this but is used to

lib/Core/Executor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6763,7 +6763,7 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv,
67636763

67646764
if (guidanceKind == GuidanceKind::ErrorGuidance) {
67656765
std::map<klee::KFunction *, klee::ref<klee::TargetForest>,
6766-
klee::TargetedExecutionManager::KFunctionLess>
6766+
klee::KFunctionCompare>
67676767
prepTargets;
67686768
if (FunctionCallReproduce == "") {
67696769
auto &paths = interpreterOpts.Paths.value();

lib/Core/Searcher.cpp

Lines changed: 125 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,7 @@ weight_type TargetedSearcher::getWeight(ExecutionState *es) {
181181
return weight;
182182
}
183183
auto distRes = distanceCalculator.getDistance(*es, target->getBlock());
184-
weight = klee::util::ulog2(distRes.weight + es->steppedMemoryInstructions +
185-
1); // [0, 32)
184+
weight = klee::util::ulog2(distRes.weight + 1); // [0, 32)
186185
if (!distRes.isInsideFunction) {
187186
weight += 32; // [32, 64)
188187
}
@@ -193,12 +192,12 @@ weight_type TargetedSearcher::getWeight(ExecutionState *es) {
193192

194193
ExecutionState &GuidedSearcher::selectState() {
195194
unsigned size = historiesAndTargets.size();
196-
index = theRNG.getInt32() % (size + 1);
195+
interleave ^= 1;
197196
ExecutionState *state = nullptr;
198-
if (index == size) {
197+
if (interleave || !size) {
199198
state = &baseSearcher->selectState();
200199
} else {
201-
index = index % size;
200+
index = theRNG.getInt32() % size;
202201
auto &historyTargetPair = historiesAndTargets[index];
203202
ref<const TargetsHistory> history = historyTargetPair.first;
204203
ref<Target> target = historyTargetPair.second;
@@ -763,3 +762,124 @@ void InterleavedSearcher::printName(llvm::raw_ostream &os) {
763762
searcher->printName(os);
764763
os << "</InterleavedSearcher>\n";
765764
}
765+
766+
///
767+
768+
BlockLevelSearcher::BlockLevelSearcher(RNG &rng) : theRNG{rng} {}
769+
770+
ExecutionState &BlockLevelSearcher::selectState() {
771+
unsigned rnd = 0;
772+
unsigned index = 0;
773+
unsigned mod = 10;
774+
unsigned border = 9;
775+
776+
auto kfi = data.begin();
777+
index = theRNG.getInt32() % data.size();
778+
std::advance(kfi, index);
779+
auto &sizesTo = kfi->second;
780+
781+
for (auto &sizesSize : sizesTo) {
782+
rnd = theRNG.getInt32();
783+
if (rnd % mod < border) {
784+
for (auto &size : sizesSize.second) {
785+
rnd = theRNG.getInt32();
786+
if (rnd % mod < border) {
787+
auto lbi = size.second.begin();
788+
index = theRNG.getInt32() % size.second.size();
789+
std::advance(lbi, index);
790+
auto &level = *lbi;
791+
auto si = level.second.begin();
792+
index = theRNG.getInt32() % level.second.size();
793+
std::advance(si, index);
794+
auto &state = *si;
795+
return *state;
796+
}
797+
}
798+
}
799+
}
800+
801+
return **(sizesTo.begin()->second.begin()->second.begin()->second.begin());
802+
}
803+
804+
void BlockLevelSearcher::clear(ExecutionState &state) {
805+
KFunction *kf = state.initPC->parent->parent;
806+
BlockLevel &bl = stateToBlockLevel[&state];
807+
auto &sizeTo = data[kf];
808+
auto &sizesTo = sizeTo[bl.sizeOfLevel];
809+
auto &levelTo = sizesTo[bl.sizesOfFrameLevels];
810+
auto &states = levelTo[bl.maxMultilevel];
811+
812+
states.erase(&state);
813+
if (states.size() == 0) {
814+
levelTo.erase(bl.maxMultilevel);
815+
}
816+
if (levelTo.size() == 0) {
817+
sizesTo.erase(bl.sizesOfFrameLevels);
818+
}
819+
if (sizesTo.size() == 0) {
820+
sizeTo.erase(bl.sizeOfLevel);
821+
}
822+
if (sizeTo.size() == 0) {
823+
data.erase(kf);
824+
}
825+
}
826+
827+
void BlockLevelSearcher::update(ExecutionState *current,
828+
const StateIterable &addedStates,
829+
const StateIterable &removedStates) {
830+
if (current && std::find(removedStates.begin(), removedStates.end(),
831+
current) == removedStates.end()) {
832+
KFunction *kf = current->initPC->parent->parent;
833+
BlockLevel &bl = stateToBlockLevel[current];
834+
sizes.clear();
835+
unsigned long long maxMultilevel = 0u;
836+
for (auto &infoFrame : current->stack.infoStack()) {
837+
sizes.push_back(infoFrame.level.size());
838+
maxMultilevel = std::max(maxMultilevel, infoFrame.maxMultilevel);
839+
}
840+
for (auto &kfLevel : current->stack.multilevel) {
841+
maxMultilevel = std::max(maxMultilevel, kfLevel.second);
842+
}
843+
if (sizes != bl.sizesOfFrameLevels ||
844+
current->level.size() != bl.sizeOfLevel ||
845+
maxMultilevel != bl.maxMultilevel) {
846+
clear(*current);
847+
848+
data[kf][current->level.size()][sizes][maxMultilevel].insert(current);
849+
850+
stateToBlockLevel[current] =
851+
BlockLevel(kf, current->level.size(), sizes, maxMultilevel);
852+
}
853+
}
854+
855+
for (const auto state : addedStates) {
856+
KFunction *kf = state->initPC->parent->parent;
857+
858+
sizes.clear();
859+
unsigned long long maxMultilevel = 0u;
860+
for (auto &infoFrame : state->stack.infoStack()) {
861+
sizes.push_back(infoFrame.level.size());
862+
maxMultilevel = std::max(maxMultilevel, infoFrame.maxMultilevel);
863+
}
864+
for (auto &kfLevel : state->stack.multilevel) {
865+
maxMultilevel = std::max(maxMultilevel, kfLevel.second);
866+
}
867+
868+
data[kf][state->level.size()][sizes][maxMultilevel].insert(state);
869+
870+
stateToBlockLevel[state] =
871+
BlockLevel(kf, state->level.size(), sizes, maxMultilevel);
872+
}
873+
874+
// remove states
875+
for (const auto state : removedStates) {
876+
clear(*state);
877+
stateToBlockLevel.erase(state);
878+
}
879+
}
880+
881+
bool BlockLevelSearcher::empty() { return stateToBlockLevel.empty(); }
882+
883+
void BlockLevelSearcher::printName(llvm::raw_ostream &os) {
884+
os << "BlockLevelSearcher\n";
885+
}

lib/Core/Searcher.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class Searcher {
7676
enum CoreSearchType : std::uint8_t {
7777
DFS,
7878
BFS,
79+
BlockLevelSearch,
7980
RandomState,
8081
RandomPath,
8182
NURS_CovNew,
@@ -169,6 +170,7 @@ class GuidedSearcher final : public Searcher, public TargetManagerSubscriber {
169170
DistanceCalculator &distanceCalculator;
170171
RNG &theRNG;
171172
unsigned index{1};
173+
bool interleave = true;
172174

173175
TargetForestHistoryTargetSet localHistoryTargets;
174176
std::vector<ExecutionState *> baseAddedStates;
@@ -363,6 +365,68 @@ class InterleavedSearcher final : public Searcher {
363365
void printName(llvm::raw_ostream &os) override;
364366
};
365367

368+
class BlockLevelSearcher final : public Searcher {
369+
private:
370+
struct KBlockSetsCompare {
371+
bool operator()(const std::set<KBlock *, KBlockCompare> &a,
372+
const std::set<KBlock *, KBlockCompare> &b) const {
373+
return a.size() > b.size() || (a.size() == b.size() && a > b);
374+
}
375+
};
376+
377+
struct VectorsCompare {
378+
bool operator()(const std::vector<unsigned> &a,
379+
const std::vector<unsigned> &b) const {
380+
auto ai = a.begin(), ae = a.end(), bi = b.begin(), be = b.end();
381+
for (; ai != ae && bi != be; ++ai, ++bi) {
382+
if (*ai != *bi) {
383+
return *ai > *bi;
384+
}
385+
}
386+
return bi != be;
387+
}
388+
};
389+
390+
struct BlockLevel {
391+
KFunction *kf;
392+
unsigned sizeOfLevel;
393+
std::vector<unsigned> sizesOfFrameLevels;
394+
unsigned long long maxMultilevel;
395+
BlockLevel(KFunction *kf, unsigned sizeOfLevel,
396+
const std::vector<unsigned> &sizesOfFrameLevels,
397+
unsigned long long maxMultilevel)
398+
: kf(kf), sizeOfLevel(sizeOfLevel),
399+
sizesOfFrameLevels(sizesOfFrameLevels), maxMultilevel(maxMultilevel) {
400+
}
401+
BlockLevel() : kf(nullptr), sizeOfLevel(0), maxMultilevel(0){};
402+
};
403+
404+
using states_ty = std::set<ExecutionState *, ExecutionStateIDCompare>;
405+
using kblocks_ty = std::set<KBlock *, KBlockCompare>;
406+
407+
using ThirdLayer =
408+
std::map<unsigned long long, states_ty, std::less<unsigned long long>>;
409+
using SecondLayer =
410+
std::map<std::vector<unsigned>, ThirdLayer, VectorsCompare>;
411+
using FirstLayer = std::map<unsigned, SecondLayer, std::greater<unsigned>>;
412+
using Data = std::map<KFunction *, FirstLayer, KFunctionCompare>;
413+
414+
Data data;
415+
std::unordered_map<ExecutionState *, BlockLevel> stateToBlockLevel;
416+
std::vector<unsigned> sizes;
417+
RNG &theRNG;
418+
419+
void clear(ExecutionState &state);
420+
421+
public:
422+
explicit BlockLevelSearcher(RNG &rng);
423+
ExecutionState &selectState() override;
424+
void update(ExecutionState *current, const StateIterable &addedStates,
425+
const StateIterable &removedStates) override;
426+
bool empty() override;
427+
void printName(llvm::raw_ostream &os) override;
428+
};
429+
366430
} // namespace klee
367431

368432
#endif /* KLEE_SEARCHER_H */

lib/Core/TargetedExecutionManager.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -493,13 +493,12 @@ KFunction *TargetedExecutionManager::tryResolveEntryFunction(
493493
return resKf;
494494
}
495495

496-
std::map<KFunction *, ref<TargetForest>,
497-
TargetedExecutionManager::KFunctionLess>
496+
std::map<KFunction *, ref<TargetForest>, KFunctionCompare>
498497
TargetedExecutionManager::prepareTargets(KModule *kmodule, SarifReport paths) {
499498
Locations locations = collectAllLocations(paths);
500499
LocationToBlocks locToBlocks = prepareAllLocations(kmodule, locations);
501500

502-
std::map<KFunction *, ref<TargetForest>, KFunctionLess> whitelists;
501+
std::map<KFunction *, ref<TargetForest>, KFunctionCompare> whitelists;
503502

504503
for (auto &result : paths.results) {
505504
bool isFullyResolved = tryResolveLocations(result, locToBlocks);

lib/Core/TargetedExecutionManager.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,17 +115,11 @@ class TargetedExecutionManager {
115115
StatesSet localStates;
116116

117117
public:
118-
struct KFunctionLess {
119-
bool operator()(const KFunction *a, const KFunction *b) const {
120-
return a->id < b->id;
121-
}
122-
};
123-
124118
explicit TargetedExecutionManager(CodeGraphInfo &codeGraphInfo_,
125119
TargetManager &targetManager_)
126120
: codeGraphInfo(codeGraphInfo_), targetManager(targetManager_) {}
127121
~TargetedExecutionManager() = default;
128-
std::map<KFunction *, ref<TargetForest>, KFunctionLess>
122+
std::map<KFunction *, ref<TargetForest>, KFunctionCompare>
129123
prepareTargets(KModule *kmodule, SarifReport paths);
130124

131125
void reportFalseNegative(ExecutionState &state, ReachWithError error);

lib/Core/UserSearcher.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ cl::list<Searcher::CoreSearchType> CoreSearch(
3838
clEnumValN(Searcher::BFS, "bfs",
3939
"use Breadth First Search (BFS), where scheduling decisions "
4040
"are taken at the level of (2-way) forks"),
41+
clEnumValN(Searcher::BlockLevelSearch, "bls",
42+
"use Block Level Search (BLS), where selection "
43+
"between states with same level is is carried out randomly"),
4144
clEnumValN(Searcher::RandomState, "random-state",
4245
"randomly select a state to explore"),
4346
clEnumValN(Searcher::RandomPath, "random-path",
@@ -92,7 +95,7 @@ void klee::initializeSearchOptions() {
9295
// default values
9396
if (CoreSearch.empty()) {
9497
CoreSearch.push_back(Searcher::RandomPath);
95-
CoreSearch.push_back(Searcher::NURS_CovNew);
98+
CoreSearch.push_back(Searcher::BlockLevelSearch);
9699
}
97100
}
98101

@@ -119,6 +122,9 @@ Searcher *getNewSearcher(Searcher::CoreSearchType type, RNG &rng,
119122
case Searcher::BFS:
120123
searcher = new BFSSearcher();
121124
break;
125+
case Searcher::BlockLevelSearch:
126+
searcher = new BlockLevelSearcher(rng);
127+
break;
122128
case Searcher::RandomState:
123129
searcher = new RandomSearcher(rng);
124130
break;

test/Feature/BlockPath.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
; REQUIRES: geq-llvm-12.0
22
; RUN: %llvmas %s -o %t1.bc
33
; RUN: rm -rf %t.klee-out
4-
; RUN: %klee --output-dir=%t.klee-out --libc=klee --write-kpaths %t1.bc
4+
; RUN: %klee --output-dir=%t.klee-out --libc=klee --search=dfs --write-kpaths %t1.bc
55
; RUN: grep "(path: 0 (main: %0 %5 %6 %8 (abs: %1 %8 %11 %13) %8 %10 %16 %17 %19" %t.klee-out/test000001.kpath
66
; RUN: grep "(path: 0 (main: %0 %5 %6 %8 (abs: %1 %6 %11 %13) %8 %10 %16 %17 %19" %t.klee-out/test000002.kpath
77

0 commit comments

Comments
 (0)