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 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 |
OLD | NEW |