/* * 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 #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 SubType *SafeCast() { return SubType::kType == type() ? static_cast(this) : NULL; } template const SubType *SafeCast() const { return SubType::kType == type() ? static_cast(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 { 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(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(); } inline TimingMetric *MetricBase::AsTiming() { return SafeCast(); } inline IntegerMetric *MetricBase::AsInteger() { return SafeCast(); } inline BoolMetric *MetricBase::AsBool() { return SafeCast(); } inline const CountMetric *MetricBase::AsCount() const { return SafeCast(); } inline const TimingMetric *MetricBase::AsTiming() const { return SafeCast(); } inline const IntegerMetric *MetricBase::AsInteger() const { return SafeCast(); } inline const BoolMetric *MetricBase::AsBool() const { return SafeCast(); } } // namespace stats_report #endif // O3D_STATSREPORT_METRICS_H__