OSDN Git Service

Make STATISTIC() values available programmatically
authorDaniel Sanders <daniel_l_sanders@apple.com>
Mon, 5 Mar 2018 17:41:45 +0000 (17:41 +0000)
committerDaniel Sanders <daniel_l_sanders@apple.com>
Mon, 5 Mar 2018 17:41:45 +0000 (17:41 +0000)
Summary:
It can be useful for tools to be able to retrieve the values of variables
declared via STATISTIC() directly without having to emit them and parse
them back. Use cases include:
* Needing to report specific statistics to a test harness
* Wanting to post-process statistics. For example, to produce a percentage of
  functions that were fully selected by GlobalISel

Make this possible by adding llvm::GetStatistics() which returns an
iterator_range that can be used to inspect the statistics that have been
touched during execution. When statistics are disabled (NDEBUG and not
LLVM_ENABLE_STATISTICS) this method will return an empty range.

This patch doesn't address the effect of multiple compilations within the same
process. In such situations, the statistics will be cumulative for all
compilations up to the GetStatistics() call.

Reviewers: qcolombet, rtereshin, aditya_nandakumar, bogner

Reviewed By: rtereshin, bogner

Subscribers: llvm-commits, mgorny

Differential Revision: https://reviews.llvm.org/D43901

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@326723 91177308-0d34-0410-b5e6-96231b3b80d8

cmake/modules/HandleLLVMOptions.cmake
include/llvm/ADT/Statistic.h
include/llvm/Config/llvm-config.h.cmake
lib/Support/Statistic.cpp
unittests/ADT/CMakeLists.txt
unittests/ADT/StatisticTest.cpp [new file with mode: 0644]

index 2dc5690..f508db2 100644 (file)
@@ -881,3 +881,7 @@ function(get_compile_definitions)
   set(LLVM_DEFINITIONS "${result}" PARENT_SCOPE)
 endfunction()
 get_compile_definitions()
+
+# The default for LLVM_ENABLE_STATS depends on whether NDEBUG is defined or not.
+# LLVM_ENABLE_ASSERTIONS controls that so re-use it as the default.
+option(LLVM_ENABLE_STATS "Enable statistics collection" ${LLVM_ENABLE_ASSERTIONS})
index 6d478b1..26e6883 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef LLVM_ADT_STATISTIC_H
 #define LLVM_ADT_STATISTIC_H
 
+#include "llvm/Config/llvm-config.h"
 #include "llvm/Support/Compiler.h"
 #include <atomic>
 #include <memory>
