| /* |
| * Copyright 2009, Google Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| |
| // Declares the interface to in-memory metrics capture |
| #ifndef O3D_STATSREPORT_METRICS_H__ |
| #define O3D_STATSREPORT_METRICS_H__ |
| |
| #include <iterator> |
| #include "base/basictypes.h" |
| #include "base/logging.h" |
| #include "statsreport/common/highres_timer.h" |
| |
| // Macros to declare & define named & typed metrics. |
| // Put declarations in headers or in cpp files, where you need access |
| // to the metrics. For each declared metric, there must be precisely |
| // one definition in a compilation unit someplace. |
| |
| // A count metric should be used to report anything that monotonically |
| // increases. |
| // Examples: |
| // # event count |
| // how often does this condition hit, this function get called |
| // # aggregate sums |
| // how many bytes are written |
| #define DECLARE_METRIC_count(name) DECLARE_METRIC(CountMetric, name) |
| #define DEFINE_METRIC_count(name) DEFINE_METRIC(CountMetric, name) |
| |
| // Use timing metrics to report on the performance of important things. |
| // A timing metric will report the count of occurrences, as well as the |
| // average, min and max times. |
| // Samples are measured in milliseconds if you use the TIME_SCOPE macro |
| // or the HighResTimer class to collect samples. |
| #define DECLARE_METRIC_timing(name) DECLARE_METRIC(TimingMetric, name) |
| #define DEFINE_METRIC_timing(name) DEFINE_METRIC(TimingMetric, name) |
| |
| // Collects a sample from here to the end of the current scope, and |
| // adds the sample to the timing metric supplied |
| #define TIME_SCOPE(timing) \ |
| stats_report::TimingSample __xxsample__(&timing) |
| |
| // Use integer metrics to report runtime values that fluctuate. |
| // Examples: |
| // # object count |
| // How many objects of some type exist |
| // # disk space or memory |
| // How much disk space or memory is in use |
| #define DECLARE_METRIC_integer(name) DECLARE_METRIC(IntegerMetric, name) |
| #define DEFINE_METRIC_integer(name) DEFINE_METRIC(IntegerMetric, name) |
| |
| |
| // Use boolean metrics to report the occurrence of important but rare events |
| // or conditions. Note that a boolean metric is tri-state, so you typically |
| // want to set it only in one direction, and typically to true. |
| // Setting a boolean metric one way or another on a trigger event will report |
| // the setting of the boolean immediately prior to reporting, which is |
| // typically not what you want. |
| #define DECLARE_METRIC_bool(name) DECLARE_METRIC(BoolMetric, name) |
| #define DEFINE_METRIC_bool(name) DEFINE_METRIC(BoolMetric, name) |
| |
| |
| // Implementation macros |
| #define DECLARE_METRIC(type, name) \ |
| namespace o3d_statsreport { \ |
| extern stats_report::type metric_##name; \ |
| } \ |
| using o3d_statsreport::metric_##name |
| |
| #define DEFINE_METRIC(type, name) \ |
| namespace o3d_statsreport { \ |
| stats_report::type metric_##name(#name, \ |
| &stats_report::g_global_metric_storage); \ |
| } \ |
| using o3d_statsreport::metric_##name |
| |
| |
| namespace stats_report { |
| |
| enum MetricType { |
| // use zero for invalid, because global storage defaults to zero |
| kInvalidType = 0, |
| kCountType, |
| kTimingType, |
| kIntegerType, |
| kBoolType |
| }; |
| |
| // fwd. |
| struct MetricCollectionBase; |
| class MetricCollection; |
| class MetricBase; |
| class IntegerMetricBase; |
| class CountMetric; |
| class TimingMetric; |
| class IntegerMetric; |
| class BoolMetric; |
| |
| // Base class for all stats instances. |
| // Stats instances are chained together against a MetricCollection to |
| // allow enumerating stats. |
| // |
| // MetricCollection is factored into a class to make it easier to unittest |
| // the implementation. |
| class MetricBase { |
| public: |
| // @name Safe downcasts |
| // @{ |
| CountMetric *AsCount(); |
| TimingMetric *AsTiming(); |
| IntegerMetric *AsInteger(); |
| BoolMetric *AsBool(); |
| |
| const CountMetric *AsCount() const; |
| const TimingMetric *AsTiming() const; |
| const IntegerMetric *AsInteger() const; |
| const BoolMetric *AsBool() const; |
| // @} |
| |
| // @name Accessors |
| // @{ |
| MetricType type() const { return type_; } |
| MetricBase *next() const { return next_; } |
| const char *name() const { return name_; } |
| // @} |
| |
| // TODO: does this need to be virtual? |
| virtual ~MetricBase() = 0; |
| |
| protected: |
| class ObjectLock; |
| void Lock() const; |
| void Unlock() const; |
| |
| // Constructs a MetricBase and adds to the provided MetricCollection. |
| // @note Metrics can only be constructed up to the point where the |
| // MetricCollection is initialized, and there's no locking performed. |
| // The assumption is that outside unit tests, Metrics will we declared |
| // as static/global variables, and initialized at static initialization |
| // time - and static initialization is single-threaded. |
| MetricBase(const char *name, MetricType type, MetricCollectionBase *coll); |
| |
| // Constructs a named typed MetricBase |
| MetricBase(const char *name, MetricType type); |
| |
| // Our name |
| char const *const name_; |
| |
| // type of this metric |
| MetricType const type_; |
| |
| // chains to next stat instance |
| MetricBase *const next_; |
| |
| // The collection we're created against |
| MetricCollectionBase *const coll_; |
| |
| private: |
| template <class SubType> |
| SubType *SafeCast() { |
| return SubType::kType == type() ? static_cast<SubType*>(this) : NULL; |
| } |
| template <class SubType> |
| const SubType *SafeCast() const { |
| return SubType::kType == type() ? static_cast<const SubType*>(this) : NULL; |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(MetricBase); |
| }; |
| |
| // Must be a POD |
| struct MetricCollectionBase { |
| bool initialized_; |
| MetricBase *first_; |
| }; |
| |
| // Inherit from base, which is a POD and can be initialized at link time. |
| // |
| // The global MetricCollection is aliased to a link-time initialized |
| // instance of MetricCollectionBase, and must not extend the size of its |
| // base class. |
| class MetricCollection: public MetricCollectionBase { |
| public: |
| MetricCollection() { |
| initialized_ = false; |
| first_ = NULL; |
| } |
| ~MetricCollection() { |
| DCHECK(NULL == first_); |
| } |
| |
| // Initialize must be called after all metrics have been added to the |
| // collection, but before enumerating it for e.g. aggregation or reporting. |
| // The intent is that outside unit tests, there will only be the global |
| // metrics collection, which will accrue all metrics defined with the |
| // DEFINE_METRIC_* macros. |
| // Typically you'd call Initialize very early in your main function, and |
| // Uninitialize towards the end of main. |
| // It is an error to Initialize() when the collection is initialized(). |
| void Initialize(); |
| |
| // Uninitialize must be called before removing (deleting or deconstructing) |
| // metrics from the collection. |
| // It is an error to Uninitialize() when the collection is !initialized(). |
| void Uninitialize(); |
| |
| MetricBase *first() const { return first_; } |
| bool initialized() const { return initialized_; } |
| |
| private: |
| using MetricCollectionBase::initialized_; |
| using MetricCollectionBase::first_; |
| |
| // MetricBase is intimate with us |
| friend class MetricBase; |
| |
| DISALLOW_COPY_AND_ASSIGN(MetricCollection); |
| }; |
| |
| // Implements a forward_iterator for MetricCollection. |
| class MetricIterator: public std::iterator<std::forward_iterator_tag, |
| MetricBase *> { |
| public: |
| MetricIterator() : curr_(NULL) { |
| } |
| // We need a non-explicit copy constructor to satisfy the iterator interface. |
| MetricIterator(const MetricIterator &other) : curr_(other.curr_) { |
| } |
| explicit MetricIterator(const MetricCollection &coll) : curr_(coll.first()) { |
| DCHECK(coll.initialized()); |
| } |
| |
| MetricBase *operator*() const { |
| return curr_; |
| } |
| MetricBase *operator->() const { |
| return curr_; |
| } |
| MetricIterator operator++() { // preincrement |
| if (curr_) |
| curr_ = curr_->next(); |
| |
| return *this; |
| } |
| MetricIterator operator++(int dummy) { // postincrement |
| MetricIterator ret(*this); |
| ++*this; |
| return ret; |
| } |
| |
| private: |
| MetricBase *curr_; |
| }; |
| |
| inline bool operator == (const MetricIterator &a, const MetricIterator &b) { |
| return *a == *b; |
| } |
| inline bool operator != (const MetricIterator &a, const MetricIterator &b) { |
| return !operator == (a, b); |
| } |
| |
| // Globally defined counters are registered here |
| extern MetricCollectionBase g_global_metric_storage; |
| |
| // And more conveniently accessed through here |
| extern MetricCollection &g_global_metrics; |
| |
| // Base class for integer metrics |
| class IntegerMetricBase: public MetricBase { |
| public: |
| // Sets the current value |
| void Set(uint64 value); |
| |
| // Retrieves the current value |
| uint64 value() const; |
| |
| void operator ++() { Increment(); } |
| void operator ++(int dummy) { Increment(); } |
| void operator +=(uint64 addend) { Add(addend); } |
| |
| protected: |
| IntegerMetricBase(const char *name, |
| MetricType type, |
| MetricCollectionBase *coll) |
| : MetricBase(name, type, coll), value_(0) { |
| } |
| IntegerMetricBase(const char *name, MetricType type, uint64 value) |
| : MetricBase(name, type), value_(value) { |
| } |
| |
| void Increment(); |
| void Decrement(); |
| void Add(uint64 value); |
| void Subtract(uint64 value); |
| |
| uint64 value_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(IntegerMetricBase); |
| }; |
| |
| // A count metric is a cumulative counter of events. |
| class CountMetric: public IntegerMetricBase { |
| public: |
| // Our type. |
| static const int kType = kCountType; |
| |
| CountMetric(const char *name, MetricCollectionBase *coll) |
| : IntegerMetricBase(name, kCountType, coll) { |
| } |
| |
| CountMetric(const char *name, uint64 value) |
| : IntegerMetricBase(name, kCountType, value) { |
| } |
| |
| // Nulls the metric and returns the current values. |
| uint64 Reset(); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(CountMetric); |
| }; |
| |
| class TimingMetric: public MetricBase { |
| public: |
| static const int kType = kTimingType; |
| |
| struct TimingData { |
| uint32 count; |
| uint32 align; // allow access to the alignment gap between count and sum, |
| // makes it easier to unittest. |
| uint64 sum; // ms |
| uint64 minimum; // ms |
| uint64 maximum; // ms |
| }; |
| |
| TimingMetric(const char *name, MetricCollectionBase *coll) |
| : MetricBase(name, kTimingType, coll) { |
| Clear(); |
| } |
| |
| TimingMetric(const char *name, const TimingData &value) |
| : MetricBase(name, kTimingType), data_(value) { |
| } |
| |
| uint32 count() const; |
| uint64 sum() const; |
| uint64 minimum() const; |
| uint64 maximum() const; |
| uint64 average() const; |
| |
| // Adds a single sample to the metric |
| // @param time_ms time (in milliseconds) for this sample |
| void AddSample(uint64 time_ms); |
| |
| // Adds count samples to the metric |
| // @note use this when capturing time over a variable number of items to |
| // normalize e.g. download time per byte or KB. This records one sample |
| // over count items, which is numerically more stable for the average |
| // than dividing the captured time by the item count. As a side benefit |
| // the timer will also record the item count. |
| // @note if count == 0, no sample will be recorded |
| // @param count number of samples to add |
| // @param total_time_ms the total time consumed by all the "count" samples |
| void AddSamples(uint64 count, uint64 total_time_ms); |
| |
| // Nulls the metric and returns the current values. |
| TimingData Reset(); |
| |
| private: |
| void Clear(); |
| |
| TimingData data_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TimingMetric); |
| }; |
| |
| // A convenience class to sample the time from construction to destruction |
| // against a given timing metric. |
| class TimingSample { |
| public: |
| // @param timing the metric the sample is to be tallied against |
| explicit TimingSample(TimingMetric *timing) : timing_(timing), count_(1) { |
| DCHECK(NULL != timing); |
| } |
| |
| // @param timing the metric the sample is to be tallied against |
| // @param item_count count of items processed, used to divide the sampled |
| // time so as to capture time per item, which is often a better measure |
| // than the total time over a varying number of items. |
| TimingSample(TimingMetric *timing, uint32 item_count) : timing_(timing), |
| count_(item_count) { |
| DCHECK(NULL != timing); |
| } |
| |
| ~TimingSample() { |
| // We discard samples with a zero count |
| if (1 == count_) |
| timing_->AddSample(timer_.GetElapsedMs()); |
| else |
| timing_->AddSamples(count_, timer_.GetElapsedMs()); |
| } |
| |
| // @name Accessors |
| // @{ |
| uint32 count() const { return count_; } |
| void set_count(uint32 count) { count_ = count; } |
| // @} |
| |
| private: |
| // Collects the sample for us. |
| HighresTimer timer_; |
| |
| // The metric we tally against. |
| TimingMetric *timing_; |
| |
| // The item count we divide the captured time by |
| uint32 count_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TimingSample); |
| }; |
| |
| // An integer metric is used to sample values that vary over time. |
| // On aggregation the instantaneous value of the integer metric is captured. |
| class IntegerMetric: public IntegerMetricBase { |
| public: |
| static const int kType = kIntegerType; |
| |
| IntegerMetric(const char *name, MetricCollectionBase *coll) |
| : IntegerMetricBase(name, kIntegerType, coll) { |
| } |
| |
| IntegerMetric(const char *name, uint64 value) |
| : IntegerMetricBase(name, kIntegerType, value) { |
| } |
| |
| void operator = (uint64 value) { Set(value); } |
| |
| void operator --() { Decrement(); } |
| void operator --(int dummy) { Decrement(); } |
| void operator -=(uint64 sub) { Subtract(sub); } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(IntegerMetric); |
| }; |
| |
| // A bool metric is tri-state, and can be: |
| // - unset, |
| // - true or |
| // - false |
| // to match other metrics, which are implicitly unset if they've not changed |
| // from their initial value. |
| class BoolMetric: public MetricBase { |
| public: |
| static const int kType = kBoolType; |
| |
| // Values we can take |
| enum TristateBoolValue { |
| kBoolUnset = -1, |
| kBoolFalse, |
| kBoolTrue, |
| }; |
| |
| BoolMetric(const char *name, MetricCollectionBase *coll) |
| : MetricBase(name, kBoolType, coll), value_(kBoolUnset) { |
| } |
| |
| BoolMetric(const char *name, uint32 value) |
| : MetricBase(name, kBoolType) { |
| switch (value) { |
| case kBoolFalse: |
| case kBoolTrue: |
| value_ = static_cast<TristateBoolValue>(value); |
| break; |
| |
| default: |
| DCHECK(false && "Unexpected tristate bool value on construction"); |
| value_ = kBoolUnset; |
| } |
| } |
| |
| // Sets the flag to the provided value. |
| void Set(bool value); |
| |
| void operator = (bool value) { |
| Set(value); |
| } |
| |
| // Nulls the metric and returns the current values. |
| TristateBoolValue Reset(); |
| |
| // Returns the current value - not threadsafe. |
| TristateBoolValue value() const { return value_; } |
| |
| private: |
| TristateBoolValue value_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BoolMetric); |
| }; |
| |
| inline CountMetric *MetricBase::AsCount() { |
| return SafeCast<CountMetric>(); |
| } |
| |
| inline TimingMetric *MetricBase::AsTiming() { |
| return SafeCast<TimingMetric>(); |
| } |
| |
| inline IntegerMetric *MetricBase::AsInteger() { |
| return SafeCast<IntegerMetric>(); |
| } |
| |
| inline BoolMetric *MetricBase::AsBool() { |
| return SafeCast<BoolMetric>(); |
| } |
| |
| inline const CountMetric *MetricBase::AsCount() const { |
| return SafeCast<CountMetric>(); |
| } |
| |
| inline const TimingMetric *MetricBase::AsTiming() const { |
| return SafeCast<TimingMetric>(); |
| } |
| |
| inline const IntegerMetric *MetricBase::AsInteger() const { |
| return SafeCast<IntegerMetric>(); |
| } |
| |
| inline const BoolMetric *MetricBase::AsBool() const { |
| return SafeCast<BoolMetric>(); |
| } |
| |
| } // namespace stats_report |
| |
| #endif // O3D_STATSREPORT_METRICS_H__ |