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

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, 7 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 std::unique_ptr<SampleCountIterator> it = other.Iterator(); 191 std::unique_ptr<SampleCountIterator> it = other.Iterator();
90 bool success = AddSubtractImpl(it.get(), ADD); 192 bool success = AddSubtractImpl(it.get(), ADD);
91 DCHECK(success); 193 DCHECK(success);
92 } 194 }
93 195
94 bool HistogramSamples::AddFromPickle(PickleIterator* iter) { 196 bool HistogramSamples::AddFromPickle(PickleIterator* iter) {
95 int64_t sum; 197 int64_t sum;
96 HistogramBase::Count redundant_count; 198 HistogramBase::Count redundant_count;
97 199
98 if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count)) 200 if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count))
99 return false; 201 return false;
100 202
101 IncreaseSum(sum); 203 IncreaseSumAndCount(sum, redundant_count);
102 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count,
103 redundant_count);
104 204
105 SampleCountPickleIterator pickle_iter(iter); 205 SampleCountPickleIterator pickle_iter(iter);
106 return AddSubtractImpl(&pickle_iter, ADD); 206 return AddSubtractImpl(&pickle_iter, ADD);
107 } 207 }
108 208
109 void HistogramSamples::Subtract(const HistogramSamples& other) { 209 void HistogramSamples::Subtract(const HistogramSamples& other) {
110 IncreaseSum(-other.sum()); 210 IncreaseSumAndCount(-other.sum(), -other.redundant_count());
111 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count,
112 -other.redundant_count());
113 std::unique_ptr<SampleCountIterator> it = other.Iterator(); 211 std::unique_ptr<SampleCountIterator> it = other.Iterator();
114 bool success = AddSubtractImpl(it.get(), SUBTRACT); 212 bool success = AddSubtractImpl(it.get(), SUBTRACT);
115 DCHECK(success); 213 DCHECK(success);
116 } 214 }
117 215
118 bool HistogramSamples::Serialize(Pickle* pickle) const { 216 bool HistogramSamples::Serialize(Pickle* pickle) const {
119 if (!pickle->WriteInt64(sum())) 217 if (!pickle->WriteInt64(sum()))
120 return false; 218 return false;
121 if (!pickle->WriteInt(redundant_count())) 219 if (!pickle->WriteInt(redundant_count()))
122 return false; 220 return false;
123 221
124 HistogramBase::Sample min; 222 HistogramBase::Sample min;
125 HistogramBase::Sample max; 223 HistogramBase::Sample max;
126 HistogramBase::Count count; 224 HistogramBase::Count count;
127 for (std::unique_ptr<SampleCountIterator> it = Iterator(); !it->Done(); 225 for (std::unique_ptr<SampleCountIterator> it = Iterator(); !it->Done();
128 it->Next()) { 226 it->Next()) {
129 it->Get(&min, &max, &count); 227 it->Get(&min, &max, &count);
130 if (!pickle->WriteInt(min) || 228 if (!pickle->WriteInt(min) ||
131 !pickle->WriteInt(max) || 229 !pickle->WriteInt(max) ||
132 !pickle->WriteInt(count)) 230 !pickle->WriteInt(count))
133 return false; 231 return false;
134 } 232 }
135 return true; 233 return true;
136 } 234 }
137 235
138 void HistogramSamples::IncreaseSum(int64_t diff) { 236 bool HistogramSamples::AccumulateSingleSample(HistogramBase::Sample value,
139 #ifdef ARCH_CPU_64_BITS 237 HistogramBase::Count count,
140 subtle::NoBarrier_AtomicIncrement(&meta_->sum, diff); 238 size_t bucket) {
141 #else 239 if (single_sample().Accumulate(bucket, count)) {
142 meta_->sum += diff; 240 // Success. Update the (separate) sum and redundant-count.
143 #endif 241 IncreaseSumAndCount(static_cast<int64_t>(value) * count, count);
242 return true;
243 }
244 return false;
144 } 245 }
145 246
146 void HistogramSamples::IncreaseRedundantCount(HistogramBase::Count diff) { 247 void HistogramSamples::IncreaseSumAndCount(int64_t sum,
147 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, diff); 248 HistogramBase::Count count) {
249 #ifdef ARCH_CPU_64_BITS
250 subtle::NoBarrier_AtomicIncrement(&meta_->sum, sum);
251 #else
252 meta_->sum += sum;
253 #endif
254 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, count);
148 } 255 }
149 256
150 SampleCountIterator::~SampleCountIterator() {} 257 SampleCountIterator::~SampleCountIterator() {}
151 258
152 bool SampleCountIterator::GetBucketIndex(size_t* index) const { 259 bool SampleCountIterator::GetBucketIndex(size_t* index) const {
153 DCHECK(!Done()); 260 DCHECK(!Done());
154 return false; 261 return false;
155 } 262 }
156 263
264 SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min,
265 HistogramBase::Sample max,
266 HistogramBase::Count count)
267 : SingleSampleIterator(min, max, count, kSizeMax) {}
268
269 SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min,
270 HistogramBase::Sample max,
271 HistogramBase::Count count,
272 size_t bucket_index)
273 : min_(min), max_(max), bucket_index_(bucket_index), count_(count) {}
274
275 SingleSampleIterator::~SingleSampleIterator() {}
276
277 bool SingleSampleIterator::Done() const {
278 return count_ == 0;
279 }
280
281 void SingleSampleIterator::Next() {
282 DCHECK(!Done());
283 count_ = 0;
284 }
285
286 void SingleSampleIterator::Get(HistogramBase::Sample* min,
287 HistogramBase::Sample* max,
288 HistogramBase::Count* count) const {
289 DCHECK(!Done());
290 if (min != nullptr)
291 *min = min_;
292 if (max != nullptr)
293 *max = max_;
294 if (count != nullptr)
295 *count = count_;
296 }
297
298 bool SingleSampleIterator::GetBucketIndex(size_t* index) const {
299 DCHECK(!Done());
300 if (bucket_index_ == kSizeMax)
301 return false;
302 *index = bucket_index_;
303 return true;
304 }
305
157 } // namespace base 306 } // 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