histogram_samples.h 10 KB

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