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