1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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 
14 #include "base/atomicops.h"
15 #include "base/macros.h"
16 #include "base/metrics/histogram_base.h"
17 
18 namespace base {
19 
20 class Pickle;
21 class PickleIterator;
22 class SampleCountIterator;
23 
24 // HistogramSamples is a container storing all samples of a histogram. All
25 // elements must be of a fixed width to ensure 32/64-bit interoperability.
26 // If this structure changes, bump the version number for kTypeIdHistogram
27 // in persistent_histogram_allocator.cc.
28 //
29 // Note that though these samples are individually consistent (through the use
30 // of atomic operations on the counts), there is only "eventual consistency"
31 // overall when multiple threads are accessing this data. That means that the
32 // sum, redundant-count, etc. could be momentarily out-of-sync with the stored
33 // counts but will settle to a consistent "steady state" once all threads have
34 // exited this code.
35 class BASE_EXPORT HistogramSamples {
36  public:
37   // A single bucket and count. To fit within a single atomic on 32-bit build
38   // architectures, both |bucket| and |count| are limited in size to 16 bits.
39   // This limits the functionality somewhat but if an entry can't fit then
40   // the full array of samples can be allocated and used.
41   struct SingleSample {
42     uint16_t bucket;
43     uint16_t count;
44   };
45 
46   // A structure for managing an atomic single sample. Because this is generally
47   // used in association with other atomic values, the defined methods use
48   // acquire/release operations to guarantee ordering with outside values.
49   union BASE_EXPORT AtomicSingleSample {
AtomicSingleSample()50     AtomicSingleSample() : as_atomic(0) {}
AtomicSingleSample(subtle::Atomic32 rhs)51     AtomicSingleSample(subtle::Atomic32 rhs) : as_atomic(rhs) {}
52 
53     // Returns the single sample in an atomic manner. This in an "acquire"
54     // load. The returned sample isn't shared and thus its fields can be safely
55     // accessed.
56     SingleSample Load() const;
57 
58     // Extracts the single sample in an atomic manner. If |disable| is true
59     // then this object will be set so it will never accumulate another value.
60     // This is "no barrier" so doesn't enforce ordering with other atomic ops.
61     SingleSample Extract(bool disable);
62 
63     // Adds a given count to the held bucket. If not possible, it returns false
64     // and leaves the parts unchanged. Once extracted/disabled, this always
65     // returns false. This in an "acquire/release" operation.
66     bool Accumulate(size_t bucket, HistogramBase::Count count);
67 
68     // Returns if the sample has been "disabled" (via Extract) and thus not
69     // allowed to accept further accumulation.
70     bool IsDisabled() const;
71 
72    private:
73     // union field: The actual sample bucket and count.
74     SingleSample as_parts;
75 
76     // union field: The sample as an atomic value. Atomic64 would provide
77     // more flexibility but isn't available on all builds. This can hold a
78     // special, internal "disabled" value indicating that it must not accept
79     // further accumulation.
80     subtle::Atomic32 as_atomic;
81   };
82 
83   // A structure of information about the data, common to all sample containers.
84   // Because of how this is used in persistent memory, it must be a POD object
85   // that makes sense when initialized to all zeros.
86   struct Metadata {
87     // Expected size for 32/64-bit check.
88     static constexpr size_t kExpectedInstanceSize = 24;
89 
90     // Initialized when the sample-set is first created with a value provided
91     // by the caller. It is generally used to identify the sample-set across
92     // threads and processes, though not necessarily uniquely as it is possible
93     // to have multiple sample-sets representing subsets of the data.
94     uint64_t id;
95 
96     // The sum of all the entries, effectivly the sum(sample * count) for
97     // all samples. Despite being atomic, no guarantees are made on the
98     // accuracy of this value; there may be races during histogram
99     // accumulation and snapshotting that we choose to accept. It should
100     // be treated as approximate.
101 #ifdef ARCH_CPU_64_BITS
102     subtle::Atomic64 sum;
103 #else
104     // 32-bit systems don't have atomic 64-bit operations. Use a basic type
105     // and don't worry about "shearing".
106     int64_t sum;
107 #endif
108 
109     // A "redundant" count helps identify memory corruption. It redundantly
110     // stores the total number of samples accumulated in the histogram. We
111     // can compare this count to the sum of the counts (TotalCount() function),
112     // and detect problems. Note, depending on the implementation of different
113     // histogram types, there might be races during histogram accumulation
114     // and snapshotting that we choose to accept. In this case, the tallies
115     // might mismatch even when no memory corruption has happened.
116     HistogramBase::AtomicCount redundant_count;
117 
118     // A single histogram value and associated count. This allows histograms
119     // that typically report only a single value to not require full storage
120     // to be allocated.
121     AtomicSingleSample single_sample;  // 32 bits
122   };
123 
124   // Because structures held in persistent memory must be POD, there can be no
125   // default constructor to clear the fields. This derived class exists just
126   // to clear them when being allocated on the heap.
127   struct BASE_EXPORT LocalMetadata : Metadata {
128     LocalMetadata();
129   };
130 
131   HistogramSamples(uint64_t id, Metadata* meta);
132   virtual ~HistogramSamples();
133 
134   virtual void Accumulate(HistogramBase::Sample value,
135                           HistogramBase::Count count) = 0;
136   virtual HistogramBase::Count GetCount(HistogramBase::Sample value) const = 0;
137   virtual HistogramBase::Count TotalCount() const = 0;
138 
139   virtual void Add(const HistogramSamples& other);
140 
141   // Add from serialized samples.
142   virtual bool AddFromPickle(PickleIterator* iter);
143 
144   virtual void Subtract(const HistogramSamples& other);
145 
146   virtual std::unique_ptr<SampleCountIterator> Iterator() const = 0;
147   virtual void Serialize(Pickle* pickle) const;
148 
149   // Accessor fuctions.
id()150   uint64_t id() const { return meta_->id; }
sum()151   int64_t sum() const {
152 #ifdef ARCH_CPU_64_BITS
153     return subtle::NoBarrier_Load(&meta_->sum);
154 #else
155     return meta_->sum;
156 #endif
157   }
redundant_count()158   HistogramBase::Count redundant_count() const {
159     return subtle::NoBarrier_Load(&meta_->redundant_count);
160   }
161 
162  protected:
163   enum NegativeSampleReason {
164     SAMPLES_HAVE_LOGGED_BUT_NOT_SAMPLE,
165     SAMPLES_SAMPLE_LESS_THAN_LOGGED,
166     SAMPLES_ADDED_NEGATIVE_COUNT,
167     SAMPLES_ADD_WENT_NEGATIVE,
168     SAMPLES_ADD_OVERFLOW,
169     SAMPLES_ACCUMULATE_NEGATIVE_COUNT,
170     SAMPLES_ACCUMULATE_WENT_NEGATIVE,
171     DEPRECATED_SAMPLES_ACCUMULATE_OVERFLOW,
172     SAMPLES_ACCUMULATE_OVERFLOW,
173     MAX_NEGATIVE_SAMPLE_REASONS
174   };
175 
176   // Based on |op| type, add or subtract sample counts data from the iterator.
177   enum Operator { ADD, SUBTRACT };
178   virtual bool AddSubtractImpl(SampleCountIterator* iter, Operator op) = 0;
179 
180   // Accumulates to the embedded single-sample field if possible. Returns true
181   // on success, false otherwise. Sum and redundant-count are also updated in
182   // the success case.
183   bool AccumulateSingleSample(HistogramBase::Sample value,
184                               HistogramBase::Count count,
185                               size_t bucket);
186 
187   // Atomically adjust the sum and redundant-count.
188   void IncreaseSumAndCount(int64_t sum, HistogramBase::Count count);
189 
190   // Record a negative-sample observation and the reason why.
191   void RecordNegativeSample(NegativeSampleReason reason,
192                             HistogramBase::Count increment);
193 
single_sample()194   AtomicSingleSample& single_sample() { return meta_->single_sample; }
single_sample()195   const AtomicSingleSample& single_sample() const {
196     return meta_->single_sample;
197   }
198 
meta()199   Metadata* meta() { return meta_; }
200 
201  private:
202   // Depending on derived class meta values can come from local stoarge or
203   // external storage in which case HistogramSamples class cannot take ownership
204   // of Metadata*.
205   Metadata* meta_;
206 
207   DISALLOW_COPY_AND_ASSIGN(HistogramSamples);
208 };
209 
210 class BASE_EXPORT SampleCountIterator {
211  public:
212   virtual ~SampleCountIterator();
213 
214   virtual bool Done() const = 0;
215   virtual void Next() = 0;
216 
217   // Get the sample and count at current position.
218   // |min| |max| and |count| can be NULL if the value is not of interest.
219   // Note: |max| is int64_t because histograms support logged values in the
220   // full int32_t range and bucket max is exclusive, so it needs to support
221   // values up to MAXINT32+1.
222   // Requires: !Done();
223   virtual void Get(HistogramBase::Sample* min,
224                    int64_t* max,
225                    HistogramBase::Count* count) const = 0;
226   static_assert(std::numeric_limits<HistogramBase::Sample>::max() <
227                     std::numeric_limits<int64_t>::max(),
228                 "Get() |max| must be able to hold Histogram::Sample max + 1");
229 
230   // Get the index of current histogram bucket.
231   // For histograms that don't use predefined buckets, it returns false.
232   // Requires: !Done();
233   virtual bool GetBucketIndex(size_t* index) const;
234 };
235 
236 class BASE_EXPORT SingleSampleIterator : public SampleCountIterator {
237  public:
238   SingleSampleIterator(HistogramBase::Sample min,
239                        int64_t max,
240                        HistogramBase::Count count);
241   SingleSampleIterator(HistogramBase::Sample min,
242                        int64_t max,
243                        HistogramBase::Count count,
244                        size_t bucket_index);
245   ~SingleSampleIterator() override;
246 
247   // SampleCountIterator:
248   bool Done() const override;
249   void Next() override;
250   void Get(HistogramBase::Sample* min,
251            int64_t* max,
252            HistogramBase::Count* count) const override;
253 
254   // SampleVector uses predefined buckets so iterator can return bucket index.
255   bool GetBucketIndex(size_t* index) const override;
256 
257  private:
258   // Information about the single value to return.
259   const HistogramBase::Sample min_;
260   const int64_t max_;
261   const size_t bucket_index_;
262   HistogramBase::Count count_;
263 };
264 
265 }  // namespace base
266 
267 #endif  // BASE_METRICS_HISTOGRAM_SAMPLES_H_
268