xref: /aosp_15_r20/external/libchrome/base/metrics/sample_vector.cc (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
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