Index: llvm/trunk/include/llvm/ADT/Statistic.h =================================================================== --- llvm/trunk/include/llvm/ADT/Statistic.h +++ llvm/trunk/include/llvm/ADT/Statistic.h @@ -199,6 +199,21 @@ /// completes. const std::vector> GetStatistics(); +/// \brief Reset the statistics. This can be used to zero and de-register the +/// statistics in order to measure a compilation. +/// +/// When this function begins to call destructors prior to returning, all +/// statistics will be zero and unregistered. However, that might not remain the +/// case by the time this function finishes returning. Whether update from other +/// threads are lost or merely deferred until during the function return is +/// timing sensitive. +/// +/// Callers who intend to use this to measure statistics for a single +/// 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(). +void ResetStatistics(); + } // end namespace llvm #endif // LLVM_ADT_STATISTIC_H Index: llvm/trunk/lib/Support/Statistic.cpp =================================================================== --- llvm/trunk/lib/Support/Statistic.cpp +++ llvm/trunk/lib/Support/Statistic.cpp @@ -58,7 +58,7 @@ /// This class is also used to look up statistic values from applications that /// use LLVM. class StatisticInfo { - std::vector Stats; + std::vector Stats; friend void llvm::PrintStatistics(); friend void llvm::PrintStatistics(raw_ostream &OS); @@ -67,12 +67,12 @@ /// Sort statistics by debugtype,name,description. void sort(); public: - using const_iterator = std::vector::const_iterator; + using const_iterator = std::vector::const_iterator; StatisticInfo(); ~StatisticInfo(); - void addStatistic(const Statistic *S) { + void addStatistic(Statistic *S) { Stats.push_back(S); } @@ -81,6 +81,8 @@ iterator_range statistics() const { return {begin(), end()}; } + + void reset(); }; } // end anonymous namespace @@ -135,6 +137,28 @@ }); } +void StatisticInfo::reset() { + sys::SmartScopedLock Writer(*StatLock); + + // Tell each statistic that it isn't registered so it has to register + // 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) { + // 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 + // 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(); +} + void llvm::PrintStatistics(raw_ostream &OS) { StatisticInfo &Stats = *StatInfo; @@ -227,3 +251,7 @@ ReturnStats.emplace_back(Stat->getName(), Stat->getValue()); return ReturnStats; } + +void llvm::ResetStatistics() { + StatInfo->reset(); +} Index: llvm/trunk/unittests/ADT/StatisticTest.cpp =================================================================== --- llvm/trunk/unittests/ADT/StatisticTest.cpp +++ llvm/trunk/unittests/ADT/StatisticTest.cpp @@ -12,11 +12,24 @@ #include "gtest/gtest.h" using namespace llvm; +using OptionalStatistic = Optional>; + namespace { #define DEBUG_TYPE "unittest" STATISTIC(Counter, "Counts things"); STATISTIC(Counter2, "Counts other things"); +static void +extractCounters(const std::vector> &Range, + OptionalStatistic &S1, OptionalStatistic &S2) { + for (const auto &S : Range) { + if (S.first == "Counter") + S1 = S; + if (S.first == "Counter2") + S2 = S; + } +} + TEST(StatisticTest, Count) { EnableStatistics(); @@ -56,51 +69,90 @@ #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; - } + { + const auto Range1 = GetStatistics(); + EXPECT_NE(Range1.begin(), Range1.end()); + EXPECT_EQ(Range1.begin() + 1, Range1.end()); + + OptionalStatistic S1; + OptionalStatistic S2; + extractCounters(Range1, S1, S2); - EXPECT_NE(S1.hasValue(), false); - EXPECT_EQ(S2.hasValue(), false); + EXPECT_EQ(S1.hasValue(), true); + 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; - } + { + const auto Range = GetStatistics(); + EXPECT_NE(Range.begin(), Range.end()); + EXPECT_EQ(Range.begin() + 2, Range.end()); + + OptionalStatistic S1; + OptionalStatistic S2; + extractCounters(Range, S1, S2); - EXPECT_NE(S1.hasValue(), false); - EXPECT_NE(S2.hasValue(), false); + EXPECT_EQ(S1.hasValue(), true); + EXPECT_EQ(S2.hasValue(), true); - EXPECT_EQ(S1->first, "Counter"); - EXPECT_EQ(S1->second, 2u); + EXPECT_EQ(S1->first, "Counter"); + EXPECT_EQ(S1->second, 2u); - EXPECT_EQ(S2->first, "Counter2"); - EXPECT_EQ(S2->second, 1u); + EXPECT_EQ(S2->first, "Counter2"); + EXPECT_EQ(S2->second, 1u); + } #else Counter2++; auto &Range = GetStatistics(); EXPECT_EQ(Range.begin(), Range.end()); #endif + +#if LLVM_ENABLE_STATS + // Check that resetting the statistics works correctly. + // It should empty the list and zero the counters. + ResetStatistics(); + { + auto &Range = GetStatistics(); + EXPECT_EQ(Range.begin(), Range.end()); + EXPECT_EQ(Counter, 0u); + EXPECT_EQ(Counter2, 0u); + OptionalStatistic S1; + OptionalStatistic S2; + extractCounters(Range, S1, S2); + EXPECT_EQ(S1.hasValue(), false); + EXPECT_EQ(S2.hasValue(), false); + } + + // Now check that they successfully re-register and count. + 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.hasValue(), true); + EXPECT_EQ(S2.hasValue(), 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. + ResetStatistics(); +#endif } } // end anonymous namespace