blob: f71b4fbf64bac37f320907512a8a38522d4da072 [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#include "counter.h"
6
Darin Petkovcd8c3172010-06-24 10:13:54 -07007#include <fcntl.h>
Darin Petkovf1e85e42010-06-10 15:59:53 -07008
Darin Petkovf1e85e42010-06-10 15:59:53 -07009#include <base/logging.h>
Chris Masonee10b5482013-02-14 12:15:35 -080010#include <base/posix/eintr_wrapper.h>
11
Ken Mixter4c5daa42010-08-26 18:35:06 -070012#include "metrics_library.h"
Darin Petkovf1e85e42010-06-10 15:59:53 -070013
14namespace chromeos_metrics {
15
16// TaggedCounter::Record implementation.
Luigi Semenzato859b3f02014-02-05 15:33:19 -080017void TaggedCounter::Record::Init(uint32 report_tag,
18 uint32 reset_tag,
19 int32 count) {
20 report_tag_ = report_tag;
21 reset_tag_ = reset_tag;
Darin Petkovf1e85e42010-06-10 15:59:53 -070022 count_ = (count > 0) ? count : 0;
23}
24
Darin Petkovcd8c3172010-06-24 10:13:54 -070025void TaggedCounter::Record::Add(int32 count) {
Darin Petkovf1e85e42010-06-10 15:59:53 -070026 if (count <= 0)
27 return;
28
Darin Petkovcd8c3172010-06-24 10:13:54 -070029 // Saturates on positive overflow.
30 int64 new_count = static_cast<int64>(count_) + static_cast<int64>(count);
31 if (new_count > kint32max)
32 count_ = kint32max;
33 else
34 count_ = static_cast<int32>(new_count);
Darin Petkovf1e85e42010-06-10 15:59:53 -070035}
36
37// TaggedCounter implementation.
38TaggedCounter::TaggedCounter()
Ken Mixter4c5daa42010-08-26 18:35:06 -070039 : reporter_(NULL),
Darin Petkovcd8c3172010-06-24 10:13:54 -070040 reporter_handle_(NULL),
Darin Petkovf1e85e42010-06-10 15:59:53 -070041 record_state_(kRecordInvalid) {}
42
43TaggedCounter::~TaggedCounter() {}
44
45void TaggedCounter::Init(const char* filename,
46 Reporter reporter, void* reporter_handle) {
47 DCHECK(filename);
48 filename_ = filename;
49 reporter_ = reporter;
50 reporter_handle_ = reporter_handle;
51 record_state_ = kRecordInvalid;
52}
53
Luigi Semenzato859b3f02014-02-05 15:33:19 -080054void TaggedCounter::Update(uint32 report_tag, uint32 reset_tag, int32 count) {
55 UpdateInternal(report_tag,
56 reset_tag,
Darin Petkovf1e85e42010-06-10 15:59:53 -070057 count,
58 false); // No flush.
59}
60
61void TaggedCounter::Flush() {
Luigi Semenzato859b3f02014-02-05 15:33:19 -080062 UpdateInternal(0, // report_tag
63 0, // reset_tag
Darin Petkovf1e85e42010-06-10 15:59:53 -070064 0, // count
65 true); // Do flush.
66}
67
Luigi Semenzato859b3f02014-02-05 15:33:19 -080068void TaggedCounter::UpdateInternal(uint32 report_tag,
69 uint32 reset_tag,
70 int32 count,
71 bool flush) {
Darin Petkov1bb904e2010-06-16 15:58:06 -070072 if (flush) {
73 // Flushing but record is null, so nothing to do.
74 if (record_state_ == kRecordNull)
75 return;
76 } else {
77 // If there's no new data and the last record in the aggregation
78 // file is with the same tag, there's nothing to do.
Luigi Semenzato859b3f02014-02-05 15:33:19 -080079 if (count <= 0 &&
80 record_state_ == kRecordValid &&
81 record_.report_tag() == report_tag &&
82 record_.reset_tag() == reset_tag)
Darin Petkov1bb904e2010-06-16 15:58:06 -070083 return;
84 }
Darin Petkovf1e85e42010-06-10 15:59:53 -070085
Luigi Semenzato859b3f02014-02-05 15:33:19 -080086 DLOG(INFO) << "report_tag: " << report_tag << " reset_tag: " << reset_tag
87 << " count: " << count << " flush: " << flush;
Ken Mixter4c5daa42010-08-26 18:35:06 -070088 DCHECK(!filename_.empty());
Darin Petkovf1e85e42010-06-10 15:59:53 -070089
90 // NOTE: The assumption is that this TaggedCounter object is the
91 // sole owner of the persistent storage file so no locking is
92 // necessary.
Ken Mixter4c5daa42010-08-26 18:35:06 -070093 int fd = HANDLE_EINTR(open(filename_.c_str(),
94 O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
Darin Petkovf1e85e42010-06-10 15:59:53 -070095 if (fd < 0) {
96 PLOG(WARNING) << "Unable to open the persistent counter file";
97 return;
98 }
Darin Petkovf1e85e42010-06-10 15:59:53 -070099 ReadRecord(fd);
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800100 ReportRecord(report_tag, reset_tag, flush);
101 UpdateRecord(report_tag, reset_tag, count, flush);
Darin Petkovf1e85e42010-06-10 15:59:53 -0700102 WriteRecord(fd);
103
104 HANDLE_EINTR(close(fd));
105}
106
107void TaggedCounter::ReadRecord(int fd) {
108 if (record_state_ != kRecordInvalid)
109 return;
110
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800111 // Three cases: 1. empty file (first time), 2. size of file == size of record
112 // (normal case), 3. size of file != size of record (new version). We treat
113 // cases 1 and 3 identically.
Darin Petkovf1e85e42010-06-10 15:59:53 -0700114 if (HANDLE_EINTR(read(fd, &record_, sizeof(record_))) == sizeof(record_)) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700115 if (record_.count() >= 0) {
Darin Petkovf1e85e42010-06-10 15:59:53 -0700116 record_state_ = kRecordValid;
117 return;
118 }
119 // This shouldn't happen normally unless somebody messed with the
120 // persistent storage file.
121 NOTREACHED();
122 record_state_ = kRecordNullDirty;
123 return;
124 }
Darin Petkovf1e85e42010-06-10 15:59:53 -0700125 record_state_ = kRecordNull;
126}
127
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800128void TaggedCounter::ReportRecord(uint32 report_tag,
129 uint32 reset_tag,
130 bool flush) {
Darin Petkovf1e85e42010-06-10 15:59:53 -0700131 // If no valid record, there's nothing to report.
132 if (record_state_ != kRecordValid) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700133 DCHECK_EQ(record_state_, kRecordNull);
Darin Petkovf1e85e42010-06-10 15:59:53 -0700134 return;
135 }
136
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800137 // If the current record has the same tags as the new ones, it's
138 // not ready to be reported yet.
139 if (!flush &&
140 record_.report_tag() == report_tag &&
141 record_.reset_tag() == reset_tag)
Darin Petkovf1e85e42010-06-10 15:59:53 -0700142 return;
143
144 if (reporter_) {
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800145 reporter_(reporter_handle_, record_.count());
Darin Petkovf1e85e42010-06-10 15:59:53 -0700146 }
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800147 // If the report tag has changed, update it to the current one.
148 if (record_.report_tag() != report_tag) {
149 record_.set_report_tag(report_tag);
150 record_state_ = kRecordValidDirty;
151 }
152 // If the reset tag has changed, the new state is NullDirty, no
153 // matter whether the report tag has changed or not.
154 if (record_.reset_tag() != reset_tag) {
155 record_state_ = kRecordNullDirty;
156 }
Darin Petkovf1e85e42010-06-10 15:59:53 -0700157}
158
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800159void TaggedCounter::UpdateRecord(uint32 report_tag,
160 uint32 reset_tag,
161 int32 count,
162 bool flush) {
163 if (flush &&
164 (record_state_ == kRecordNull || record_state_ == kRecordNullDirty))
Darin Petkovf1e85e42010-06-10 15:59:53 -0700165 return;
166
167 switch (record_state_) {
168 case kRecordNull:
169 case kRecordNullDirty:
170 // Current record is null, starting a new record.
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800171 record_.Init(report_tag, reset_tag, count);
Darin Petkovf1e85e42010-06-10 15:59:53 -0700172 record_state_ = kRecordValidDirty;
173 break;
174
175 case kRecordValid:
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800176 case kRecordValidDirty:
Darin Petkovf1e85e42010-06-10 15:59:53 -0700177 // If there's an existing record for the current tag,
178 // accumulates the counts.
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800179 DCHECK_EQ(record_.report_tag(), report_tag);
180 DCHECK_EQ(record_.reset_tag(), reset_tag);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700181 if (count > 0) {
182 record_.Add(count);
183 record_state_ = kRecordValidDirty;
184 }
Darin Petkovf1e85e42010-06-10 15:59:53 -0700185 break;
186
187 default:
188 NOTREACHED();
189 }
190}
191
192void TaggedCounter::WriteRecord(int fd) {
193 switch (record_state_) {
194 case kRecordNullDirty:
195 // Truncates the aggregation file to discard the record.
196 PLOG_IF(WARNING, HANDLE_EINTR(ftruncate(fd, 0)) != 0);
197 record_state_ = kRecordNull;
198 break;
199
200 case kRecordValidDirty:
201 // Updates the accumulator record in the file if there's new data.
202 PLOG_IF(WARNING, HANDLE_EINTR(lseek(fd, 0, SEEK_SET)) != 0);
203 PLOG_IF(WARNING,
204 HANDLE_EINTR(write(fd, &record_, sizeof(record_))) !=
205 sizeof(record_));
206 record_state_ = kRecordValid;
207 break;
208
209 case kRecordNull:
210 case kRecordValid:
211 // Nothing to do.
212 break;
213
214 default:
215 NOTREACHED();
216 }
217}
218
Ken Mixter4c5daa42010-08-26 18:35:06 -0700219MetricsLibraryInterface* TaggedCounterReporter::metrics_lib_ = NULL;
220
221TaggedCounterReporter::TaggedCounterReporter()
222 : tagged_counter_(new TaggedCounter()),
223 min_(0),
224 max_(0),
225 buckets_(0) {
226}
227
228TaggedCounterReporter::~TaggedCounterReporter() {
229}
230
231void TaggedCounterReporter::Init(const char* filename,
232 const char* histogram_name,
233 int min,
234 int max,
235 int buckets) {
236 tagged_counter_->Init(filename, Report, this);
237 histogram_name_ = histogram_name;
238 min_ = min;
239 max_ = max;
240 buckets_ = buckets;
241 CHECK(min_ >= 0);
242 CHECK(max_ > min_);
243 CHECK(buckets_ > 0);
244}
245
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800246void TaggedCounterReporter::Report(void* handle, int32 count) {
Ken Mixter4c5daa42010-08-26 18:35:06 -0700247 TaggedCounterReporter* this_reporter =
248 reinterpret_cast<TaggedCounterReporter*>(handle);
249 DLOG(INFO) << "received metric: " << this_reporter->histogram_name_
250 << " " << count << " " << this_reporter->min_ << " "
251 << this_reporter->max_ << " " << this_reporter->buckets_;
252 CHECK(metrics_lib_ != NULL);
253 CHECK(this_reporter->buckets_ > 0);
254 metrics_lib_->SendToUMA(this_reporter->histogram_name_,
255 count,
256 this_reporter->min_,
257 this_reporter->max_,
258 this_reporter->buckets_);
259}
260
Ken Mixterccd84c02010-08-16 19:57:13 -0700261FrequencyCounter::FrequencyCounter() : cycle_duration_(1) {
262}
263
264FrequencyCounter::~FrequencyCounter() {
265}
266
Ken Mixter4c5daa42010-08-26 18:35:06 -0700267void FrequencyCounter::Init(TaggedCounterInterface* tagged_counter,
Ken Mixterccd84c02010-08-16 19:57:13 -0700268 time_t cycle_duration) {
Ken Mixter4c5daa42010-08-26 18:35:06 -0700269 tagged_counter_.reset(tagged_counter);
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800270 DCHECK_GT(cycle_duration, 0);
Ken Mixterccd84c02010-08-16 19:57:13 -0700271 cycle_duration_ = cycle_duration;
272}
273
274void FrequencyCounter::UpdateInternal(int32 count, time_t now) {
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800275 DCHECK(tagged_counter_.get());
276 tagged_counter_->Update(0, GetCycleNumber(now), count);
Ken Mixterccd84c02010-08-16 19:57:13 -0700277}
278
279int32 FrequencyCounter::GetCycleNumber(time_t now) {
280 return now / cycle_duration_;
281}
282
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800283VersionCounter::VersionCounter() : cycle_duration_(1) {
284}
285
286VersionCounter::~VersionCounter() {
287}
288
289void VersionCounter::Init(TaggedCounterInterface* tagged_counter,
290 time_t cycle_duration) {
291 tagged_counter_.reset(tagged_counter);
292 DCHECK_GT(cycle_duration, 0);
293 cycle_duration_ = cycle_duration;
294}
295
296void VersionCounter::UpdateInternal(int32 count,
297 time_t now,
298 uint32 version_hash) {
299 DCHECK(tagged_counter_.get());
300 tagged_counter_->Update(GetCycleNumber(now), version_hash, count);
301}
302
303int32 VersionCounter::GetCycleNumber(time_t now) {
304 return now / cycle_duration_;
305}
306
Darin Petkovf1e85e42010-06-10 15:59:53 -0700307} // namespace chromeos_metrics