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