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 void Serialize(Pickle* pickle) const; 169 170 // Returns ASCII representation of histograms data for histogram samples. 171 // The dictionary returned will be of the form 172 // {"name":<string>, "header":<string>, "body": <string>} 173 base::Value::Dict ToGraphDict(StringPiece histogram_name, 174 int32_t flags) const; 175 176 // Accessor functions. id()177 uint64_t id() const { return meta_->id; } sum()178 int64_t sum() const { 179 #ifdef ARCH_CPU_64_BITS 180 return subtle::NoBarrier_Load(&meta_->sum); 181 #else 182 return meta_->sum; 183 #endif 184 } redundant_count()185 HistogramBase::Count redundant_count() const { 186 return subtle::NoBarrier_Load(&meta_->redundant_count); 187 } 188 189 protected: 190 enum NegativeSampleReason { 191 SAMPLES_HAVE_LOGGED_BUT_NOT_SAMPLE, 192 SAMPLES_SAMPLE_LESS_THAN_LOGGED, 193 SAMPLES_ADDED_NEGATIVE_COUNT, 194 SAMPLES_ADD_WENT_NEGATIVE, 195 SAMPLES_ADD_OVERFLOW, 196 SAMPLES_ACCUMULATE_NEGATIVE_COUNT, 197 SAMPLES_ACCUMULATE_WENT_NEGATIVE, 198 DEPRECATED_SAMPLES_ACCUMULATE_OVERFLOW, 199 SAMPLES_ACCUMULATE_OVERFLOW, 200 MAX_NEGATIVE_SAMPLE_REASONS 201 }; 202 203 HistogramSamples(uint64_t id, Metadata* meta); 204 HistogramSamples(uint64_t id, std::unique_ptr<Metadata> meta); 205 206 // Based on |op| type, add or subtract sample counts data from the iterator. 207 enum Operator { ADD, SUBTRACT }; 208 virtual bool AddSubtractImpl(SampleCountIterator* iter, Operator op) = 0; 209 210 // Accumulates to the embedded single-sample field if possible. Returns true 211 // on success, false otherwise. Sum and redundant-count are also updated in 212 // the success case. 213 bool AccumulateSingleSample(HistogramBase::Sample value, 214 HistogramBase::Count count, 215 size_t bucket); 216 217 // Atomically adjust the sum and redundant-count. 218 void IncreaseSumAndCount(int64_t sum, HistogramBase::Count count); 219 220 // Record a negative-sample observation and the reason why. 221 void RecordNegativeSample(NegativeSampleReason reason, 222 HistogramBase::Count increment); 223 single_sample()224 AtomicSingleSample& single_sample() { return meta_->single_sample; } single_sample()225 const AtomicSingleSample& single_sample() const { 226 return meta_->single_sample; 227 } 228 229 // Produces an actual graph (set of blank vs non blank char's) for a bucket. 230 void WriteAsciiBucketGraph(double x_count, 231 int line_length, 232 std::string* output) const; 233 234 // Writes textual description of the bucket contents (relative to histogram). 235 // Output is the count in the buckets, as well as the percentage. 236 void WriteAsciiBucketValue(HistogramBase::Count current, 237 double scaled_sum, 238 std::string* output) const; 239 240 // Gets a body for this histogram samples. 241 virtual std::string GetAsciiBody() const; 242 243 // Gets a header message describing this histogram samples. 244 virtual std::string GetAsciiHeader(StringPiece histogram_name, 245 int32_t flags) const; 246 247 // Returns a string description of what goes in a given bucket. 248 const std::string GetSimpleAsciiBucketRange( 249 HistogramBase::Sample sample) const; 250 meta()251 Metadata* meta() { return meta_; } 252 253 private: 254 // Depending on derived class `meta_` can come from: 255 // - Local storage: Then `meta_owned_` is set and meta_ points to it. 256 // - External storage: Then `meta_owned_` is null, and `meta_` point toward an 257 // external object. The callers guarantees the value will outlive this 258 // instance. 259 std::unique_ptr<Metadata> meta_owned_; 260 raw_ptr<Metadata> meta_; 261 }; 262 263 class BASE_EXPORT SampleCountIterator { 264 public: 265 virtual ~SampleCountIterator(); 266 267 virtual bool Done() const = 0; 268 virtual void Next() = 0; 269 270 // Get the sample and count at current position. 271 // Note: |max| is int64_t because histograms support logged values in the 272 // full int32_t range and bucket max is exclusive, so it needs to support 273 // values up to MAXINT32+1. 274 // Requires: !Done(); 275 virtual void Get(HistogramBase::Sample* min, 276 int64_t* max, 277 HistogramBase::Count* count) = 0; 278 static_assert(std::numeric_limits<HistogramBase::Sample>::max() < 279 std::numeric_limits<int64_t>::max(), 280 "Get() |max| must be able to hold Histogram::Sample max + 1"); 281 282 // Get the index of current histogram bucket. 283 // For histograms that don't use predefined buckets, it returns false. 284 // Requires: !Done(); 285 virtual bool GetBucketIndex(size_t* index) const; 286 }; 287 288 class BASE_EXPORT SingleSampleIterator : public SampleCountIterator { 289 public: 290 SingleSampleIterator(HistogramBase::Sample min, 291 int64_t max, 292 HistogramBase::Count count, 293 size_t bucket_index, 294 bool value_was_extracted); 295 ~SingleSampleIterator() override; 296 297 // SampleCountIterator: 298 bool Done() const override; 299 void Next() override; 300 void Get(HistogramBase::Sample* min, 301 int64_t* max, 302 HistogramBase::Count* count) override; 303 304 // SampleVector uses predefined buckets so iterator can return bucket index. 305 bool GetBucketIndex(size_t* index) const override; 306 307 private: 308 // Information about the single value to return. 309 const HistogramBase::Sample min_; 310 const int64_t max_; 311 const size_t bucket_index_; 312 HistogramBase::Count count_; 313 314 // Whether the value that this iterator holds was extracted from the 315 // underlying data (i.e., reset to 0). 316 const bool value_was_extracted_; 317 }; 318 319 } // namespace base 320 321 #endif // BASE_METRICS_HISTOGRAM_SAMPLES_H_ 322