[analyzer] Run remove dead bindings right before leaving a function.

This is needed to ensure that we always report issues in the correct
function. For example, leaks are identified when we call remove dead
bindings. In order to make sure we report a callee's leak in the callee,
we have to run the operation in the callee's context.

This change required quite a bit of infrastructure work since:
 - We used to only run remove dead bindings before a given statement;
here we need to run it after the last statement in the function. For
this, we added additional Program Point and special mode in the
SymbolReaper to remove all symbols in context lower than the current
one.
 - The call exit operation turned into a sequence of nodes, which are
now guarded by CallExitBegin and CallExitEnd nodes for clarity and
convenience.

(Sorry for the long diff.)

llvm-svn: 155244
This commit is contained in:
Anna Zaks 2012-04-20 21:59:08 +00:00
parent 20492bf0b6
commit 7e53bd6fb0
16 changed files with 4815 additions and 2846 deletions

View File

@ -45,14 +45,16 @@ public:
PostLoadKind,
PreStoreKind,
PostStoreKind,
PostPurgeDeadSymbolsKind,
PostStmtPurgeDeadSymbolsKind,
PreStmtPurgeDeadSymbolsKind,
PostConditionKind,
PostLValueKind,
PostInitializerKind,
CallEnterKind,
CallExitKind,
CallExitBeginKind,
CallExitEndKind,
MinPostStmtKind = PostStmtKind,
MaxPostStmtKind = CallExitKind,
MaxPostStmtKind = CallExitEndKind,
EpsilonKind};
private:
@ -113,6 +115,14 @@ public:
return (Kind) x;
}
/// \brief Is this a program point corresponding to purge/removal of dead
/// symbols and bindings.
bool isPurgeKind() {
Kind K = getKind();
return (K == PostStmtPurgeDeadSymbolsKind ||
K == PreStmtPurgeDeadSymbolsKind);
}
const ProgramPointTag *getTag() const { return Tag; }
const LocationContext *getLocationContext() const {
@ -340,14 +350,29 @@ public:
}
};
class PostPurgeDeadSymbols : public PostStmt {
/// \class Represents a point after we ran remove dead bindings BEFORE
/// processing the given statement.
class PreStmtPurgeDeadSymbols : public PostStmt {
public:
PostPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
PreStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
const ProgramPointTag *tag = 0)
: PostStmt(S, PostPurgeDeadSymbolsKind, L, tag) {}
: PostStmt(S, PreStmtPurgeDeadSymbolsKind, L, tag) { }
static bool classof(const ProgramPoint* Location) {
return Location->getKind() == PostPurgeDeadSymbolsKind;
return Location->getKind() == PreStmtPurgeDeadSymbolsKind;
}
};
/// \class Represents a point after we ran remove dead bindings AFTER
/// processing the given statement.
class PostStmtPurgeDeadSymbols : public PostStmt {
public:
PostStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
const ProgramPointTag *tag = 0)
: PostStmt(S, PostStmtPurgeDeadSymbolsKind, L, tag) { }
static bool classof(const ProgramPoint* Location) {
return Location->getKind() == PostStmtPurgeDeadSymbolsKind;
}
};
@ -383,6 +408,7 @@ public:
}
};
/// \class Represents a point when we begin processing an inlined call.
class CallEnter : public StmtPoint {
public:
CallEnter(const Stmt *stmt, const StackFrameContext *calleeCtx,
@ -402,14 +428,38 @@ public:
}
};
class CallExit : public StmtPoint {
/// \class Represents a point when we start the call exit sequence (for
/// inlined call).
///
/// The call exit is simulated with a sequence of nodes, which occur between
/// CallExitBegin and CallExitEnd. The following operations occur between the
/// two program points:
/// - CallExitBegin
/// - Bind the return value
/// - Run Remove dead bindings (to clean up the dead symbols from the callee).
/// - CallExitEnd
class CallExitBegin : public StmtPoint {
public:
// CallExit uses the callee's location context.
CallExit(const Stmt *S, const LocationContext *L)
: StmtPoint(S, 0, CallExitKind, L, 0) {}
// CallExitBegin uses the callee's location context.
CallExitBegin(const Stmt *S, const LocationContext *L)
: StmtPoint(S, 0, CallExitBeginKind, L, 0) {}
static bool classof(const ProgramPoint *Location) {
return Location->getKind() == CallExitKind;
return Location->getKind() == CallExitBeginKind;
}
};
/// \class Represents a point when we finish the call exit sequence (for
/// inlined call).
/// \sa CallExitBegin
class CallExitEnd : public StmtPoint {
public:
// CallExitEnd uses the caller's location context.
CallExitEnd(const Stmt *S, const LocationContext *L)
: StmtPoint(S, 0, CallExitEndKind, L, 0) {}
static bool classof(const ProgramPoint *Location) {
return Location->getKind() == CallExitEndKind;
}
};

View File

@ -362,7 +362,7 @@ public:
/// \brief Interface for classes constructing Stack hints.
///
/// If a PathDiagnosticEvent occurs in a different frame than the final
/// diagnostic the hints can be used to summarise the effect of the call.
/// diagnostic the hints can be used to summarize the effect of the call.
class StackHintGenerator {
public:
virtual ~StackHintGenerator() = 0;
@ -510,7 +510,7 @@ public:
}
static PathDiagnosticCallPiece *construct(const ExplodedNode *N,
const CallExit &CE,
const CallExitEnd &CE,
const SourceManager &SM);
static PathDiagnosticCallPiece *construct(PathPieces &pieces,

View File

@ -272,7 +272,8 @@ public:
void runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
SymbolReaper &SymReaper, const Stmt *S,
ExprEngine &Eng);
ExprEngine &Eng,
ProgramPoint::Kind K);
/// \brief True if at least one checker wants to check region changes.
bool wantsRegionChangeUpdate(ProgramStateRef state);

