1 // Copyright 2021 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 <string>
8
9 #include "base/command_line.h"
10 #include "base/files/file_path.h"
11 #include "base/functional/callback.h"
12 #include "base/metrics/histogram.h"
13 #include "base/test/metrics/histogram_tester.h"
14 #include "base/test/scoped_feature_list.h"
15 #include "build/build_config.h"
16 #include "components/metrics/content/extensions_helper.h"
17 #include "components/prefs/testing_pref_service.h"
18 #include "components/variations/hashing.h"
19 #include "content/public/browser/browser_child_process_observer.h"
20 #include "content/public/browser/child_process_data.h"
21 #include "content/public/browser/child_process_termination_info.h"
22 #include "content/public/browser/service_process_host.h"
23 #include "content/public/common/content_features.h"
24 #include "content/public/common/content_switches.h"
25 #include "content/public/test/browser_test.h"
26 #include "content/public/test/browser_test_base.h"
27 #include "content/public/test/content_browser_test.h"
28 #include "content/public/test/content_browser_test_utils.h"
29 #include "content/public/test/test_service.mojom.h"
30 #include "sandbox/policy/mojom/sandbox.mojom.h"
31
32 #if BUILDFLAG(IS_WIN)
33 #include <windows.h>
34
35 #include "sandbox/win/src/sandbox_types.h"
36 #endif
37
38 namespace content {
39
40 template <>
GetServiceSandboxType()41 sandbox::mojom::Sandbox GetServiceSandboxType<content::mojom::TestService>() {
42 // On Windows, the sandbox does not like having a different binary name
43 // 'non_existent_path' from the browser process, so set no sandbox here.
44 #if BUILDFLAG(IS_WIN)
45 return sandbox::mojom::Sandbox::kNoSandbox;
46 #else
47 return sandbox::mojom::Sandbox::kService;
48 #endif
49 }
50
51 } // namespace content
52
53 namespace metrics {
54
55 class ContentStabilityProviderBrowserTest
56 : public content::ContentBrowserTest,
57 content::BrowserChildProcessObserver {
58 public:
ContentStabilityProviderBrowserTest()59 ContentStabilityProviderBrowserTest() {
60 feature_list_.InitAndDisableFeature(
61 features::kSpareRendererForSitePerProcess);
62 }
63
64 // Either the process launched, or did not launch. Both cause the run_loop to
65 // terminate.
BrowserChildProcessLaunchFailed(const content::ChildProcessData & data,const content::ChildProcessTerminationInfo & info)66 void BrowserChildProcessLaunchFailed(
67 const content::ChildProcessData& data,
68 const content::ChildProcessTerminationInfo& info) override {
69 if (data.metrics_name == content::mojom::TestService::Name_)
70 std::move(done_closure_).Run();
71 }
72
BrowserChildProcessLaunchedAndConnected(const content::ChildProcessData & data)73 void BrowserChildProcessLaunchedAndConnected(
74 const content::ChildProcessData& data) override {
75 if (data.metrics_name == content::mojom::TestService::Name_)
76 std::move(done_closure_).Run();
77 }
78
79 protected:
AddObserver()80 void AddObserver() { content::BrowserChildProcessObserver::Add(this); }
81
RemoveObserver()82 void RemoveObserver() { content::BrowserChildProcessObserver::Remove(this); }
83
84 base::OnceClosure done_closure_;
85 TestingPrefServiceSimple prefs_;
86 base::test::ScopedFeatureList feature_list_;
87 };
88
IN_PROC_BROWSER_TEST_F(ContentStabilityProviderBrowserTest,FailedUtilityProcessLaunches)89 IN_PROC_BROWSER_TEST_F(ContentStabilityProviderBrowserTest,
90 FailedUtilityProcessLaunches) {
91 base::RunLoop run_loop;
92 done_closure_ = run_loop.QuitClosure();
93 AddObserver();
94
95 ContentStabilityMetricsProvider provider(&prefs_, nullptr);
96 base::HistogramTester histogram_tester;
97
98 // Simulate a catastrophic utility process launch failure by specifying a bad
99 // path.
100 base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
101 switches::kBrowserSubprocessPath,
102 base::FilePath(FILE_PATH_LITERAL("non_existent_path")));
103 mojo::Remote<content::mojom::TestService> test_service;
104 content::ServiceProcessHost::Launch(
105 test_service.BindNewPipeAndPassReceiver());
106
107 // run_loop runs until either the process launches or fails to launch.
108 run_loop.Run();
109
110 RemoveObserver();
111
112 histogram_tester.ExpectUniqueSample(
113 "ChildProcess.LaunchFailed.UtilityProcessHash",
114 variations::HashName(content::mojom::TestService::Name_), 1);
115 #if BUILDFLAG(IS_WIN)
116 int expected_error_code =
117 sandbox::SBOX_ERROR_CANNOT_LAUNCH_UNSANDBOXED_PROCESS;
118 #else
119 int expected_error_code =
120 1003; // content::LaunchResultCode::LAUNCH_RESULT_FAILURE.
121 #endif
122 histogram_tester.ExpectUniqueSample(
123 "ChildProcess.LaunchFailed.UtilityProcessErrorCode", expected_error_code,
124 1);
125
126 #if BUILDFLAG(IS_WIN)
127 // Last Error is only recorded on Windows.
128 histogram_tester.ExpectUniqueSample("ChildProcess.LaunchFailed.WinLastError",
129 DWORD{ERROR_FILE_NOT_FOUND}, 1);
130 #endif
131 }
132
133 // Class to execute a closure after we observer a renderer process launch or
134 // launch failure.
135 class RenderProcessCreationObserver
136 : content::RenderProcessHostCreationObserver {
137 public:
RenderProcessCreationObserver(base::OnceClosure done_closure)138 RenderProcessCreationObserver(base::OnceClosure done_closure)
139 : done_closure_(std::move(done_closure)) {}
140 ~RenderProcessCreationObserver() override = default;
141
OnRenderProcessHostCreated(content::RenderProcessHost * process_host)142 void OnRenderProcessHostCreated(
143 content::RenderProcessHost* process_host) override {
144 if (done_closure_) {
145 std::move(done_closure_).Run();
146 }
147 }
148
OnRenderProcessHostCreationFailed(content::RenderProcessHost * host,const content::ChildProcessTerminationInfo & info)149 void OnRenderProcessHostCreationFailed(
150 content::RenderProcessHost* host,
151 const content::ChildProcessTerminationInfo& info) override {
152 ASSERT_EQ(base::TERMINATION_STATUS_LAUNCH_FAILED, info.status);
153 if (done_closure_) {
154 std::move(done_closure_).Run();
155 }
156 }
157
158 private:
159 base::OnceClosure done_closure_;
160 };
161
IN_PROC_BROWSER_TEST_F(ContentStabilityProviderBrowserTest,FailedRendererProcessLaunches)162 IN_PROC_BROWSER_TEST_F(ContentStabilityProviderBrowserTest,
163 FailedRendererProcessLaunches) {
164 base::RunLoop run_loop;
165 ContentStabilityMetricsProvider provider(&prefs_, nullptr);
166 base::HistogramTester histogram_tester;
167 {
168 RenderProcessCreationObserver renderer_observer(run_loop.QuitClosure());
169
170 // Simulate a catastrophic renderer process launch failure by specifying a
171 // bad path.
172 base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
173 switches::kBrowserSubprocessPath,
174 base::FilePath(FILE_PATH_LITERAL("non_existent_path")));
175
176 ASSERT_FALSE(content::NavigateToURL(shell(), GURL("about:blank")));
177
178 // run_loop runs until either the process launches or fails to launch.
179 run_loop.Run();
180 }
181
182 histogram_tester.ExpectUniqueSample(
183 "BrowserRenderProcessHost.ChildLaunchFailures",
184 1 /* CoarseRendererType::kRenderer */, 1);
185 #if BUILDFLAG(IS_WIN)
186 int expected_error_code = sandbox::SBOX_ERROR_CREATE_PROCESS;
187 #else
188 int expected_error_code =
189 1003; // content::LaunchResultCode::LAUNCH_RESULT_FAILURE.
190 #endif
191 histogram_tester.ExpectUniqueSample(
192 "BrowserRenderProcessHost.ChildLaunchFailureCodes", expected_error_code,
193 1);
194
195 histogram_tester.ExpectBucketCount(
196 "Stability.Counts2", StabilityEventType::kRendererFailedLaunch, 1);
197 }
198
199 } // namespace metrics
200