1e5dd7070Spatrick //==--AnalyzerStatsChecker.cpp - Analyzer visitation statistics --*- C++ -*-==//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick // This file reports various statistics about analyzer visitation.
9e5dd7070Spatrick //===----------------------------------------------------------------------===//
10e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
11e5dd7070Spatrick #include "clang/AST/DeclObjC.h"
12e5dd7070Spatrick #include "clang/Basic/SourceManager.h"
13e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
14e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
15e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h"
16e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
17e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
18e5dd7070Spatrick #include "llvm/ADT/SmallPtrSet.h"
19e5dd7070Spatrick #include "llvm/ADT/SmallString.h"
20e5dd7070Spatrick #include "llvm/ADT/Statistic.h"
21e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
22*12c85518Srobert #include <optional>
23e5dd7070Spatrick
24e5dd7070Spatrick using namespace clang;
25e5dd7070Spatrick using namespace ento;
26e5dd7070Spatrick
27e5dd7070Spatrick #define DEBUG_TYPE "StatsChecker"
28e5dd7070Spatrick
29e5dd7070Spatrick STATISTIC(NumBlocks,
30e5dd7070Spatrick "The # of blocks in top level functions");
31e5dd7070Spatrick STATISTIC(NumBlocksUnreachable,
32e5dd7070Spatrick "The # of unreachable blocks in analyzing top level functions");
33e5dd7070Spatrick
34e5dd7070Spatrick namespace {
35e5dd7070Spatrick class AnalyzerStatsChecker : public Checker<check::EndAnalysis> {
36e5dd7070Spatrick public:
37e5dd7070Spatrick void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,ExprEngine &Eng) const;
38e5dd7070Spatrick };
39e5dd7070Spatrick }
40e5dd7070Spatrick
checkEndAnalysis(ExplodedGraph & G,BugReporter & B,ExprEngine & Eng) const41e5dd7070Spatrick void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
42e5dd7070Spatrick BugReporter &B,
43e5dd7070Spatrick ExprEngine &Eng) const {
44e5dd7070Spatrick const CFG *C = nullptr;
45e5dd7070Spatrick const SourceManager &SM = B.getSourceManager();
46e5dd7070Spatrick llvm::SmallPtrSet<const CFGBlock*, 32> reachable;
47e5dd7070Spatrick
48e5dd7070Spatrick // Root node should have the location context of the top most function.
49e5dd7070Spatrick const ExplodedNode *GraphRoot = *G.roots_begin();
50e5dd7070Spatrick const LocationContext *LC = GraphRoot->getLocation().getLocationContext();
51e5dd7070Spatrick
52e5dd7070Spatrick const Decl *D = LC->getDecl();
53e5dd7070Spatrick
54e5dd7070Spatrick // Iterate over the exploded graph.
55e5dd7070Spatrick for (ExplodedGraph::node_iterator I = G.nodes_begin();
56e5dd7070Spatrick I != G.nodes_end(); ++I) {
57e5dd7070Spatrick const ProgramPoint &P = I->getLocation();
58e5dd7070Spatrick
59e5dd7070Spatrick // Only check the coverage in the top level function (optimization).
60e5dd7070Spatrick if (D != P.getLocationContext()->getDecl())
61e5dd7070Spatrick continue;
62e5dd7070Spatrick
63*12c85518Srobert if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
64e5dd7070Spatrick const CFGBlock *CB = BE->getBlock();
65e5dd7070Spatrick reachable.insert(CB);
66e5dd7070Spatrick }
67e5dd7070Spatrick }
68e5dd7070Spatrick
69e5dd7070Spatrick // Get the CFG and the Decl of this block.
70e5dd7070Spatrick C = LC->getCFG();
71e5dd7070Spatrick
72e5dd7070Spatrick unsigned total = 0, unreachable = 0;
73e5dd7070Spatrick
74e5dd7070Spatrick // Find CFGBlocks that were not covered by any node
75e5dd7070Spatrick for (CFG::const_iterator I = C->begin(); I != C->end(); ++I) {
76e5dd7070Spatrick const CFGBlock *CB = *I;
77e5dd7070Spatrick ++total;
78e5dd7070Spatrick // Check if the block is unreachable
79e5dd7070Spatrick if (!reachable.count(CB)) {
80e5dd7070Spatrick ++unreachable;
81e5dd7070Spatrick }
82e5dd7070Spatrick }
83e5dd7070Spatrick
84e5dd7070Spatrick // We never 'reach' the entry block, so correct the unreachable count
85e5dd7070Spatrick unreachable--;
86e5dd7070Spatrick // There is no BlockEntrance corresponding to the exit block as well, so
87e5dd7070Spatrick // assume it is reached as well.
88e5dd7070Spatrick unreachable--;
89e5dd7070Spatrick
90e5dd7070Spatrick // Generate the warning string
91e5dd7070Spatrick SmallString<128> buf;
92e5dd7070Spatrick llvm::raw_svector_ostream output(buf);
93e5dd7070Spatrick PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
94e5dd7070Spatrick if (!Loc.isValid())
95e5dd7070Spatrick return;
96e5dd7070Spatrick
97*12c85518Srobert if (isa<FunctionDecl, ObjCMethodDecl>(D)) {
98e5dd7070Spatrick const NamedDecl *ND = cast<NamedDecl>(D);
99e5dd7070Spatrick output << *ND;
100*12c85518Srobert } else if (isa<BlockDecl>(D)) {
101e5dd7070Spatrick output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn();
102e5dd7070Spatrick }
103e5dd7070Spatrick
104e5dd7070Spatrick NumBlocksUnreachable += unreachable;
105e5dd7070Spatrick NumBlocks += total;
106ec727ea7Spatrick std::string NameOfRootFunction = std::string(output.str());
107e5dd7070Spatrick
108e5dd7070Spatrick output << " -> Total CFGBlocks: " << total << " | Unreachable CFGBlocks: "
109e5dd7070Spatrick << unreachable << " | Exhausted Block: "
110e5dd7070Spatrick << (Eng.wasBlocksExhausted() ? "yes" : "no")
111e5dd7070Spatrick << " | Empty WorkList: "
112e5dd7070Spatrick << (Eng.hasEmptyWorkList() ? "yes" : "no");
113e5dd7070Spatrick
114e5dd7070Spatrick B.EmitBasicReport(D, this, "Analyzer Statistics", "Internal Statistics",
115e5dd7070Spatrick output.str(), PathDiagnosticLocation(D, SM));
116e5dd7070Spatrick
117e5dd7070Spatrick // Emit warning for each block we bailed out on.
118e5dd7070Spatrick typedef CoreEngine::BlocksExhausted::const_iterator ExhaustedIterator;
119e5dd7070Spatrick const CoreEngine &CE = Eng.getCoreEngine();
120e5dd7070Spatrick for (ExhaustedIterator I = CE.blocks_exhausted_begin(),
121e5dd7070Spatrick E = CE.blocks_exhausted_end(); I != E; ++I) {
122e5dd7070Spatrick const BlockEdge &BE = I->first;
123e5dd7070Spatrick const CFGBlock *Exit = BE.getDst();
124e5dd7070Spatrick if (Exit->empty())
125e5dd7070Spatrick continue;
126e5dd7070Spatrick const CFGElement &CE = Exit->front();
127*12c85518Srobert if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
128e5dd7070Spatrick SmallString<128> bufI;
129e5dd7070Spatrick llvm::raw_svector_ostream outputI(bufI);
130e5dd7070Spatrick outputI << "(" << NameOfRootFunction << ")" <<
131e5dd7070Spatrick ": The analyzer generated a sink at this point";
132e5dd7070Spatrick B.EmitBasicReport(
133e5dd7070Spatrick D, this, "Sink Point", "Internal Statistics", outputI.str(),
134e5dd7070Spatrick PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC));
135e5dd7070Spatrick }
136e5dd7070Spatrick }
137e5dd7070Spatrick }
138e5dd7070Spatrick
registerAnalyzerStatsChecker(CheckerManager & mgr)139e5dd7070Spatrick void ento::registerAnalyzerStatsChecker(CheckerManager &mgr) {
140e5dd7070Spatrick mgr.registerChecker<AnalyzerStatsChecker>();
141e5dd7070Spatrick }
142e5dd7070Spatrick
shouldRegisterAnalyzerStatsChecker(const CheckerManager & mgr)143ec727ea7Spatrick bool ento::shouldRegisterAnalyzerStatsChecker(const CheckerManager &mgr) {
144e5dd7070Spatrick return true;
145e5dd7070Spatrick }
146