@@ -34,6 +35,7 @@ namespace llvm {
 
 class raw_ostream;
 class raw_fd_ostream;
+class StringRef;
 
 class Statistic {
 public:
@@ -60,7 +62,7 @@ public:
   // Allow use of this class as the value itself.
   operator unsigned() const { return getValue(); }
 
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_STATS)
+#if LLVM_ENABLE_STATS
    const Statistic &operator=(unsigned Val) {
     Value.store(Val, std::memory_order_relaxed);
     return init();
@@ -142,7 +144,7 @@ public:
 
   void updateMax(unsigned V) {}
 
-#endif  // !defined(NDEBUG) || defined(LLVM_ENABLE_STATS)
+#endif  // LLVM_ENABLE_STATS
 
 protected:
   Statistic &init() {
@@ -180,6 +182,15 @@ void PrintStatistics(raw_ostream &OS);
 /// PrintStatisticsJSON().
 void PrintStatisticsJSON(raw_ostream &OS);
 
+/// \brief Get the statistics. This can be used to look up the value of
+/// statistics without needing to parse JSON.
+///
+/// This function does not prevent statistics being updated by other threads
+/// during it's execution. It will return the value at the point that it is
+/// read. However, it will prevent new statistics from registering until it
+/// completes.
+const std::vector<std::pair<StringRef, unsigned>> GetStatistics();
+
 } // end namespace llvm
 
 #endif // LLVM_ADT_STATISTIC_H
index 4daa00f..6b1cb0b 100644 (file)
@@ -77,4 +77,9 @@
 /* LLVM version string */
 #define LLVM_VERSION_STRING "${PACKAGE_VERSION}"
 
+/* Whether LLVM records statistics for use with GetStatistics(),
+ * PrintStatistics() or PrintStatisticsJSON()
+ */
+#cmakedefine01 LLVM_ENABLE_STATS
+
 #endif
index 370274d..67b0716 100644 (file)
@@ -52,11 +52,14 @@ static bool Enabled;
 static bool PrintOnExit;
 
 namespace {
-/// StatisticInfo - This class is used in a ManagedStatic so that it is created
-/// on demand (when the first statistic is bumped) and destroyed only when
-/// llvm_shutdown is called.  We print statistics from the destructor.
+/// This class is used in a ManagedStatic so that it is created on demand (when
+/// the first statistic is bumped) and destroyed only when llvm_shutdown is
+/// called. We print statistics from the destructor.
+/// This class is also used to look up statistic values from applications that
+/// use LLVM.
 class StatisticInfo {
   std::vector<const Statistic*> Stats;
+
   friend void llvm::PrintStatistics();
   friend void llvm::PrintStatistics(raw_ostream &OS);
   friend void llvm::PrintStatisticsJSON(raw_ostream &OS);
@@ -64,14 +67,22 @@ class StatisticInfo {
   /// Sort statistics by debugtype,name,description.
   void sort();
 public:
+  using const_iterator = std::vector<const Statistic *>::const_iterator;
+
   StatisticInfo();
   ~StatisticInfo();
 
   void addStatistic(const Statistic *S) {
     Stats.push_back(S);
   }
+
+  const_iterator begin() const { return Stats.begin(); }
+  const_iterator end() const { return Stats.end(); }
+  iterator_range<const_iterator> statistics() const {
+    return {begin(), end()};
+  }
 };
-}
+} // end anonymous namespace
 
 static ManagedStatic<StatisticInfo> StatInfo;
 static ManagedStatic<sys::SmartMutex<true> > StatLock;
@@ -180,7 +191,7 @@ void llvm::PrintStatisticsJSON(raw_ostream &OS) {
 }
 
 void llvm::PrintStatistics() {
-#if !defined(NDEBUG) || defined(LLVM_ENABLE_STATS)
+#if LLVM_ENABLE_STATS
   StatisticInfo &Stats = *StatInfo;
 
   // Statistics not enabled?
@@ -205,3 +216,12 @@ void llvm::PrintStatistics() {
   }
 #endif
 }
+
+const std::vector<std::pair<StringRef, unsigned>> llvm::GetStatistics() {
+  sys::SmartScopedLock<true> Reader(*StatLock);
+  std::vector<std::pair<StringRef, unsigned>> ReturnStats;
+
+  for (const auto &Stat : StatInfo->statistics())
+    ReturnStats.emplace_back(Stat->getName(), Stat->getValue());
+  return ReturnStats;
+}
index c0d5110..d11bdc7 100644 (file)
@@ -56,6 +56,7 @@ set(ADTSources
   SparseBitVectorTest.cpp
   SparseMultiSetTest.cpp
   SparseSetTest.cpp
+  StatisticTest.cpp
   StringExtrasTest.cpp
   StringMapTest.cpp
   StringRefTest.cpp
diff --git a/unittests/ADT/StatisticTest.cpp b/unittests/ADT/StatisticTest.cpp
new file mode 100644 (file)
index 0000000..3ddfe56
--- /dev/null
@@ -0,0 +1,106 @@
+//===- llvm/unittest/ADT/StatisticTest.cpp - Statistic unit tests ---------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+using namespace llvm;
+
+namespace {
+#define DEBUG_TYPE "unittest"
+STATISTIC(Counter, "Counts things");
+STATISTIC(Counter2, "Counts other things");
+
+TEST(StatisticTest, Count) {
+  EnableStatistics();
+
+  Counter = 0;
+  EXPECT_EQ(Counter, 0u);
+  Counter++;
+  Counter++;
+#if LLVM_ENABLE_STATS
+  EXPECT_EQ(Counter, 2u);
+#else
+  EXPECT_EQ(Counter, 0u);
+#endif
+}
+
+TEST(StatisticTest, Assign) {
+  EnableStatistics();
+
+  Counter = 2;
+#if LLVM_ENABLE_STATS
+  EXPECT_EQ(Counter, 2u);
+#else
+  EXPECT_EQ(Counter, 0u);
+#endif
+}
+
+TEST(StatisticTest, API) {
+  EnableStatistics();
+
+  Counter = 0;
+  EXPECT_EQ(Counter, 0u);
+  Counter++;
+  Counter++;
+#if LLVM_ENABLE_STATS
+  EXPECT_EQ(Counter, 2u);
+#else
+  EXPECT_EQ(Counter, 0u);
+#endif
+
+#if LLVM_ENABLE_STATS
+  const auto Range1 = GetStatistics();
+  EXPECT_NE(Range1.begin(), Range1.end());
+  EXPECT_EQ(Range1.begin() + 1, Range1.end());
+
+  Optional<std::pair<StringRef, unsigned>> S1;
+  Optional<std::pair<StringRef, unsigned>> S2;
+  for (const auto &S : Range1) {
+    if (std::string(S.first) == "Counter")
+      S1 = S;
+    if (std::string(S.first) == "Counter2")
+      S2 = S;
+  }
+
+  EXPECT_NE(S1.hasValue(), false);
+  EXPECT_EQ(S2.hasValue(), false);
+
+  // Counter2 will be registered when it's first touched.
+  Counter2++;
+
+  const auto Range2 = GetStatistics();
+  EXPECT_NE(Range2.begin(), Range2.end());
+  EXPECT_EQ(Range2.begin() + 2, Range2.end());
+
+  S1 = None;
+  S2 = None;
+  for (const auto &S : Range2) {
+    if (std::string(S.first) == "Counter")
+      S1 = S;
+    if (std::string(S.first) == "Counter2")
+      S2 = S;
+  }
+
+  EXPECT_NE(S1.hasValue(), false);
+  EXPECT_NE(S2.hasValue(), false);
+
+  EXPECT_EQ(S1->first, "Counter");
+  EXPECT_EQ(S1->second, 2u);
+
+  EXPECT_EQ(S2->first, "Counter2");
+  EXPECT_EQ(S2->second, 1u);
+#else
+  Counter2++;
+  auto &Range = GetStatistics();
+  EXPECT_EQ(Range.begin(), Range.end());
+#endif
+}
+
+} // end anonymous namespace