[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:
Devin Coughlin 2016-02-19 01:35:10 +00:00
parent bde5ede526
commit 8d922aa746
14 changed files with 170 additions and 21 deletions

View File

@ -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() {}

View File

@ -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,

View File

@ -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,
@ -426,6 +432,9 @@ 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;

View File

@ -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,

View File

@ -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.

View File

@ -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;

View File

@ -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 {}

View File

@ -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";
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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);
}

View File

@ -11,6 +11,7 @@ int c();
#define CHECK(x) (x)
#endif
// CHECK: --BEGIN FUNCTION--
void testRemoveDeadBindings() {
int i = a();
if (CHECK(i))