OLD | NEW |
---|---|
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 Loading... | |
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 subtle::Atomic32 existing; | |
119 subtle::Atomic32 found; | |
Alexei Svitkine (slow)
2017/04/20 19:56:10
Nit: How about renaming found to updated_value - s
bcwhite
2017/04/20 21:18:07
I think "updated_value" could be construed as the
Alexei Svitkine (slow)
2017/04/20 22:11:34
True, updated_value isn't a great name. How about
bcwhite
2017/04/21 13:25:31
Done. I've also changed the names from existing/f
| |
120 do { | |
121 existing = subtle::Acquire_Load(&as_atomic); | |
122 if (existing == kDisabledSingleSample) | |
123 return false; | |
124 single_sample.as_atomic = existing; | |
125 if (single_sample.as_atomic != 0) { | |
126 // Only the same bucket (parameter and stored) can be counted multiple | |
127 // times. | |
128 if (single_sample.as_parts.bucket != bucket16) | |
129 return false; | |
130 } else { | |
131 // The single sample was zero so becomes the |bucket| parameter, the | |
Alexei Svitkine (slow)
2017/04/20 19:56:10
Nit: "single sample" -> "|single_sample|" to make
bcwhite
2017/04/20 21:18:07
Done.
| |
132 // contents of which where checked above to fit in 16 bits. | |
Alexei Svitkine (slow)
2017/04/20 19:56:10
Nit: where -> were
bcwhite
2017/04/20 21:18:07
Done.
| |
133 single_sample.as_parts.bucket = bucket16; | |
134 } | |
135 | |
136 // Update count, making sure that it doesn't overflow. | |
137 CheckedNumeric<uint16_t> new_count(single_sample.as_parts.count); | |
138 new_count += count16; | |
139 if (!new_count.AssignIfValid(&single_sample.as_parts.count)) | |
140 return false; | |
141 | |
142 // Store the updated single-sample back into memory. | |
143 if (single_sample.as_atomic == kDisabledSingleSample) | |
144 return false; | |
145 found = subtle::Release_CompareAndSwap(&as_atomic, existing, | |
146 single_sample.as_atomic); | |
147 } while (found != existing); | |
148 | |
149 return true; | |
150 } | |
151 | |
152 bool HistogramSamples::AtomicSingleSample::IsDisabled() const { | |
153 return subtle::Acquire_Load(&as_atomic) == kDisabledSingleSample; | |
154 } | |
155 | |
156 HistogramSamples::LocalMetadata::LocalMetadata() { | |
157 // This is the same way it's done for persistent metadata since no ctor | |
158 // is called for the data members in that case. | |
159 memset(this, 0, sizeof(*this)); | |
160 } | |
161 | |
62 // Don't try to delegate behavior to the constructor below that accepts a | 162 // 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 | 163 // Matadata pointer by passing &local_meta_. Such cannot be reliably passed |
64 // because it has not yet been constructed -- no member variables have; the | 164 // 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 | 165 // 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_ | 166 // initialize meta_ is okay because the object now exists and local_meta_ |
67 // is before meta_ in the construction order. | 167 // is before meta_ in the construction order. |
68 HistogramSamples::HistogramSamples(uint64_t id) | 168 HistogramSamples::HistogramSamples(uint64_t id) |
69 : meta_(&local_meta_) { | 169 : meta_(&local_meta_) { |
70 meta_->id = id; | 170 meta_->id = id; |
71 } | 171 } |
72 | 172 |
73 HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta) | 173 HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta) |
74 : meta_(meta) { | 174 : meta_(meta) { |
75 DCHECK(meta_->id == 0 || meta_->id == id); | 175 DCHECK(meta_->id == 0 || meta_->id == id); |
76 | 176 |
77 // It's possible that |meta| is contained in initialized, read-only memory | 177 // 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. | 178 // so it's essential that no write be done in that case. |
79 if (!meta_->id) | 179 if (!meta_->id) |
80 meta_->id = id; | 180 meta_->id = id; |
81 } | 181 } |
82 | 182 |
83 HistogramSamples::~HistogramSamples() {} | 183 HistogramSamples::~HistogramSamples() {} |
84 | 184 |
85 void HistogramSamples::Add(const HistogramSamples& other) { | 185 void HistogramSamples::Add(const HistogramSamples& other) { |
86 IncreaseSum(other.sum()); | 186 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); | 187 bool success = AddSubtractImpl(other.Iterator().get(), ADD); |
90 DCHECK(success); | 188 DCHECK(success); |
91 } | 189 } |
92 | 190 |
93 bool HistogramSamples::AddFromPickle(PickleIterator* iter) { | 191 bool HistogramSamples::AddFromPickle(PickleIterator* iter) { |
94 int64_t sum; | 192 int64_t sum; |
95 HistogramBase::Count redundant_count; | 193 HistogramBase::Count redundant_count; |
96 | 194 |
97 if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count)) | 195 if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count)) |
98 return false; | 196 return false; |
99 | 197 |
100 IncreaseSum(sum); | 198 IncreaseSumAndCount(sum, redundant_count); |
101 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, | |
102 redundant_count); | |
103 | 199 |
104 SampleCountPickleIterator pickle_iter(iter); | 200 SampleCountPickleIterator pickle_iter(iter); |
105 return AddSubtractImpl(&pickle_iter, ADD); | 201 return AddSubtractImpl(&pickle_iter, ADD); |
106 } | 202 } |
107 | 203 |
108 void HistogramSamples::Subtract(const HistogramSamples& other) { | 204 void HistogramSamples::Subtract(const HistogramSamples& other) { |
109 IncreaseSum(-other.sum()); | 205 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); | 206 bool success = AddSubtractImpl(other.Iterator().get(), SUBTRACT); |
113 DCHECK(success); | 207 DCHECK(success); |
114 } | 208 } |
115 | 209 |
116 bool HistogramSamples::Serialize(Pickle* pickle) const { | 210 bool HistogramSamples::Serialize(Pickle* pickle) const { |
117 if (!pickle->WriteInt64(sum())) | 211 if (!pickle->WriteInt64(sum())) |
118 return false; | 212 return false; |
119 if (!pickle->WriteInt(redundant_count())) | 213 if (!pickle->WriteInt(redundant_count())) |
120 return false; | 214 return false; |
121 | 215 |
122 HistogramBase::Sample min; | 216 HistogramBase::Sample min; |
123 HistogramBase::Sample max; | 217 HistogramBase::Sample max; |
124 HistogramBase::Count count; | 218 HistogramBase::Count count; |
125 for (std::unique_ptr<SampleCountIterator> it = Iterator(); !it->Done(); | 219 for (std::unique_ptr<SampleCountIterator> it = Iterator(); !it->Done(); |
126 it->Next()) { | 220 it->Next()) { |
127 it->Get(&min, &max, &count); | 221 it->Get(&min, &max, &count); |
128 if (!pickle->WriteInt(min) || | 222 if (!pickle->WriteInt(min) || |
129 !pickle->WriteInt(max) || | 223 !pickle->WriteInt(max) || |
130 !pickle->WriteInt(count)) | 224 !pickle->WriteInt(count)) |
131 return false; | 225 return false; |
132 } | 226 } |
133 return true; | 227 return true; |
134 } | 228 } |
135 | 229 |
136 void HistogramSamples::IncreaseSum(int64_t diff) { | 230 bool HistogramSamples::AccumulateSingleSample(HistogramBase::Sample value, |
137 #ifdef ARCH_CPU_64_BITS | 231 HistogramBase::Count count, |
138 subtle::NoBarrier_AtomicIncrement(&meta_->sum, diff); | 232 size_t bucket) { |
139 #else | 233 if (single_sample().Accumulate(bucket, count)) { |
140 meta_->sum += diff; | 234 // Success. Update the (separate) sum and redundant-count. |
141 #endif | 235 IncreaseSumAndCount(static_cast<int64_t>(value) * count, count); |
236 return true; | |
237 } | |
238 return false; | |
142 } | 239 } |
143 | 240 |
144 void HistogramSamples::IncreaseRedundantCount(HistogramBase::Count diff) { | 241 void HistogramSamples::IncreaseSumAndCount(int64_t sum, |
145 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, diff); | 242 HistogramBase::Count count) { |
243 #ifdef ARCH_CPU_64_BITS | |
244 subtle::NoBarrier_AtomicIncrement(&meta_->sum, sum); | |
245 #else | |
246 meta_->sum += sum; | |
247 #endif | |
248 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, count); | |
146 } | 249 } |
147 | 250 |
148 SampleCountIterator::~SampleCountIterator() {} | 251 SampleCountIterator::~SampleCountIterator() {} |
149 | 252 |
150 bool SampleCountIterator::GetBucketIndex(size_t* index) const { | 253 bool SampleCountIterator::GetBucketIndex(size_t* index) const { |
151 DCHECK(!Done()); | 254 DCHECK(!Done()); |
152 return false; | 255 return false; |
153 } | 256 } |
154 | 257 |
258 SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min, | |
259 HistogramBase::Sample max, | |
260 HistogramBase::Count count) | |
261 : SingleSampleIterator(min, max, count, kSizeMax) {} | |
262 | |
263 SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min, | |
264 HistogramBase::Sample max, | |
265 HistogramBase::Count count, | |
266 size_t bucket_index) | |
267 : min_(min), max_(max), bucket_index_(bucket_index), count_(count) {} | |
268 | |
269 SingleSampleIterator::~SingleSampleIterator() {} | |
270 | |
271 bool SingleSampleIterator::Done() const { | |
272 return count_ == 0; | |
273 } | |
274 | |
275 void SingleSampleIterator::Next() { | |
276 DCHECK(!Done()); | |
277 count_ = 0; | |
278 } | |
279 | |
280 void SingleSampleIterator::Get(HistogramBase::Sample* min, | |
281 HistogramBase::Sample* max, | |
282 HistogramBase::Count* count) const { | |
283 DCHECK(!Done()); | |
284 if (min != nullptr) | |
285 *min = min_; | |
286 if (max != nullptr) | |
287 *max = max_; | |
288 if (count != nullptr) | |
289 *count = count_; | |
290 } | |
291 | |
292 bool SingleSampleIterator::GetBucketIndex(size_t* index) const { | |
293 DCHECK(!Done()); | |
294 if (bucket_index_ == kSizeMax) | |
295 return false; | |
296 *index = bucket_index_; | |
297 return true; | |
298 } | |
299 | |
155 } // namespace base | 300 } // namespace base |
OLD | NEW |