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