• 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   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