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