| 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);
 | 
| +}
 | 
| 
 |