xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
10b57cec5SDimitry Andric //==--AnalyzerStatsChecker.cpp - Analyzer visitation statistics --*- C++ -*-==//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric // This file reports various statistics about analyzer visitation.
90b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
100b57cec5SDimitry Andric #include "clang/AST/DeclObjC.h"
110b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h"
12*06c3fb27SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
130b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
18*06c3fb27SDimitry Andric #include "llvm/ADT/STLExtras.h"
190b57cec5SDimitry Andric #include "llvm/ADT/SmallPtrSet.h"
200b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
210b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h"
220b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
23bdd1243dSDimitry Andric #include <optional>
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric using namespace clang;
260b57cec5SDimitry Andric using namespace ento;
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric #define DEBUG_TYPE "StatsChecker"
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric STATISTIC(NumBlocks,
310b57cec5SDimitry Andric           "The # of blocks in top level functions");
320b57cec5SDimitry Andric STATISTIC(NumBlocksUnreachable,
330b57cec5SDimitry Andric           "The # of unreachable blocks in analyzing top level functions");
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric namespace {
360b57cec5SDimitry Andric class AnalyzerStatsChecker : public Checker<check::EndAnalysis> {
370b57cec5SDimitry Andric public:
380b57cec5SDimitry Andric   void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,ExprEngine &Eng) const;
390b57cec5SDimitry Andric };
400b57cec5SDimitry Andric }
410b57cec5SDimitry Andric 
checkEndAnalysis(ExplodedGraph & G,BugReporter & B,ExprEngine & Eng) const420b57cec5SDimitry Andric void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
430b57cec5SDimitry Andric                                             BugReporter &B,
440b57cec5SDimitry Andric                                             ExprEngine &Eng) const {
450b57cec5SDimitry Andric   const CFG *C = nullptr;
460b57cec5SDimitry Andric   const SourceManager &SM = B.getSourceManager();
470b57cec5SDimitry Andric   llvm::SmallPtrSet<const CFGBlock*, 32> reachable;
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric   // Root node should have the location context of the top most function.
500b57cec5SDimitry Andric   const ExplodedNode *GraphRoot = *G.roots_begin();
510b57cec5SDimitry Andric   const LocationContext *LC = GraphRoot->getLocation().getLocationContext();
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric   const Decl *D = LC->getDecl();
540b57cec5SDimitry Andric 
550b57cec5SDimitry Andric   // Iterate over the exploded graph.
56*06c3fb27SDimitry Andric   for (const ExplodedNode &N : G.nodes()) {
57*06c3fb27SDimitry Andric     const ProgramPoint &P = N.getLocation();
580b57cec5SDimitry Andric 
590b57cec5SDimitry Andric     // Only check the coverage in the top level function (optimization).
600b57cec5SDimitry Andric     if (D != P.getLocationContext()->getDecl())
610b57cec5SDimitry Andric       continue;
620b57cec5SDimitry Andric 
63bdd1243dSDimitry Andric     if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
640b57cec5SDimitry Andric       const CFGBlock *CB = BE->getBlock();
650b57cec5SDimitry Andric       reachable.insert(CB);
660b57cec5SDimitry Andric     }
670b57cec5SDimitry Andric   }
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric   // Get the CFG and the Decl of this block.
700b57cec5SDimitry Andric   C = LC->getCFG();
710b57cec5SDimitry Andric 
720b57cec5SDimitry Andric   unsigned total = 0, unreachable = 0;
730b57cec5SDimitry Andric 
740b57cec5SDimitry Andric   // Find CFGBlocks that were not covered by any node
750b57cec5SDimitry Andric   for (CFG::const_iterator I = C->begin(); I != C->end(); ++I) {
760b57cec5SDimitry Andric     const CFGBlock *CB = *I;
770b57cec5SDimitry Andric     ++total;
780b57cec5SDimitry Andric     // Check if the block is unreachable
790b57cec5SDimitry Andric     if (!reachable.count(CB)) {
800b57cec5SDimitry Andric       ++unreachable;
810b57cec5SDimitry Andric     }
820b57cec5SDimitry Andric   }
830b57cec5SDimitry Andric 
840b57cec5SDimitry Andric   // We never 'reach' the entry block, so correct the unreachable count
850b57cec5SDimitry Andric   unreachable--;
860b57cec5SDimitry Andric   // There is no BlockEntrance corresponding to the exit block as well, so
870b57cec5SDimitry Andric   // assume it is reached as well.
880b57cec5SDimitry Andric   unreachable--;
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric   // Generate the warning string
910b57cec5SDimitry Andric   SmallString<128> buf;
920b57cec5SDimitry Andric   llvm::raw_svector_ostream output(buf);
930b57cec5SDimitry Andric   PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
940b57cec5SDimitry Andric   if (!Loc.isValid())
950b57cec5SDimitry Andric     return;
960b57cec5SDimitry Andric 
97349cc55cSDimitry Andric   if (isa<FunctionDecl, ObjCMethodDecl>(D)) {
980b57cec5SDimitry Andric     const NamedDecl *ND = cast<NamedDecl>(D);
990b57cec5SDimitry Andric     output << *ND;
100349cc55cSDimitry Andric   } else if (isa<BlockDecl>(D)) {
1010b57cec5SDimitry Andric     output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn();
1020b57cec5SDimitry Andric   }
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric   NumBlocksUnreachable += unreachable;
1050b57cec5SDimitry Andric   NumBlocks += total;
1065ffd83dbSDimitry Andric   std::string NameOfRootFunction = std::string(output.str());
1070b57cec5SDimitry Andric 
1080b57cec5SDimitry Andric   output << " -> Total CFGBlocks: " << total << " | Unreachable CFGBlocks: "
1090b57cec5SDimitry Andric       << unreachable << " | Exhausted Block: "
1100b57cec5SDimitry Andric       << (Eng.wasBlocksExhausted() ? "yes" : "no")
1110b57cec5SDimitry Andric       << " | Empty WorkList: "
1120b57cec5SDimitry Andric       << (Eng.hasEmptyWorkList() ? "yes" : "no");
1130b57cec5SDimitry Andric 
1140b57cec5SDimitry Andric   B.EmitBasicReport(D, this, "Analyzer Statistics", "Internal Statistics",
1150b57cec5SDimitry Andric                     output.str(), PathDiagnosticLocation(D, SM));
1160b57cec5SDimitry Andric 
1170b57cec5SDimitry Andric   // Emit warning for each block we bailed out on.
1180b57cec5SDimitry Andric   const CoreEngine &CE = Eng.getCoreEngine();
119*06c3fb27SDimitry Andric   for (const BlockEdge &BE : make_first_range(CE.exhausted_blocks())) {
1200b57cec5SDimitry Andric     const CFGBlock *Exit = BE.getDst();
1210b57cec5SDimitry Andric     if (Exit->empty())
1220b57cec5SDimitry Andric       continue;
1230b57cec5SDimitry Andric     const CFGElement &CE = Exit->front();
124bdd1243dSDimitry Andric     if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
1250b57cec5SDimitry Andric       SmallString<128> bufI;
1260b57cec5SDimitry Andric       llvm::raw_svector_ostream outputI(bufI);
1270b57cec5SDimitry Andric       outputI << "(" << NameOfRootFunction << ")" <<
1280b57cec5SDimitry Andric                  ": The analyzer generated a sink at this point";
1290b57cec5SDimitry Andric       B.EmitBasicReport(
1300b57cec5SDimitry Andric           D, this, "Sink Point", "Internal Statistics", outputI.str(),
1310b57cec5SDimitry Andric           PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC));
1320b57cec5SDimitry Andric     }
1330b57cec5SDimitry Andric   }
1340b57cec5SDimitry Andric }
1350b57cec5SDimitry Andric 
registerAnalyzerStatsChecker(CheckerManager & mgr)1360b57cec5SDimitry Andric void ento::registerAnalyzerStatsChecker(CheckerManager &mgr) {
1370b57cec5SDimitry Andric   mgr.registerChecker<AnalyzerStatsChecker>();
1380b57cec5SDimitry Andric }
1390b57cec5SDimitry Andric 
shouldRegisterAnalyzerStatsChecker(const CheckerManager & mgr)1405ffd83dbSDimitry Andric bool ento::shouldRegisterAnalyzerStatsChecker(const CheckerManager &mgr) {
1410b57cec5SDimitry Andric   return true;
1420b57cec5SDimitry Andric }
143