[analyze] Convert EndOfPath callback to use CheckerContext

Get rid of the EndOfPathBuilder completely.
Use the generic NodeBuilder to generate nodes.
Enqueue the end of path frontier explicitly.

llvm-svn: 142943
This commit is contained in:
Anna Zaks 2011-10-25 19:56:48 +00:00
parent 52cd8acafc
commit 3eae33412d
13 changed files with 106 additions and 175 deletions

View File

@ -199,9 +199,9 @@ public:
class EndPath {
template <typename CHECKER>
static void _checkEndPath(void *checker, EndOfFunctionNodeBuilder &B,
ExprEngine &Eng) {
((const CHECKER *)checker)->checkEndPath(B, Eng);
static void _checkEndPath(void *checker,
CheckerContext &C) {
((const CHECKER *)checker)->checkEndPath(C);
}
public:

View File

@ -39,8 +39,8 @@ namespace ento {
class ExplodedNodeSet;
class ExplodedGraph;
class ProgramState;
class EndOfFunctionNodeBuilder;
class NodeBuilder;
struct NodeBuilderContext;
class MemRegion;
class SymbolReaper;
@ -230,7 +230,9 @@ public:
ExprEngine &Eng);
/// \brief Run checkers for end of path.
void runCheckersForEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng);
void runCheckersForEndPath(NodeBuilderContext &BC,
ExplodedNodeSet &Dst,
ExprEngine &Eng);
/// \brief Run checkers for branch condition.
void runCheckersForBranchCondition(const Stmt *condition,
@ -334,7 +336,7 @@ public:
typedef CheckerFn<void (ExplodedGraph &, BugReporter &, ExprEngine &)>
CheckEndAnalysisFunc;
typedef CheckerFn<void (EndOfFunctionNodeBuilder &, ExprEngine &)>
typedef CheckerFn<void (CheckerContext &)>
CheckEndPathFunc;
typedef CheckerFn<void (const Stmt *, NodeBuilder &, ExplodedNode *Pred,

View File

@ -171,9 +171,9 @@ public:
struct NodeBuilderContext {
CoreEngine &Eng;
const CFGBlock *Block;
ExplodedNode *ContextPred;
ExplodedNode *Pred;
NodeBuilderContext(CoreEngine &E, const CFGBlock *B, ExplodedNode *N)
: Eng(E), Block(B), ContextPred(N) { assert(B); assert(!N->isSink()); }
: Eng(E), Block(B), Pred(N) { assert(B); assert(!N->isSink()); }
/// \brief Return the CFGBlock associated with this builder.
const CFGBlock *getBlock() const { return Block; }
@ -182,10 +182,9 @@ struct NodeBuilderContext {
/// visited on the exploded graph path.
unsigned getCurrentBlockCount() const {
return Eng.WList->getBlockCounter().getNumVisited(
ContextPred->getLocationContext()->getCurrentStackFrame(),
Pred->getLocationContext()->getCurrentStackFrame(),
Block->getBlockID());
}
};
/// \class NodeBuilder
@ -289,15 +288,6 @@ public:
void addNodes(ExplodedNode *N) { Frontier.Add(N); }
};
class CommonNodeBuilder {
protected:
ExplodedNode *Pred;
CoreEngine &Eng;
CommonNodeBuilder(CoreEngine* E, ExplodedNode *P) : Pred(P), Eng(*E) {}
BlockCounter getBlockCounter() const { return Eng.WList->getBlockCounter(); }
};
/// \class StmtNodeBuilder
/// \brief This builder class is useful for generating nodes that resulted from
/// visiting a statement. The main difference from it's parent NodeBuilder is
@ -523,47 +513,6 @@ public:
const PP_T &getProgramPoint() const { return cast<PP_T>(pp); }
};
class EndOfFunctionNodeBuilder : public CommonNodeBuilder {
const CFGBlock &B;
const ProgramPointTag *Tag;
public:
bool hasGeneratedNode;
public:
EndOfFunctionNodeBuilder(const CFGBlock *b, ExplodedNode *N, CoreEngine* e,
const ProgramPointTag *tag = 0)
: CommonNodeBuilder(e, N), B(*b), Tag(tag), hasGeneratedNode(false) {}
~EndOfFunctionNodeBuilder();
EndOfFunctionNodeBuilder withCheckerTag(const ProgramPointTag *tag) {
return EndOfFunctionNodeBuilder(&B, Pred, &Eng, tag);
}
WorkList &getWorkList() { return *Eng.WList; }
ExplodedNode *getPredecessor() const { return Pred; }
unsigned getCurrentBlockCount() const {
return getBlockCounter().getNumVisited(
Pred->getLocationContext()->getCurrentStackFrame(),
B.getBlockID());
}
ExplodedNode *generateNode(const ProgramState *State,
ExplodedNode *P = 0,
const ProgramPointTag *tag = 0);
void GenerateCallExitNode(const ProgramState *state);
const CFGBlock *getBlock() const { return &B; }
const ProgramState *getState() const {
return getPredecessor()->getState();
}
};
class CallEnterNodeBuilder {
CoreEngine &Eng;

View File

@ -124,6 +124,8 @@ public:
const Stmt *getStmt() const;
void GenerateAutoTransition(ExplodedNode *N);
void enqueueEndOfPath(ExplodedNodeSet &S);
void GenerateCallExitNode(ExplodedNode *N);
/// ViewGraph - Visualize the ExplodedGraph created by executing the
/// simulation.
@ -181,7 +183,7 @@ public:
/// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path
/// nodes when the control reaches the end of a function.
void processEndOfFunction(EndOfFunctionNodeBuilder& builder);
void processEndOfFunction(NodeBuilderContext& BC);
/// Generate the entry node of the callee.
void processCallEnter(CallEnterNodeBuilder &builder);

View File

@ -82,7 +82,7 @@ public:
/// Called by CoreEngine. Used to generate end-of-path
/// nodes when the control reaches the end of a function.
virtual void processEndOfFunction(EndOfFunctionNodeBuilder& builder) = 0;
virtual void processEndOfFunction(NodeBuilderContext& BC) = 0;
// Generate the entry node of the callee.
virtual void processCallEnter(CallEnterNodeBuilder &builder) = 0;

View File

@ -58,7 +58,7 @@ public:
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
void checkEndPath(CheckerContext &Ctx) const;
private:
typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
@ -557,9 +557,8 @@ void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
}
// TODO: Remove this after we ensure that checkDeadSymbols are always called.
void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
ExprEngine &Eng) const {
const ProgramState *state = B.getState();
void MacOSKeychainAPIChecker::checkEndPath(CheckerContext &Ctx) const {
const ProgramState *state = Ctx.getState();
AllocatedSetTy AS = state->get<AllocatedData>();
if (AS.isEmpty())
return;
@ -575,7 +574,7 @@ void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
// allocation, do not report.
if (state->getSymVal(I.getKey()) ||
definitelyReturnedError(I->second.Region, state,
Eng.getSValBuilder())) {
Ctx.getSValBuilder())) {
continue;
}
Errors.push_back(std::make_pair(I->first, &I->second));
@ -585,15 +584,14 @@ void MacOSKeychainAPIChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
if (!Changed)
return;
ExplodedNode *N = B.generateNode(state);
ExplodedNode *N = Ctx.generateNode(state);
if (!N)
return;
// Generate the error reports.
for (AllocationPairVec::iterator I = Errors.begin(), E = Errors.end();
I != E; ++I) {
Eng.getBugReporter().EmitReport(
generateAllocatedDataNotReleasedReport(*I, N));
Ctx.EmitReport(generateAllocatedDataNotReleasedReport(*I, N));
}
}

View File

@ -78,7 +78,7 @@ public:
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
void checkEndPath(CheckerContext &C) const;
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
const ProgramState *evalAssume(const ProgramState *state, SVal Cond,
bool Assumption) const;
@ -604,21 +604,20 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
}
}
void MallocChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
ExprEngine &Eng) const {
const ProgramState *state = B.getState();
void MallocChecker::checkEndPath(CheckerContext &Ctx) const {
const ProgramState *state = Ctx.getState();
RegionStateTy M = state->get<RegionState>();
for (RegionStateTy::iterator I = M.begin(), E = M.end(); I != E; ++I) {
RefState RS = I->second;
if (RS.isAllocated()) {
ExplodedNode *N = B.generateNode(state);
ExplodedNode *N = Ctx.generateNode(state);
if (N) {
if (!BT_Leak)
BT_Leak.reset(new BuiltinBug("Memory leak",
"Allocated memory never released. Potential memory leak."));
BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N);
Eng.getBugReporter().EmitReport(R);
Ctx.EmitReport(R);
}
}
}

View File

@ -47,24 +47,13 @@ class GenericNodeBuilderRefCount {
EndOfFunctionNodeBuilder *ENB;
public:
GenericNodeBuilderRefCount(CheckerContext &c,
const ProgramPointTag *t)
const ProgramPointTag *t = 0)
: C(&c), tag(t), ENB(0) {}
GenericNodeBuilderRefCount(EndOfFunctionNodeBuilder &enb)
: C(0), tag(0), ENB(&enb) {}
ExplodedNode *MakeNode(const ProgramState *state, ExplodedNode *Pred,
bool MarkAsSink = false) {
if (C) {
return C->generateNode(state, Pred, tag, false, MarkAsSink);
}
assert(ENB);
ExplodedNode *N = ENB->generateNode(state, Pred);
if (MarkAsSink)
N->markAsSink();
return N;
assert(C);
return C->generateNode(state, Pred, tag, false, MarkAsSink);
}
};
} // end anonymous namespace
@ -2445,7 +2434,7 @@ public:
SymbolRef Sym, const ProgramState *state) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
void checkEndPath(EndOfFunctionNodeBuilder &Builder, ExprEngine &Eng) const;
void checkEndPath(CheckerContext &C) const;
const ProgramState *updateSymbol(const ProgramState *state, SymbolRef sym,
RefVal V, ArgEffect E, RefVal::Kind &hasErr,
@ -3439,12 +3428,12 @@ RetainCountChecker::processLeaks(const ProgramState *state,
return N;
}
void RetainCountChecker::checkEndPath(EndOfFunctionNodeBuilder &Builder,
ExprEngine &Eng) const {
const ProgramState *state = Builder.getState();
GenericNodeBuilderRefCount Bd(Builder);
void RetainCountChecker::checkEndPath(CheckerContext &Ctx) const {
const ProgramState *state = Ctx.getState();
GenericNodeBuilderRefCount Bd(Ctx);
RefBindings B = state->get<RefBindings>();
ExplodedNode *Pred = Builder.getPredecessor();
ExplodedNode *Pred = Ctx.getPredecessor();
ExprEngine &Eng = Ctx.getEngine();
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, Eng,

View File

@ -31,7 +31,7 @@ class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>,
public:
void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
void checkEndPath(CheckerContext &Ctx) const;
private:
void EmitStackError(CheckerContext &C, const MemRegion *R,
const Expr *RetE) const;
@ -136,22 +136,22 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
}
}
void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
ExprEngine &Eng) const {
const ProgramState *state = B.getState();
void StackAddrEscapeChecker::checkEndPath(CheckerContext &Ctx) const {
const ProgramState *state = Ctx.getState();
// Iterate over all bindings to global variables and see if it contains
// a memory region in the stack space.
class CallBack : public StoreManager::BindingsHandler {
private:
ExprEngine &Eng;
CheckerContext &Ctx;
const StackFrameContext *CurSFC;
public:
SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V;
CallBack(ExprEngine &Eng, const LocationContext *LCtx)
: Eng(Eng), CurSFC(LCtx->getCurrentStackFrame()) {}
CallBack(CheckerContext &CC) :
Ctx(CC),
CurSFC(CC.getPredecessor()->getLocationContext()->getCurrentStackFrame())
{}
bool HandleBinding(StoreManager &SMgr, Store store,
const MemRegion *region, SVal val) {
@ -165,7 +165,7 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
// Under automated retain release, it is okay to assign a block
// directly to a global variable.
if (Eng.getContext().getLangOptions().ObjCAutoRefCount &&
if (Ctx.getASTContext().getLangOptions().ObjCAutoRefCount &&
isa<BlockDataRegion>(vR))
return true;
@ -181,14 +181,14 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
}
};
CallBack cb(Eng, B.getPredecessor()->getLocationContext());
CallBack cb(Ctx);
state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb);
if (cb.V.empty())
return;
// Generate an error node.
ExplodedNode *N = B.generateNode(state);
ExplodedNode *N = Ctx.generateNode(state);
if (!N)
return;
@ -204,7 +204,7 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
llvm::SmallString<512> buf;
llvm::raw_svector_ostream os(buf);
SourceRange range = GenName(os, cb.V[i].second,
Eng.getContext().getSourceManager());
Ctx.getSourceManager());
os << " is still referred to by the global variable '";
const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion());
os << *VR->getDecl()
@ -213,7 +213,7 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
if (range.isValid())
report->addRange(range);
Eng.getBugReporter().EmitReport(report);
Ctx.EmitReport(report);
}
}

