1 // Copyright 2015 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/content_stability_metrics_provider.h"
6
7 #include "base/memory/raw_ptr.h"
8 #include "base/test/metrics/histogram_tester.h"
9 #include "build/build_config.h"
10 #include "components/metrics/content/extensions_helper.h"
11 #include "components/prefs/pref_service.h"
12 #include "components/prefs/scoped_user_pref_update.h"
13 #include "components/prefs/testing_pref_service.h"
14 #include "components/variations/hashing.h"
15 #include "content/public/browser/browser_context.h"
16 #include "content/public/browser/child_process_data.h"
17 #include "content/public/browser/child_process_termination_info.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/browser/site_instance.h"
20 #include "content/public/common/process_type.h"
21 #include "content/public/test/browser_task_environment.h"
22 #include "content/public/test/mock_render_process_host.h"
23 #include "content/public/test/test_browser_context.h"
24 #include "extensions/buildflags/buildflags.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "third_party/metrics_proto/system_profile.pb.h"
27
28 namespace metrics {
29
30 namespace {
31
32 const char kTestUtilityProcessName[] = "test_utility_process";
33 const char kTestCdmServiceUtilityProcessName[] = "media.mojom.CdmServiceBroker";
34 #if BUILDFLAG(IS_WIN)
35 const char kTestMediaFoundationServiceUtilityProcessName[] =
36 "media.mojom.MediaFoundationServiceBroker";
37 #endif
38
39 class MockExtensionsHelper : public ExtensionsHelper {
40 public:
41 MockExtensionsHelper() = default;
42 MockExtensionsHelper(const MockExtensionsHelper&) = delete;
43 MockExtensionsHelper& operator=(const MockExtensionsHelper&) = delete;
44 ~MockExtensionsHelper() override = default;
45
set_extension_host(content::RenderProcessHost * host)46 void set_extension_host(content::RenderProcessHost* host) { host_ = host; }
47 // ExtensionsHelper:
IsExtensionProcess(content::RenderProcessHost * render_process_host)48 bool IsExtensionProcess(
49 content::RenderProcessHost* render_process_host) override {
50 return render_process_host == host_;
51 }
52
53 private:
54 raw_ptr<content::RenderProcessHost> host_ = nullptr;
55 };
56
57 } // namespace
58
59 class ContentStabilityMetricsProviderTest : public testing::Test {
60 protected:
ContentStabilityMetricsProviderTest()61 ContentStabilityMetricsProviderTest()
62 : prefs_(std::make_unique<TestingPrefServiceSimple>()) {
63 metrics::StabilityMetricsHelper::RegisterPrefs(prefs()->registry());
64 }
65 ContentStabilityMetricsProviderTest(
66 const ContentStabilityMetricsProviderTest&) = delete;
67 ContentStabilityMetricsProviderTest& operator=(
68 const ContentStabilityMetricsProviderTest&) = delete;
69 ~ContentStabilityMetricsProviderTest() override = default;
70
prefs()71 TestingPrefServiceSimple* prefs() { return prefs_.get(); }
72
73 private:
74 std::unique_ptr<TestingPrefServiceSimple> prefs_;
75 content::BrowserTaskEnvironment task_environment_;
76 };
77
TEST_F(ContentStabilityMetricsProviderTest,BrowserChildProcessObserverUtility)78 TEST_F(ContentStabilityMetricsProviderTest,
79 BrowserChildProcessObserverUtility) {
80 base::HistogramTester histogram_tester;
81 metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
82
83 content::ChildProcessData child_process_data(content::PROCESS_TYPE_UTILITY);
84 child_process_data.metrics_name = kTestUtilityProcessName;
85
86 provider.BrowserChildProcessLaunchedAndConnected(child_process_data);
87 const int kExitCode = 1;
88 content::ChildProcessTerminationInfo abnormal_termination_info;
89 abnormal_termination_info.status =
90 base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
91 abnormal_termination_info.exit_code = kExitCode;
92 provider.BrowserChildProcessCrashed(child_process_data,
93 abnormal_termination_info);
94 provider.BrowserChildProcessCrashed(child_process_data,
95 abnormal_termination_info);
96
97 // Verify metrics.
98 histogram_tester.ExpectUniqueSample(
99 "ChildProcess.Launched.UtilityProcessHash",
100 variations::HashName(kTestUtilityProcessName), 1);
101 histogram_tester.ExpectBucketCount("Stability.Counts2",
102 StabilityEventType::kUtilityLaunch, 1);
103 histogram_tester.ExpectUniqueSample(
104 "ChildProcess.Crashed.UtilityProcessHash",
105 variations::HashName(kTestUtilityProcessName), 2);
106 histogram_tester.ExpectUniqueSample(
107 "ChildProcess.Crashed.UtilityProcessExitCode", kExitCode, 2);
108 histogram_tester.ExpectBucketCount("Stability.Counts2",
109 StabilityEventType::kUtilityCrash, 2);
110 }
111
TEST_F(ContentStabilityMetricsProviderTest,CdmServiceProcessObserverUtility)112 TEST_F(ContentStabilityMetricsProviderTest, CdmServiceProcessObserverUtility) {
113 base::HistogramTester histogram_tester;
114 metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
115
116 content::ChildProcessData child_process_data(content::PROCESS_TYPE_UTILITY);
117 child_process_data.metrics_name = kTestCdmServiceUtilityProcessName;
118 child_process_data.sandbox_type = sandbox::mojom::Sandbox::kCdm;
119
120 provider.BrowserChildProcessLaunchedAndConnected(child_process_data);
121 const int kExitCode = 333;
122 content::ChildProcessTerminationInfo abnormal_termination_info;
123 abnormal_termination_info.status =
124 base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
125 abnormal_termination_info.exit_code = kExitCode;
126 provider.BrowserChildProcessCrashed(child_process_data,
127 abnormal_termination_info);
128 provider.BrowserChildProcessCrashed(child_process_data,
129 abnormal_termination_info);
130
131 // Verify metrics.
132 histogram_tester.ExpectUniqueSample(
133 "Stability.Media.CdmServiceBroker.Crash.ExitCode", kExitCode, 2);
134 }
135
TEST_F(ContentStabilityMetricsProviderTest,CdmServiceProcessObserverUtilityLaunchFailed)136 TEST_F(ContentStabilityMetricsProviderTest,
137 CdmServiceProcessObserverUtilityLaunchFailed) {
138 base::HistogramTester histogram_tester;
139 metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
140
141 content::ChildProcessData child_process_data(content::PROCESS_TYPE_UTILITY);
142 child_process_data.metrics_name = kTestCdmServiceUtilityProcessName;
143 child_process_data.sandbox_type = sandbox::mojom::Sandbox::kCdm;
144
145 const int kExitCode = 777;
146 content::ChildProcessTerminationInfo abnormal_termination_info;
147 abnormal_termination_info.status = base::TERMINATION_STATUS_LAUNCH_FAILED;
148 abnormal_termination_info.exit_code = kExitCode;
149 #if BUILDFLAG(IS_WIN)
150 const int kLastError = 9;
151 abnormal_termination_info.last_error = kLastError;
152 #endif
153 provider.BrowserChildProcessLaunchFailed(child_process_data,
154 abnormal_termination_info);
155
156 // Verify metrics.
157 histogram_tester.ExpectUniqueSample("Stability.Media.CdmServiceBroker.Launch",
158 false, 1);
159 histogram_tester.ExpectUniqueSample(
160 "Stability.Media.CdmServiceBroker.Launch.LaunchErrorCode", kExitCode, 1);
161 #if BUILDFLAG(IS_WIN)
162 histogram_tester.ExpectUniqueSample(
163 "Stability.Media.CdmServiceBroker.Launch.WinLastError", kLastError, 1);
164 #endif
165 }
166
167 #if BUILDFLAG(IS_WIN)
TEST_F(ContentStabilityMetricsProviderTest,MediaFoundationServiceProcessObserverUtility)168 TEST_F(ContentStabilityMetricsProviderTest,
169 MediaFoundationServiceProcessObserverUtility) {
170 base::HistogramTester histogram_tester;
171 metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
172
173 content::ChildProcessData child_process_data(content::PROCESS_TYPE_UTILITY);
174 child_process_data.metrics_name =
175 kTestMediaFoundationServiceUtilityProcessName;
176 child_process_data.sandbox_type =
177 sandbox::mojom::Sandbox::kMediaFoundationCdm;
178
179 provider.BrowserChildProcessLaunchedAndConnected(child_process_data);
180 const int kExitCode = 555;
181 content::ChildProcessTerminationInfo abnormal_termination_info;
182 abnormal_termination_info.status =
183 base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
184 abnormal_termination_info.exit_code = kExitCode;
185 provider.BrowserChildProcessCrashed(child_process_data,
186 abnormal_termination_info);
187 provider.BrowserChildProcessCrashed(child_process_data,
188 abnormal_termination_info);
189
190 // Verify metrics.
191 histogram_tester.ExpectUniqueSample(
192 "Stability.Media.MediaFoundationServiceBroker.Crash.ExitCode", kExitCode,
193 2);
194 }
195 #endif // BUILDFLAG(IS_WIN)
196
197 #if !BUILDFLAG(IS_ANDROID)
TEST_F(ContentStabilityMetricsProviderTest,RenderProcessObserver)198 TEST_F(ContentStabilityMetricsProviderTest, RenderProcessObserver) {
199 metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
200 content::TestBrowserContext browser_context;
201 content::MockRenderProcessHostFactory rph_factory;
202 scoped_refptr<content::SiteInstance> site_instance(
203 content::SiteInstance::Create(&browser_context));
204
205 // Owned by rph_factory.
206 content::RenderProcessHost* host(rph_factory.CreateRenderProcessHost(
207 &browser_context, site_instance.get()));
208
209 base::HistogramTester histogram_tester;
210
211 // Crash and abnormal termination should increment renderer crash count.
212 content::ChildProcessTerminationInfo crash_details;
213 crash_details.status = base::TERMINATION_STATUS_PROCESS_CRASHED;
214 crash_details.exit_code = 1;
215 provider.OnRenderProcessHostCreated(host);
216 provider.RenderProcessExited(host, crash_details);
217
218 content::ChildProcessTerminationInfo term_details;
219 term_details.status = base::TERMINATION_STATUS_ABNORMAL_TERMINATION;
220 term_details.exit_code = 1;
221 provider.OnRenderProcessHostCreated(host);
222 provider.RenderProcessExited(host, term_details);
223
224 // Kill does not increment renderer crash count.
225 content::ChildProcessTerminationInfo kill_details;
226 kill_details.status = base::TERMINATION_STATUS_PROCESS_WAS_KILLED;
227 kill_details.exit_code = 1;
228 provider.OnRenderProcessHostCreated(host);
229 provider.RenderProcessExited(host, kill_details);
230
231 // Failed launch increments failed launch count.
232 content::ChildProcessTerminationInfo failed_launch_details;
233 failed_launch_details.status = base::TERMINATION_STATUS_LAUNCH_FAILED;
234 failed_launch_details.exit_code = 1;
235 provider.OnRenderProcessHostCreationFailed(host, failed_launch_details);
236
237 // Verify metrics.
238 histogram_tester.ExpectBucketCount("Stability.Counts2",
239 StabilityEventType::kRendererCrash, 2);
240 histogram_tester.ExpectBucketCount(
241 "Stability.Counts2", StabilityEventType::kRendererFailedLaunch, 1);
242 histogram_tester.ExpectBucketCount("Stability.Counts2",
243 StabilityEventType::kExtensionCrash, 0);
244 }
245
TEST_F(ContentStabilityMetricsProviderTest,MetricsServicesWebContentsObserver)246 TEST_F(ContentStabilityMetricsProviderTest,
247 MetricsServicesWebContentsObserver) {
248 metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
249 base::HistogramTester histogram_tester;
250 histogram_tester.ExpectBucketCount("Stability.Counts2",
251 StabilityEventType::kPageLoad, 0);
252
253 // Simulate page loads.
254 const auto expected_page_load_count = 4;
255 for (int i = 0; i < expected_page_load_count; i++) {
256 provider.OnPageLoadStarted();
257 }
258
259 // Verify metrics.
260 histogram_tester.ExpectBucketCount("Stability.Counts2",
261 StabilityEventType::kPageLoad,
262 expected_page_load_count);
263 }
264
265 #endif // !BUILDFLAG(IS_ANDROID)
266
267 // Assertions for an extension related crash.
268 // This test only works if extensions are enabled as there is a DCHECK in
269 // StabilityMetricsHelper that it is only called with a value of true for
270 // extension process if extensions are enabled.
271 #if BUILDFLAG(ENABLE_EXTENSIONS)
TEST_F(ContentStabilityMetricsProviderTest,ExtensionsNotificationObserver)272 TEST_F(ContentStabilityMetricsProviderTest, ExtensionsNotificationObserver) {
273 content::TestBrowserContext browser_context;
274 content::MockRenderProcessHostFactory rph_factory;
275 scoped_refptr<content::SiteInstance> site_instance(
276 content::SiteInstance::Create(&browser_context));
277
278 // Owned by rph_factory.
279 content::RenderProcessHost* extension_host =
280 rph_factory.CreateRenderProcessHost(&browser_context,
281 site_instance.get());
282 auto extensions_helper = std::make_unique<MockExtensionsHelper>();
283 extensions_helper->set_extension_host(extension_host);
284 metrics::ContentStabilityMetricsProvider provider(
285 prefs(), std::move(extensions_helper));
286
287 base::HistogramTester histogram_tester;
288
289 // Crash and abnormal termination should increment extension crash count.
290 content::ChildProcessTerminationInfo crash_details;
291 crash_details.status = base::TERMINATION_STATUS_PROCESS_CRASHED;
292 crash_details.exit_code = 1;
293 provider.OnRenderProcessHostCreated(extension_host);
294 provider.RenderProcessExited(extension_host, crash_details);
295
296 // Failed launch increments failed launch count.
297 content::ChildProcessTerminationInfo failed_launch_details;
298 failed_launch_details.status = base::TERMINATION_STATUS_LAUNCH_FAILED;
299 failed_launch_details.exit_code = 1;
300 provider.OnRenderProcessHostCreationFailed(extension_host,
301 failed_launch_details);
302
303 // Verify metrics.
304 histogram_tester.ExpectBucketCount("Stability.Counts2",
305 StabilityEventType::kRendererCrash, 0);
306 histogram_tester.ExpectBucketCount("Stability.Counts2",
307 StabilityEventType::kExtensionCrash, 1);
308 histogram_tester.ExpectBucketCount(
309 "Stability.Counts2", StabilityEventType::kExtensionRendererFailedLaunch,
310 1);
311 }
312 #endif
313
314 } // namespace metrics
315