From 077aeb2ef2d216d397ec010138203a95f84496f8 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Wed, 13 Sep 2023 01:11:21 +0400 Subject: [PATCH 1/4] [feat] Improve `TargetCalculator` --- include/klee/Module/CodeGraphDistance.h | 9 ++ include/klee/Module/KModule.h | 7 ++ lib/Core/ExecutionState.cpp | 4 +- lib/Core/ExecutionState.h | 2 +- lib/Core/TargetCalculator.cpp | 152 +++++++++++++++++------- lib/Core/TargetCalculator.h | 11 +- lib/Core/TargetManager.cpp | 8 +- lib/Module/CodeGraphDistance.cpp | 19 +++ 8 files changed, 161 insertions(+), 51 deletions(-) diff --git a/include/klee/Module/CodeGraphDistance.h b/include/klee/Module/CodeGraphDistance.h index 3192344ec4..3d8175a1e7 100644 --- a/include/klee/Module/CodeGraphDistance.h +++ b/include/klee/Module/CodeGraphDistance.h @@ -30,6 +30,9 @@ class CodeGraphDistance { std::unordered_map>>; + using functionBranchesSet = + std::unordered_map>>; + private: blockDistanceMap blockDistance; blockDistanceMap blockBackwardDistance; @@ -41,6 +44,8 @@ class CodeGraphDistance { functionDistanceList functionSortedDistance; functionDistanceList functionSortedBackwardDistance; + functionBranchesSet functionBranches; + private: void calculateDistance(KBlock *bb); void calculateBackwardDistance(KBlock *bb); @@ -48,6 +53,8 @@ class CodeGraphDistance { void calculateDistance(KFunction *kf); void calculateBackwardDistance(KFunction *kf); + void calculateFunctionBranches(KFunction *kf); + public: const std::unordered_map &getDistance(KBlock *kb); const std::unordered_map & @@ -68,6 +75,8 @@ class CodeGraphDistance { void getNearestPredicateSatisfying(KBlock *from, KBlockPredicate predicate, std::set &result); + + const std::map> &getFunctionBranches(KFunction *kf); }; } // namespace klee diff --git a/include/klee/Module/KModule.h b/include/klee/Module/KModule.h index bfd30c019b..e601d91e43 100644 --- a/include/klee/Module/KModule.h +++ b/include/klee/Module/KModule.h @@ -189,6 +189,13 @@ struct KFunction : public KCallable { } }; +struct KBlockCompare { + bool operator()(const KBlock *a, const KBlock *b) const { + return a->parent->id < b->parent->id || + (a->parent->id == b->parent->id && a->id < b->id); + } +}; + class KConstant { public: /// Actual LLVM constant this represents. diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index ddec6ec63b..5cc0a1dd1e 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -471,7 +471,7 @@ 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}); - level.insert(srcbb); + level.insert(prevPC->parent); } if (srcbb != dstbb) { transitionLevel.insert(std::make_pair(srcbb, dstbb)); @@ -483,7 +483,7 @@ bool ExecutionState::isGEPExpr(ref expr) const { } bool ExecutionState::visited(KBlock *block) const { - return level.count(block->basicBlock) != 0; + return level.count(block) != 0; } bool ExecutionState::reachedTarget(ref target) const { diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index c0bfeb7301..93d24d5d34 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -290,7 +290,7 @@ class ExecutionState { std::uint32_t depth = 0; /// @brief Exploration level, i.e., number of times KLEE cycled for this state - std::unordered_set level; + std::set level; std::unordered_set transitionLevel; /// @brief Address space used by this state (e.g. Global and Heap) diff --git a/lib/Core/TargetCalculator.cpp b/lib/Core/TargetCalculator.cpp index c2340e4af2..7116117077 100644 --- a/lib/Core/TargetCalculator.cpp +++ b/lib/Core/TargetCalculator.cpp @@ -39,28 +39,66 @@ llvm::cl::opt TargetCalculatorMode( void TargetCalculator::update(const ExecutionState &state) { Function *initialFunction = state.getInitPCBlock()->getParent(); - switch (TargetCalculatorMode) { - case TargetCalculateBy::Default: - blocksHistory[initialFunction][state.getPrevPCBlock()].insert( - state.getInitPCBlock()); - if (state.prevPC == state.prevPC->parent->getLastInstruction()) { - coveredBlocks[state.getPrevPCBlock()->getParent()].insert( - state.getPrevPCBlock()); + + if (state.prevPC == state.prevPC->parent->getLastInstruction()) { + coveredBlocks[state.getPrevPCBlock()->getParent()].insert( + state.prevPC->parent); + } + if (state.prevPC == state.prevPC->parent->getLastInstruction()) { + unsigned index = 0; + coveredBranches[state.prevPC->parent->parent][state.prevPC->parent]; + for (auto succ : successors(state.getPrevPCBlock())) { + if (succ == state.getPCBlock()) { + coveredBranches[state.prevPC->parent->parent][state.prevPC->parent] + .insert(index); + break; + } + ++index; } - if (state.prevPC == state.prevPC->parent->getLastInstruction()) { - unsigned index = 0; - coveredBranches[state.getPrevPCBlock()->getParent()] - [state.getPrevPCBlock()]; - for (auto succ : successors(state.getPrevPCBlock())) { - if (succ == state.getPCBlock()) { - coveredBranches[state.getPrevPCBlock()->getParent()] - [state.getPrevPCBlock()] - .insert(index); - break; + if (!coveredFunctionsInBranches.count(state.prevPC->parent->parent) && + codeGraphDistance.getFunctionBranches(state.prevPC->parent->parent) == + coveredBranches[state.prevPC->parent->parent]) { + coveredFunctionsInBranches.insert(state.prevPC->parent->parent); + } + if (!fullyCoveredFunctions.count(state.prevPC->parent->parent) && + coveredFunctionsInBranches.count(state.prevPC->parent->parent)) { + bool covered = true; + std::set fnsTaken; + std::deque fns; + fns.push_back(state.prevPC->parent->parent); + + while (!fns.empty() && covered) { + KFunction *currKF = fns.front(); + fnsTaken.insert(currKF); + for (auto &kcallBlock : currKF->kCallBlocks) { + if (kcallBlock->calledFunctions.size() == 1) { + auto calledFunction = *kcallBlock->calledFunctions.begin(); + KFunction *calledKFunction = state.prevPC->parent->parent->parent + ->functionMap[calledFunction]; + if (calledKFunction->numInstructions != 0 && + coveredFunctionsInBranches.count(calledKFunction) == 0) { + covered = false; + break; + } + if (!fnsTaken.count(calledKFunction) && + fullyCoveredFunctions.count(calledKFunction) == 0) { + fns.push_back(calledKFunction); + } + } } - ++index; + fns.pop_front(); + } + + if (covered) { + fullyCoveredFunctions.insert(state.prevPC->parent->parent); } } + } + + switch (TargetCalculatorMode) { + case TargetCalculateBy::Default: + blocksHistory[initialFunction][state.getPrevPCBlock()].insert( + state.initPC->parent); break; case TargetCalculateBy::Blocks: @@ -81,10 +119,10 @@ bool TargetCalculator::differenceIsEmpty( const ExecutionState &state, const std::unordered_map &history, KBlock *target) { - std::vector diff; - std::set left(state.level.begin(), state.level.end()); - std::set right(history.at(target->basicBlock).begin(), - history.at(target->basicBlock).end()); + std::vector diff; + std::set left(state.level.begin(), state.level.end()); + std::set right(history.at(target->basicBlock).begin(), + history.at(target->basicBlock).end()); std::set_difference(left.begin(), left.end(), right.begin(), right.end(), std::inserter(diff, diff.begin())); return diff.empty(); @@ -112,27 +150,35 @@ bool TargetCalculator::uncoveredBlockPredicate(ExecutionState *state, std::unordered_map &transitionHistory = transitionsHistory[initialFunction]; bool result = false; + if (coveredBranches[kblock->parent].count(kblock) == 0) { + result = true; + } else { + auto &cb = coveredBranches[kblock->parent][kblock]; + if (isa(kblock) && + cast(kblock)->calledFunctions.size() == 1) { + auto calledFunction = *cast(kblock)->calledFunctions.begin(); + KFunction *calledKFunction = + kblock->parent->parent->functionMap[calledFunction]; + result = fullyCoveredFunctions.count(calledKFunction) == 0 && + calledKFunction->numInstructions; + } + result |= + kblock->basicBlock->getTerminator()->getNumSuccessors() > cb.size(); + } + switch (TargetCalculatorMode) { case TargetCalculateBy::Default: { - if (coveredBranches[kblock->parent->function].count(kblock->basicBlock) == - 0) { - result = true; - } else { - auto &cb = coveredBranches[kblock->parent->function][kblock->basicBlock]; - result = - kblock->basicBlock->getTerminator()->getNumSuccessors() > cb.size(); - } break; } case TargetCalculateBy::Blocks: { if (history[kblock->basicBlock].size() != 0) { - result = !differenceIsEmpty(*state, history, kblock); + result |= !differenceIsEmpty(*state, history, kblock); } break; } case TargetCalculateBy::Transitions: { if (history[kblock->basicBlock].size() != 0) { - result = !differenceIsEmpty(*state, transitionHistory, kblock); + result |= !differenceIsEmpty(*state, transitionHistory, kblock); } break; } @@ -146,6 +192,10 @@ TargetHashSet TargetCalculator::calculate(ExecutionState &state) { const KModule &module = *state.pc->parent->parent->parent; KFunction *kf = module.functionMap.at(bb->getParent()); KBlock *kb = kf->blockMap[bb]; + kb = !isa(kb) || (kb->getLastInstruction() != state.pc) + ? kb + : kf->blockMap[state.pc->parent->basicBlock->getTerminator() + ->getSuccessor(0)]; for (auto sfi = state.stack.callStack().rbegin(), sfe = state.stack.callStack().rend(); sfi != sfe; sfi++) { @@ -160,17 +210,30 @@ TargetHashSet TargetCalculator::calculate(ExecutionState &state) { if (!blocks.empty()) { TargetHashSet targets; for (auto block : blocks) { - if (coveredBranches[block->parent->function].count(block->basicBlock) == - 0) { - targets.insert(ReachBlockTarget::create(block, true)); + if (coveredBranches[block->parent].count(block) == 0) { + targets.insert(ReachBlockTarget::create(block, false)); } else { - auto &cb = - coveredBranches[block->parent->function][block->basicBlock]; - for (unsigned index = 0; - index < block->basicBlock->getTerminator()->getNumSuccessors(); - ++index) { - if (!cb.count(index)) - targets.insert(CoverBranchTarget::create(block, index)); + auto &cb = coveredBranches[block->parent][block]; + bool notCoveredFunction = false; + if (isa(block) && + cast(block)->calledFunctions.size() == 1) { + auto calledFunction = + *cast(block)->calledFunctions.begin(); + KFunction *calledKFunction = + block->parent->parent->functionMap[calledFunction]; + notCoveredFunction = + fullyCoveredFunctions.count(calledKFunction) == 0 && + calledKFunction->numInstructions; + } + if (notCoveredFunction) { + targets.insert(ReachBlockTarget::create(block, true)); + } else { + for (unsigned index = 0; + index < block->basicBlock->getTerminator()->getNumSuccessors(); + ++index) { + if (!cb.count(index)) + targets.insert(CoverBranchTarget::create(block, index)); + } } } } @@ -179,6 +242,11 @@ TargetHashSet TargetCalculator::calculate(ExecutionState &state) { if (sfi->caller) { kb = sfi->caller->parent; + + kb = !isa(kb) || (kb->getLastInstruction() != sfi->caller) + ? kb + : kf->blockMap[sfi->caller->parent->basicBlock->getTerminator() + ->getSuccessor(0)]; } } diff --git a/lib/Core/TargetCalculator.h b/lib/Core/TargetCalculator.h index 7369519afd..d3a3937ed5 100644 --- a/lib/Core/TargetCalculator.h +++ b/lib/Core/TargetCalculator.h @@ -42,7 +42,7 @@ typedef std::pair Transition; typedef std::pair Branch; class TargetCalculator { - typedef std::unordered_set VisitedBlocks; + typedef std::unordered_set VisitedBlocks; typedef std::unordered_set VisitedTransitions; typedef std::unordered_set VisitedBranches; @@ -56,11 +56,12 @@ class TargetCalculator { std::unordered_map> TransitionsHistory; - typedef std::unordered_map< - llvm::Function *, - std::unordered_map>> + typedef std::unordered_map>> CoveredBranches; + typedef std::unordered_set CoveredFunctionsBranches; + typedef std::unordered_map CoveredBlocks; public: @@ -76,6 +77,8 @@ class TargetCalculator { BlocksHistory blocksHistory; TransitionsHistory transitionsHistory; CoveredBranches coveredBranches; + CoveredFunctionsBranches coveredFunctionsInBranches; + CoveredFunctionsBranches fullyCoveredFunctions; CoveredBlocks coveredBlocks; bool differenceIsEmpty( diff --git a/lib/Core/TargetManager.cpp b/lib/Core/TargetManager.cpp index 58579130fb..a25ff130e0 100644 --- a/lib/Core/TargetManager.cpp +++ b/lib/Core/TargetManager.cpp @@ -41,7 +41,11 @@ void TargetManager::updateDone(ExecutionState &state, ref target) { setHistory(state, stateTargetForest.getHistory()); if (guidance == Interpreter::GuidanceKind::CoverageGuidance || target->shouldFailOnThisTarget()) { - reachedTargets.insert(target); + if (target->shouldFailOnThisTarget() || + !isa(target->getBlock())) { + reachedTargets.insert(target); + } + for (auto es : states) { if (isTargeted(*es)) { auto &esTargetForest = targetForest(*es); @@ -112,7 +116,7 @@ void TargetManager::updateReached(ExecutionState &state) { if (state.getPrevPCBlock()->getTerminator()->getNumSuccessors() == 0) { target = ReachBlockTarget::create(state.prevPC->parent, true); - } else { + } else if (!isa(state.prevPC->parent)) { unsigned index = 0; for (auto succ : successors(state.getPrevPCBlock())) { if (succ == state.getPCBlock()) { diff --git a/lib/Module/CodeGraphDistance.cpp b/lib/Module/CodeGraphDistance.cpp index 54839c6cd3..988cb4db27 100644 --- a/lib/Module/CodeGraphDistance.cpp +++ b/lib/Module/CodeGraphDistance.cpp @@ -120,6 +120,18 @@ void CodeGraphDistance::calculateBackwardDistance(KFunction *kf) { } } +void CodeGraphDistance::calculateFunctionBranches(KFunction *kf) { + std::map> &fbranches = functionBranches[kf]; + for (auto &kb : kf->blocks) { + fbranches[kb.get()]; + for (unsigned branch = 0; + branch < kb->basicBlock->getTerminator()->getNumSuccessors(); + ++branch) { + fbranches[kb.get()].insert(branch); + } + } +} + const std::unordered_map & CodeGraphDistance::getDistance(KBlock *kb) { if (blockDistance.count(kb) == 0) @@ -200,3 +212,10 @@ void CodeGraphDistance::getNearestPredicateSatisfying( nodes.pop_front(); } } + +const std::map> & +CodeGraphDistance::getFunctionBranches(KFunction *kf) { + if (functionBranches.count(kf) == 0) + calculateFunctionBranches(kf); + return functionBranches.at(kf); +} From e2a864e7cb68c97cfc80bb2253c9c35cd0290dc8 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Thu, 14 Sep 2023 17:29:51 +0400 Subject: [PATCH 2/4] [refactor] `CodeGraphDistance` -> `CodeGraphInfo` --- .../{CodeGraphDistance.h => CodeGraphInfo.h} | 7 ++-- lib/Core/DistanceCalculator.cpp | 12 +++--- lib/Core/DistanceCalculator.h | 8 ++-- lib/Core/Executor.cpp | 10 ++--- lib/Core/Executor.h | 4 +- lib/Core/TargetCalculator.cpp | 36 ++++++++++-------- lib/Core/TargetCalculator.h | 8 ++-- lib/Core/TargetedExecutionManager.cpp | 12 +++--- lib/Core/TargetedExecutionManager.h | 8 ++-- lib/Module/CMakeLists.txt | 2 +- ...odeGraphDistance.cpp => CodeGraphInfo.cpp} | 38 +++++++++---------- lib/Module/Target.cpp | 2 +- 12 files changed, 76 insertions(+), 71 deletions(-) rename include/klee/Module/{CodeGraphDistance.h => CodeGraphInfo.h} (93%) rename lib/Module/{CodeGraphDistance.cpp => CodeGraphInfo.cpp} (84%) diff --git a/include/klee/Module/CodeGraphDistance.h b/include/klee/Module/CodeGraphInfo.h similarity index 93% rename from include/klee/Module/CodeGraphDistance.h rename to include/klee/Module/CodeGraphInfo.h index 3d8175a1e7..c13a7a0337 100644 --- a/include/klee/Module/CodeGraphDistance.h +++ b/include/klee/Module/CodeGraphInfo.h @@ -1,4 +1,4 @@ -//===-- CodeGraphDistance.h -------------------------------------*- C++ -*-===// +//===-- CodeGraphInfo.h -----------------------------------------*- C++ -*-===// // // The KLEE Symbolic Virtual Machine // @@ -16,7 +16,7 @@ namespace klee { -class CodeGraphDistance { +class CodeGraphInfo { using blockDistanceMap = std::unordered_map>; @@ -76,7 +76,8 @@ class CodeGraphDistance { void getNearestPredicateSatisfying(KBlock *from, KBlockPredicate predicate, std::set &result); - const std::map> &getFunctionBranches(KFunction *kf); + const std::map> & + getFunctionBranches(KFunction *kf); }; } // namespace klee diff --git a/lib/Core/DistanceCalculator.cpp b/lib/Core/DistanceCalculator.cpp index 15cf13be42..ebfdb7eec6 100644 --- a/lib/Core/DistanceCalculator.cpp +++ b/lib/Core/DistanceCalculator.cpp @@ -9,7 +9,7 @@ #include "DistanceCalculator.h" #include "ExecutionState.h" -#include "klee/Module/CodeGraphDistance.h" +#include "klee/Module/CodeGraphInfo.h" #include "klee/Module/KInstruction.h" #include "klee/Module/Target.h" @@ -67,7 +67,7 @@ DistanceResult DistanceCalculator::getDistance(KBlock *kb, TargetKind kind, DistanceResult DistanceCalculator::computeDistance(KBlock *kb, TargetKind kind, KBlock *target) const { const auto &distanceToTargetFunction = - codeGraphDistance.getBackwardDistance(target->parent); + codeGraphInfo.getBackwardDistance(target->parent); weight_type weight = 0; WeightResult res = Miss; bool isInsideFunction = true; @@ -99,7 +99,7 @@ DistanceResult DistanceCalculator::getDistance( KBlock *kb = pc->parent; const auto &distanceToTargetFunction = - codeGraphDistance.getBackwardDistance(target->parent); + codeGraphInfo.getBackwardDistance(target->parent); unsigned int minCallWeight = UINT_MAX, minSfNum = UINT_MAX, sfNum = 0; auto sfi = frames.rbegin(), sfe = frames.rend(); bool strictlyAfterKB = @@ -145,7 +145,7 @@ bool DistanceCalculator::distanceInCallGraph( KBlock *target, bool strictlyAfterKB) const { distance = UINT_MAX; const std::unordered_map &dist = - codeGraphDistance.getDistance(origKB); + codeGraphInfo.getDistance(origKB); KBlock *targetBB = target; KFunction *targetF = targetBB->parent; @@ -176,7 +176,7 @@ bool DistanceCalculator::distanceInCallGraph( KBlock *target) const { distance = UINT_MAX; const std::unordered_map &dist = - codeGraphDistance.getDistance(kb); + codeGraphInfo.getDistance(kb); for (auto &kCallBlock : kf->kCallBlocks) { if (dist.count(kCallBlock) == 0) @@ -199,7 +199,7 @@ DistanceCalculator::tryGetLocalWeight(KBlock *kb, weight_type &weight, KFunction *currentKF = kb->parent; KBlock *currentKB = kb; const std::unordered_map &dist = - codeGraphDistance.getDistance(currentKB); + codeGraphInfo.getDistance(currentKB); weight = UINT_MAX; for (auto &end : localTargets) { if (dist.count(end) > 0) { diff --git a/lib/Core/DistanceCalculator.h b/lib/Core/DistanceCalculator.h index f0fdd15655..77da2254f1 100644 --- a/lib/Core/DistanceCalculator.h +++ b/lib/Core/DistanceCalculator.h @@ -17,7 +17,7 @@ class BasicBlock; } // namespace llvm namespace klee { -class CodeGraphDistance; +class CodeGraphInfo; class Target; enum WeightResult : std::uint8_t { @@ -45,8 +45,8 @@ struct DistanceResult { class DistanceCalculator { public: - explicit DistanceCalculator(CodeGraphDistance &codeGraphDistance_) - : codeGraphDistance(codeGraphDistance_) {} + explicit DistanceCalculator(CodeGraphInfo &codeGraphInfo_) + : codeGraphInfo(codeGraphInfo_) {} DistanceResult getDistance(const ExecutionState &es, KBlock *target); @@ -97,7 +97,7 @@ class DistanceCalculator { using StatesSet = std::unordered_set; - CodeGraphDistance &codeGraphDistance; + CodeGraphInfo &codeGraphInfo; TargetToSpeculativeStateToDistanceResultMap distanceResultCache; StatesSet localStates; diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 8bc362391d..ff355b0b9f 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -50,7 +50,7 @@ #include "klee/Expr/IndependentSet.h" #include "klee/Expr/Symcrete.h" #include "klee/Module/Cell.h" -#include "klee/Module/CodeGraphDistance.h" +#include "klee/Module/CodeGraphInfo.h" #include "klee/Module/InstructionInfoTable.h" #include "klee/Module/KCallable.h" #include "klee/Module/KInstruction.h" @@ -455,13 +455,13 @@ Executor::Executor(LLVMContext &ctx, const InterpreterOptions &opts, externalDispatcher(new ExternalDispatcher(ctx)), statsTracker(0), pathWriter(0), symPathWriter(0), specialFunctionHandler(0), timers{time::Span(TimerInterval)}, - guidanceKind(opts.Guidance), codeGraphDistance(new CodeGraphDistance()), - distanceCalculator(new DistanceCalculator(*codeGraphDistance)), - targetCalculator(new TargetCalculator(*codeGraphDistance)), + guidanceKind(opts.Guidance), codeGraphInfo(new CodeGraphInfo()), + distanceCalculator(new DistanceCalculator(*codeGraphInfo)), + targetCalculator(new TargetCalculator(*codeGraphInfo)), targetManager(new TargetManager(guidanceKind, *distanceCalculator, *targetCalculator)), targetedExecutionManager( - new TargetedExecutionManager(*codeGraphDistance, *targetManager)), + new TargetedExecutionManager(*codeGraphInfo, *targetManager)), replayKTest(0), replayPath(0), usingSeeds(0), atMemoryLimit(false), inhibitForking(false), haltExecution(HaltExecution::NotHalt), ivcEnabled(false), debugLogBuffer(debugBufferString) { diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index d66ff5986f..9a89fdcf04 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -72,7 +72,7 @@ namespace klee { class AddressManager; class Array; struct Cell; -class CodeGraphDistance; +class CodeGraphInfo; class DistanceCalculator; class ExecutionState; class ExternalDispatcher; @@ -146,7 +146,7 @@ class Executor : public Interpreter { TimerGroup timers; std::unique_ptr processForest; GuidanceKind guidanceKind; - std::unique_ptr codeGraphDistance; + std::unique_ptr codeGraphInfo; std::unique_ptr distanceCalculator; std::unique_ptr targetCalculator; std::unique_ptr targetManager; diff --git a/lib/Core/TargetCalculator.cpp b/lib/Core/TargetCalculator.cpp index 7116117077..6b6a2f7d3e 100644 --- a/lib/Core/TargetCalculator.cpp +++ b/lib/Core/TargetCalculator.cpp @@ -11,7 +11,7 @@ #include "ExecutionState.h" -#include "klee/Module/CodeGraphDistance.h" +#include "klee/Module/CodeGraphInfo.h" #include "klee/Module/KInstruction.h" #include "klee/Module/Target.h" #include "klee/Module/TargetHash.h" @@ -44,21 +44,24 @@ void TargetCalculator::update(const ExecutionState &state) { coveredBlocks[state.getPrevPCBlock()->getParent()].insert( state.prevPC->parent); } - if (state.prevPC == state.prevPC->parent->getLastInstruction()) { - unsigned index = 0; - coveredBranches[state.prevPC->parent->parent][state.prevPC->parent]; - for (auto succ : successors(state.getPrevPCBlock())) { - if (succ == state.getPCBlock()) { - coveredBranches[state.prevPC->parent->parent][state.prevPC->parent] - .insert(index); - break; + if (state.prevPC == state.prevPC->parent->getLastInstruction() && + !fullyCoveredFunctions.count(state.prevPC->parent->parent)) { + + if (!coveredFunctionsInBranches.count(state.prevPC->parent->parent)) { + unsigned index = 0; + coveredBranches[state.prevPC->parent->parent][state.prevPC->parent]; + for (auto succ : successors(state.getPrevPCBlock())) { + if (succ == state.getPCBlock()) { + coveredBranches[state.prevPC->parent->parent][state.prevPC->parent] + .insert(index); + break; + } + ++index; + } + if (codeGraphInfo.getFunctionBranches(state.prevPC->parent->parent) == + coveredBranches[state.prevPC->parent->parent]) { + coveredFunctionsInBranches.insert(state.prevPC->parent->parent); } - ++index; - } - if (!coveredFunctionsInBranches.count(state.prevPC->parent->parent) && - codeGraphDistance.getFunctionBranches(state.prevPC->parent->parent) == - coveredBranches[state.prevPC->parent->parent]) { - coveredFunctionsInBranches.insert(state.prevPC->parent->parent); } if (!fullyCoveredFunctions.count(state.prevPC->parent->parent) && coveredFunctionsInBranches.count(state.prevPC->parent->parent)) { @@ -205,7 +208,7 @@ TargetHashSet TargetCalculator::calculate(ExecutionState &state) { using std::placeholders::_1; KBlockPredicate func = std::bind(&TargetCalculator::uncoveredBlockPredicate, this, &state, _1); - codeGraphDistance.getNearestPredicateSatisfying(kb, func, blocks); + codeGraphInfo.getNearestPredicateSatisfying(kb, func, blocks); if (!blocks.empty()) { TargetHashSet targets; @@ -237,6 +240,7 @@ TargetHashSet TargetCalculator::calculate(ExecutionState &state) { } } } + assert(!targets.empty()); return targets; } diff --git a/lib/Core/TargetCalculator.h b/lib/Core/TargetCalculator.h index d3a3937ed5..ba78a89f20 100644 --- a/lib/Core/TargetCalculator.h +++ b/lib/Core/TargetCalculator.h @@ -32,7 +32,7 @@ DISABLE_WARNING_POP #include namespace klee { -class CodeGraphDistance; +class CodeGraphInfo; class ExecutionState; struct TransitionHash; @@ -65,15 +65,15 @@ class TargetCalculator { typedef std::unordered_map CoveredBlocks; public: - TargetCalculator(CodeGraphDistance &codeGraphDistance) - : codeGraphDistance(codeGraphDistance) {} + TargetCalculator(CodeGraphInfo &codeGraphInfo) + : codeGraphInfo(codeGraphInfo) {} void update(const ExecutionState &state); TargetHashSet calculate(ExecutionState &state); private: - CodeGraphDistance &codeGraphDistance; + CodeGraphInfo &codeGraphInfo; BlocksHistory blocksHistory; TransitionsHistory transitionsHistory; CoveredBranches coveredBranches; diff --git a/lib/Core/TargetedExecutionManager.cpp b/lib/Core/TargetedExecutionManager.cpp index 25357773ae..3e421a684e 100644 --- a/lib/Core/TargetedExecutionManager.cpp +++ b/lib/Core/TargetedExecutionManager.cpp @@ -14,7 +14,7 @@ #include "ExecutionState.h" #include "klee/Core/TerminationTypes.h" -#include "klee/Module/CodeGraphDistance.h" +#include "klee/Module/CodeGraphInfo.h" #include "klee/Module/KInstruction.h" #include "klee/Support/ErrorHandling.h" @@ -375,18 +375,18 @@ bool TargetedExecutionManager::canReach(const ref &from, return true; } - const auto &blockDist = codeGraphDistance.getDistance(fromBlock); + const auto &blockDist = codeGraphInfo.getDistance(fromBlock); if (blockDist.count(toBlock) != 0) { return true; } } else { - const auto &funcDist = codeGraphDistance.getDistance(fromKf); + const auto &funcDist = codeGraphInfo.getDistance(fromKf); if (funcDist.count(toKf) != 0) { return true; } const auto &backwardFuncDist = - codeGraphDistance.getBackwardDistance(fromKf); + codeGraphInfo.getBackwardDistance(fromKf); if (backwardFuncDist.count(toKf) != 0) { return true; } @@ -449,7 +449,7 @@ KFunction *TargetedExecutionManager::tryResolveEntryFunction( if (i == j) { continue; } - const auto &funcDist = codeGraphDistance.getDistance(resKf); + const auto &funcDist = codeGraphInfo.getDistance(resKf); std::vector currKFs; for (auto block : locToBlocks[result.locations[j]]) { @@ -462,7 +462,7 @@ KFunction *TargetedExecutionManager::tryResolveEntryFunction( for (size_t m = 0; m < currKFs.size() && !curKf; ++m) { curKf = currKFs.at(m); if (funcDist.count(curKf) == 0) { - const auto &curFuncDist = codeGraphDistance.getDistance(curKf); + const auto &curFuncDist = codeGraphInfo.getDistance(curKf); if (curFuncDist.count(resKf) == 0) { curKf = nullptr; } else { diff --git a/lib/Core/TargetedExecutionManager.h b/lib/Core/TargetedExecutionManager.h index 7d5b285d41..e5a66cbf32 100644 --- a/lib/Core/TargetedExecutionManager.h +++ b/lib/Core/TargetedExecutionManager.h @@ -60,7 +60,7 @@ extern llvm::cl::opt TimerInterval; extern llvm::cl::opt MaxCycles; -class CodeGraphDistance; +class CodeGraphInfo; class TargetedHaltsOnTraces { using HaltTypeToConfidence = @@ -116,7 +116,7 @@ class TargetedExecutionManager { KFunction *tryResolveEntryFunction(const Result &result, LocationToBlocks &locToBlocks) const; - CodeGraphDistance &codeGraphDistance; + CodeGraphInfo &codeGraphInfo; TargetManager &targetManager; StatesSet localStates; @@ -127,9 +127,9 @@ class TargetedExecutionManager { } }; - explicit TargetedExecutionManager(CodeGraphDistance &codeGraphDistance_, + explicit TargetedExecutionManager(CodeGraphInfo &codeGraphInfo_, TargetManager &targetManager_) - : codeGraphDistance(codeGraphDistance_), targetManager(targetManager_) {} + : codeGraphInfo(codeGraphInfo_), targetManager(targetManager_) {} ~TargetedExecutionManager() = default; std::map, KFunctionLess> prepareTargets(KModule *kmodule, SarifReport paths); diff --git a/lib/Module/CMakeLists.txt b/lib/Module/CMakeLists.txt index 0bdb2e67eb..81112acbe0 100644 --- a/lib/Module/CMakeLists.txt +++ b/lib/Module/CMakeLists.txt @@ -9,7 +9,7 @@ set(KLEE_MODULE_COMPONENT_SRCS CallSplitter.cpp Checks.cpp - CodeGraphDistance.cpp + CodeGraphInfo.cpp FunctionAlias.cpp InstructionInfoTable.cpp InstructionOperandTypeCheckPass.cpp diff --git a/lib/Module/CodeGraphDistance.cpp b/lib/Module/CodeGraphInfo.cpp similarity index 84% rename from lib/Module/CodeGraphDistance.cpp rename to lib/Module/CodeGraphInfo.cpp index 988cb4db27..045c379932 100644 --- a/lib/Module/CodeGraphDistance.cpp +++ b/lib/Module/CodeGraphInfo.cpp @@ -1,5 +1,4 @@ -//===-- CodeGraphDistance.cpp -//---------------------------------------------------===// +//===-- CodeGraphInfo.cpp -------------------------------------------------===// // // The KLEE Symbolic Virtual Machine // @@ -8,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "klee/Module/CodeGraphDistance.h" +#include "klee/Module/CodeGraphInfo.h" #include "klee/Support/CompilerWarning.h" DISABLE_WARNING_PUSH @@ -21,7 +20,7 @@ DISABLE_WARNING_POP using namespace klee; -void CodeGraphDistance::calculateDistance(KBlock *bb) { +void CodeGraphInfo::calculateDistance(KBlock *bb) { auto blockMap = bb->parent->blockMap; std::unordered_map &dist = blockDistance[bb]; std::vector> &sort = blockSortedDistance[bb]; @@ -42,7 +41,7 @@ void CodeGraphDistance::calculateDistance(KBlock *bb) { } } -void CodeGraphDistance::calculateBackwardDistance(KBlock *bb) { +void CodeGraphInfo::calculateBackwardDistance(KBlock *bb) { auto blockMap = bb->parent->blockMap; std::unordered_map &bdist = blockBackwardDistance[bb]; std::vector> &bsort = @@ -64,7 +63,7 @@ void CodeGraphDistance::calculateBackwardDistance(KBlock *bb) { } } -void CodeGraphDistance::calculateDistance(KFunction *kf) { +void CodeGraphInfo::calculateDistance(KFunction *kf) { auto &functionMap = kf->parent->functionMap; std::unordered_map &dist = functionDistance[kf]; std::vector> &sort = @@ -92,7 +91,7 @@ void CodeGraphDistance::calculateDistance(KFunction *kf) { } } -void CodeGraphDistance::calculateBackwardDistance(KFunction *kf) { +void CodeGraphInfo::calculateBackwardDistance(KFunction *kf) { auto &functionMap = kf->parent->functionMap; auto &callMap = kf->parent->callMap; std::unordered_map &bdist = @@ -120,7 +119,7 @@ void CodeGraphDistance::calculateBackwardDistance(KFunction *kf) { } } -void CodeGraphDistance::calculateFunctionBranches(KFunction *kf) { +void CodeGraphInfo::calculateFunctionBranches(KFunction *kf) { std::map> &fbranches = functionBranches[kf]; for (auto &kb : kf->blocks) { fbranches[kb.get()]; @@ -133,63 +132,64 @@ void CodeGraphDistance::calculateFunctionBranches(KFunction *kf) { } const std::unordered_map & -CodeGraphDistance::getDistance(KBlock *kb) { +CodeGraphInfo::getDistance(KBlock *kb) { if (blockDistance.count(kb) == 0) calculateDistance(kb); return blockDistance.at(kb); } const std::unordered_map & -CodeGraphDistance::getBackwardDistance(KBlock *kb) { +CodeGraphInfo::getBackwardDistance(KBlock *kb) { if (blockBackwardDistance.count(kb) == 0) calculateBackwardDistance(kb); return blockBackwardDistance.at(kb); } const std::vector> & -CodeGraphDistance::getSortedDistance(KBlock *kb) { +CodeGraphInfo::getSortedDistance(KBlock *kb) { if (blockDistance.count(kb) == 0) calculateDistance(kb); return blockSortedDistance.at(kb); } const std::vector> & -CodeGraphDistance::getSortedBackwardDistance(KBlock *kb) { +CodeGraphInfo::getSortedBackwardDistance(KBlock *kb) { if (blockBackwardDistance.count(kb) == 0) calculateBackwardDistance(kb); return blockSortedBackwardDistance.at(kb); } const std::unordered_map & -CodeGraphDistance::getDistance(KFunction *kf) { +CodeGraphInfo::getDistance(KFunction *kf) { if (functionDistance.count(kf) == 0) calculateDistance(kf); return functionDistance.at(kf); } const std::unordered_map & -CodeGraphDistance::getBackwardDistance(KFunction *kf) { +CodeGraphInfo::getBackwardDistance(KFunction *kf) { if (functionBackwardDistance.count(kf) == 0) calculateBackwardDistance(kf); return functionBackwardDistance.at(kf); } const std::vector> & -CodeGraphDistance::getSortedDistance(KFunction *kf) { +CodeGraphInfo::getSortedDistance(KFunction *kf) { if (functionDistance.count(kf) == 0) calculateDistance(kf); return functionSortedDistance.at(kf); } const std::vector> & -CodeGraphDistance::getSortedBackwardDistance(KFunction *kf) { +CodeGraphInfo::getSortedBackwardDistance(KFunction *kf) { if (functionBackwardDistance.count(kf) == 0) calculateBackwardDistance(kf); return functionSortedBackwardDistance.at(kf); } -void CodeGraphDistance::getNearestPredicateSatisfying( - KBlock *from, KBlockPredicate predicate, std::set &result) { +void CodeGraphInfo::getNearestPredicateSatisfying(KBlock *from, + KBlockPredicate predicate, + std::set &result) { std::set visited; auto blockMap = from->parent->blockMap; @@ -214,7 +214,7 @@ void CodeGraphDistance::getNearestPredicateSatisfying( } const std::map> & -CodeGraphDistance::getFunctionBranches(KFunction *kf) { +CodeGraphInfo::getFunctionBranches(KFunction *kf) { if (functionBranches.count(kf) == 0) calculateFunctionBranches(kf); return functionBranches.at(kf); diff --git a/lib/Module/Target.cpp b/lib/Module/Target.cpp index 633d27f2a6..916e73f0e8 100644 --- a/lib/Module/Target.cpp +++ b/lib/Module/Target.cpp @@ -10,7 +10,7 @@ #include "klee/Module/Target.h" #include "klee/Module/TargetHash.h" -#include "klee/Module/CodeGraphDistance.h" +#include "klee/Module/CodeGraphInfo.h" #include "klee/Module/KInstruction.h" #include From d0d4033a4fde0da50e628c572d2491f726d70e06 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Fri, 15 Sep 2023 17:47:30 +0400 Subject: [PATCH 3/4] [feat] Improve `only-output-states-covering-new` option --- include/klee/ADT/Ref.h | 12 ++++++++++++ lib/Core/ExecutionState.cpp | 16 +++++++++------- lib/Core/ExecutionState.h | 2 +- lib/Core/Executor.cpp | 8 +++++--- lib/Core/StatsTracker.cpp | 3 --- lib/Core/TargetCalculator.cpp | 17 ++++++++++++++--- 6 files changed, 41 insertions(+), 17 deletions(-) diff --git a/include/klee/ADT/Ref.h b/include/klee/ADT/Ref.h index 45e9632ca2..008580cc7e 100644 --- a/include/klee/ADT/Ref.h +++ b/include/klee/ADT/Ref.h @@ -227,6 +227,18 @@ inline std::stringstream &operator<<(std::stringstream &os, const ref &e) { return os; } +template class box { + friend class ref>; + +private: + /// @brief Required by klee::ref-managed objects + class ReferenceCounter _refCount; + +public: + box(T value_) : value(value_) {} + ReferenceCounter count() { return _refCount; } + T value; +}; } // end namespace klee namespace llvm { diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index 5cc0a1dd1e..2b37c844ce 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -128,8 +128,9 @@ ExecutionState::ExecutionState() : initPC(nullptr), pc(nullptr), prevPC(nullptr), incomingBBIndex(-1), depth(0), ptreeNode(nullptr), steppedInstructions(0), steppedMemoryInstructions(0), instsSinceCovNew(0), - roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), - forkDisabled(false), prevHistory_(TargetsHistory::create()), + roundingMode(llvm::APFloat::rmNearestTiesToEven), + coveredNew(new box(false)), forkDisabled(false), + prevHistory_(TargetsHistory::create()), history_(TargetsHistory::create()) { setID(); } @@ -138,8 +139,9 @@ ExecutionState::ExecutionState(KFunction *kf) : initPC(kf->instructions), pc(initPC), prevPC(pc), incomingBBIndex(-1), depth(0), ptreeNode(nullptr), steppedInstructions(0), steppedMemoryInstructions(0), instsSinceCovNew(0), - roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), - forkDisabled(false), prevHistory_(TargetsHistory::create()), + roundingMode(llvm::APFloat::rmNearestTiesToEven), + coveredNew(new box(false)), forkDisabled(false), + prevHistory_(TargetsHistory::create()), history_(TargetsHistory::create()) { pushFrame(nullptr, kf); setID(); @@ -149,8 +151,9 @@ ExecutionState::ExecutionState(KFunction *kf, KBlock *kb) : initPC(kb->instructions), pc(initPC), prevPC(pc), incomingBBIndex(-1), depth(0), ptreeNode(nullptr), steppedInstructions(0), steppedMemoryInstructions(0), instsSinceCovNew(0), - roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), - forkDisabled(false), prevHistory_(TargetsHistory::create()), + roundingMode(llvm::APFloat::rmNearestTiesToEven), + coveredNew(new box(false)), forkDisabled(false), + prevHistory_(TargetsHistory::create()), history_(TargetsHistory::create()) { pushFrame(nullptr, kf); setID(); @@ -189,7 +192,6 @@ ExecutionState *ExecutionState::branch() { auto *falseState = new ExecutionState(*this); falseState->setID(); - falseState->coveredNew = false; falseState->coveredLines.clear(); return falseState; diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index 93d24d5d34..8853cb2536 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -369,7 +369,7 @@ class ExecutionState { std::uint32_t id = 0; /// @brief Whether a new instruction was covered in this state - bool coveredNew = false; + mutable ref> coveredNew; /// @brief Disables forking for this state. Set by user code bool forkDisabled = false; diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index ff355b0b9f..761f832c47 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -4053,7 +4053,7 @@ bool Executor::checkMemoryUsage() { unsigned idx = theRNG.getInt32() % N; // Make two pulls to try and not hit a state that // covered new code. - if (arr[idx]->coveredNew) + if (arr[idx]->coveredNew->value) idx = theRNG.getInt32() % N; std::swap(arr[idx], arr[N - 1]); @@ -4512,7 +4512,9 @@ void Executor::terminateState(ExecutionState &state, } static bool shouldWriteTest(const ExecutionState &state) { - return !OnlyOutputStatesCoveringNew || state.coveredNew; + bool coveredNew = state.coveredNew->value; + state.coveredNew->value = false; + return !OnlyOutputStatesCoveringNew || coveredNew; } static std::string terminationTypeFileExtension(StateTerminationType type) { @@ -7273,7 +7275,7 @@ void Executor::dumpStates() { *os << "{"; *os << "'depth' : " << es->depth << ", "; *os << "'queryCost' : " << es->queryMetaData.queryCost << ", "; - *os << "'coveredNew' : " << es->coveredNew << ", "; + *os << "'coveredNew' : " << es->coveredNew->value << ", "; *os << "'instsSinceCovNew' : " << es->instsSinceCovNew << ", "; *os << "'md2u' : " << md2u << ", "; *os << "'icnt' : " << icnt << ", "; diff --git a/lib/Core/StatsTracker.cpp b/lib/Core/StatsTracker.cpp index 372c6fc61b..e662644be3 100644 --- a/lib/Core/StatsTracker.cpp +++ b/lib/Core/StatsTracker.cpp @@ -411,7 +411,6 @@ void StatsTracker::stepInstruction(ExecutionState &es) { // FIXME: This trick no longer works, we should fix this in the line // number propogation. es.coveredLines[&ii.file].insert(ii.line); - es.coveredNew = true; es.instsSinceCovNew = 1; ++stats::coveredInstructions; stats::uncoveredInstructions += (uint64_t)-1; @@ -480,7 +479,6 @@ void StatsTracker::markBranchVisited(ExecutionState *visitedTrue, uint64_t hasFalse = theStatisticManager->getIndexedValue(stats::falseBranches, id); if (visitedTrue && !hasTrue) { - visitedTrue->coveredNew = true; visitedTrue->instsSinceCovNew = 1; ++stats::trueBranches; if (hasFalse) { @@ -491,7 +489,6 @@ void StatsTracker::markBranchVisited(ExecutionState *visitedTrue, hasTrue = 1; } if (visitedFalse && !hasFalse) { - visitedFalse->coveredNew = true; visitedFalse->instsSinceCovNew = 1; ++stats::falseBranches; if (hasTrue) { diff --git a/lib/Core/TargetCalculator.cpp b/lib/Core/TargetCalculator.cpp index 6b6a2f7d3e..0c5d72fedb 100644 --- a/lib/Core/TargetCalculator.cpp +++ b/lib/Core/TargetCalculator.cpp @@ -49,11 +49,22 @@ void TargetCalculator::update(const ExecutionState &state) { if (!coveredFunctionsInBranches.count(state.prevPC->parent->parent)) { unsigned index = 0; - coveredBranches[state.prevPC->parent->parent][state.prevPC->parent]; + if (!coveredBranches[state.prevPC->parent->parent].count( + state.prevPC->parent)) { + state.coveredNew->value = false; + state.coveredNew = new box(true); + coveredBranches[state.prevPC->parent->parent][state.prevPC->parent]; + } for (auto succ : successors(state.getPrevPCBlock())) { if (succ == state.getPCBlock()) { - coveredBranches[state.prevPC->parent->parent][state.prevPC->parent] - .insert(index); + if (!coveredBranches[state.prevPC->parent->parent] + [state.prevPC->parent] + .count(index)) { + state.coveredNew->value = false; + state.coveredNew = new box(true); + coveredBranches[state.prevPC->parent->parent][state.prevPC->parent] + .insert(index); + } break; } ++index; From c1b7529b599702286728c6ca3d809248218de542 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Sat, 16 Sep 2023 13:24:32 +0400 Subject: [PATCH 4/4] [feat] Add `cover-on-the-fly` option --- include/klee/Core/TerminationTypes.h | 3 +- lib/Core/Executor.cpp | 59 +++++++++++++++++++--------- test/Feature/CoverOnTheFly.c | 35 +++++++++++++++++ test/Feature/CoverageCheck.c | 2 +- 4 files changed, 78 insertions(+), 21 deletions(-) create mode 100644 test/Feature/CoverOnTheFly.c diff --git a/include/klee/Core/TerminationTypes.h b/include/klee/Core/TerminationTypes.h index 06d1522923..786cf610d6 100644 --- a/include/klee/Core/TerminationTypes.h +++ b/include/klee/Core/TerminationTypes.h @@ -40,7 +40,8 @@ enum class StateTerminationClass : std::uint8_t { TTYPE(OutOfMemory, 12U, "early") \ TTYPE(OutOfStackMemory, 13U, "early") \ TTYPE(MaxCycles, 14U, "early") \ - TTMARK(EARLY, 14U) \ + TTYPE(CoverOnTheFly, 15U, "early") \ + TTMARK(EARLY, 15U) \ TTYPE(Solver, 20U, "solver.err") \ TTMARK(SOLVERERR, 20U) \ TTYPE(Abort, 30U, "abort.err") \ diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 761f832c47..65a6dcdedb 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -239,6 +239,18 @@ cl::opt EmitAllErrors( "(default=false, i.e. one per (error,instruction) pair)"), cl::cat(TestGenCat)); +cl::opt CoverOnTheFly( + "cover-on-the-fly", cl::init(false), + cl::desc("Generate tests cases for each new covered block or branch " + "(default=false, i.e. one per (error,instruction) pair)"), + cl::cat(TestGenCat)); + +cl::opt DelayCoverOnTheFly( + "delay-cover-on-the-fly", cl::init(10000), + cl::desc("Start on the fly tests generation after this many instructions " + "(default=10000)"), + cl::cat(TestGenCat)); + /* Constraint solving options */ cl::opt MaxSymArraySize( @@ -4325,8 +4337,36 @@ void Executor::initializeTypeManager() { typeSystemManager->initModule(); } +static bool shouldWriteTest(const ExecutionState &state) { + bool coveredNew = state.coveredNew->value; + state.coveredNew->value = false; + return !OnlyOutputStatesCoveringNew || coveredNew; +} + +static std::string terminationTypeFileExtension(StateTerminationType type) { + std::string ret; +#undef TTYPE +#undef TTMARK +#define TTYPE(N, I, S) \ + case StateTerminationType::N: \ + ret = (S); \ + break; +#define TTMARK(N, I) + switch (type) { TERMINATION_TYPES } + return ret; +}; + void Executor::executeStep(ExecutionState &state) { KInstruction *prevKI = state.prevPC; + + if (CoverOnTheFly && guidanceKind != GuidanceKind::ErrorGuidance && + stats::instructions > DelayCoverOnTheFly && shouldWriteTest(state)) { + interpreterHandler->processTestCase( + state, nullptr, + terminationTypeFileExtension(StateTerminationType::CoverOnTheFly) + .c_str()); + } + if (targetManager->isTargeted(state) && state.targets().empty()) { terminateStateEarlyAlgorithm(state, "State missed all it's targets.", StateTerminationType::MissedAllTargets); @@ -4511,25 +4551,6 @@ void Executor::terminateState(ExecutionState &state, removedStates.push_back(&state); } -static bool shouldWriteTest(const ExecutionState &state) { - bool coveredNew = state.coveredNew->value; - state.coveredNew->value = false; - return !OnlyOutputStatesCoveringNew || coveredNew; -} - -static std::string terminationTypeFileExtension(StateTerminationType type) { - std::string ret; -#undef TTYPE -#undef TTMARK -#define TTYPE(N, I, S) \ - case StateTerminationType::N: \ - ret = (S); \ - break; -#define TTMARK(N, I) - switch (type) { TERMINATION_TYPES } - return ret; -}; - void Executor::terminateStateOnExit(ExecutionState &state) { auto terminationType = StateTerminationType::Exit; ++stats::terminationExit; diff --git a/test/Feature/CoverOnTheFly.c b/test/Feature/CoverOnTheFly.c new file mode 100644 index 0000000000..85c0ead78c --- /dev/null +++ b/test/Feature/CoverOnTheFly.c @@ -0,0 +1,35 @@ +// ASAN fails because KLEE does not cleanup states with -dump-states-on-halt=false +// REQUIRES: not-asan +// RUN: %clang %s -emit-llvm %O0opt -c -o %t.bc +// RUN: rm -rf %t.klee-out +// RUN: %klee --only-output-states-covering-new --max-instructions=2000 --delay-cover-on-the-fly=500 --dump-states-on-halt=false --cover-on-the-fly --search=bfs --use-guided-search=none --output-dir=%t.klee-out %t.bc 2>&1 | FileCheck %s + +#include "klee/klee.h" + +#define a (2) +int main() { + int res = 0; + for (;;) { + int n = klee_int("n"); + switch (n) { + case 1: + res += 1; + break; + case 2: + res += 2; + break; + case 3: + res += 3; + break; + case 4: + res += 4; + break; + + default: + break; + } + } +} + +// CHECK: KLEE: done: completed paths = 0 +// CHECK: KLEE: done: generated tests = 5 diff --git a/test/Feature/CoverageCheck.c b/test/Feature/CoverageCheck.c index 4800010350..a9c9eb2b63 100644 --- a/test/Feature/CoverageCheck.c +++ b/test/Feature/CoverageCheck.c @@ -1,6 +1,6 @@ // RUN: %clang %s -emit-llvm %O0opt -c -o %t.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --use-cov-check=instruction-based --output-dir=%t.klee-out %t.bc > %t.log +// RUN: %klee --use-guided-search=none --use-cov-check=instruction-based --output-dir=%t.klee-out %t.bc > %t.log #include "klee/klee.h" #define a (2)