summaryrefslogtreecommitdiff
path: root/base/metrics/statistics_recorder.cc
diff options
context:
space:
mode:
Diffstat (limited to 'base/metrics/statistics_recorder.cc')
-rw-r--r--base/metrics/statistics_recorder.cc582
1 files changed, 228 insertions, 354 deletions
diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc
index ba2101bccf..28773a13de 100644
--- a/base/metrics/statistics_recorder.cc
+++ b/base/metrics/statistics_recorder.cc
@@ -12,259 +12,162 @@
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_snapshot_manager.h"
#include "base/metrics/metrics_hashes.h"
#include "base/metrics/persistent_histogram_allocator.h"
+#include "base/metrics/record_histogram_checker.h"
#include "base/stl_util.h"
#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 a->histogram_name() < b->histogram_name();
+ return strcmp(a->histogram_name(), b->histogram_name()) < 0;
}
} // namespace
-namespace base {
-
-StatisticsRecorder::HistogramIterator::HistogramIterator(
- const HistogramMap::iterator& iter, bool include_persistent)
- : iter_(iter),
- include_persistent_(include_persistent) {
- // The starting location could point to a persistent histogram when such
- // is not wanted. If so, skip it.
- if (!include_persistent_ && iter_ != histograms_->end() &&
- (iter_->second->flags() & HistogramBase::kIsPersistent)) {
- // This operator will continue to skip until a non-persistent histogram
- // is found.
- operator++();
- }
-}
-
-StatisticsRecorder::HistogramIterator::HistogramIterator(
- const HistogramIterator& rhs)
- : iter_(rhs.iter_),
- include_persistent_(rhs.include_persistent_) {
-}
-
-StatisticsRecorder::HistogramIterator::~HistogramIterator() {}
+// static
+LazyInstance<Lock>::Leaky StatisticsRecorder::lock_;
-StatisticsRecorder::HistogramIterator&
-StatisticsRecorder::HistogramIterator::operator++() {
- const HistogramMap::iterator histograms_end = histograms_->end();
- if (iter_ == histograms_end)
- return *this;
+// static
+StatisticsRecorder* StatisticsRecorder::top_ = nullptr;
- base::AutoLock auto_lock(lock_.Get());
+// static
+bool StatisticsRecorder::is_vlog_initialized_ = false;
- for (;;) {
- ++iter_;
- if (iter_ == histograms_end)
- break;
- if (!include_persistent_ && (iter_->second->flags() &
- HistogramBase::kIsPersistent)) {
- continue;
- }
- break;
- }
+size_t StatisticsRecorder::BucketRangesHash::operator()(
+ const BucketRanges* const a) const {
+ return a->checksum();
+}
- return *this;
+bool StatisticsRecorder::BucketRangesEqual::operator()(
+ const BucketRanges* const a,
+ const BucketRanges* const b) const {
+ return a->Equals(b);
}
StatisticsRecorder::~StatisticsRecorder() {
- 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();
+ const AutoLock auto_lock(lock_.Get());
+ DCHECK_EQ(this, top_);
+ top_ = previous_;
}
// static
-void StatisticsRecorder::Initialize() {
- // 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_)
+void StatisticsRecorder::EnsureGlobalRecorderWhileLocked() {
+ lock_.Get().AssertAcquired();
+ if (top_)
return;
- // Ensure that an instance of the StatisticsRecorder object is created.
- g_statistics_recorder_.Get();
-}
-
-// static
-bool StatisticsRecorder::IsActive() {
- base::AutoLock auto_lock(lock_.Get());
- return histograms_ != nullptr;
+ const StatisticsRecorder* const p = new StatisticsRecorder;
+ // The global recorder is never deleted.
+ ANNOTATE_LEAKING_OBJECT_PTR(p);
+ DCHECK_EQ(p, top_);
}
// static
void StatisticsRecorder::RegisterHistogramProvider(
const WeakPtr<HistogramProvider>& provider) {
- providers_->push_back(provider);
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
+ top_->providers_.push_back(provider);
}
// static
HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
HistogramBase* histogram) {
- HistogramBase* histogram_to_delete = nullptr;
- HistogramBase* histogram_to_return = nullptr;
- {
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_) {
- histogram_to_return = histogram;
-
- // 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 (!histograms_)|.
- ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
- } else {
- const std::string& name = histogram->histogram_name();
- HistogramMap::iterator it = histograms_->find(name);
- if (histograms_->end() == it) {
- // The StringKey references the name within |histogram| rather than
- // making a copy.
- (*histograms_)[name] = histogram;
- ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
- // If there are callbacks for this histogram, we set the kCallbackExists
- // flag.
- auto callback_iterator = callbacks_->find(name);
- if (callback_iterator != callbacks_->end()) {
- if (!callback_iterator->second.is_null())
- histogram->SetFlags(HistogramBase::kCallbackExists);
- else
- histogram->ClearFlags(HistogramBase::kCallbackExists);
- }
- histogram_to_return = histogram;
- } else if (histogram == it->second) {
- // The histogram was registered before.
- histogram_to_return = histogram;
- } else {
- // We already have one histogram with this name.
- DCHECK_EQ(histogram->histogram_name(),
- it->second->histogram_name()) << "hash collision";
- histogram_to_return = it->second;
- histogram_to_delete = histogram;
- }
+ // Declared before |auto_lock| to ensure correct destruction order.
+ std::unique_ptr<HistogramBase> histogram_deleter;
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
+
+ const char* const name = histogram->histogram_name();
+ HistogramBase*& registered = top_->histograms_[name];
+
+ if (!registered) {
+ // |name| is guaranteed to never change or be deallocated so long
+ // as the histogram is alive (which is forever).
+ registered = histogram;
+ 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()) {
+ if (!callback_iterator->second.is_null())
+ histogram->SetFlags(HistogramBase::kCallbackExists);
+ else
+ histogram->ClearFlags(HistogramBase::kCallbackExists);
}
+ return histogram;
}
- delete histogram_to_delete;
- return histogram_to_return;
+
+ if (histogram == registered) {
+ // The histogram was registered before.
+ return histogram;
+ }
+
+ // We already have one histogram with this name.
+ histogram_deleter.reset(histogram);
+ return registered;
}
// static
const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
const BucketRanges* ranges) {
DCHECK(ranges->HasValidChecksum());
+
+ // Declared before |auto_lock| to ensure correct destruction order.
std::unique_ptr<const BucketRanges> ranges_deleter;
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
- base::AutoLock auto_lock(lock_.Get());
- if (!ranges_) {
+ const BucketRanges* const registered = *top_->ranges_.insert(ranges).first;
+ if (registered == ranges) {
ANNOTATE_LEAKING_OBJECT_PTR(ranges);
- return ranges;
- }
-
- std::list<const BucketRanges*>* checksum_matching_list;
- RangesMap::iterator ranges_it = ranges_->find(ranges->checksum());
- if (ranges_->end() == ranges_it) {
- // Add a new matching list to map.
- checksum_matching_list = new std::list<const BucketRanges*>();
- ANNOTATE_LEAKING_OBJECT_PTR(checksum_matching_list);
- (*ranges_)[ranges->checksum()] = checksum_matching_list;
} else {
- checksum_matching_list = ranges_it->second;
+ ranges_deleter.reset(ranges);
}
- for (const BucketRanges* existing_ranges : *checksum_matching_list) {
- if (existing_ranges->Equals(ranges)) {
- if (existing_ranges == ranges) {
- return ranges;
- } else {
- ranges_deleter.reset(ranges);
- return existing_ranges;
- }
- }
- }
- // We haven't found a BucketRanges which has the same ranges. Register the
- // new BucketRanges.
- checksum_matching_list->push_front(ranges);
- return ranges;
+ return registered;
}
// 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);
- for (const HistogramBase* histogram : snapshot) {
+ for (const HistogramBase* const histogram :
+ Sort(WithName(GetHistograms(), query))) {
histogram->WriteHTMLGraph(output);
- output->append("<br><hr><br>");
+ *output += "<br><hr><br>";
}
}
// 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
output->append("Collections of all histograms\n");
- Histograms snapshot;
- GetSnapshot(query, &snapshot);
- std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser);
- for (const HistogramBase* histogram : snapshot) {
+ for (const HistogramBase* const histogram :
+ Sort(WithName(GetHistograms(), query))) {
histogram->WriteAscii(output);
output->append("\n");
}
}
// static
-std::string StatisticsRecorder::ToJSON(const std::string& query) {
- if (!IsActive())
- return std::string();
-
- std::string output("{");
- if (!query.empty()) {
- output += "\"query\":";
- EscapeJSONString(query, true, &output);
- output += ",";
- }
-
- Histograms snapshot;
- GetSnapshot(query, &snapshot);
- output += "\"histograms\":[";
- bool first_histogram = true;
- for (const HistogramBase* histogram : snapshot) {
- if (first_histogram)
- first_histogram = false;
- else
- output += ",";
+std::string StatisticsRecorder::ToJSON(JSONVerbosityLevel verbosity_level) {
+ std::string output = "{\"histograms\":[";
+ const char* sep = "";
+ for (const HistogramBase* const histogram : Sort(GetHistograms())) {
+ output += sep;
+ sep = ",";
std::string json;
- histogram->WriteJSON(&json);
+ histogram->WriteJSON(&json, verbosity_level);
output += json;
}
output += "]}";
@@ -272,28 +175,13 @@ std::string StatisticsRecorder::ToJSON(const std::string& query) {
}
// static
-void StatisticsRecorder::GetHistograms(Histograms* output) {
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
- return;
-
- for (const auto& entry : *histograms_) {
- output->push_back(entry.second);
- }
-}
-
-// static
-void StatisticsRecorder::GetBucketRanges(
- std::vector<const BucketRanges*>* output) {
- base::AutoLock auto_lock(lock_.Get());
- if (!ranges_)
- return;
-
- for (const auto& entry : *ranges_) {
- for (auto* range_entry : *entry.second) {
- output->push_back(range_entry);
- }
- }
+std::vector<const BucketRanges*> StatisticsRecorder::GetBucketRanges() {
+ std::vector<const BucketRanges*> out;
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
+ out.reserve(top_->ranges_.size());
+ out.assign(top_->ranges_.begin(), top_->ranges_.end());
+ return out;
}
// static
@@ -303,23 +191,25 @@ HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) {
// will acquire the lock at that time.
ImportGlobalPersistentHistograms();
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
- return nullptr;
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
- HistogramMap::iterator it = histograms_->find(name);
- if (histograms_->end() == it)
- return nullptr;
- return it->second;
+ const HistogramMap::const_iterator it = top_->histograms_.find(name);
+ return it != top_->histograms_.end() ? it->second : nullptr;
}
// static
-void StatisticsRecorder::ImportProvidedHistograms() {
- if (!providers_)
- return;
+StatisticsRecorder::HistogramProviders
+StatisticsRecorder::GetHistogramProviders() {
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
+ return top_->providers_;
+}
+// static
+void StatisticsRecorder::ImportProvidedHistograms() {
// Merge histogram data from each provider in turn.
- for (const WeakPtr<HistogramProvider>& provider : *providers_) {
+ for (const WeakPtr<HistogramProvider>& provider : GetHistogramProviders()) {
// Weak-pointer may be invalid if the provider was destructed, though they
// generally never are.
if (provider)
@@ -328,51 +218,22 @@ void StatisticsRecorder::ImportProvidedHistograms() {
}
// static
-StatisticsRecorder::HistogramIterator StatisticsRecorder::begin(
- bool include_persistent) {
- DCHECK(histograms_);
- ImportGlobalPersistentHistograms();
-
- HistogramMap::iterator iter_begin;
- {
- base::AutoLock auto_lock(lock_.Get());
- iter_begin = histograms_->begin();
- }
- return HistogramIterator(iter_begin, include_persistent);
-}
-
-// static
-StatisticsRecorder::HistogramIterator StatisticsRecorder::end() {
- HistogramMap::iterator iter_end;
- {
- base::AutoLock auto_lock(lock_.Get());
- iter_end = histograms_->end();
- }
- return HistogramIterator(iter_end, true);
+void StatisticsRecorder::PrepareDeltas(
+ bool include_persistent,
+ HistogramBase::Flags flags_to_set,
+ HistogramBase::Flags required_flags,
+ HistogramSnapshotManager* snapshot_manager) {
+ Histograms histograms = GetHistograms();
+ if (!include_persistent)
+ histograms = NonPersistent(std::move(histograms));
+ snapshot_manager->PrepareDeltas(Sort(std::move(histograms)), flags_to_set,
+ required_flags);
}
// static
void StatisticsRecorder::InitLogOnShutdown() {
- if (!histograms_)
- return;
-
- base::AutoLock auto_lock(lock_.Get());
- g_statistics_recorder_.Get().InitLogOnShutdownWithoutLock();
-}
-
-// static
-void StatisticsRecorder::GetSnapshot(const std::string& query,
- Histograms* snapshot) {
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
- return;
-
- ImportGlobalPersistentHistograms();
-
- for (const auto& entry : *histograms_) {
- if (entry.second->histogram_name().find(query) != std::string::npos)
- snapshot->push_back(entry.second);
- }
+ const AutoLock auto_lock(lock_.Get());
+ InitLogOnShutdownWhileLocked();
}
// static
@@ -380,16 +241,14 @@ bool StatisticsRecorder::SetCallback(
const std::string& name,
const StatisticsRecorder::OnSampleCallback& cb) {
DCHECK(!cb.is_null());
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
- return false;
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
- if (ContainsKey(*callbacks_, name))
+ if (!top_->callbacks_.insert({name, cb}).second)
return false;
- callbacks_->insert(std::make_pair(name, cb));
- auto it = histograms_->find(name);
- if (it != histograms_->end())
+ const HistogramMap::const_iterator it = top_->histograms_.find(name);
+ if (it != top_->histograms_.end())
it->second->SetFlags(HistogramBase::kCallbackExists);
return true;
@@ -397,146 +256,161 @@ bool StatisticsRecorder::SetCallback(
// static
void StatisticsRecorder::ClearCallback(const std::string& name) {
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
- return;
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
- callbacks_->erase(name);
+ top_->callbacks_.erase(name);
// We also clear the flag from the histogram (if it exists).
- auto it = histograms_->find(name);
- if (it != histograms_->end())
+ const HistogramMap::const_iterator it = top_->histograms_.find(name);
+ if (it != top_->histograms_.end())
it->second->ClearFlags(HistogramBase::kCallbackExists);
}
// static
StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback(
const std::string& name) {
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
- return OnSampleCallback();
-
- auto callback_iterator = callbacks_->find(name);
- return callback_iterator != callbacks_->end() ? callback_iterator->second
- : OnSampleCallback();
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
+ const auto it = top_->callbacks_.find(name);
+ return it != top_->callbacks_.end() ? it->second : OnSampleCallback();
}
// static
size_t StatisticsRecorder::GetHistogramCount() {
- base::AutoLock auto_lock(lock_.Get());
- if (!histograms_)
- return 0;
- return histograms_->size();
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
+ return top_->histograms_.size();
}
// static
void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) {
- if (histograms_)
- histograms_->erase(name);
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
+
+ const HistogramMap::iterator found = top_->histograms_.find(name);
+ if (found == top_->histograms_.end())
+ return;
+
+ HistogramBase* const base = found->second;
+ if (base->GetHistogramType() != SPARSE_HISTOGRAM) {
+ // When forgetting a histogram, it's likely that other information is
+ // also becoming invalid. Clear the persistent reference that may no
+ // longer be valid. There's no danger in this as, at worst, duplicates
+ // will be created in persistent memory.
+ static_cast<Histogram*>(base)->bucket_ranges()->set_persistent_reference(0);
+ }
+
+ top_->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();
+void StatisticsRecorder::SetRecordChecker(
+ std::unique_ptr<RecordHistogramChecker> record_checker) {
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
+ top_->record_checker_ = std::move(record_checker);
+}
- // 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
+bool StatisticsRecorder::ShouldRecordHistogram(uint64_t histogram_hash) {
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
+ return !top_->record_checker_ ||
+ top_->record_checker_->ShouldRecord(histogram_hash);
}
// static
-void StatisticsRecorder::ImportGlobalPersistentHistograms() {
- if (!histograms_)
- return;
+StatisticsRecorder::Histograms StatisticsRecorder::GetHistograms() {
+ // This must be called *before* the lock is acquired below because it will
+ // call back into this object to register histograms. Those called methods
+ // will acquire the lock at that time.
+ ImportGlobalPersistentHistograms();
- // 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 does nothing.
- GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get();
- if (allocator)
- allocator->ImportHistogramsToStatisticsRecorder();
-}
+ Histograms out;
-// This singleton instance should be started during the single threaded portion
-// of main(), and hence it is not thread safe. It initializes globals to
-// provide support for all future calls.
-StatisticsRecorder::StatisticsRecorder() {
- base::AutoLock auto_lock(lock_.Get());
+ const AutoLock auto_lock(lock_.Get());
+ EnsureGlobalRecorderWhileLocked();
- existing_histograms_.reset(histograms_);
- existing_callbacks_.reset(callbacks_);
- existing_ranges_.reset(ranges_);
- existing_providers_.reset(providers_);
+ out.reserve(top_->histograms_.size());
+ for (const auto& entry : top_->histograms_)
+ out.push_back(entry.second);
- histograms_ = new HistogramMap;
- callbacks_ = new CallbackMap;
- ranges_ = new RangesMap;
- providers_ = new HistogramProviders;
+ return out;
+}
- InitLogOnShutdownWithoutLock();
+// static
+StatisticsRecorder::Histograms StatisticsRecorder::Sort(Histograms histograms) {
+ std::sort(histograms.begin(), histograms.end(), &HistogramNameLesser);
+ return histograms;
}
-void StatisticsRecorder::InitLogOnShutdownWithoutLock() {
- if (!vlog_initialized_ && VLOG_IS_ON(1)) {
- vlog_initialized_ = true;
- AtExitManager::RegisterCallback(&DumpHistogramsToVlog, this);
- }
+// static
+StatisticsRecorder::Histograms StatisticsRecorder::WithName(
+ Histograms histograms,
+ const std::string& query) {
+ // Need a C-string query for comparisons against C-string histogram name.
+ const char* const query_string = query.c_str();
+ histograms.erase(std::remove_if(histograms.begin(), histograms.end(),
+ [query_string](const HistogramBase* const h) {
+ return !strstr(h->histogram_name(),
+ query_string);
+ }),
+ histograms.end());
+ return histograms;
}
// static
-void StatisticsRecorder::Reset() {
-
- std::unique_ptr<HistogramMap> histograms_deleter;
- std::unique_ptr<CallbackMap> callbacks_deleter;
- std::unique_ptr<RangesMap> ranges_deleter;
- std::unique_ptr<HistogramProviders> providers_deleter;
- {
- base::AutoLock auto_lock(lock_.Get());
- histograms_deleter.reset(histograms_);
- callbacks_deleter.reset(callbacks_);
- ranges_deleter.reset(ranges_);
- providers_deleter.reset(providers_);
- histograms_ = nullptr;
- callbacks_ = nullptr;
- ranges_ = nullptr;
- providers_ = nullptr;
- }
- // We are going to leak the histograms and the ranges.
+StatisticsRecorder::Histograms StatisticsRecorder::NonPersistent(
+ Histograms histograms) {
+ histograms.erase(
+ std::remove_if(histograms.begin(), histograms.end(),
+ [](const HistogramBase* const h) {
+ return (h->flags() & HistogramBase::kIsPersistent) != 0;
+ }),
+ histograms.end());
+ return histograms;
}
// static
-void StatisticsRecorder::DumpHistogramsToVlog(void* instance) {
- std::string output;
- StatisticsRecorder::WriteGraph(std::string(), &output);
- VLOG(1) << output;
+void StatisticsRecorder::ImportGlobalPersistentHistograms() {
+ // 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
+ // does nothing.
+ if (GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get())
+ allocator->ImportHistogramsToStatisticsRecorder();
}
+// This singleton instance should be started during the single threaded portion
+// 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
-StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = nullptr;
-// static
-StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = nullptr;
-// static
-StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = nullptr;
-// static
-StatisticsRecorder::HistogramProviders* StatisticsRecorder::providers_;
-// static
-base::LazyInstance<base::Lock>::Leaky StatisticsRecorder::lock_ =
- LAZY_INSTANCE_INITIALIZER;
+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);
+ }
+}
} // namespace base