clang: lib/StaticAnalyzer/Core/CoreEngine.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

13

31#include "llvm/Support/ErrorHandling.h"

32#include "llvm/Support/FormatVariadic.h"

33#include "llvm/Support/TimeProfiler.h"

34#include

35#include

36#include

37#include

38#include

39

40using namespace clang;

41using namespace ento;

42

43#define DEBUG_TYPE "CoreEngine"

44

46STAT_COUNTER(NumSTUSteps, "The # of STU steps executed.");

47STAT_COUNTER(NumCTUSteps, "The # of CTU steps executed.");

49 "The # of times we reached the max number of steps.");

50STAT_COUNTER(NumPathsExplored, "The # of paths explored by the analyzer.");

51

52

53

54

55

70 }

71 llvm_unreachable("Unknown AnalyzerOptions::ExplorationStrategyKind");

72}

73

78 BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {}

79

81 WList->setBlockCounter(C);

82 if (CTUWList)

83 CTUWList->setBlockCounter(C);

84}

85

86

89 if (G.empty()) {

90 assert(!G.getRoot() && "empty graph must not have a root node");

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

100 FunctionSummaries->markVisitedBasicBlock(Entry->getBlockID(),

103

104

106

107

108

109 BlockEdge StartLoc(Entry, Succ, L);

110

111

112 setBlockCounter(BCounterFactory.GetEmptyCounter());

113

114 if (!InitState)

115 InitState = ExprEng.getInitialState(L);

116

117 bool IsNew;

118 ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew);

119 assert(IsNew);

120 G.designateAsRoot(Node);

121

124 ExprEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc);

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 =

168 this->ExprEng.getAnalysisManager().options.CTUMaxNodesMin;

169 const unsigned Pct =

170 this->ExprEng.getAnalysisManager().options.CTUMaxNodesPercentage;

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

178 ExprEng.processEndWorklist();

179 return WList->hasWork();

180}

181

183 if (llvm::timeTraceProfilerEnabled()) {

184 return llvm::formatv("dispatchWorkItem {0}",

186 .str();

187 }

188 return "";

189}

190

193

194 assert(llvm::timeTraceProfilerEnabled());

195 std::string Detail = "";

197 if (const Stmt *S = SP->getStmt())

198 Detail = S->getStmtClassName();

199 }

200 auto SLoc = Loc.getSourceLocation();

201 if (!SLoc)

202 return llvm::TimeTraceMetadata{std::move(Detail), ""};

207 auto Line = SM.getPresumedLineNumber(*SLoc);

208 auto Fname = SM.getFilename(*SLoc);

209 return llvm::TimeTraceMetadata{std::move(Detail), Fname.str(),

210 static_cast<int>(Line)};

211}

212

217 }};

219

223 break;

224

227 break;

228

230 assert(false && "BlockExit location never occur in forward analysis.");

231 break;

232

235 break;

236

238 ExprEng.processCallExit(Pred);

239 break;

240

243 "Assume epsilon has exactly one predecessor by construction");

246 break;

247 }

248 default:

256 break;

257 }

258}

259

263

264

269

270

271

276

277

278 return "Virtual base initialization skipped because "

279 "it has already been handled by the most derived class";

280 },

281 true));

282

285 Pred = Bldr.generateNode(P, Pred->getState(), Pred);

286 if (!Pred)

287 return;

288 }

289

290

293 "EXIT block cannot contain Stmts.");

294

295

299 if (std::optional LastStmt = LastElement.getAs<CFGStmt>()) {

300 RS = dyn_cast(LastStmt->getStmt());

301 } else if (std::optional AutoDtor =

302 LastElement.getAs()) {

303 RS = dyn_cast(AutoDtor->getTriggerStmt());

304 }

305 }

306

307 ExplodedNodeSet CheckerNodes;

309 ExprEng.runCheckersForBlockEntrance(BuilderCtx, BE, Pred, CheckerNodes);

310

311

312 for (ExplodedNode *P : CheckerNodes) {

313 ExprEng.processEndOfFunction(BuilderCtx, P, RS);

314 }

