|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef BASE_METRICS_HISTOGRAM_SAMPLES_H_ | 
|  | #define BASE_METRICS_HISTOGRAM_SAMPLES_H_ | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <limits> | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/atomicops.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/metrics/histogram_base.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | class Pickle; | 
|  | class PickleIterator; | 
|  | class SampleCountIterator; | 
|  |  | 
|  | // HistogramSamples is a container storing all samples of a histogram. All | 
|  | // elements must be of a fixed width to ensure 32/64-bit interoperability. | 
|  | // If this structure changes, bump the version number for kTypeIdHistogram | 
|  | // in persistent_histogram_allocator.cc. | 
|  | // | 
|  | // Note that though these samples are individually consistent (through the use | 
|  | // of atomic operations on the counts), there is only "eventual consistency" | 
|  | // overall when multiple threads are accessing this data. That means that the | 
|  | // sum, redundant-count, etc. could be momentarily out-of-sync with the stored | 
|  | // counts but will settle to a consistent "steady state" once all threads have | 
|  | // exited this code. | 
|  | class BASE_EXPORT HistogramSamples { | 
|  | public: | 
|  | // A single bucket and count. To fit within a single atomic on 32-bit build | 
|  | // architectures, both |bucket| and |count| are limited in size to 16 bits. | 
|  | // This limits the functionality somewhat but if an entry can't fit then | 
|  | // the full array of samples can be allocated and used. | 
|  | struct SingleSample { | 
|  | uint16_t bucket; | 
|  | uint16_t count; | 
|  | }; | 
|  |  | 
|  | // A structure for managing an atomic single sample. Because this is generally | 
|  | // used in association with other atomic values, the defined methods use | 
|  | // acquire/release operations to guarantee ordering with outside values. | 
|  | union BASE_EXPORT AtomicSingleSample { | 
|  | AtomicSingleSample() : as_atomic(0) {} | 
|  | AtomicSingleSample(subtle::Atomic32 rhs) : as_atomic(rhs) {} | 
|  |  | 
|  | // Returns the single sample in an atomic manner. This in an "acquire" | 
|  | // load. The returned sample isn't shared and thus its fields can be safely | 
|  | // accessed. | 
|  | SingleSample Load() const; | 
|  |  | 
|  | // Extracts the single sample in an atomic manner. If |disable| is true | 
|  | // then this object will be set so it will never accumulate another value. | 
|  | // This is "no barrier" so doesn't enforce ordering with other atomic ops. | 
|  | SingleSample Extract(bool disable); | 
|  |  | 
|  | // Adds a given count to the held bucket. If not possible, it returns false | 
|  | // and leaves the parts unchanged. Once extracted/disabled, this always | 
|  | // returns false. This in an "acquire/release" operation. | 
|  | bool Accumulate(size_t bucket, HistogramBase::Count count); | 
|  |  | 
|  | // Returns if the sample has been "disabled" (via Extract) and thus not | 
|  | // allowed to accept further accumulation. | 
|  | bool IsDisabled() const; | 
|  |  | 
|  | private: | 
|  | // union field: The actual sample bucket and count. | 
|  | SingleSample as_parts; | 
|  |  | 
|  | // union field: The sample as an atomic value. Atomic64 would provide | 
|  | // more flexibility but isn't available on all builds. This can hold a | 
|  | // special, internal "disabled" value indicating that it must not accept | 
|  | // further accumulation. | 
|  | subtle::Atomic32 as_atomic; | 
|  | }; | 
|  |  | 
|  | // A structure of information about the data, common to all sample containers. | 
|  | // Because of how this is used in persistent memory, it must be a POD object | 
|  | // that makes sense when initialized to all zeros. | 
|  | struct Metadata { | 
|  | // Expected size for 32/64-bit check. | 
|  | static constexpr size_t kExpectedInstanceSize = 24; | 
|  |  | 
|  | // Initialized when the sample-set is first created with a value provided | 
|  | // by the caller. It is generally used to identify the sample-set across | 
|  | // threads and processes, though not necessarily uniquely as it is possible | 
|  | // to have multiple sample-sets representing subsets of the data. | 
|  | uint64_t id; | 
|  |  | 
|  | // The sum of all the entries, effectivly the sum(sample * count) for | 
|  | // all samples. Despite being atomic, no guarantees are made on the | 
|  | // accuracy of this value; there may be races during histogram | 
|  | // accumulation and snapshotting that we choose to accept. It should | 
|  | // be treated as approximate. | 
|  | #ifdef ARCH_CPU_64_BITS | 
|  | subtle::Atomic64 sum; | 
|  | #else | 
|  | // 32-bit systems don't have atomic 64-bit operations. Use a basic type | 
|  | // and don't worry about "shearing". | 
|  | int64_t sum; | 
|  | #endif | 
|  |  | 
|  | // A "redundant" count helps identify memory corruption. It redundantly | 
|  | // stores the total number of samples accumulated in the histogram. We | 
|  | // can compare this count to the sum of the counts (TotalCount() function), | 
|  | // and detect problems. Note, depending on the implementation of different | 
|  | // histogram types, there might be races during histogram accumulation | 
|  | // and snapshotting that we choose to accept. In this case, the tallies | 
|  | // might mismatch even when no memory corruption has happened. | 
|  | HistogramBase::AtomicCount redundant_count; | 
|  |  | 
|  | // A single histogram value and associated count. This allows histograms | 
|  | // that typically report only a single value to not require full storage | 
|  | // to be allocated. | 
|  | AtomicSingleSample single_sample;  // 32 bits | 
|  | }; | 
|  |  | 
|  | // Because structures held in persistent memory must be POD, there can be no | 
|  | // default constructor to clear the fields. This derived class exists just | 
|  | // to clear them when being allocated on the heap. | 
|  | struct BASE_EXPORT LocalMetadata : Metadata { | 
|  | LocalMetadata(); | 
|  | }; | 
|  |  | 
|  | HistogramSamples(uint64_t id, Metadata* meta); | 
|  | virtual ~HistogramSamples(); | 
|  |  | 
|  | virtual void Accumulate(HistogramBase::Sample value, | 
|  | HistogramBase::Count count) = 0; | 
|  | virtual HistogramBase::Count GetCount(HistogramBase::Sample value) const = 0; | 
|  | virtual HistogramBase::Count TotalCount() const = 0; | 
|  |  | 
|  | virtual void Add(const HistogramSamples& other); | 
|  |  | 
|  | // Add from serialized samples. | 
|  | virtual bool AddFromPickle(PickleIterator* iter); | 
|  |  | 
|  | virtual void Subtract(const HistogramSamples& other); | 
|  |  | 
|  | virtual std::unique_ptr<SampleCountIterator> Iterator() const = 0; | 
|  | virtual void Serialize(Pickle* pickle) const; | 
|  |  | 
|  | // Accessor fuctions. | 
|  | uint64_t id() const { return meta_->id; } | 
|  | int64_t sum() const { | 
|  | #ifdef ARCH_CPU_64_BITS | 
|  | return subtle::NoBarrier_Load(&meta_->sum); | 
|  | #else | 
|  | return meta_->sum; | 
|  | #endif | 
|  | } | 
|  | HistogramBase::Count redundant_count() const { | 
|  | return subtle::NoBarrier_Load(&meta_->redundant_count); | 
|  | } | 
|  |  | 
|  | // Temporarily visible for crash debugging. Should be protected. | 
|  | // TODO(bcwhite): Move this back where it belongs. | 
|  | // https://bugs.chromium.org/p/chromium/issues/detail?id=836875 | 
|  | Metadata* meta() { return meta_; } | 
|  |  | 
|  | protected: | 
|  | enum NegativeSampleReason { | 
|  | SAMPLES_HAVE_LOGGED_BUT_NOT_SAMPLE, | 
|  | SAMPLES_SAMPLE_LESS_THAN_LOGGED, | 
|  | SAMPLES_ADDED_NEGATIVE_COUNT, | 
|  | SAMPLES_ADD_WENT_NEGATIVE, | 
|  | SAMPLES_ADD_OVERFLOW, | 
|  | SAMPLES_ACCUMULATE_NEGATIVE_COUNT, | 
|  | SAMPLES_ACCUMULATE_WENT_NEGATIVE, | 
|  | DEPRECATED_SAMPLES_ACCUMULATE_OVERFLOW, | 
|  | SAMPLES_ACCUMULATE_OVERFLOW, | 
|  | MAX_NEGATIVE_SAMPLE_REASONS | 
|  | }; | 
|  |  | 
|  | // Based on |op| type, add or subtract sample counts data from the iterator. | 
|  | enum Operator { ADD, SUBTRACT }; | 
|  | virtual bool AddSubtractImpl(SampleCountIterator* iter, Operator op) = 0; | 
|  |  | 
|  | // Accumulates to the embedded single-sample field if possible. Returns true | 
|  | // on success, false otherwise. Sum and redundant-count are also updated in | 
|  | // the success case. | 
|  | bool AccumulateSingleSample(HistogramBase::Sample value, | 
|  | HistogramBase::Count count, | 
|  | size_t bucket); | 
|  |  | 
|  | // Atomically adjust the sum and redundant-count. | 
|  | void IncreaseSumAndCount(int64_t sum, HistogramBase::Count count); | 
|  |  | 
|  | // Record a negative-sample observation and the reason why. | 
|  | void RecordNegativeSample(NegativeSampleReason reason, | 
|  | HistogramBase::Count increment); | 
|  |  | 
|  | AtomicSingleSample& single_sample() { return meta_->single_sample; } | 
|  | const AtomicSingleSample& single_sample() const { | 
|  | return meta_->single_sample; | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Depending on derived class meta values can come from local stoarge or | 
|  | // external storage in which case HistogramSamples class cannot take ownership | 
|  | // of Metadata*. | 
|  | Metadata* meta_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(HistogramSamples); | 
|  | }; | 
|  |  | 
|  | class BASE_EXPORT SampleCountIterator { | 
|  | public: | 
|  | virtual ~SampleCountIterator(); | 
|  |  | 
|  | virtual bool Done() const = 0; | 
|  | virtual void Next() = 0; | 
|  |  | 
|  | // Get the sample and count at current position. | 
|  | // |min| |max| and |count| can be NULL if the value is not of interest. | 
|  | // Note: |max| is int64_t because histograms support logged values in the | 
|  | // full int32_t range and bucket max is exclusive, so it needs to support | 
|  | // values up to MAXINT32+1. | 
|  | // Requires: !Done(); | 
|  | virtual void Get(HistogramBase::Sample* min, | 
|  | int64_t* max, | 
|  | HistogramBase::Count* count) const = 0; | 
|  | static_assert(std::numeric_limits<HistogramBase::Sample>::max() < | 
|  | std::numeric_limits<int64_t>::max(), | 
|  | "Get() |max| must be able to hold Histogram::Sample max + 1"); | 
|  |  | 
|  | // Get the index of current histogram bucket. | 
|  | // For histograms that don't use predefined buckets, it returns false. | 
|  | // Requires: !Done(); | 
|  | virtual bool GetBucketIndex(size_t* index) const; | 
|  | }; | 
|  |  | 
|  | class BASE_EXPORT SingleSampleIterator : public SampleCountIterator { | 
|  | public: | 
|  | SingleSampleIterator(HistogramBase::Sample min, | 
|  | int64_t max, | 
|  | HistogramBase::Count count); | 
|  | SingleSampleIterator(HistogramBase::Sample min, | 
|  | int64_t max, | 
|  | HistogramBase::Count count, | 
|  | size_t bucket_index); | 
|  | ~SingleSampleIterator() override; | 
|  |  | 
|  | // SampleCountIterator: | 
|  | bool Done() const override; | 
|  | void Next() override; | 
|  | void Get(HistogramBase::Sample* min, | 
|  | int64_t* max, | 
|  | HistogramBase::Count* count) const override; | 
|  |  | 
|  | // SampleVector uses predefined buckets so iterator can return bucket index. | 
|  | bool GetBucketIndex(size_t* index) const override; | 
|  |  | 
|  | private: | 
|  | // Information about the single value to return. | 
|  | const HistogramBase::Sample min_; | 
|  | const int64_t max_; | 
|  | const size_t bucket_index_; | 
|  | HistogramBase::Count count_; | 
|  | }; | 
|  |  | 
|  | }  // namespace base | 
|  |  | 
|  | #endif  // BASE_METRICS_HISTOGRAM_SAMPLES_H_ |