blob: d5471c5bcebbc74f22acfe2d280babaa8ab46b23 [file] [log] [blame]
jcivelli828cd7f2017-01-18 19:50:461// Copyright 2017 The Chromium Authors. All rights reserved.
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 "content/browser/child_process_launcher_helper.h"
6
Ken Rockot86bea1c2017-05-16 06:21:357#include "base/command_line.h"
jcivelli828cd7f2017-01-18 19:50:468#include "base/metrics/histogram_macros.h"
9#include "content/browser/child_process_launcher.h"
10#include "content/public/common/content_switches.h"
11#include "content/public/common/sandboxed_process_launcher_delegate.h"
12#include "mojo/edk/embedder/platform_channel_pair.h"
13
14namespace content {
15namespace internal {
16
17namespace {
18
19void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) {
20 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
21 // Log the launch time, separating out the first one (which will likely be
22 // slower due to the rest of the browser initializing at the same time).
23 static bool done_first_launch = false;
24 if (done_first_launch) {
25 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time);
26 } else {
27 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time);
28 done_first_launch = true;
29 }
30}
31
32} // namespace
33
34ChildProcessLauncherHelper::Process::Process(Process&& other)
35 : process(std::move(other.process))
36#if defined(OS_LINUX)
37 , zygote(other.zygote)
38#endif
39{
40}
41
42ChildProcessLauncherHelper::Process&
43ChildProcessLauncherHelper::Process::Process::operator=(
44 ChildProcessLauncherHelper::Process&& other) {
45 DCHECK_NE(this, &other);
46 process = std::move(other.process);
47#if defined(OS_LINUX)
48 zygote = other.zygote;
49#endif
50 return *this;
51}
52
53ChildProcessLauncherHelper::ChildProcessLauncherHelper(
54 int child_process_id,
55 BrowserThread::ID client_thread_id,
56 std::unique_ptr<base::CommandLine> command_line,
57 std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
58 const base::WeakPtr<ChildProcessLauncher>& child_process_launcher,
Dmitry Skiba1b7dbaf82017-11-09 19:32:0259 bool terminate_on_shutdown,
60 std::unique_ptr<mojo::edk::OutgoingBrokerClientInvitation>
61 broker_client_invitation,
62 const mojo::edk::ProcessErrorCallback& process_error_callback)
jcivelli828cd7f2017-01-18 19:50:4663 : child_process_id_(child_process_id),
64 client_thread_id_(client_thread_id),
65 command_line_(std::move(command_line)),
66 delegate_(std::move(delegate)),
67 child_process_launcher_(child_process_launcher),
Dmitry Skiba1b7dbaf82017-11-09 19:32:0268 terminate_on_shutdown_(terminate_on_shutdown),
69 broker_client_invitation_(std::move(broker_client_invitation)),
70 process_error_callback_(process_error_callback) {}
jcivelli828cd7f2017-01-18 19:50:4671
72ChildProcessLauncherHelper::~ChildProcessLauncherHelper() {
73}
74
75void ChildProcessLauncherHelper::StartLaunchOnClientThread() {
76 DCHECK_CURRENTLY_ON(client_thread_id_);
77
78 BeforeLaunchOnClientThread();
79
80 mojo_server_handle_ = PrepareMojoPipeHandlesOnClientThread();
81 if (!mojo_server_handle_.is_valid()) {
82 mojo::edk::PlatformChannelPair channel_pair;
83 mojo_server_handle_ = channel_pair.PassServerHandle();
84 mojo_client_handle_ = channel_pair.PassClientHandle();
85 }
86
87 BrowserThread::PostTask(
88 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
tzik4fea24af2017-08-23 11:41:4789 base::BindOnce(&ChildProcessLauncherHelper::LaunchOnLauncherThread,
90 this));
jcivelli828cd7f2017-01-18 19:50:4691}
92
93void ChildProcessLauncherHelper::LaunchOnLauncherThread() {
94 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
95
96 begin_launch_time_ = base::TimeTicks::Now();
97
98 std::unique_ptr<FileMappedForLaunch> files_to_register = GetFilesToMap();
99
100 bool is_synchronous_launch = true;
101 int launch_result = LAUNCH_RESULT_FAILURE;
102 base::LaunchOptions options;
jcivelli828cd7f2017-01-18 19:50:46103
Greg Kerra1bc9d02018-01-04 23:22:31104 Process process;
105 if (BeforeLaunchOnLauncherThread(*files_to_register, &options)) {
106 process =
107 LaunchProcessOnLauncherThread(options, std::move(files_to_register),
108 &is_synchronous_launch, &launch_result);
jcivelli828cd7f2017-01-18 19:50:46109
Greg Kerra1bc9d02018-01-04 23:22:31110 AfterLaunchOnLauncherThread(process, options);
111 }
jcivelli828cd7f2017-01-18 19:50:46112
113 if (is_synchronous_launch) {
boliu5674fee2017-04-26 23:41:59114 PostLaunchOnLauncherThread(std::move(process), launch_result);
jcivelli828cd7f2017-01-18 19:50:46115 }
116}
117
118void ChildProcessLauncherHelper::PostLaunchOnLauncherThread(
119 ChildProcessLauncherHelper::Process process,
boliu5674fee2017-04-26 23:41:59120 int launch_result) {
jcivelli828cd7f2017-01-18 19:50:46121 // Release the client handle now that the process has been started (the pipe
122 // may not signal when the process dies otherwise and we would not detect the
123 // child process died).
124 mojo_client_handle_.reset();
125
126 if (process.process.IsValid()) {
boliu5674fee2017-04-26 23:41:59127 RecordHistogramsOnLauncherThread(base::TimeTicks::Now() -
128 begin_launch_time_);
jcivelli828cd7f2017-01-18 19:50:46129 }
130
Dmitry Skiba1b7dbaf82017-11-09 19:32:02131 // Take ownership of the broker client invitation here so it's destroyed when
132 // we go out of scope regardless of the outcome below.
133 std::unique_ptr<mojo::edk::OutgoingBrokerClientInvitation> invitation =
134 std::move(broker_client_invitation_);
135 if (process.process.IsValid()) {
136 // Set up Mojo IPC to the new process.
137 DCHECK(invitation);
138 invitation->Send(
139 process.process.Handle(),
140 mojo::edk::ConnectionParams(mojo::edk::TransportProtocol::kLegacy,
141 std::move(mojo_server_handle_)),
142 process_error_callback_);
143 }
144
boliu5674fee2017-04-26 23:41:59145 BrowserThread::PostTask(
146 client_thread_id_, FROM_HERE,
tzik4fea24af2017-08-23 11:41:47147 base::BindOnce(&ChildProcessLauncherHelper::PostLaunchOnClientThread,
148 this, base::Passed(&process), launch_result));
jcivelli828cd7f2017-01-18 19:50:46149}
150
151void ChildProcessLauncherHelper::PostLaunchOnClientThread(
152 ChildProcessLauncherHelper::Process process,
153 int error_code) {
154 if (child_process_launcher_) {
Dmitry Skiba1b7dbaf82017-11-09 19:32:02155 child_process_launcher_->Notify(std::move(process), error_code);
jcivelli828cd7f2017-01-18 19:50:46156 } else if (process.process.IsValid() && terminate_on_shutdown_) {
157 // Client is gone, terminate the process.
158 ForceNormalProcessTerminationAsync(std::move(process));
159 }
160}
161
162std::string ChildProcessLauncherHelper::GetProcessType() {
163 return command_line()->GetSwitchValueASCII(switches::kProcessType);
164}
165
166// static
167void ChildProcessLauncherHelper::ForceNormalProcessTerminationAsync(
168 ChildProcessLauncherHelper::Process process) {
169 if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) {
170 ForceNormalProcessTerminationSync(std::move(process));
171 return;
172 }
173 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep!
174 // So don't do this on the UI/IO threads.
175 BrowserThread::PostTask(
176 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
tzik4fea24af2017-08-23 11:41:47177 base::BindOnce(
178 &ChildProcessLauncherHelper::ForceNormalProcessTerminationSync,
179 base::Passed(&process)));
jcivelli828cd7f2017-01-18 19:50:46180}
181
182} // namespace internal
183} // namespace content