315

316

317 return;

318 }

319

320

322 ExplodedNodeSet DstNodes;

323 NodeBuilderWithSinks NodeBuilder(Pred, DstNodes, BuilderCtx, BE);

324 ExprEng.processCFGBlockEntrance(L, NodeBuilder, Pred);

325

326

328 NodeBuilder.generateNode(Pred->State, Pred);

329 }

330

331 ExplodedNodeSet CheckerNodes;

332 for (auto *N : DstNodes) {

333 ExprEng.runCheckersForBlockEntrance(BuilderCtx, BE, N, CheckerNodes);

334 }

335

336

338}

339

340void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,

342

345 BlockCounter Counter = WList->getBlockCounter();

346 Counter = BCounterFactory.IncrementCount(Counter, LC->getStackFrame(),

347 BlockId);

348 setBlockCounter(Counter);

349

350

353 ExprEng.processCFGElement(*E, Pred, 0, &Ctx);

354 } else

355 HandleBlockExit(L.getBlock(), Pred);

356}

357

358void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {

360 switch (Term->getStmtClass()) {

361 default:

362 llvm_unreachable("Analysis for this terminator not implemented.");

363

364 case Stmt::CXXBindTemporaryExprClass:

365 HandleCleanupTemporaryBranch(

367 return;

368

369

370 case Stmt::DeclStmtClass:

372 return;

373

374 case Stmt::BinaryOperatorClass:

376 return;

377

378 case Stmt::BinaryConditionalOperatorClass:

379 case Stmt::ConditionalOperatorClass:

381 Term, B, Pred);

382 return;

383

384

385

386

387 case Stmt::ChooseExprClass:

388 HandleBranch(cast(Term)->getCond(), Term, B, Pred);

389 return;

390

391 case Stmt::CXXTryStmtClass:

392

393

395 et = B->succ_end(); it != et; ++it) {

396 if (const CFGBlock *succ = *it) {

398 Pred->State, Pred);

399 }

400 }

401 return;

402

403 case Stmt::DoStmtClass:

404 HandleBranch(cast(Term)->getCond(), Term, B, Pred);

405 return;

406

407 case Stmt::CXXForRangeStmtClass:

409 return;

410

411 case Stmt::ForStmtClass:

412 HandleBranch(cast(Term)->getCond(), Term, B, Pred);

413 return;

414

415 case Stmt::SEHLeaveStmtClass:

416 case Stmt::ContinueStmtClass:

417 case Stmt::BreakStmtClass:

418 case Stmt::GotoStmtClass:

419 break;

420

421 case Stmt::IfStmtClass:

422 HandleBranch(cast(Term)->getCond(), Term, B, Pred);

423 return;

424

425 case Stmt::IndirectGotoStmtClass: {

426

428

432

433 ExprEng.processIndirectGoto(builder);

434 return;

435 }

436

437 case Stmt::ObjCForCollectionStmtClass:

438

439

440

441

442

443

444

445

446

447

448 HandleBranch(Term, Term, B, Pred);

449 return;

450

451 case Stmt::SwitchStmtClass: {

453 this);

454

455 ExprEng.processSwitch(builder);

456 return;

457 }

458

459 case Stmt::WhileStmtClass:

460 HandleBranch(cast(Term)->getCond(), Term, B, Pred);

461 return;

462

463 case Stmt::GCCAsmStmtClass:

464 assert(cast(Term)->isAsmGoto() && "Encountered GCCAsmStmt without labels");

465

466 return;

467 }

468 }

469

471 HandleVirtualBaseBranch(B, Pred);

472 return;

473 }

474

476 "Blocks with no terminator should have at most 1 successor.");

477

479 Pred->State, Pred);

480}

481

482void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) {

484 ExprEng.processCallEnter(BuilderCtx, CE, Pred);

485}

486

487void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,

491 ExplodedNodeSet Dst;

492 ExprEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()),

494 getCompletedIterationCount(B, Pred));

495

497}

498

499void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,

500 const CFGBlock *B,

504 ExplodedNodeSet Dst;

