Index: cmake/modules/HandleLLVMOptions.cmake =================================================================== --- cmake/modules/HandleLLVMOptions.cmake +++ cmake/modules/HandleLLVMOptions.cmake @@ -881,3 +881,7 @@ 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: include/llvm/ADT/Statistic.h =================================================================== --- include/llvm/ADT/Statistic.h +++ include/llvm/ADT/Statistic.h @@ -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 #include @@ -34,6 +35,7 @@ class raw_ostream; class raw_fd_ostream; +class StringRef; class Statistic { public: @@ -60,7 +62,7 @@ // 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 @@ void updateMax(unsigned V) {} -#endif // !defined(NDEBUG) || defined(LLVM_ENABLE_STATS) +#endif // LLVM_ENABLE_STATS protected: Statistic &init() { @@ -180,6 +182,15 @@ /// 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> GetStatistics(); + } // end namespace llvm #endif // LLVM_ADT_STATISTIC_H Index: include/llvm/Config/llvm-config.h.cmake =================================================================== --- include/llvm/Config/llvm-config.h.cmake +++ include/llvm/Config/llvm-config.h.cmake @@ -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: lib/Support/Statistic.cpp =================================================================== --- lib/Support/Statistic.cpp +++ lib/Support/Statistic.cpp @@ -51,12 +51,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 Stats; + friend void llvm::PrintStatistics(); friend void llvm::PrintStatistics(raw_ostream &OS); friend void llvm::PrintStatisticsJSON(raw_ostream &OS); @@ -64,14 +66,21 @@ /// Sort statistics by debugtype,name,description. void sort(); public: + using const_iterator = std::vector::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 statistics() const { + return {begin(), end()}; + } }; -} static ManagedStatic StatInfo; static ManagedStatic > StatLock; @@ -180,7 +189,7 @@ } void llvm::PrintStatistics() { -#if !defined(NDEBUG) || defined(LLVM_ENABLE_STATS) +#if LLVM_ENABLE_STATS StatisticInfo &Stats = *StatInfo; // Statistics not enabled? @@ -205,3 +214,12 @@ } #endif } + +const std::vector> llvm::GetStatistics() { + sys::SmartScopedLock Reader(*StatLock); + std::vector> ReturnStats; + + for (const auto &Stat : StatInfo->statistics()) + ReturnStats.emplace_back(Stat->getName(), Stat->getValue()); + return ReturnStats; +} Index: unittests/ADT/CMakeLists.txt =================================================================== --- unittests/ADT/CMakeLists.txt +++ unittests/ADT/CMakeLists.txt @@ -56,6 +56,7 @@ SparseBitVectorTest.cpp SparseMultiSetTest.cpp SparseSetTest.cpp + StatisticTest.cpp StringExtrasTest.cpp StringMapTest.cpp StringRefTest.cpp Index: unittests/ADT/StatisticTest.cpp =================================================================== --- /dev/null +++ unittests/ADT/StatisticTest.cpp @@ -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> S1; + Optional> 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