xref: /aosp_15_r20/external/cronet/base/metrics/sample_vector.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker // SampleVector implements HistogramSamples interface. It is used by all
6*6777b538SAndroid Build Coastguard Worker // Histogram based classes to store samples.
7*6777b538SAndroid Build Coastguard Worker 
8*6777b538SAndroid Build Coastguard Worker #ifndef BASE_METRICS_SAMPLE_VECTOR_H_
9*6777b538SAndroid Build Coastguard Worker #define BASE_METRICS_SAMPLE_VECTOR_H_
10*6777b538SAndroid Build Coastguard Worker 
11*6777b538SAndroid Build Coastguard Worker #include <stddef.h>
12*6777b538SAndroid Build Coastguard Worker #include <stdint.h>
13*6777b538SAndroid Build Coastguard Worker 
14*6777b538SAndroid Build Coastguard Worker #include <atomic>
15*6777b538SAndroid Build Coastguard Worker #include <memory>
16*6777b538SAndroid Build Coastguard Worker #include <string>
17*6777b538SAndroid Build Coastguard Worker #include <string_view>
18*6777b538SAndroid Build Coastguard Worker #include <vector>
19*6777b538SAndroid Build Coastguard Worker 
20*6777b538SAndroid Build Coastguard Worker #include "base/base_export.h"
21*6777b538SAndroid Build Coastguard Worker #include "base/compiler_specific.h"
22*6777b538SAndroid Build Coastguard Worker #include "base/containers/span.h"
23*6777b538SAndroid Build Coastguard Worker #include "base/gtest_prod_util.h"
24*6777b538SAndroid Build Coastguard Worker #include "base/memory/raw_ptr.h"
25*6777b538SAndroid Build Coastguard Worker #include "base/metrics/bucket_ranges.h"
26*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_base.h"
27*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_samples.h"
28*6777b538SAndroid Build Coastguard Worker #include "base/metrics/persistent_memory_allocator.h"
29*6777b538SAndroid Build Coastguard Worker 
30*6777b538SAndroid Build Coastguard Worker namespace base {
31*6777b538SAndroid Build Coastguard Worker 
32*6777b538SAndroid Build Coastguard Worker class BucketRanges;
33*6777b538SAndroid Build Coastguard Worker 
34*6777b538SAndroid Build Coastguard Worker class BASE_EXPORT SampleVectorBase : public HistogramSamples {
35*6777b538SAndroid Build Coastguard Worker  public:
36*6777b538SAndroid Build Coastguard Worker   SampleVectorBase(const SampleVectorBase&) = delete;
37*6777b538SAndroid Build Coastguard Worker   SampleVectorBase& operator=(const SampleVectorBase&) = delete;
38*6777b538SAndroid Build Coastguard Worker   ~SampleVectorBase() override;
39*6777b538SAndroid Build Coastguard Worker 
40*6777b538SAndroid Build Coastguard Worker   // HistogramSamples:
41*6777b538SAndroid Build Coastguard Worker   void Accumulate(HistogramBase::Sample value,
42*6777b538SAndroid Build Coastguard Worker                   HistogramBase::Count count) override;
43*6777b538SAndroid Build Coastguard Worker   HistogramBase::Count GetCount(HistogramBase::Sample value) const override;
44*6777b538SAndroid Build Coastguard Worker   HistogramBase::Count TotalCount() const override;
45*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<SampleCountIterator> Iterator() const override;
46*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<SampleCountIterator> ExtractingIterator() override;
47*6777b538SAndroid Build Coastguard Worker 
48*6777b538SAndroid Build Coastguard Worker   // Get count of a specific bucket.
49*6777b538SAndroid Build Coastguard Worker   HistogramBase::Count GetCountAtIndex(size_t bucket_index) const;
50*6777b538SAndroid Build Coastguard Worker 
51*6777b538SAndroid Build Coastguard Worker   // Access the bucket ranges held externally.
bucket_ranges()52*6777b538SAndroid Build Coastguard Worker   const BucketRanges* bucket_ranges() const { return bucket_ranges_; }
53*6777b538SAndroid Build Coastguard Worker 
SingleSampleForTesting()54*6777b538SAndroid Build Coastguard Worker   AtomicSingleSample* SingleSampleForTesting() { return &single_sample(); }
55*6777b538SAndroid Build Coastguard Worker 
56*6777b538SAndroid Build Coastguard Worker  protected:
57*6777b538SAndroid Build Coastguard Worker   SampleVectorBase(uint64_t id,
58*6777b538SAndroid Build Coastguard Worker                    Metadata* meta,
59*6777b538SAndroid Build Coastguard Worker                    const BucketRanges* bucket_ranges);
60*6777b538SAndroid Build Coastguard Worker   SampleVectorBase(uint64_t id,
61*6777b538SAndroid Build Coastguard Worker                    std::unique_ptr<Metadata> meta,
62*6777b538SAndroid Build Coastguard Worker                    const BucketRanges* bucket_ranges);
63*6777b538SAndroid Build Coastguard Worker 
64*6777b538SAndroid Build Coastguard Worker   bool AddSubtractImpl(
65*6777b538SAndroid Build Coastguard Worker       SampleCountIterator* iter,
66*6777b538SAndroid Build Coastguard Worker       HistogramSamples::Operator op) override;  // |op| is ADD or SUBTRACT.
67*6777b538SAndroid Build Coastguard Worker 
68*6777b538SAndroid Build Coastguard Worker   virtual size_t GetBucketIndex(HistogramBase::Sample value) const;
69*6777b538SAndroid Build Coastguard Worker 
70*6777b538SAndroid Build Coastguard Worker   // Moves the single-sample value to a mounted "counts" array.
71*6777b538SAndroid Build Coastguard Worker   void MoveSingleSampleToCounts();
72*6777b538SAndroid Build Coastguard Worker 
73*6777b538SAndroid Build Coastguard Worker   // Mounts (creating if necessary) an array of "counts" for multi-value
74*6777b538SAndroid Build Coastguard Worker   // storage.
75*6777b538SAndroid Build Coastguard Worker   void MountCountsStorageAndMoveSingleSample();
76*6777b538SAndroid Build Coastguard Worker 
77*6777b538SAndroid Build Coastguard Worker   // Mounts "counts" storage that already exists. This does not attempt to move
78*6777b538SAndroid Build Coastguard Worker   // any single-sample information to that storage as that would violate the
79*6777b538SAndroid Build Coastguard Worker   // "const" restriction that is often used to indicate read-only memory.
80*6777b538SAndroid Build Coastguard Worker   virtual bool MountExistingCountsStorage() const = 0;
81*6777b538SAndroid Build Coastguard Worker 
82*6777b538SAndroid Build Coastguard Worker   // Creates "counts" storage and returns a span to it. The span's size must
83*6777b538SAndroid Build Coastguard Worker   // be the number of counts required by the histogram. Ownership of the
84*6777b538SAndroid Build Coastguard Worker   // array remains with the called method but will never change. This must be
85*6777b538SAndroid Build Coastguard Worker   // called while some sort of lock is held to prevent reentry.
86*6777b538SAndroid Build Coastguard Worker   virtual span<HistogramBase::Count> CreateCountsStorageWhileLocked() = 0;
87*6777b538SAndroid Build Coastguard Worker 
counts()88*6777b538SAndroid Build Coastguard Worker   std::optional<span<HistogramBase::AtomicCount>> counts() {
89*6777b538SAndroid Build Coastguard Worker     HistogramBase::AtomicCount* data =
90*6777b538SAndroid Build Coastguard Worker         counts_data_.load(std::memory_order_acquire);
91*6777b538SAndroid Build Coastguard Worker     if (data == nullptr) {
92*6777b538SAndroid Build Coastguard Worker       return std::nullopt;
93*6777b538SAndroid Build Coastguard Worker     }
94*6777b538SAndroid Build Coastguard Worker     return std::make_optional(make_span(data, counts_size_));
95*6777b538SAndroid Build Coastguard Worker   }
96*6777b538SAndroid Build Coastguard Worker 
counts()97*6777b538SAndroid Build Coastguard Worker   std::optional<span<const HistogramBase::AtomicCount>> counts() const {
98*6777b538SAndroid Build Coastguard Worker     const HistogramBase::AtomicCount* data =
99*6777b538SAndroid Build Coastguard Worker         counts_data_.load(std::memory_order_acquire);
100*6777b538SAndroid Build Coastguard Worker     if (data == nullptr) {
101*6777b538SAndroid Build Coastguard Worker       return std::nullopt;
102*6777b538SAndroid Build Coastguard Worker     }
103*6777b538SAndroid Build Coastguard Worker     return std::make_optional(make_span(data, counts_size_));
104*6777b538SAndroid Build Coastguard Worker   }
105*6777b538SAndroid Build Coastguard Worker 
set_counts(span<HistogramBase::AtomicCount> counts)106*6777b538SAndroid Build Coastguard Worker   void set_counts(span<HistogramBase::AtomicCount> counts) const {
107*6777b538SAndroid Build Coastguard Worker     CHECK_EQ(counts.size(), counts_size_);
108*6777b538SAndroid Build Coastguard Worker     counts_data_.store(counts.data(), std::memory_order_release);
109*6777b538SAndroid Build Coastguard Worker   }
110*6777b538SAndroid Build Coastguard Worker 
counts_size()111*6777b538SAndroid Build Coastguard Worker   size_t counts_size() const { return counts_size_; }
112*6777b538SAndroid Build Coastguard Worker 
113*6777b538SAndroid Build Coastguard Worker  private:
114*6777b538SAndroid Build Coastguard Worker   friend class SampleVectorTest;
115*6777b538SAndroid Build Coastguard Worker   FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
116*6777b538SAndroid Build Coastguard Worker   FRIEND_TEST_ALL_PREFIXES(SharedHistogramTest, CorruptSampleCounts);
117*6777b538SAndroid Build Coastguard Worker 
118*6777b538SAndroid Build Coastguard Worker   // Returns a reference into the `counts()` array. As `counts()` may be an
119*6777b538SAndroid Build Coastguard Worker   // empty optional until the array is populated, `counts()` must be checked for
120*6777b538SAndroid Build Coastguard Worker   // having a value before calling `counts_at()`, or this method may CHECK-fail.
counts_at(size_t index)121*6777b538SAndroid Build Coastguard Worker   const HistogramBase::AtomicCount& counts_at(size_t index) const {
122*6777b538SAndroid Build Coastguard Worker     return (counts().value())[index];
123*6777b538SAndroid Build Coastguard Worker   }
counts_at(size_t index)124*6777b538SAndroid Build Coastguard Worker   HistogramBase::AtomicCount& counts_at(size_t index) {
125*6777b538SAndroid Build Coastguard Worker     return (counts().value())[index];
126*6777b538SAndroid Build Coastguard Worker   }
127*6777b538SAndroid Build Coastguard Worker 
128*6777b538SAndroid Build Coastguard Worker   // Shares the same BucketRanges with Histogram object.
129*6777b538SAndroid Build Coastguard Worker   const raw_ptr<const BucketRanges> bucket_ranges_;
130*6777b538SAndroid Build Coastguard Worker 
131*6777b538SAndroid Build Coastguard Worker   // The number of counts in the histogram. Once `counts_data_` becomes
132*6777b538SAndroid Build Coastguard Worker   // non-null, this is the number of values in the `counts_data_` array that
133*6777b538SAndroid Build Coastguard Worker   // are usable by the SampleVector.
134*6777b538SAndroid Build Coastguard Worker   const size_t counts_size_;
135*6777b538SAndroid Build Coastguard Worker 
136*6777b538SAndroid Build Coastguard Worker   // `counts_data_` is a pointer to a `HistogramBase::AtomicCount` array that is
137*6777b538SAndroid Build Coastguard Worker   // held as an atomic pointer for concurrency reasons. When combined with the
138*6777b538SAndroid Build Coastguard Worker   // single_sample held in the metadata, there are four possible states:
139*6777b538SAndroid Build Coastguard Worker   //   1) single_sample == zero, counts_ == null
140*6777b538SAndroid Build Coastguard Worker   //   2) single_sample != zero, counts_ == null
141*6777b538SAndroid Build Coastguard Worker   //   3) single_sample != zero, counts_ != null BUT IS EMPTY
142*6777b538SAndroid Build Coastguard Worker   //   4) single_sample == zero, counts_ != null and may have data
143*6777b538SAndroid Build Coastguard Worker   // Once `counts_data_` is set to a value, it can never be changed and any
144*6777b538SAndroid Build Coastguard Worker   // existing single-sample must be moved to this storage. It is mutable because
145*6777b538SAndroid Build Coastguard Worker   // changing it doesn't change the (const) data but must adapt if a non-const
146*6777b538SAndroid Build Coastguard Worker   // object causes the storage to be allocated and updated.
147*6777b538SAndroid Build Coastguard Worker   //
148*6777b538SAndroid Build Coastguard Worker   // Held as raw pointer in atomic, instead of as a span, to avoid locks. The
149*6777b538SAndroid Build Coastguard Worker   // `counts_size_` is the size of the would-be span, which is CHECKd when
150*6777b538SAndroid Build Coastguard Worker   // setting the pointer, and used to recreate a span on the way out.
151*6777b538SAndroid Build Coastguard Worker   mutable std::atomic<HistogramBase::AtomicCount*> counts_data_;
152*6777b538SAndroid Build Coastguard Worker };
153*6777b538SAndroid Build Coastguard Worker 
154*6777b538SAndroid Build Coastguard Worker // A sample vector that uses local memory for the counts array.
155*6777b538SAndroid Build Coastguard Worker class BASE_EXPORT SampleVector : public SampleVectorBase {
156*6777b538SAndroid Build Coastguard Worker  public:
157*6777b538SAndroid Build Coastguard Worker   explicit SampleVector(const BucketRanges* bucket_ranges);
158*6777b538SAndroid Build Coastguard Worker   SampleVector(uint64_t id, const BucketRanges* bucket_ranges);
159*6777b538SAndroid Build Coastguard Worker   SampleVector(const SampleVector&) = delete;
160*6777b538SAndroid Build Coastguard Worker   SampleVector& operator=(const SampleVector&) = delete;
161*6777b538SAndroid Build Coastguard Worker   ~SampleVector() override;
162*6777b538SAndroid Build Coastguard Worker 
163*6777b538SAndroid Build Coastguard Worker   // HistogramSamples:
164*6777b538SAndroid Build Coastguard Worker   bool IsDefinitelyEmpty() const override;
165*6777b538SAndroid Build Coastguard Worker 
166*6777b538SAndroid Build Coastguard Worker  private:
167*6777b538SAndroid Build Coastguard Worker   FRIEND_TEST_ALL_PREFIXES(SampleVectorTest, GetPeakBucketSize);
168*6777b538SAndroid Build Coastguard Worker 
169*6777b538SAndroid Build Coastguard Worker   // HistogramSamples:
170*6777b538SAndroid Build Coastguard Worker   std::string GetAsciiBody() const override;
171*6777b538SAndroid Build Coastguard Worker   std::string GetAsciiHeader(std::string_view histogram_name,
172*6777b538SAndroid Build Coastguard Worker                              int32_t flags) const override;
173*6777b538SAndroid Build Coastguard Worker 
174*6777b538SAndroid Build Coastguard Worker   // SampleVectorBase:
175*6777b538SAndroid Build Coastguard Worker   bool MountExistingCountsStorage() const override;
176*6777b538SAndroid Build Coastguard Worker   span<HistogramBase::Count> CreateCountsStorageWhileLocked() override;
177*6777b538SAndroid Build Coastguard Worker 
178*6777b538SAndroid Build Coastguard Worker   // Writes cumulative percentage information based on the number
179*6777b538SAndroid Build Coastguard Worker   // of past, current, and remaining bucket samples.
180*6777b538SAndroid Build Coastguard Worker   void WriteAsciiBucketContext(int64_t past,
181*6777b538SAndroid Build Coastguard Worker                                HistogramBase::Count current,
182*6777b538SAndroid Build Coastguard Worker                                int64_t remaining,
183*6777b538SAndroid Build Coastguard Worker                                uint32_t current_bucket_index,
184*6777b538SAndroid Build Coastguard Worker                                std::string* output) const;
185*6777b538SAndroid Build Coastguard Worker 
186*6777b538SAndroid Build Coastguard Worker   // Finds out how large (graphically) the largest bucket will appear to be.
187*6777b538SAndroid Build Coastguard Worker   double GetPeakBucketSize() const;
188*6777b538SAndroid Build Coastguard Worker 
bucket_count()189*6777b538SAndroid Build Coastguard Worker   size_t bucket_count() const { return bucket_ranges()->bucket_count(); }
190*6777b538SAndroid Build Coastguard Worker 
191*6777b538SAndroid Build Coastguard Worker   // Simple local storage for counts.
192*6777b538SAndroid Build Coastguard Worker   mutable std::vector<HistogramBase::AtomicCount> local_counts_;
193*6777b538SAndroid Build Coastguard Worker };
194*6777b538SAndroid Build Coastguard Worker 
195*6777b538SAndroid Build Coastguard Worker // A sample vector that uses persistent memory for the counts array.
196*6777b538SAndroid Build Coastguard Worker class BASE_EXPORT PersistentSampleVector : public SampleVectorBase {
197*6777b538SAndroid Build Coastguard Worker  public:
198*6777b538SAndroid Build Coastguard Worker   PersistentSampleVector(uint64_t id,
199*6777b538SAndroid Build Coastguard Worker                          const BucketRanges* bucket_ranges,
200*6777b538SAndroid Build Coastguard Worker                          Metadata* meta,
201*6777b538SAndroid Build Coastguard Worker                          const DelayedPersistentAllocation& counts);
202*6777b538SAndroid Build Coastguard Worker   PersistentSampleVector(const PersistentSampleVector&) = delete;
203*6777b538SAndroid Build Coastguard Worker   PersistentSampleVector& operator=(const PersistentSampleVector&) = delete;
204*6777b538SAndroid Build Coastguard Worker   ~PersistentSampleVector() override;
205*6777b538SAndroid Build Coastguard Worker 
206*6777b538SAndroid Build Coastguard Worker   // HistogramSamples:
207*6777b538SAndroid Build Coastguard Worker   bool IsDefinitelyEmpty() const override;
208*6777b538SAndroid Build Coastguard Worker 
209*6777b538SAndroid Build Coastguard Worker  private:
210*6777b538SAndroid Build Coastguard Worker   // SampleVectorBase:
211*6777b538SAndroid Build Coastguard Worker   bool MountExistingCountsStorage() const override;
212*6777b538SAndroid Build Coastguard Worker   span<HistogramBase::Count> CreateCountsStorageWhileLocked() override;
213*6777b538SAndroid Build Coastguard Worker 
214*6777b538SAndroid Build Coastguard Worker   // Persistent storage for counts.
215*6777b538SAndroid Build Coastguard Worker   DelayedPersistentAllocation persistent_counts_;
216*6777b538SAndroid Build Coastguard Worker };
217*6777b538SAndroid Build Coastguard Worker 
218*6777b538SAndroid Build Coastguard Worker }  // namespace base
219*6777b538SAndroid Build Coastguard Worker 
220*6777b538SAndroid Build Coastguard Worker #endif  // BASE_METRICS_SAMPLE_VECTOR_H_
221