Commit 4c009ba6 authored by Wez's avatar Wez Committed by Commit Bot

Revert "Simplified and optimized StatisticsRecorder."

This reverts commit b86ebc88.

Reason for revert: Introduces flakiness due to removal of global StatisticsRecorder teardown - see crbug.com/798717. This is a cross-platyform issue but in practice only caused base_unittests failures on the Fuchsia FYI bots, presumably due to differences in test ordering.

Original change's description:
> Simplified and optimized StatisticsRecorder.
> 
> StatisticsRecorder is now an actual container of collections.
> 
> There is only one global recorder at any time, referenced by
> StatisticsRecorder::top_.
> 
> Each StatisticsRecorder also has a pointer to the previous global recorder.
> 
> We now have a stack (singly linked list) of StatisticsRecorder objects, where
> the top of the stack (StatisticsRecorder::top_) is the current global recorder.
> 
> This pattern is clearer and cleaner. It avoids a bunch of static variables,
> indirections, unique_ptrs and calls to operator new. It gets rid of the ugly
> hacks of UninitializeForTesting. It reduces memory fragmentation by having fewer
> dynamically allocated objects.
> 
> Removed methods Reset, UninitializeForTesting and DumpHistogramsToVlog. They are
> now unnecessary.
> 
> Reviewed, clarified and documented the thread safety status of each method.
> 
> Added comments.
> 
> 
> Change-Id: Ia1a7611905009d0449068c801464ad0df7813c0d
> Bug: 
> Reviewed-on: https://chromium-review.googlesource.com/830997
> Commit-Queue: François Degros <fdegros@chromium.org>
> Reviewed-by: Alexei Svitkine <asvitkine@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#526570}

TBR=asvitkine@chromium.org,gayane@chromium.org,holte@chromium.org,skare@chromium.org,fdegros@chromium.org

