kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 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 | |
avi | 9b6f4293 | 2015-12-26 22:15:14 | [diff] [blame] | 11 | #include <stddef.h> |
bcwhite | b036e432 | 2015-12-10 18:36:34 | [diff] [blame] | 12 | #include <stdint.h> |
avi | 9b6f4293 | 2015-12-26 22:15:14 | [diff] [blame] | 13 | |
Benoit Lize | 8ff573d | 2021-04-14 11:37:05 | [diff] [blame] | 14 | #include <atomic> |
dcheng | 093de9b | 2016-04-04 21:25:51 | [diff] [blame] | 15 | #include <memory> |
Sumaid Syed | 22f60eeb | 2021-08-26 05:16:26 | [diff] [blame] | 16 | #include <string> |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 17 | #include <vector> |
| 18 | |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 19 | #include "base/compiler_specific.h" |
| 20 | #include "base/gtest_prod_util.h" |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 21 | #include "base/metrics/bucket_ranges.h" |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 22 | #include "base/metrics/histogram_base.h" |
| 23 | #include "base/metrics/histogram_samples.h" |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 24 | #include "base/metrics/persistent_memory_allocator.h" |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 25 | |
| 26 | namespace base { |
| 27 | |
| 28 | class BucketRanges; |
| 29 | |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 30 | class BASE_EXPORT SampleVectorBase : public HistogramSamples { |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 31 | public: |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 32 | SampleVectorBase(uint64_t id, |
| 33 | Metadata* meta, |
| 34 | const BucketRanges* bucket_ranges); |
David Bienvenu | f1f52448 | 2020-09-16 19:37:37 | [diff] [blame] | 35 | SampleVectorBase(const SampleVectorBase&) = delete; |
| 36 | SampleVectorBase& operator=(const SampleVectorBase&) = delete; |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 37 | ~SampleVectorBase() override; |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 38 | |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 39 | // HistogramSamples: |
dcheng | 5648818 | 2014-10-21 10:54:51 | [diff] [blame] | 40 | void Accumulate(HistogramBase::Sample value, |
| 41 | HistogramBase::Count count) override; |
| 42 | HistogramBase::Count GetCount(HistogramBase::Sample value) const override; |
| 43 | HistogramBase::Count TotalCount() const override; |
dcheng | 093de9b | 2016-04-04 21:25:51 | [diff] [blame] | 44 | std::unique_ptr<SampleCountIterator> Iterator() const override; |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 45 | |
| 46 | // Get count of a specific bucket. |
| 47 | HistogramBase::Count GetCountAtIndex(size_t bucket_index) const; |
| 48 | |
Brian White | 32052d5b | 2017-08-07 16:46:57 | [diff] [blame] | 49 | // Access the bucket ranges held externally. |
| 50 | const BucketRanges* bucket_ranges() const { return bucket_ranges_; } |
| 51 | |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 52 | protected: |
dcheng | 5648818 | 2014-10-21 10:54:51 | [diff] [blame] | 53 | bool AddSubtractImpl( |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 54 | SampleCountIterator* iter, |
mostynb | 9e096de | 2014-10-07 17:59:11 | [diff] [blame] | 55 | HistogramSamples::Operator op) override; // |op| is ADD or SUBTRACT. |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 56 | |
| 57 | virtual size_t GetBucketIndex(HistogramBase::Sample value) const; |
| 58 | |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 59 | // Moves the single-sample value to a mounted "counts" array. |
Gabriel Charette | a2bb40e | 2021-09-29 17:44:17 | [diff] [blame^] | 60 | // TODO(crbug.com/1254354): Remove NOINLINE after diagnosis. |
| 61 | void NOINLINE MoveSingleSampleToCounts(); |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 62 | |
| 63 | // Mounts (creating if necessary) an array of "counts" for multi-value |
| 64 | // storage. |
Gabriel Charette | a2bb40e | 2021-09-29 17:44:17 | [diff] [blame^] | 65 | // TODO(crbug.com/1254354): Remove NOINLINE after diagnosis. |
| 66 | void NOINLINE MountCountsStorageAndMoveSingleSample(); |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 67 | |
| 68 | // Mounts "counts" storage that already exists. This does not attempt to move |
| 69 | // any single-sample information to that storage as that would violate the |
| 70 | // "const" restriction that is often used to indicate read-only memory. |
| 71 | virtual bool MountExistingCountsStorage() const = 0; |
| 72 | |
| 73 | // Creates "counts" storage and returns a pointer to it. Ownership of the |
| 74 | // array remains with the called method but will never change. This must be |
| 75 | // called while some sort of lock is held to prevent reentry. |
| 76 | virtual HistogramBase::Count* CreateCountsStorageWhileLocked() = 0; |
| 77 | |
| 78 | HistogramBase::AtomicCount* counts() { |
Benoit Lize | 8ff573d | 2021-04-14 11:37:05 | [diff] [blame] | 79 | return counts_.load(std::memory_order_acquire); |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 80 | } |
| 81 | |
| 82 | const HistogramBase::AtomicCount* counts() const { |
Benoit Lize | 8ff573d | 2021-04-14 11:37:05 | [diff] [blame] | 83 | return counts_.load(std::memory_order_acquire); |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 84 | } |
| 85 | |
Benoit Lize | 8ff573d | 2021-04-14 11:37:05 | [diff] [blame] | 86 | void set_counts(HistogramBase::AtomicCount* counts) const { |
| 87 | counts_.store(counts, std::memory_order_release); |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 88 | } |
| 89 | |
| 90 | size_t counts_size() const { return bucket_ranges_->bucket_count(); } |
| 91 | |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 92 | private: |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 93 | friend class SampleVectorTest; |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 94 | FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts); |
bcwhite | 5cb99eb | 2016-02-01 21:07:56 | [diff] [blame] | 95 | FRIEND_TEST_ALL_PREFIXES(SharedHistogramTest, CorruptSampleCounts); |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 96 | |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 97 | // |counts_| is actually a pointer to a HistogramBase::AtomicCount array but |
Benoit Lize | 8ff573d | 2021-04-14 11:37:05 | [diff] [blame] | 98 | // is held as an atomic pointer for concurrency reasons. When combined with |
| 99 | // the single_sample held in the metadata, there are four possible states: |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 100 | // 1) single_sample == zero, counts_ == null |
| 101 | // 2) single_sample != zero, counts_ == null |
| 102 | // 3) single_sample != zero, counts_ != null BUT IS EMPTY |
| 103 | // 4) single_sample == zero, counts_ != null and may have data |
| 104 | // Once |counts_| is set, it can never revert and any existing single-sample |
| 105 | // must be moved to this storage. It is mutable because changing it doesn't |
| 106 | // change the (const) data but must adapt if a non-const object causes the |
| 107 | // storage to be allocated and updated. |
Benoit Lize | 8ff573d | 2021-04-14 11:37:05 | [diff] [blame] | 108 | mutable std::atomic<HistogramBase::AtomicCount*> counts_{nullptr}; |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 109 | |
| 110 | // Shares the same BucketRanges with Histogram object. |
| 111 | const BucketRanges* const bucket_ranges_; |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 112 | }; |
| 113 | |
| 114 | // A sample vector that uses local memory for the counts array. |
| 115 | class BASE_EXPORT SampleVector : public SampleVectorBase { |
| 116 | public: |
| 117 | explicit SampleVector(const BucketRanges* bucket_ranges); |
| 118 | SampleVector(uint64_t id, const BucketRanges* bucket_ranges); |
David Bienvenu | f1f52448 | 2020-09-16 19:37:37 | [diff] [blame] | 119 | SampleVector(const SampleVector&) = delete; |
| 120 | SampleVector& operator=(const SampleVector&) = delete; |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 121 | ~SampleVector() override; |
| 122 | |
| 123 | private: |
Quang Minh Tuan Nguyen | 39b1fed | 2021-03-16 00:49:38 | [diff] [blame] | 124 | FRIEND_TEST_ALL_PREFIXES(SampleVectorTest, GetPeakBucketSize); |
| 125 | |
| 126 | // HistogramSamples: |
| 127 | std::string GetAsciiBody() const override; |
| 128 | std::string GetAsciiHeader(StringPiece histogram_name, |
| 129 | int32_t flags) const override; |
| 130 | |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 131 | // SampleVectorBase: |
| 132 | bool MountExistingCountsStorage() const override; |
| 133 | HistogramBase::Count* CreateCountsStorageWhileLocked() override; |
| 134 | |
Quang Minh Tuan Nguyen | 39b1fed | 2021-03-16 00:49:38 | [diff] [blame] | 135 | // Writes cumulative percentage information based on the number |
| 136 | // of past, current, and remaining bucket samples. |
| 137 | void WriteAsciiBucketContext(int64_t past, |
| 138 | HistogramBase::Count current, |
| 139 | int64_t remaining, |
| 140 | uint32_t current_bucket_index, |
| 141 | std::string* output) const; |
| 142 | |
| 143 | // Finds out how large (graphically) the largest bucket will appear to be. |
| 144 | double GetPeakBucketSize() const; |
| 145 | |
| 146 | size_t bucket_count() const { return bucket_ranges()->bucket_count(); } |
| 147 | |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 148 | // Simple local storage for counts. |
| 149 | mutable std::vector<HistogramBase::AtomicCount> local_counts_; |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 150 | }; |
| 151 | |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 152 | // A sample vector that uses persistent memory for the counts array. |
| 153 | class BASE_EXPORT PersistentSampleVector : public SampleVectorBase { |
| 154 | public: |
| 155 | PersistentSampleVector(uint64_t id, |
| 156 | const BucketRanges* bucket_ranges, |
| 157 | Metadata* meta, |
| 158 | const DelayedPersistentAllocation& counts); |
David Bienvenu | f1f52448 | 2020-09-16 19:37:37 | [diff] [blame] | 159 | PersistentSampleVector(const PersistentSampleVector&) = delete; |
| 160 | PersistentSampleVector& operator=(const PersistentSampleVector&) = delete; |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 161 | ~PersistentSampleVector() override; |
| 162 | |
| 163 | private: |
| 164 | // SampleVectorBase: |
| 165 | bool MountExistingCountsStorage() const override; |
| 166 | HistogramBase::Count* CreateCountsStorageWhileLocked() override; |
| 167 | |
| 168 | // Persistent storage for counts. |
| 169 | DelayedPersistentAllocation persistent_counts_; |
bcwhite | fa8485b | 2017-05-01 16:43:25 | [diff] [blame] | 170 | }; |
| 171 | |
| 172 | // An iterator for sample vectors. This could be defined privately in the .cc |
| 173 | // file but is here for easy testing. |
xhwang | 3e9ca56 | 2015-11-06 18:50:36 | [diff] [blame] | 174 | class BASE_EXPORT SampleVectorIterator : public SampleCountIterator { |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 175 | public: |
glider@chromium.org | d2efe46 | 2014-01-21 07:59:28 | [diff] [blame] | 176 | SampleVectorIterator(const std::vector<HistogramBase::AtomicCount>* counts, |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 177 | const BucketRanges* bucket_ranges); |
bcwhite | b036e432 | 2015-12-10 18:36:34 | [diff] [blame] | 178 | SampleVectorIterator(const HistogramBase::AtomicCount* counts, |
| 179 | size_t counts_size, |
| 180 | const BucketRanges* bucket_ranges); |
dcheng | 5648818 | 2014-10-21 10:54:51 | [diff] [blame] | 181 | ~SampleVectorIterator() override; |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 182 | |
| 183 | // SampleCountIterator implementation: |
dcheng | 5648818 | 2014-10-21 10:54:51 | [diff] [blame] | 184 | bool Done() const override; |
| 185 | void Next() override; |
| 186 | void Get(HistogramBase::Sample* min, |
asvitkine | 3f17b146 | 2017-05-03 21:37:37 | [diff] [blame] | 187 | int64_t* max, |
dcheng | 5648818 | 2014-10-21 10:54:51 | [diff] [blame] | 188 | HistogramBase::Count* count) const override; |
kaiwang@chromium.org | b4af2ec | 2012-10-05 21:29:44 | [diff] [blame] | 189 | |
| 190 | // SampleVector uses predefined buckets, so iterator can return bucket index. |
dcheng | 5648818 | 2014-10-21 10:54:51 | [diff] [blame] | 191 | bool GetBucketIndex(size_t* index) const override; |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 192 | |
| 193 | private: |
| 194 | void SkipEmptyBuckets(); |
| 195 | |
bcwhite | b036e432 | 2015-12-10 18:36:34 | [diff] [blame] | 196 | const HistogramBase::AtomicCount* counts_; |
| 197 | size_t counts_size_; |
kaiwang@chromium.org | 2f7d9cd | 2012-09-22 03:42:12 | [diff] [blame] | 198 | const BucketRanges* bucket_ranges_; |
| 199 | |
| 200 | size_t index_; |
| 201 | }; |
| 202 | |
| 203 | } // namespace base |
| 204 | |
| 205 | #endif // BASE_METRICS_SAMPLE_VECTOR_H_ |