naiveproxy/src/base/metrics/statistics_recorder.h

441 lines
18 KiB
C
Raw Normal View History

2024-11-20 14:32:56 +03:00
// 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.
// StatisticsRecorder holds all Histograms and BucketRanges that are used by
// Histograms in the system. It provides a general place for
// Histograms/BucketRanges to register, and supports a global API for accessing
// (i.e., dumping, or graphing) the data.
#ifndef BASE_METRICS_STATISTICS_RECORDER_H_
#define BASE_METRICS_STATISTICS_RECORDER_H_
#include <stdint.h>
#include <atomic> // For std::memory_order_*.
#include <memory>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include "base/base_export.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/lazy_instance.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/ranges_manager.h"
#include "base/metrics/record_histogram_checker.h"
#include "base/observer_list_threadsafe.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "base/types/pass_key.h"
namespace base {
class BucketRanges;
class HistogramSnapshotManager;
// In-memory recorder of usage statistics (aka metrics, aka histograms).
//
// All the public methods are static and act on a global recorder. This global
// recorder is internally synchronized and all the static methods are thread
// safe. This is intended to only be run/used in the browser process.
//
// StatisticsRecorder doesn't have any public constructor. For testing purpose,
// you can create a temporary recorder using the factory method
// CreateTemporaryForTesting(). This temporary recorder becomes the global one
// until deleted. When this temporary recorder is deleted, it restores the
// previous global one.
class BASE_EXPORT StatisticsRecorder {
public:
// An interface class that allows the StatisticsRecorder to forcibly merge
// histograms from providers when necessary.
class HistogramProvider {
public:
// Merges all histogram information into the global versions. If |async| is
// true, the work may be done asynchronously (though this is not mandatory).
// If false, the work must be done ASAP/synchronously (e.g., because the
// browser is shutting down). |done_callback| should be called on the
// calling thread when all work is finished, regardless of the value of
// |async|.
//
// NOTE: It is possible for this to be called with |async| set to false
// even before a previous call with |async| set to true has finished. Hence,
// if the implementation allows for asynchronous work, ensure that it is
// done in a thread-safe way.
virtual void MergeHistogramDeltas(bool async,
OnceClosure done_callback) = 0;
};
// OnSampleCallback is a convenient callback type that provides information
// about a histogram sample. This is used in conjunction with
// ScopedHistogramSampleObserver to get notified when a sample is collected.
using OnSampleCallback =
base::RepeatingCallback<void(const char* /*=histogram_name*/,
uint64_t /*=name_hash*/,
HistogramBase::Sample)>;
// An observer that gets notified whenever a new sample is recorded for a
// particular histogram. Clients only need to construct it with the histogram
// name and the callback to be invoked. The class starts observing on
// construction and removes itself from the observer list on destruction. The
// clients are always notified on the same sequence in which they were
// registered. This will not get a notification if created while sending out
// that notification.
class BASE_EXPORT ScopedHistogramSampleObserver {
public:
// Constructor. Called with the desired histogram name and the callback to
// be invoked when a sample is recorded.
explicit ScopedHistogramSampleObserver(const std::string& histogram_name,
OnSampleCallback callback);
~ScopedHistogramSampleObserver();
private:
friend class StatisticsRecorder;
// Runs the callback.
void RunCallback(const char* histogram_name,
uint64_t name_hash,
HistogramBase::Sample sample);
// The name of the histogram to observe.
const std::string histogram_name_;
// The client supplied callback that is invoked when the histogram sample is
// collected.
const OnSampleCallback callback_;
};
typedef std::vector<HistogramBase*> Histograms;
typedef size_t SnapshotTransactionId;
StatisticsRecorder(const StatisticsRecorder&) = delete;
StatisticsRecorder& operator=(const StatisticsRecorder&) = delete;
// Restores the previous global recorder.
//
// When several temporary recorders are created using
// CreateTemporaryForTesting(), these recorders must be deleted in reverse
// order of creation.
//
// This method is thread safe.
//
// Precondition: The recorder being deleted is the current global recorder.
~StatisticsRecorder();
// Registers a provider of histograms that can be called to merge those into
// the global recorder. Calls to ImportProvidedHistograms() will fetch from
// registered providers.
//
// This method is thread safe.
static void RegisterHistogramProvider(
const WeakPtr<HistogramProvider>& provider);
// Registers or adds a new histogram to the collection of statistics. If an
// identically named histogram is already registered, then the argument
// |histogram| will be deleted. The returned value is always the registered
// histogram (either the argument, or the pre-existing registered histogram).
//
// This method is thread safe.
static HistogramBase* RegisterOrDeleteDuplicate(HistogramBase* histogram);
// Registers or adds a new BucketRanges. If an equivalent BucketRanges is
// already registered, then the argument |ranges| will be deleted. The
// returned value is always the registered BucketRanges (either the argument,
// or the pre-existing one).
//
// This method is thread safe.
static const BucketRanges* RegisterOrDeleteDuplicateRanges(
const BucketRanges* ranges);
// A method for appending histogram data to a string. Only histograms which
// have |query| as a substring are written to |output| (an empty string will
// process all registered histograms).
//
// This method is thread safe.
static void WriteGraph(const std::string& query, std::string* output);
// Returns the histograms with |verbosity_level| as the serialization
// verbosity.
//
// This method is thread safe.
static std::string ToJSON(JSONVerbosityLevel verbosity_level);
// Gets existing histograms. |include_persistent| determines whether
// histograms held in persistent storage are included.
//
// The order of returned histograms is not guaranteed.
//
// Ownership of the individual histograms remains with the StatisticsRecorder.
//
// This method is thread safe.
static Histograms GetHistograms(bool include_persistent = true)
LOCKS_EXCLUDED(GetLock());
// Gets BucketRanges used by all histograms registered. The order of returned
// BucketRanges is not guaranteed.
//
// This method is thread safe.
static std::vector<const BucketRanges*> GetBucketRanges();
// Finds a histogram by name. Matches the exact name. Returns a null pointer
// if a matching histogram is not found.
//
// This method is thread safe.
static HistogramBase* FindHistogram(std::string_view name);
// Imports histograms from providers. If |async| is true, the providers may do
// the work asynchronously (though this is not guaranteed and it is up to the
// providers to decide). If false, the work will be done synchronously.
// |done_callback| is called on the calling thread when all providers have
// finished.
//
// This method must be called on the UI thread.
static void ImportProvidedHistograms(bool async, OnceClosure done_callback);
// Convenience function that calls ImportProvidedHistograms() with |async|
// set to false, and with a no-op |done_callback|.
static void ImportProvidedHistogramsSync();
// Snapshots all histogram deltas via |snapshot_manager|. This marks the
// deltas as logged. |include_persistent| determines whether histograms held
// in persistent storage are snapshotted. |flags_to_set| is used to set flags
// for each histogram. |required_flags| is used to select which histograms to
// record. Only histograms with all required flags are selected. If all
// histograms should be recorded, use |Histogram::kNoFlags| as the required
// flag. This is logically equivalent to calling SnapshotUnloggedSamples()
// followed by HistogramSnapshotManager::MarkUnloggedSamplesAsLogged() on
// |snapshot_manager|. Returns the snapshot transaction ID associated with
// this operation. Thread-safe.
static SnapshotTransactionId PrepareDeltas(
bool include_persistent,
HistogramBase::Flags flags_to_set,
HistogramBase::Flags required_flags,
HistogramSnapshotManager* snapshot_manager)
LOCKS_EXCLUDED(snapshot_lock_.Pointer());
// Same as PrepareDeltas() above, but the samples are not marked as logged.
// This includes persistent histograms, and no flags will be set. A call to
// HistogramSnapshotManager::MarkUnloggedSamplesAsLogged() on the passed
// |snapshot_manager| should be made to mark them as logged. Returns the
// snapshot transaction ID associated with this operation. Thread-safe.
static SnapshotTransactionId SnapshotUnloggedSamples(
HistogramBase::Flags required_flags,
HistogramSnapshotManager* snapshot_manager)
LOCKS_EXCLUDED(snapshot_lock_.Pointer());
// Returns the transaction ID of the last snapshot performed (either through
// PrepareDeltas() or SnapshotUnloggedSamples()). Returns 0 if a snapshot was
// never taken so far. Thread-safe.
static SnapshotTransactionId GetLastSnapshotTransactionId()
LOCKS_EXCLUDED(snapshot_lock_.Pointer());
// Retrieves and runs the list of callbacks for the histogram referred to by
// |histogram_name|, if any.
//
// This method is thread safe.
static void FindAndRunHistogramCallbacks(base::PassKey<HistogramBase>,
const char* histogram_name,
uint64_t name_hash,
HistogramBase::Sample sample);
// Returns the number of known histograms.
//
// This method is thread safe.
static size_t GetHistogramCount();
// Initializes logging histograms with --v=1. Safe to call multiple times.
// Is called from ctor but for browser it seems that it is more useful to
// start logging after statistics recorder, so we need to init log-on-shutdown
// later.
//
// This method is thread safe.
static void InitLogOnShutdown();
// Removes a histogram from the internal set of known ones. This can be
// necessary during testing persistent histograms where the underlying
// memory is being released.
//
// This method is thread safe.
static void ForgetHistogramForTesting(std::string_view name);
// Creates a temporary StatisticsRecorder object for testing purposes. All new
// histograms will be registered in it until it is destructed or pushed aside
// for the lifetime of yet another StatisticsRecorder object. The destruction
// of the returned object will re-activate the previous one.
// StatisticsRecorder objects must be deleted in the opposite order to which
// they're created.
//
// This method is thread safe.
[[nodiscard]] static std::unique_ptr<StatisticsRecorder>
CreateTemporaryForTesting();
// Sets the record checker for determining if a histogram should be recorded.
// Record checker doesn't affect any already recorded histograms, so this
// method must be called very early, before any threads have started.
// Record checker methods can be called on any thread, so they shouldn't
// mutate any state.
static void SetRecordChecker(
std::unique_ptr<RecordHistogramChecker> record_checker);
// Checks if the given histogram should be recorded based on the
// ShouldRecord() method of the record checker. If the record checker is not
// set, returns true.
// |histogram_hash| corresponds to the result of HashMetricNameAs32Bits().
//
// This method is thread safe.
static bool ShouldRecordHistogram(uint32_t histogram_hash);
// Sorts histograms by name.
static Histograms Sort(Histograms histograms);
// Filters histograms by name. Only histograms which have |query| as a
// substring in their name are kept. An empty query keeps all histograms.
// |case_sensitive| determines whether the matching should be done in a
// case sensitive way.
static Histograms WithName(Histograms histograms,
const std::string& query,
bool case_sensitive = true);
using GlobalSampleCallback = void (*)(const char* /*=histogram_name*/,
uint64_t /*=name_hash*/,
HistogramBase::Sample);
// Installs a global callback which will be called for every added
// histogram sample. The given callback is a raw function pointer in order
// to be accessed lock-free and can be called on any thread.
static void SetGlobalSampleCallback(
const GlobalSampleCallback& global_sample_callback);
// Returns the global callback, if any, that should be called every time a
// histogram sample is added.
static GlobalSampleCallback global_sample_callback() {
return global_sample_callback_.load(std::memory_order_relaxed);
}
// Returns whether there's either a global histogram callback set,
// or if any individual histograms have callbacks set. Used for early return
// when histogram samples are added.
static bool have_active_callbacks() {
return have_active_callbacks_.load(std::memory_order_relaxed);
}
private:
static Lock& GetLock() { return lock_.Get(); }
static void AssertLockHeld() { lock_.Get().AssertAcquired(); }
// Returns the histogram registered with |hash|, if there is one. Returns
// nullptr otherwise.
// Note: |name| is only used in DCHECK builds to assert that there was no
// collision (i.e. different histograms with the same hash).
HistogramBase* FindHistogramByHashInternal(uint64_t hash,
std::string_view name) const
EXCLUSIVE_LOCKS_REQUIRED(GetLock());
// Adds an observer to be notified when a new sample is recorded on
// the histogram referred to by |histogram_name|. Observers added
// while sending out notification are not notified. Can be called
// before or after the histogram is created.
//
// This method is thread safe.
static void AddHistogramSampleObserver(
const std::string& histogram_name,
ScopedHistogramSampleObserver* observer);
// Clears the given |observer| set on the histogram referred to by
// |histogram_name|.
//
// This method is thread safe.
static void RemoveHistogramSampleObserver(
const std::string& histogram_name,
ScopedHistogramSampleObserver* observer);
typedef std::vector<WeakPtr<HistogramProvider>> HistogramProviders;
// A map of histogram name hash (see HashMetricName()) to histogram object.
typedef std::unordered_map<uint64_t, HistogramBase*> HistogramMap;
// A map of histogram name hash (see HashMetricName()) to registered observers
// If the histogram isn't created yet, the observers will be added after
// creation.
using HistogramSampleObserverList =
base::ObserverListThreadSafe<ScopedHistogramSampleObserver>;
typedef std::unordered_map<uint64_t,
scoped_refptr<HistogramSampleObserverList>>
ObserverMap;
friend class StatisticsRecorderTest;
FRIEND_TEST_ALL_PREFIXES(StatisticsRecorderTest, IterationTest);
// Initializes the global recorder if it doesn't already exist. Safe to call
// multiple times.
static void EnsureGlobalRecorderWhileLocked()
EXCLUSIVE_LOCKS_REQUIRED(GetLock());
// Gets histogram providers.
//
// This method is thread safe.
static HistogramProviders GetHistogramProviders();
// Imports histograms from global persistent memory.
static void ImportGlobalPersistentHistograms() LOCKS_EXCLUDED(GetLock());
// Constructs a new StatisticsRecorder and sets it as the current global
// recorder.
//
// This singleton instance should be started during the single-threaded
// portion of startup and hence it is not thread safe. It initializes globals
// to provide support for all future calls.
StatisticsRecorder() EXCLUSIVE_LOCKS_REQUIRED(GetLock());
// Initialize implementation but without lock. Caller should guard
// StatisticsRecorder by itself if needed (it isn't in unit tests).
static void InitLogOnShutdownWhileLocked()
EXCLUSIVE_LOCKS_REQUIRED(GetLock());
HistogramMap histograms_;
ObserverMap observers_;
HistogramProviders providers_;
RangesManager ranges_manager_;
std::unique_ptr<RecordHistogramChecker> record_checker_;
// Previous global recorder that existed when this one was created.
raw_ptr<StatisticsRecorder> previous_ = nullptr;
// Global lock for internal synchronization.
// Note: Care must be taken to not read or write anything to persistent memory
// while holding this lock, as that could cause a file I/O stall.
static LazyInstance<Lock>::Leaky lock_;
// Global lock for internal synchronization of histogram snapshots.
static LazyInstance<base::Lock>::Leaky snapshot_lock_;
// A strictly increasing number that is incremented every time a snapshot is
// taken (by either calling SnapshotUnloggedSamples() or PrepareDeltas()).
// This represents the transaction ID of the last snapshot taken.
static SnapshotTransactionId last_snapshot_transaction_id_
GUARDED_BY(snapshot_lock_.Get());
// Current global recorder. This recorder is used by static methods. When a
// new global recorder is created by CreateTemporaryForTesting(), then the
// previous global recorder is referenced by top_->previous_.
static StatisticsRecorder* top_ GUARDED_BY(GetLock());
// Tracks whether InitLogOnShutdownWhileLocked() has registered a logging
// function that will be called when the program finishes.
static bool is_vlog_initialized_;
// Track whether there are active histogram callbacks present.
static std::atomic<bool> have_active_callbacks_;
// Stores a raw callback which should be called on any every histogram sample
// which gets added.
static std::atomic<GlobalSampleCallback> global_sample_callback_;
};
} // namespace base
#endif // BASE_METRICS_STATISTICS_RECORDER_H_