blob: 95d1faf875b5a32df53ab443b2249a6e034f2e9c [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
9#include <base/eintr_wrapper.h>
10#include <base/logging.h>
Ken Mixter4c5daa42010-08-26 18:35:06 -070011#include "metrics_library.h"
Darin Petkovf1e85e42010-06-10 15:59:53 -070012
13namespace chromeos_metrics {
14
15// TaggedCounter::Record implementation.
Darin Petkovcd8c3172010-06-24 10:13:54 -070016void TaggedCounter::Record::Init(int32 tag, int32 count) {
Darin Petkovf1e85e42010-06-10 15:59:53 -070017 tag_ = tag;
18 count_ = (count > 0) ? count : 0;
19}
20
Darin Petkovcd8c3172010-06-24 10:13:54 -070021void TaggedCounter::Record::Add(int32 count) {
Darin Petkovf1e85e42010-06-10 15:59:53 -070022 if (count <= 0)
23 return;
24
Darin Petkovcd8c3172010-06-24 10:13:54 -070025 // Saturates on positive overflow.
26 int64 new_count = static_cast<int64>(count_) + static_cast<int64>(count);
27 if (new_count > kint32max)
28 count_ = kint32max;
29 else
30 count_ = static_cast<int32>(new_count);
Darin Petkovf1e85e42010-06-10 15:59:53 -070031}
32
33// TaggedCounter implementation.
34TaggedCounter::TaggedCounter()
Ken Mixter4c5daa42010-08-26 18:35:06 -070035 : reporter_(NULL),
Darin Petkovcd8c3172010-06-24 10:13:54 -070036 reporter_handle_(NULL),
Darin Petkovf1e85e42010-06-10 15:59:53 -070037 record_state_(kRecordInvalid) {}
38
39TaggedCounter::~TaggedCounter() {}
40
41void TaggedCounter::Init(const char* filename,
42 Reporter reporter, void* reporter_handle) {
43 DCHECK(filename);
44 filename_ = filename;
45 reporter_ = reporter;
46 reporter_handle_ = reporter_handle;
47 record_state_ = kRecordInvalid;
48}
49
Darin Petkovcd8c3172010-06-24 10:13:54 -070050void TaggedCounter::Update(int32 tag, int32 count) {
Darin Petkovf1e85e42010-06-10 15:59:53 -070051 UpdateInternal(tag,
52 count,
53 false); // No flush.
54}
55
56void TaggedCounter::Flush() {
57 UpdateInternal(0, // tag
58 0, // count
59 true); // Do flush.
60}
61
Darin Petkovcd8c3172010-06-24 10:13:54 -070062void TaggedCounter::UpdateInternal(int32 tag, int32 count, bool flush) {
Darin Petkov1bb904e2010-06-16 15:58:06 -070063 if (flush) {
64 // Flushing but record is null, so nothing to do.
65 if (record_state_ == kRecordNull)
66 return;
67 } else {
68 // If there's no new data and the last record in the aggregation
69 // file is with the same tag, there's nothing to do.
70 if (count <= 0 && record_state_ == kRecordValid && record_.tag() == tag)
71 return;
72 }
Darin Petkovf1e85e42010-06-10 15:59:53 -070073
74 DLOG(INFO) << "tag: " << tag << " count: " << count << " flush: " << flush;
Ken Mixter4c5daa42010-08-26 18:35:06 -070075 DCHECK(!filename_.empty());
Darin Petkovf1e85e42010-06-10 15:59:53 -070076
77 // NOTE: The assumption is that this TaggedCounter object is the
78 // sole owner of the persistent storage file so no locking is
79 // necessary.
Ken Mixter4c5daa42010-08-26 18:35:06 -070080 int fd = HANDLE_EINTR(open(filename_.c_str(),
81 O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
Darin Petkovf1e85e42010-06-10 15:59:53 -070082 if (fd < 0) {
83 PLOG(WARNING) << "Unable to open the persistent counter file";
84 return;
85 }
86
87 ReadRecord(fd);
88 ReportRecord(tag, flush);
Darin Petkov1bb904e2010-06-16 15:58:06 -070089 UpdateRecord(tag, count, flush);
Darin Petkovf1e85e42010-06-10 15:59:53 -070090 WriteRecord(fd);
91
92 HANDLE_EINTR(close(fd));
93}
94
95void TaggedCounter::ReadRecord(int fd) {
96 if (record_state_ != kRecordInvalid)
97 return;
98
99 if (HANDLE_EINTR(read(fd, &record_, sizeof(record_))) == sizeof(record_)) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700100 if (record_.count() >= 0) {
Darin Petkovf1e85e42010-06-10 15:59:53 -0700101 record_state_ = kRecordValid;
102 return;
103 }
104 // This shouldn't happen normally unless somebody messed with the
105 // persistent storage file.
106 NOTREACHED();
107 record_state_ = kRecordNullDirty;
108 return;
109 }
Darin Petkovf1e85e42010-06-10 15:59:53 -0700110 record_state_ = kRecordNull;
111}
112
Darin Petkovcd8c3172010-06-24 10:13:54 -0700113void TaggedCounter::ReportRecord(int32 tag, bool flush) {
Darin Petkovf1e85e42010-06-10 15:59:53 -0700114 // If no valid record, there's nothing to report.
115 if (record_state_ != kRecordValid) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700116 DCHECK_EQ(record_state_, kRecordNull);
Darin Petkovf1e85e42010-06-10 15:59:53 -0700117 return;
118 }
119
120 // If the current record has the same tag as the new tag, it's not
121 // ready to be reported yet.
122 if (!flush && record_.tag() == tag)
123 return;
124
125 if (reporter_) {
126 reporter_(reporter_handle_, record_.tag(), record_.count());
127 }
128 record_state_ = kRecordNullDirty;
129}
130
Darin Petkovcd8c3172010-06-24 10:13:54 -0700131void TaggedCounter::UpdateRecord(int32 tag, int32 count, bool flush) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700132 if (flush) {
133 DCHECK(record_state_ == kRecordNull || record_state_ == kRecordNullDirty);
Darin Petkovf1e85e42010-06-10 15:59:53 -0700134 return;
Darin Petkov1bb904e2010-06-16 15:58:06 -0700135 }
Darin Petkovf1e85e42010-06-10 15:59:53 -0700136
137 switch (record_state_) {
138 case kRecordNull:
139 case kRecordNullDirty:
140 // Current record is null, starting a new record.
141 record_.Init(tag, count);
142 record_state_ = kRecordValidDirty;
143 break;
144
145 case kRecordValid:
146 // If there's an existing record for the current tag,
147 // accumulates the counts.
148 DCHECK_EQ(record_.tag(), tag);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700149 if (count > 0) {
150 record_.Add(count);
151 record_state_ = kRecordValidDirty;
152 }
Darin Petkovf1e85e42010-06-10 15:59:53 -0700153 break;
154
155 default:
156 NOTREACHED();
157 }
158}
159
160void TaggedCounter::WriteRecord(int fd) {
161 switch (record_state_) {
162 case kRecordNullDirty:
163 // Truncates the aggregation file to discard the record.
164 PLOG_IF(WARNING, HANDLE_EINTR(ftruncate(fd, 0)) != 0);
165 record_state_ = kRecordNull;
166 break;
167
168 case kRecordValidDirty:
169 // Updates the accumulator record in the file if there's new data.
170 PLOG_IF(WARNING, HANDLE_EINTR(lseek(fd, 0, SEEK_SET)) != 0);
171 PLOG_IF(WARNING,
172 HANDLE_EINTR(write(fd, &record_, sizeof(record_))) !=
173 sizeof(record_));
174 record_state_ = kRecordValid;
175 break;
176
177 case kRecordNull:
178 case kRecordValid:
179 // Nothing to do.
180 break;
181
182 default:
183 NOTREACHED();
184 }
185}
186
Ken Mixter4c5daa42010-08-26 18:35:06 -0700187MetricsLibraryInterface* TaggedCounterReporter::metrics_lib_ = NULL;
188
189TaggedCounterReporter::TaggedCounterReporter()
190 : tagged_counter_(new TaggedCounter()),
191 min_(0),
192 max_(0),
193 buckets_(0) {
194}
195
196TaggedCounterReporter::~TaggedCounterReporter() {
197}
198
199void TaggedCounterReporter::Init(const char* filename,
200 const char* histogram_name,
201 int min,
202 int max,
203 int buckets) {
204 tagged_counter_->Init(filename, Report, this);
205 histogram_name_ = histogram_name;
206 min_ = min;
207 max_ = max;
208 buckets_ = buckets;
209 CHECK(min_ >= 0);
210 CHECK(max_ > min_);
211 CHECK(buckets_ > 0);
212}
213
214void TaggedCounterReporter::Report(void* handle, int32 tag, int32 count) {
215 TaggedCounterReporter* this_reporter =
216 reinterpret_cast<TaggedCounterReporter*>(handle);
217 DLOG(INFO) << "received metric: " << this_reporter->histogram_name_
218 << " " << count << " " << this_reporter->min_ << " "
219 << this_reporter->max_ << " " << this_reporter->buckets_;
220 CHECK(metrics_lib_ != NULL);
221 CHECK(this_reporter->buckets_ > 0);
222 metrics_lib_->SendToUMA(this_reporter->histogram_name_,
223 count,
224 this_reporter->min_,
225 this_reporter->max_,
226 this_reporter->buckets_);
227}
228
Ken Mixterccd84c02010-08-16 19:57:13 -0700229FrequencyCounter::FrequencyCounter() : cycle_duration_(1) {
230}
231
232FrequencyCounter::~FrequencyCounter() {
233}
234
Ken Mixter4c5daa42010-08-26 18:35:06 -0700235void FrequencyCounter::Init(TaggedCounterInterface* tagged_counter,
Ken Mixterccd84c02010-08-16 19:57:13 -0700236 time_t cycle_duration) {
Ken Mixter4c5daa42010-08-26 18:35:06 -0700237 tagged_counter_.reset(tagged_counter);
Ken Mixterccd84c02010-08-16 19:57:13 -0700238 DCHECK(cycle_duration > 0);
239 cycle_duration_ = cycle_duration;
240}
241
242void FrequencyCounter::UpdateInternal(int32 count, time_t now) {
243 DCHECK(tagged_counter_.get() != NULL);
244 tagged_counter_->Update(GetCycleNumber(now), count);
245}
246
247int32 FrequencyCounter::GetCycleNumber(time_t now) {
248 return now / cycle_duration_;
249}
250
Darin Petkovf1e85e42010-06-10 15:59:53 -0700251} // namespace chromeos_metrics