[clang] [analyzer] Introduce per-entry-point statistics (PR #131175)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 13 10:53:11 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Arseniy Zaostrovnykh (necto)
<details>
<summary>Changes</summary>
So far CSA was relying on the LLVM Statistic package that allowed us to gather some data about analysis of an entire translation unit. However, the translation unit consists of a collection of loosely related entry points. Aggregating data across multiple such entry points is often counter productive.
This change introduces a new lightweight always-on facility to collect Boolean or numerical statistics for each entry point and dump them in a CSV format. Such format makes it easy to aggregate data across multiple translation units and analyze it with common data-processing tools.
We break down the existing statistics that were collected on the per-TU basis into values per entry point.
Additionally, we enable the statistics unconditionally (STATISTIC -> ALWAYS_ENABLED_STATISTIC) to facilitate their use (you can gather the data with a simple run-time flag rather than having to recompile the analyzer). These statistics are very light and add virtually no overhead.
@<!-- -->steakhal (Balázs Benics) started this design and I picked over the baton.
---
CPP-6160
---
Patch is 35.92 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/131175.diff
17 Files Affected:
- (modified) clang/docs/analyzer/developer-docs.rst (+1)
- (added) clang/docs/analyzer/developer-docs/Statistics.rst (+21)
- (modified) clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def (+6)
- (added) clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h (+162)
- (modified) clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp (+4-5)
- (modified) clang/lib/StaticAnalyzer/Core/BugReporter.cpp (+14-14)
- (modified) clang/lib/StaticAnalyzer/Core/CMakeLists.txt (+1)
- (modified) clang/lib/StaticAnalyzer/Core/CoreEngine.cpp (+7-9)
- (added) clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp (+201)
- (modified) clang/lib/StaticAnalyzer/Core/ExprEngine.cpp (+13-11)
- (modified) clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp (+7-7)
- (modified) clang/lib/StaticAnalyzer/Core/WorkList.cpp (+5-5)
- (modified) clang/lib/StaticAnalyzer/Core/Z3CrosscheckVisitor.cpp (+16-15)
- (modified) clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp (+50-12)
- (modified) clang/test/Analysis/analyzer-config.c (+1)
- (added) clang/test/Analysis/csv2json.py (+98)
- (modified) clang/test/lit.cfg.py (+10)
``````````diff
diff --git a/clang/docs/analyzer/developer-docs.rst b/clang/docs/analyzer/developer-docs.rst
index 60c0e71ad847c..a925cf7ca02e1 100644
--- a/clang/docs/analyzer/developer-docs.rst
+++ b/clang/docs/analyzer/developer-docs.rst
@@ -12,3 +12,4 @@ Contents:
developer-docs/nullability
developer-docs/RegionStore
developer-docs/PerformanceInvestigation
+ developer-docs/Statistics
diff --git a/clang/docs/analyzer/developer-docs/Statistics.rst b/clang/docs/analyzer/developer-docs/Statistics.rst
new file mode 100644
index 0000000000000..d352bb6f01ebc
--- /dev/null
+++ b/clang/docs/analyzer/developer-docs/Statistics.rst
@@ -0,0 +1,21 @@
+======================
+Metrics and Statistics
+======================
+
+TODO: write this once the design is settled (@reviewer, don't look here yet)
+
+CSA enjoys two facilities to collect statistics per translation unit and per entry point.
+
+Mention the following tools:
+- STATISTIC macro
+- ALLWAYS_ENABLED_STATISTIC macro
+
+- STAT_COUNTER macro
+- STAT_MAX macro
+
+- BoolEPStat
+- UnsignedEPStat
+- CounterEPStat
+- UnsignedMaxEPStat
+
+- dump-se-metrics-to-csv="%t.csv"
diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
index 2aa00db411844..b88bce5e262a7 100644
--- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
+++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
@@ -353,6 +353,12 @@ ANALYZER_OPTION(bool, DisplayCTUProgress, "display-ctu-progress",
"the analyzer's progress related to ctu.",
false)
+ANALYZER_OPTION(
+ StringRef, DumpSEStatsToCSV, "dump-se-stats-to-csv",
+ "If provided, the analyzer will dump statistics per entry point "
+ "into the specified CSV file.",
+ "")
+
ANALYZER_OPTION(bool, ShouldTrackConditions, "track-conditions",
"Whether to track conditions that are a control dependency of "
"an already tracked variable.",
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h
new file mode 100644
index 0000000000000..16c9fdf97fc30
--- /dev/null
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h
@@ -0,0 +1,162 @@
+// EntryPointStats.h - Tracking statistics per entry point -*- C++ -*-//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------===//
+
+#ifndef CLANG_INCLUDE_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ENTRYPOINTSTATS_H
+#define CLANG_INCLUDE_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ENTRYPOINTSTATS_H
+
+#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace llvm {
+class raw_ostream;
+} // namespace llvm
+
+namespace clang {
+class Decl;
+
+namespace ento {
+
+class EntryPointStat {
+public:
+ llvm::StringLiteral name() const { return Name; }
+
+ static void lockRegistry();
+
+ static void takeSnapshot(const Decl *EntryPoint);
+ static void dumpStatsAsCSV(llvm::raw_ostream &OS);
+ static void dumpStatsAsCSV(llvm::StringRef FileName);
+
+protected:
+ explicit EntryPointStat(llvm::StringLiteral Name) : Name{Name} {}
+ EntryPointStat(const EntryPointStat &) = delete;
+ EntryPointStat(EntryPointStat &&) = delete;
+ EntryPointStat &operator=(EntryPointStat &) = delete;
+ EntryPointStat &operator=(EntryPointStat &&) = delete;
+
+private:
+ llvm::StringLiteral Name;
+};
+
+class BoolEPStat : public EntryPointStat {
+ std::optional<bool> Value = {};
+
+public:
+ explicit BoolEPStat(llvm::StringLiteral Name);
+ unsigned value() const { return Value && *Value; }
+ void set(bool V) {
+ assert(!Value.has_value());
+ Value = V;
+ }
+ void reset() { Value = {}; }
+};
+
+// used by CounterEntryPointTranslationUnitStat
+class CounterEPStat : public EntryPointStat {
+ using EntryPointStat::EntryPointStat;
+ unsigned Value = {};
+
+public:
+ explicit CounterEPStat(llvm::StringLiteral Name);
+ unsigned value() const { return Value; }
+ void reset() { Value = {}; }
+ CounterEPStat &operator++() {
+ ++Value;
+ return *this;
+ }
+
+ CounterEPStat &operator++(int) {
+ // No difference as you can't extract the value
+ return ++(*this);
+ }
+
+ CounterEPStat &operator+=(unsigned Inc) {
+ Value += Inc;
+ return *this;
+ }
+};
+
+// used by UnsignedMaxEtryPointTranslationUnitStatistic
+class UnsignedMaxEPStat : public EntryPointStat {
+ using EntryPointStat::EntryPointStat;
+ unsigned Value = {};
+
+public:
+ explicit UnsignedMaxEPStat(llvm::StringLiteral Name);
+ unsigned value() const { return Value; }
+ void reset() { Value = {}; }
+ void updateMax(unsigned X) { Value = std::max(Value, X); }
+};
+
+class UnsignedEPStat : public EntryPointStat {
+ using EntryPointStat::EntryPointStat;
+ std::optional<unsigned> Value = {};
+
+public:
+ explicit UnsignedEPStat(llvm::StringLiteral Name);
+ unsigned value() const { return Value.value_or(0); }
+ void reset() { Value.reset(); }
+ void set(unsigned V) {
+ assert(!Value.has_value());
+ Value = V;
+ }
+};
+
+class CounterEntryPointTranslationUnitStat {
+ CounterEPStat M;
+ llvm::TrackingStatistic S;
+
+public:
+ CounterEntryPointTranslationUnitStat(const char *DebugType,
+ llvm::StringLiteral Name,
+ llvm::StringLiteral Desc)
+ : M(Name), S(DebugType, Name.data(), Desc.data()) {}
+ CounterEntryPointTranslationUnitStat &operator++() {
+ ++M;
+ ++S;
+ return *this;
+ }
+
+ CounterEntryPointTranslationUnitStat &operator++(int) {
+ // No difference with prefix as the value is not observable.
+ return ++(*this);
+ }
+
+ CounterEntryPointTranslationUnitStat &operator+=(unsigned Inc) {
+ M += Inc;
+ S += Inc;
+ return *this;
+ }
+};
+
+class UnsignedMaxEtryPointTranslationUnitStatistic {
+ UnsignedMaxEPStat M;
+ llvm::TrackingStatistic S;
+
+public:
+ UnsignedMaxEtryPointTranslationUnitStatistic(const char *DebugType,
+ llvm::StringLiteral Name,
+ llvm::StringLiteral Desc)
+ : M(Name), S(DebugType, Name.data(), Desc.data()) {}
+ void updateMax(uint64_t Value) {
+ M.updateMax(static_cast<unsigned>(Value));
+ S.updateMax(Value);
+ }
+};
+
+#define STAT_COUNTER(VARNAME, DESC) \
+ static clang::ento::CounterEntryPointTranslationUnitStat VARNAME = { \
+ DEBUG_TYPE, #VARNAME, DESC}
+
+#define STAT_MAX(VARNAME, DESC) \
+ static clang::ento::UnsignedMaxEtryPointTranslationUnitStatistic VARNAME = { \
+ DEBUG_TYPE, #VARNAME, DESC}
+
+} // namespace ento
+} // namespace clang
+
+#endif // CLANG_INCLUDE_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ENTRYPOINTSTATS_H
diff --git a/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
index a54f1b1e71d47..d030e69a2a6e0 100644
--- a/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
@@ -13,12 +13,12 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/Statistic.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>
@@ -27,10 +27,9 @@ using namespace ento;
#define DEBUG_TYPE "StatsChecker"
-STATISTIC(NumBlocks,
- "The # of blocks in top level functions");
-STATISTIC(NumBlocksUnreachable,
- "The # of unreachable blocks in analyzing top level functions");
+STAT_COUNTER(NumBlocks, "The # of blocks in top level functions");
+STAT_COUNTER(NumBlocksUnreachable,
+ "The # of unreachable blocks in analyzing top level functions");
namespace {
class AnalyzerStatsChecker : public Checker<check::EndAnalysis> {
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index a4f9e092e8205..5f78fc433275d 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -39,6 +39,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/CheckerRegistryData.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
@@ -54,7 +55,6 @@
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
@@ -82,19 +82,19 @@ using namespace llvm;
#define DEBUG_TYPE "BugReporter"
-STATISTIC(MaxBugClassSize,
- "The maximum number of bug reports in the same equivalence class");
-STATISTIC(MaxValidBugClassSize,
- "The maximum number of bug reports in the same equivalence class "
- "where at least one report is valid (not suppressed)");
-
-STATISTIC(NumTimesReportPassesZ3, "Number of reports passed Z3");
-STATISTIC(NumTimesReportRefuted, "Number of reports refuted by Z3");
-STATISTIC(NumTimesReportEQClassAborted,
- "Number of times a report equivalence class was aborted by the Z3 "
- "oracle heuristic");
-STATISTIC(NumTimesReportEQClassWasExhausted,
- "Number of times all reports of an equivalence class was refuted");
+STAT_MAX(MaxBugClassSize,
+ "The maximum number of bug reports in the same equivalence class");
+STAT_MAX(MaxValidBugClassSize,
+ "The maximum number of bug reports in the same equivalence class "
+ "where at least one report is valid (not suppressed)");
+
+STAT_COUNTER(NumTimesReportPassesZ3, "Number of reports passed Z3");
+STAT_COUNTER(NumTimesReportRefuted, "Number of reports refuted by Z3");
+STAT_COUNTER(NumTimesReportEQClassAborted,
+ "Number of times a report equivalence class was aborted by the Z3 "
+ "oracle heuristic");
+STAT_COUNTER(NumTimesReportEQClassWasExhausted,
+ "Number of times all reports of an equivalence class was refuted");
BugReporterVisitor::~BugReporterVisitor() = default;
diff --git a/clang/lib/StaticAnalyzer/Core/CMakeLists.txt b/clang/lib/StaticAnalyzer/Core/CMakeLists.txt
index fb9394a519eb7..d0a9b202f9c52 100644
--- a/clang/lib/StaticAnalyzer/Core/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Core/CMakeLists.txt
@@ -24,6 +24,7 @@ add_clang_library(clangStaticAnalyzerCore
CoreEngine.cpp
DynamicExtent.cpp
DynamicType.cpp
+ EntryPointStats.cpp
Environment.cpp
ExplodedGraph.cpp
ExprEngine.cpp
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index d96211c3a6635..5c05c9c87f124 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -22,12 +22,12 @@
#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
@@ -43,14 +43,12 @@ using namespace ento;
#define DEBUG_TYPE "CoreEngine"
-STATISTIC(NumSteps,
- "The # of steps executed.");
-STATISTIC(NumSTUSteps, "The # of STU steps executed.");
-STATISTIC(NumCTUSteps, "The # of CTU steps executed.");
-STATISTIC(NumReachedMaxSteps,
- "The # of times we reached the max number of steps.");
-STATISTIC(NumPathsExplored,
- "The # of paths explored by the analyzer.");
+STAT_COUNTER(NumSteps, "The # of steps executed.");
+STAT_COUNTER(NumSTUSteps, "The # of STU steps executed.");
+STAT_COUNTER(NumCTUSteps, "The # of CTU steps executed.");
+ALWAYS_ENABLED_STATISTIC(NumReachedMaxSteps,
+ "The # of times we reached the max number of steps.");
+STAT_COUNTER(NumPathsExplored, "The # of paths explored by the analyzer.");
//===----------------------------------------------------------------------===//
// Core analysis engine.
diff --git a/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp b/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
new file mode 100644
index 0000000000000..f17d0522f983a
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
@@ -0,0 +1,201 @@
+//===- EntryPointStats.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/raw_ostream.h"
+#include <iterator>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+struct Registry {
+ std::vector<BoolEPStat *> BoolStats;
+ std::vector<CounterEPStat *> CounterStats;
+ std::vector<UnsignedMaxEPStat *> UnsignedMaxStats;
+ std::vector<UnsignedEPStat *> UnsignedStats;
+
+ bool IsLocked = false;
+
+ struct Snapshot {
+ const Decl *EntryPoint;
+ std::vector<bool> BoolStatValues;
+ std::vector<unsigned> UnsignedStatValues;
+
+ void dumpDynamicStatsAsCSV(llvm::raw_ostream &OS) const;
+ };
+
+ std::vector<Snapshot> Snapshots;
+};
+} // namespace
+
+static llvm::ManagedStatic<Registry> StatsRegistry;
+
+namespace {
+template <typename Callback> void enumerateStatVectors(const Callback &Fn) {
+ Fn(StatsRegistry->BoolStats);
+ Fn(StatsRegistry->CounterStats);
+ Fn(StatsRegistry->UnsignedMaxStats);
+ Fn(StatsRegistry->UnsignedStats);
+}
+} // namespace
+
+static void checkStatName(const EntryPointStat *M) {
+#ifdef NDEBUG
+ return;
+#endif // NDEBUG
+ constexpr std::array AllowedSpecialChars = {
+ '+', '-', '_', '=', ':', '(', ')', '@', '!', '~',
+ '$', '%', '^', '&', '*', '\'', ';', '<', '>', '/'};
+ for (unsigned char C : M->name()) {
+ if (!std::isalnum(C) && !llvm::is_contained(AllowedSpecialChars, C)) {
+ llvm::errs() << "Stat name \"" << M->name() << "\" contains character '"
+ << C << "' (" << static_cast<int>(C)
+ << ") that is not allowed.";
+ assert(false && "The Stat name contains unallowed character");
+ }
+ }
+}
+
+void EntryPointStat::lockRegistry() {
+ auto CmpByNames = [](const EntryPointStat *L, const EntryPointStat *R) {
+ return L->name() < R->name();
+ };
+ enumerateStatVectors(
+ [CmpByNames](auto &Stats) { llvm::sort(Stats, CmpByNames); });
+ enumerateStatVectors(
+ [](const auto &Stats) { llvm::for_each(Stats, checkStatName); });
+ StatsRegistry->IsLocked = true;
+}
+
+static bool isRegistered(llvm::StringLiteral Name) {
+ auto ByName = [Name](const EntryPointStat *M) { return M->name() == Name; };
+ bool Result = false;
+ enumerateStatVectors([ByName, &Result](const auto &Stats) {
+ Result = Result || llvm::any_of(Stats, ByName);
+ });
+ return Result;
+}
+
+BoolEPStat::BoolEPStat(llvm::StringLiteral Name) : EntryPointStat(Name) {
+ assert(!StatsRegistry->IsLocked);
+ assert(!isRegistered(Name));
+ StatsRegistry->BoolStats.push_back(this);
+}
+
+CounterEPStat::CounterEPStat(llvm::StringLiteral Name) : EntryPointStat(Name) {
+ assert(!StatsRegistry->IsLocked);
+ assert(!isRegistered(Name));
+ StatsRegistry->CounterStats.push_back(this);
+}
+
+UnsignedMaxEPStat::UnsignedMaxEPStat(llvm::StringLiteral Name)
+ : EntryPointStat(Name) {
+ assert(!StatsRegistry->IsLocked);
+ assert(!isRegistered(Name));
+ StatsRegistry->UnsignedMaxStats.push_back(this);
+}
+
+UnsignedEPStat::UnsignedEPStat(llvm::StringLiteral Name)
+ : EntryPointStat(Name) {
+ assert(!StatsRegistry->IsLocked);
+ assert(!isRegistered(Name));
+ StatsRegistry->UnsignedStats.push_back(this);
+}
+
+static std::vector<unsigned> consumeUnsignedStats() {
+ std::vector<unsigned> Result;
+ Result.reserve(StatsRegistry->CounterStats.size() +
+ StatsRegistry->UnsignedMaxStats.size() +
+ StatsRegistry->UnsignedStats.size());
+ for (auto *M : StatsRegistry->CounterStats) {
+ Result.push_back(M->value());
+ M->reset();
+ }
+ for (auto *M : StatsRegistry->UnsignedMaxStats) {
+ Result.push_back(M->value());
+ M->reset();
+ }
+ for (auto *M : StatsRegistry->UnsignedStats) {
+ Result.push_back(M->value());
+ M->reset();
+ }
+ return Result;
+}
+
+static std::vector<llvm::StringLiteral> getStatNames() {
+ std::vector<llvm::StringLiteral> Ret;
+ auto GetName = [](const EntryPointStat *M) { return M->name(); };
+ enumerateStatVectors([GetName, &Ret](const auto &Stats) {
+ transform(Stats, std::back_inserter(Ret), GetName);
+ });
+ return Ret;
+}
+
+void Registry::Snapshot::dumpDynamicStatsAsCSV(llvm::raw_ostream &OS) const {
+ OS << '"';
+ llvm::printEscapedString(
+ clang::AnalysisDeclContext::getFunctionName(EntryPoint), OS);
+ OS << "\", ";
+ auto PrintAsBool = [&OS](bool B) { OS << (B ? "true" : "false"); };
+ llvm::interleaveComma(BoolStatValues, OS, PrintAsBool);
+ OS << ((BoolStatValues.empty() || UnsignedStatValues.empty()) ? "" : ", ");
+ llvm::interleaveComma(UnsignedStatValues, OS);
+}
+
+static std::vector<bool> consumeBoolStats() {
+ std::vector<bool> Result;
+ Result.reserve(StatsRegistry->BoolStats.size());
+ for (auto *M : StatsRegistry->BoolStats) {
+ Result.push_back(M->value());
+ M->reset();
+ }
+ return Result;
+}
+
+void EntryPointStat::takeSnapshot(const Decl *EntryPoint) {
+ auto BoolValues = consumeBoolStats();
+ auto UnsignedValues = consumeUnsignedStats();
+ StatsRegistry->Snapshots.push_back(
+ {EntryPoint, std::move(BoolValues), std::move(UnsignedValues)});
+}
+
+void EntryPointStat::dumpStatsAsCSV(llvm::StringRef FileName) {
+ std::error_code EC;
+ llvm::raw_fd_ostream File(FileName, EC, llvm::sys::fs::OF_Text);
+ if (EC)
+ return;
+ dumpStatsAsCSV(File);
+}
+
+void EntryPointStat::dumpStatsAsCSV(llvm::raw_ostream &OS) {
+ OS << "EntryPoint, ";
+ llvm::interleaveComma(getStatNames(), OS);
+ OS << "\n";
+
+ std::vector<std::string> Rows;
+ Rows.reserve(StatsRegistry->Snapshots.size());
+ for (const auto &Snapshot : StatsRegistry->Snapshots) {
+ std::string Row;
+ llvm::raw_string_ostream RowOs(Row);
+ Snapshot.dumpDynamicStatsAsCSV(RowOs);
+ RowOs << "\n";
+ Rows.push_back(RowOs.str());
+ }
+ llvm::sort(Rows);
+ for (const auto &Row : Rows) {
+ OS << Row;
+ }
+}
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 914eb0f4ef6bd..12a5b248c843f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -49,6 +49,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
#include "clang/StaticAnalyz...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/131175
More information about the cfe-commits
mailing list