forked from OSchip/llvm-project
[analyzer] Add checker callback for beginning of function.
Add a checker callback that is called when the analyzer starts analyzing a function either at the top level or when inlined. This will be used by a follow-on patch making the DeallocChecker path sensitive. Differential Revision: http://reviews.llvm.org/D17418 llvm-svn: 261293
This commit is contained in:
parent
bde5ede526
commit
8d922aa746
|
@ -595,6 +595,13 @@ public:
|
|||
return static_cast<const StackFrameContext *>(getData2());
|
||||
}
|
||||
|
||||
/// Returns the entry block in the CFG for the entered function.
|
||||
const CFGBlock *getEntry() const {
|
||||
const StackFrameContext *CalleeCtx = getCalleeContext();
|
||||
const CFG *CalleeCFG = CalleeCtx->getCFG();
|
||||
return &(CalleeCFG->getEntry());
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ProgramPoint;
|
||||
CallEnter() {}
|
||||
|
|
|
@ -238,6 +238,20 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class BeginFunction {
|
||||
template <typename CHECKER>
|
||||
static void _checkBeginFunction(void *checker, CheckerContext &C) {
|
||||
((const CHECKER *)checker)->checkBeginFunction(C);
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename CHECKER>
|
||||
static void _register(CHECKER *checker, CheckerManager &mgr) {
|
||||
mgr._registerForBeginFunction(CheckerManager::CheckBeginFunctionFunc(
|
||||
checker, _checkBeginFunction<CHECKER>));
|
||||
}
|
||||
};
|
||||
|
||||
class EndFunction {
|
||||
template <typename CHECKER>
|
||||
static void _checkEndFunction(void *checker,
|
||||
|
|
|
@ -287,6 +287,12 @@ public:
|
|||
void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR,
|
||||
ExprEngine &Eng);
|
||||
|
||||
/// \brief Run checkers on begining of function.
|
||||
void runCheckersForBeginFunction(ExplodedNodeSet &Dst,
|
||||
const BlockEdge &L,
|
||||
ExplodedNode *Pred,
|
||||
ExprEngine &Eng);
|
||||
|
||||
/// \brief Run checkers on end of function.
|
||||
void runCheckersForEndFunction(NodeBuilderContext &BC,
|
||||
ExplodedNodeSet &Dst,
|
||||
|
@ -425,7 +431,10 @@ public:
|
|||
|
||||
typedef CheckerFn<void (ExplodedGraph &, BugReporter &, ExprEngine &)>
|
||||
CheckEndAnalysisFunc;
|
||||
|
||||
|
||||
typedef CheckerFn<void (CheckerContext &)>
|
||||
CheckBeginFunctionFunc;
|
||||
|
||||
typedef CheckerFn<void (CheckerContext &)>
|
||||
CheckEndFunctionFunc;
|
||||
|
||||
|
@ -484,6 +493,7 @@ public:
|
|||
|
||||
void _registerForEndAnalysis(CheckEndAnalysisFunc checkfn);
|
||||
|
||||
void _registerForBeginFunction(CheckEndFunctionFunc checkfn);
|
||||
void _registerForEndFunction(CheckEndFunctionFunc checkfn);
|
||||
|
||||
void _registerForBranchCondition(CheckBranchConditionFunc checkfn);
|
||||
|
@ -593,6 +603,7 @@ private:
|
|||
|
||||
std::vector<CheckEndAnalysisFunc> EndAnalysisCheckers;
|
||||
|
||||
std::vector<CheckBeginFunctionFunc> BeginFunctionCheckers;
|
||||
std::vector<CheckEndFunctionFunc> EndFunctionCheckers;
|
||||
|
||||
std::vector<CheckBranchConditionFunc> BranchConditionCheckers;
|
||||
|
|
|
@ -91,6 +91,9 @@ private:
|
|||
void HandleBlockEdge(const BlockEdge &E, ExplodedNode *Pred);
|
||||
void HandleBlockEntrance(const BlockEntrance &E, ExplodedNode *Pred);
|
||||
void HandleBlockExit(const CFGBlock *B, ExplodedNode *Pred);
|
||||
|
||||
void HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred);
|
||||
|
||||
void HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred);
|
||||
|
||||
void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B,
|
||||
|
|
|
@ -253,8 +253,14 @@ public:
|
|||
/// nodes by processing the 'effects' of a switch statement.
|
||||
void processSwitch(SwitchNodeBuilder& builder) override;
|
||||
|
||||
/// Called by CoreEngine. Used to generate end-of-path
|
||||
/// nodes when the control reaches the end of a function.
|
||||
/// Called by CoreEngine. Used to notify checkers that processing a
|
||||
/// function has begun. Called for both inlined and and top-level functions.
|
||||
void processBeginOfFunction(NodeBuilderContext &BC,
|
||||
ExplodedNode *Pred, ExplodedNodeSet &Dst,
|
||||
const BlockEdge &L) override;
|
||||
|
||||
/// Called by CoreEngine. Used to notify checkers that processing a
|
||||
/// function has ended. Called for both inlined and and top-level functions.
|
||||
void processEndOfFunction(NodeBuilderContext& BC,
|
||||
ExplodedNode *Pred) override;
|
||||
|
||||
|
@ -264,7 +270,8 @@ public:
|
|||
ExplodedNodeSet &Dst);
|
||||
|
||||
/// Generate the entry node of the callee.
|
||||
void processCallEnter(CallEnter CE, ExplodedNode *Pred) override;
|
||||
void processCallEnter(NodeBuilderContext& BC, CallEnter CE,
|
||||
ExplodedNode *Pred) override;
|
||||
|
||||
/// Generate the sequence of nodes that simulate the call exit and the post
|
||||
/// visit for CallExpr.
|
||||
|
|
|
@ -99,13 +99,21 @@ public:
|
|||
/// nodes by processing the 'effects' of a switch statement.
|
||||
virtual void processSwitch(SwitchNodeBuilder& builder) = 0;
|
||||
|
||||
/// Called by CoreEngine. Used to generate end-of-path
|
||||
/// nodes when the control reaches the end of a function.
|
||||
/// Called by CoreEngine. Used to notify checkers that processing a
|
||||
/// function has begun. Called for both inlined and and top-level functions.
|
||||
virtual void processBeginOfFunction(NodeBuilderContext &BC,
|
||||
ExplodedNode *Pred,
|
||||
ExplodedNodeSet &Dst,
|
||||
const BlockEdge &L) = 0;
|
||||
|
||||
/// Called by CoreEngine. Used to notify checkers that processing a
|
||||
/// function has ended. Called for both inlined and and top-level functions.
|
||||
virtual void processEndOfFunction(NodeBuilderContext& BC,
|
||||
ExplodedNode *Pred) = 0;
|
||||
|
||||
// Generate the entry node of the callee.
|
||||
virtual void processCallEnter(CallEnter CE, ExplodedNode *Pred) = 0;
|
||||
virtual void processCallEnter(NodeBuilderContext& BC, CallEnter CE,
|
||||
ExplodedNode *Pred) = 0;
|
||||
|
||||
// Generate the first post callsite node.
|
||||
virtual void processCallExit(ExplodedNode *Pred) = 0;
|
||||
|
|
|
@ -162,8 +162,16 @@ public:
|
|||
/// check::DeadSymbols
|
||||
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const {}
|
||||
|
||||
|
||||
/// \brief Called when the analyzer core starts analyzing a function,
|
||||
/// regardless of whether it is analyzed at the top level or is inlined.
|
||||
///
|
||||
/// check::BeginFunction
|
||||
void checkBeginFunction(CheckerContext &Ctx) const {}
|
||||
|
||||
/// \brief Called when the analyzer core reaches the end of a
|
||||
/// function being analyzed.
|
||||
/// function being analyzed regardless of whether it is analyzed at the top
|
||||
/// level or is inlined.
|
||||
///
|
||||
/// check::EndFunction
|
||||
void checkEndFunction(CheckerContext &Ctx) const {}
|
||||
|
|
|
@ -25,9 +25,11 @@ using namespace ento;
|
|||
|
||||
namespace {
|
||||
class TraversalDumper : public Checker< check::BranchCondition,
|
||||
check::BeginFunction,
|
||||
check::EndFunction > {
|
||||
public:
|
||||
void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const;
|
||||
void checkBeginFunction(CheckerContext &C) const;
|
||||
void checkEndFunction(CheckerContext &C) const;
|
||||
};
|
||||
}
|
||||
|
@ -50,6 +52,10 @@ void TraversalDumper::checkBranchCondition(const Stmt *Condition,
|
|||
<< Parent->getStmtClassName() << "\n";
|
||||
}
|
||||
|
||||
void TraversalDumper::checkBeginFunction(CheckerContext &C) const {
|
||||
llvm::outs() << "--BEGIN FUNCTION--\n";
|
||||
}
|
||||
|
||||
void TraversalDumper::checkEndFunction(CheckerContext &C) const {
|
||||
llvm::outs() << "--END FUNCTION--\n";
|
||||
}
|
||||
|
|
|
@ -377,6 +377,40 @@ void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G,
|
|||
EndAnalysisCheckers[i](G, BR, Eng);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct CheckBeginFunctionContext {
|
||||
typedef std::vector<CheckerManager::CheckBeginFunctionFunc> CheckersTy;
|
||||
const CheckersTy &Checkers;
|
||||
ExprEngine &Eng;
|
||||
const ProgramPoint &PP;
|
||||
|
||||
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
|
||||
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
|
||||
|
||||
CheckBeginFunctionContext(const CheckersTy &Checkers, ExprEngine &Eng,
|
||||
const ProgramPoint &PP)
|
||||
: Checkers(Checkers), Eng(Eng), PP(PP) {}
|
||||
|
||||
void runChecker(CheckerManager::CheckBeginFunctionFunc checkFn,
|
||||
NodeBuilder &Bldr, ExplodedNode *Pred) {
|
||||
const ProgramPoint &L = PP.withTag(checkFn.Checker);
|
||||
CheckerContext C(Bldr, Eng, Pred, L);
|
||||
|
||||
checkFn(C);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst,
|
||||
const BlockEdge &L,
|
||||
ExplodedNode *Pred,
|
||||
ExprEngine &Eng) {
|
||||
ExplodedNodeSet Src;
|
||||
Src.insert(Pred);
|
||||
CheckBeginFunctionContext C(BeginFunctionCheckers, Eng, L);
|
||||
expandGraphWithCheckers(C, Dst, Src);
|
||||
}
|
||||
|
||||
/// \brief Run checkers for end of path.
|
||||
// Note, We do not chain the checker output (like in expandGraphWithCheckers)
|
||||
// for this callback since end of path nodes are expected to be final.
|
||||
|
@ -671,6 +705,10 @@ void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) {
|
|||
EndAnalysisCheckers.push_back(checkfn);
|
||||
}
|
||||
|
||||
void CheckerManager::_registerForBeginFunction(CheckBeginFunctionFunc checkfn) {
|
||||
BeginFunctionCheckers.push_back(checkfn);
|
||||
}
|
||||
|
||||
void CheckerManager::_registerForEndFunction(CheckEndFunctionFunc checkfn) {
|
||||
EndFunctionCheckers.push_back(checkfn);
|
||||
}
|
||||
|
|
|
@ -192,10 +192,18 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
|
|||
WList->setBlockCounter(BCounterFactory.GetEmptyCounter());
|
||||
|
||||
if (!InitState)
|
||||
// Generate the root.
|
||||
generateNode(StartLoc, SubEng.getInitialState(L), nullptr);
|
||||
else
|
||||
generateNode(StartLoc, InitState, nullptr);
|
||||
InitState = SubEng.getInitialState(L);
|
||||
|
||||
bool IsNew;
|
||||
ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew);
|
||||
assert (IsNew);
|
||||
G.addRoot(Node);
|
||||
|
||||
NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node);
|
||||
ExplodedNodeSet DstBegin;
|
||||
SubEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc);
|
||||
|
||||
enqueue(DstBegin);
|
||||
}
|
||||
|
||||
// Check if we have a steps limit
|
||||
|
@ -243,8 +251,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
|
|||
break;
|
||||
|
||||
case ProgramPoint::CallEnterKind: {
|
||||
CallEnter CEnter = Loc.castAs<CallEnter>();
|
||||
SubEng.processCallEnter(CEnter, Pred);
|
||||
HandleCallEnter(Loc.castAs<CallEnter>(), Pred);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -456,6 +463,11 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
|
|||
Pred->State, Pred);
|
||||
}
|
||||
|
||||
void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) {
|
||||
NodeBuilderContext BuilderCtx(*this, CE.getEntry(), Pred);
|
||||
SubEng.processCallEnter(BuilderCtx, CE, Pred);
|
||||
}
|
||||
|
||||
void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
|
||||
const CFGBlock * B, ExplodedNode *Pred) {
|
||||
assert(B->succ_size() == 2);
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "llvm/ADT/ImmutableList.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/SaveAndRestore.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include "llvm/Support/GraphWriter.h"
|
||||
|
@ -1749,6 +1750,14 @@ static bool stackFrameDoesNotContainInitializedTemporaries(ExplodedNode &Pred) {
|
|||
}
|
||||
#endif
|
||||
|
||||
void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC,
|
||||
ExplodedNode *Pred,
|
||||
ExplodedNodeSet &Dst,
|
||||
const BlockEdge &L) {
|
||||
SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC);
|
||||
getCheckerManager().runCheckersForBeginFunction(Dst, L, Pred, *this);
|
||||
}
|
||||
|
||||
/// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path
|
||||
/// nodes when the control reaches the end of a function.
|
||||
void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
|
||||
|
|
|
@ -37,13 +37,12 @@ STATISTIC(NumInlinedCalls,
|
|||
STATISTIC(NumReachedInlineCountMax,
|
||||
"The # of times we reached inline count maximum");
|
||||
|
||||
void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
|
||||
void ExprEngine::processCallEnter(NodeBuilderContext& BC, CallEnter CE,
|
||||
ExplodedNode *Pred) {
|
||||
// Get the entry block in the CFG of the callee.
|
||||
const StackFrameContext *calleeCtx = CE.getCalleeContext();
|
||||
PrettyStackTraceLocationContext CrashInfo(calleeCtx);
|
||||
|
||||
const CFG *CalleeCFG = calleeCtx->getCFG();
|
||||
const CFGBlock *Entry = &(CalleeCFG->getEntry());
|
||||
const CFGBlock *Entry = CE.getEntry();
|
||||
|
||||
// Validate the CFG.
|
||||
assert(Entry->empty());
|
||||
|
@ -57,12 +56,16 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
|
|||
|
||||
ProgramStateRef state = Pred->getState();
|
||||
|
||||
// Construct a new node and add it to the worklist.
|
||||
// Construct a new node, notify checkers that analysis of the function has
|
||||
// begun, and add the resultant nodes to the worklist.
|
||||
bool isNew;
|
||||
ExplodedNode *Node = G.getNode(Loc, state, false, &isNew);
|
||||
Node->addPredecessor(Pred, G);
|
||||
if (isNew)
|
||||
Engine.getWorkList()->enqueue(Node);
|
||||
if (isNew) {
|
||||
ExplodedNodeSet DstBegin;
|
||||
processBeginOfFunction(BC, Node, DstBegin, Loc);
|
||||
Engine.enqueue(DstBegin);
|
||||
}
|
||||
}
|
||||
|
||||
// Find the last statement on the path to the exploded node and the
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.DumpTraversal %s | FileCheck %s
|
||||
|
||||
void inline_callee(int i);
|
||||
|
||||
// CHECK: --BEGIN FUNCTION--
|
||||
void inline_caller() {
|
||||
// CHECK: --BEGIN FUNCTION--
|
||||
// CHECK: --BEGIN FUNCTION--
|
||||
// CHECK: --BEGIN FUNCTION--
|
||||
inline_callee(3);
|
||||
// CHECK: --END FUNCTION--
|
||||
// CHECK: --END FUNCTION--
|
||||
// CHECK: --END FUNCTION--
|
||||
}
|
||||
// CHECK: --END FUNCTION--
|
||||
|
||||
void inline_callee(int i) {
|
||||
if (i <= 1)
|
||||
return;
|
||||
|
||||
inline_callee(i - 1);
|
||||
}
|
|
@ -11,6 +11,7 @@ int c();
|
|||
#define CHECK(x) (x)
|
||||
#endif
|
||||
|
||||
// CHECK: --BEGIN FUNCTION--
|
||||
void testRemoveDeadBindings() {
|
||||
int i = a();
|
||||
if (CHECK(i))
|
||||
|
|
Loading…
Reference in New Issue