View File

@ -104,7 +104,7 @@ private:
CoreEngine(const CoreEngine&); // Do not implement.
CoreEngine& operator=(const CoreEngine&);
ExplodedNode *generateCallExitNode(ExplodedNode *N);
ExplodedNode *generateCallExitBeginNode(ExplodedNode *N);
public:
/// Construct a CoreEngine object to analyze the provided CFG using

View File

@ -151,6 +151,25 @@ public:
ExplodedGraph& getGraph() { return G; }
const ExplodedGraph& getGraph() const { return G; }
/// \brief Run the analyzer's garbage collection - remove dead symbols and
/// bindings.
///
/// \param Node - The predecessor node, from which the processing should
/// start.
/// \param Out - The returned set of output nodes.
/// \param ReferenceStmt - Run garbage collection using the symbols,
/// which are live before the given statement.
/// \param LC - The location context of the ReferenceStmt.
/// \param DiagnosticStmt - the statement used to associate the diagnostic
/// message, if any warnings should occur while removing the dead (leaks
/// are usually reported here).
/// \param K - In some cases it is possible to use PreStmt kind. (Do
/// not use it unless you know what you are doing.)
void removeDead(ExplodedNode *Node, ExplodedNodeSet &Out,
const Stmt *ReferenceStmt, const LocationContext *LC,
const Stmt *DiagnosticStmt,
ProgramPoint::Kind K = ProgramPoint::PreStmtPurgeDeadSymbolsKind);
/// processCFGElement - Called by CoreEngine. Used to generate new successor
/// nodes by processing the 'effects' of a CFG element.
void processCFGElement(const CFGElement E, ExplodedNode *Pred,
@ -199,7 +218,8 @@ public:
/// Generate the entry node of the callee.
void processCallEnter(CallEnter CE, ExplodedNode *Pred);
/// Generate the first post callsite node.
/// Generate the sequence of nodes that simulate the call exit and the post
/// visit for CallExpr.
void processCallExit(ExplodedNode *Pred);
/// Called by CoreEngine when the analysis worklist has terminated.

View File

@ -553,6 +553,7 @@ public:
BasicValueFactory &getBasicVals() { return BV; }
};
/// \class A class responsible for cleaning up unused symbols.
class SymbolReaper {
enum SymbolStatus {
NotProcessed,
@ -576,6 +577,11 @@ class SymbolReaper {
llvm::DenseMap<const MemRegion *, unsigned> includedRegionCache;
public:
/// \brief Construct a reaper object, which removes everything which is not
/// live before we execute statement s in the given location context.
///
/// If the statement is NULL, everything is this and parent contexts is
/// considered live.
SymbolReaper(const LocationContext *ctx, const Stmt *s, SymbolManager& symmgr,
StoreManager &storeMgr)
: LCtx(ctx), Loc(s), SymMgr(symmgr), reapedStore(0, storeMgr) {}

View File

@ -36,8 +36,10 @@ ProgramPoint ProgramPoint::getProgramPoint(const Stmt *S, ProgramPoint::Kind K,
return PreStore(S, LC, tag);
case ProgramPoint::PostLValueKind:
return PostLValue(S, LC, tag);
case ProgramPoint::PostPurgeDeadSymbolsKind:
return PostPurgeDeadSymbols(S, LC, tag);
case ProgramPoint::PostStmtPurgeDeadSymbolsKind:
return PostStmtPurgeDeadSymbols(S, LC, tag);
case ProgramPoint::PreStmtPurgeDeadSymbolsKind:
return PreStmtPurgeDeadSymbols(S, LC, tag);
}
}

View File

@ -427,7 +427,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
ProgramPoint P = N->getLocation();
if (const CallExit *CE = dyn_cast<CallExit>(&P)) {
if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) {
PathDiagnosticCallPiece *C =
PathDiagnosticCallPiece::construct(N, *CE, SMgr);
PD.getActivePath().push_front(C);
@ -440,7 +440,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
PD.popActivePath();
// The current active path should never be empty. Either we
// just added a bunch of stuff to the top-level path, or
// we have a previous CallExit. If the front of the active
// we have a previous CallExitEnd. If the front of the active
// path is not a PathDiagnosticCallPiece, it means that the
// path terminated within a function call. We must then take the
// current contents of the active path and place it within
@ -1066,10 +1066,10 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
ProgramPoint P = N->getLocation();
do {
if (const CallExit *CE = dyn_cast<CallExit>(&P)) {
if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) {
const StackFrameContext *LCtx =
CE->getLocationContext()->getCurrentStackFrame();
PathDiagnosticLocation Loc(LCtx->getCallSite(),
PathDiagnosticLocation Loc(CE->getStmt(),
PDB.getSourceManager(),
LCtx);
EB.addEdge(Loc, true);
@ -1099,7 +1099,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
// The current active path should never be empty. Either we
// just added a bunch of stuff to the top-level path, or
// we have a previous CallExit. If the front of the active
// we have a previous CallExitEnd. If the front of the active
// path is not a PathDiagnosticCallPiece, it means that the
// path terminated within a function call. We must then take the
// current contents of the active path and place it within

View File

@ -381,21 +381,25 @@ namespace {
SymbolReaper &SR;
const Stmt *S;
ExprEngine &Eng;
ProgramPoint::Kind ProgarmPointKind;
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr,
const Stmt *s, ExprEngine &eng)
: Checkers(checkers), SR(sr), S(s), Eng(eng) { }
const Stmt *s, ExprEngine &eng,
ProgramPoint::Kind K)
: Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) { }
void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
ProgramPoint::Kind K = ProgramPoint::PostPurgeDeadSymbolsKind;
const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
const ProgramPoint &L = ProgramPoint::getProgramPoint(S, ProgarmPointKind,
Pred->getLocationContext(), checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L);
// Note, do not pass the statement to the checkers without letting them
// differentiate if we ran remove dead bindings before or after the
// statement.
checkFn(SR, C);
}
};
@ -406,8 +410,9 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
SymbolReaper &SymReaper,
const Stmt *S,
ExprEngine &Eng) {
CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng);
ExprEngine &Eng,
ProgramPoint::Kind K) {
CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K);
expandGraphWithCheckers(C, Dst, Src);
}

