• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/metrics/content/subprocess_metrics_provider.h"
6 
7 #include <utility>
8 
9 #include "base/logging.h"
10 #include "base/metrics/histogram_base.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/metrics/persistent_histogram_allocator.h"
13 #include "base/metrics/persistent_memory_allocator.h"
14 #include "components/metrics/metrics_service.h"
15 #include "content/public/browser/browser_child_process_host.h"
16 #include "content/public/browser/child_process_data.h"
17 
18 namespace metrics {
19 namespace {
20 
21 // This is used by tests that don't have an easy way to access the global
22 // instance of this class.
23 SubprocessMetricsProvider* g_subprocess_metrics_provider_for_testing;
24 
25 }  // namespace
26 
SubprocessMetricsProvider()27 SubprocessMetricsProvider::SubprocessMetricsProvider() {
28   base::StatisticsRecorder::RegisterHistogramProvider(
29       weak_ptr_factory_.GetWeakPtr());
30   content::BrowserChildProcessObserver::Add(this);
31   RegisterExistingRenderProcesses();
32   g_subprocess_metrics_provider_for_testing = this;
33 }
34 
~SubprocessMetricsProvider()35 SubprocessMetricsProvider::~SubprocessMetricsProvider() {
36   // Safe even if this object has never been added as an observer.
37   content::BrowserChildProcessObserver::Remove(this);
38   g_subprocess_metrics_provider_for_testing = nullptr;
39 }
40 
41 // static
MergeHistogramDeltasForTesting()42 void SubprocessMetricsProvider::MergeHistogramDeltasForTesting() {
43   DCHECK(g_subprocess_metrics_provider_for_testing);
44   g_subprocess_metrics_provider_for_testing->MergeHistogramDeltas();
45 }
46 
RegisterExistingRenderProcesses()47 void SubprocessMetricsProvider::RegisterExistingRenderProcesses() {
48   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
49 
50   for (auto it(content::RenderProcessHost::AllHostsIterator()); !it.IsAtEnd();
51        it.Advance()) {
52     auto* rph = it.GetCurrentValue();
53     OnRenderProcessHostCreated(rph);
54     if (rph->IsReady())
55       RenderProcessReady(rph);
56   }
57 }
58 
RegisterSubprocessAllocator(int id,std::unique_ptr<base::PersistentHistogramAllocator> allocator)59 void SubprocessMetricsProvider::RegisterSubprocessAllocator(
60     int id,
61     std::unique_ptr<base::PersistentHistogramAllocator> allocator) {
62   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
63   DCHECK(!allocators_by_id_.Lookup(id));
64 
65   // Stop now if this was called without an allocator, typically because
66   // GetSubprocessHistogramAllocatorOnIOThread exited early and returned
67   // null.
68   if (!allocator)
69     return;
70 
71   // Map is "MapOwnPointer" so transfer ownership to it.
72   allocators_by_id_.AddWithID(std::move(allocator), id);
73 }
74 
DeregisterSubprocessAllocator(int id)75 void SubprocessMetricsProvider::DeregisterSubprocessAllocator(int id) {
76   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
77 
78   if (!allocators_by_id_.Lookup(id))
79     return;
80 
81   // Extract the matching allocator from the list of active ones. It will
82   // be automatically released when this method exits.
83   std::unique_ptr<base::PersistentHistogramAllocator> allocator(
84       allocators_by_id_.Replace(id, nullptr));
85   allocators_by_id_.Remove(id);
86   DCHECK(allocator);
87 
88   // Merge the last deltas from the allocator before it is released.
89   MergeHistogramDeltasFromAllocator(id, allocator.get());
90 }
91 
MergeHistogramDeltasFromAllocator(int id,base::PersistentHistogramAllocator * allocator)92 void SubprocessMetricsProvider::MergeHistogramDeltasFromAllocator(
93     int id,
94     base::PersistentHistogramAllocator* allocator) {
95   DCHECK(allocator);
96 
97   int histogram_count = 0;
98   base::PersistentHistogramAllocator::Iterator hist_iter(allocator);
99   while (true) {
100     std::unique_ptr<base::HistogramBase> histogram = hist_iter.GetNext();
101     if (!histogram)
102       break;
103     allocator->MergeHistogramDeltaToStatisticsRecorder(histogram.get());
104     ++histogram_count;
105   }
106 
107   DVLOG(1) << "Reported " << histogram_count << " histograms from subprocess #"
108            << id;
109 }
110 
MergeHistogramDeltas()111 void SubprocessMetricsProvider::MergeHistogramDeltas() {
112   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
113 
114   for (AllocatorByIdMap::iterator iter(&allocators_by_id_); !iter.IsAtEnd();
115        iter.Advance()) {
116     MergeHistogramDeltasFromAllocator(iter.GetCurrentKey(),
117                                       iter.GetCurrentValue());
118   }
119 }
120 
BrowserChildProcessLaunchedAndConnected(const content::ChildProcessData & data)121 void SubprocessMetricsProvider::BrowserChildProcessLaunchedAndConnected(
122     const content::ChildProcessData& data) {
123   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
124 
125   // See if the new process has a memory allocator and take control of it if so.
126   // This call can only be made on the browser's IO thread.
127   content::BrowserChildProcessHost* host =
128       content::BrowserChildProcessHost::FromID(data.id);
129   CHECK(host);  // TODO(pmonette): Change to DCHECK after September 1st, after
130                 //                 confirming that this never gets hit.
131 
132   std::unique_ptr<base::PersistentMemoryAllocator> allocator =
133       host->TakeMetricsAllocator();
134   // The allocator can be null in tests.
135   if (!allocator)
136     return;
137 
138   RegisterSubprocessAllocator(
139       data.id, std::make_unique<base::PersistentHistogramAllocator>(
140                    std::move(allocator)));
141 }
142 
BrowserChildProcessHostDisconnected(const content::ChildProcessData & data)143 void SubprocessMetricsProvider::BrowserChildProcessHostDisconnected(
144     const content::ChildProcessData& data) {
145   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
146   DeregisterSubprocessAllocator(data.id);
147 }
148 
BrowserChildProcessCrashed(const content::ChildProcessData & data,const content::ChildProcessTerminationInfo & info)149 void SubprocessMetricsProvider::BrowserChildProcessCrashed(
150     const content::ChildProcessData& data,
151     const content::ChildProcessTerminationInfo& info) {
152   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
153   DeregisterSubprocessAllocator(data.id);
154 }
155 
BrowserChildProcessKilled(const content::ChildProcessData & data,const content::ChildProcessTerminationInfo & info)156 void SubprocessMetricsProvider::BrowserChildProcessKilled(
157     const content::ChildProcessData& data,
158     const content::ChildProcessTerminationInfo& info) {
159   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
160   DeregisterSubprocessAllocator(data.id);
161 }
162 
OnRenderProcessHostCreated(content::RenderProcessHost * host)163 void SubprocessMetricsProvider::OnRenderProcessHostCreated(
164     content::RenderProcessHost* host) {
165   // Sometimes, the same host will cause multiple notifications in tests so
166   // could possibly do the same in a release build.
167   if (!scoped_observations_.IsObservingSource(host))
168     scoped_observations_.AddObservation(host);
169 }
170 
RenderProcessReady(content::RenderProcessHost * host)171 void SubprocessMetricsProvider::RenderProcessReady(
172     content::RenderProcessHost* host) {
173   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
174 
175   // If the render-process-host passed a persistent-memory-allocator to the
176   // renderer process, extract it and register it here.
177   std::unique_ptr<base::PersistentMemoryAllocator> allocator =
178       host->TakeMetricsAllocator();
179   if (allocator) {
180     RegisterSubprocessAllocator(
181         host->GetID(), std::make_unique<base::PersistentHistogramAllocator>(
182                            std::move(allocator)));
183   }
184 }
185 
RenderProcessExited(content::RenderProcessHost * host,const content::ChildProcessTerminationInfo & info)186 void SubprocessMetricsProvider::RenderProcessExited(
187     content::RenderProcessHost* host,
188     const content::ChildProcessTerminationInfo& info) {
189   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
190 
191   DeregisterSubprocessAllocator(host->GetID());
192 }
193 
RenderProcessHostDestroyed(content::RenderProcessHost * host)194 void SubprocessMetricsProvider::RenderProcessHostDestroyed(
195     content::RenderProcessHost* host) {
196   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
197 
198   // It's possible for a Renderer to terminate without RenderProcessExited
199   // (above) being called so it's necessary to de-register also upon the
200   // destruction of the host. If both get called, no harm is done.
201 
202   DeregisterSubprocessAllocator(host->GetID());
203   scoped_observations_.RemoveObservation(host);
204 }
205 
206 }  // namespace metrics
207