histogram_samples.h 12 KB

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