Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(703)

Side by Side Diff: counter.cc

Issue 2731008: Implement a persistent storage aggregation counter class. (Closed) Base URL: ssh://git@chromiumos-git/metrics.git
Patch Set: Address kmixter's comments. Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « counter.h ('k') | counter_mock.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // 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
7 #include <sys/file.h>
8
9 #include <base/eintr_wrapper.h>
10 #include <base/logging.h>
11
12 namespace chromeos_metrics {
13
14 // TaggedCounter::Record implementation.
15 void TaggedCounter::Record::Init(int tag, int count) {
16 tag_ = tag;
17 count_ = (count > 0) ? count : 0;
18 }
19
20 void TaggedCounter::Record::Add(int count) {
21 if (count <= 0)
22 return;
23
24 count_ += count;
25
26 // Saturates on postive overflow.
27 if (count_ < 0) {
28 count_ = INT_MAX;
29 }
30 }
31
32 // TaggedCounter implementation.
33 TaggedCounter::TaggedCounter()
34 : filename_(NULL), reporter_(NULL), reporter_handle_(NULL),
35 record_state_(kRecordInvalid) {}
36
37 TaggedCounter::~TaggedCounter() {}
38
39 void TaggedCounter::Init(const char* filename,
40 Reporter reporter, void* reporter_handle) {
41 DCHECK(filename);
42 filename_ = filename;
43 reporter_ = reporter;
44 reporter_handle_ = reporter_handle;
45 record_state_ = kRecordInvalid;
46 }
47
48 void TaggedCounter::Update(int tag, int count) {
49 UpdateInternal(tag,
50 count,
51 false); // No flush.
52 }
53
54 void TaggedCounter::Flush() {
55 UpdateInternal(0, // tag
56 0, // count
57 true); // Do flush.
58 }
59
60 void TaggedCounter::UpdateInternal(int tag, int count, bool flush) {
61 // If there's no new data and the last record in the aggregation
62 // file is with the same tag, there's nothing to do.
63 if (!flush && count <= 0 &&
64 record_state_ == kRecordValid && record_.tag() == tag)
65 return;
66
67 DLOG(INFO) << "tag: " << tag << " count: " << count << " flush: " << flush;
68 DCHECK(filename_);
69
70 // NOTE: The assumption is that this TaggedCounter object is the
71 // sole owner of the persistent storage file so no locking is
72 // necessary.
73 int fd = HANDLE_EINTR(open(filename_, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
74 if (fd < 0) {
75 PLOG(WARNING) << "Unable to open the persistent counter file";
76 return;
77 }
78
79 ReadRecord(fd);
80 ReportRecord(tag, flush);
81 UpdateRecord(tag, count);
82 WriteRecord(fd);
83
84 HANDLE_EINTR(close(fd));
85 }
86
87 void TaggedCounter::ReadRecord(int fd) {
88 if (record_state_ != kRecordInvalid)
89 return;
90
91 if (HANDLE_EINTR(read(fd, &record_, sizeof(record_))) == sizeof(record_)) {
92 if (record_.count() > 0) {
93 record_state_ = kRecordValid;
94 return;
95 }
96 // This shouldn't happen normally unless somebody messed with the
97 // persistent storage file.
98 NOTREACHED();
99 record_state_ = kRecordNullDirty;
100 return;
101 }
102
103 record_state_ = kRecordNull;
104 }
105
106 void TaggedCounter::ReportRecord(int tag, bool flush) {
107 // If no valid record, there's nothing to report.
108 if (record_state_ != kRecordValid) {
109 DCHECK(record_state_ == kRecordNull);
110 return;
111 }
112
113 // If the current record has the same tag as the new tag, it's not
114 // ready to be reported yet.
115 if (!flush && record_.tag() == tag)
116 return;
117
118 if (reporter_) {
119 reporter_(reporter_handle_, record_.tag(), record_.count());
120 }
121 record_state_ = kRecordNullDirty;
122 }
123
124 void TaggedCounter::UpdateRecord(int tag, int count) {
125 if (count <= 0)
126 return;
127
128 switch (record_state_) {
129 case kRecordNull:
130 case kRecordNullDirty:
131 // Current record is null, starting a new record.
132 record_.Init(tag, count);
133 record_state_ = kRecordValidDirty;
134 break;
135
136 case kRecordValid:
137 // If there's an existing record for the current tag,
138 // accumulates the counts.
139 DCHECK_EQ(record_.tag(), tag);
140 record_.Add(count);
141 record_state_ = kRecordValidDirty;
142 break;
143
144 default:
145 NOTREACHED();
146 }
147 }
148
149 void TaggedCounter::WriteRecord(int fd) {
150 switch (record_state_) {
151 case kRecordNullDirty:
152 // Truncates the aggregation file to discard the record.
153 PLOG_IF(WARNING, HANDLE_EINTR(ftruncate(fd, 0)) != 0);
154 record_state_ = kRecordNull;
155 break;
156
157 case kRecordValidDirty:
158 // Updates the accumulator record in the file if there's new data.
159 PLOG_IF(WARNING, HANDLE_EINTR(lseek(fd, 0, SEEK_SET)) != 0);
160 PLOG_IF(WARNING,
161 HANDLE_EINTR(write(fd, &record_, sizeof(record_))) !=
162 sizeof(record_));
163 record_state_ = kRecordValid;
164 break;
165
166 case kRecordNull:
167 case kRecordValid:
168 // Nothing to do.
169 break;
170
171 default:
172 NOTREACHED();
173 }
174 }
175
176 } // namespace chromeos_metrics
OLDNEW
« no previous file with comments | « counter.h ('k') | counter_mock.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698