Chromium Code Reviews| Index: chrome/browser/metrics/subprocess_metrics_provider.cc |
| diff --git a/chrome/browser/metrics/subprocess_metrics_provider.cc b/chrome/browser/metrics/subprocess_metrics_provider.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..bfa6d67d4146ea4e8e57ca423df69677ad5bdb8f |
| --- /dev/null |
| +++ b/chrome/browser/metrics/subprocess_metrics_provider.cc |
| @@ -0,0 +1,167 @@ |
| +// Copyright 2016 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 "chrome/browser/metrics/subprocess_metrics_provider.h" |
| + |
| +#include "base/logging.h" |
| +#include "base/metrics/histogram_base.h" |
| +#include "base/metrics/histogram_macros.h" |
| +#include "base/metrics/persistent_histogram_allocator.h" |
| +#include "base/metrics/persistent_memory_allocator.h" |
| +#include "components/metrics/metrics_service.h" |
| +#include "content/public/browser/notification_service.h" |
| +#include "content/public/browser/notification_types.h" |
| +#include "content/public/browser/render_process_host.h" |
| + |
| +SubprocessMetricsProvider::SubprocessMetricsProvider() |
| + : scoped_observer_(this) { |
| + registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED, |
| + content::NotificationService::AllBrowserContextsAndSources()); |
| +} |
| + |
| +SubprocessMetricsProvider::~SubprocessMetricsProvider() {} |
| + |
| +void SubprocessMetricsProvider::RegisterSubprocessAllocator( |
| + int id, |
| + scoped_ptr<base::PersistentHistogramAllocator> allocator) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(!allocators_by_id_.Lookup(id)); |
| + |
| + // Map is "MapOwnPointer" so transfer ownership to it. |
| + allocators_by_id_.AddWithID(allocator.release(), id); |
| +} |
| + |
| +void SubprocessMetricsProvider::DeregisterSubprocessAllocator(int id) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (!allocators_by_id_.Lookup(id)) |
| + return; |
| + |
| + // Extract the matching allocator from the list of active ones. |
| + scoped_ptr<base::PersistentHistogramAllocator> allocator( |
| + allocators_by_id_.Replace(id, nullptr)); |
| + allocators_by_id_.Remove(id); |
| + DCHECK(allocator); |
| + |
| + // If metrics recording is enabled, transfer the allocator to the "release" |
| + // list. The allocator will continue to live (and keep the associated shared |
| + // memory alive) until the next upload after which it will be released. |
| + // Otherwise, the allocator and its memory will be released when the |
| + // scoped_ptr goes out of scope at the end of this method. |
| + if (metrics_recording_enabled_) |
| + allocators_to_release_.push_back(std::move(allocator)); |
| +} |
| + |
| +void SubprocessMetricsProvider::OnRecordingEnabled() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + metrics_recording_enabled_ = true; |
| +} |
| + |
| +void SubprocessMetricsProvider::OnRecordingDisabled() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + metrics_recording_enabled_ = false; |
| + allocators_to_release_.clear(); |
| +} |
| + |
| +void SubprocessMetricsProvider::RecordHistogramSnapshotsFromAllocator( |
| + base::HistogramSnapshotManager* snapshot_manager, |
| + int id, |
| + base::PersistentHistogramAllocator* allocator) { |
| + DCHECK(allocator); |
| + |
| + int histogram_count = 0; |
| + base::PersistentHistogramAllocator::Iterator hist_iter; |
| + allocator->CreateIterator(&hist_iter); |
| + while (true) { |
| + scoped_ptr<base::HistogramBase> histogram = |
| + allocator->GetNextHistogram(&hist_iter); |
| + if (!histogram) |
| + break; |
| + snapshot_manager->PrepareDeltaTakingOwnership(std::move(histogram)); |
| + ++histogram_count; |
| + } |
| + |
| + DVLOG(1) << "Reported " << histogram_count << " histograms from subprocess #" |
| + << id; |
| +} |
| + |
| +void SubprocessMetricsProvider::RecordHistogramSnapshots( |
| + base::HistogramSnapshotManager* snapshot_manager) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + for (AllocatorByIdMap::iterator iter(&allocators_by_id_); !iter.IsAtEnd(); |
| + iter.Advance()) { |
| + RecordHistogramSnapshotsFromAllocator( |
| + snapshot_manager, iter.GetCurrentKey(), iter.GetCurrentValue()); |
| + } |
| + |
| + for (auto& allocator : allocators_to_release_) |
| + RecordHistogramSnapshotsFromAllocator(snapshot_manager, 0, allocator.get()); |
| + |
| + UMA_HISTOGRAM_COUNTS_100( |
| + "UMA.SubprocessMetricsProvider.SubprocessCount", |
| + allocators_by_id_.size() + allocators_to_release_.size()); |
| + |
| + // The snapshot-manager has taken ownership of the histograms but needs |
| + // access to only the histogram objects, not "sample" data it uses. Thus, |
| + // it is safe to release shared-memory segments without waiting for the |
| + // snapshot-manager to "finish". |
| + allocators_to_release_.clear(); |
| +} |
| + |
| +void SubprocessMetricsProvider::Observe( |
| + int type, |
| + const content::NotificationSource& source, |
| + const content::NotificationDetails& details) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK_EQ(content::NOTIFICATION_RENDERER_PROCESS_CREATED, type); |
| + |
| + content::RenderProcessHost* host = |
| + content::Source<content::RenderProcessHost>(source).ptr(); |
| + |
| + // Sometimes, the same host will cause multiple notifications in tests so |
| + // could possibly do the same in a release build. |
| + if (!scoped_observer_.IsObserving(host)) |
| + scoped_observer_.Add(host); |
| +} |
| + |
| +void SubprocessMetricsProvider::RenderProcessReady( |
| + content::RenderProcessHost* host) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + // If the render-process-host passed a persistent-memory-allocator to the |
| + // renderer process, extract it and register it here. |
| + scoped_ptr<base::SharedPersistentMemoryAllocator> allocator = |
| + host->TakeMetricsAllocator(); |
| + if (allocator) { |
| + RegisterSubprocessAllocator( |
| + host->GetID(), |
| + make_scoped_ptr(new base::PersistentHistogramAllocator( |
| + std::move(allocator)))); |
| + } |
| +} |
| + |
| +void SubprocessMetricsProvider::RenderProcessExited( |
| + content::RenderProcessHost* host, |
| + base::TerminationStatus status, |
| + int exit_code) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + DeregisterSubprocessAllocator(host->GetID()); |
| + scoped_observer_.Remove(host); |
|
grt (UTC plus 2)
2016/04/01 20:45:33
should this removal be here? don't you need to kee
grt (UTC plus 2)
2016/04/04 16:46:44
ping
bcwhite
2016/04/04 19:32:27
Not forgotten. Just working on other CLs.
|
| +} |
| + |
| +void SubprocessMetricsProvider::RenderProcessHostDestroyed( |
| + content::RenderProcessHost* host) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + // It's possible for a Renderer to terminate without RenderProcessExited |
| + // (above) being called so it's necessary to de-register also upon the |
| + // destruction of the host. If both get called, no harm is done. |
| + |
| + DeregisterSubprocessAllocator(host->GetID()); |
| + scoped_observer_.Remove(host); |
| +} |