505 ExprEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()),

507

509}

510

511void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,

515 ExplodedNodeSet Dst;

516 ExprEng.processStaticInitializer(DS, Ctx, Pred, Dst,

518

520}

521

522void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,

524 assert(B);

525 assert(!B->empty());

526

527 if (StmtIdx == B->size())

528 HandleBlockExit(B, Pred);

529 else {

531 ExprEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx);

532 }

533}

534

535void CoreEngine::HandleVirtualBaseBranch(const CFGBlock *B,

538 if (const auto *CallerCtor = dyn_cast_or_null(

540 switch (CallerCtor->getConstructionKind()) {

543 BlockEdge Loc(B, *B->succ_begin(), LCtx);

544 HandleBlockEdge(Loc, Pred);

545 return;

546 }

547 default:

548 break;

549 }

550 }

551

552

553

554 BlockEdge Loc(B, *(B->succ_begin() + 1), LCtx);

555 HandleBlockEdge(Loc, Pred);

556}

557

558

559

560void CoreEngine::generateNode(const ProgramPoint &Loc,

563 assert(Pred);

564 bool IsNew;

565 ExplodedNode *Node = G.getNode(Loc, State, false, &IsNew);

566

567 Node->addPredecessor(Pred, G);

568

569

570 if (IsNew) WList->enqueue(Node);

571}

572

576 assert(!N->isSink());

577

578

580

581

582 WList->enqueue(N, Block, Idx);

583 return;

584 }

585

586

590 WList->enqueue(N, Block, Idx+1);

591 return;

592 }

593

595 WList->enqueue(N, Block, Idx);

596 return;

597 }

598

600 WList->enqueue(N, Block, Idx+1);

601 return;

602 }

603

604

607

609

610

611 WList->enqueue(N, Block, Idx+1);

612 return;

613 }

614

615 bool IsNew;

618

619 if (IsNew)

620 WList->enqueue(Succ, Block, Idx+1);

621}

622

625

627

628

630

631 bool isNew;

634 return isNew ? Node : nullptr;

635}

636

637std::optional

638CoreEngine::getCompletedIterationCount(const CFGBlock *B,

641 BlockCounter Counter = WList->getBlockCounter();

642 unsigned BlockCount =

644

647 assert(BlockCount >= 1 &&

648 "Block count of currently analyzed block must be >= 1");

649 return BlockCount - 1;

650 }

652

653

654 return BlockCount;

655 }

656

657

658 return std::nullopt;

659}

660

662 for (const auto I : Set)

663 WList->enqueue(I);

664}

665

668 for (const auto I : Set)

670}

671

673 for (auto *I : Set) {

674

675 if (I->getLocationContext()->getParent()) {

676 I = generateCallExitBeginNode(I, RS);

677 if (I)

678 WList->enqueue(I);

679 } else {

680

681 G.addEndOfPath(I);

682 NumPathsExplored++;

683 }

684 }

685}

686

687void NodeBuilder::anchor() {}

688

692 bool MarkAsSink) {

694 bool IsNew;

695 ExplodedNode *N = C.getEngine().G.getNode(Loc, State, MarkAsSink, &IsNew);

698

699 if (!IsNew)

700 return nullptr;

701

702 if (!MarkAsSink)

704

705 return N;

706}

707

708void NodeBuilderWithSinks::anchor() {}

709

711 if (EnclosingBldr)

712 for (const auto I : Frontier)

713 EnclosingBldr->addNodes(I);

714}

715

716void BranchNodeBuilder::anchor() {}

717

719 bool Branch,

721 const CFGBlock *Dst = Branch ? DstT : DstF;

722

723 if (!Dst)

724 return nullptr;

725

729 return Succ;

730}

731

735 bool IsSink) {

736 bool IsNew;

738 Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()),

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}

750

754 bool IsNew;

756 Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()),

757 St, false, &IsNew);

759 if (!IsNew)

760 return nullptr;

761

762 Eng.WList->enqueue(Succ);

763 return Succ;

764}

765

