clang: lib/StaticAnalyzer/Core/CoreEngine.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/Statistic.h"
31#include "llvm/Support/Casting.h"
32#include "llvm/Support/ErrorHandling.h"
33#include
34#include
35#include
36#include
37#include
38
39using namespace clang;
40using namespace ento;
41
42#define DEBUG_TYPE "CoreEngine"
43
45 "The # of steps executed.");
46STATISTIC(NumSTUSteps, "The # of STU steps executed.");
47STATISTIC(NumCTUSteps, "The # of CTU steps executed.");
49 "The # of times we reached the max number of steps.");
51 "The # of paths explored by the analyzer.");
52
53
54
55
56
59 case ExplorationStrategyKind::DFS:
61 case ExplorationStrategyKind::BFS:
63 case ExplorationStrategyKind::BFSBlockDFSContents:
65 case ExplorationStrategyKind::UnexploredFirst:
67 case ExplorationStrategyKind::UnexploredFirstQueue:
69 case ExplorationStrategyKind::UnexploredFirstLocationQueue:
71 }
72 llvm_unreachable("Unknown AnalyzerOptions::ExplorationStrategyKind");
73}
74
78 CTUWList(Opts.IsNaiveCTUEnabled ? generateWorkList(Opts) : nullptr),
79 BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {}
80
82 WList->setBlockCounter(C);
83 if (CTUWList)
84 CTUWList->setBlockCounter(C);
85}
86
87
90 if (G.num_roots() == 0) {
91
92
94
95 assert(Entry->empty() && "Entry block must be empty.");
96
97 assert(Entry->succ_size() == 1 && "Entry block must have 1 successor.");
98
99
103
104
106
107
108
109 BlockEdge StartLoc(Entry, Succ, L);
110
111
113
114 if (!InitState)
116
117 bool IsNew;
119 assert(IsNew);
121
125
127 }
128
129
130 bool UnlimitedSteps = MaxSteps == 0;
131
132
133
134 const unsigned PreReservationCap = 4000000;
135 if(!UnlimitedSteps)
136 G.reserve(std::min(MaxSteps, PreReservationCap));
137
138 auto ProcessWList = [this, UnlimitedSteps](unsigned MaxSteps) {
139 unsigned Steps = MaxSteps;
140 while (WList->hasWork()) {
141 if (!UnlimitedSteps) {
142 if (Steps == 0) {
143 NumReachedMaxSteps++;
144 break;
145 }
146 --Steps;
147 }
148
149 NumSteps++;
150
152
153
155
156
158
160 }
161 return MaxSteps - Steps;
162 };
163 const unsigned STUSteps = ProcessWList(MaxSteps);
164
165 if (CTUWList) {
166 NumSTUSteps += STUSteps;
167 const unsigned MinCTUSteps =
169 const unsigned Pct =
171 unsigned MaxCTUSteps = std::max(STUSteps * Pct / 100, MinCTUSteps);
172
173 WList = std::move(CTUWList);
174 const unsigned CTUSteps = ProcessWList(MaxCTUSteps);
175 NumCTUSteps += CTUSteps;
176 }
177
179 return WList->hasWork();
180}
181
184
188 break;
189
192 break;
193
195 assert(false && "BlockExit location never occur in forward analysis.");
196 break;
197
200 break;
201
204 break;
205
208 "Assume epsilon has exactly one predecessor by construction");
211 break;
212 }
213 default:
221 break;
222 }
223}
224
228
229
234
235
236
241
242
243 return "Virtual base initialization skipped because "
244 "it has already been handled by the most derived class";
245 },
246 true));
247
250 Pred = Bldr.generateNode(P, Pred->getState(), Pred);
251 if (!Pred)
252 return;
253 }
254
255
258 "EXIT block cannot contain Stmts.");
259
260
264 if (std::optional LastStmt = LastElement.getAs<CFGStmt>()) {
265 RS = dyn_cast(LastStmt->getStmt());
266 } else if (std::optional AutoDtor =
268 RS = dyn_cast(AutoDtor->getTriggerStmt());
269 }
270 }
271
272
274
275
276 return;
277 }
278
279
284
285
286 if (!nodeBuilder.hasGeneratedNodes()) {
287 nodeBuilder.generateNode(Pred->State, Pred);
288 }
289
290
292}
293
294void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
296
299 BlockCounter Counter = WList->getBlockCounter();
301 BlockId);
302 setBlockCounter(Counter);
303
304
308 } else
309 HandleBlockExit(L.getBlock(), Pred);
310}
311
314 switch (Term->getStmtClass()) {
315 default:
316 llvm_unreachable("Analysis for this terminator not implemented.");
317
318 case Stmt::CXXBindTemporaryExprClass:
319 HandleCleanupTemporaryBranch(
320 cast(Term), B, Pred);
321 return;
322
323
324 case Stmt::DeclStmtClass:
325 HandleStaticInit(cast(Term), B, Pred);
326 return;
327
328 case Stmt::BinaryOperatorClass:
329 HandleBranch(cast(Term)->getLHS(), Term, B, Pred);
330 return;
331
332 case Stmt::BinaryConditionalOperatorClass:
333 case Stmt::ConditionalOperatorClass:
334 HandleBranch(cast(Term)->getCond(),
335 Term, B, Pred);
336 return;
337
338
339
340
341 case Stmt::ChooseExprClass:
342 HandleBranch(cast(Term)->getCond(), Term, B, Pred);
343 return;
344
345 case Stmt::CXXTryStmtClass:
346
347
349 et = B->succ_end(); it != et; ++it) {
350 if (const CFGBlock *succ = *it) {
352 Pred->State, Pred);
353 }
354 }
355 return;
356
357 case Stmt::DoStmtClass:
358 HandleBranch(cast(Term)->getCond(), Term, B, Pred);
359 return;
360
361 case Stmt::CXXForRangeStmtClass:
362 HandleBranch(cast(Term)->getCond(), Term, B, Pred);
363 return;
364
365 case Stmt::ForStmtClass:
366 HandleBranch(cast(Term)->getCond(), Term, B, Pred);
367 return;
368
369 case Stmt::SEHLeaveStmtClass:
370 case Stmt::ContinueStmtClass:
371 case Stmt::BreakStmtClass:
372 case Stmt::GotoStmtClass:
373 break;
374
375 case Stmt::IfStmtClass:
376 HandleBranch(cast(Term)->getCond(), Term, B, Pred);
377 return;
378
379 case Stmt::IndirectGotoStmtClass: {
380
382
384 builder(Pred, B, cast(Term)->getTarget(),
386
388 return;
389 }
390
391 case Stmt::ObjCForCollectionStmtClass:
392
393
394
395
396
397
398
399
400
401
402 HandleBranch(Term, Term, B, Pred);
403 return;
404
405 case Stmt::SwitchStmtClass: {
406 SwitchNodeBuilder builder(Pred, B, cast(Term)->getCond(),
407 this);
408
410 return;
411 }
412
413 case Stmt::WhileStmtClass:
414 HandleBranch(cast(Term)->getCond(), Term, B, Pred);
415 return;
416
417 case Stmt::GCCAsmStmtClass:
418 assert(cast(Term)->isAsmGoto() && "Encountered GCCAsmStmt without labels");
419
420 return;
421 }
422 }
423
425 HandleVirtualBaseBranch(B, Pred);
426 return;
427 }
428
430 "Blocks with no terminator should have at most 1 successor.");
431
433 Pred->State, Pred);
434}
435
439}
440
441void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
448 getCompletedIterationCount(B, Pred));
449
451}
452
453void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
461
463}
464
465void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
472
474}
475
476void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
478 assert(B);
479 assert(!B->empty());
480
481 if (StmtIdx == B->size())
482 HandleBlockExit(B, Pred);
483 else {
486 }
487}
488
489void CoreEngine::HandleVirtualBaseBranch(const CFGBlock *B,
492 if (const auto *CallerCtor = dyn_cast_or_null(
494 switch (CallerCtor->getConstructionKind()) {
498 HandleBlockEdge(Loc, Pred);
499 return;
500 }
501 default:
502 break;
503 }
504 }
505
506
507
509 HandleBlockEdge(Loc, Pred);
510}
511
512
513
517 bool IsNew;
519
520 if (Pred)
521 Node->addPredecessor(Pred, G);
522 else {
523 assert(IsNew);
525 }
526
527
528 if (IsNew) WList->enqueue(Node);
529}
530
534 assert(!N->isSink());
535
536
538
539
540 WList->enqueue(N, Block, Idx);
541 return;
542 }
543
544
548 WList->enqueue(N, Block, Idx+1);
549 return;
550 }
551
553 WList->enqueue(N, Block, Idx);
554 return;
555 }
556
558 WList->enqueue(N, Block, Idx+1);
559 return;
560 }
561
562
565
567
568
569 WList->enqueue(N, Block, Idx+1);
570 return;
571 }
572
573 bool IsNew;
576
577 if (IsNew)
578 WList->enqueue(Succ, Block, Idx+1);
579}
580
583
584 const auto *LocCtx = cast(N->getLocationContext());
585
586
588
589 bool isNew;
591 Node->addPredecessor(N, G);
592 return isNew ? Node : nullptr;
593}
594
595std::optional
596CoreEngine::getCompletedIterationCount(const CFGBlock *B,
599 BlockCounter Counter = WList->getBlockCounter();
600 unsigned BlockCount =
602
604 if (isa<ForStmt, WhileStmt, CXXForRangeStmt>(Term)) {
605 assert(BlockCount >= 1 &&
606 "Block count of currently analyzed block must be >= 1");
607 return BlockCount - 1;
608 }
609 if (isa(Term)) {
610
611
612 return BlockCount;
613 }
614
615
616 return std::nullopt;
617}
618
620 for (const auto I : Set)
621 WList->enqueue(I);
622}
623
626 for (const auto I : Set)
628}
629
631 for (auto *I : Set) {
632
633 if (I->getLocationContext()->getParent()) {
634 I = generateCallExitBeginNode(I, RS);
635 if (I)
636 WList->enqueue(I);
637 } else {
638
640 NumPathsExplored++;
641 }
642 }
643}
644
645void NodeBuilder::anchor() {}
646
650 bool MarkAsSink) {
652 bool IsNew;
656
657 if (!IsNew)
658 return nullptr;
659
660 if (!MarkAsSink)
662
663 return N;
664}
665
666void NodeBuilderWithSinks::anchor() {}
667
669 if (EnclosingBldr)
670 for (const auto I : Frontier)
672}
673
674void BranchNodeBuilder::anchor() {}
675
677 bool Branch,
679 const CFGBlock *Dst = Branch ? DstT : DstF;
680
681 if (!Dst)
682 return nullptr;
683
687 return Succ;
688}
689
693 bool IsSink) {
694 bool IsNew;
697 St, IsSink, &IsNew);
699
700 if (!IsNew)
701 return nullptr;
702
703 if (!IsSink)
704 Eng.WList->enqueue(Succ);
705
706 return Succ;
707}
708
712 bool IsNew;
715 St, false, &IsNew);
717 if (!IsNew)
718 return nullptr;
719
720 Eng.WList->enqueue(Succ);
721 return Succ;
722}
723
726 bool IsSink) {
727
730
731
732
733 if (!DefaultBlock)
734 return nullptr;
735
736 bool IsNew;
739 St, IsSink, &IsNew);
741
742 if (!IsNew)
743 return nullptr;
744
745 if (!IsSink)
746 Eng.WList->enqueue(Succ);
747
748 return Succ;
749}
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
static std::unique_ptr< WorkList > generateWorkList(AnalyzerOptions &Opts)
STATISTIC(NumSteps, "The # of steps executed.")
static Decl::Kind getKind(const Decl *D)
Defines the clang::Expr interface and subclasses for C++ expressions.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Stores options for the analyzer from the command line.
ExplorationStrategyKind getExplorationStrategy() const
const CFGBlock * getSrc() const
const CFGBlock * getDst() const
std::optional< CFGElement > getFirstElement() const
const CFGBlock * getBlock() const
Represents C++ object destructor implicitly generated for automatic object or temporary bound to cons...
Represents a single basic block in a source-level CFG.
succ_reverse_iterator succ_rend()
succ_reverse_iterator succ_rbegin()
CFGTerminator getTerminator() const
succ_iterator succ_begin()
Stmt * getTerminatorStmt()
unsigned getBlockID() const
AdjacentBlocks::const_iterator const_succ_iterator
unsigned succ_size() const
Represents a top-level expression in a basic block.
T castAs() const
Convert to the specified CFGElement type, asserting that this CFGElement is of the desired type.
std::optional< T > getAs() const
Convert to the specified CFGElement type, returning std::nullopt if this CFGElement is not of the des...
const Stmt * getStmt() const
bool isVirtualBaseBranch() const
unsigned getNumBlockIDs() const
Returns the total number of BlockIDs allocated (which start at 0).
Represents binding an expression to a temporary.
Represents a point when we begin processing an inlined call.
const CFGBlock * getEntry() const
Returns the entry block in the CFG for the entered function.
Represents a point when we start the call exit sequence (for inlined call).
Represents a point when we finish the call exit sequence (for inlined call).
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
This is a meta program point, which should be skipped by all the diagnostic reasoning etc.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
const Decl * getDecl() const
const StackFrameContext * getStackFrame() const
Represents a point when we exit a loop.
Represents a program point just after an implicit call event.
ProgramPoint withTag(const ProgramPointTag *tag) const
Create a new ProgramPoint object that is the same as the original except for using the specified tag ...
std::optional< T > getAs() const
Convert to the specified ProgramPoint type, returning std::nullopt if this ProgramPoint is not of the...
const LocationContext * getLocationContext() const
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
const Stmt * getCallSite() const
Stmt - This represents one statement.
AnalyzerOptions & options
BlockCounter IncrementCount(BlockCounter BC, const StackFrameContext *CallSite, unsigned BlockID)
BlockCounter GetEmptyCounter()
An abstract data type used to count the number of times a given block has been visited along a path a...
unsigned getNumVisited(const StackFrameContext *CallSite, unsigned BlockID) const
ExplodedNode * generateNode(ProgramStateRef State, bool branch, ExplodedNode *Pred)
CoreEngine(ExprEngine &exprengine, FunctionSummariesTy *FS, AnalyzerOptions &Opts)
Construct a CoreEngine object to analyze the provided CFG.
DataTag::Factory & getDataTags()
void enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx)
Enqueue a single node created as a result of statement processing.
void dispatchWorkItem(ExplodedNode *Pred, ProgramPoint Loc, const WorkListUnit &WU)
Dispatch the work list item based on the given location information.
bool ExecuteWorkList(const LocationContext *L, unsigned Steps, ProgramStateRef InitState)
ExecuteWorkList - Run the worklist algorithm for a maximum number of steps.
void enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS)
enqueue the nodes corresponding to the end of function onto the end of path / work list.
void enqueue(ExplodedNodeSet &Set)
Enqueue the given set of nodes onto the work list.
ExplodedNode * addRoot(ExplodedNode *V)
addRoot - Add an untyped node to the set of roots.
void reserve(unsigned NodeCount)
unsigned num_roots() const
ExplodedNode * getNode(const ProgramPoint &L, ProgramStateRef State, bool IsSink=false, bool *IsNew=nullptr)
Retrieve the node associated with a (Location,State) pair, where the 'Location' is a ProgramPoint in ...
ExplodedNode * addEndOfPath(ExplodedNode *V)
addEndOfPath - Add an untyped node to the set of EOP nodes.
bool erase(ExplodedNode *N)
void Add(ExplodedNode *N)
const ProgramStateRef & getState() const
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
void addPredecessor(ExplodedNode *V, ExplodedGraph &G)
addPredeccessor - Adds a predecessor to the current node, and in tandem add this node as a successor ...
bool hasSinglePred() const
const LocationContext * getLocationContext() const
ExplodedNode * getFirstPred()
void processEndOfFunction(NodeBuilderContext &BC, ExplodedNode *Pred, const ReturnStmt *RS=nullptr)
Called by CoreEngine.
void processCallEnter(NodeBuilderContext &BC, CallEnter CE, ExplodedNode *Pred)
Generate the entry node of the callee.
void processBeginOfFunction(NodeBuilderContext &BC, ExplodedNode *Pred, ExplodedNodeSet &Dst, const BlockEdge &L)
Called by CoreEngine.
ProgramStateRef getInitialState(const LocationContext *InitLoc)
getInitialState - Return the initial state used for the root vertex in the ExplodedGraph.
void processCallExit(ExplodedNode *Pred)
Generate the sequence of nodes that simulate the call exit and the post visit for CallExpr.
void processCFGBlockEntrance(const BlockEdge &L, NodeBuilderWithSinks &nodeBuilder, ExplodedNode *Pred)
Called by CoreEngine when processing the entrance of a CFGBlock.
void processCFGElement(const CFGElement E, ExplodedNode *Pred, unsigned StmtIdx, NodeBuilderContext *Ctx)
processCFGElement - Called by CoreEngine.
void processStaticInitializer(const DeclStmt *DS, NodeBuilderContext &BuilderCtx, ExplodedNode *Pred, ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF)
Called by CoreEngine.
void processBranch(const Stmt *Condition, NodeBuilderContext &BuilderCtx, ExplodedNode *Pred, ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF, std::optional< unsigned > IterationsCompletedInLoop)
ProcessBranch - Called by CoreEngine.
void processSwitch(SwitchNodeBuilder &builder)
ProcessSwitch - Called by CoreEngine.
void processEndWorklist()
Called by CoreEngine when the analysis worklist has terminated.
void processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, NodeBuilderContext &BldCtx, ExplodedNode *Pred, ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF)
Called by CoreEngine.
AnalysisManager & getAnalysisManager()
void processIndirectGoto(IndirectGotoNodeBuilder &builder)
processIndirectGoto - Called by CoreEngine.
void markVisitedBasicBlock(unsigned ID, const Decl *D, unsigned TotalIDs)
const CFGBlock * getBlock() const
ExplodedNode * generateNode(const iterator &I, ProgramStateRef State, bool isSink=false)
const CoreEngine & getEngine() const
Return the CoreEngine associated with this builder.
const CFGBlock * getBlock() const
Return the CFGBlock associated with this builder.
This node builder keeps track of the generated sink nodes.
This is the simplest builder which generates nodes in the ExplodedGraph.
const NodeBuilderContext & C
ExplodedNodeSet & Frontier
The frontier set - a set of nodes which need to be propagated after the builder dies.
void addNodes(const ExplodedNodeSet &S)
ExplodedNode * generateNodeImpl(const ProgramPoint &PP, ProgramStateRef State, ExplodedNode *Pred, bool MarkAsSink=false)
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
~StmtNodeBuilder() override
const CFGBlock * getBlock() const
ExplodedNode * generateDefaultCaseNode(ProgramStateRef State, bool isSink=false)
ExplodedNode * generateCaseStmtNode(const iterator &I, ProgramStateRef State)
ExplodedNode * getNode() const
Returns the node associated with the worklist unit.
unsigned getIndex() const
Return the index within the CFGBlock for the worklist unit.
const CFGBlock * getBlock() const
Returns the CFGblock associated with the worklist unit.
BlockCounter getBlockCounter() const
Returns the block counter map associated with the worklist unit.
static std::unique_ptr< WorkList > makeUnexploredFirstPriorityLocationQueue()
static std::unique_ptr< WorkList > makeUnexploredFirstPriorityQueue()
static std::unique_ptr< WorkList > makeBFSBlockDFSContents()
static std::unique_ptr< WorkList > makeBFS()
static std::unique_ptr< WorkList > makeDFS()
static std::unique_ptr< WorkList > makeUnexploredFirst()
The JSON file list parser is used to communicate input to InstallAPI.