910e62b5创建于 1月15日历史提交
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// SampleVector implements HistogramSamples interface. It is used by all
// Histogram based classes to store samples.

#ifndef BASE_METRICS_SAMPLE_VECTOR_H_
#define BASE_METRICS_SAMPLE_VECTOR_H_

#include <stddef.h>
#include <stdint.h>

#include <atomic>
#include <memory>
#include <string>
#include <string_view>
#include <vector>

#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/bucket_ranges.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_samples.h"
#include "base/metrics/persistent_memory_allocator.h"

namespace base {

class BucketRanges;

class BASE_EXPORT SampleVectorBase : public HistogramSamples {
 public:
  SampleVectorBase(const SampleVectorBase&) = delete;
  SampleVectorBase& operator=(const SampleVectorBase&) = delete;
  ~SampleVectorBase() override;

  // HistogramSamples:
  void Accumulate(HistogramBase::Sample32 value,
                  HistogramBase::Count32 count) override;
  HistogramBase::Count32 GetCount(HistogramBase::Sample32 value) const override;
  HistogramBase::Count32 TotalCount() const override;
  std::unique_ptr<SampleCountIterator> Iterator() const override;
  std::unique_ptr<SampleCountIterator> ExtractingIterator() override;

  // Get count of a specific bucket.
  HistogramBase::Count32 GetCountAtIndex(size_t bucket_index) const;

  // Access the bucket ranges held externally.
  const BucketRanges* bucket_ranges() const { return bucket_ranges_; }

  AtomicSingleSample* SingleSampleForTesting() { return &single_sample(); }

 protected:
  SampleVectorBase(uint64_t id,
                   Metadata* meta,
                   const BucketRanges* bucket_ranges);
  SampleVectorBase(uint64_t id,
                   std::unique_ptr<Metadata> meta,
                   const BucketRanges* bucket_ranges);

  bool AddSubtractImpl(
      SampleCountIterator* iter,
      HistogramSamples::Operator op) override;  // |op| is ADD or SUBTRACT.

  virtual size_t GetBucketIndex(HistogramBase::Sample32 value) const;

  // Gets the destination bucket corresponding to `iter` and its `count` value.
  // Validates that the destination bucket matches the min/max from the iterator
  // and returns SIZE_MAX on a mismatch.
  size_t GetDestinationBucketIndexAndCount(SampleCountIterator& iter,
                                           HistogramBase::Count32* count);

  // Moves the single-sample value to a mounted "counts" array.
  void MoveSingleSampleToCounts();

  // Mounts (creating if necessary) an array of "counts" for multi-value
  // storage.
  void MountCountsStorageAndMoveSingleSample();

  // Mounts "counts" storage that already exists. This does not attempt to move
  // any single-sample information to that storage as that would violate the
  // "const" restriction that is often used to indicate read-only memory.
  virtual bool MountExistingCountsStorage() const = 0;

  // Creates "counts" storage and returns a span to it. The span's size must
  // be the number of counts required by the histogram. Ownership of the
  // array remains with the called method but will never change. This must be
  // called while some sort of lock is held to prevent reentry.
  virtual span<HistogramBase::Count32> CreateCountsStorageWhileLocked() = 0;

  std::optional<span<HistogramBase::AtomicCount>> counts() {
    HistogramBase::AtomicCount* data =
        counts_data_.load(std::memory_order_acquire);
    if (data == nullptr) {
      return std::nullopt;
    }
    return UNSAFE_TODO(span(data, counts_size_));
  }

  std::optional<span<const HistogramBase::AtomicCount>> counts() const {
    const HistogramBase::AtomicCount* data =
        counts_data_.load(std::memory_order_acquire);
    if (data == nullptr) {
      return std::nullopt;
    }
    return UNSAFE_TODO(span(data, counts_size_));
  }

  void set_counts(span<HistogramBase::AtomicCount> counts) const {
    CHECK_EQ(counts.size(), counts_size_);
    counts_data_.store(counts.data(), std::memory_order_release);
  }

  size_t counts_size() const { return counts_size_; }

 private:
  friend class SampleVectorTest;
  FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
  FRIEND_TEST_ALL_PREFIXES(SharedHistogramTest, CorruptSampleCounts);

  // Returns a reference into the `counts()` array. As `counts()` may be an
  // empty optional until the array is populated, `counts()` must be checked for
  // having a value before calling `counts_at()`, or this method may CHECK-fail.
  const HistogramBase::AtomicCount& counts_at(size_t index) const {
    return (counts().value())[index];
  }
  HistogramBase::AtomicCount& counts_at(size_t index) {
    return (counts().value())[index];
  }

  // Shares the same BucketRanges with Histogram object.
  const raw_ptr<const BucketRanges> bucket_ranges_;

  // The number of counts in the histogram. Once `counts_data_` becomes
  // non-null, this is the number of values in the `counts_data_` array that
  // are usable by the SampleVector.
  const size_t counts_size_;

