michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: michael@0: #ifndef BASE_STATS_COUNTERS_H__ michael@0: #define BASE_STATS_COUNTERS_H__ michael@0: michael@0: #include michael@0: #include "base/stats_table.h" michael@0: #include "base/time.h" michael@0: michael@0: // StatsCounters are dynamically created values which can be tracked in michael@0: // the StatsTable. They are designed to be lightweight to create and michael@0: // easy to use. michael@0: // michael@0: // Since StatsCounters can be created dynamically by name, there is michael@0: // a hash table lookup to find the counter in the table. A StatsCounter michael@0: // object can be created once and used across multiple threads safely. michael@0: // michael@0: // Example usage: michael@0: // { michael@0: // StatsCounter request_count("RequestCount"); michael@0: // request_count.Increment(); michael@0: // } michael@0: // michael@0: // Note that creating counters on the stack does work, however creating michael@0: // the counter object requires a hash table lookup. For inner loops, it michael@0: // may be better to create the counter either as a member of another object michael@0: // (or otherwise outside of the loop) for maximum performance. michael@0: // michael@0: // Internally, a counter represents a value in a row of a StatsTable. michael@0: // The row has a 32bit value for each process/thread in the table and also michael@0: // a name (stored in the table metadata). michael@0: // michael@0: // NOTE: In order to make stats_counters usable in lots of different code, michael@0: // avoid any dependencies inside this header file. michael@0: // michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Define macros for ease of use. They also allow us to change definitions michael@0: // as the implementation varies, or depending on compile options. michael@0: //------------------------------------------------------------------------------ michael@0: // First provide generic macros, which exist in production as well as debug. michael@0: #define STATS_COUNTER(name, delta) do { \ michael@0: static StatsCounter counter(name); \ michael@0: counter.Add(delta); \ michael@0: } while (0) michael@0: michael@0: #define SIMPLE_STATS_COUNTER(name) STATS_COUNTER(name, 1) michael@0: michael@0: #define RATE_COUNTER(name, duration) do { \ michael@0: static StatsRate hit_count(name); \ michael@0: hit_count.AddTime(duration); \ michael@0: } while (0) michael@0: michael@0: // Define Debug vs non-debug flavors of macros. michael@0: #ifndef NDEBUG michael@0: michael@0: #define DSTATS_COUNTER(name, delta) STATS_COUNTER(name, delta) michael@0: #define DSIMPLE_STATS_COUNTER(name) SIMPLE_STATS_COUNTER(name) michael@0: #define DRATE_COUNTER(name, duration) RATE_COUNTER(name, duration) michael@0: michael@0: #else // NDEBUG michael@0: michael@0: #define DSTATS_COUNTER(name, delta) do {} while (0) michael@0: #define DSIMPLE_STATS_COUNTER(name) do {} while (0) michael@0: #define DRATE_COUNTER(name, duration) do {} while (0) michael@0: michael@0: #endif // NDEBUG michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // StatsCounter represents a counter in the StatsTable class. michael@0: class StatsCounter { michael@0: public: michael@0: // Create a StatsCounter object. michael@0: explicit StatsCounter(const std::string& name) michael@0: : counter_id_(-1) { michael@0: // We prepend the name with 'c:' to indicate that it is a counter. michael@0: name_ = "c:"; michael@0: name_.append(name); michael@0: }; michael@0: michael@0: virtual ~StatsCounter() {} michael@0: michael@0: // Sets the counter to a specific value. michael@0: void Set(int value) { michael@0: int* loc = GetPtr(); michael@0: if (loc) *loc = value; michael@0: } michael@0: michael@0: // Increments the counter. michael@0: void Increment() { michael@0: Add(1); michael@0: } michael@0: michael@0: virtual void Add(int value) { michael@0: int* loc = GetPtr(); michael@0: if (loc) michael@0: (*loc) += value; michael@0: } michael@0: michael@0: // Decrements the counter. michael@0: void Decrement() { michael@0: Add(-1); michael@0: } michael@0: michael@0: void Subtract(int value) { michael@0: Add(-value); michael@0: } michael@0: michael@0: // Is this counter enabled? michael@0: // Returns false if table is full. michael@0: bool Enabled() { michael@0: return GetPtr() != NULL; michael@0: } michael@0: michael@0: int value() { michael@0: int* loc = GetPtr(); michael@0: if (loc) return *loc; michael@0: return 0; michael@0: } michael@0: michael@0: protected: michael@0: StatsCounter() michael@0: : counter_id_(-1) { michael@0: } michael@0: michael@0: // Returns the cached address of this counter location. michael@0: int* GetPtr() { michael@0: StatsTable* table = StatsTable::current(); michael@0: if (!table) michael@0: return NULL; michael@0: michael@0: // If counter_id_ is -1, then we haven't looked it up yet. michael@0: if (counter_id_ == -1) { michael@0: counter_id_ = table->FindCounter(name_); michael@0: if (table->GetSlot() == 0) { michael@0: if (!table->RegisterThread("")) { michael@0: // There is no room for this thread. This thread michael@0: // cannot use counters. michael@0: counter_id_ = 0; michael@0: return NULL; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // If counter_id_ is > 0, then we have a valid counter. michael@0: if (counter_id_ > 0) michael@0: return table->GetLocation(counter_id_, table->GetSlot()); michael@0: michael@0: // counter_id_ was zero, which means the table is full. michael@0: return NULL; michael@0: } michael@0: michael@0: std::string name_; michael@0: // The counter id in the table. We initialize to -1 (an invalid value) michael@0: // and then cache it once it has been looked up. The counter_id is michael@0: // valid across all threads and processes. michael@0: int32_t counter_id_; michael@0: }; michael@0: michael@0: michael@0: // A StatsCounterTimer is a StatsCounter which keeps a timer during michael@0: // the scope of the StatsCounterTimer. On destruction, it will record michael@0: // its time measurement. michael@0: class StatsCounterTimer : protected StatsCounter { michael@0: public: michael@0: // Constructs and starts the timer. michael@0: explicit StatsCounterTimer(const std::string& name) { michael@0: // we prepend the name with 't:' to indicate that it is a timer. michael@0: name_ = "t:"; michael@0: name_.append(name); michael@0: } michael@0: michael@0: // Start the timer. michael@0: void Start() { michael@0: if (!Enabled()) michael@0: return; michael@0: start_time_ = base::TimeTicks::Now(); michael@0: stop_time_ = base::TimeTicks(); michael@0: } michael@0: michael@0: // Stop the timer and record the results. michael@0: void Stop() { michael@0: if (!Enabled() || !Running()) michael@0: return; michael@0: stop_time_ = base::TimeTicks::Now(); michael@0: Record(); michael@0: } michael@0: michael@0: // Returns true if the timer is running. michael@0: bool Running() { michael@0: return Enabled() && !start_time_.is_null() && stop_time_.is_null(); michael@0: } michael@0: michael@0: // Accept a TimeDelta to increment. michael@0: virtual void AddTime(base::TimeDelta time) { michael@0: Add(static_cast(time.InMilliseconds())); michael@0: } michael@0: michael@0: protected: michael@0: // Compute the delta between start and stop, in milliseconds. michael@0: void Record() { michael@0: AddTime(stop_time_ - start_time_); michael@0: } michael@0: michael@0: base::TimeTicks start_time_; michael@0: base::TimeTicks stop_time_; michael@0: }; michael@0: michael@0: // A StatsRate is a timer that keeps a count of the number of intervals added so michael@0: // that several statistics can be produced: michael@0: // min, max, avg, count, total michael@0: class StatsRate : public StatsCounterTimer { michael@0: public: michael@0: // Constructs and starts the timer. michael@0: explicit StatsRate(const char* name) michael@0: : StatsCounterTimer(name), michael@0: counter_(name), michael@0: largest_add_(std::string(" ").append(name).append("MAX").c_str()) { michael@0: } michael@0: michael@0: virtual void Add(int value) { michael@0: counter_.Increment(); michael@0: StatsCounterTimer::Add(value); michael@0: if (value > largest_add_.value()) michael@0: largest_add_.Set(value); michael@0: } michael@0: michael@0: private: michael@0: StatsCounter counter_; michael@0: StatsCounter largest_add_; michael@0: }; michael@0: michael@0: michael@0: // Helper class for scoping a timer or rate. michael@0: template class StatsScope { michael@0: public: michael@0: explicit StatsScope(T& timer) michael@0: : timer_(timer) { michael@0: timer_.Start(); michael@0: } michael@0: michael@0: ~StatsScope() { michael@0: timer_.Stop(); michael@0: } michael@0: michael@0: void Stop() { michael@0: timer_.Stop(); michael@0: } michael@0: michael@0: private: michael@0: T& timer_; michael@0: }; michael@0: michael@0: #endif // BASE_STATS_COUNTERS_H__