histogram_samples.h 10 KB

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