Skip to content

Commit 2171ce3

Browse files
committed
[feat] Separate branch and block coverage
1 parent ca88a99 commit 2171ce3

8 files changed

Lines changed: 1956 additions & 159 deletions

File tree

include/klee/Module/CodeGraphInfo.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ class CodeGraphInfo {
4545
functionDistanceList functionSortedBackwardDistance;
4646

4747
functionBranchesSet functionBranches;
48+
functionBranchesSet functionConditionalBranches;
49+
functionBranchesSet functionBlocks;
4850

4951
private:
5052
void calculateDistance(KBlock *bb);
@@ -54,6 +56,8 @@ class CodeGraphInfo {
5456
void calculateBackwardDistance(KFunction *kf);
5557

5658
void calculateFunctionBranches(KFunction *kf);
59+
void calculateFunctionConditionalBranches(KFunction *kf);
60+
void calculateFunctionBlocks(KFunction *kf);
5761

5862
public:
5963
const std::unordered_map<KBlock *, unsigned int> &getDistance(KBlock *kb);
@@ -78,6 +82,10 @@ class CodeGraphInfo {
7882

7983
const std::map<KBlock *, std::set<unsigned>> &
8084
getFunctionBranches(KFunction *kf);
85+
const std::map<KBlock *, std::set<unsigned>> &
86+
getFunctionConditionalBranches(KFunction *kf);
87+
const std::map<KBlock *, std::set<unsigned>> &
88+
getFunctionBlocks(KFunction *kf);
8189
};
8290

8391
} // namespace klee

lib/Core/Executor.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ bool allLeafsAreConstant(const ref<Expr> &expr) {
445445
extern llvm::cl::opt<uint64_t> MaxConstantAllocationSize;
446446
extern llvm::cl::opt<uint64_t> MaxSymbolicAllocationSize;
447447
extern llvm::cl::opt<bool> UseSymbolicSizeAllocation;
448+
extern llvm::cl::opt<TrackCoverageBy> TrackCoverage;
448449

449450
// XXX hack
450451
extern "C" unsigned dumpStates, dumpPForest;
@@ -4373,6 +4374,7 @@ static std::string terminationTypeFileExtension(StateTerminationType type) {
43734374
};
43744375

43754376
void Executor::executeStep(ExecutionState &state) {
4377+
KFunction *initKF = state.initPC->parent->parent;
43764378
if (CoverOnTheFly && guidanceKind != GuidanceKind::ErrorGuidance &&
43774379
stats::instructions > DelayCoverOnTheFly && shouldWriteTest(state)) {
43784380
state.clearCoveredNew();
@@ -4407,6 +4409,11 @@ void Executor::executeStep(ExecutionState &state) {
44074409
// pressure
44084410
updateStates(nullptr);
44094411
}
4412+
4413+
if (targetCalculator && TrackCoverage != TrackCoverageBy::None &&
4414+
targetCalculator->isCovered(initKF)) {
4415+
haltExecution = HaltExecution::CovCheck;
4416+
}
44104417
}
44114418

44124419
void Executor::targetedRun(ExecutionState &initialState, KBlock *target,

lib/Core/TargetCalculator.cpp

Lines changed: 103 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -22,52 +22,49 @@
2222
using namespace llvm;
2323
using namespace klee;
2424

25-
namespace klee {
26-
llvm::cl::opt<TargetCalculateBy> TargetCalculatorMode(
27-
"target-calculator-kind", cl::desc("Specifiy the target calculator mode."),
28-
cl::values(
29-
clEnumValN(TargetCalculateBy::Default, "default",
30-
"Looks for the closest uncovered block."),
31-
clEnumValN(
32-
TargetCalculateBy::Blocks, "blocks",
33-
"Looks for the closest uncovered block by state blocks history."),
34-
clEnumValN(TargetCalculateBy::Transitions, "transitions",
35-
"Looks for the closest uncovered block by state transitions "
36-
"history.")),
37-
cl::init(TargetCalculateBy::Default), cl::cat(ExecCat));
38-
} // namespace klee
25+
llvm::cl::opt<TrackCoverageBy> TrackCoverage(
26+
"track-coverage", cl::desc("Specifiy the track coverage mode."),
27+
cl::values(clEnumValN(TrackCoverageBy::None, "none", "Not track coverage."),
28+
clEnumValN(TrackCoverageBy::Blocks, "blocks",
29+
"Track only covered block."),
30+
clEnumValN(TrackCoverageBy::Branches, "branches",
31+
"Track only covered conditional branches."),
32+
clEnumValN(TrackCoverageBy::All, "all", "Track all.")),
33+
cl::init(TrackCoverageBy::None), cl::cat(ExecCat));
3934

4035
void TargetCalculator::update(const ExecutionState &state) {
4136
Function *initialFunction = state.getInitPCBlock()->getParent();
4237

43-
if (state.prevPC == state.prevPC->parent->getLastInstruction()) {
44-
coveredBlocks[state.getPrevPCBlock()->getParent()].insert(
45-
state.prevPC->parent);
46-
}
4738
if (state.prevPC == state.prevPC->parent->getLastInstruction() &&
4839
!fullyCoveredFunctions.count(state.prevPC->parent->parent)) {
40+
auto &fBranches = getCoverageTargets(state.prevPC->parent->parent);
4941

5042
if (!coveredFunctionsInBranches.count(state.prevPC->parent->parent)) {
51-
unsigned index = 0;
52-
if (!coveredBranches[state.prevPC->parent->parent].count(
53-
state.prevPC->parent)) {
54-
state.coverNew();
55-
coveredBranches[state.prevPC->parent->parent][state.prevPC->parent];
56-
}
57-
for (auto succ : successors(state.getPrevPCBlock())) {
58-
if (succ == state.getPCBlock()) {
59-
if (!coveredBranches[state.prevPC->parent->parent]
60-
[state.prevPC->parent]
61-
.count(index)) {
62-
state.coverNew();
63-
coveredBranches[state.prevPC->parent->parent][state.prevPC->parent]
64-
.insert(index);
43+
if (fBranches.count(state.prevPC->parent) != 0) {
44+
if (!coveredBranches[state.prevPC->parent->parent].count(
45+
state.prevPC->parent)) {
46+
state.coverNew();
47+
coveredBranches[state.prevPC->parent->parent][state.prevPC->parent];
48+
}
49+
if (!fBranches.at(state.prevPC->parent).empty()) {
50+
unsigned index = 0;
51+
for (auto succ : successors(state.getPrevPCBlock())) {
52+
if (succ == state.getPCBlock()) {
53+
if (!coveredBranches[state.prevPC->parent->parent]
54+
[state.prevPC->parent]
55+
.count(index)) {
56+
state.coverNew();
57+
coveredBranches[state.prevPC->parent->parent]
58+
[state.prevPC->parent]
59+
.insert(index);
60+
}
61+
break;
62+
}
63+
++index;
6564
}
66-
break;
6765
}
68-
++index;
6966
}
70-
if (codeGraphInfo.getFunctionBranches(state.prevPC->parent->parent) ==
67+
if (getCoverageTargets(state.prevPC->parent->parent) ==
7168
coveredBranches[state.prevPC->parent->parent]) {
7269
coveredFunctionsInBranches.insert(state.prevPC->parent->parent);
7370
}
@@ -88,12 +85,15 @@ void TargetCalculator::update(const ExecutionState &state) {
8885
KFunction *calledKFunction = state.prevPC->parent->parent->parent
8986
->functionMap[calledFunction];
9087
if (calledKFunction->numInstructions != 0 &&
91-
coveredFunctionsInBranches.count(calledKFunction) == 0) {
88+
coveredFunctionsInBranches.count(calledKFunction) == 0 &&
89+
!getCoverageTargets(calledKFunction).empty()) {
9290
covered = false;
9391
break;
9492
}
9593
if (!fnsTaken.count(calledKFunction) &&
96-
fullyCoveredFunctions.count(calledKFunction) == 0) {
94+
fullyCoveredFunctions.count(calledKFunction) == 0 &&
95+
calledKFunction->numInstructions != 0 &&
96+
!getCoverageTargets(calledKFunction).empty()) {
9797
fns.push_back(calledKFunction);
9898
}
9999
}
@@ -106,25 +106,6 @@ void TargetCalculator::update(const ExecutionState &state) {
106106
}
107107
}
108108
}
109-
110-
switch (TargetCalculatorMode) {
111-
case TargetCalculateBy::Default:
112-
blocksHistory[initialFunction][state.getPrevPCBlock()].insert(
113-
state.initPC->parent);
114-
break;
115-
116-
case TargetCalculateBy::Blocks:
117-
blocksHistory[initialFunction][state.getPrevPCBlock()].insert(
118-
state.level.begin(), state.level.end());
119-
break;
120-
121-
case TargetCalculateBy::Transitions:
122-
blocksHistory[initialFunction][state.getPrevPCBlock()].insert(
123-
state.level.begin(), state.level.end());
124-
transitionsHistory[initialFunction][state.getPrevPCBlock()].insert(
125-
state.transitionLevel.begin(), state.transitionLevel.end());
126-
break;
127-
}
128109
}
129110

130111
void TargetCalculator::update(
@@ -151,73 +132,48 @@ void TargetCalculator::update(
151132
localStates.clear();
152133
}
153134

154-
bool TargetCalculator::differenceIsEmpty(
155-
const ExecutionState &state,
156-
const std::unordered_map<llvm::BasicBlock *, VisitedBlocks> &history,
157-
KBlock *target) {
158-
std::vector<KBlock *> diff;
159-
std::set<KBlock *> left(state.level.begin(), state.level.end());
160-
std::set<KBlock *> right(history.at(target->basicBlock).begin(),
161-
history.at(target->basicBlock).end());
162-
std::set_difference(left.begin(), left.end(), right.begin(), right.end(),
163-
std::inserter(diff, diff.begin()));
164-
return diff.empty();
165-
}
135+
const std::map<KBlock *, std::set<unsigned>> &
136+
TargetCalculator::getCoverageTargets(KFunction *kf) {
137+
switch (TrackCoverage) {
138+
case TrackCoverageBy::Blocks:
139+
return codeGraphInfo.getFunctionBlocks(kf);
140+
case TrackCoverageBy::Branches:
141+
return codeGraphInfo.getFunctionConditionalBranches(kf);
142+
case TrackCoverageBy::None:
143+
case TrackCoverageBy::All:
144+
return codeGraphInfo.getFunctionBranches(kf);
166145

167-
bool TargetCalculator::differenceIsEmpty(
168-
const ExecutionState &state,
169-
const std::unordered_map<llvm::BasicBlock *, VisitedTransitions> &history,
170-
KBlock *target) {
171-
std::vector<Transition> diff;
172-
std::set<Transition> left(state.transitionLevel.begin(),
173-
state.transitionLevel.end());
174-
std::set<Transition> right(history.at(target->basicBlock).begin(),
175-
history.at(target->basicBlock).end());
176-
std::set_difference(left.begin(), left.end(), right.begin(), right.end(),
177-
std::inserter(diff, diff.begin()));
178-
return diff.empty();
146+
default:
147+
assert(0 && "not implemented");
148+
}
179149
}
180150

181151
bool TargetCalculator::uncoveredBlockPredicate(ExecutionState *state,
182152
KBlock *kblock) {
183153
Function *initialFunction = state->getInitPCBlock()->getParent();
184-
std::unordered_map<llvm::BasicBlock *, VisitedBlocks> &history =
185-
blocksHistory[initialFunction];
186-
std::unordered_map<llvm::BasicBlock *, VisitedTransitions>
187-
&transitionHistory = transitionsHistory[initialFunction];
188154
bool result = false;
189-
if (coveredBranches[kblock->parent].count(kblock) == 0) {
190-
result = true;
191-
} else {
192-
auto &cb = coveredBranches[kblock->parent][kblock];
193-
if (isa<KCallBlock>(kblock) &&
194-
cast<KCallBlock>(kblock)->calledFunctions.size() == 1) {
195-
auto calledFunction = *cast<KCallBlock>(kblock)->calledFunctions.begin();
196-
KFunction *calledKFunction =
197-
kblock->parent->parent->functionMap[calledFunction];
198-
result = fullyCoveredFunctions.count(calledKFunction) == 0 &&
199-
calledKFunction->numInstructions;
200-
}
201-
result |=
202-
kblock->basicBlock->getTerminator()->getNumSuccessors() > cb.size();
203-
}
204155

205-
switch (TargetCalculatorMode) {
206-
case TargetCalculateBy::Default: {
207-
break;
208-
}
209-
case TargetCalculateBy::Blocks: {
210-
if (history[kblock->basicBlock].size() != 0) {
211-
result |= !differenceIsEmpty(*state, history, kblock);
212-
}
213-
break;
214-
}
215-
case TargetCalculateBy::Transitions: {
216-
if (history[kblock->basicBlock].size() != 0) {
217-
result |= !differenceIsEmpty(*state, transitionHistory, kblock);
156+
auto &fBranches = getCoverageTargets(kblock->parent);
157+
158+
if (fBranches.count(kblock) != 0 || isa<KCallBlock>(kblock)) {
159+
if (coveredBranches[kblock->parent].count(kblock) == 0) {
160+
result = true;
161+
} else {
162+
auto &cb = coveredBranches[kblock->parent][kblock];
163+
if (isa<KCallBlock>(kblock) &&
164+
cast<KCallBlock>(kblock)->calledFunctions.size() == 1) {
165+
auto calledFunction =
166+
*cast<KCallBlock>(kblock)->calledFunctions.begin();
167+
KFunction *calledKFunction =
168+
kblock->parent->parent->functionMap[calledFunction];
169+
result = fullyCoveredFunctions.count(calledKFunction) == 0 &&
170+
calledKFunction->numInstructions;
171+
}
172+
if (fBranches.at(kblock) != cb) {
173+
result |=
174+
kblock->basicBlock->getTerminator()->getNumSuccessors() > cb.size();
175+
}
218176
}
219-
break;
220-
}
221177
}
222178

223179
return result;
@@ -246,29 +202,36 @@ TargetHashSet TargetCalculator::calculate(ExecutionState &state) {
246202
if (!blocks.empty()) {
247203
TargetHashSet targets;
248204
for (auto block : blocks) {
249-
if (coveredBranches[block->parent].count(block) == 0) {
250-
targets.insert(ReachBlockTarget::create(block, false));
251-
} else {
252-
auto &cb = coveredBranches[block->parent][block];
253-
bool notCoveredFunction = false;
254-
if (isa<KCallBlock>(block) &&
255-
cast<KCallBlock>(block)->calledFunctions.size() == 1) {
256-
auto calledFunction =
257-
*cast<KCallBlock>(block)->calledFunctions.begin();
258-
KFunction *calledKFunction =
259-
block->parent->parent->functionMap[calledFunction];
260-
notCoveredFunction =
261-
fullyCoveredFunctions.count(calledKFunction) == 0 &&
262-
calledKFunction->numInstructions;
263-
}
264-
if (notCoveredFunction) {
265-
targets.insert(ReachBlockTarget::create(block, true));
205+
auto &fBranches = getCoverageTargets(block->parent);
206+
207+
if (fBranches.count(block) != 0 || isa<KCallBlock>(block)) {
208+
if (coveredBranches[block->parent].count(block) == 0) {
209+
targets.insert(ReachBlockTarget::create(block, false));
266210
} else {
267-
for (unsigned index = 0;
268-
index < block->basicBlock->getTerminator()->getNumSuccessors();
269-
++index) {
270-
if (!cb.count(index))
271-
targets.insert(CoverBranchTarget::create(block, index));
211+
auto &cb = coveredBranches[block->parent][block];
212+
bool notCoveredFunction = false;
213+
if (isa<KCallBlock>(block) &&
214+
cast<KCallBlock>(block)->calledFunctions.size() == 1) {
215+
auto calledFunction =
216+
*cast<KCallBlock>(block)->calledFunctions.begin();
217+
KFunction *calledKFunction =
218+
block->parent->parent->functionMap[calledFunction];
219+
notCoveredFunction =
220+
fullyCoveredFunctions.count(calledKFunction) == 0 &&
221+
calledKFunction->numInstructions;
222+
}
223+
if (notCoveredFunction) {
224+
targets.insert(ReachBlockTarget::create(block, true));
225+
} else {
226+
if (fBranches.at(block) != cb) {
227+
for (unsigned index = 0;
228+
index <
229+
block->basicBlock->getTerminator()->getNumSuccessors();
230+
++index) {
231+
if (!cb.count(index))
232+
targets.insert(CoverBranchTarget::create(block, index));
233+
}
234+
}
272235
}
273236
}
274237
}
@@ -289,3 +252,7 @@ TargetHashSet TargetCalculator::calculate(ExecutionState &state) {
289252

290253
return {};
291254
}
255+
256+
bool TargetCalculator::isCovered(KFunction *kf) const {
257+
return fullyCoveredFunctions.count(kf) != 0;
258+
}

0 commit comments

Comments
 (0)