1 //==--AnalyzerStatsChecker.cpp - Analyzer visitation statistics --*- C++ -*-==//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
8 // This file reports various statistics about analyzer visitation.
9 //===----------------------------------------------------------------------===//
10 #include "clang/AST/DeclObjC.h"
11 #include "clang/Basic/SourceManager.h"
12 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
13 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
14 #include "clang/StaticAnalyzer/Core/Checker.h"
15 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
16 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
17 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/ADT/SmallPtrSet.h"
20 #include "llvm/ADT/SmallString.h"
21 #include "llvm/ADT/Statistic.h"
22 #include "llvm/Support/raw_ostream.h"
25 using namespace clang
;
28 #define DEBUG_TYPE "StatsChecker"
31 "The # of blocks in top level functions");
32 STATISTIC(NumBlocksUnreachable
,
33 "The # of unreachable blocks in analyzing top level functions");
36 class AnalyzerStatsChecker
: public Checker
<check::EndAnalysis
> {
38 void checkEndAnalysis(ExplodedGraph
&G
, BugReporter
&B
,ExprEngine
&Eng
) const;
42 void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph
&G
,
44 ExprEngine
&Eng
) const {
45 const CFG
*C
= nullptr;
46 const SourceManager
&SM
= B
.getSourceManager();
47 llvm::SmallPtrSet
<const CFGBlock
*, 32> reachable
;
49 // Root node should have the location context of the top most function.
50 const ExplodedNode
*GraphRoot
= *G
.roots_begin();
51 const LocationContext
*LC
= GraphRoot
->getLocation().getLocationContext();
53 const Decl
*D
= LC
->getDecl();
55 // Iterate over the exploded graph.
56 for (const ExplodedNode
&N
: G
.nodes()) {
57 const ProgramPoint
&P
= N
.getLocation();
59 // Only check the coverage in the top level function (optimization).
60 if (D
!= P
.getLocationContext()->getDecl())
63 if (std::optional
<BlockEntrance
> BE
= P
.getAs
<BlockEntrance
>()) {
64 const CFGBlock
*CB
= BE
->getBlock();
69 // Get the CFG and the Decl of this block.
72 unsigned total
= 0, unreachable
= 0;
74 // Find CFGBlocks that were not covered by any node
75 for (CFG::const_iterator I
= C
->begin(); I
!= C
->end(); ++I
) {
76 const CFGBlock
*CB
= *I
;
78 // Check if the block is unreachable
79 if (!reachable
.count(CB
)) {
84 // We never 'reach' the entry block, so correct the unreachable count
86 // There is no BlockEntrance corresponding to the exit block as well, so
87 // assume it is reached as well.
90 // Generate the warning string
92 llvm::raw_svector_ostream
output(buf
);
93 PresumedLoc Loc
= SM
.getPresumedLoc(D
->getLocation());
97 if (isa
<FunctionDecl
, ObjCMethodDecl
>(D
)) {
98 const NamedDecl
*ND
= cast
<NamedDecl
>(D
);
100 } else if (isa
<BlockDecl
>(D
)) {
101 output
<< "block(line:" << Loc
.getLine() << ":col:" << Loc
.getColumn();
104 NumBlocksUnreachable
+= unreachable
;
106 std::string NameOfRootFunction
= std::string(output
.str());
108 output
<< " -> Total CFGBlocks: " << total
<< " | Unreachable CFGBlocks: "
109 << unreachable
<< " | Exhausted Block: "
110 << (Eng
.wasBlocksExhausted() ? "yes" : "no")
111 << " | Empty WorkList: "
112 << (Eng
.hasEmptyWorkList() ? "yes" : "no");
114 B
.EmitBasicReport(D
, this, "Analyzer Statistics", "Internal Statistics",
115 output
.str(), PathDiagnosticLocation(D
, SM
));
117 // Emit warning for each block we bailed out on.
118 const CoreEngine
&CE
= Eng
.getCoreEngine();
119 for (const BlockEdge
&BE
: make_first_range(CE
.exhausted_blocks())) {
120 const CFGBlock
*Exit
= BE
.getDst();
123 const CFGElement
&CE
= Exit
->front();
124 if (std::optional
<CFGStmt
> CS
= CE
.getAs
<CFGStmt
>()) {
125 SmallString
<128> bufI
;
126 llvm::raw_svector_ostream
outputI(bufI
);
127 outputI
<< "(" << NameOfRootFunction
<< ")" <<
128 ": The analyzer generated a sink at this point";
130 D
, this, "Sink Point", "Internal Statistics", outputI
.str(),
131 PathDiagnosticLocation::createBegin(CS
->getStmt(), SM
, LC
));
136 void ento::registerAnalyzerStatsChecker(CheckerManager
&mgr
) {
137 mgr
.registerChecker
<AnalyzerStatsChecker
>();
140 bool ento::shouldRegisterAnalyzerStatsChecker(const CheckerManager
&mgr
) {