• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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