View File

@ -75,7 +75,7 @@ public:
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
void checkEndPath(EndOfFunctionNodeBuilder &B, ExprEngine &Eng) const;
void checkEndPath(CheckerContext &Ctx) const;
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
private:
@ -418,23 +418,22 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
}
}
void StreamChecker::checkEndPath(EndOfFunctionNodeBuilder &B,
ExprEngine &Eng) const {
const ProgramState *state = B.getState();
void StreamChecker::checkEndPath(CheckerContext &Ctx) const {
const ProgramState *state = Ctx.getState();
typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap;
SymMap M = state->get<StreamState>();
for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) {
StreamState SS = I->second;
if (SS.isOpened()) {
ExplodedNode *N = B.generateNode(state);
ExplodedNode *N = Ctx.generateNode(state);
if (N) {
if (!BT_ResourceLeak)
BT_ResourceLeak.reset(new BuiltinBug("Resource Leak",
"Opened File never closed. Potential Resource leak."));
BugReport *R = new BugReport(*BT_ResourceLeak,
BT_ResourceLeak->getDescription(), N);
Eng.getBugReporter().EmitReport(R);
Ctx.EmitReport(R);
}
}
}

View File

@ -298,12 +298,25 @@ void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G,
}
/// \brief Run checkers for end of path.
void CheckerManager::runCheckersForEndPath(EndOfFunctionNodeBuilder &B,
// Note, We do not chain the checker output (like in expandGraphWithCheckers)
// for this callback since end of path nodes are expected to be final.
void CheckerManager::runCheckersForEndPath(NodeBuilderContext &BC,
ExplodedNodeSet &Dst,
ExprEngine &Eng) {
ExplodedNode *Pred = BC.Pred;
// We define the builder outside of the loop bacause if at least one checkers
// creates a sucsessor for Pred, we do not need to generate an
// autotransition for it.
NodeBuilder Bldr(Pred, Dst, BC);
for (unsigned i = 0, e = EndPathCheckers.size(); i != e; ++i) {
CheckEndPathFunc fn = EndPathCheckers[i];
EndOfFunctionNodeBuilder specialB = B.withCheckerTag(fn.Checker);
fn(specialB, Eng);
CheckEndPathFunc checkFn = EndPathCheckers[i];
const ProgramPoint &L = BlockEntrance(BC.Block,
Pred->getLocationContext(),
checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L, 0);
checkFn(C);
}
}

View File

@ -269,8 +269,8 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
&& "EXIT block cannot contain Stmts.");
// Process the final state transition.
EndOfFunctionNodeBuilder Builder(Blk, Pred, this);
SubEng.processEndOfFunction(Builder);
NodeBuilderContext BuilderCtx(*this, Blk, Pred);
SubEng.processEndOfFunction(BuilderCtx);
// This path is done. Don't enqueue any more nodes.
return;
@ -597,57 +597,6 @@ SwitchNodeBuilder::generateDefaultCaseNode(const ProgramState *St,
return NULL;
}
EndOfFunctionNodeBuilder::~EndOfFunctionNodeBuilder() {
// Auto-generate an EOP node if one has not been generated.
if (!hasGeneratedNode) {
// If we are in an inlined call, generate CallExit node.
if (Pred->getLocationContext()->getParent())
GenerateCallExitNode(Pred->State);
else
generateNode(Pred->State);
}
}
ExplodedNode*
EndOfFunctionNodeBuilder::generateNode(const ProgramState *State,
ExplodedNode *P,
const ProgramPointTag *tag) {
hasGeneratedNode = true;
bool IsNew;
ExplodedNode *Node = Eng.G->getNode(BlockEntrance(&B,
Pred->getLocationContext(), tag ? tag : Tag),
State, &IsNew);
Node->addPredecessor(P ? P : Pred, *Eng.G);
if (IsNew) {
Eng.G->addEndOfPath(Node);
return Node;
}
return NULL;
}
void EndOfFunctionNodeBuilder::GenerateCallExitNode(const ProgramState *state) {
hasGeneratedNode = true;
// Create a CallExit node and enqueue it.
const StackFrameContext *LocCtx
= cast<StackFrameContext>(Pred->getLocationContext());
const Stmt *CE = LocCtx->getCallSite();
// Use the the callee location context.
CallExit Loc(CE, LocCtx);
bool isNew;
ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew);
Node->addPredecessor(Pred, *Eng.G);
if (isNew)
Eng.WList->enqueue(Node);
}
void CallEnterNodeBuilder::generateNode(const ProgramState *state) {
// Check if the callee is in the same translation unit.
if (CalleeCtx->getTranslationUnit() !=

View File

@ -1153,11 +1153,42 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
builder.generateNode(I, state);
}
// TODO: The next two functions should be moved into CoreEngine.
void ExprEngine::GenerateCallExitNode(ExplodedNode *N) {
// Create a CallExit node and enqueue it.
const StackFrameContext *LocCtx
= cast<StackFrameContext>(N->getLocationContext());
const Stmt *CE = LocCtx->getCallSite();
// Use the the callee location context.
CallExit Loc(CE, LocCtx);
bool isNew;
ExplodedNode *Node = Engine.G->getNode(Loc, N->getState(), &isNew);
Node->addPredecessor(N, *Engine.G);
if (isNew)
Engine.WList->enqueue(Node);
}
void ExprEngine::enqueueEndOfPath(ExplodedNodeSet &S) {
for (ExplodedNodeSet::iterator I = S.begin(), E = S.end(); I != E; ++I) {
ExplodedNode *N = *I;
// If we are in an inlined call, generate CallExit node.
if (N->getLocationContext()->getParent())
GenerateCallExitNode(N);
else
Engine.G->addEndOfPath(N);
}
}
/// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path
/// nodes when the control reaches the end of a function.
void ExprEngine::processEndOfFunction(EndOfFunctionNodeBuilder& builder) {
StateMgr.EndPath(builder.getState());
getCheckerManager().runCheckersForEndPath(builder, *this);
void ExprEngine::processEndOfFunction(NodeBuilderContext& BC) {
StateMgr.EndPath(BC.Pred->getState());
ExplodedNodeSet Dst;
getCheckerManager().runCheckersForEndPath(BC, Dst, *this);
enqueueEndOfPath(Dst);
}
/// ProcessSwitch - Called by CoreEngine. Used to generate successor