View File

@ -248,7 +248,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
break;
}
case ProgramPoint::CallExitKind:
case ProgramPoint::CallExitBeginKind:
SubEng.processCallExit(Pred);
break;
@ -531,14 +531,14 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N,
WList->enqueue(Succ, Block, Idx+1);
}
ExplodedNode *CoreEngine::generateCallExitNode(ExplodedNode *N) {
// Create a CallExit node and enqueue it.
ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N) {
// Create a CallExitBegin 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);
CallExitBegin Loc(CE, LocCtx);
bool isNew;
ExplodedNode *Node = G->getNode(Loc, N->getState(), false, &isNew);
@ -565,12 +565,13 @@ void CoreEngine::enqueue(ExplodedNodeSet &Set,
void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set) {
for (ExplodedNodeSet::iterator I = Set.begin(), E = Set.end(); I != E; ++I) {
ExplodedNode *N = *I;
// If we are in an inlined call, generate CallExit node.
// If we are in an inlined call, generate CallExitBegin node.
if (N->getLocationContext()->getParent()) {
N = generateCallExitNode(N);
N = generateCallExitBeginNode(N);
if (N)
WList->enqueue(N);
} else {
// TODO: We should run remove dead bindings here.
G->addEndOfPath(N);
NumPathsExplored++;
}

View File

@ -57,7 +57,7 @@ ExplodedGraph::~ExplodedGraph() {}
//===----------------------------------------------------------------------===//
bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
// Reclaimn all nodes that match *all* the following criteria:
// Reclaim all nodes that match *all* the following criteria:
//
// (1) 1 predecessor (that has one successor)
// (2) 1 successor (that has one predecessor)
@ -83,7 +83,8 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
// Condition 3.
ProgramPoint progPoint = node->getLocation();
if (!isa<PostStmt>(progPoint) ||
(isa<CallEnter>(progPoint) || isa<CallExit>(progPoint)))
(isa<CallEnter>(progPoint) ||
isa<CallExitBegin>(progPoint) || isa<CallExitEnd>(progPoint)))
return false;
// Condition 4.

View File

@ -42,8 +42,6 @@ using llvm::APSInt;
STATISTIC(NumRemoveDeadBindings,
"The # of times RemoveDeadBindings is called");
STATISTIC(NumRemoveDeadBindingsSkipped,
"The # of times RemoveDeadBindings is skipped");
STATISTIC(NumMaxBlockCountReached,
"The # of aborted paths due to reaching the maximum block count in "
"a top level function");
@ -231,6 +229,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
ProcessImplicitDtor(*E.getAs<CFGImplicitDtor>(), Pred);
return;
}
currentBuilderContext = 0;
}
static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
@ -260,6 +259,72 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
return !PM.isConsumedExpr(cast<Expr>(S.getStmt()));
}
void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
const Stmt *ReferenceStmt,
const LocationContext *LC,
const Stmt *DiagnosticStmt,
ProgramPoint::Kind K) {
assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind ||
ReferenceStmt == 0) && "PreStmt is not generally supported by "
"the SymbolReaper yet");
NumRemoveDeadBindings++;
CleanedState = Pred->getState();
SymbolReaper SymReaper(LC, ReferenceStmt, SymMgr, getStoreManager());
getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
// Create a state in which dead bindings are removed from the environment
// and the store. TODO: The function should just return new env and store,
// not a new state.
const StackFrameContext *SFC = LC->getCurrentStackFrame();
CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper);
// Process any special transfer function for dead symbols.
// A tag to track convenience transitions, which can be removed at cleanup.
static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node");
if (!SymReaper.hasDeadSymbols()) {
// Generate a CleanedNode that has the environment and store cleaned
// up. Since no symbols are dead, we can optimize and not clean out
// the constraint manager.
StmtNodeBuilder Bldr(Pred, Out, *currentBuilderContext);
Bldr.generateNode(DiagnosticStmt, Pred, CleanedState, false, &cleanupTag,K);
} else {
// Call checkers with the non-cleaned state so that they could query the
// values of the soon to be dead symbols.
ExplodedNodeSet CheckedSet;
getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper,
DiagnosticStmt, *this, K);
// For each node in CheckedSet, generate CleanedNodes that have the
// environment, the store, and the constraints cleaned up but have the
// user-supplied states as the predecessors.
StmtNodeBuilder Bldr(CheckedSet, Out, *currentBuilderContext);
for (ExplodedNodeSet::const_iterator
I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) {
ProgramStateRef CheckerState = (*I)->getState();
// The constraint manager has not been cleaned up yet, so clean up now.
CheckerState = getConstraintManager().removeDeadBindings(CheckerState,
SymReaper);
assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) &&
"Checkers are not allowed to modify the Environment as a part of "
"checkDeadSymbols processing.");
assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) &&
"Checkers are not allowed to modify the Store as a part of "
"checkDeadSymbols processing.");
// Create a state based on CleanedState with CheckerState GDM and
// generate a transition to that state.
ProgramStateRef CleanedCheckerSt =
StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, false,
&cleanupTag, K);
}
}
}
void ExprEngine::ProcessStmt(const CFGStmt S,
ExplodedNode *Pred) {
// Reclaim any unnecessary nodes in the ExplodedGraph.
@ -270,78 +335,19 @@ void ExprEngine::ProcessStmt(const CFGStmt S,
currentStmt->getLocStart(),
"Error evaluating statement");
// Remove dead bindings and symbols.
EntryNode = Pred;
ExplodedNodeSet CleanedStates;
if (shouldRemoveDeadBindings(AMgr, S, Pred, EntryNode->getLocationContext())){
removeDead(EntryNode, CleanedStates, currentStmt,
Pred->getLocationContext(), currentStmt);
} else
CleanedStates.Add(EntryNode);
ProgramStateRef EntryState = EntryNode->getState();
CleanedState = EntryState;
// Create the cleaned state.
const LocationContext *LC = EntryNode->getLocationContext();
SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager());
if (shouldRemoveDeadBindings(AMgr, S, Pred, LC)) {
NumRemoveDeadBindings++;
getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
const StackFrameContext *SFC = LC->getCurrentStackFrame();
// Create a state in which dead bindings are removed from the environment
// and the store. TODO: The function should just return new env and store,
// not a new state.
CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper);
} else {
NumRemoveDeadBindingsSkipped++;
}
// Process any special transfer function for dead symbols.
ExplodedNodeSet Tmp;
// A tag to track convenience transitions, which can be removed at cleanup.
static SimpleProgramPointTag cleanupTag("ExprEngine : Clean Node");
if (!SymReaper.hasDeadSymbols()) {
// Generate a CleanedNode that has the environment and store cleaned
// up. Since no symbols are dead, we can optimize and not clean out
// the constraint manager.
StmtNodeBuilder Bldr(Pred, Tmp, *currentBuilderContext);
Bldr.generateNode(currentStmt, EntryNode, CleanedState, false, &cleanupTag);
} else {
// Call checkers with the non-cleaned state so that they could query the
// values of the soon to be dead symbols.
ExplodedNodeSet CheckedSet;
getCheckerManager().runCheckersForDeadSymbols(CheckedSet, EntryNode,
SymReaper, currentStmt, *this);
// For each node in CheckedSet, generate CleanedNodes that have the
// environment, the store, and the constraints cleaned up but have the
// user-supplied states as the predecessors.
StmtNodeBuilder Bldr(CheckedSet, Tmp, *currentBuilderContext);
for (ExplodedNodeSet::const_iterator
I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) {
ProgramStateRef CheckerState = (*I)->getState();
// The constraint manager has not been cleaned up yet, so clean up now.
CheckerState = getConstraintManager().removeDeadBindings(CheckerState,
SymReaper);
assert(StateMgr.haveEqualEnvironments(CheckerState, EntryState) &&
"Checkers are not allowed to modify the Environment as a part of "
"checkDeadSymbols processing.");
assert(StateMgr.haveEqualStores(CheckerState, EntryState) &&
"Checkers are not allowed to modify the Store as a part of "
"checkDeadSymbols processing.");
// Create a state based on CleanedState with CheckerState GDM and
// generate a transition to that state.
ProgramStateRef CleanedCheckerSt =
StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
Bldr.generateNode(currentStmt, *I, CleanedCheckerSt, false, &cleanupTag,
ProgramPoint::PostPurgeDeadSymbolsKind);
}
}
// Visit the statement.
ExplodedNodeSet Dst;
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
for (ExplodedNodeSet::iterator I = CleanedStates.begin(),
E = CleanedStates.end(); I != E; ++I) {
ExplodedNodeSet DstI;
// Visit the statement.
Visit(currentStmt, *I, DstI);
@ -994,7 +1000,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
continue;
// We reached the caller. Find the node right before we started
// processing the CallExpr.
if (isa<PostPurgeDeadSymbols>(L))
if (L.isPurgeKind())
continue;
if (const StmtPoint *SP = dyn_cast<StmtPoint>(&L))
if (SP->getStmt() == CalleeSF->getCallSite())
@ -1861,10 +1867,17 @@ struct DOTGraphTraits<ExplodedNode*> :
ProgramPoint Loc = N->getLocation();
switch (Loc.getKind()) {
case ProgramPoint::BlockEntranceKind:
case ProgramPoint::BlockEntranceKind: {
Out << "Block Entrance: B"
<< cast<BlockEntrance>(Loc).getBlock()->getBlockID();
if (const NamedDecl *ND =
dyn_cast<NamedDecl>(Loc.getLocationContext()->getDecl())) {
Out << " (";
ND->printName(Out);
Out << ")";
}
break;
}
case ProgramPoint::BlockExitKind:
assert (false);
@ -1874,8 +1887,20 @@ struct DOTGraphTraits<ExplodedNode*> :
Out << "CallEnter";
break;
case ProgramPoint::CallExitKind:
Out << "CallExit";
case ProgramPoint::CallExitBeginKind:
Out << "CallExitBegin";
break;
case ProgramPoint::CallExitEndKind:
Out << "CallExitEnd";
break;
case ProgramPoint::PostStmtPurgeDeadSymbolsKind:
Out << "PostStmtPurgeDeadSymbols";
break;
case ProgramPoint::PreStmtPurgeDeadSymbolsKind:
Out << "PreStmtPurgeDeadSymbols";
break;
case ProgramPoint::EpsilonKind:

View File

@ -51,34 +51,71 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
Engine.getWorkList()->enqueue(Node);
}
static const ReturnStmt *getReturnStmt(const ExplodedNode *Node) {
// Find the last statement on the path to the exploded node and the
// corresponding Block.
static std::pair<const Stmt*,
const CFGBlock*> getLastStmt(const ExplodedNode *Node) {
const Stmt *S = 0;
const CFGBlock *Blk = 0;
const StackFrameContext *SF =
Node->getLocation().getLocationContext()->getCurrentStackFrame();
while (Node) {
const ProgramPoint &PP = Node->getLocation();
// Skip any BlockEdges.
if (isa<BlockEdge>(PP) || isa<CallExit>(PP)) {
// Skip any BlockEdges, empty blocks, and the CallExitBegin node.
if (isa<BlockEdge>(PP) || isa<CallExitBegin>(PP) || isa<BlockEntrance>(PP)){
assert(Node->pred_size() == 1);
Node = *Node->pred_begin();
continue;
}
// If we reached the CallEnter, the function has no statements.
if (isa<CallEnter>(PP))
break;
if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) {
const Stmt *S = SP->getStmt();
return dyn_cast<ReturnStmt>(S);
S = SP->getStmt();
// Now, get the enclosing basic block.
while (Node && Node->pred_size() >=1 ) {
const ProgramPoint &PP = Node->getLocation();
if (isa<BlockEdge>(PP) &&
(PP.getLocationContext()->getCurrentStackFrame() == SF)) {
BlockEdge &EPP = cast<BlockEdge>(PP);
Blk = EPP.getDst();
break;
}
Node = *Node->pred_begin();
}
break;
}
return 0;
break;
}
return std::pair<const Stmt*, const CFGBlock*>(S, Blk);
}
void ExprEngine::processCallExit(ExplodedNode *Pred) {
ProgramStateRef state = Pred->getState();
/// The call exit is simulated with a sequence of nodes, which occur between
/// CallExitBegin and CallExitEnd. The following operations occur between the
/// two program points:
/// 1. CallExitBegin (triggers the start of call exit sequence)
/// 2. Bind the return value
/// 3. Run Remove dead bindings to clean up the dead symbols from the callee.
/// 4. CallExitEnd (switch to the caller context)
/// 5. PostStmt<CallExpr>
void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
// Step 1 CEBNode was generated before the call.
const StackFrameContext *calleeCtx =
Pred->getLocationContext()->getCurrentStackFrame();
CEBNode->getLocationContext()->getCurrentStackFrame();
const LocationContext *callerCtx = calleeCtx->getParent();
const Stmt *CE = calleeCtx->getCallSite();
ProgramStateRef state = CEBNode->getState();
// Find the last statement in the function and the corresponding basic block.
const Stmt *LastSt = 0;
const CFGBlock *Blk = 0;
llvm::tie(LastSt, Blk) = getLastStmt(CEBNode);
// Step 2: generate node with binded return value: CEBNode -> BindedRetNode.
// If the callee returns an expression, bind its value to CallExpr.
if (const ReturnStmt *RS = getReturnStmt(Pred)) {
const LocationContext *LCtx = Pred->getLocationContext();
if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) {
const LocationContext *LCtx = CEBNode->getLocationContext();
SVal V = state->getSVal(RS, LCtx);
state = state->BindExpr(CE, callerCtx, V);
}
@ -90,33 +127,65 @@ void ExprEngine::processCallExit(ExplodedNode *Pred) {
SVal ThisV = state->getSVal(ThisR);
// Always bind the region to the CXXConstructExpr.
state = state->BindExpr(CCE, Pred->getLocationContext(), ThisV);
state = state->BindExpr(CCE, CEBNode->getLocationContext(), ThisV);
}
static SimpleProgramPointTag returnTag("ExprEngine : Call Return");
PostStmt Loc(CE, callerCtx, &returnTag);
static SimpleProgramPointTag retValBindTag("ExprEngine : Bind Return Value");
PostStmt Loc(LastSt, calleeCtx, &retValBindTag);
bool isNew;
ExplodedNode *N = G.getNode(Loc, state, false, &isNew);
N->addPredecessor(Pred, G);
ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew);
BindedRetNode->addPredecessor(CEBNode, G);
if (!isNew)
return;
// Perform the post-condition check of the CallExpr.
// Step 3: BindedRetNode -> CleanedNodes
// If we can find a statement and a block in the inlined function, run remove
// dead bindings before returning from the call. This is important to ensure
// that we report the issues such as leaks in the stack contexts in which
// they occurred.
ExplodedNodeSet CleanedNodes;
if (LastSt && Blk) {
NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode);
currentBuilderContext = &Ctx;
// Here, we call the Symbol Reaper with 0 statement and caller location
// context, telling it to clean up everything in the callee's context
// (and it's children). We use LastStmt as a diagnostic statement, which
// which the PreStmtPurge Dead point will be associated.
removeDead(BindedRetNode, CleanedNodes, 0, callerCtx, LastSt,
ProgramPoint::PostStmtPurgeDeadSymbolsKind);
currentBuilderContext = 0;
}
for (ExplodedNodeSet::iterator I = CleanedNodes.begin(),
E = CleanedNodes.end(); I != E; ++I) {
// Step 4: Generate the CallExit and leave the callee's context.
// CleanedNodes -> CEENode
CallExitEnd Loc(CE, callerCtx);
bool isNew;
ExplodedNode *CEENode = G.getNode(Loc, (*I)->getState(), false, &isNew);
CEENode->addPredecessor(*I, G);
if (!isNew)
return;
// Step 5: Perform the post-condition check of the CallExpr and enqueue the
// result onto the work list.
// CEENode -> Dst -> WorkList
ExplodedNodeSet Dst;
NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), N);
NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode);
SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext,
&Ctx);
SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex());
getCheckerManager().runCheckersForPostStmt(Dst, N, CE, *this,
/* wasInlined */ true);
getCheckerManager().runCheckersForPostStmt(Dst, CEENode, CE, *this, true);
// Enqueue the next element in the block.
for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I) {
Engine.getWorkList()->enqueue(*I,
calleeCtx->getCallSiteBlock(),
for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end();
PSI != PSE; ++PSI) {
Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(),
calleeCtx->getIndex()+1);
}
}
}
static unsigned getNumberStackFrames(const LocationContext *LCtx) {

View File

@ -510,9 +510,9 @@ static PathDiagnosticLocation getLastStmtLoc(const ExplodedNode *N,
PathDiagnosticCallPiece *
PathDiagnosticCallPiece::construct(const ExplodedNode *N,
const CallExit &CE,
const CallExitEnd &CE,
const SourceManager &SM) {
const Decl *caller = CE.getLocationContext()->getParent()->getDecl();
const Decl *caller = CE.getLocationContext()->getDecl();
PathDiagnosticLocation pos = getLastStmtLoc(N, SM);
return new PathDiagnosticCallPiece(caller, pos);
}
@ -667,16 +667,13 @@ StackHintGenerator::~StackHintGenerator() {}
std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
ProgramPoint P = N->getLocation();
const CallExit *CExit = dyn_cast<CallExit>(&P);
assert(CExit && "Stack Hints should be constructed at CallExit points.");
const CallExitEnd *CExit = dyn_cast<CallExitEnd>(&P);
assert(CExit && "Stack Hints should be constructed at CallExitEnd points.");
const CallExpr *CE = dyn_cast_or_null<CallExpr>(CExit->getStmt());
if (!CE)
return "";
// Get the successor node to make sure the return statement is evaluated and
// CE is set to the result value.
N = *N->succ_begin();
if (!N)
return getMessageForSymbolNotFound();

View File

@ -501,6 +501,9 @@ SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const {
return false;
return true;
}
// If no statement is provided, everything is this and parent contexts is live.
if (!Loc)
return true;
return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal);
}
@ -510,6 +513,10 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame();
if (VarContext == CurrentContext) {
// If no statemetnt is provided, everything is live.
if (!Loc)
return true;
if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl()))
return true;

File diff suppressed because it is too large Load Diff