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 #ifndef BASE_METRICS_HISTOGRAM_SAMPLES_H_ 6 #define BASE_METRICS_HISTOGRAM_SAMPLES_H_ 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 #include <limits> 12 #include <memory> 13 14 #include "base/atomicops.h" 15 #include "base/macros.h" 16 #include "base/metrics/histogram_base.h" 17 18 namespace base { 19 20 class Pickle; 21 class PickleIterator; 22 class SampleCountIterator; 23 24 // HistogramSamples is a container storing all samples of a histogram. All 25 // elements must be of a fixed width to ensure 32/64-bit interoperability. 26 // If this structure changes, bump the version number for kTypeIdHistogram 27 // in persistent_histogram_allocator.cc. 28 // 29 // Note that though these samples are individually consistent (through the use 30 // of atomic operations on the counts), there is only "eventual consistency" 31 // overall when multiple threads are accessing this data. That means that the 32 // sum, redundant-count, etc. could be momentarily out-of-sync with the stored 33 // counts but will settle to a consistent "steady state" once all threads have 34 // exited this code. 35 class BASE_EXPORT HistogramSamples { 36 public: 37 // A single bucket and count. To fit within a single atomic on 32-bit build 38 // architectures, both |bucket| and |count| are limited in size to 16 bits. 39 // This limits the functionality somewhat but if an entry can't fit then 40 // the full array of samples can be allocated and used. 41 struct SingleSample { 42 uint16_t bucket; 43 uint16_t count; 44 }; 45 46 // A structure for managing an atomic single sample. Because this is generally 47 // used in association with other atomic values, the defined methods use 48 // acquire/release operations to guarantee ordering with outside values. 49 union BASE_EXPORT AtomicSingleSample { AtomicSingleSample()50 AtomicSingleSample() : as_atomic(0) {} AtomicSingleSample(subtle::Atomic32 rhs)51 AtomicSingleSample(subtle::Atomic32 rhs) : as_atomic(rhs) {} 52 53 // Returns the single sample in an atomic manner. This in an "acquire" 54 // load. The returned sample isn't shared and thus its fields can be safely 55 // accessed. 56 SingleSample Load() const; 57 58 // Extracts the single sample in an atomic manner. If |disable| is true 59 // then this object will be set so it will never accumulate another value. 60 // This is "no barrier" so doesn't enforce ordering with other atomic ops. 61 SingleSample Extract(bool disable); 62 63 // Adds a given count to the held bucket. If not possible, it returns false 64 // and leaves the parts unchanged. Once extracted/disabled, this always 65 // returns false. This in an "acquire/release" operation. 66 bool Accumulate(size_t bucket, HistogramBase::Count count); 67 68 // Returns if the sample has been "disabled" (via Extract) and thus not 69 // allowed to accept further accumulation. 70 bool IsDisabled() const; 71 72 private: 73 // union field: The actual sample bucket and count. 74 SingleSample as_parts; 75 76 // union field: The sample as an atomic value. Atomic64 would provide 77 // more flexibility but isn't available on all builds. This can hold a 78 // special, internal "disabled" value indicating that it must not accept 79 // further accumulation. 80 subtle::Atomic32 as_atomic; 81 }; 82 83 // A structure of information about the data, common to all sample containers. 84 // Because of how this is used in persistent memory, it must be a POD object 85 // that makes sense when initialized to all zeros. 86 struct Metadata { 87 // Expected size for 32/64-bit check. 88 static constexpr size_t kExpectedInstanceSize = 24; 89 90 // Initialized when the sample-set is first created with a value provided 91 // by the caller. It is generally used to identify the sample-set across 92 // threads and processes, though not necessarily uniquely as it is possible 93 // to have multiple sample-sets representing subsets of the data. 94 uint64_t id; 95 96 // The sum of all the entries, effectivly the sum(sample * count) for 97 // all samples. Despite being atomic, no guarantees are made on the 98 // accuracy of this value; there may be races during histogram 99 // accumulation and snapshotting that we choose to accept. It should 100 // be treated as approximate. 101 #ifdef ARCH_CPU_64_BITS 102 subtle::Atomic64 sum; 103 #else 104 // 32-bit systems don't have atomic 64-bit operations. Use a basic type 105 // and don't worry about "shearing". 106 int64_t sum; 107 #endif 108 109 // A "redundant" count helps identify memory corruption. It redundantly 110 // stores the total number of samples accumulated in the histogram. We 111 // can compare this count to the sum of the counts (TotalCount() function), 112 // and detect problems. Note, depending on the implementation of different 113 // histogram types, there might be races during histogram accumulation 114 // and snapshotting that we choose to accept. In this case, the tallies 115 // might mismatch even when no memory corruption has happened. 116 HistogramBase::AtomicCount redundant_count; 117 118 // A single histogram value and associated count. This allows histograms 119 // that typically report only a single value to not require full storage 120 // to be allocated. 121 AtomicSingleSample single_sample; // 32 bits 122 }; 123 124 // Because structures held in persistent memory must be POD, there can be no 125 // default constructor to clear the fields. This derived class exists just 126 // to clear them when being allocated on the heap. 127 struct BASE_EXPORT LocalMetadata : Metadata { 128 LocalMetadata(); 129 }; 130 131 HistogramSamples(uint64_t id, Metadata* meta); 132 virtual ~HistogramSamples(); 133 134 virtual void Accumulate(HistogramBase::Sample value, 135 HistogramBase::Count count) = 0; 136 virtual HistogramBase::Count GetCount(HistogramBase::Sample value) const = 0; 137 virtual HistogramBase::Count TotalCount() const = 0; 138 139 virtual void Add(const HistogramSamples& other); 140 141 // Add from serialized samples. 142 virtual bool AddFromPickle(PickleIterator* iter); 143 144 virtual void Subtract(const HistogramSamples& other); 145 146 virtual std::unique_ptr<SampleCountIterator> Iterator() const = 0; 147 virtual void Serialize(Pickle* pickle) const; 148 149 // Accessor fuctions. id()150 uint64_t id() const { return meta_->id; } sum()151 int64_t sum() const { 152 #ifdef ARCH_CPU_64_BITS 153 return subtle::NoBarrier_Load(&meta_->sum); 154 #else 155 return meta_->sum; 156 #endif 157 } redundant_count()158 HistogramBase::Count redundant_count() const { 159 return subtle::NoBarrier_Load(&meta_->redundant_count); 160 } 161 162 protected: 163 enum NegativeSampleReason { 164 SAMPLES_HAVE_LOGGED_BUT_NOT_SAMPLE, 165 SAMPLES_SAMPLE_LESS_THAN_LOGGED, 166 SAMPLES_ADDED_NEGATIVE_COUNT, 167 SAMPLES_ADD_WENT_NEGATIVE, 168 SAMPLES_ADD_OVERFLOW, 169 SAMPLES_ACCUMULATE_NEGATIVE_COUNT, 170 SAMPLES_ACCUMULATE_WENT_NEGATIVE, 171 DEPRECATED_SAMPLES_ACCUMULATE_OVERFLOW, 172 SAMPLES_ACCUMULATE_OVERFLOW, 173 MAX_NEGATIVE_SAMPLE_REASONS 174 }; 175 176 // Based on |op| type, add or subtract sample counts data from the iterator. 177 enum Operator { ADD, SUBTRACT }; 178 virtual bool AddSubtractImpl(SampleCountIterator* iter, Operator op) = 0; 179 180 // Accumulates to the embedded single-sample field if possible. Returns true 181 // on success, false otherwise. Sum and redundant-count are also updated in 182 // the success case. 183 bool AccumulateSingleSample(HistogramBase::Sample value, 184 HistogramBase::Count count, 185 size_t bucket); 186 187 // Atomically adjust the sum and redundant-count. 188 void IncreaseSumAndCount(int64_t sum, HistogramBase::Count count); 189 190 // Record a negative-sample observation and the reason why. 191 void RecordNegativeSample(NegativeSampleReason reason, 192 HistogramBase::Count increment); 193 single_sample()194 AtomicSingleSample& single_sample() { return meta_->single_sample; } single_sample()195 const AtomicSingleSample& single_sample() const { 196 return meta_->single_sample; 197 } 198 meta()199 Metadata* meta() { return meta_; } 200 201 private: 202 // Depending on derived class meta values can come from local stoarge or 203 // external storage in which case HistogramSamples class cannot take ownership 204 // of Metadata*. 205 Metadata* meta_; 206 207 DISALLOW_COPY_AND_ASSIGN(HistogramSamples); 208 }; 209 210 class BASE_EXPORT SampleCountIterator { 211 public: 212 virtual ~SampleCountIterator(); 213 214 virtual bool Done() const = 0; 215 virtual void Next() = 0; 216 217 // Get the sample and count at current position. 218 // |min| |max| and |count| can be NULL if the value is not of interest. 219 // Note: |max| is int64_t because histograms support logged values in the 220 // full int32_t range and bucket max is exclusive, so it needs to support 221 // values up to MAXINT32+1. 222 // Requires: !Done(); 223 virtual void Get(HistogramBase::Sample* min, 224 int64_t* max, 225 HistogramBase::Count* count) const = 0; 226 static_assert(std::numeric_limits<HistogramBase::Sample>::max() < 227 std::numeric_limits<int64_t>::max(), 228 "Get() |max| must be able to hold Histogram::Sample max + 1"); 229 230 // Get the index of current histogram bucket. 231 // For histograms that don't use predefined buckets, it returns false. 232 // Requires: !Done(); 233 virtual bool GetBucketIndex(size_t* index) const; 234 }; 235 236 class BASE_EXPORT SingleSampleIterator : public SampleCountIterator { 237 public: 238 SingleSampleIterator(HistogramBase::Sample min, 239 int64_t max, 240 HistogramBase::Count count); 241 SingleSampleIterator(HistogramBase::Sample min, 242 int64_t max, 243 HistogramBase::Count count, 244 size_t bucket_index); 245 ~SingleSampleIterator() override; 246 247 // SampleCountIterator: 248 bool Done() const override; 249 void Next() override; 250 void Get(HistogramBase::Sample* min, 251 int64_t* max, 252 HistogramBase::Count* count) const override; 253 254 // SampleVector uses predefined buckets so iterator can return bucket index. 255 bool GetBucketIndex(size_t* index) const override; 256 257 private: 258 // Information about the single value to return. 259 const HistogramBase::Sample min_; 260 const int64_t max_; 261 const size_t bucket_index_; 262 HistogramBase::Count count_; 263 }; 264 265 } // namespace base 266 267 #endif // BASE_METRICS_HISTOGRAM_SAMPLES_H_ 268