768 bool IsSink) {

769

770 assert(Src->succ_rbegin() != Src->succ_rend());

772

773

774

775 if (!DefaultBlock)

776 return nullptr;

777

778 bool IsNew;

780 Eng.G.getNode(BlockEdge(Src, DefaultBlock, Pred->getLocationContext()),

781 St, IsSink, &IsNew);

783

784 if (!IsNew)

785 return nullptr;

786

787 if (!IsSink)

788 Eng.WList->enqueue(Succ);

789

790 return Succ;

791}

This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...

static std::unique_ptr< WorkList > generateWorkList(AnalyzerOptions &Opts)

Definition CoreEngine.cpp:56

ALWAYS_ENABLED_STATISTIC(NumReachedMaxSteps, "The # of times we reached the max number of steps.")

static std::string timeTraceScopeName(const ProgramPoint &Loc)

Definition CoreEngine.cpp:182

static llvm::TimeTraceMetadata timeTraceMetadata(const ExplodedNode *Pred, const ProgramPoint &Loc)

Definition CoreEngine.cpp:191

static Decl::Kind getKind(const Decl *D)

#define STAT_COUNTER(VARNAME, DESC)

Defines the clang::Expr interface and subclasses for C++ expressions.

Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.

SourceManager & getSourceManager()

ASTContext & getASTContext() const

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 a single basic block in a source-level CFG.

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

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

LLVM_ATTRIBUTE_RETURNS_NONNULL AnalysisDeclContext * getAnalysisDeclContext() const

const StackFrameContext * getStackFrame() const

Represents a point when we exit a loop.

Represents a program point just after an implicit call event.

static StringRef getProgramPointKindName(Kind K)

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.

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)

Definition CoreEngine.cpp:718

friend class SwitchNodeBuilder

CoreEngine(ExprEngine &exprengine, FunctionSummariesTy *FS, AnalyzerOptions &Opts)

Construct a CoreEngine object to analyze the provided CFG.

Definition CoreEngine.cpp:74

DataTag::Factory & getDataTags()

void enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx)

Enqueue a single node created as a result of statement processing.

Definition CoreEngine.cpp:573

void dispatchWorkItem(ExplodedNode *Pred, ProgramPoint Loc, const WorkListUnit &WU)

Dispatch the work list item based on the given location information.

Definition CoreEngine.cpp:213

friend class IndirectGotoNodeBuilder

friend class NodeBuilderContext

bool ExecuteWorkList(const LocationContext *L, unsigned Steps, ProgramStateRef InitState)

ExecuteWorkList - Run the worklist algorithm for a maximum number of steps.

Definition CoreEngine.cpp:87

void enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS)

enqueue the nodes corresponding to the end of function onto the end of path / work list.

Definition CoreEngine.cpp:672

void enqueue(ExplodedNodeSet &Set)

Enqueue the given set of nodes onto the work list.

Definition CoreEngine.cpp:661

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

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 markVisitedBasicBlock(unsigned ID, const Decl *D, unsigned TotalIDs)

const CFGBlock * getBlock() const

ExplodedNode * generateNode(const iterator &I, ProgramStateRef State, bool isSink=false)

Definition CoreEngine.cpp:733

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.

ExplodedNode * generateNodeImpl(const ProgramPoint &PP, ProgramStateRef State, ExplodedNode *Pred, bool MarkAsSink=false)

Definition CoreEngine.cpp:689

While alive, includes the current analysis stack in a crash trace.

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

Definition CoreEngine.cpp:710

const CFGBlock * getBlock() const

ExplodedNode * generateDefaultCaseNode(ProgramStateRef State, bool isSink=false)

Definition CoreEngine.cpp:767

ExplodedNode * generateCaseStmtNode(const iterator &I, ProgramStateRef State)

Definition CoreEngine.cpp:752

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()

IntrusiveRefCntPtr< const ProgramState > ProgramStateRef

The JSON file list parser is used to communicate input to InstallAPI.

bool isa(CodeGen::Address addr)

nullptr

This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...

U cast(CodeGen::Address addr)

@ UnexploredFirstLocationQueue