diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-01-31 16:33:43 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-02-06 16:33:22 +0000 |
commit | da51f56cc21233c2d30f0fe0d171727c3102b2e0 (patch) | |
tree | 4e579ab70ce4b19bee7984237f3ce05a96d59d83 /chromium/base/metrics/statistics_recorder.cc | |
parent | c8c2d1901aec01e934adf561a9fdf0cc776cdef8 (diff) | |
download | qtwebengine-chromium-da51f56cc21233c2d30f0fe0d171727c3102b2e0.tar.gz |
BASELINE: Update Chromium to 65.0.3525.40
Also imports missing submodules
Change-Id: I36901b7c6a325cda3d2c10cedb2186c25af3b79b
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/base/metrics/statistics_recorder.cc')
-rw-r--r-- | chromium/base/metrics/statistics_recorder.cc | 544 |
1 files changed, 205 insertions, 339 deletions
diff --git a/chromium/base/metrics/statistics_recorder.cc b/chromium/base/metrics/statistics_recorder.cc index a21adc09a0c..60e430eb72a 100644 --- a/chromium/base/metrics/statistics_recorder.cc +++ b/chromium/base/metrics/statistics_recorder.cc @@ -20,184 +20,138 @@ #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 { +// static +LazyInstance<Lock>::Leaky StatisticsRecorder::lock_; -StatisticsRecorder::~StatisticsRecorder() { - DCHECK(histograms_); - DCHECK(ranges_); +// static +StatisticsRecorder* StatisticsRecorder::top_ = nullptr; - // 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 +bool StatisticsRecorder::is_vlog_initialized_ = false; + +size_t StatisticsRecorder::BucketRangesHash::operator()( + const BucketRanges* const a) const { + return a->checksum(); } -// 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_) - return; +bool StatisticsRecorder::BucketRangesEqual::operator()( + const BucketRanges* const a, + const BucketRanges* const b) const { + return a->Equals(b); +} - // Ensure that an instance of the StatisticsRecorder object is created. - g_statistics_recorder_.Get(); +StatisticsRecorder::~StatisticsRecorder() { + const AutoLock auto_lock(lock_.Get()); + DCHECK_EQ(this, top_); + top_ = previous_; } // static -bool StatisticsRecorder::IsActive() { - base::AutoLock auto_lock(lock_.Get()); - return histograms_ != nullptr; +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) { - 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 char* name = histogram->histogram_name(); - StringPiece name_piece(name); - HistogramMap::iterator it = histograms_->find(name_piece); - if (histograms_->end() == it) { - // |name_piece| is guaranteed to never change or be deallocated so long - // as the histogram is alive (which is forever). - (*histograms_)[name_piece] = 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(StringPiece(histogram->histogram_name()), - StringPiece(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; + } + + if (histogram == registered) { + // The histogram was registered before. + return histogram; } - delete histogram_to_delete; - return histogram_to_return; + + // 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 : GetSnapshot(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 : GetSnapshot(query)) { histogram->WriteAscii(output); output->append("\n"); } @@ -205,19 +159,11 @@ void StatisticsRecorder::WriteGraph(const std::string& query, // static std::string StatisticsRecorder::ToJSON(JSONVerbosityLevel verbosity_level) { - if (!IsActive()) - return std::string(); - - std::string output("{"); - Histograms snapshot; - GetSnapshot(std::string(), &snapshot); - output += "\"histograms\":["; - bool first_histogram = true; - for (const HistogramBase* histogram : snapshot) { - if (first_histogram) - first_histogram = false; - else - output += ","; + std::string output = "{\"histograms\":["; + const char* sep = ""; + for (const HistogramBase* const histogram : GetHistograms()) { + output += sep; + sep = ","; std::string json; histogram->WriteJSON(&json, verbosity_level); output += json; @@ -227,28 +173,13 @@ std::string StatisticsRecorder::ToJSON(JSONVerbosityLevel verbosity_level) { } // 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 @@ -258,23 +189,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) @@ -288,42 +221,14 @@ void StatisticsRecorder::PrepareDeltas( HistogramBase::Flags flags_to_set, HistogramBase::Flags required_flags, HistogramSnapshotManager* snapshot_manager) { - if (include_persistent) - ImportGlobalPersistentHistograms(); - - auto known = GetKnownHistograms(include_persistent); - snapshot_manager->PrepareDeltas(known.begin(), known.end(), flags_to_set, - required_flags); + snapshot_manager->PrepareDeltas(GetKnownHistograms(include_persistent), + 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) { - // 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(); - - 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 : *histograms_) { - if (strstr(entry.second->histogram_name(), query_string) != nullptr) - snapshot->push_back(entry.second); - } + const AutoLock auto_lock(lock_.Get()); + InitLogOnShutdownWhileLocked(); } // static @@ -331,16 +236,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; @@ -348,199 +251,162 @@ 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_) - return; + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); - HistogramMap::iterator found = histograms_->find(name); - if (found == histograms_->end()) + const HistogramMap::iterator found = top_->histograms_.find(name); + if (found == top_->histograms_.end()) return; - HistogramBase* base = found->second; + 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. - Histogram* histogram = static_cast<Histogram*>(base); - histogram->bucket_ranges()->set_persistent_reference(0); + static_cast<Histogram*>(base)->bucket_ranges()->set_persistent_reference(0); } - histograms_->erase(found); + 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(); - - // 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) { - record_checker_ = record_checker.release(); + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); + top_->record_checker_ = std::move(record_checker); } // static bool StatisticsRecorder::ShouldRecordHistogram(uint64_t histogram_hash) { - return !record_checker_ || record_checker_->ShouldRecord(histogram_hash); + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); + return !top_->record_checker_ || + top_->record_checker_->ShouldRecord(histogram_hash); } // static -std::vector<HistogramBase*> StatisticsRecorder::GetKnownHistograms( - bool include_persistent) { - std::vector<HistogramBase*> known; - base::AutoLock auto_lock(lock_.Get()); - if (!histograms_ || histograms_->empty()) - return known; - - known.reserve(histograms_->size()); - for (const auto& h : *histograms_) { - if (!include_persistent && - (h.second->flags() & HistogramBase::kIsPersistent)) { - continue; +template <typename Predicate> +StatisticsRecorder::Histograms StatisticsRecorder::GetHistogramsWithPredicate( + const Predicate predicate) { + // 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_) { + const HistogramBase* const histogram = entry.second; + DCHECK(histogram); + if (predicate(*histogram)) + out.push_back(entry.second); } - known.push_back(h.second); } - return known; + std::sort(out.begin(), out.end(), &HistogramNameLesser); + return out; } // 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 does nothing. - GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get(); - if (allocator) - allocator->ImportHistogramsToStatisticsRecorder(); +StatisticsRecorder::Histograms StatisticsRecorder::GetHistograms() { + return GetHistogramsWithPredicate([](const HistogramBase&) { return true; }); } -// 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()); - - 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 +StatisticsRecorder::Histograms StatisticsRecorder::GetKnownHistograms( + bool include_persistent) { + return GetHistogramsWithPredicate( + [include_persistent](const HistogramBase& histogram) { + return include_persistent || + (histogram.flags() & HistogramBase::kIsPersistent) == 0; + }); } // 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; - 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. +StatisticsRecorder::Histograms StatisticsRecorder::GetSnapshot( + const std::string& query) { + // Need a C-string query for comparisons against C-string histogram name. + const char* const query_string = query.c_str(); + return GetHistogramsWithPredicate( + [query_string](const HistogramBase& histogram) { + return strstr(histogram.histogram_name(), query_string) != nullptr; + }); } // 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(); } - -// static -StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = nullptr; -// static -StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = nullptr; -// static -StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = nullptr; -// static -StatisticsRecorder::HistogramProviders* StatisticsRecorder::providers_; -// static -RecordHistogramChecker* StatisticsRecorder::record_checker_ = nullptr; -// static -base::LazyInstance<base::Lock>::Leaky StatisticsRecorder::lock_ = - LAZY_INSTANCE_INITIALIZER; +// 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 +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 |