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