• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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