[llvm] [Support] Introduce a function to disable statistics and ensure statistics are properly re-registered when statistics are re-enabled (PR #195832)

Benjamin Stott via llvm-commits llvm-commits at lists.llvm.org
Tue May 5 04:06:37 PDT 2026


https://github.com/BStott6 created https://github.com/llvm/llvm-project/pull/195832

For [daemonizing the regression tests](https://discourse.llvm.org/t/rfc-reducing-process-creation-overhead-in-llvm-regression-tests/88612/9) we need to be able to disable statistics so that if a tool is invoked with statistics enabled, all in the same process, and then with them disabled, statistics are not enabled in the second run.

Before this PR, the `StatisticInfo` only stores a vector of statistics that are enabled. The first time a statistic is bumped, it calls `init`, which adds itself to the `Stats` vector in `StatisticInfo` _only if_ statistics are enabled. This causes a problem for our purposes: `StatisticInfo` iterates the `Stats` vector to reset statistics by zeroing the counter and setting `Initialized` back to false, in order to get the statistic to re-register itself. The problem is that the statistics are only added to the Stats vector if statistics were enabled: if a statistic is bumped while statistics are disabled, it will mark itself as `Initialized` but not add itself to the `Stats` vector, so its `Initialized` flag is never set back to `true`, and the statistic never re-activates. To fix this, I introduce a separate `AllStats` vector, which tracks every statistic that has ever been registered, and rename the `Stats` vector to `EnabledStats` for disambiguation. `AllStats` is iterated instead of `EnabledStats` to make sure every statistic has `Initialized` set back to false, not just ones that are enabled. `EnabledStats` is used in every other case.

I've also made it so statistics are implicitly reset whenever statistics are enabled or disabled, as I expect that this would be the expected behaviour by consumers of the API. Specifically, statistics are reset when disabled so that `getStatistics()` becomes empty as expected, and reset when re-enabled so that if a statistic was bumped while disabled, the counter is reset when statistics are re-enabled, as I don't think people would expect that bumping the statistic while statistics are disabled would carry over to the next time statistics are enabled.

I've also extended the unit test to check that the disabling and re-enabling works as expected.



>From 317bc324657e13fa36182e15e38f15364e651455 Mon Sep 17 00:00:00 2001
From: BStott <Benjamin.Stott at sony.com>
Date: Fri, 1 May 2026 16:06:45 +0100
Subject: [PATCH] Introduce a function to disable statistics and ensure
 statistics are properly re-registered when statistics are re-enabled

---
 llvm/include/llvm/ADT/Statistic.h    | 12 +++++-
 llvm/lib/Support/Statistic.cpp       | 61 +++++++++++++++++++---------
 llvm/unittests/ADT/StatisticTest.cpp | 36 ++++++++++++++++
 3 files changed, 87 insertions(+), 22 deletions(-)

diff --git a/llvm/include/llvm/ADT/Statistic.h b/llvm/include/llvm/ADT/Statistic.h
index 75d608beb0134..8a611410f0a2a 100644
--- a/llvm/include/llvm/ADT/Statistic.h
+++ b/llvm/include/llvm/ADT/Statistic.h
@@ -55,11 +55,12 @@ class TrackingStatistic {
 
   std::atomic<uint64_t> Value;
   std::atomic<bool> Initialized;
+  std::atomic<bool> Registered;
 
   constexpr TrackingStatistic(const char *DebugType, const char *Name,
                               const char *Desc)
       : DebugType(DebugType), Name(Name), Desc(Desc), Value(0),
-        Initialized(false) {}
+        Initialized(false), Registered(false) {}
 
   const char *getDebugType() const { return DebugType; }
   const char *getName() const { return Name; }
@@ -177,9 +178,14 @@ using Statistic = NoopStatistic;
 #define ALWAYS_ENABLED_STATISTIC(VARNAME, DESC)                                \
   static llvm::TrackingStatistic VARNAME = {DEBUG_TYPE, #VARNAME, DESC}
 
-/// Enable the collection and printing of statistics.
+/// Enable the collection and printing of statistics. If statistics were
+/// previously disabled, statistics are reset.
 LLVM_ABI void EnableStatistics(bool DoPrintOnExit = true);
 
+/// Disable the collection and printing of statistics. If statistics were
+/// previously enabled, statistics are reset.
+LLVM_ABI void DisableStatistics();
+
 /// Check if statistics are enabled.
 LLVM_ABI bool AreStatisticsEnabled();
 
@@ -220,6 +226,8 @@ LLVM_ABI std::vector<std::pair<StringRef, uint64_t>> GetStatistics();
 /// compilation should ensure that no compilations are in progress at the point
 /// this function is called and that only one compilation executes until calling
 /// GetStatistics().
+///
+/// This function is called whenever statistics are enabled and disabled.
 LLVM_ABI void ResetStatistics();
 
 } // end namespace llvm
diff --git a/llvm/lib/Support/Statistic.cpp b/llvm/lib/Support/Statistic.cpp
index 24ef3e9abaebc..bb451ce1e6a17 100644
--- a/llvm/lib/Support/Statistic.cpp
+++ b/llvm/lib/Support/Statistic.cpp
@@ -64,7 +64,10 @@ namespace {
 /// This class is also used to look up statistic values from applications that
 /// use LLVM.
 class StatisticInfo {
-  std::vector<TrackingStatistic *> Stats;
+  /// Currently enabled statistics.
+  std::vector<TrackingStatistic *> EnabledStats;
+  /// All statistics that have ever been registered.
+  std::vector<TrackingStatistic *> AllStats;
 
   friend void llvm::PrintStatistics();
   friend void llvm::PrintStatistics(raw_ostream &OS);
@@ -78,10 +81,11 @@ class StatisticInfo {
   StatisticInfo();
   ~StatisticInfo();
 
-  void addStatistic(TrackingStatistic *S) { Stats.push_back(S); }
+  void registerStatistic(TrackingStatistic *S) { AllStats.push_back(S); }
+  void enableStatistic(TrackingStatistic *S) { EnabledStats.push_back(S); }
 
-  const_iterator begin() const { return Stats.begin(); }
-  const_iterator end() const { return Stats.end(); }
+  const_iterator begin() const { return EnabledStats.begin(); }
+  const_iterator end() const { return EnabledStats.end(); }
   iterator_range<const_iterator> statistics() const {
     return {begin(), end()};
   }
@@ -111,8 +115,15 @@ void TrackingStatistic::RegisterStatistic() {
     // Check Initialized again after acquiring the lock.
     if (Initialized.load(std::memory_order_relaxed))
       return;
+    if (!Registered) {
+      // registerStatistic must be called once for each statistic, regardless
+      // of whether statistics are enabled. Even if statistics are disabled now,
+      // they may be enabled later.
+      SI.registerStatistic(this);
+      Registered = true;
+    }
     if (EnableStats || Enabled)
-      SI.addStatistic(this);
+      SI.enableStatistic(this);
 
     // Remember we have been registered.
     Initialized.store(true, std::memory_order_release);
@@ -132,23 +143,32 @@ StatisticInfo::~StatisticInfo() {
 }
 
 void llvm::EnableStatistics(bool DoPrintOnExit) {
+  if (!AreStatisticsEnabled())
+    ResetStatistics();
   Enabled = true;
   PrintOnExit = DoPrintOnExit;
 }
 
+void llvm::DisableStatistics() {
+  if (AreStatisticsEnabled())
+    ResetStatistics();
+  Enabled = false;
+  PrintOnExit = false;
+}
+
 bool llvm::AreStatisticsEnabled() { return Enabled || EnableStats; }
 
 void StatisticInfo::sort() {
-  llvm::stable_sort(
-      Stats, [](const TrackingStatistic *LHS, const TrackingStatistic *RHS) {
-        if (int Cmp = std::strcmp(LHS->getDebugType(), RHS->getDebugType()))
-          return Cmp < 0;
+  llvm::stable_sort(EnabledStats, [](const TrackingStatistic *LHS,
+                                     const TrackingStatistic *RHS) {
+    if (int Cmp = std::strcmp(LHS->getDebugType(), RHS->getDebugType()))
+      return Cmp < 0;
 
-        if (int Cmp = std::strcmp(LHS->getName(), RHS->getName()))
-          return Cmp < 0;
+    if (int Cmp = std::strcmp(LHS->getName(), RHS->getName()))
+      return Cmp < 0;
 
-        return std::strcmp(LHS->getDesc(), RHS->getDesc()) < 0;
-      });
+    return std::strcmp(LHS->getDesc(), RHS->getDesc()) < 0;
+  });
 }
 
 void StatisticInfo::reset() {
@@ -158,19 +178,19 @@ void StatisticInfo::reset() {
   // again. We're holding the lock so it won't be able to do so until we're
   // finished. Once we've forced it to re-register (after we return), then zero
   // the value.
-  for (auto *Stat : Stats) {
+  for (auto *Stat : AllStats) {
     // Value updates to a statistic that complete before this statement in the
     // iteration for that statistic will be lost as intended.
     Stat->Initialized = false;
     Stat->Value = 0;
   }
 
-  // Clear the registration list and release the lock once we're done. Any
+  // Clear the enabled statistic list and release the lock once we're done. Any
   // pending updates from other threads will safely take effect after we return.
   // That might not be what the user wants if they're measuring a compilation
   // but it's their responsibility to prevent concurrent compilations to make
   // a single compilation measurable.
-  Stats.clear();
+  EnabledStats.clear();
 }
 
 void llvm::PrintStatistics(raw_ostream &OS) {
@@ -178,7 +198,7 @@ void llvm::PrintStatistics(raw_ostream &OS) {
 
   // Figure out how long the biggest Value and Name fields are.
   unsigned MaxDebugTypeLen = 0, MaxValLen = 0;
-  for (TrackingStatistic *Stat : Stats.Stats) {
+  for (TrackingStatistic *Stat : Stats.EnabledStats) {
     MaxValLen = std::max(MaxValLen, (unsigned)utostr(Stat->getValue()).size());
     MaxDebugTypeLen =
         std::max(MaxDebugTypeLen, (unsigned)std::strlen(Stat->getDebugType()));
@@ -192,7 +212,7 @@ void llvm::PrintStatistics(raw_ostream &OS) {
      << "===" << std::string(73, '-') << "===\n\n";
 
   // Print all of the statistics.
-  for (TrackingStatistic *Stat : Stats.Stats)
+  for (TrackingStatistic *Stat : Stats.EnabledStats)
     OS << format("%*" PRIu64 " %-*s - %s\n", MaxValLen, Stat->getValue(),
                  MaxDebugTypeLen, Stat->getDebugType(), Stat->getDesc());
 
@@ -209,7 +229,7 @@ void llvm::PrintStatisticsJSON(raw_ostream &OS) {
   // Print all of the statistics.
   OS << "{\n";
   const char *delim = "";
-  for (const TrackingStatistic *Stat : Stats.Stats) {
+  for (const TrackingStatistic *Stat : Stats.EnabledStats) {
     OS << delim;
     assert(yaml::needsQuotes(Stat->getDebugType()) == yaml::QuotingType::None &&
            "Statistic group/type name is simple.");
@@ -232,7 +252,8 @@ void llvm::PrintStatistics() {
   StatisticInfo &Stats = *StatInfo;
 
   // Statistics not enabled?
-  if (Stats.Stats.empty()) return;
+  if (Stats.EnabledStats.empty())
+    return;
 
   // Get the stream to write to.
   std::unique_ptr<raw_ostream> OutStream = CreateInfoOutputFile();
diff --git a/llvm/unittests/ADT/StatisticTest.cpp b/llvm/unittests/ADT/StatisticTest.cpp
index 91bc7f24c7f2f..c78b2d0b66f6e 100644
--- a/llvm/unittests/ADT/StatisticTest.cpp
+++ b/llvm/unittests/ADT/StatisticTest.cpp
@@ -162,6 +162,42 @@ TEST(StatisticTest, API) {
     EXPECT_EQ(S2->first, "Counter2");
     EXPECT_EQ(S2->second, 1u);
   }
+
+  // Check that disabling the statistics works correctly.
+  DisableStatistics();
+  EXPECT_FALSE(AreStatisticsEnabled());
+
+  // Incrementing should do nothing.
+  Counter++;
+  Counter2++;
+  EXPECT_TRUE(GetStatistics().empty());
+
+  // Check that they successfully re-register and count after re-enabling.
+  EnableStatistics();
+
+  Counter++;
+  Counter2++;
+
+  {
+    auto Range = GetStatistics();
+    EXPECT_EQ(Range.begin() + 2, Range.end());
+    EXPECT_EQ(Counter, 1u);
+    EXPECT_EQ(Counter2, 1u);
+
+    OptionalStatistic S1;
+    OptionalStatistic S2;
+    extractCounters(Range, S1, S2);
+
+    EXPECT_EQ(S1.has_value(), true);
+    EXPECT_EQ(S2.has_value(), true);
+
+    EXPECT_EQ(S1->first, "Counter");
+    EXPECT_EQ(S1->second, 1u);
+
+    EXPECT_EQ(S2->first, "Counter2");
+    EXPECT_EQ(S2->second, 1u);
+  }
+
 #else
   // No need to test the output ResetStatistics(), there's nothing to reset so
   // we can't tell if it failed anyway.



More information about the llvm-commits mailing list