Change-Id: I4bd60c636a821bf681eb452636ade1098d3a4899
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/848273Reviewed-by: default avatarWez <wez@chromium.org>
Commit-Queue: Wez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#526718}
parent e8955cf2
......@@ -20,9 +20,12 @@
#include "base/strings/stringprintf.h"
#include "base/values.h"
namespace base {
namespace {
// Initialize histogram statistics gathering system.
base::LazyInstance<base::StatisticsRecorder>::Leaky g_statistics_recorder_ =
LAZY_INSTANCE_INITIALIZER;
bool HistogramNameLesser(const base::HistogramBase* a,
const base::HistogramBase* b) {
return strcmp(a->histogram_name(), b->histogram_name()) < 0;
......@@ -30,14 +33,7 @@ bool HistogramNameLesser(const base::HistogramBase* a,
} // namespace
// static
LazyInstance<Lock>::Leaky StatisticsRecorder::lock_;
// static
StatisticsRecorder* StatisticsRecorder::top_ = nullptr;
// static
bool StatisticsRecorder::is_vlog_initialized_ = false;
namespace base {
size_t StatisticsRecorder::BucketRangesHash::operator()(
const BucketRanges* const a) const {
......@@ -51,55 +47,67 @@ bool StatisticsRecorder::BucketRangesEqual::operator()(
}
StatisticsRecorder::~StatisticsRecorder() {
const AutoLock auto_lock(lock_.Get());
DCHECK_EQ(this, top_);
top_ = previous_;
DCHECK(histograms_);
DCHECK(ranges_);
// Clean out what this object created and then restore what existed before.
Reset();
base::AutoLock auto_lock(lock_.Get());
histograms_ = existing_histograms_.release();
callbacks_ = existing_callbacks_.release();
ranges_ = existing_ranges_.release();
providers_ = existing_providers_.release();
record_checker_ = existing_record_checker_.release();
}
// static
void StatisticsRecorder::Initialize() {
const AutoLock auto_lock(lock_.Get());
if (top_)
// Tests sometimes create local StatisticsRecorders in order to provide a
// contained environment of histograms that can be later discarded. If a
// true global instance gets created in this environment then it will
// eventually get disconnected when the local instance destructs and
// restores the previous state, resulting in no StatisticsRecorder at all.
// The global lazy instance, however, will remain valid thus ensuring that
// another never gets installed via this method. If a |histograms_| map
// exists then assume the StatisticsRecorder is already "initialized".
if (histograms_)
return;
const StatisticsRecorder* const p = new StatisticsRecorder;
// The global recorder is never deleted.
ANNOTATE_LEAKING_OBJECT_PTR(p);
DCHECK_EQ(p, top_);
// Ensure that an instance of the StatisticsRecorder object is created.
g_statistics_recorder_.Get();
}
// static
bool StatisticsRecorder::IsActive() {
const AutoLock auto_lock(lock_.Get());
return top_ != nullptr;
base::AutoLock auto_lock(lock_.Get());
return histograms_ != nullptr;
}
// static
void StatisticsRecorder::RegisterHistogramProvider(
const WeakPtr<HistogramProvider>& provider) {
const AutoLock auto_lock(lock_.Get());
top_->providers_.push_back(provider);
providers_->push_back(provider);
}
// static
HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
HistogramBase* histogram) {
// Declared before |auto_lock| to ensure correct destruction order.
// Declared before auto_lock to ensure correct destruction order.
std::unique_ptr<HistogramBase> histogram_deleter;
const AutoLock auto_lock(lock_.Get());
base::AutoLock auto_lock(lock_.Get());
if (!top_) {
if (!histograms_) {
// As per crbug.com/79322 the histograms are intentionally leaked, so we
// need to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used
// only once for an object, the duplicates should not be annotated.
// Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr)
// twice |if (!top_)|.
// twice |if (!histograms_)|.
ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
return histogram;
}
const char* const name = histogram->histogram_name();
HistogramBase*& registered = top_->histograms_[name];
HistogramBase*& registered = (*histograms_)[name];
if (!registered) {
// |name| is guaranteed to never change or be deallocated so long
......@@ -108,8 +116,8 @@ HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
// If there are callbacks for this histogram, we set the kCallbackExists
// flag.
const auto callback_iterator = top_->callbacks_.find(name);
if (callback_iterator != top_->callbacks_.end()) {
const auto callback_iterator = callbacks_->find(name);
if (callback_iterator != callbacks_->end()) {
if (!callback_iterator->second.is_null())
histogram->SetFlags(HistogramBase::kCallbackExists);
else
......@@ -133,16 +141,16 @@ const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
const BucketRanges* ranges) {
DCHECK(ranges->HasValidChecksum());
// Declared before |auto_lock| to ensure correct destruction order.
// Declared before auto_lock to ensure correct destruction order.
std::unique_ptr<const BucketRanges> ranges_deleter;
const AutoLock auto_lock(lock_.Get());
base::AutoLock auto_lock(lock_.Get());
if (!top_) {
if (!ranges_) {
ANNOTATE_LEAKING_OBJECT_PTR(ranges);
return ranges;
}
const BucketRanges* const registered = *top_->ranges_.insert(ranges).first;
const BucketRanges* const registered = *ranges_->insert(ranges).first;
if (registered == ranges) {
ANNOTATE_LEAKING_OBJECT_PTR(ranges);
} else {
......@@ -155,6 +163,9 @@ const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
// static
void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
std::string* output) {
if (!IsActive())
return;
Histograms snapshot;
GetSnapshot(query, &snapshot);
std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser);
......@@ -167,6 +178,8 @@ void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
// static
void StatisticsRecorder::WriteGraph(const std::string& query,
std::string* output) {
if (!IsActive())
return;
if (query.length())
StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
else
......@@ -183,6 +196,9 @@ void StatisticsRecorder::WriteGraph(const std::string& query,
// static
std::string StatisticsRecorder::ToJSON(JSONVerbosityLevel verbosity_level) {
if (!IsActive())
return std::string();
Histograms snapshot;
GetSnapshot(std::string(), &snapshot);
......@@ -201,11 +217,11 @@ std::string StatisticsRecorder::ToJSON(JSONVerbosityLevel verbosity_level) {
// static
void StatisticsRecorder::GetHistograms(Histograms* output) {
const AutoLock auto_lock(lock_.Get());
if (!top_)
base::AutoLock auto_lock(lock_.Get());
if (!histograms_)
return;
for (const auto& entry : top_->histograms_) {
for (const auto& entry : *histograms_) {
output->push_back(entry.second);
}
}
......@@ -213,11 +229,11 @@ void StatisticsRecorder::GetHistograms(Histograms* output) {
// static
void StatisticsRecorder::GetBucketRanges(
std::vector<const BucketRanges*>* output) {
const AutoLock auto_lock(lock_.Get());
if (!top_)
base::AutoLock auto_lock(lock_.Get());
if (!ranges_)
return;
for (const BucketRanges* const p : top_->ranges_) {
for (const BucketRanges* const p : *ranges_) {
output->push_back(p);
}
}
......@@ -229,28 +245,21 @@ HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) {
// will acquire the lock at that time.
ImportGlobalPersistentHistograms();
const AutoLock auto_lock(lock_.Get());
if (!top_)
base::AutoLock auto_lock(lock_.Get());
if (!histograms_)
return nullptr;
const HistogramMap::const_iterator it = top_->histograms_.find(name);
return it != top_->histograms_.end() ? it->second : nullptr;
}
// static
StatisticsRecorder::HistogramProviders
StatisticsRecorder::GetHistogramProviders() {
const AutoLock auto_lock(lock_.Get());
if (!top_)
return {};
return top_->providers_;
const HistogramMap::const_iterator it = histograms_->find(name);
return it != histograms_->end() ? it->second : nullptr;
}
// static
void StatisticsRecorder::ImportProvidedHistograms() {
if (!providers_)
return;
// Merge histogram data from each provider in turn.
for (const WeakPtr<HistogramProvider>& provider : GetHistogramProviders()) {
for (const WeakPtr<HistogramProvider>& provider : *providers_) {
// Weak-pointer may be invalid if the provider was destructed, though they
// generally never are.
if (provider)
......@@ -274,8 +283,11 @@ void StatisticsRecorder::PrepareDeltas(
// static
void StatisticsRecorder::InitLogOnShutdown() {
const AutoLock auto_lock(lock_.Get());
InitLogOnShutdownWhileLocked();
if (!histograms_)
return;
base::AutoLock auto_lock(lock_.Get());
g_statistics_recorder_.Get().InitLogOnShutdownWithoutLock();
}
// static
......@@ -286,14 +298,14 @@ void StatisticsRecorder::GetSnapshot(const std::string& query,
// will acquire the lock at that time.
ImportGlobalPersistentHistograms();
const AutoLock auto_lock(lock_.Get());
if (!top_)
base::AutoLock auto_lock(lock_.Get());
if (!histograms_)
return;
// Need a c-string query for comparisons against c-string histogram name.
const char* query_string = query.c_str();
for (const auto& entry : top_->histograms_) {
for (const auto& entry : *histograms_) {
if (strstr(entry.second->histogram_name(), query_string) != nullptr)
snapshot->push_back(entry.second);
}
......@@ -304,15 +316,15 @@ bool StatisticsRecorder::SetCallback(
const std::string& name,
const StatisticsRecorder::OnSampleCallback& cb) {
DCHECK(!cb.is_null());
const AutoLock auto_lock(lock_.Get());
if (!top_)
base::AutoLock auto_lock(lock_.Get());
if (!histograms_)
return false;
if (!top_->callbacks_.insert({name, cb}).second)
if (!callbacks_->insert({name, cb}).second)
return false;
const HistogramMap::const_iterator it = top_->histograms_.find(name);
if (it != top_->histograms_.end())
const HistogramMap::const_iterator it = histograms_->find(name);
if (it != histograms_->end())
it->second->SetFlags(HistogramBase::kCallbackExists);
return true;
......@@ -320,43 +332,42 @@ bool StatisticsRecorder::SetCallback(
// static
void StatisticsRecorder::ClearCallback(const std::string& name) {
const AutoLock auto_lock(lock_.Get());
if (!top_)
base::AutoLock auto_lock(lock_.Get());
if (!histograms_)
return;
top_->callbacks_.erase(name);
callbacks_->erase(name);
// We also clear the flag from the histogram (if it exists).
const HistogramMap::const_iterator it = top_->histograms_.find(name);
if (it != top_->histograms_.end())
const HistogramMap::const_iterator it = histograms_->find(name);
if (it != histograms_->end())
it->second->ClearFlags(HistogramBase::kCallbackExists);
}
// static
StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback(
const std::string& name) {
const AutoLock auto_lock(lock_.Get());
if (!top_)
base::AutoLock auto_lock(lock_.Get());
if (!histograms_)
return OnSampleCallback();
const auto it = top_->callbacks_.find(name);
return it != top_->callbacks_.end() ? it->second : OnSampleCallback();
const auto it = callbacks_->find(name);
return it != callbacks_->end() ? it->second : OnSampleCallback();
}
// static
size_t StatisticsRecorder::GetHistogramCount() {
const AutoLock auto_lock(lock_.Get());
return top_ ? top_->histograms_.size() : 0;
base::AutoLock auto_lock(lock_.Get());
return histograms_ ? histograms_->size() : 0;
}
// static
void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) {
const AutoLock auto_lock(lock_.Get());
if (!top_)
if (!histograms_)
return;
const HistogramMap::iterator found = top_->histograms_.find(name);
if (found == top_->histograms_.end())
const HistogramMap::iterator found = histograms_->find(name);
if (found == histograms_->end())
return;
HistogramBase* const base = found->second;
......@@ -368,40 +379,53 @@ void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) {
static_cast<Histogram*>(base)->bucket_ranges()->set_persistent_reference(0);
}
top_->histograms_.erase(found);
histograms_->erase(found);
}
// static
std::unique_ptr<StatisticsRecorder>
StatisticsRecorder::CreateTemporaryForTesting() {
const AutoLock auto_lock(lock_.Get());
return WrapUnique(new StatisticsRecorder());
}
// static
void StatisticsRecorder::UninitializeForTesting() {
// Stop now if it's never been initialized.
if (!histograms_)
return;
// Get the global instance and destruct it. It's held in static memory so
// can't "delete" it; call the destructor explicitly.
DCHECK(g_statistics_recorder_.private_instance_);
g_statistics_recorder_.Get().~StatisticsRecorder();
// Now the ugly part. There's no official way to release a LazyInstance once
// created so it's necessary to clear out an internal variable which
// shouldn't be publicly visible but is for initialization reasons.
g_statistics_recorder_.private_instance_ = 0;
}
// static
void StatisticsRecorder::SetRecordChecker(
std::unique_ptr<RecordHistogramChecker> record_checker) {
const AutoLock auto_lock(lock_.Get());
top_->record_checker_ = std::move(record_checker);
record_checker_ = record_checker.release();
}
// static
bool StatisticsRecorder::ShouldRecordHistogram(uint64_t histogram_hash) {
const AutoLock auto_lock(lock_.Get());
return !top_ || !top_->record_checker_ ||
top_->record_checker_->ShouldRecord(histogram_hash);
return !record_checker_ || record_checker_->ShouldRecord(histogram_hash);
}
// static
StatisticsRecorder::Histograms StatisticsRecorder::GetKnownHistograms(
bool include_persistent) {
Histograms known;
const AutoLock auto_lock(lock_.Get());
if (!top_ || top_->histograms_.empty())
base::AutoLock auto_lock(lock_.Get());
if (!histograms_ || histograms_->empty())
return known;
known.reserve(top_->histograms_.size());
for (const auto& h : top_->histograms_) {
known.reserve(histograms_->size());
for (const auto& h : *histograms_) {
if (include_persistent ||
(h.second->flags() & HistogramBase::kIsPersistent) == 0)
known.push_back(h.second);
......@@ -412,6 +436,9 @@ StatisticsRecorder::Histograms StatisticsRecorder::GetKnownHistograms(
// static
void StatisticsRecorder::ImportGlobalPersistentHistograms() {
if (!histograms_)
return;
// Import histograms from known persistent storage. Histograms could have been
// added by other processes and they must be fetched and recognized locally.
// If the persistent memory segment is not shared between processes, this call
......@@ -424,24 +451,73 @@ void StatisticsRecorder::ImportGlobalPersistentHistograms() {
// of main(), and hence it is not thread safe. It initializes globals to provide
// support for all future calls.
StatisticsRecorder::StatisticsRecorder() {
lock_.Get().AssertAcquired();
previous_ = top_;
top_ = this;
InitLogOnShutdownWhileLocked();
}
// static
void StatisticsRecorder::InitLogOnShutdownWhileLocked() {
lock_.Get().AssertAcquired();
if (!is_vlog_initialized_ && VLOG_IS_ON(1)) {
is_vlog_initialized_ = true;
const auto dump_to_vlog = [](void*) {
std::string output;
WriteGraph("", &output);
VLOG(1) << output;
};
AtExitManager::RegisterCallback(dump_to_vlog, nullptr);
base::AutoLock auto_lock(lock_.Get());
existing_histograms_.reset(histograms_);
existing_callbacks_.reset(callbacks_);
existing_ranges_.reset(ranges_);
existing_providers_.reset(providers_);
existing_record_checker_.reset(record_checker_);
histograms_ = new HistogramMap;
callbacks_ = new CallbackMap;
ranges_ = new RangesMap;
providers_ = new HistogramProviders;
record_checker_ = nullptr;
InitLogOnShutdownWithoutLock();
}
void StatisticsRecorder::InitLogOnShutdownWithoutLock() {
if (!vlog_initialized_ && VLOG_IS_ON(1)) {
vlog_initialized_ = true;
AtExitManager::RegisterCallback(&DumpHistogramsToVlog, this);
}
}
// static
void StatisticsRecorder::Reset() {
// Declared before auto_lock to ensure correct destruction order.
std::unique_ptr<HistogramMap> histograms_deleter;
std::unique_ptr<CallbackMap> callbacks_deleter;
std::unique_ptr<RangesMap> ranges_deleter;
std::unique_ptr<HistogramProviders> providers_deleter;
std::unique_ptr<RecordHistogramChecker> record_checker_deleter;
base::AutoLock auto_lock(lock_.Get());
histograms_deleter.reset(histograms_);
callbacks_deleter.reset(callbacks_);
ranges_deleter.reset(ranges_);
providers_deleter.reset(providers_);
record_checker_deleter.reset(record_checker_);
histograms_ = nullptr;
callbacks_ = nullptr;
ranges_ = nullptr;
providers_ = nullptr;
record_checker_ = nullptr;
// We are going to leak the histograms and the ranges.
}
// static
void StatisticsRecorder::DumpHistogramsToVlog(void* instance) {
std::string output;
StatisticsRecorder::WriteGraph(std::string(), &output);
VLOG(1) << output;
}
// static
StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = nullptr;
// static
StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = nullptr;
// static
StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = nullptr;
// static
StatisticsRecorder::HistogramProviders* StatisticsRecorder::providers_ =
nullptr;
// static
RecordHistogramChecker* StatisticsRecorder::record_checker_ = nullptr;
// static
base::LazyInstance<base::Lock>::Leaky StatisticsRecorder::lock_ =
LAZY_INSTANCE_INITIALIZER;
} // namespace base
......@@ -34,18 +34,6 @@ namespace base {
class BucketRanges;
class HistogramSnapshotManager;
// In-memory recorder of usage statistics (aka metrics, aka histograms).
//
// Most of the methods are static and act on a global recorder. This global
// recorder must first be initialized using Initialize() before being used. 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
......@@ -58,83 +46,58 @@ class BASE_EXPORT StatisticsRecorder {
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();
// Initializes the global recorder. Safe to call multiple times.
//
// This method is thread safe.
// Initializes the StatisticsRecorder system. Safe to call multiple times.
static void Initialize();
// Finds out if histograms can now be registered into our list.
//
// This method is thread safe.
// Find out if histograms can now be registered into our list.
static bool IsActive();
// 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.
// Register a provider of histograms that can be called to merge those into
// the global StatisticsRecorder. Calls to ImportProvidedHistograms() will
// fetch from registered providers.
static void RegisterHistogramProvider(
const WeakPtr<HistogramProvider>& provider);
// Registers or adds a new histogram to the collection of statistics. If an
// Register, or add 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| will 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.
// Register, or add a new BucketRanges. If an identically BucketRanges is
// already registered, then the argument |ranges| will deleted. The returned
// value is always the registered BucketRanges (either the argument, or the
// pre-existing one).
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);
// Extracts histograms which were marked for use by UMA.
// Method for extracting histograms which were marked for use by UMA.
//
// This method is thread safe.
static void GetHistograms(Histograms* output);
// Extracts BucketRanges used by all histograms registered.
// Method for extracting BucketRanges used by all histograms registered.
static void GetBucketRanges(std::vector<const BucketRanges*>* output);
// Finds a histogram by name. Matches the exact name. Returns a null pointer
// if a matching histogram is not found.
// Find a histogram by name. It matches the exact name.
// It returns NULL 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.
// Imports histograms from providers. This must be called on the UI thread.
static void ImportProvidedHistograms();
// Snapshots all histograms via |snapshot_manager|. |flags_to_set| is used to
......@@ -147,81 +110,71 @@ class BASE_EXPORT StatisticsRecorder {
HistogramBase::Flags required_flags,
HistogramSnapshotManager* snapshot_manager);
// Extracts registered histograms. Only histograms which have |query| as a
// substring are extracted. An empty query will extract all registered
// histograms.
// GetSnapshot copies some of the pointers to registered histograms into the
// caller supplied vector (Histograms). Only histograms which have |query| as
// a substring are copied (an empty string will process all registered
// histograms).
//
// This method is thread safe.
static void GetSnapshot(const std::string& query, Histograms* snapshot);
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.
// SetCallback sets the callback to notify when a new sample is recorded on
// the histogram referred to by |histogram_name|. The call to this method can
// be be done before or after the histogram is created. This method is thread
// safe. The return value is whether or not the callback was successfully set.
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.
// ClearCallback 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.
// FindCallback 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.
// Creates a local 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 SR object. The destruction of the
// returned object will re-activate the previous one. Always release SR
// objects in the opposite order to which they're created.
static std::unique_ptr<StatisticsRecorder> CreateTemporaryForTesting()
WARN_UNUSED_RESULT;
// Resets any global instance of the statistics-recorder that was created
// by a call to Initialize().
static void UninitializeForTesting();
// 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.
//
// TODO(iburak): This is not yet hooked up to histogram recording
// infrastructure.
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.
// Returns true iff the given histogram should be recorded based on
// the ShouldRecord() method of the record checker.
// If the record checker is not set, returns true.
static bool ShouldRecordHistogram(uint64_t histogram_hash);
private:
......@@ -246,57 +199,52 @@ class BASE_EXPORT StatisticsRecorder {
unordered_set<const BucketRanges*, BucketRangesHash, BucketRangesEqual>
RangesMap;
friend struct LazyInstanceTraitsBase<StatisticsRecorder>;
friend class StatisticsRecorderTest;
FRIEND_TEST_ALL_PREFIXES(StatisticsRecorderTest, IterationTest);
// Fetches set of existing histograms. Ownership of the individual histograms
// Fetch set of existing histograms. Ownership of the individual histograms
// remains with the StatisticsRecorder.
//
// This method is thread safe.
static Histograms GetKnownHistograms(bool include_persistent);
// 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.
// Imports histograms from global persistent memory. 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.
// The constructor just initializes static members. Usually client code should
// use Initialize to do this. But in test code, you can friend this class and
// call the constructor to get a clean StatisticsRecorder.
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_;
void InitLogOnShutdownWithoutLock();
// These are copies of everything that existed when the (test) Statistics-
// Recorder was created. The global ones have to be moved aside to create a
// clean environment.
std::unique_ptr<HistogramMap> existing_histograms_;
std::unique_ptr<CallbackMap> existing_callbacks_;
std::unique_ptr<RangesMap> existing_ranges_;
std::unique_ptr<HistogramProviders> existing_providers_;
std::unique_ptr<RecordHistogramChecker> existing_record_checker_;
bool vlog_initialized_ = false;
static void Reset();
static void DumpHistogramsToVlog(void* instance);
static HistogramMap* histograms_;
static CallbackMap* callbacks_;
static RangesMap* ranges_;
static HistogramProviders* providers_;
static RecordHistogramChecker* record_checker_;
// Lock protects access to above maps. This is a LazyInstance to avoid races
// when the above methods are used before Initialize(). Previously each method
// would do |if (!lock_) return;| which would race with
// |lock_ = new Lock;| in StatisticsRecorder(). http://crbug.com/672852.
static base::LazyInstance<base::Lock>::Leaky lock_;
DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder);
};
......
......@@ -30,7 +30,9 @@ class LogStateSaver {
public:
LogStateSaver() : old_min_log_level_(logging::GetMinLogLevel()) {}
~LogStateSaver() { logging::SetMinLogLevel(old_min_log_level_); }
~LogStateSaver() {
logging::SetMinLogLevel(old_min_log_level_);
}
private:
int old_min_log_level_;
......@@ -68,8 +70,8 @@ class StatisticsRecorderTest : public testing::TestWithParam<bool> {
// Use persistent memory for histograms if so indicated by test parameter.
if (use_persistent_histogram_allocator_) {
GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0,
"StatisticsRecorderTest");
GlobalHistogramAllocator::CreateWithLocalMemory(
kAllocatorMemorySize, 0, "StatisticsRecorderTest");
}
}
......@@ -80,10 +82,14 @@ class StatisticsRecorderTest : public testing::TestWithParam<bool> {
void InitializeStatisticsRecorder() {
DCHECK(!statistics_recorder_);
StatisticsRecorder::UninitializeForTesting();
statistics_recorder_ = StatisticsRecorder::CreateTemporaryForTesting();
}
void UninitializeStatisticsRecorder() { statistics_recorder_.reset(); }
void UninitializeStatisticsRecorder() {
statistics_recorder_.reset();
StatisticsRecorder::UninitializeForTesting();
}
Histogram* CreateHistogram(const char* name,
HistogramBase::Sample min,
......@@ -96,15 +102,18 @@ class StatisticsRecorderTest : public testing::TestWithParam<bool> {
return new Histogram(name, min, max, registered_ranges);
}
void DeleteHistogram(HistogramBase* histogram) { delete histogram; }
void InitLogOnShutdown() { StatisticsRecorder::InitLogOnShutdown(); }
void DeleteHistogram(HistogramBase* histogram) {
delete histogram;
}
bool IsVLogInitialized() { return StatisticsRecorder::is_vlog_initialized_; }
void InitLogOnShutdown() {
DCHECK(statistics_recorder_);
statistics_recorder_->InitLogOnShutdownWithoutLock();
}
void ResetVLogInitialized() {
UninitializeStatisticsRecorder();
StatisticsRecorder::is_vlog_initialized_ = false;
bool VLogInitialized() {
DCHECK(statistics_recorder_);
return statistics_recorder_->vlog_initialized_;
}
const bool use_persistent_histogram_allocator_;
......@@ -268,8 +277,8 @@ TEST_P(StatisticsRecorderTest, RegisterHistogramWithFactoryGet) {
ASSERT_EQ(0u, registered_histograms.size());
// Create a histogram.
HistogramBase* histogram = Histogram::FactoryGet("TestHistogram", 1, 1000, 10,
HistogramBase::kNoFlags);
HistogramBase* histogram = Histogram::FactoryGet(
"TestHistogram", 1, 1000, 10, HistogramBase::kNoFlags);
registered_histograms.clear();
StatisticsRecorder::GetHistograms(&registered_histograms);
EXPECT_EQ(1u, registered_histograms.size());
......@@ -283,15 +292,15 @@ TEST_P(StatisticsRecorderTest, RegisterHistogramWithFactoryGet) {
EXPECT_EQ(histogram, histogram2);
// Create a LinearHistogram.
histogram = LinearHistogram::FactoryGet("TestLinearHistogram", 1, 1000, 10,
HistogramBase::kNoFlags);
histogram = LinearHistogram::FactoryGet(
"TestLinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags);
registered_histograms.clear();
StatisticsRecorder::GetHistograms(&registered_histograms);
EXPECT_EQ(2u, registered_histograms.size());
// Create a BooleanHistogram.
histogram = BooleanHistogram::FactoryGet("TestBooleanHistogram",
HistogramBase::kNoFlags);
histogram = BooleanHistogram::FactoryGet(
"TestBooleanHistogram", HistogramBase::kNoFlags);
registered_histograms.clear();
StatisticsRecorder::GetHistograms(&registered_histograms);
EXPECT_EQ(3u, registered_histograms.size());
......@@ -300,8 +309,8 @@ TEST_P(StatisticsRecorderTest, RegisterHistogramWithFactoryGet) {
std::vector<int> custom_ranges;
custom_ranges.push_back(1);
custom_ranges.push_back(5);
histogram = CustomHistogram::FactoryGet("TestCustomHistogram", custom_ranges,
HistogramBase::kNoFlags);
histogram = CustomHistogram::FactoryGet(
"TestCustomHistogram", custom_ranges, HistogramBase::kNoFlags);
registered_histograms.clear();
StatisticsRecorder::GetHistograms(&registered_histograms);
EXPECT_EQ(4u, registered_histograms.size());
......@@ -615,33 +624,33 @@ TEST_P(StatisticsRecorderTest, CallbackUsedBeforeHistogramCreatedTest) {
}
TEST_P(StatisticsRecorderTest, LogOnShutdownNotInitialized) {
ResetVLogInitialized();
UninitializeStatisticsRecorder();
logging::SetMinLogLevel(logging::LOG_WARNING);
InitializeStatisticsRecorder();
EXPECT_FALSE(VLOG_IS_ON(1));
EXPECT_FALSE(IsVLogInitialized());
EXPECT_FALSE(VLogInitialized());
InitLogOnShutdown();
EXPECT_FALSE(IsVLogInitialized());
EXPECT_FALSE(VLogInitialized());
}
TEST_P(StatisticsRecorderTest, LogOnShutdownInitializedExplicitly) {
ResetVLogInitialized();
UninitializeStatisticsRecorder();
logging::SetMinLogLevel(logging::LOG_WARNING);
InitializeStatisticsRecorder();
EXPECT_FALSE(VLOG_IS_ON(1));
EXPECT_FALSE(IsVLogInitialized());
EXPECT_FALSE(VLogInitialized());
logging::SetMinLogLevel(logging::LOG_VERBOSE);
EXPECT_TRUE(VLOG_IS_ON(1));
InitLogOnShutdown();
EXPECT_TRUE(IsVLogInitialized());
EXPECT_TRUE(VLogInitialized());
}
TEST_P(StatisticsRecorderTest, LogOnShutdownInitialized) {
ResetVLogInitialized();
UninitializeStatisticsRecorder();
logging::SetMinLogLevel(logging::LOG_VERBOSE);
InitializeStatisticsRecorder();
EXPECT_TRUE(VLOG_IS_ON(1));
EXPECT_TRUE(IsVLogInitialized());
EXPECT_TRUE(VLogInitialized());
}
class TestHistogramProvider : public StatisticsRecorder::HistogramProvider {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment