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..6d0365d58c7da0ad838ffc4057c5018ac164830b |
--- /dev/null |
+++ b/chrome/browser/metrics/subprocess_metrics_provider.cc |
@@ -0,0 +1,165 @@ |
+// 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/memory/ptr_util.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, |
+ std::unique_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. |
+ std::unique_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 |
+ // unique_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); |
+ while (true) { |
+ std::unique_ptr<base::HistogramBase> histogram = hist_iter.GetNext(); |
+ 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. |
+ std::unique_ptr<base::SharedPersistentMemoryAllocator> allocator = |
+ host->TakeMetricsAllocator(); |
+ if (allocator) { |
+ RegisterSubprocessAllocator( |
+ host->GetID(), |
+ WrapUnique(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()); |
+} |
+ |
+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); |
+} |