139 lines · cpp
1//==--AnalyzerStatsChecker.cpp - Analyzer visitation statistics --*- C++ -*-==//2//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-exception6//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/EntryPointStats.h"17#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"18#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"19#include "llvm/ADT/STLExtras.h"20#include "llvm/ADT/SmallPtrSet.h"21#include "llvm/Support/raw_ostream.h"22#include <optional>23 24using namespace clang;25using namespace ento;26 27#define DEBUG_TYPE "StatsChecker"28 29STAT_COUNTER(NumBlocks, "The # of blocks in top level functions");30STAT_COUNTER(NumBlocksUnreachable,31 "The # of unreachable blocks in analyzing top level functions");32 33namespace {34class AnalyzerStatsChecker : public Checker<check::EndAnalysis> {35public:36 void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,ExprEngine &Eng) const;37};38}39 40void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,41 BugReporter &B,42 ExprEngine &Eng) const {43 const CFG *C = nullptr;44 const SourceManager &SM = B.getSourceManager();45 llvm::SmallPtrSet<const CFGBlock*, 32> reachable;46 47 const LocationContext *LC = Eng.getRootLocationContext();48 49 const Decl *D = LC->getDecl();50 51 // Iterate over the exploded graph.52 for (const ExplodedNode &N : G.nodes()) {53 const ProgramPoint &P = N.getLocation();54 55 // Only check the coverage in the top level function (optimization).56 if (D != P.getLocationContext()->getDecl())57 continue;58 59 if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {60 const CFGBlock *CB = BE->getBlock();61 reachable.insert(CB);62 }63 }64 65 // Get the CFG and the Decl of this block.66 C = LC->getCFG();67 68 unsigned total = 0, unreachable = 0;69 70 // Find CFGBlocks that were not covered by any node71 for (CFG::const_iterator I = C->begin(); I != C->end(); ++I) {72 const CFGBlock *CB = *I;73 ++total;74 // Check if the block is unreachable75 if (!reachable.count(CB)) {76 ++unreachable;77 }78 }79 80 // We never 'reach' the entry block, so correct the unreachable count81 unreachable--;82 // There is no BlockEntrance corresponding to the exit block as well, so83 // assume it is reached as well.84 unreachable--;85 86 // Generate the warning string87 SmallString<128> buf;88 llvm::raw_svector_ostream output(buf);89 PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());90 if (!Loc.isValid())91 return;92 93 if (isa<FunctionDecl, ObjCMethodDecl>(D)) {94 const NamedDecl *ND = cast<NamedDecl>(D);95 output << *ND;96 } else if (isa<BlockDecl>(D)) {97 output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn();98 }99 100 NumBlocksUnreachable += unreachable;101 NumBlocks += total;102 std::string NameOfRootFunction = std::string(output.str());103 104 output << " -> Total CFGBlocks: " << total << " | Unreachable CFGBlocks: "105 << unreachable << " | Exhausted Block: "106 << (Eng.wasBlocksExhausted() ? "yes" : "no")107 << " | Empty WorkList: "108 << (Eng.hasEmptyWorkList() ? "yes" : "no");109 110 B.EmitBasicReport(D, this, "Analyzer Statistics", "Internal Statistics",111 output.str(), PathDiagnosticLocation(D, SM));112 113 // Emit warning for each block we bailed out on.114 const CoreEngine &CE = Eng.getCoreEngine();115 for (const BlockEdge &BE : make_first_range(CE.exhausted_blocks())) {116 const CFGBlock *Exit = BE.getDst();117 if (Exit->empty())118 continue;119 const CFGElement &CE = Exit->front();120 if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {121 SmallString<128> bufI;122 llvm::raw_svector_ostream outputI(bufI);123 outputI << "(" << NameOfRootFunction << ")" <<124 ": The analyzer generated a sink at this point";125 B.EmitBasicReport(126 D, this, "Sink Point", "Internal Statistics", outputI.str(),127 PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC));128 }129 }130}131 132void ento::registerAnalyzerStatsChecker(CheckerManager &mgr) {133 mgr.registerChecker<AnalyzerStatsChecker>();134}135 136bool ento::shouldRegisterAnalyzerStatsChecker(const CheckerManager &mgr) {137 return true;138}139