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 #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 #include <string> 14 15 #include "base/atomicops.h" 16 #include "base/base_export.h" 17 #include "base/memory/raw_ptr.h" 18 #include "base/metrics/histogram_base.h" 19 20 namespace base { 21 22 class Pickle; 23 class PickleIterator; 24 class SampleCountIterator; 25 26 // HistogramSamples is a container storing all samples of a histogram. All 27 // elements must be of a fixed width to ensure 32/64-bit interoperability. 28 // If this structure changes, bump the version number for kTypeIdHistogram 29 // in persistent_histogram_allocator.cc. 30 // 31 // Note that though these samples are individually consistent (through the use 32 // of atomic operations on the counts), there is only "eventual consistency" 33 // overall when multiple threads are accessing this data. That means that the 34 // sum, redundant-count, etc. could be momentarily out-of-sync with the stored 35 // counts but will settle to a consistent "steady state" once all threads have 36 // exited this code. 37 class BASE_EXPORT HistogramSamples { 38 public: 39 // A single bucket and count. To fit within a single atomic on 32-bit build 40 // architectures, both |bucket| and |count| are limited in size to 16 bits. 41 // This limits the functionality somewhat but if an entry can't fit then 42 // the full array of samples can be allocated and used. 43 struct SingleSample { 44 uint16_t bucket; 45 uint16_t count; 46 }; 47 48 // A structure for managing an atomic single sample. Because this is generally 49 // used in association with other atomic values, the defined methods use 50 // acquire/release operations to guarantee ordering with outside values. 51 union BASE_EXPORT AtomicSingleSample { AtomicSingleSample()52 AtomicSingleSample() : as_atomic(0) {} AtomicSingleSample(subtle::Atomic32 rhs)53 explicit AtomicSingleSample(subtle::Atomic32 rhs) : as_atomic(rhs) {} 54 55 // Returns the single sample in an atomic manner. This in an "acquire" 56 // load. The returned sample isn't shared and thus its fields can be safely 57 // accessed. If this object is disabled, this will return an empty sample 58 // (bucket count set to 0). 59 SingleSample Load() const; 60 61 // Extracts and returns the single sample and changes it to |new_value| in 62 // an atomic manner. If this object is disabled, this will return an empty 63 // sample (bucket count set to 0). 64 SingleSample Extract(AtomicSingleSample new_value = AtomicSingleSample(0)); 65 66 // Like Extract() above, but also disables this object so that it will 67 // never accumulate another value. If this object is already disabled, this 68 // will return an empty sample (bucket count set to 0). 69 SingleSample ExtractAndDisable(); 70 71 // Adds a given count to the held bucket. If not possible, it returns false 72 // and leaves the parts unchanged. Once extracted/disabled, this always 73 // returns false. This in an "acquire/release" operation. 74 bool Accumulate(size_t bucket, HistogramBase::Count count); 75 76 // Returns if the sample has been "disabled" (via Extract) and thus not 77 // allowed to accept further accumulation. 78 bool IsDisabled() const; 79 80 private: 81 // union field: The actual sample bucket and count. 82 SingleSample as_parts; 83 84 // union field: The sample as an atomic value. Atomic64 would provide 85 // more flexibility but isn't available on all builds. This can hold a 86 // special, internal "disabled" value indicating that it must not accept 87 // further accumulation. 88 subtle::Atomic32 as_atomic; 89 }; 90 91 // A structure of information about the data, common to all sample containers. 92 // Because of how this is used in persistent memory, it must be a POD object 93 // that makes sense when initialized to all zeros. 94 struct Metadata { 95 // Expected size for 32/64-bit check. 96 static constexpr size_t kExpectedInstanceSize = 24; 97 98 // Initialized when the sample-set is first created with a value provided 99 // by the caller. It is generally used to identify the sample-set across 100 // threads and processes, though not necessarily uniquely as it is possible 101 // to have multiple sample-sets representing subsets of the data. 102 uint64_t id; 103 104 // The sum of all the entries, effectivly the sum(sample * count) for 105 // all samples. Despite being atomic, no guarantees are made on the 106 // accuracy of this value; there may be races during histogram 107 // accumulation and snapshotting that we choose to accept. It should 108 // be treated as approximate. 109 #ifdef ARCH_CPU_64_BITS 110 subtle::Atomic64 sum; 111 #else 112 // 32-bit systems don't have atomic 64-bit operations. Use a basic type 113 // and don't worry about "shearing". 114 int64_t sum; 115 #endif 116 117 // A "redundant" count helps identify memory corruption. It redundantly 118 // stores the total number of samples accumulated in the histogram. We 119 // can compare this count to the sum of the counts (TotalCount() function), 120 // and detect problems. Note, depending on the implementation of different 121 // histogram types, there might be races during histogram accumulation 122 // and snapshotting that we choose to accept. In this case, the tallies 123 // might mismatch even when no memory corruption has happened. 124 HistogramBase::AtomicCount redundant_count{0}; 125 126 // A single histogram value and associated count. This allows histograms 127 // that typically report only a single value to not require full storage 128 // to be allocated. 129 AtomicSingleSample single_sample; // 32 bits 130 }; 131 132 // Because structures held in persistent memory must be POD, there can be no 133 // default constructor to clear the fields. This derived class exists just 134 // to clear them when being allocated on the heap. 135 struct BASE_EXPORT LocalMetadata : Metadata { 136 LocalMetadata(); 137 }; 138 139 HistogramSamples(const HistogramSamples&) = delete; 140 HistogramSamples& operator=(const HistogramSamples&) = delete; 141 virtual ~HistogramSamples(); 142 143 virtual void Accumulate(HistogramBase::Sample value, 144 HistogramBase::Count count) = 0; 145 virtual HistogramBase::Count GetCount(HistogramBase::Sample value) const = 0; 146 virtual HistogramBase::Count TotalCount() const = 0; 147 148 void Add(const HistogramSamples& other); 149 150 // Add from serialized samples. 151 bool AddFromPickle(PickleIterator* iter); 152 153 void Subtract(const HistogramSamples& other); 154 155 // Adds the samples from |other| while also resetting |other|'s sample counts 156 // to 0. 157 void Extract(HistogramSamples& other); 158 159 // Returns an iterator to read the sample counts. 160 virtual std::unique_ptr<SampleCountIterator> Iterator() const = 0; 161 162 // Returns a special kind of iterator that resets the underlying sample count 163 // to 0 when Get() is called. The returned iterator must be consumed 164 // completely before being destroyed, otherwise samples may be lost (this is 165 // enforced by a DCHECK in the destructor). 166 virtual std::unique_ptr<SampleCountIterator> ExtractingIterator() = 0; 167 168 // Returns true if |this| is empty (has no samples, has a |sum| of zero, and 169 // has a |redundant_count| of zero), which is indicative that the caller does 170 // not need to process |this|. 171 // - Note 1: This should only be called when |this| is only manipulated on one 172 // thread at a time (e.g., the underlying data does not change on another 173 // thread). If this is not the case, then the returned value cannot be trusted 174 // at all. 175 // - Note 2: For performance reasons, this is not guaranteed to return the 176 // correct value. If false is returned, |this| may or may not be empty. 177 // However, if true is returned, then |this| is guaranteed to be empty (no 178 // false positives). Of course, this assumes that "Note 1" is respected. 179 // - Note 3: The base implementation of this method checks for |sum| and 180 // |redundant_count|, but the child implementations should also check for 181 // samples. 182 virtual bool IsDefinitelyEmpty() const; 183 184 void Serialize(Pickle* pickle) const; 185 186 // Returns ASCII representation of histograms data for histogram samples. 187 // The dictionary returned will be of the form 188 // {"name":<string>, "header":<string>, "body": <string>} 189 base::Value::Dict ToGraphDict(StringPiece histogram_name, 190 int32_t flags) const; 191 192 // Accessor functions. id()193 uint64_t id() const { return meta_->id; } sum()194 int64_t sum() const { 195 #ifdef ARCH_CPU_64_BITS 196 return subtle::NoBarrier_Load(&meta_->sum); 197 #else 198 return meta_->sum; 199 #endif 200 } redundant_count()201 HistogramBase::Count redundant_count() const { 202 return subtle::NoBarrier_Load(&meta_->redundant_count); 203 } 204 205 protected: 206 enum NegativeSampleReason { 207 SAMPLES_HAVE_LOGGED_BUT_NOT_SAMPLE, 208 SAMPLES_SAMPLE_LESS_THAN_LOGGED, 209 SAMPLES_ADDED_NEGATIVE_COUNT, 210 SAMPLES_ADD_WENT_NEGATIVE, 211 SAMPLES_ADD_OVERFLOW, 212 SAMPLES_ACCUMULATE_NEGATIVE_COUNT, 213 SAMPLES_ACCUMULATE_WENT_NEGATIVE, 214 DEPRECATED_SAMPLES_ACCUMULATE_OVERFLOW, 215 SAMPLES_ACCUMULATE_OVERFLOW, 216 MAX_NEGATIVE_SAMPLE_REASONS 217 }; 218 219 HistogramSamples(uint64_t id, Metadata* meta); 220 HistogramSamples(uint64_t id, std::unique_ptr<Metadata> meta); 221 222 // Based on |op| type, add or subtract sample counts data from the iterator. 223 enum Operator { ADD, SUBTRACT }; 224 virtual bool AddSubtractImpl(SampleCountIterator* iter, Operator op) = 0; 225 226 // Accumulates to the embedded single-sample field if possible. Returns true 227 // on success, false otherwise. Sum and redundant-count are also updated in 228 // the success case. 229 bool AccumulateSingleSample(HistogramBase::Sample value, 230 HistogramBase::Count count, 231 size_t bucket); 232 233 // Atomically adjust the sum and redundant-count. 234 void IncreaseSumAndCount(int64_t sum, HistogramBase::Count count); 235 236 // Record a negative-sample observation and the reason why. 237 void RecordNegativeSample(NegativeSampleReason reason, 238 HistogramBase::Count increment); 239 single_sample()240 AtomicSingleSample& single_sample() { return meta_->single_sample; } single_sample()241 const AtomicSingleSample& single_sample() const { 242 return meta_->single_sample; 243 } 244 245 // Produces an actual graph (set of blank vs non blank char's) for a bucket. 246 void WriteAsciiBucketGraph(double x_count, 247 int line_length, 248 std::string* output) const; 249 250 // Writes textual description of the bucket contents (relative to histogram). 251 // Output is the count in the buckets, as well as the percentage. 252 void WriteAsciiBucketValue(HistogramBase::Count current, 253 double scaled_sum, 254 std::string* output) const; 255 256 // Gets a body for this histogram samples. 257 virtual std::string GetAsciiBody() const; 258 259 // Gets a header message describing this histogram samples. 260 virtual std::string GetAsciiHeader(StringPiece histogram_name, 261 int32_t flags) const; 262 263 // Returns a string description of what goes in a given bucket. 264 const std::string GetSimpleAsciiBucketRange( 265 HistogramBase::Sample sample) const; 266 meta()267 Metadata* meta() { return meta_; } 268 269 private: 270 // Depending on derived class `meta_` can come from: 271 // - Local storage: Then `meta_owned_` is set and meta_ points to it. 272 // - External storage: Then `meta_owned_` is null, and `meta_` point toward an 273 // external object. The callers guarantees the value will outlive this 274 // instance. 275 std::unique_ptr<Metadata> meta_owned_; 276 raw_ptr<Metadata> meta_; 277 }; 278 279 class BASE_EXPORT SampleCountIterator { 280 public: 281 virtual ~SampleCountIterator(); 282 283 virtual bool Done() const = 0; 284 virtual void Next() = 0; 285 286 // Get the sample and count at current position. 287 // Note: |max| is int64_t because histograms support logged values in the 288 // full int32_t range and bucket max is exclusive, so it needs to support 289 // values up to MAXINT32+1. 290 // Requires: !Done(); 291 virtual void Get(HistogramBase::Sample* min, 292 int64_t* max, 293 HistogramBase::Count* count) = 0; 294 static_assert(std::numeric_limits<HistogramBase::Sample>::max() < 295 std::numeric_limits<int64_t>::max(), 296 "Get() |max| must be able to hold Histogram::Sample max + 1"); 297 298 // Get the index of current histogram bucket. 299 // For histograms that don't use predefined buckets, it returns false. 300 // Requires: !Done(); 301 virtual bool GetBucketIndex(size_t* index) const; 302 }; 303 304 class BASE_EXPORT SingleSampleIterator : public SampleCountIterator { 305 public: 306 SingleSampleIterator(HistogramBase::Sample min, 307 int64_t max, 308 HistogramBase::Count count, 309 size_t bucket_index, 310 bool value_was_extracted); 311 ~SingleSampleIterator() override; 312 313 // SampleCountIterator: 314 bool Done() const override; 315 void Next() override; 316 void Get(HistogramBase::Sample* min, 317 int64_t* max, 318 HistogramBase::Count* count) override; 319 320 // SampleVector uses predefined buckets so iterator can return bucket index. 321 bool GetBucketIndex(size_t* index) const override; 322 323 private: 324 // Information about the single value to return. 325 const HistogramBase::Sample min_; 326 const int64_t max_; 327 const size_t bucket_index_; 328 HistogramBase::Count count_; 329 330 // Whether the value that this iterator holds was extracted from the 331 // underlying data (i.e., reset to 0). 332 const bool value_was_extracted_; 333 }; 334 335 } // namespace base 336 337 #endif // BASE_METRICS_HISTOGRAM_SAMPLES_H_ 338