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