blob: 679aaafc90d33125b759c48c189afbc246e3c388 [file] [log] [blame]
Darin Petkovf1e85e42010-06-10 15:59:53 -07001// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef METRICS_COUNTER_H_
6#define METRICS_COUNTER_H_
7
Ken Mixter4c5daa42010-08-26 18:35:06 -07008#include <string>
Ken Mixterccd84c02010-08-16 19:57:13 -07009#include <time.h>
10
Darin Petkovcd8c3172010-06-24 10:13:54 -070011#include <base/basictypes.h>
Chris Masone817016a2011-05-12 14:14:48 -070012#include <base/memory/scoped_ptr.h>
Darin Petkovf1e85e42010-06-10 15:59:53 -070013#include <gtest/gtest_prod.h> // for FRIEND_TEST
14
Ken Mixter4c5daa42010-08-26 18:35:06 -070015class MetricsLibraryInterface;
16
Darin Petkovf1e85e42010-06-10 15:59:53 -070017namespace chromeos_metrics {
18
Ken Mixterccd84c02010-08-16 19:57:13 -070019// Constants useful for frequency statistics.
20const int kSecondsPerDay = 60 * 60 * 24;
21const int kSecondsPerWeek = kSecondsPerDay * 7;
22
Luigi Semenzato859b3f02014-02-05 15:33:19 -080023// TaggedCounter maintains a persistent storage (i.e., a file) aggregation
24// counter for given tags (e.g., day, hour, version number) that survives
Darin Petkovf1e85e42010-06-10 15:59:53 -070025// system shutdowns, reboots and crashes, as well as daemon process
Luigi Semenzato859b3f02014-02-05 15:33:19 -080026// restarts. The counter object is initialized by pointing to the persistent
27// storage file and providing a callback used for reporting aggregated data.
28// The counter can then be updated with additional event counts. The
29// aggregated count is reported through the callback when the counter is
30// explicitly flushed or when data for a new tag arrives.
Darin Petkovcd8c3172010-06-24 10:13:54 -070031//
32// The primary reason for using an interface is to allow easier unit
33// testing in clients through mocking thus avoiding file access and
34// callbacks. Of course, it also enables alternative implementations
35// of the counter with additional features.
Darin Petkovf1e85e42010-06-10 15:59:53 -070036class TaggedCounterInterface {
37 public:
38 // Callback type used for reporting aggregated or flushed data.
39 // Once this callback is invoked by the counter, the reported
Darin Petkov1bb904e2010-06-16 15:58:06 -070040 // aggregated data is discarded.
Darin Petkovf1e85e42010-06-10 15:59:53 -070041 //
42 // |handle| is the |reporter_handle| pointer passed through Init.
Darin Petkovf1e85e42010-06-10 15:59:53 -070043 // |count| is aggregated count.
Luigi Semenzato859b3f02014-02-05 15:33:19 -080044 typedef void (*Reporter)(void* handle, int32 count);
Darin Petkovf1e85e42010-06-10 15:59:53 -070045
46 virtual ~TaggedCounterInterface() {}
47
Luigi Semenzato859b3f02014-02-05 15:33:19 -080048 // Adds |count| of events for the given tags. If there's an existing
49 // aggregated count for different tags, it's reported through the reporter
50 // callback, and optionally discarded, depending on whether |report_tag|
51 // changed or |reset_tag| changed.
52 virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count) = 0;
Darin Petkovf1e85e42010-06-10 15:59:53 -070053
54 // Reports the current aggregated count (if any) through the
55 // reporter callback and discards it.
56 virtual void Flush() = 0;
57};
58
59class TaggedCounter : public TaggedCounterInterface {
60 public:
61 TaggedCounter();
Ken Mixter4c5daa42010-08-26 18:35:06 -070062 virtual ~TaggedCounter();
63
64 // Initializes the counter by providing the persistent storage
65 // location |filename| and a |reporter| callback for reporting
66 // aggregated counts. |reporter_handle| is sent to the |reporter|
67 // along with the aggregated counts.
68 //
69 // NOTE: The assumption is that this object is the sole owner of the
70 // persistent storage file so no locking is currently implemented.
71 virtual void Init(const char* filename,
72 Reporter reporter, void* reporter_handle);
Darin Petkovf1e85e42010-06-10 15:59:53 -070073
74 // Implementation of interface methods.
Luigi Semenzato859b3f02014-02-05 15:33:19 -080075 virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count);
Ken Mixter4c5daa42010-08-26 18:35:06 -070076 virtual void Flush();
Darin Petkovf1e85e42010-06-10 15:59:53 -070077
78 private:
79 friend class RecordTest;
80 friend class TaggedCounterTest;
81 FRIEND_TEST(TaggedCounterTest, BadFileLocation);
82 FRIEND_TEST(TaggedCounterTest, Flush);
83 FRIEND_TEST(TaggedCounterTest, InitFromFile);
84 FRIEND_TEST(TaggedCounterTest, Update);
85
Luigi Semenzato859b3f02014-02-05 15:33:19 -080086 // The current record is cached by the counter object to
Darin Petkovf1e85e42010-06-10 15:59:53 -070087 // avoid potentially unnecessary I/O. The cached record can be in
88 // one of the following states:
89 enum RecordState {
90 kRecordInvalid, // Invalid record, sync from persistent storage needed.
91 kRecordNull, // No current record, persistent storage synced.
92 kRecordNullDirty, // No current record, persistent storage is invalid.
93 kRecordValid, // Current record valid, persistent storage synced.
94 kRecordValidDirty // Current record valid, persistent storage is invalid.
95 };
96
Luigi Semenzato859b3f02014-02-05 15:33:19 -080097 // Defines the record. Objects of this class are synced
Darin Petkovf1e85e42010-06-10 15:59:53 -070098 // with the persistent storage through binary reads/writes.
99 class Record {
100 public:
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800101 // Creates a new Record with all fields reset to 0.
102 Record() : report_tag_(0), reset_tag_(0), count_(0) {}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700103
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800104 // Initializes with |report_tag|, |reset_tag| and |count|.
105 // If |count| is negative, |count_| is set to 0.
106 void Init(uint32 report_tag, uint32 reset_tag, int32 count);
Darin Petkovf1e85e42010-06-10 15:59:53 -0700107
108 // Adds |count| to the current |count_|. Negative |count| is
109 // ignored. In case of positive overflow, |count_| is saturated to
Darin Petkovcd8c3172010-06-24 10:13:54 -0700110 // kint32max.
111 void Add(int32 count);
Darin Petkovf1e85e42010-06-10 15:59:53 -0700112
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800113 uint32 report_tag() const { return report_tag_; }
114 void set_report_tag(uint32 report_tag) { report_tag_ = report_tag; }
115 uint32 reset_tag() const { return reset_tag_; }
Darin Petkovcd8c3172010-06-24 10:13:54 -0700116 int32 count() const { return count_; }
Darin Petkovf1e85e42010-06-10 15:59:53 -0700117
118 private:
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800119 // When |report_tag_| changes, the counter is reported as a UMA sample.
120 // When |reset_tag_| changes, the counter is both reported and reset.
121 uint32 report_tag_;
122 uint32 reset_tag_;
Darin Petkovcd8c3172010-06-24 10:13:54 -0700123 int32 count_;
Darin Petkovf1e85e42010-06-10 15:59:53 -0700124 };
125
126 // Implementation of the Update and Flush methods. Goes through the
127 // necessary steps to read, report, update, and sync the aggregated
128 // record.
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800129 void UpdateInternal(uint32 report_tag,
130 uint32 reset_tag,
131 int32 count,
132 bool flush);
Darin Petkovf1e85e42010-06-10 15:59:53 -0700133
134 // If the current cached record is invalid, reads it from persistent
135 // storage specified through file descriptor |fd| and updates the
136 // cached record state to either null, or valid depending on the
137 // persistent storage contents.
138 void ReadRecord(int fd);
139
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800140 // If there's an existing valid record and either |flush| is true, or either
141 // new tag is different than the old one, reports the aggregated data through
142 // the reporter callback, and possibly resets the cached record.
143 void ReportRecord(uint32 report_tag, uint32 reset_tag, bool flush);
Darin Petkovf1e85e42010-06-10 15:59:53 -0700144
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800145 // Updates the cached record given the new tags and |count|. This
Darin Petkovf1e85e42010-06-10 15:59:53 -0700146 // method expects either a null cached record, or a valid cached
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800147 // record with the same tags as given. If |flush| is true, the method
Darin Petkov1bb904e2010-06-16 15:58:06 -0700148 // asserts that the cached record is null and returns.
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800149 void UpdateRecord(uint32 report_tag,
150 uint32 reset_tag,
151 int32 count,
152 bool flush);
Darin Petkovf1e85e42010-06-10 15:59:53 -0700153
154 // If the cached record state is dirty, updates the persistent
155 // storage specified through file descriptor |fd| and switches the
156 // record state to non-dirty.
157 void WriteRecord(int fd);
158
159 // Persistent storage file path.
Ken Mixter4c5daa42010-08-26 18:35:06 -0700160 std::string filename_;
Darin Petkovf1e85e42010-06-10 15:59:53 -0700161
162 // Aggregated data reporter callback and handle to pass-through.
163 Reporter reporter_;
164 void* reporter_handle_;
165
166 // Current cached aggregation record.
167 Record record_;
168
169 // Current cached aggregation record state.
170 RecordState record_state_;
171};
172
Ken Mixter4c5daa42010-08-26 18:35:06 -0700173// TaggedCounterReporter provides a TaggedCounterInterface which both
174// counts tagged events and reports them up through the metrics
175// library to UMA.
176class TaggedCounterReporter : public TaggedCounterInterface {
177 public:
178 TaggedCounterReporter();
179 virtual ~TaggedCounterReporter();
180
181 // Set the metrics library used by all TaggedCounterReporter
182 // instances. We assume there is only one metrics library
183 // shared amongst all reporters.
184 static void SetMetricsLibraryInterface(MetricsLibraryInterface* metrics_lib) {
185 metrics_lib_ = metrics_lib;
186 }
187
188 // Initializes the counter by providing the persistent storage
189 // location |filename|, a |histogram_name| (a linear histogram) to
190 // report to with |min|, |max|, and |buckets| attributes for the
191 // histogram.
192 virtual void Init(const char* filename,
193 const char* histogram_name,
194 int min,
195 int max,
196 int buckets);
197
198 // Implementation of interface method.
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800199 virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count) {
200 tagged_counter_->Update(report_tag, reset_tag, count);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700201 }
202 // Implementation of interface method.
203 virtual void Flush() {
204 tagged_counter_->Flush();
205 }
206
207 // Accessor functions.
208 const std::string& histogram_name() const {
209 return histogram_name_;
210 }
211
212 int min() const {
213 return min_;
214 }
215
216 int max() const {
217 return max_;
218 }
219
220 int buckets() const {
221 return buckets_;
222 }
223
224 protected:
225 friend class TaggedCounterReporterTest;
226 FRIEND_TEST(TaggedCounterReporterTest, Report);
227
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800228 static void Report(void* handle, int32 count);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700229
230 static MetricsLibraryInterface* metrics_lib_;
231 scoped_ptr<TaggedCounter> tagged_counter_;
232 std::string histogram_name_;
233 int min_;
234 int max_;
235 int buckets_;
236};
237
Ken Mixterccd84c02010-08-16 19:57:13 -0700238// FrequencyCounter uses TaggedCounter to maintain a persistent
239// storage of the number of events that occur in a given cycle
240// duration (in other words, a frequency count). For example, to
241// count the number of blips per day, initialize |cycle_duration| to
242// chromeos_metrics::kSecondsPerDay, and call Update with the number
243// of blips that happen concurrently (usually 1). Reporting of the
244// value is done through TaggedCounter's reporter function.
245class FrequencyCounter {
246 public:
247 // Create a new frequency counter.
248 FrequencyCounter();
249 virtual ~FrequencyCounter();
250
Ken Mixter4c5daa42010-08-26 18:35:06 -0700251 // Initialize a frequency counter, which is necessary before first
252 // use. |tagged_counter| is used to store the counts, its memory
253 // will be managed by this FrequencyCounter. |cycle_duration| is
254 // the number of seconds in a cycle.
255 virtual void Init(TaggedCounterInterface* tagged_counter,
Ken Mixterccd84c02010-08-16 19:57:13 -0700256 time_t cycle_duration);
257 // Record that an event occurred. |count| is the number of concurrent
258 // events that have occurred. The time is implicitly assumed to be the
259 // time of the call.
260 virtual void Update(int32 count) {
261 UpdateInternal(count, time(NULL));
262 }
263
Ken Mixter4c5daa42010-08-26 18:35:06 -0700264 // Update the frequency counter based on the current time. If a
265 // cycle has finished, this will have the effect of flushing the
266 // cycle's count, without first requiring another update to the
267 // frequency counter. The more often this is called, the lower the
268 // latency to have a new sample submitted.
269 virtual void FlushFinishedCycles() {
270 Update(0);
271 }
272
273 // Accessor function.
274 const TaggedCounterInterface& tagged_counter() const {
275 return *tagged_counter_;
276 }
277
278 time_t cycle_duration() const {
279 return cycle_duration_;
280 }
281
Ken Mixterccd84c02010-08-16 19:57:13 -0700282 private:
283 friend class FrequencyCounterTest;
284 FRIEND_TEST(FrequencyCounterTest, UpdateInternal);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700285 FRIEND_TEST(FrequencyCounterTest, GetCycleNumberForWeek);
286 FRIEND_TEST(FrequencyCounterTest, GetCycleNumberForDay);
Ken Mixterccd84c02010-08-16 19:57:13 -0700287
288 void UpdateInternal(int32 count, time_t now);
289 int32 GetCycleNumber(time_t now);
290
291 time_t cycle_duration_;
292 scoped_ptr<TaggedCounterInterface> tagged_counter_;
293};
294
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800295// VersionCounter is like a FrequencyCounter, but it exposes
296// separate "report" and "reset" tags, for counters that should
297// be reported more often than they are reset.
298class VersionCounter {
299 public:
300 VersionCounter();
301 virtual ~VersionCounter();
302
303 // Initialize a version counter, which is necessary before first use.
304 // |tagged_counter| is used to store the counts. Its memory is managed
305 // by this FrequencyCounter. |cycle_duration| is the number of seconds in a
306 // cycle.
307 virtual void Init(TaggedCounterInterface* tagged_counter,
308 time_t cycle_duration);
309 // Record that |count| events have occurred. The
310 // time is implicitly assumed to be the time of the call.
311 // The version hash is passed.
312 virtual void Update(int32 count, uint32 version_hash) {
313 UpdateInternal(count, time(NULL), version_hash);
314 }
315
316 // Reports the counter if enough time has passed, and also resets it if the
317 // version number has changed.
318 virtual void FlushOnChange(uint32 version_hash) {
319 UpdateInternal(0, time(NULL), version_hash);
320 }
321
322 // Accessor function.
323 const TaggedCounterInterface& tagged_counter() const {
324 return *tagged_counter_;
325 }
326
327 time_t cycle_duration() const {
328 return cycle_duration_;
329 }
330
331 private:
332 friend class VersionCounterTest;
333 FRIEND_TEST(VersionCounterTest, UpdateInternal);
334
335 void UpdateInternal(int32 count, time_t now, uint32 version_hash);
336 // TODO(semenzato): it's generally better to use base::TimeTicks (for
337 // monotonically-increasing timestamps) or base::Time (for wall time)
338 int32 GetCycleNumber(time_t now);
339 time_t cycle_duration_;
340 scoped_ptr<TaggedCounterInterface> tagged_counter_;
341};
342
Darin Petkovf1e85e42010-06-10 15:59:53 -0700343} // namespace chromeos_metrics
344
345#endif // METRICS_COUNTER_H_