1*635a8641SAndroid Build Coastguard Worker // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2*635a8641SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*635a8641SAndroid Build Coastguard Worker // found in the LICENSE file.
4*635a8641SAndroid Build Coastguard Worker
5*635a8641SAndroid Build Coastguard Worker #include "base/metrics/sample_vector.h"
6*635a8641SAndroid Build Coastguard Worker
7*635a8641SAndroid Build Coastguard Worker #include "base/lazy_instance.h"
8*635a8641SAndroid Build Coastguard Worker #include "base/logging.h"
9*635a8641SAndroid Build Coastguard Worker #include "base/memory/ptr_util.h"
10*635a8641SAndroid Build Coastguard Worker #include "base/metrics/persistent_memory_allocator.h"
11*635a8641SAndroid Build Coastguard Worker #include "base/numerics/safe_conversions.h"
12*635a8641SAndroid Build Coastguard Worker #include "base/synchronization/lock.h"
13*635a8641SAndroid Build Coastguard Worker #include "base/threading/platform_thread.h"
14*635a8641SAndroid Build Coastguard Worker
15*635a8641SAndroid Build Coastguard Worker // This SampleVector makes use of the single-sample embedded in the base
16*635a8641SAndroid Build Coastguard Worker // HistogramSamples class. If the count is non-zero then there is guaranteed
17*635a8641SAndroid Build Coastguard Worker // (within the bounds of "eventual consistency") to be no allocated external
18*635a8641SAndroid Build Coastguard Worker // storage. Once the full counts storage is allocated, the single-sample must
19*635a8641SAndroid Build Coastguard Worker // be extracted and disabled.
20*635a8641SAndroid Build Coastguard Worker
21*635a8641SAndroid Build Coastguard Worker namespace base {
22*635a8641SAndroid Build Coastguard Worker
23*635a8641SAndroid Build Coastguard Worker typedef HistogramBase::Count Count;
24*635a8641SAndroid Build Coastguard Worker typedef HistogramBase::Sample Sample;
25*635a8641SAndroid Build Coastguard Worker
SampleVectorBase(uint64_t id,Metadata * meta,const BucketRanges * bucket_ranges)26*635a8641SAndroid Build Coastguard Worker SampleVectorBase::SampleVectorBase(uint64_t id,
27*635a8641SAndroid Build Coastguard Worker Metadata* meta,
28*635a8641SAndroid Build Coastguard Worker const BucketRanges* bucket_ranges)
29*635a8641SAndroid Build Coastguard Worker : HistogramSamples(id, meta), bucket_ranges_(bucket_ranges) {
30*635a8641SAndroid Build Coastguard Worker CHECK_GE(bucket_ranges_->bucket_count(), 1u);
31*635a8641SAndroid Build Coastguard Worker }
32*635a8641SAndroid Build Coastguard Worker
33*635a8641SAndroid Build Coastguard Worker SampleVectorBase::~SampleVectorBase() = default;
34*635a8641SAndroid Build Coastguard Worker
Accumulate(Sample value,Count count)35*635a8641SAndroid Build Coastguard Worker void SampleVectorBase::Accumulate(Sample value, Count count) {
36*635a8641SAndroid Build Coastguard Worker const size_t bucket_index = GetBucketIndex(value);
37*635a8641SAndroid Build Coastguard Worker
38*635a8641SAndroid Build Coastguard Worker // Handle the single-sample case.
39*635a8641SAndroid Build Coastguard Worker if (!counts()) {
40*635a8641SAndroid Build Coastguard Worker // Try to accumulate the parameters into the single-count entry.
41*635a8641SAndroid Build Coastguard Worker if (AccumulateSingleSample(value, count, bucket_index)) {
42*635a8641SAndroid Build Coastguard Worker // A race condition could lead to a new single-sample being accumulated
43*635a8641SAndroid Build Coastguard Worker // above just after another thread executed the MountCountsStorage below.
44*635a8641SAndroid Build Coastguard Worker // Since it is mounted, it could be mounted elsewhere and have values
45*635a8641SAndroid Build Coastguard Worker // written to it. It's not allowed to have both a single-sample and
46*635a8641SAndroid Build Coastguard Worker // entries in the counts array so move the single-sample.
47*635a8641SAndroid Build Coastguard Worker if (counts())
48*635a8641SAndroid Build Coastguard Worker MoveSingleSampleToCounts();
49*635a8641SAndroid Build Coastguard Worker return;
50*635a8641SAndroid Build Coastguard Worker }
51*635a8641SAndroid Build Coastguard Worker
52*635a8641SAndroid Build Coastguard Worker // Need real storage to store both what was in the single-sample plus the
53*635a8641SAndroid Build Coastguard Worker // parameter information.
54*635a8641SAndroid Build Coastguard Worker MountCountsStorageAndMoveSingleSample();
55*635a8641SAndroid Build Coastguard Worker }
56*635a8641SAndroid Build Coastguard Worker
57*635a8641SAndroid Build Coastguard Worker // Handle the multi-sample case.
58*635a8641SAndroid Build Coastguard Worker Count new_value =
59*635a8641SAndroid Build Coastguard Worker subtle::NoBarrier_AtomicIncrement(&counts()[bucket_index], count);
60*635a8641SAndroid Build Coastguard Worker IncreaseSumAndCount(strict_cast<int64_t>(count) * value, count);
61*635a8641SAndroid Build Coastguard Worker
62*635a8641SAndroid Build Coastguard Worker // TODO(bcwhite) Remove after crbug.com/682680.
63*635a8641SAndroid Build Coastguard Worker Count old_value = new_value - count;
64*635a8641SAndroid Build Coastguard Worker if ((new_value >= 0) != (old_value >= 0) && count > 0)
65*635a8641SAndroid Build Coastguard Worker RecordNegativeSample(SAMPLES_ACCUMULATE_OVERFLOW, count);
66*635a8641SAndroid Build Coastguard Worker }
67*635a8641SAndroid Build Coastguard Worker
GetCount(Sample value) const68*635a8641SAndroid Build Coastguard Worker Count SampleVectorBase::GetCount(Sample value) const {
69*635a8641SAndroid Build Coastguard Worker return GetCountAtIndex(GetBucketIndex(value));
70*635a8641SAndroid Build Coastguard Worker }
71*635a8641SAndroid Build Coastguard Worker
TotalCount() const72*635a8641SAndroid Build Coastguard Worker Count SampleVectorBase::TotalCount() const {
73*635a8641SAndroid Build Coastguard Worker // Handle the single-sample case.
74*635a8641SAndroid Build Coastguard Worker SingleSample sample = single_sample().Load();
75*635a8641SAndroid Build Coastguard Worker if (sample.count != 0)
76*635a8641SAndroid Build Coastguard Worker return sample.count;
77*635a8641SAndroid Build Coastguard Worker
78*635a8641SAndroid Build Coastguard Worker // Handle the multi-sample case.
79*635a8641SAndroid Build Coastguard Worker if (counts() || MountExistingCountsStorage()) {
80*635a8641SAndroid Build Coastguard Worker Count count = 0;
81*635a8641SAndroid Build Coastguard Worker size_t size = counts_size();
82*635a8641SAndroid Build Coastguard Worker const HistogramBase::AtomicCount* counts_array = counts();
83*635a8641SAndroid Build Coastguard Worker for (size_t i = 0; i < size; ++i) {
84*635a8641SAndroid Build Coastguard Worker count += subtle::NoBarrier_Load(&counts_array[i]);
85*635a8641SAndroid Build Coastguard Worker }
86*635a8641SAndroid Build Coastguard Worker return count;
87*635a8641SAndroid Build Coastguard Worker }
88*635a8641SAndroid Build Coastguard Worker
89*635a8641SAndroid Build Coastguard Worker // And the no-value case.
90*635a8641SAndroid Build Coastguard Worker return 0;
91*635a8641SAndroid Build Coastguard Worker }
92*635a8641SAndroid Build Coastguard Worker
GetCountAtIndex(size_t bucket_index) const93*635a8641SAndroid Build Coastguard Worker Count SampleVectorBase::GetCountAtIndex(size_t bucket_index) const {
94*635a8641SAndroid Build Coastguard Worker DCHECK(bucket_index < counts_size());
95*635a8641SAndroid Build Coastguard Worker
96*635a8641SAndroid Build Coastguard Worker // Handle the single-sample case.
97*635a8641SAndroid Build Coastguard Worker SingleSample sample = single_sample().Load();
98*635a8641SAndroid Build Coastguard Worker if (sample.count != 0)
99*635a8641SAndroid Build Coastguard Worker return sample.bucket == bucket_index ? sample.count : 0;
100*635a8641SAndroid Build Coastguard Worker
101*635a8641SAndroid Build Coastguard Worker // Handle the multi-sample case.
102*635a8641SAndroid Build Coastguard Worker if (counts() || MountExistingCountsStorage())
103*635a8641SAndroid Build Coastguard Worker return subtle::NoBarrier_Load(&counts()[bucket_index]);
104*635a8641SAndroid Build Coastguard Worker
105*635a8641SAndroid Build Coastguard Worker // And the no-value case.
106*635a8641SAndroid Build Coastguard Worker return 0;
107*635a8641SAndroid Build Coastguard Worker }
108*635a8641SAndroid Build Coastguard Worker
Iterator() const109*635a8641SAndroid Build Coastguard Worker std::unique_ptr<SampleCountIterator> SampleVectorBase::Iterator() const {
110*635a8641SAndroid Build Coastguard Worker // Handle the single-sample case.
111*635a8641SAndroid Build Coastguard Worker SingleSample sample = single_sample().Load();
112*635a8641SAndroid Build Coastguard Worker if (sample.count != 0) {
113*635a8641SAndroid Build Coastguard Worker return std::make_unique<SingleSampleIterator>(
114*635a8641SAndroid Build Coastguard Worker bucket_ranges_->range(sample.bucket),
115*635a8641SAndroid Build Coastguard Worker bucket_ranges_->range(sample.bucket + 1), sample.count, sample.bucket);
116*635a8641SAndroid Build Coastguard Worker }
117*635a8641SAndroid Build Coastguard Worker
118*635a8641SAndroid Build Coastguard Worker // Handle the multi-sample case.
119*635a8641SAndroid Build Coastguard Worker if (counts() || MountExistingCountsStorage()) {
120*635a8641SAndroid Build Coastguard Worker return std::make_unique<SampleVectorIterator>(counts(), counts_size(),
121*635a8641SAndroid Build Coastguard Worker bucket_ranges_);
122*635a8641SAndroid Build Coastguard Worker }
123*635a8641SAndroid Build Coastguard Worker
124*635a8641SAndroid Build Coastguard Worker // And the no-value case.
125*635a8641SAndroid Build Coastguard Worker return std::make_unique<SampleVectorIterator>(nullptr, 0, bucket_ranges_);
126*635a8641SAndroid Build Coastguard Worker }
127*635a8641SAndroid Build Coastguard Worker
AddSubtractImpl(SampleCountIterator * iter,HistogramSamples::Operator op)128*635a8641SAndroid Build Coastguard Worker bool SampleVectorBase::AddSubtractImpl(SampleCountIterator* iter,
129*635a8641SAndroid Build Coastguard Worker HistogramSamples::Operator op) {
130*635a8641SAndroid Build Coastguard Worker // Stop now if there's nothing to do.
131*635a8641SAndroid Build Coastguard Worker if (iter->Done())
132*635a8641SAndroid Build Coastguard Worker return true;
133*635a8641SAndroid Build Coastguard Worker
134*635a8641SAndroid Build Coastguard Worker // Get the first value and its index.
135*635a8641SAndroid Build Coastguard Worker HistogramBase::Sample min;
136*635a8641SAndroid Build Coastguard Worker int64_t max;
137*635a8641SAndroid Build Coastguard Worker HistogramBase::Count count;
138*635a8641SAndroid Build Coastguard Worker iter->Get(&min, &max, &count);
139*635a8641SAndroid Build Coastguard Worker size_t dest_index = GetBucketIndex(min);
140*635a8641SAndroid Build Coastguard Worker
141*635a8641SAndroid Build Coastguard Worker // The destination must be a superset of the source meaning that though the
142*635a8641SAndroid Build Coastguard Worker // incoming ranges will find an exact match, the incoming bucket-index, if
143*635a8641SAndroid Build Coastguard Worker // it exists, may be offset from the destination bucket-index. Calculate
144*635a8641SAndroid Build Coastguard Worker // that offset of the passed iterator; there are are no overflow checks
145*635a8641SAndroid Build Coastguard Worker // because 2's compliment math will work it out in the end.
146*635a8641SAndroid Build Coastguard Worker //
147*635a8641SAndroid Build Coastguard Worker // Because GetBucketIndex() always returns the same true or false result for
148*635a8641SAndroid Build Coastguard Worker // a given iterator object, |index_offset| is either set here and used below,
149*635a8641SAndroid Build Coastguard Worker // or never set and never used. The compiler doesn't know this, though, which
150*635a8641SAndroid Build Coastguard Worker // is why it's necessary to initialize it to something.
151*635a8641SAndroid Build Coastguard Worker size_t index_offset = 0;
152*635a8641SAndroid Build Coastguard Worker size_t iter_index;
153*635a8641SAndroid Build Coastguard Worker if (iter->GetBucketIndex(&iter_index))
154*635a8641SAndroid Build Coastguard Worker index_offset = dest_index - iter_index;
155*635a8641SAndroid Build Coastguard Worker if (dest_index >= counts_size())
156*635a8641SAndroid Build Coastguard Worker return false;
157*635a8641SAndroid Build Coastguard Worker
158*635a8641SAndroid Build Coastguard Worker // Post-increment. Information about the current sample is not available
159*635a8641SAndroid Build Coastguard Worker // after this point.
160*635a8641SAndroid Build Coastguard Worker iter->Next();
161*635a8641SAndroid Build Coastguard Worker
162*635a8641SAndroid Build Coastguard Worker // Single-value storage is possible if there is no counts storage and the
163*635a8641SAndroid Build Coastguard Worker // retrieved entry is the only one in the iterator.
164*635a8641SAndroid Build Coastguard Worker if (!counts()) {
165*635a8641SAndroid Build Coastguard Worker if (iter->Done()) {
166*635a8641SAndroid Build Coastguard Worker // Don't call AccumulateSingleSample because that updates sum and count
167*635a8641SAndroid Build Coastguard Worker // which was already done by the caller of this method.
168*635a8641SAndroid Build Coastguard Worker if (single_sample().Accumulate(
169*635a8641SAndroid Build Coastguard Worker dest_index, op == HistogramSamples::ADD ? count : -count)) {
170*635a8641SAndroid Build Coastguard Worker // Handle race-condition that mounted counts storage between above and
171*635a8641SAndroid Build Coastguard Worker // here.
172*635a8641SAndroid Build Coastguard Worker if (counts())
173*635a8641SAndroid Build Coastguard Worker MoveSingleSampleToCounts();
174*635a8641SAndroid Build Coastguard Worker return true;
175*635a8641SAndroid Build Coastguard Worker }
176*635a8641SAndroid Build Coastguard Worker }
177*635a8641SAndroid Build Coastguard Worker
178*635a8641SAndroid Build Coastguard Worker // The counts storage will be needed to hold the multiple incoming values.
179*635a8641SAndroid Build Coastguard Worker MountCountsStorageAndMoveSingleSample();
180*635a8641SAndroid Build Coastguard Worker }
181*635a8641SAndroid Build Coastguard Worker
182*635a8641SAndroid Build Coastguard Worker // Go through the iterator and add the counts into correct bucket.
183*635a8641SAndroid Build Coastguard Worker while (true) {
184*635a8641SAndroid Build Coastguard Worker // Ensure that the sample's min/max match the ranges min/max.
185*635a8641SAndroid Build Coastguard Worker if (min != bucket_ranges_->range(dest_index) ||
186*635a8641SAndroid Build Coastguard Worker max != bucket_ranges_->range(dest_index + 1)) {
187*635a8641SAndroid Build Coastguard Worker NOTREACHED() << "sample=" << min << "," << max
188*635a8641SAndroid Build Coastguard Worker << "; range=" << bucket_ranges_->range(dest_index) << ","
189*635a8641SAndroid Build Coastguard Worker << bucket_ranges_->range(dest_index + 1);
190*635a8641SAndroid Build Coastguard Worker return false;
191*635a8641SAndroid Build Coastguard Worker }
192*635a8641SAndroid Build Coastguard Worker
193*635a8641SAndroid Build Coastguard Worker // Sample's bucket matches exactly. Adjust count.
194*635a8641SAndroid Build Coastguard Worker subtle::NoBarrier_AtomicIncrement(
195*635a8641SAndroid Build Coastguard Worker &counts()[dest_index], op == HistogramSamples::ADD ? count : -count);
196*635a8641SAndroid Build Coastguard Worker
197*635a8641SAndroid Build Coastguard Worker // Advance to the next iterable sample. See comments above for how
198*635a8641SAndroid Build Coastguard Worker // everything works.
199*635a8641SAndroid Build Coastguard Worker if (iter->Done())
200*635a8641SAndroid Build Coastguard Worker return true;
201*635a8641SAndroid Build Coastguard Worker iter->Get(&min, &max, &count);
202*635a8641SAndroid Build Coastguard Worker if (iter->GetBucketIndex(&iter_index)) {
203*635a8641SAndroid Build Coastguard Worker // Destination bucket is a known offset from the source bucket.
204*635a8641SAndroid Build Coastguard Worker dest_index = iter_index + index_offset;
205*635a8641SAndroid Build Coastguard Worker } else {
206*635a8641SAndroid Build Coastguard Worker // Destination bucket has to be determined anew each time.
207*635a8641SAndroid Build Coastguard Worker dest_index = GetBucketIndex(min);
208*635a8641SAndroid Build Coastguard Worker }
209*635a8641SAndroid Build Coastguard Worker if (dest_index >= counts_size())
210*635a8641SAndroid Build Coastguard Worker return false;
211*635a8641SAndroid Build Coastguard Worker iter->Next();
212*635a8641SAndroid Build Coastguard Worker }
213*635a8641SAndroid Build Coastguard Worker }
214*635a8641SAndroid Build Coastguard Worker
215*635a8641SAndroid Build Coastguard Worker // Use simple binary search. This is very general, but there are better
216*635a8641SAndroid Build Coastguard Worker // approaches if we knew that the buckets were linearly distributed.
GetBucketIndex(Sample value) const217*635a8641SAndroid Build Coastguard Worker size_t SampleVectorBase::GetBucketIndex(Sample value) const {
218*635a8641SAndroid Build Coastguard Worker size_t bucket_count = bucket_ranges_->bucket_count();
219*635a8641SAndroid Build Coastguard Worker CHECK_GE(bucket_count, 1u);
220*635a8641SAndroid Build Coastguard Worker CHECK_GE(value, bucket_ranges_->range(0));
221*635a8641SAndroid Build Coastguard Worker CHECK_LT(value, bucket_ranges_->range(bucket_count));
222*635a8641SAndroid Build Coastguard Worker
223*635a8641SAndroid Build Coastguard Worker size_t under = 0;
224*635a8641SAndroid Build Coastguard Worker size_t over = bucket_count;
225*635a8641SAndroid Build Coastguard Worker size_t mid;
226*635a8641SAndroid Build Coastguard Worker do {
227*635a8641SAndroid Build Coastguard Worker DCHECK_GE(over, under);
228*635a8641SAndroid Build Coastguard Worker mid = under + (over - under)/2;
229*635a8641SAndroid Build Coastguard Worker if (mid == under)
230*635a8641SAndroid Build Coastguard Worker break;
231*635a8641SAndroid Build Coastguard Worker if (bucket_ranges_->range(mid) <= value)
232*635a8641SAndroid Build Coastguard Worker under = mid;
233*635a8641SAndroid Build Coastguard Worker else
234*635a8641SAndroid Build Coastguard Worker over = mid;
235*635a8641SAndroid Build Coastguard Worker } while (true);
236*635a8641SAndroid Build Coastguard Worker
237*635a8641SAndroid Build Coastguard Worker DCHECK_LE(bucket_ranges_->range(mid), value);
238*635a8641SAndroid Build Coastguard Worker CHECK_GT(bucket_ranges_->range(mid + 1), value);
239*635a8641SAndroid Build Coastguard Worker return mid;
240*635a8641SAndroid Build Coastguard Worker }
241*635a8641SAndroid Build Coastguard Worker
MoveSingleSampleToCounts()242*635a8641SAndroid Build Coastguard Worker void SampleVectorBase::MoveSingleSampleToCounts() {
243*635a8641SAndroid Build Coastguard Worker DCHECK(counts());
244*635a8641SAndroid Build Coastguard Worker
245*635a8641SAndroid Build Coastguard Worker // Disable the single-sample since there is now counts storage for the data.
246*635a8641SAndroid Build Coastguard Worker SingleSample sample = single_sample().Extract(/*disable=*/true);
247*635a8641SAndroid Build Coastguard Worker
248*635a8641SAndroid Build Coastguard Worker // Stop here if there is no "count" as trying to find the bucket index of
249*635a8641SAndroid Build Coastguard Worker // an invalid (including zero) "value" will crash.
250*635a8641SAndroid Build Coastguard Worker if (sample.count == 0)
251*635a8641SAndroid Build Coastguard Worker return;
252*635a8641SAndroid Build Coastguard Worker
253*635a8641SAndroid Build Coastguard Worker // Move the value into storage. Sum and redundant-count already account
254*635a8641SAndroid Build Coastguard Worker // for this entry so no need to call IncreaseSumAndCount().
255*635a8641SAndroid Build Coastguard Worker subtle::NoBarrier_AtomicIncrement(&counts()[sample.bucket], sample.count);
256*635a8641SAndroid Build Coastguard Worker }
257*635a8641SAndroid Build Coastguard Worker
MountCountsStorageAndMoveSingleSample()258*635a8641SAndroid Build Coastguard Worker void SampleVectorBase::MountCountsStorageAndMoveSingleSample() {
259*635a8641SAndroid Build Coastguard Worker // There are many SampleVector objects and the lock is needed very
260*635a8641SAndroid Build Coastguard Worker // infrequently (just when advancing from single-sample to multi-sample) so
261*635a8641SAndroid Build Coastguard Worker // define a single, global lock that all can use. This lock only prevents
262*635a8641SAndroid Build Coastguard Worker // concurrent entry into the code below; access and updates to |counts_|
263*635a8641SAndroid Build Coastguard Worker // still requires atomic operations.
264*635a8641SAndroid Build Coastguard Worker static LazyInstance<Lock>::Leaky counts_lock = LAZY_INSTANCE_INITIALIZER;
265*635a8641SAndroid Build Coastguard Worker if (subtle::NoBarrier_Load(&counts_) == 0) {
266*635a8641SAndroid Build Coastguard Worker AutoLock lock(counts_lock.Get());
267*635a8641SAndroid Build Coastguard Worker if (subtle::NoBarrier_Load(&counts_) == 0) {
268*635a8641SAndroid Build Coastguard Worker // Create the actual counts storage while the above lock is acquired.
269*635a8641SAndroid Build Coastguard Worker HistogramBase::Count* counts = CreateCountsStorageWhileLocked();
270*635a8641SAndroid Build Coastguard Worker DCHECK(counts);
271*635a8641SAndroid Build Coastguard Worker
272*635a8641SAndroid Build Coastguard Worker // Point |counts_| to the newly created storage. This is done while
273*635a8641SAndroid Build Coastguard Worker // locked to prevent possible concurrent calls to CreateCountsStorage
274*635a8641SAndroid Build Coastguard Worker // but, between that call and here, other threads could notice the
275*635a8641SAndroid Build Coastguard Worker // existence of the storage and race with this to set_counts(). That's
276*635a8641SAndroid Build Coastguard Worker // okay because (a) it's atomic and (b) it always writes the same value.
277*635a8641SAndroid Build Coastguard Worker set_counts(counts);
278*635a8641SAndroid Build Coastguard Worker }
279*635a8641SAndroid Build Coastguard Worker }
280*635a8641SAndroid Build Coastguard Worker
281*635a8641SAndroid Build Coastguard Worker // Move any single-sample into the newly mounted storage.
282*635a8641SAndroid Build Coastguard Worker MoveSingleSampleToCounts();
283*635a8641SAndroid Build Coastguard Worker }
284*635a8641SAndroid Build Coastguard Worker
SampleVector(const BucketRanges * bucket_ranges)285*635a8641SAndroid Build Coastguard Worker SampleVector::SampleVector(const BucketRanges* bucket_ranges)
286*635a8641SAndroid Build Coastguard Worker : SampleVector(0, bucket_ranges) {}
287*635a8641SAndroid Build Coastguard Worker
SampleVector(uint64_t id,const BucketRanges * bucket_ranges)288*635a8641SAndroid Build Coastguard Worker SampleVector::SampleVector(uint64_t id, const BucketRanges* bucket_ranges)
289*635a8641SAndroid Build Coastguard Worker : SampleVectorBase(id, new LocalMetadata(), bucket_ranges) {}
290*635a8641SAndroid Build Coastguard Worker
~SampleVector()291*635a8641SAndroid Build Coastguard Worker SampleVector::~SampleVector() {
292*635a8641SAndroid Build Coastguard Worker delete static_cast<LocalMetadata*>(meta());
293*635a8641SAndroid Build Coastguard Worker }
294*635a8641SAndroid Build Coastguard Worker
MountExistingCountsStorage() const295*635a8641SAndroid Build Coastguard Worker bool SampleVector::MountExistingCountsStorage() const {
296*635a8641SAndroid Build Coastguard Worker // There is never any existing storage other than what is already in use.
297*635a8641SAndroid Build Coastguard Worker return counts() != nullptr;
298*635a8641SAndroid Build Coastguard Worker }
299*635a8641SAndroid Build Coastguard Worker
CreateCountsStorageWhileLocked()300*635a8641SAndroid Build Coastguard Worker HistogramBase::AtomicCount* SampleVector::CreateCountsStorageWhileLocked() {
301*635a8641SAndroid Build Coastguard Worker local_counts_.resize(counts_size());
302*635a8641SAndroid Build Coastguard Worker return &local_counts_[0];
303*635a8641SAndroid Build Coastguard Worker }
304*635a8641SAndroid Build Coastguard Worker
PersistentSampleVector(uint64_t id,const BucketRanges * bucket_ranges,Metadata * meta,const DelayedPersistentAllocation & counts)305*635a8641SAndroid Build Coastguard Worker PersistentSampleVector::PersistentSampleVector(
306*635a8641SAndroid Build Coastguard Worker uint64_t id,
307*635a8641SAndroid Build Coastguard Worker const BucketRanges* bucket_ranges,
308*635a8641SAndroid Build Coastguard Worker Metadata* meta,
309*635a8641SAndroid Build Coastguard Worker const DelayedPersistentAllocation& counts)
310*635a8641SAndroid Build Coastguard Worker : SampleVectorBase(id, meta, bucket_ranges), persistent_counts_(counts) {
311*635a8641SAndroid Build Coastguard Worker // Only mount the full storage if the single-sample has been disabled.
312*635a8641SAndroid Build Coastguard Worker // Otherwise, it is possible for this object instance to start using (empty)
313*635a8641SAndroid Build Coastguard Worker // storage that was created incidentally while another instance continues to
314*635a8641SAndroid Build Coastguard Worker // update to the single sample. This "incidental creation" can happen because
315*635a8641SAndroid Build Coastguard Worker // the memory is a DelayedPersistentAllocation which allows multiple memory
316*635a8641SAndroid Build Coastguard Worker // blocks within it and applies an all-or-nothing approach to the allocation.
317*635a8641SAndroid Build Coastguard Worker // Thus, a request elsewhere for one of the _other_ blocks would make _this_
318*635a8641SAndroid Build Coastguard Worker // block available even though nothing has explicitly requested it.
319*635a8641SAndroid Build Coastguard Worker //
320*635a8641SAndroid Build Coastguard Worker // Note that it's not possible for the ctor to mount existing storage and
321*635a8641SAndroid Build Coastguard Worker // move any single-sample to it because sometimes the persistent memory is
322*635a8641SAndroid Build Coastguard Worker // read-only. Only non-const methods (which assume that memory is read/write)
323*635a8641SAndroid Build Coastguard Worker // can do that.
324*635a8641SAndroid Build Coastguard Worker if (single_sample().IsDisabled()) {
325*635a8641SAndroid Build Coastguard Worker bool success = MountExistingCountsStorage();
326*635a8641SAndroid Build Coastguard Worker DCHECK(success);
327*635a8641SAndroid Build Coastguard Worker }
328*635a8641SAndroid Build Coastguard Worker }
329*635a8641SAndroid Build Coastguard Worker
330*635a8641SAndroid Build Coastguard Worker PersistentSampleVector::~PersistentSampleVector() = default;
331*635a8641SAndroid Build Coastguard Worker
MountExistingCountsStorage() const332*635a8641SAndroid Build Coastguard Worker bool PersistentSampleVector::MountExistingCountsStorage() const {
333*635a8641SAndroid Build Coastguard Worker // There is no early exit if counts is not yet mounted because, given that
334*635a8641SAndroid Build Coastguard Worker // this is a virtual function, it's more efficient to do that at the call-
335*635a8641SAndroid Build Coastguard Worker // site. There is no danger, however, should this get called anyway (perhaps
336*635a8641SAndroid Build Coastguard Worker // because of a race condition) because at worst the |counts_| value would
337*635a8641SAndroid Build Coastguard Worker // be over-written (in an atomic manner) with the exact same address.
338*635a8641SAndroid Build Coastguard Worker
339*635a8641SAndroid Build Coastguard Worker if (!persistent_counts_.reference())
340*635a8641SAndroid Build Coastguard Worker return false; // Nothing to mount.
341*635a8641SAndroid Build Coastguard Worker
342*635a8641SAndroid Build Coastguard Worker // Mount the counts array in position.
343*635a8641SAndroid Build Coastguard Worker set_counts(
344*635a8641SAndroid Build Coastguard Worker static_cast<HistogramBase::AtomicCount*>(persistent_counts_.Get()));
345*635a8641SAndroid Build Coastguard Worker
346*635a8641SAndroid Build Coastguard Worker // The above shouldn't fail but can if the data is corrupt or incomplete.
347*635a8641SAndroid Build Coastguard Worker return counts() != nullptr;
348*635a8641SAndroid Build Coastguard Worker }
349*635a8641SAndroid Build Coastguard Worker
350*635a8641SAndroid Build Coastguard Worker HistogramBase::AtomicCount*
CreateCountsStorageWhileLocked()351*635a8641SAndroid Build Coastguard Worker PersistentSampleVector::CreateCountsStorageWhileLocked() {
352*635a8641SAndroid Build Coastguard Worker void* mem = persistent_counts_.Get();
353*635a8641SAndroid Build Coastguard Worker if (!mem) {
354*635a8641SAndroid Build Coastguard Worker // The above shouldn't fail but can if Bad Things(tm) are occurring in the
355*635a8641SAndroid Build Coastguard Worker // persistent allocator. Crashing isn't a good option so instead just
356*635a8641SAndroid Build Coastguard Worker // allocate something from the heap and return that. There will be no
357*635a8641SAndroid Build Coastguard Worker // sharing or persistence but worse things are already happening.
358*635a8641SAndroid Build Coastguard Worker return new HistogramBase::AtomicCount[counts_size()];
359*635a8641SAndroid Build Coastguard Worker }
360*635a8641SAndroid Build Coastguard Worker
361*635a8641SAndroid Build Coastguard Worker return static_cast<HistogramBase::AtomicCount*>(mem);
362*635a8641SAndroid Build Coastguard Worker }
363*635a8641SAndroid Build Coastguard Worker
SampleVectorIterator(const std::vector<HistogramBase::AtomicCount> * counts,const BucketRanges * bucket_ranges)364*635a8641SAndroid Build Coastguard Worker SampleVectorIterator::SampleVectorIterator(
365*635a8641SAndroid Build Coastguard Worker const std::vector<HistogramBase::AtomicCount>* counts,
366*635a8641SAndroid Build Coastguard Worker const BucketRanges* bucket_ranges)
367*635a8641SAndroid Build Coastguard Worker : counts_(&(*counts)[0]),
368*635a8641SAndroid Build Coastguard Worker counts_size_(counts->size()),
369*635a8641SAndroid Build Coastguard Worker bucket_ranges_(bucket_ranges),
370*635a8641SAndroid Build Coastguard Worker index_(0) {
371*635a8641SAndroid Build Coastguard Worker DCHECK_GE(bucket_ranges_->bucket_count(), counts_size_);
372*635a8641SAndroid Build Coastguard Worker SkipEmptyBuckets();
373*635a8641SAndroid Build Coastguard Worker }
374*635a8641SAndroid Build Coastguard Worker
SampleVectorIterator(const HistogramBase::AtomicCount * counts,size_t counts_size,const BucketRanges * bucket_ranges)375*635a8641SAndroid Build Coastguard Worker SampleVectorIterator::SampleVectorIterator(
376*635a8641SAndroid Build Coastguard Worker const HistogramBase::AtomicCount* counts,
377*635a8641SAndroid Build Coastguard Worker size_t counts_size,
378*635a8641SAndroid Build Coastguard Worker const BucketRanges* bucket_ranges)
379*635a8641SAndroid Build Coastguard Worker : counts_(counts),
380*635a8641SAndroid Build Coastguard Worker counts_size_(counts_size),
381*635a8641SAndroid Build Coastguard Worker bucket_ranges_(bucket_ranges),
382*635a8641SAndroid Build Coastguard Worker index_(0) {
383*635a8641SAndroid Build Coastguard Worker DCHECK_GE(bucket_ranges_->bucket_count(), counts_size_);
384*635a8641SAndroid Build Coastguard Worker SkipEmptyBuckets();
385*635a8641SAndroid Build Coastguard Worker }
386*635a8641SAndroid Build Coastguard Worker
387*635a8641SAndroid Build Coastguard Worker SampleVectorIterator::~SampleVectorIterator() = default;
388*635a8641SAndroid Build Coastguard Worker
Done() const389*635a8641SAndroid Build Coastguard Worker bool SampleVectorIterator::Done() const {
390*635a8641SAndroid Build Coastguard Worker return index_ >= counts_size_;
391*635a8641SAndroid Build Coastguard Worker }
392*635a8641SAndroid Build Coastguard Worker
Next()393*635a8641SAndroid Build Coastguard Worker void SampleVectorIterator::Next() {
394*635a8641SAndroid Build Coastguard Worker DCHECK(!Done());
395*635a8641SAndroid Build Coastguard Worker index_++;
396*635a8641SAndroid Build Coastguard Worker SkipEmptyBuckets();
397*635a8641SAndroid Build Coastguard Worker }
398*635a8641SAndroid Build Coastguard Worker
Get(HistogramBase::Sample * min,int64_t * max,HistogramBase::Count * count) const399*635a8641SAndroid Build Coastguard Worker void SampleVectorIterator::Get(HistogramBase::Sample* min,
400*635a8641SAndroid Build Coastguard Worker int64_t* max,
401*635a8641SAndroid Build Coastguard Worker HistogramBase::Count* count) const {
402*635a8641SAndroid Build Coastguard Worker DCHECK(!Done());
403*635a8641SAndroid Build Coastguard Worker if (min != nullptr)
404*635a8641SAndroid Build Coastguard Worker *min = bucket_ranges_->range(index_);
405*635a8641SAndroid Build Coastguard Worker if (max != nullptr)
406*635a8641SAndroid Build Coastguard Worker *max = strict_cast<int64_t>(bucket_ranges_->range(index_ + 1));
407*635a8641SAndroid Build Coastguard Worker if (count != nullptr)
408*635a8641SAndroid Build Coastguard Worker *count = subtle::NoBarrier_Load(&counts_[index_]);
409*635a8641SAndroid Build Coastguard Worker }
410*635a8641SAndroid Build Coastguard Worker
GetBucketIndex(size_t * index) const411*635a8641SAndroid Build Coastguard Worker bool SampleVectorIterator::GetBucketIndex(size_t* index) const {
412*635a8641SAndroid Build Coastguard Worker DCHECK(!Done());
413*635a8641SAndroid Build Coastguard Worker if (index != nullptr)
414*635a8641SAndroid Build Coastguard Worker *index = index_;
415*635a8641SAndroid Build Coastguard Worker return true;
416*635a8641SAndroid Build Coastguard Worker }
417*635a8641SAndroid Build Coastguard Worker
SkipEmptyBuckets()418*635a8641SAndroid Build Coastguard Worker void SampleVectorIterator::SkipEmptyBuckets() {
419*635a8641SAndroid Build Coastguard Worker if (Done())
420*635a8641SAndroid Build Coastguard Worker return;
421*635a8641SAndroid Build Coastguard Worker
422*635a8641SAndroid Build Coastguard Worker while (index_ < counts_size_) {
423*635a8641SAndroid Build Coastguard Worker if (subtle::NoBarrier_Load(&counts_[index_]) != 0)
424*635a8641SAndroid Build Coastguard Worker return;
425*635a8641SAndroid Build Coastguard Worker index_++;
426*635a8641SAndroid Build Coastguard Worker }
427*635a8641SAndroid Build Coastguard Worker }
428*635a8641SAndroid Build Coastguard Worker
429*635a8641SAndroid Build Coastguard Worker } // namespace base
430