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

Side by Side Diff: base/metrics/histogram_samples.cc

Issue 2811713003: Embed a single sample in histogram metadata. (Closed)
Patch Set: rebased Created 3 years, 8 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 | « base/metrics/histogram_samples.h ('k') | base/metrics/histogram_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/metrics/histogram_samples.h" 5 #include "base/metrics/histogram_samples.h"
6 6
7 #include <limits>
8
7 #include "base/compiler_specific.h" 9 #include "base/compiler_specific.h"
10 #include "base/numerics/safe_math.h"
8 #include "base/pickle.h" 11 #include "base/pickle.h"
9 12
10 namespace base { 13 namespace base {
11 14
12 namespace { 15 namespace {
13 16
17 // A shorthand constant for the max value of size_t.
18 constexpr size_t kSizeMax = std::numeric_limits<size_t>::max();
19
20 // A constant stored in an AtomicSingleSample (as_atomic) to indicate that the
21 // sample is "disabled" and no further accumulation should be done with it. The
22 // value is chosen such that it will be MAX_UINT16 for both |bucket| & |count|,
23 // and thus less likely to conflict with real use. Conflicts are explicitly
24 // handled in the code but it's worth making them as unlikely as possible.
25 constexpr int32_t kDisabledSingleSample = -1;
26
14 class SampleCountPickleIterator : public SampleCountIterator { 27 class SampleCountPickleIterator : public SampleCountIterator {
15 public: 28 public:
16 explicit SampleCountPickleIterator(PickleIterator* iter); 29 explicit SampleCountPickleIterator(PickleIterator* iter);
17 30
18 bool Done() const override; 31 bool Done() const override;
19 void Next() override; 32 void Next() override;
20 void Get(HistogramBase::Sample* min, 33 void Get(HistogramBase::Sample* min,
21 HistogramBase::Sample* max, 34 HistogramBase::Sample* max,
22 HistogramBase::Count* count) const override; 35 HistogramBase::Count* count) const override;
23 36
(...skipping 28 matching lines...) Expand all
52 HistogramBase::Sample* max, 65 HistogramBase::Sample* max,
53 HistogramBase::Count* count) const { 66 HistogramBase::Count* count) const {
54 DCHECK(!Done()); 67 DCHECK(!Done());
55 *min = min_; 68 *min = min_;
56 *max = max_; 69 *max = max_;
57 *count = count_; 70 *count = count_;
58 } 71 }
59 72
60 } // namespace 73 } // namespace
61 74
75 static_assert(sizeof(HistogramSamples::AtomicSingleSample) ==
76 sizeof(subtle::Atomic32),
77 "AtomicSingleSample isn't 32 bits");
78
79 HistogramSamples::SingleSample HistogramSamples::AtomicSingleSample::Load()
80 const {
81 AtomicSingleSample single_sample = subtle::Acquire_Load(&as_atomic);
82
83 // If the sample was extracted/disabled, it's still zero to the outside.
84 if (single_sample.as_atomic == kDisabledSingleSample)
85 single_sample.as_atomic = 0;
86
87 return single_sample.as_parts;
88 }
89
90 HistogramSamples::SingleSample HistogramSamples::AtomicSingleSample::Extract(
91 bool disable) {
92 AtomicSingleSample single_sample = subtle::NoBarrier_AtomicExchange(
93 &as_atomic, disable ? kDisabledSingleSample : 0);
94 if (single_sample.as_atomic == kDisabledSingleSample)
95 single_sample.as_atomic = 0;
96 return single_sample.as_parts;
97 }
98
99 bool HistogramSamples::AtomicSingleSample::Accumulate(
100 size_t bucket,
101 HistogramBase::Count count) {
102 if (count == 0)
103 return true;
104
105 // Convert the parameters to 16-bit variables because it's all 16-bit below.
106 if (count < std::numeric_limits<uint16_t>::min() ||
107 count > std::numeric_limits<uint16_t>::max() ||
108 bucket > std::numeric_limits<uint16_t>::max()) {
109 return false;
110 }
111 uint16_t bucket16 = static_cast<uint16_t>(bucket);
112 uint16_t count16 = static_cast<uint16_t>(count);
113
114 // A local, unshared copy of the single-sample is necessary so the parts
115 // can be manipulated without worrying about atomicity.
116 AtomicSingleSample single_sample;
117
118 bool sample_updated;
119 do {
120 subtle::Atomic32 original = subtle::Acquire_Load(&as_atomic);
121 if (original == kDisabledSingleSample)
122 return false;
123 single_sample.as_atomic = original;
124 if (single_sample.as_atomic != 0) {
125 // Only the same bucket (parameter and stored) can be counted multiple
126 // times.
127 if (single_sample.as_parts.bucket != bucket16)
128 return false;
129 } else {
130 // The |single_ sample| was zero so becomes the |bucket| parameter, the
131 // contents of which were checked above to fit in 16 bits.
132 single_sample.as_parts.bucket = bucket16;
133 }
134
135 // Update count, making sure that it doesn't overflow.
136 CheckedNumeric<uint16_t> new_count(single_sample.as_parts.count);
137 new_count += count16;
138 if (!new_count.AssignIfValid(&single_sample.as_parts.count))
139 return false;
140
141 // Don't let this become equivalent to the "disabled" value.
142 if (single_sample.as_atomic == kDisabledSingleSample)
143 return false;
144
145 // Store the updated single-sample back into memory. |existing| is what
146 // was in that memory location at the time of the call; if it doesn't
147 // match |original| then the swap didn't happen so loop again.
148 subtle::Atomic32 existing = subtle::Release_CompareAndSwap(
149 &as_atomic, original, single_sample.as_atomic);
150 sample_updated = (existing == original);
151 } while (!sample_updated);
152
153 return true;
154 }
155
156 bool HistogramSamples::AtomicSingleSample::IsDisabled() const {
157 return subtle::Acquire_Load(&as_atomic) == kDisabledSingleSample;
158 }
159
160 HistogramSamples::LocalMetadata::LocalMetadata() {
161 // This is the same way it's done for persistent metadata since no ctor
162 // is called for the data members in that case.
163 memset(this, 0, sizeof(*this));
164 }
165
62 // Don't try to delegate behavior to the constructor below that accepts a 166 // Don't try to delegate behavior to the constructor below that accepts a
63 // Matadata pointer by passing &local_meta_. Such cannot be reliably passed 167 // Matadata pointer by passing &local_meta_. Such cannot be reliably passed
64 // because it has not yet been constructed -- no member variables have; the 168 // because it has not yet been constructed -- no member variables have; the
65 // class itself is in the middle of being constructed. Using it to 169 // class itself is in the middle of being constructed. Using it to
66 // initialize meta_ is okay because the object now exists and local_meta_ 170 // initialize meta_ is okay because the object now exists and local_meta_
67 // is before meta_ in the construction order. 171 // is before meta_ in the construction order.
68 HistogramSamples::HistogramSamples(uint64_t id) 172 HistogramSamples::HistogramSamples(uint64_t id)
69 : meta_(&local_meta_) { 173 : meta_(&local_meta_) {
70 meta_->id = id; 174 meta_->id = id;
71 } 175 }
72 176
73 HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta) 177 HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta)
74 : meta_(meta) { 178 : meta_(meta) {
75 DCHECK(meta_->id == 0 || meta_->id == id); 179 DCHECK(meta_->id == 0 || meta_->id == id);
76 180
77 // It's possible that |meta| is contained in initialized, read-only memory 181 // It's possible that |meta| is contained in initialized, read-only memory
78 // so it's essential that no write be done in that case. 182 // so it's essential that no write be done in that case.
79 if (!meta_->id) 183 if (!meta_->id)
80 meta_->id = id; 184 meta_->id = id;
81 } 185 }
82 186
83 HistogramSamples::~HistogramSamples() {} 187 HistogramSamples::~HistogramSamples() {}
84 188
85 void HistogramSamples::Add(const HistogramSamples& other) { 189 void HistogramSamples::Add(const HistogramSamples& other) {
86 IncreaseSum(other.sum()); 190 IncreaseSumAndCount(other.sum(), other.redundant_count());
87 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count,
88 other.redundant_count());
89 bool success = AddSubtractImpl(other.Iterator().get(), ADD); 191 bool success = AddSubtractImpl(other.Iterator().get(), ADD);
90 DCHECK(success); 192 DCHECK(success);
91 } 193 }
92 194
93 bool HistogramSamples::AddFromPickle(PickleIterator* iter) { 195 bool HistogramSamples::AddFromPickle(PickleIterator* iter) {
94 int64_t sum; 196 int64_t sum;
95 HistogramBase::Count redundant_count; 197 HistogramBase::Count redundant_count;
96 198
97 if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count)) 199 if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count))
98 return false; 200 return false;
99 201
100 IncreaseSum(sum); 202 IncreaseSumAndCount(sum, redundant_count);
101 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count,
102 redundant_count);
103 203
104 SampleCountPickleIterator pickle_iter(iter); 204 SampleCountPickleIterator pickle_iter(iter);
105 return AddSubtractImpl(&pickle_iter, ADD); 205 return AddSubtractImpl(&pickle_iter, ADD);
106 } 206 }
107 207
108 void HistogramSamples::Subtract(const HistogramSamples& other) { 208 void HistogramSamples::Subtract(const HistogramSamples& other) {
109 IncreaseSum(-other.sum()); 209 IncreaseSumAndCount(-other.sum(), -other.redundant_count());
110 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count,
111 -other.redundant_count());
112 bool success = AddSubtractImpl(other.Iterator().get(), SUBTRACT); 210 bool success = AddSubtractImpl(other.Iterator().get(), SUBTRACT);
113 DCHECK(success); 211 DCHECK(success);
114 } 212 }
115 213
116 bool HistogramSamples::Serialize(Pickle* pickle) const { 214 bool HistogramSamples::Serialize(Pickle* pickle) const {
117 if (!pickle->WriteInt64(sum())) 215 if (!pickle->WriteInt64(sum()))
118 return false; 216 return false;
119 if (!pickle->WriteInt(redundant_count())) 217 if (!pickle->WriteInt(redundant_count()))
120 return false; 218 return false;
121 219
122 HistogramBase::Sample min; 220 HistogramBase::Sample min;
123 HistogramBase::Sample max; 221 HistogramBase::Sample max;
124 HistogramBase::Count count; 222 HistogramBase::Count count;
125 for (std::unique_ptr<SampleCountIterator> it = Iterator(); !it->Done(); 223 for (std::unique_ptr<SampleCountIterator> it = Iterator(); !it->Done();
126 it->Next()) { 224 it->Next()) {
127 it->Get(&min, &max, &count); 225 it->Get(&min, &max, &count);
128 if (!pickle->WriteInt(min) || 226 if (!pickle->WriteInt(min) ||
129 !pickle->WriteInt(max) || 227 !pickle->WriteInt(max) ||
130 !pickle->WriteInt(count)) 228 !pickle->WriteInt(count))
131 return false; 229 return false;
132 } 230 }
133 return true; 231 return true;
134 } 232 }
135 233
136 void HistogramSamples::IncreaseSum(int64_t diff) { 234 bool HistogramSamples::AccumulateSingleSample(HistogramBase::Sample value,
137 #ifdef ARCH_CPU_64_BITS 235 HistogramBase::Count count,
138 subtle::NoBarrier_AtomicIncrement(&meta_->sum, diff); 236 size_t bucket) {
139 #else 237 if (single_sample().Accumulate(bucket, count)) {
140 meta_->sum += diff; 238 // Success. Update the (separate) sum and redundant-count.
141 #endif 239 IncreaseSumAndCount(static_cast<int64_t>(value) * count, count);
240 return true;
241 }
242 return false;
142 } 243 }
143 244
144 void HistogramSamples::IncreaseRedundantCount(HistogramBase::Count diff) { 245 void HistogramSamples::IncreaseSumAndCount(int64_t sum,
145 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, diff); 246 HistogramBase::Count count) {
247 #ifdef ARCH_CPU_64_BITS
248 subtle::NoBarrier_AtomicIncrement(&meta_->sum, sum);
249 #else
250 meta_->sum += sum;
251 #endif
252 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, count);
146 } 253 }
147 254
148 SampleCountIterator::~SampleCountIterator() {} 255 SampleCountIterator::~SampleCountIterator() {}
149 256
150 bool SampleCountIterator::GetBucketIndex(size_t* index) const { 257 bool SampleCountIterator::GetBucketIndex(size_t* index) const {
151 DCHECK(!Done()); 258 DCHECK(!Done());
152 return false; 259 return false;
153 } 260 }
154 261
262 SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min,
263 HistogramBase::Sample max,
264 HistogramBase::Count count)
265 : SingleSampleIterator(min, max, count, kSizeMax) {}
266
267 SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min,
268 HistogramBase::Sample max,
269 HistogramBase::Count count,
270 size_t bucket_index)
271 : min_(min), max_(max), bucket_index_(bucket_index), count_(count) {}
272
273 SingleSampleIterator::~SingleSampleIterator() {}
274
275 bool SingleSampleIterator::Done() const {
276 return count_ == 0;
277 }
278
279 void SingleSampleIterator::Next() {
280 DCHECK(!Done());
281 count_ = 0;
282 }
283
284 void SingleSampleIterator::Get(HistogramBase::Sample* min,
285 HistogramBase::Sample* max,
286 HistogramBase::Count* count) const {
287 DCHECK(!Done());
288 if (min != nullptr)
289 *min = min_;
290 if (max != nullptr)
291 *max = max_;
292 if (count != nullptr)
293 *count = count_;
294 }
295
296 bool SingleSampleIterator::GetBucketIndex(size_t* index) const {
297 DCHECK(!Done());
298 if (bucket_index_ == kSizeMax)
299 return false;
300 *index = bucket_index_;
301 return true;
302 }
303
155 } // namespace base 304 } // namespace base
OLDNEW
« no previous file with comments | « base/metrics/histogram_samples.h ('k') | base/metrics/histogram_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698