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