123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- // 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.
- #include "base/metrics/statistics_recorder.h"
- #include <memory>
- #include "base/at_exit.h"
- #include "base/containers/contains.h"
- #include "base/debug/leak_annotations.h"
- #include "base/json/string_escape.h"
- #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/ranges/algorithm.h"
- #include "base/strings/stringprintf.h"
- #include "base/values.h"
- namespace base {
- namespace {
- bool HistogramNameLesser(const base::HistogramBase* a,
- const base::HistogramBase* b) {
- return strcmp(a->histogram_name(), b->histogram_name()) < 0;
- }
- } // namespace
- // static
- LazyInstance<Lock>::Leaky StatisticsRecorder::lock_ = LAZY_INSTANCE_INITIALIZER;
- // static
- StatisticsRecorder* StatisticsRecorder::top_ = nullptr;
- // static
- bool StatisticsRecorder::is_vlog_initialized_ = false;
- // static
- std::atomic<bool> StatisticsRecorder::have_active_callbacks_{false};
- // static
- std::atomic<StatisticsRecorder::GlobalSampleCallback>
- StatisticsRecorder::global_sample_callback_{nullptr};
- StatisticsRecorder::ScopedHistogramSampleObserver::
- ScopedHistogramSampleObserver(const std::string& name,
- OnSampleCallback callback)
- : histogram_name_(name), callback_(callback) {
- StatisticsRecorder::AddHistogramSampleObserver(histogram_name_, this);
- }
- StatisticsRecorder::ScopedHistogramSampleObserver::
- ~ScopedHistogramSampleObserver() {
- StatisticsRecorder::RemoveHistogramSampleObserver(histogram_name_, this);
- }
- void StatisticsRecorder::ScopedHistogramSampleObserver::RunCallback(
- const char* histogram_name,
- uint64_t name_hash,
- HistogramBase::Sample sample) {
- callback_.Run(histogram_name, name_hash, sample);
- }
- StatisticsRecorder::~StatisticsRecorder() {
- const AutoLock auto_lock(lock_.Get());
- DCHECK_EQ(this, top_);
- top_ = previous_;
- }
- // static
- void StatisticsRecorder::EnsureGlobalRecorderWhileLocked() {
- lock_.Get().AssertAcquired();
- if (top_)
- return;
- 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) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- top_->providers_.push_back(provider);
- }
- // static
- HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
- HistogramBase* 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.
- if (base::Contains(top_->observers_, name))
- histogram->SetFlags(HistogramBase::kCallbackExists);
- return histogram;
- }
- 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) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- const BucketRanges* const registered =
- top_->ranges_manager_.RegisterOrDeleteDuplicateRanges(ranges);
- if (registered == ranges)
- ANNOTATE_LEAKING_OBJECT_PTR(ranges);
- return registered;
- }
- // static
- void StatisticsRecorder::WriteGraph(const std::string& query,
- std::string* output) {
- if (query.length())
- StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
- else
- output->append("Collections of all histograms\n");
- for (const HistogramBase* const histogram :
- Sort(WithName(GetHistograms(), query))) {
- histogram->WriteAscii(output);
- output->append("\n");
- }
- }
- // static
- 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, verbosity_level);
- output += json;
- }
- output += "]}";
- return output;
- }
- // static
- std::vector<const BucketRanges*> StatisticsRecorder::GetBucketRanges() {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- return top_->ranges_manager_.GetBucketRanges();
- }
- // static
- HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) {
- // 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();
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- 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());
- EnsureGlobalRecorderWhileLocked();
- return top_->providers_;
- }
- // static
- void StatisticsRecorder::ImportProvidedHistograms() {
- // Merge histogram data from each provider in turn.
- for (const WeakPtr<HistogramProvider>& provider : GetHistogramProviders()) {
- // Weak-pointer may be invalid if the provider was destructed, though they
- // generally never are.
- if (provider)
- provider->MergeHistogramDeltas();
- }
- }
- // static
- 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() {
- const AutoLock auto_lock(lock_.Get());
- InitLogOnShutdownWhileLocked();
- }
- // static
- void StatisticsRecorder::AddHistogramSampleObserver(
- const std::string& name,
- StatisticsRecorder::ScopedHistogramSampleObserver* observer) {
- DCHECK(observer);
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- auto iter = top_->observers_.find(name);
- if (iter == top_->observers_.end()) {
- top_->observers_.insert(
- {name, base::MakeRefCounted<HistogramSampleObserverList>()});
- }
- top_->observers_[name]->AddObserver(observer);
- const HistogramMap::const_iterator it = top_->histograms_.find(name);
- if (it != top_->histograms_.end())
- it->second->SetFlags(HistogramBase::kCallbackExists);
- have_active_callbacks_.store(
- global_sample_callback() || !top_->observers_.empty(),
- std::memory_order_relaxed);
- }
- // static
- void StatisticsRecorder::RemoveHistogramSampleObserver(
- const std::string& name,
- StatisticsRecorder::ScopedHistogramSampleObserver* observer) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- auto iter = top_->observers_.find(name);
- DCHECK(iter != top_->observers_.end());
- auto result = iter->second->RemoveObserver(observer);
- if (result ==
- HistogramSampleObserverList::RemoveObserverResult::kWasOrBecameEmpty) {
- top_->observers_.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())
- it->second->ClearFlags(HistogramBase::kCallbackExists);
- }
- have_active_callbacks_.store(
- global_sample_callback() || !top_->observers_.empty(),
- std::memory_order_relaxed);
- }
- // static
- void StatisticsRecorder::FindAndRunHistogramCallbacks(
- base::PassKey<HistogramBase>,
- const char* histogram_name,
- uint64_t name_hash,
- HistogramBase::Sample sample) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- auto it = top_->observers_.find(histogram_name);
- // Ensure that this observer is still registered, as it might have been
- // unregistered before we acquired the lock.
- if (it == top_->observers_.end())
- return;
- it->second->Notify(FROM_HERE, &ScopedHistogramSampleObserver::RunCallback,
- histogram_name, name_hash, sample);
- }
- // static
- void StatisticsRecorder::SetGlobalSampleCallback(
- const GlobalSampleCallback& new_global_sample_callback) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- DCHECK(!global_sample_callback() || !new_global_sample_callback);
- global_sample_callback_.store(new_global_sample_callback);
- have_active_callbacks_.store(
- new_global_sample_callback || !top_->observers_.empty(),
- std::memory_order_relaxed);
- }
- // static
- size_t StatisticsRecorder::GetHistogramCount() {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- return top_->histograms_.size();
- }
- // static
- void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece 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());
- std::unique_ptr<StatisticsRecorder> temporary_recorder =
- WrapUnique(new StatisticsRecorder());
- temporary_recorder->ranges_manager_
- .DoNotReleaseRangesOnDestroyForTesting(); // IN-TEST
- return temporary_recorder;
- }
- // static
- void StatisticsRecorder::SetRecordChecker(
- std::unique_ptr<RecordHistogramChecker> record_checker) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- top_->record_checker_ = std::move(record_checker);
- }
- // static
- bool StatisticsRecorder::ShouldRecordHistogram(uint32_t histogram_hash) {
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- return !top_->record_checker_ ||
- top_->record_checker_->ShouldRecord(histogram_hash);
- }
- // static
- 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();
- Histograms out;
- const AutoLock auto_lock(lock_.Get());
- EnsureGlobalRecorderWhileLocked();
- out.reserve(top_->histograms_.size());
- for (const auto& entry : top_->histograms_)
- out.push_back(entry.second);
- return out;
- }
- // static
- StatisticsRecorder::Histograms StatisticsRecorder::Sort(Histograms histograms) {
- ranges::sort(histograms, &HistogramNameLesser);
- return histograms;
- }
- // 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(
- ranges::remove_if(histograms,
- [query_string](const HistogramBase* const h) {
- return !strstr(h->histogram_name(), query_string);
- }),
- histograms.end());
- return histograms;
- }
- // static
- StatisticsRecorder::Histograms StatisticsRecorder::NonPersistent(
- Histograms histograms) {
- histograms.erase(
- ranges::remove_if(histograms,
- [](const HistogramBase* const h) {
- return (h->flags() & HistogramBase::kIsPersistent) !=
- 0;
- }),
- histograms.end());
- return histograms;
- }
- // static
- 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();
- }
- 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);
- }
- }
- } // namespace base
|