  // `counts_data_` is a pointer to a `HistogramBase::AtomicCount` array that is
  // held as an atomic pointer for concurrency reasons. When combined with the
  // single_sample held in the metadata, there are four possible states:
  //   1) single_sample == zero, counts_ == null
  //   2) single_sample != zero, counts_ == null
  //   3) single_sample != zero, counts_ != null BUT IS EMPTY
  //   4) single_sample == zero, counts_ != null and may have data
  // Once `counts_data_` is set to a value, it can never be changed and any
  // existing single-sample must be moved to this storage. It is mutable because
  // changing it doesn't change the (const) data but must adapt if a non-const
  // object causes the storage to be allocated and updated.
  //
  // Held as raw pointer in atomic, instead of as a span, to avoid locks. The
  // `counts_size_` is the size of the would-be span, which is CHECKd when
  // setting the pointer, and used to recreate a span on the way out.
  mutable std::atomic<HistogramBase::AtomicCount*> counts_data_;
};

// A sample vector that uses local memory for the counts array.
class BASE_EXPORT SampleVector : public SampleVectorBase {
 public:
  explicit SampleVector(const BucketRanges* bucket_ranges);
  SampleVector(uint64_t id, const BucketRanges* bucket_ranges);
  SampleVector(const SampleVector&) = delete;
  SampleVector& operator=(const SampleVector&) = delete;
  ~SampleVector() override;

  // HistogramSamples:
  bool IsDefinitelyEmpty() const override;

 private:
  FRIEND_TEST_ALL_PREFIXES(SampleVectorTest, GetPeakBucketSize);

  // HistogramSamples:
  std::string GetAsciiBody() const override;
  std::string GetAsciiHeader(std::string_view histogram_name,
                             int32_t flags) const override;

  // SampleVectorBase:
  bool MountExistingCountsStorage() const override;
  span<HistogramBase::Count32> CreateCountsStorageWhileLocked() override;

  // Writes cumulative percentage information based on the number
  // of past, current, and remaining bucket samples.
  void WriteAsciiBucketContext(int64_t past,
                               HistogramBase::Count32 current,
                               int64_t remaining,
                               uint32_t current_bucket_index,
                               std::string* output) const;

  // Finds out how large (graphically) the largest bucket will appear to be.
  double GetPeakBucketSize() const;

  size_t bucket_count() const { return bucket_ranges()->bucket_count(); }

  // Simple local storage for counts.
  mutable std::vector<HistogramBase::AtomicCount> local_counts_;
};

// A sample vector that uses persistent memory for the counts array.
class BASE_EXPORT PersistentSampleVector : public SampleVectorBase {
 public:
  PersistentSampleVector(std::string_view name,
                         uint64_t id,
                         const BucketRanges* bucket_ranges,
                         Metadata* meta,
                         const DelayedPersistentAllocation& counts);
  PersistentSampleVector(const PersistentSampleVector&) = delete;
  PersistentSampleVector& operator=(const PersistentSampleVector&) = delete;
  ~PersistentSampleVector() override;

  // HistogramSamples:
  bool IsDefinitelyEmpty() const override;

  // Resets the histogram used to log the result of MountExistingCountsStorage.
  // We have tests that monitor histogram creation/restoration. These tests need
  // to be able to initialize the histogram (or more precisely, the static
  // pointer to the histogram) to a known state.
  static void ResetMountExistingCountsStorageResultForTesting();

 private:
  // These values are logged to UMA. Entries should not be renumbered and
  // numeric values should never be reused. Please keep in sync with
  // "MountExistingCountsStorageResult" in
  // src/tools/metrics/histograms/metadata/uma/enums.xml.
  enum class MountExistingCountsStorageResult {
    kSucceeded = 0,
    kNothingToRead = 1,
    kCorrupt = 2,
    kMaxValue = kCorrupt,
  };

  // Pointer used to cache the MountExistingCountsStorageResult histogram for
  // PersistentSampleVector. This is used to avoid creating the histogram on
  // every MountExistingCountsStorage call. Usually, this would an
  // implementation detail hidden in the use of the UMA_HISTOGRAM_ENUMERATION
  // macro, but PersistentSampleVector is a special case where we need to be
  // able to reset the histogram pointer for testing.
  static std::atomic_uintptr_t atomic_histogram_pointer;

  static void RecordMountExistingCountsStorageResult(
      MountExistingCountsStorageResult result);

  // Private implementation of MountExistingCountsStorage
  MountExistingCountsStorageResult MountExistingCountsStorageImpl() const;

  // SampleVectorBase:
  bool MountExistingCountsStorage() const override;
  span<HistogramBase::Count32> CreateCountsStorageWhileLocked() override;

  // Persistent storage for counts.
  DelayedPersistentAllocation persistent_counts_;
};

// Histogram name used to log the result of MountExistingCountsStorage for
// PersistentSampleVector. Exposed here for testing.
inline constexpr std::string_view kMountExistingCountsStorageResult =
    "UMA.PersistentHistograms.MountExistingCountsStorageResult";

}  // namespace base

#endif  // BASE_METRICS_SAMPLE_VECTOR_H_