// 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.
// 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 <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "base/base_export.h"
#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/record_histogram_checker.h"
#include "base/strings/string_piece.h"
#include "base/synchronization/lock.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.
//
// 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:
virtual ~HistogramProvider() {}
// Merges all histogram information into the global versions.
virtual void MergeHistogramDeltas() = 0;
};
typedef std::vector<HistogramBase*> Histograms;
// 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);
// Methods 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).
//
// These methods are thread safe.
static void WriteHTMLGraph(const std::string& query, std::string* output);
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.
//
// 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();
// 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(base::StringPiece name);
// Imports histograms from providers.
//
// This method must be called on the UI thread.
static void ImportProvidedHistograms();
// Snapshots all histograms via |snapshot_manager|. |flags_to_set| is used to
// set flags for each histogram. |required_flags| is used to select
// histograms to be recorded. Only histograms that have all the flags
// specified by the argument will be chosen. If all histograms should be
// recorded, set it to |Histogram::kNoFlags|.
static void PrepareDeltas(bool include_persistent,
HistogramBase::Flags flags_to_set,
HistogramBase::Flags required_flags,
HistogramSnapshotManager* snapshot_manager);
typedef base::Callback<void(HistogramBase::Sample)> OnSampleCallback;
// Sets the callback to notify when a new sample is recorded on the histogram
// referred to by |histogram_name|. Can be called before or after the
// histogram is created. Returns whether the callback was successfully set.
//
// This method is thread safe.
static bool SetCallback(const std::string& histogram_name,
const OnSampleCallback& callback);
// Clears any callback set on the histogram referred to by |histogram_name|.
//
// This method is thread safe.
static void ClearCallback(const std::string& histogram_name);
// Retrieves the callback for the histogram referred to by |histogram_name|,
// or a null callback if no callback exists for this histogram.
//
// This method is thread safe.
static OnSampleCallback FindCallback(const std::string& histogram_name);
// 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(base::StringPiece 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.
static std::unique_ptr<StatisticsRecorder> CreateTemporaryForTesting()
WARN_UNUSED_RESULT;
// 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.
//
// This method is thread safe.
static bool ShouldRecordHistogram(uint64_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.
static Histograms WithName(Histograms histograms, const std::string& query);
// Filters histograms by persistency. Only non-persistent histograms are kept.
static Histograms NonPersistent(Histograms histograms);
private:
typedef std::vector<WeakPtr<HistogramProvider>> HistogramProviders;
typedef std::unordered_map<StringPiece, HistogramBase*, StringPieceHash>
HistogramMap;
// We keep a map of callbacks to histograms, so that as histograms are
// created, we can set the callback properly.
typedef std::unordered_map<std::string, OnSampleCallback> CallbackMap;
struct BucketRangesHash {
size_t operator()(const BucketRanges* a) const;
};
struct BucketRangesEqual {
bool operator()(const BucketRanges* a, const BucketRanges* b) const;
};
typedef std::
unordered_set<const BucketRanges*, BucketRangesHash, BucketRangesEqual>
RangesMap;
friend class StatisticsRecorderTest;
FRIEND_TEST_ALL_PREFIXES(StatisticsRecorderTest, IterationTest);
// Initializes the global recorder if it doesn't already exist. Safe to call
// multiple times.
//
// Precondition: The global lock is already acquired.
static void EnsureGlobalRecorderWhileLocked();
// Gets histogram providers.
//
// This method is thread safe.
static HistogramProviders GetHistogramProviders();
// Imports histograms from global persistent memory.
//
// Precondition: The global lock must not be held during this call.
static void ImportGlobalPersistentHistograms();
// Constructs a new StatisticsRecorder and sets it as the current global
// recorder.
//
// Precondition: The global lock is already acquired.
StatisticsRecorder();
// Initialize implementation but without lock. Caller should guard
// StatisticsRecorder by itself if needed (it isn't in unit tests).
//
// Precondition: The global lock is already acquired.
static void InitLogOnShutdownWhileLocked();
HistogramMap histograms_;
CallbackMap callbacks_;
RangesMap ranges_;
HistogramProviders providers_;
std::unique_ptr<RecordHistogramChecker> record_checker_;
// Previous global recorder that existed when this one was created.
StatisticsRecorder* previous_ = nullptr;
// Global lock for internal synchronization.
static LazyInstance<Lock>::Leaky lock_;
// 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_;
// Tracks whether InitLogOnShutdownWhileLocked() has registered a logging
// function that will be called when the program finishes.
static bool is_vlog_initialized_;
DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder);
};
} // namespace base
#endif // BASE_METRICS_STATISTICS_RECORDER_H_