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