| OLD | NEW |
| (Empty) |
| 1 // Copyright 2006-2009 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 // | |
| 16 // Declares the interface to in-memory metrics capture | |
| 17 #ifndef OMAHA_STATSREPORT_METRICS_H__ | |
| 18 #define OMAHA_STATSREPORT_METRICS_H__ | |
| 19 | |
| 20 #include <iterator> | |
| 21 #include "base/basictypes.h" | |
| 22 #include "omaha/base/highres_timer-win32.h" | |
| 23 #include "omaha/base/logging/logging.h" | |
| 24 | |
| 25 /// Macros to declare & define named & typed metrics. | |
| 26 /// Put declarations in headers or in cpp files, where you need access | |
| 27 /// to the metrics. For each declared metric, there must be precisely | |
| 28 /// one definition in a compilation unit someplace. | |
| 29 | |
| 30 /// A count metric should be used to report anything that monotonically | |
| 31 /// increases. | |
| 32 /// Examples: | |
| 33 /// # event count | |
| 34 /// how often does this condition hit, this function get called | |
| 35 /// # aggregate sums | |
| 36 /// how many bytes are written | |
| 37 #define DECLARE_METRIC_count(name) DECLARE_METRIC(CountMetric, name) | |
| 38 #define DEFINE_METRIC_count(name) DEFINE_METRIC(CountMetric, name) | |
| 39 | |
| 40 /// Use timing metrics to report on the performance of important things. | |
| 41 /// A timing metric will report the count of occurrences, as well as the | |
| 42 /// average, min and max times. | |
| 43 /// Samples are measured in milliseconds if you use the TIME_SCOPE macro | |
| 44 /// or the HighResTimer class to collect samples. | |
| 45 #define DECLARE_METRIC_timing(name) DECLARE_METRIC(TimingMetric, name) | |
| 46 #define DEFINE_METRIC_timing(name) DEFINE_METRIC(TimingMetric, name) | |
| 47 | |
| 48 /// Collects a sample from here to the end of the current scope, and | |
| 49 /// adds the sample to the timing metric supplied | |
| 50 #define TIME_SCOPE(timing) \ | |
| 51 stats_report::TimingSample __xxsample__(timing) | |
| 52 | |
| 53 /// Use integer metrics to report runtime values that fluctuate. | |
| 54 /// Examples: | |
| 55 /// # object count | |
| 56 /// How many objects of some type exist | |
| 57 /// # disk space or memory | |
| 58 /// How much disk space or memory is in use | |
| 59 #define DECLARE_METRIC_integer(name) DECLARE_METRIC(IntegerMetric, name) | |
| 60 #define DEFINE_METRIC_integer(name) DEFINE_METRIC(IntegerMetric, name) | |
| 61 | |
| 62 | |
| 63 /// Use boolean metrics to report the occurrence of important but rare events | |
| 64 /// or conditions. Note that a boolean metric is tri-state, so you typically | |
| 65 /// want to set it only in one direction, and typically to true. | |
| 66 /// Setting a boolean metric one way or another on a trigger event will report | |
| 67 /// the setting of the boolean immediately prior to reporting, which is | |
| 68 /// typically not what you want. | |
| 69 #define DECLARE_METRIC_bool(name) DECLARE_METRIC(BoolMetric, name) | |
| 70 #define DEFINE_METRIC_bool(name) DEFINE_METRIC(BoolMetric, name) | |
| 71 | |
| 72 | |
| 73 /// Implementation macros | |
| 74 #define DECLARE_METRIC(type, name) \ | |
| 75 namespace omaha_client_statsreport { \ | |
| 76 extern stats_report::type metric_##name; \ | |
| 77 } \ | |
| 78 using omaha_client_statsreport::metric_##name | |
| 79 | |
| 80 #define DEFINE_METRIC(type, name) \ | |
| 81 namespace omaha_client_statsreport { \ | |
| 82 stats_report::type metric_##name(#name, \ | |
| 83 &stats_report::g_global_metric_storage); \ | |
| 84 } \ | |
| 85 using omaha_client_statsreport::metric_##name | |
| 86 | |
| 87 | |
| 88 namespace stats_report { | |
| 89 | |
| 90 enum MetricType { | |
| 91 // use zero for invalid, because global storage defaults to zero | |
| 92 kInvalidType = 0, | |
| 93 kCountType, | |
| 94 kTimingType, | |
| 95 kIntegerType, | |
| 96 kBoolType | |
| 97 }; | |
| 98 | |
| 99 // fwd. | |
| 100 struct MetricCollectionBase; | |
| 101 class MetricCollection; | |
| 102 class MetricBase; | |
| 103 class IntegerMetricBase; | |
| 104 class CountMetric; | |
| 105 class TimingMetric; | |
| 106 class IntegerMetric; | |
| 107 class BoolMetric; | |
| 108 | |
| 109 /// Base class for all stats instances. | |
| 110 /// Stats instances are chained together against a MetricCollection to | |
| 111 /// allow enumerating stats. | |
| 112 /// | |
| 113 /// MetricCollection is factored into a class to make it easier to unittest | |
| 114 /// the implementation. | |
| 115 class MetricBase { | |
| 116 public: | |
| 117 /// @name Downcasts | |
| 118 /// @{ | |
| 119 CountMetric &AsCount(); | |
| 120 TimingMetric &AsTiming(); | |
| 121 IntegerMetric &AsInteger(); | |
| 122 BoolMetric &AsBool(); | |
| 123 | |
| 124 const CountMetric &AsCount() const; | |
| 125 const TimingMetric &AsTiming() const; | |
| 126 const IntegerMetric &AsInteger() const; | |
| 127 const BoolMetric &AsBool() const; | |
| 128 /// @} | |
| 129 | |
| 130 /// @name Accessors | |
| 131 /// @{ | |
| 132 MetricType type() const { return type_; } | |
| 133 MetricBase *next() const { return next_; } | |
| 134 const char *name() const { return name_; } | |
| 135 /// @} | |
| 136 | |
| 137 // TODO(omaha): does this need to be virtual? | |
| 138 virtual ~MetricBase() = 0; | |
| 139 | |
| 140 protected: | |
| 141 class ObjectLock; | |
| 142 void Lock() const; | |
| 143 void Unlock() const; | |
| 144 | |
| 145 /// Constructs a MetricBase and adds to the provided MetricCollection. | |
| 146 /// @note Metrics can only be constructed up to the point where the | |
| 147 /// MetricCollection is initialized, and there's no locking performed. | |
| 148 /// The assumption is that outside unit tests, Metrics will we declared | |
| 149 /// as static/global variables, and initialized at static initialization | |
| 150 /// time - and static initialization is single-threaded. | |
| 151 MetricBase(const char *name, MetricType type, MetricCollectionBase *coll); | |
| 152 | |
| 153 /// Constructs a named typed MetricBase | |
| 154 MetricBase(const char *name, MetricType type); | |
| 155 | |
| 156 /// Our name | |
| 157 char const *const name_; | |
| 158 | |
| 159 /// type of this metric | |
| 160 MetricType const type_; | |
| 161 | |
| 162 /// chains to next stat instance | |
| 163 MetricBase *const next_; | |
| 164 | |
| 165 /// The collection we're created against | |
| 166 MetricCollectionBase *const coll_; | |
| 167 | |
| 168 private: | |
| 169 DISALLOW_EVIL_CONSTRUCTORS(MetricBase); | |
| 170 }; | |
| 171 | |
| 172 /// Must be a POD | |
| 173 struct MetricCollectionBase { | |
| 174 bool initialized_; | |
| 175 MetricBase *first_; | |
| 176 }; | |
| 177 | |
| 178 /// Inherit from base, which is a POD and can be initialized at link time. | |
| 179 /// | |
| 180 /// The global MetricCollection is aliased to a link-time initialized | |
| 181 /// instance of MetricCollectionBase, and must not extend the size of its | |
| 182 /// base class. | |
| 183 class MetricCollection: public MetricCollectionBase { | |
| 184 public: | |
| 185 MetricCollection() { | |
| 186 initialized_ = false; | |
| 187 first_ = NULL; | |
| 188 } | |
| 189 ~MetricCollection() { | |
| 190 DCHECK(NULL == first_); | |
| 191 } | |
| 192 | |
| 193 /// Initialize must be called after all metrics have been added to the | |
| 194 /// collection, but before enumerating it for e.g. aggregation or reporting. | |
| 195 /// The intent is that outside unit tests, there will only be the global | |
| 196 /// metrics collection, which will accrue all metrics defined with the | |
| 197 /// DEFINE_METRIC_* macros. | |
| 198 /// Typically you'd call Initialize very early in your main function, and | |
| 199 /// Uninitialize towards the end of main. | |
| 200 /// It is an error to Initialize() when the collection is initialized(). | |
| 201 void Initialize(); | |
| 202 | |
| 203 /// Uninitialize must be called before removing (deleting or deconstructing) | |
| 204 /// metrics from the collection. | |
| 205 /// It is an error to Uninitialize() when the collection is !initialized(). | |
| 206 void Uninitialize(); | |
| 207 | |
| 208 MetricBase *first() const { return first_; } | |
| 209 bool initialized() const { return initialized_; } | |
| 210 | |
| 211 private: | |
| 212 using MetricCollectionBase::initialized_; | |
| 213 using MetricCollectionBase::first_; | |
| 214 | |
| 215 DISALLOW_EVIL_CONSTRUCTORS(MetricCollection); | |
| 216 | |
| 217 /// MetricBase is intimate with us | |
| 218 friend class MetricBase; | |
| 219 }; | |
| 220 | |
| 221 /// Implements a forward_iterator for MetricCollection. | |
| 222 class MetricIterator: public std::iterator<std::forward_iterator_tag, | |
| 223 MetricBase *> { | |
| 224 public: | |
| 225 MetricIterator() : curr_(NULL) { | |
| 226 } | |
| 227 MetricIterator(const MetricIterator &other) : curr_(other.curr_) { | |
| 228 } | |
| 229 MetricIterator(const MetricCollection &coll) : curr_(coll.first()) { | |
| 230 DCHECK(coll.initialized()); | |
| 231 } | |
| 232 | |
| 233 MetricBase *operator*() const { | |
| 234 return curr_; | |
| 235 } | |
| 236 MetricBase *operator->() const { | |
| 237 return curr_; | |
| 238 } | |
| 239 MetricIterator operator++() { // preincrement | |
| 240 if (curr_) | |
| 241 curr_ = curr_->next(); | |
| 242 | |
| 243 return (*this); | |
| 244 } | |
| 245 MetricIterator operator++(int) {// postincrement | |
| 246 MetricIterator ret = *this; | |
| 247 ++*this; | |
| 248 return (ret); | |
| 249 } | |
| 250 | |
| 251 private: | |
| 252 MetricBase *curr_; | |
| 253 }; | |
| 254 | |
| 255 inline bool operator == (const MetricIterator &a, const MetricIterator &b) { | |
| 256 return *a == *b; | |
| 257 } | |
| 258 inline bool operator != (const MetricIterator &a, const MetricIterator &b) { | |
| 259 return !operator == (a, b); | |
| 260 } | |
| 261 | |
| 262 /// Globally defined counters are registered here | |
| 263 extern MetricCollectionBase g_global_metric_storage; | |
| 264 | |
| 265 /// And more conveniently accessed through here | |
| 266 extern MetricCollection &g_global_metrics; | |
| 267 | |
| 268 /// Base class for integer metrics | |
| 269 class IntegerMetricBase: public MetricBase { | |
| 270 public: | |
| 271 /// Sets the current value | |
| 272 void Set(int64 value); | |
| 273 | |
| 274 /// Retrieves the current value | |
| 275 int64 value() const; | |
| 276 | |
| 277 void operator ++ () { Increment(); } | |
| 278 void operator ++ (int) { Increment(); } | |
| 279 void operator += (int64 addend) { Add(addend); } | |
| 280 | |
| 281 protected: | |
| 282 IntegerMetricBase(const char *name, | |
| 283 MetricType type, | |
| 284 MetricCollectionBase *coll) | |
| 285 : MetricBase(name, type, coll), value_(0) { | |
| 286 } | |
| 287 IntegerMetricBase(const char *name, MetricType type, int64 value) | |
| 288 : MetricBase(name, type), value_(value) { | |
| 289 } | |
| 290 | |
| 291 void Increment(); | |
| 292 void Decrement(); | |
| 293 void Add(int64 value); | |
| 294 void Subtract(int64 value); | |
| 295 | |
| 296 int64 value_; | |
| 297 | |
| 298 private: | |
| 299 DISALLOW_EVIL_CONSTRUCTORS(IntegerMetricBase); | |
| 300 }; | |
| 301 | |
| 302 /// A count metric is a cumulative counter of events. | |
| 303 class CountMetric: public IntegerMetricBase { | |
| 304 public: | |
| 305 CountMetric(const char *name, MetricCollectionBase *coll) | |
| 306 : IntegerMetricBase(name, kCountType, coll) { | |
| 307 } | |
| 308 | |
| 309 CountMetric(const char *name, int64 value) | |
| 310 : IntegerMetricBase(name, kCountType, value) { | |
| 311 } | |
| 312 | |
| 313 /// Nulls the metric and returns the current values. | |
| 314 int64 Reset(); | |
| 315 | |
| 316 private: | |
| 317 DISALLOW_EVIL_CONSTRUCTORS(CountMetric); | |
| 318 }; | |
| 319 | |
| 320 class TimingMetric: public MetricBase { | |
| 321 public: | |
| 322 struct TimingData { | |
| 323 uint32 count; | |
| 324 uint32 align; // allow access to the alignment gap between count and sum, | |
| 325 // makes it esier to unittest. | |
| 326 int64 sum; // ms | |
| 327 int64 minimum; // ms | |
| 328 int64 maximum; // ms | |
| 329 }; | |
| 330 | |
| 331 TimingMetric(const char *name, MetricCollectionBase *coll) | |
| 332 : MetricBase(name, kTimingType, coll) { | |
| 333 Clear(); | |
| 334 } | |
| 335 | |
| 336 TimingMetric(const char *name, const TimingData &value) | |
| 337 : MetricBase(name, kTimingType), data_(value) { | |
| 338 } | |
| 339 | |
| 340 uint32 count() const; | |
| 341 int64 sum() const; | |
| 342 int64 minimum() const; | |
| 343 int64 maximum() const; | |
| 344 int64 average() const; | |
| 345 | |
| 346 /// Adds a single sample to the metric | |
| 347 /// @param time_ms time (in milliseconds) for this sample | |
| 348 void AddSample(int64 time_ms); | |
| 349 | |
| 350 /// Adds count samples to the metric | |
| 351 /// @note use this when capturing time over a variable number of items to | |
| 352 /// normalize e.g. download time per byte or KB. This records one sample | |
| 353 /// over count items, which is numerically more stable for the average | |
| 354 /// than dividing the captured time by the item count. As a side benefit | |
| 355 /// the timer will also record the item count. | |
| 356 /// @note if count == 0, no sample will be recorded | |
| 357 /// @param count number of samples to add | |
| 358 /// @param total_time_ms the total time consumed by all the "count" samples | |
| 359 void AddSamples(int64 count, int64 total_time_ms); | |
| 360 | |
| 361 /// Nulls the metric and returns the current values. | |
| 362 TimingData Reset(); | |
| 363 | |
| 364 private: | |
| 365 DISALLOW_EVIL_CONSTRUCTORS(TimingMetric); | |
| 366 | |
| 367 void Clear(); | |
| 368 | |
| 369 TimingData data_; | |
| 370 }; | |
| 371 | |
| 372 /// A convenience class to sample the time from construction to destruction | |
| 373 /// against a given timing metric. | |
| 374 class TimingSample { | |
| 375 public: | |
| 376 /// @param timing the metric the sample is to be tallied against | |
| 377 explicit TimingSample(TimingMetric &timing) : timing_(timing), count_(1) { | |
| 378 } | |
| 379 | |
| 380 /// @param timing the metric the sample is to be tallied against | |
| 381 /// @param item_count count of items processed, used to divide the sampled | |
| 382 /// time so as to capture time per item, which is often a better measure | |
| 383 /// than the total time over a varying number of items. | |
| 384 TimingSample(TimingMetric &timing, uint32 item_count) : timing_(timing), | |
| 385 count_(item_count) { | |
| 386 } | |
| 387 | |
| 388 ~TimingSample() { | |
| 389 // We discard samples with a zero count | |
| 390 if(count_ == 1) | |
| 391 timing_.AddSample(timer_.GetElapsedMs()); | |
| 392 else | |
| 393 timing_.AddSamples(count_, timer_.GetElapsedMs()); | |
| 394 } | |
| 395 | |
| 396 /// @name Accessors | |
| 397 /// @{ | |
| 398 uint32 count() const { return count_; } | |
| 399 void set_count(uint32 count) { count_ = count; } | |
| 400 /// @} | |
| 401 | |
| 402 private: | |
| 403 /// Collects the sample for us. | |
| 404 omaha::HighresTimer timer_; | |
| 405 | |
| 406 /// The metric we tally against. | |
| 407 TimingMetric &timing_; | |
| 408 | |
| 409 /// The item count we divide the captured time by | |
| 410 uint32 count_; | |
| 411 | |
| 412 DISALLOW_EVIL_CONSTRUCTORS(TimingSample); | |
| 413 }; | |
| 414 | |
| 415 /// An integer metric is used to sample values that vary over time. | |
| 416 /// On aggregation the instantaneous value of the integer metric is captured. | |
| 417 class IntegerMetric: public IntegerMetricBase { | |
| 418 public: | |
| 419 IntegerMetric(const char *name, MetricCollectionBase *coll) | |
| 420 : IntegerMetricBase(name, kIntegerType, coll) { | |
| 421 } | |
| 422 | |
| 423 IntegerMetric(const char *name, int64 value) | |
| 424 : IntegerMetricBase(name, kIntegerType, value) { | |
| 425 } | |
| 426 | |
| 427 void operator = (int64 value) { Set(value); } | |
| 428 | |
| 429 void operator -- () { Decrement(); } | |
| 430 void operator -- (int) { Decrement(); } | |
| 431 void operator -= (int64 sub) { Subtract(sub); } | |
| 432 | |
| 433 private: | |
| 434 DISALLOW_EVIL_CONSTRUCTORS(IntegerMetric); | |
| 435 }; | |
| 436 | |
| 437 /// A bool metric is tri-state, and can be: | |
| 438 /// - unset, | |
| 439 /// - true or | |
| 440 /// - false | |
| 441 /// to match other metrics, which are implicitly unset if they've not changed | |
| 442 /// from their initial value. | |
| 443 class BoolMetric: public MetricBase { | |
| 444 public: | |
| 445 /// Values we can take | |
| 446 enum TristateBoolValue { | |
| 447 kBoolUnset = -1, | |
| 448 kBoolFalse, | |
| 449 kBoolTrue, | |
| 450 }; | |
| 451 | |
| 452 BoolMetric(const char *name, MetricCollectionBase *coll) | |
| 453 : MetricBase(name, kBoolType, coll), value_(kBoolUnset) { | |
| 454 } | |
| 455 | |
| 456 BoolMetric(const char *name, uint32 value) | |
| 457 : MetricBase(name, kBoolType) { | |
| 458 switch (value) { | |
| 459 case kBoolFalse: | |
| 460 case kBoolTrue: | |
| 461 value_ = static_cast<TristateBoolValue>(value); | |
| 462 break; | |
| 463 | |
| 464 default: | |
| 465 DCHECK(false && "Unexpected tristate bool value on construction"); | |
| 466 value_ = kBoolUnset; | |
| 467 } | |
| 468 } | |
| 469 | |
| 470 /// Sets the flag to the provided value. | |
| 471 void Set(bool value); | |
| 472 | |
| 473 void operator = (bool value) { | |
| 474 Set(value); | |
| 475 } | |
| 476 | |
| 477 /// Nulls the metric and returns the current values. | |
| 478 TristateBoolValue Reset(); | |
| 479 | |
| 480 /// Returns the current value - not threadsafe | |
| 481 TristateBoolValue value() const { return value_; }; | |
| 482 | |
| 483 private: | |
| 484 DISALLOW_EVIL_CONSTRUCTORS(BoolMetric); | |
| 485 | |
| 486 TristateBoolValue value_; | |
| 487 }; | |
| 488 | |
| 489 inline CountMetric &MetricBase::AsCount() { | |
| 490 DCHECK_EQ(kCountType, type()); | |
| 491 | |
| 492 return static_cast<CountMetric&>(*this); | |
| 493 } | |
| 494 | |
| 495 inline TimingMetric &MetricBase::AsTiming() { | |
| 496 DCHECK_EQ(kTimingType, type()); | |
| 497 | |
| 498 return static_cast<TimingMetric&>(*this); | |
| 499 } | |
| 500 | |
| 501 inline IntegerMetric &MetricBase::AsInteger() { | |
| 502 DCHECK_EQ(kIntegerType, type()); | |
| 503 | |
| 504 return static_cast<IntegerMetric&>(*this); | |
| 505 } | |
| 506 | |
| 507 inline BoolMetric &MetricBase::AsBool() { | |
| 508 DCHECK_EQ(kBoolType, type()); | |
| 509 | |
| 510 return static_cast<BoolMetric&>(*this); | |
| 511 } | |
| 512 | |
| 513 inline const CountMetric &MetricBase::AsCount() const { | |
| 514 DCHECK_EQ(kCountType, type()); | |
| 515 | |
| 516 return static_cast<const CountMetric&>(*this); | |
| 517 } | |
| 518 | |
| 519 inline const TimingMetric &MetricBase::AsTiming() const { | |
| 520 DCHECK_EQ(kTimingType, type()); | |
| 521 | |
| 522 return static_cast<const TimingMetric&>(*this); | |
| 523 } | |
| 524 | |
| 525 inline const IntegerMetric &MetricBase::AsInteger() const { | |
| 526 DCHECK_EQ(kIntegerType, type()); | |
| 527 | |
| 528 return static_cast<const IntegerMetric&>(*this); | |
| 529 } | |
| 530 | |
| 531 inline const BoolMetric &MetricBase::AsBool() const { | |
| 532 DCHECK_EQ(kBoolType, type()); | |
| 533 | |
| 534 return static_cast<const BoolMetric&>(*this); | |
| 535 } | |
| 536 | |
| 537 } // namespace stats_report | |
| 538 | |
| 539 #endif // OMAHA_STATSREPORT_METRICS_H__ | |
| OLD | NEW |