Skip to content

Commit d97a6b6

Browse files
committed
[feat] Add BlockLevelSearcher
1 parent 0cb981d commit d97a6b6

File tree

15 files changed

+265
-30
lines changed

15 files changed

+265
-30
lines changed

include/klee/Module/KModule.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,12 @@ struct KBlockCompare {
197197
}
198198
};
199199

200+
struct KFunctionCompare {
201+
bool operator()(const KFunction *a, const KFunction *b) const {
202+
return a->id < b->id;
203+
}
204+
};
205+
200206
class KConstant {
201207
public:
202208
/// Actual LLVM constant this represents.

lib/Core/ExecutionState.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,10 +443,13 @@ void ExecutionState::increaseLevel() {
443443
KFunction *kf = prevPC->parent->parent;
444444
KModule *kmodule = kf->parent;
445445

446-
if (prevPC->inst->isTerminator() && kmodule->inMainModule(*kf->function)) {
446+
if (prevPC->inst->isTerminator()) {
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
if (srcbb != dstbb) {
452455
transitionLevel.insert(std::make_pair(srcbb, dstbb));

lib/Core/ExecutionState.h

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

99101
/// Minimum distance to an uncovered instruction once the function
100102
/// 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
@@ -6663,7 +6663,7 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv,
66636663

66646664
if (guidanceKind == GuidanceKind::ErrorGuidance) {
66656665
std::map<klee::KFunction *, klee::ref<klee::TargetForest>,
6666-
klee::TargetedExecutionManager::KFunctionLess>
6666+
klee::KFunctionCompare>
66676667
prepTargets;
66686668
if (FunctionCallReproduce == "") {
66696669
auto &paths = interpreterOpts.Paths.value();

lib/Core/Searcher.cpp

Lines changed: 160 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ weight_type TargetedSearcher::getWeight(ExecutionState *es) {
195195
return weight;
196196
}
197197
auto distRes = distanceCalculator.getDistance(*es, target->getBlock());
198-
weight = ulog2(distRes.weight + es->steppedMemoryInstructions + 1); // [0, 32)
198+
weight = ulog2(distRes.weight + 1); // [0, 32)
199199
if (!distRes.isInsideFunction) {
200200
weight += 32; // [32, 64)
201201
}
@@ -206,12 +206,12 @@ weight_type TargetedSearcher::getWeight(ExecutionState *es) {
206206

207207
ExecutionState &GuidedSearcher::selectState() {
208208
unsigned size = historiesAndTargets.size();
209-
index = theRNG.getInt32() % (size + 1);
209+
interleave ^= 1;
210210
ExecutionState *state = nullptr;
211-
if (index == size) {
211+
if (interleave || !size) {
212212
state = &baseSearcher->selectState();
213213
} else {
214-
index = index % size;
214+
index = theRNG.getInt32() % size;
215215
auto &historyTargetPair = historiesAndTargets[index];
216216
ref<const TargetsHistory> history = historyTargetPair.first;
217217
ref<Target> target = historyTargetPair.second;
@@ -776,3 +776,159 @@ void InterleavedSearcher::printName(llvm::raw_ostream &os) {
776776
searcher->printName(os);
777777
os << "</InterleavedSearcher>\n";
778778
}
779+
780+
///
781+
782+
BlockLevelSearcher::BlockLevelSearcher(RNG &rng) : theRNG{rng} {}
783+
784+
ExecutionState &BlockLevelSearcher::selectState() {
785+
unsigned rnd = 0;
786+
unsigned index = 0;
787+
unsigned mod = 10;
788+
unsigned border = 9;
789+
790+
auto kfi = data.begin();
791+
index = theRNG.getInt32() % data.size();
792+
std::advance(kfi, index);
793+
auto &sizesTo = kfi->second;
794+
795+
for (auto &sizesSize : sizesTo) {
796+
rnd = theRNG.getInt32();
797+
if (rnd % mod < border) {
798+
for (auto &size : sizesSize.second) {
799+
rnd = theRNG.getInt32();
800+
if (rnd % mod < border) {
801+
auto lbi = size.second.begin();
802+
index = theRNG.getInt32() % size.second.size();
803+
std::advance(lbi, index);
804+
auto &level = *lbi;
805+
for (auto &levelMultilevels : level.second) {
806+
rnd = theRNG.getInt32();
807+
if (rnd % mod < border) {
808+
auto si = levelMultilevels.second.begin();
809+
index = theRNG.getInt32() % levelMultilevels.second.size();
810+
std::advance(si, index);
811+
auto &state = *si;
812+
return *state;
813+
}
814+
}
815+
}
816+
}
817+
}
818+
}
819+
820+
return **(sizesTo.begin()
821+
->second.begin()
822+
->second.begin()
823+
->second.begin()
824+
->second.begin());
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+
auto &sizesTo = data[kf];
834+
auto &sizeTo = sizesTo[stateToSize[current]];
835+
auto &levelTo = sizeTo[stateToSizes[current]];
836+
auto &multilevelTo = levelTo[stateToLevel[current]];
837+
auto &states = multilevelTo[stateToMultilevel[current]];
838+
sizes.clear();
839+
unsigned long long maxMultilevel = 0u;
840+
for (auto &infoFrame : current->stack.infoStack()) {
841+
sizes.push_back(infoFrame.level.size());
842+
maxMultilevel = std::max(maxMultilevel, infoFrame.maxMultilevel);
843+
}
844+
for (auto &kfLevel : current->stack.multilevel) {
845+
maxMultilevel = std::max(maxMultilevel, kfLevel.second);
846+
}
847+
if (sizes != stateToSizes[current] ||
848+
current->level.size() != stateToLevel[current].size() ||
849+
maxMultilevel != stateToMultilevel[current]) {
850+
states.erase(current);
851+
if (states.size() == 0) {
852+
multilevelTo.erase(stateToMultilevel[current]);
853+
}
854+
if (multilevelTo.size() == 0) {
855+
levelTo.erase(stateToLevel[current]);
856+
}
857+
if (levelTo.size() == 0) {
858+
sizeTo.erase(stateToSizes[current]);
859+
}
860+
if (sizeTo.size() == 0) {
861+
sizesTo.erase(stateToSize[current]);
862+
}
863+
864+
sizesTo[current->level.size()][sizes][current->level][maxMultilevel]
865+
.insert(current);
866+
867+
stateToLevel[current] = current->level;
868+
stateToMultilevel[current] = maxMultilevel;
869+
stateToSizes[current] = sizes;
870+
stateToSize[current] = current->level.size();
871+
}
872+
}
873+
874+
for (const auto state : addedStates) {
875+
KFunction *kf = state->initPC->parent->parent;
876+
auto &sizesTo = data[kf];
877+
878+
sizes.clear();
879+
unsigned long long maxMultilevel = 0u;
880+
for (auto &infoFrame : state->stack.infoStack()) {
881+
sizes.push_back(infoFrame.level.size());
882+
maxMultilevel = std::max(maxMultilevel, infoFrame.maxMultilevel);
883+
}
884+
for (auto &kfLevel : state->stack.multilevel) {
885+
maxMultilevel = std::max(maxMultilevel, kfLevel.second);
886+
}
887+
888+
sizesTo[state->level.size()][sizes][state->level][maxMultilevel].insert(
889+
state);
890+
891+
stateToLevel[state] = state->level;
892+
stateToMultilevel[state] = maxMultilevel;
893+
stateToSize[state] = state->level.size();
894+
stateToSizes[state] = sizes;
895+
}
896+
897+
// remove states
898+
for (const auto state : removedStates) {
899+
KFunction *kf = state->initPC->parent->parent;
900+
auto &sizesTo = data[kf];
901+
auto &sizeTo = sizesTo[stateToSize[state]];
902+
auto &levelTo = sizeTo[stateToSizes[state]];
903+
auto &multilevelTo = levelTo[stateToLevel[state]];
904+
auto &states = multilevelTo[stateToMultilevel[state]];
905+
906+
states.erase(state);
907+
if (states.size() == 0) {
908+
multilevelTo.erase(stateToMultilevel[state]);
909+
}
910+
if (multilevelTo.size() == 0) {
911+
levelTo.erase(stateToLevel[state]);
912+
}
913+
if (levelTo.size() == 0) {
914+
sizeTo.erase(stateToSizes[state]);
915+
}
916+
if (sizeTo.size() == 0) {
917+
sizesTo.erase(stateToSize[state]);
918+
}
919+
if (sizesTo.size() == 0) {
920+
data.erase(kf);
921+
}
922+
923+
stateToLevel.erase(state);
924+
stateToMultilevel.erase(state);
925+
stateToSizes.erase(state);
926+
stateToSize.erase(state);
927+
}
928+
}
929+
930+
bool BlockLevelSearcher::empty() { return stateToLevel.empty(); }
931+
932+
void BlockLevelSearcher::printName(llvm::raw_ostream &os) {
933+
os << "BlockLevelSearcher\n";
934+
}

lib/Core/Searcher.h

Lines changed: 69 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,73 @@ class InterleavedSearcher final : public Searcher {
363365
void printName(llvm::raw_ostream &os) override;
364366
};
365367

368+
class BlockLevelSearcher final : public Searcher {
369+
struct KBlockSetsCompare {
370+
bool operator()(const std::set<KBlock *, KBlockCompare> &a,
371+
const std::set<KBlock *, KBlockCompare> &b) const {
372+
return a.size() > b.size() || (a.size() == b.size() && a > b);
373+
}
374+
};
375+
376+
struct MultilevelCompare {
377+
bool operator()(
378+
const std::map<KBlock *, unsigned long long, KBlockCompare> &a,
379+
const std::map<KBlock *, unsigned long long, KBlockCompare> &b) const {
380+
return a < b;
381+
}
382+
};
383+
384+
struct VectorsCompare {
385+
bool operator()(const std::vector<unsigned> &a,
386+
const std::vector<unsigned> &b) const {
387+
auto ai = a.begin(), ae = a.end(), bi = b.begin(), be = b.end();
388+
for (; ai != ae && bi != be; ++ai, ++bi) {
389+
if (*ai != *bi) {
390+
return *ai > *bi;
391+
}
392+
}
393+
return bi != be;
394+
}
395+
};
396+
397+
using MultilevelsToStates =
398+
std::map<unsigned long long,
399+
std::set<ExecutionState *, ExecutionStateIDCompare>,
400+
std::less<unsigned long long>>;
401+
402+
using LevelToMultilevelsToStates =
403+
std::map<std::set<KBlock *, KBlockCompare>, MultilevelsToStates,
404+
KBlockSetsCompare>;
405+
406+
using SizesToLevelToMultilevelsToStates =
407+
std::map<std::vector<unsigned>, LevelToMultilevelsToStates,
408+
VectorsCompare>;
409+
using SizeToSizesToLevelToMultilevelsToStates =
410+
std::map<unsigned, SizesToLevelToMultilevelsToStates,
411+
std::greater<unsigned>>;
412+
413+
using FunctionToSizeToSizesToLevelToMultilevelsToStates =
414+
std::map<KFunction *, SizeToSizesToLevelToMultilevelsToStates,
415+
KFunctionCompare>;
416+
417+
FunctionToSizeToSizesToLevelToMultilevelsToStates data;
418+
std::unordered_map<ExecutionState *, std::set<KBlock *, KBlockCompare>>
419+
stateToLevel;
420+
std::unordered_map<ExecutionState *, unsigned long long> stateToMultilevel;
421+
std::unordered_map<ExecutionState *, std::vector<unsigned>> stateToSizes;
422+
std::unordered_map<ExecutionState *, unsigned> stateToSize;
423+
std::vector<unsigned> sizes;
424+
RNG &theRNG;
425+
426+
public:
427+
explicit BlockLevelSearcher(RNG &rng);
428+
ExecutionState &selectState() override;
429+
void update(ExecutionState *current, const StateIterable &addedStates,
430+
const StateIterable &removedStates) override;
431+
bool empty() override;
432+
void printName(llvm::raw_ostream &os) override;
433+
};
434+
366435
} // namespace klee
367436

368437
#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)