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 |