Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(147)

Side by Side Diff: content/browser/child_process_launcher.cc

Issue 2594203004: Unifying ChildProcessLauncher across platforms. (Closed)
Patch Set: Addressed boliu@'s comments. Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/child_process_launcher.h" 5 #include "content/browser/child_process_launcher.h"
6 6
7 #include <memory>
8 #include <utility>
9
10 #include "base/bind.h" 7 #include "base/bind.h"
11 #include "base/command_line.h" 8 #include "base/command_line.h"
12 #include "base/files/file_util.h" 9 #include "base/files/file_util.h"
13 #include "base/i18n/icu_util.h" 10 #include "base/i18n/icu_util.h"
14 #include "base/logging.h" 11 #include "base/logging.h"
15 #include "base/metrics/field_trial.h"
16 #include "base/metrics/histogram_macros.h" 12 #include "base/metrics/histogram_macros.h"
17 #include "base/process/launch.h" 13 #include "base/process/launch.h"
18 #include "base/process/process.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/synchronization/lock.h"
21 #include "base/threading/thread.h"
22 #include "build/build_config.h" 14 #include "build/build_config.h"
23 #include "content/public/browser/content_browser_client.h"
24 #include "content/public/common/content_descriptors.h"
25 #include "content/public/common/content_switches.h" 15 #include "content/public/common/content_switches.h"
26 #include "content/public/common/result_codes.h" 16 #include "content/public/common/result_codes.h"
27 #include "content/public/common/sandboxed_process_launcher_delegate.h" 17 #include "content/public/common/sandboxed_process_launcher_delegate.h"
28 #include "mojo/edk/embedder/embedder.h"
29 #include "mojo/edk/embedder/named_platform_channel_pair.h"
30 #include "mojo/edk/embedder/platform_channel_pair.h" 18 #include "mojo/edk/embedder/platform_channel_pair.h"
31 #include "mojo/edk/embedder/scoped_platform_handle.h"
32 #include "ppapi/features/features.h"
33
34 #if defined(OS_WIN)
35 #include "base/files/file_path.h"
36 #include "base/win/scoped_handle.h"
37 #include "base/win/win_util.h"
38 #include "content/common/sandbox_win.h"
39 #include "content/public/common/sandbox_init.h"
40 #include "sandbox/win/src/sandbox_types.h"
41 #elif defined(OS_MACOSX)
42 #include "content/browser/bootstrap_sandbox_manager_mac.h"
43 #include "content/browser/mach_broker_mac.h"
44 #include "sandbox/mac/bootstrap_sandbox.h"
45 #include "sandbox/mac/pre_exec_delegate.h"
46 #elif defined(OS_ANDROID)
47 #include "base/android/jni_android.h"
48 #include "content/browser/android/child_process_launcher_android.h"
49 #elif defined(OS_POSIX)
50 #include "base/memory/singleton.h"
51 #include "content/browser/renderer_host/render_sandbox_host_linux.h"
52 #include "content/browser/zygote_host/zygote_communication_linux.h"
53 #include "content/browser/zygote_host/zygote_host_impl_linux.h"
54 #include "content/common/child_process_sandbox_support_impl_linux.h"
55 #include "content/public/browser/zygote_handle_linux.h"
56 #endif
57
58 #if defined(OS_POSIX)
59 #include "base/posix/global_descriptors.h"
60 #include "content/browser/file_descriptor_info_impl.h"
61 #include "gin/v8_initializer.h"
62 #endif
63 19
64 namespace content { 20 namespace content {
65 21
66 namespace { 22 namespace {
67 23
68 typedef base::Callback<void(ZygoteHandle,
69 #if defined(OS_ANDROID)
70 base::ScopedFD,
71 #endif
72 base::Process,
73 int)>
74 NotifyCallback;
75
76 void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) { 24 void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) {
77 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); 25 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
78 // Log the launch time, separating out the first one (which will likely be 26 // Log the launch time, separating out the first one (which will likely be
79 // slower due to the rest of the browser initializing at the same time). 27 // slower due to the rest of the browser initializing at the same time).
80 static bool done_first_launch = false; 28 static bool done_first_launch = false;
81 if (done_first_launch) { 29 if (done_first_launch) {
82 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time); 30 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time);
83 } else { 31 } else {
84 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time); 32 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time);
85 done_first_launch = true; 33 done_first_launch = true;
86 } 34 }
87 } 35 }
88 36
89 #if defined(OS_ANDROID)
90 // TODO(sievers): Remove this by defining better what happens on what
91 // thread in the corresponding Java code.
92 void OnChildProcessStartedAndroid(const NotifyCallback& callback,
93 BrowserThread::ID client_thread_id,
94 const base::TimeTicks begin_launch_time,
95 base::ScopedFD mojo_fd,
96 base::ProcessHandle handle) {
97 int launch_result = (handle == base::kNullProcessHandle)
98 ? LAUNCH_RESULT_FAILURE
99 : LAUNCH_RESULT_SUCCESS;
100 // This can be called on the launcher thread or UI thread.
101 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time;
102 BrowserThread::PostTask(
103 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
104 base::Bind(&RecordHistogramsOnLauncherThread, launch_time));
105
106 base::Closure callback_on_client_thread(
107 base::Bind(callback, nullptr, base::Passed(&mojo_fd),
108 base::Passed(base::Process(handle)), launch_result));
109 if (BrowserThread::CurrentlyOn(client_thread_id)) {
110 callback_on_client_thread.Run();
111 } else {
112 BrowserThread::PostTask(
113 client_thread_id, FROM_HERE, callback_on_client_thread);
114 }
115 }
116 #endif
117
118 void LaunchOnLauncherThread(
119 const NotifyCallback& callback,
120 BrowserThread::ID client_thread_id,
121 int child_process_id,
122 std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
123 mojo::edk::ScopedPlatformHandle client_handle,
124 std::unique_ptr<base::CommandLine> cmd_line) {
125 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
126 #if !defined(OS_ANDROID)
127 ZygoteHandle zygote = nullptr;
128 int launch_result = LAUNCH_RESULT_FAILURE;
129 #endif
130 #if defined(OS_WIN)
131 bool launch_elevated = delegate->ShouldLaunchElevated();
132 #elif defined(OS_MACOSX)
133 base::EnvironmentMap env = delegate->GetEnvironment();
134 #elif defined(OS_POSIX) && !defined(OS_ANDROID)
135 base::EnvironmentMap env = delegate->GetEnvironment();
136 #endif
137 base::TimeTicks begin_launch_time = base::TimeTicks::Now();
138
139 base::Process process;
140 #if defined(OS_WIN)
141 if (launch_elevated) {
142 // When establishing a Mojo connection, the pipe path has already been added
143 // to the command line.
144 base::LaunchOptions options;
145 options.start_hidden = true;
146 process = base::LaunchElevatedProcess(*cmd_line, options);
147 } else {
148 base::HandlesToInheritVector handles;
149 handles.push_back(client_handle.get().handle);
150 base::FieldTrialList::AppendFieldTrialHandleIfNeeded(&handles);
151 cmd_line->AppendSwitchASCII(
152 mojo::edk::PlatformChannelPair::kMojoPlatformChannelHandleSwitch,
153 base::UintToString(base::win::HandleToUint32(handles[0])));
154 launch_result = StartSandboxedProcess(
155 delegate.get(), cmd_line.get(), handles, &process);
156 }
157 #elif defined(OS_POSIX)
158 std::string process_type =
159 cmd_line->GetSwitchValueASCII(switches::kProcessType);
160 std::unique_ptr<FileDescriptorInfo> files_to_register(
161 FileDescriptorInfoImpl::Create());
162
163 base::ScopedFD mojo_fd(client_handle.release().handle);
164 DCHECK(mojo_fd.is_valid());
165
166 int field_trial_handle = base::FieldTrialList::GetFieldTrialHandle();
167 if (field_trial_handle != base::kInvalidPlatformFile)
168 files_to_register->Share(kFieldTrialDescriptor, field_trial_handle);
169 #if defined(OS_ANDROID)
170 files_to_register->Share(kMojoIPCChannel, mojo_fd.get());
171 #else
172 files_to_register->Transfer(kMojoIPCChannel, std::move(mojo_fd));
173 #endif
174 #endif
175
176 #if defined(OS_POSIX) && !defined(OS_MACOSX)
177 GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess(
178 *cmd_line, child_process_id, files_to_register.get());
179 #if defined(V8_USE_EXTERNAL_STARTUP_DATA)
180 bool snapshot_loaded = false;
181 base::MemoryMappedFile::Region region;
182 #if defined(OS_ANDROID)
183 auto maybe_register = [&region, &files_to_register](int key, int fd) {
184 if (fd != -1)
185 files_to_register->ShareWithRegion(key, fd, region);
186 };
187 maybe_register(
188 kV8NativesDataDescriptor,
189 gin::V8Initializer::GetOpenNativesFileForChildProcesses(&region));
190 maybe_register(
191 kV8SnapshotDataDescriptor32,
192 gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(&region, true));
193 maybe_register(
194 kV8SnapshotDataDescriptor64,
195 gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(&region, false));
196
197 snapshot_loaded = true;
198 #else
199 base::PlatformFile natives_pf =
200 gin::V8Initializer::GetOpenNativesFileForChildProcesses(&region);
201 DCHECK_GE(natives_pf, 0);
202 files_to_register->ShareWithRegion(
203 kV8NativesDataDescriptor, natives_pf, region);
204
205 base::PlatformFile snapshot_pf =
206 gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(&region);
207 // Failure to load the V8 snapshot is not necessarily an error. V8 can start
208 // up (slower) without the snapshot.
209 if (snapshot_pf != -1) {
210 snapshot_loaded = true;
211 files_to_register->ShareWithRegion(
212 kV8SnapshotDataDescriptor, snapshot_pf, region);
213 }
214 #endif
215
216 if (process_type != switches::kZygoteProcess) {
217 cmd_line->AppendSwitch(::switches::kV8NativesPassedByFD);
218 if (snapshot_loaded) {
219 cmd_line->AppendSwitch(::switches::kV8SnapshotPassedByFD);
220 }
221 }
222 #endif // defined(V8_USE_EXTERNAL_STARTUP_DATA)
223 #endif // defined(OS_POSIX) && !defined(OS_MACOSX)
224
225 #if defined(OS_ANDROID)
226 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
227 base::MemoryMappedFile::Region icu_region;
228 base::PlatformFile icu_pf = base::i18n::GetIcuDataFileHandle(&icu_region);
229 files_to_register->ShareWithRegion(
230 kAndroidICUDataDescriptor, icu_pf, icu_region);
231 #endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
232
233 // Android WebView runs in single process, ensure that we never get here
234 // when running in single process mode.
235 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess));
236
237 StartChildProcess(
238 cmd_line->argv(), child_process_id, std::move(files_to_register),
239 base::Bind(&OnChildProcessStartedAndroid, callback, client_thread_id,
240 begin_launch_time, base::Passed(&mojo_fd)));
241
242 #elif defined(OS_POSIX)
243 // We need to close the client end of the IPC channel to reliably detect
244 // child termination.
245
246 #if !defined(OS_MACOSX)
247 ZygoteHandle* zygote_handle =
248 !base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoZygote)
249 ? delegate->GetZygote()
250 : nullptr;
251 // If |zygote_handle| is null, a zygote should not be used.
252 if (zygote_handle) {
253 // This code runs on the PROCESS_LAUNCHER thread so race conditions are not
254 // an issue with the lazy initialization.
255 if (*zygote_handle == nullptr) {
256 *zygote_handle = CreateZygote();
257 }
258 zygote = *zygote_handle;
259 base::ProcessHandle handle = zygote->ForkRequest(
260 cmd_line->argv(), std::move(files_to_register), process_type);
261 process = base::Process(handle);
262 } else
263 // Fall through to the normal posix case below when we're not zygoting.
264 #endif // !defined(OS_MACOSX)
265 {
266 // Convert FD mapping to FileHandleMappingVector
267 base::FileHandleMappingVector fds_to_map =
268 files_to_register->GetMappingWithIDAdjustment(
269 base::GlobalDescriptors::kBaseDescriptor);
270
271 #if !defined(OS_MACOSX)
272 if (process_type == switches::kRendererProcess) {
273 const int sandbox_fd =
274 RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
275 fds_to_map.push_back(std::make_pair(
276 sandbox_fd,
277 GetSandboxFD()));
278 }
279 #endif // defined(OS_MACOSX)
280
281 // Actually launch the app.
282 base::LaunchOptions options;
283 options.environ = env;
284 options.fds_to_remap = &fds_to_map;
285
286 #if defined(OS_MACOSX)
287 // Hold the MachBroker lock for the duration of LaunchProcess. The child
288 // will send its task port to the parent almost immediately after startup.
289 // The Mach message will be delivered to the parent, but updating the
290 // record of the launch will wait until after the placeholder PID is
291 // inserted below. This ensures that while the child process may send its
292 // port to the parent prior to the parent leaving LaunchProcess, the
293 // order in which the record in MachBroker is updated is correct.
294 MachBroker* broker = MachBroker::GetInstance();
295 broker->GetLock().Acquire();
296
297 // Make sure the MachBroker is running, and inform it to expect a
298 // check-in from the new process.
299 broker->EnsureRunning();
300
301 const SandboxType sandbox_type = delegate->GetSandboxType();
302 std::unique_ptr<sandbox::PreExecDelegate> pre_exec_delegate;
303 if (BootstrapSandboxManager::ShouldEnable()) {
304 BootstrapSandboxManager* sandbox_manager =
305 BootstrapSandboxManager::GetInstance();
306 if (sandbox_manager->EnabledForSandbox(sandbox_type)) {
307 pre_exec_delegate = sandbox_manager->sandbox()->NewClient(sandbox_type);
308 }
309 }
310 options.pre_exec_delegate = pre_exec_delegate.get();
311 #endif // defined(OS_MACOSX)
312
313 process = base::LaunchProcess(*cmd_line, options);
314
315 #if defined(OS_MACOSX)
316 if (process.IsValid()) {
317 broker->AddPlaceholderForPid(process.Pid(), child_process_id);
318 } else {
319 if (pre_exec_delegate) {
320 BootstrapSandboxManager::GetInstance()->sandbox()->RevokeToken(
321 pre_exec_delegate->sandbox_token());
322 }
323 }
324
325 // After updating the broker, release the lock and let the child's
326 // message be processed on the broker's thread.
327 broker->GetLock().Release();
328 #endif // defined(OS_MACOSX)
329 }
330 #endif // else defined(OS_POSIX)
331 #if !defined(OS_ANDROID)
332 if (process.IsValid()) {
333 launch_result = LAUNCH_RESULT_SUCCESS;
334 RecordHistogramsOnLauncherThread(base::TimeTicks::Now() -
335 begin_launch_time);
336 }
337 BrowserThread::PostTask(client_thread_id, FROM_HERE,
338 base::Bind(callback, zygote, base::Passed(&process),
339 launch_result));
340 #endif // !defined(OS_ANDROID)
341 }
342
343 void TerminateOnLauncherThread(ZygoteHandle zygote, base::Process process) {
344 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
345 #if defined(OS_ANDROID)
346 VLOG(1) << "ChromeProcess: Stopping process with handle "
347 << process.Handle();
348 StopChildProcess(process.Handle());
349 #else
350 // Client has gone away, so just kill the process. Using exit code 0
351 // means that UMA won't treat this as a crash.
352 process.Terminate(RESULT_CODE_NORMAL_EXIT, false);
353 // On POSIX, we must additionally reap the child.
354 #if defined(OS_POSIX)
355 #if !defined(OS_MACOSX)
356 if (zygote) {
357 // If the renderer was created via a zygote, we have to proxy the reaping
358 // through the zygote process.
359 zygote->EnsureProcessTerminated(process.Handle());
360 } else
361 #endif // !OS_MACOSX
362 base::EnsureProcessTerminated(std::move(process));
363 #endif // OS_POSIX
364 #endif // defined(OS_ANDROID)
365 }
366
367 void SetProcessBackgroundedOnLauncherThread(base::Process process,
368 bool background) {
369 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
370 if (process.CanBackgroundProcesses()) {
371 #if defined(OS_MACOSX)
372 process.SetProcessBackgrounded(MachBroker::GetInstance(), background);
373 #else
374 process.SetProcessBackgrounded(background);
375 #endif // defined(OS_MACOSX)
376 }
377 #if defined(OS_ANDROID)
378 SetChildProcessInForeground(process.Handle(), !background);
379 #endif
380 }
381
382 } // namespace 37 } // namespace
383 38
384 ChildProcessLauncher::ChildProcessLauncher( 39 ChildProcessLauncher::ChildProcessLauncher(
385 std::unique_ptr<SandboxedProcessLauncherDelegate> delegate, 40 std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
386 std::unique_ptr<base::CommandLine> cmd_line, 41 std::unique_ptr<base::CommandLine> command_line,
387 int child_process_id, 42 int child_process_id,
388 Client* client, 43 Client* client,
389 const std::string& mojo_child_token, 44 const std::string& mojo_child_token,
390 const mojo::edk::ProcessErrorCallback& process_error_callback, 45 const mojo::edk::ProcessErrorCallback& process_error_callback,
391 bool terminate_on_shutdown) 46 bool terminate_on_shutdown)
392 : client_(client), 47 : client_(client),
393 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), 48 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
394 exit_code_(RESULT_CODE_NORMAL_EXIT), 49 exit_code_(RESULT_CODE_NORMAL_EXIT),
395 zygote_(nullptr), 50 zygote_(nullptr),
396 starting_(true), 51 starting_(true),
397 process_error_callback_(process_error_callback), 52 process_error_callback_(process_error_callback),
398 #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ 53 #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \
399 defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ 54 defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \
400 defined(UNDEFINED_SANITIZER) 55 defined(UNDEFINED_SANITIZER)
401 terminate_child_on_shutdown_(false), 56 terminate_child_on_shutdown_(false),
402 #else 57 #else
403 terminate_child_on_shutdown_(terminate_on_shutdown), 58 terminate_child_on_shutdown_(terminate_on_shutdown),
404 #endif 59 #endif
405 mojo_child_token_(mojo_child_token), 60 mojo_child_token_(mojo_child_token),
406 weak_factory_(this) { 61 weak_factory_(this) {
407 DCHECK(CalledOnValidThread()); 62 DCHECK(CalledOnValidThread());
408 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); 63 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
409 Launch(std::move(delegate), std::move(cmd_line), child_process_id); 64
65 helper_ = new Helper(child_process_id, client_thread_id_,
66 std::move(command_line), std::move(delegate),
67 weak_factory_.GetWeakPtr(), terminate_on_shutdown);
68 helper_->StartLaunchOnClientThread();
410 } 69 }
411 70
412 ChildProcessLauncher::~ChildProcessLauncher() { 71 ChildProcessLauncher::~ChildProcessLauncher() {
413 DCHECK(CalledOnValidThread()); 72 DCHECK(CalledOnValidThread());
414 if (process_.IsValid() && terminate_child_on_shutdown_) { 73 if (process_.IsValid() && terminate_child_on_shutdown_)
415 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So 74 Terminate(zygote_, std::move(process_));
416 // don't this on the UI/IO threads.
417 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
418 base::Bind(&TerminateOnLauncherThread, zygote_,
419 base::Passed(&process_)));
420 }
421 }
422
423 void ChildProcessLauncher::Launch(
424 std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
425 std::unique_ptr<base::CommandLine> cmd_line,
426 int child_process_id) {
427 DCHECK(CalledOnValidThread());
428
429 #if defined(OS_ANDROID)
430 // Android only supports renderer, sandboxed utility and gpu.
431 std::string process_type =
432 cmd_line->GetSwitchValueASCII(switches::kProcessType);
433 CHECK(process_type == switches::kGpuProcess ||
434 process_type == switches::kRendererProcess ||
435 #if BUILDFLAG(ENABLE_PLUGINS)
436 process_type == switches::kPpapiPluginProcess ||
437 #endif
438 process_type == switches::kUtilityProcess)
439 << "Unsupported process type: " << process_type;
440
441 // Non-sandboxed utility or renderer process are currently not supported.
442 DCHECK(process_type == switches::kGpuProcess ||
443 !cmd_line->HasSwitch(switches::kNoSandbox));
444
445 #endif
446 mojo::edk::ScopedPlatformHandle server_handle;
447 mojo::edk::ScopedPlatformHandle client_handle;
448 #if defined(OS_WIN)
449 if (delegate->ShouldLaunchElevated()) {
450 mojo::edk::NamedPlatformChannelPair named_pair;
451 server_handle = named_pair.PassServerHandle();
452 named_pair.PrepareToPassClientHandleToChildProcess(cmd_line.get());
453 } else
454 #endif
455 {
456 mojo::edk::PlatformChannelPair channel_pair;
457 server_handle = channel_pair.PassServerHandle();
458 client_handle = channel_pair.PassClientHandle();
459 }
460 NotifyCallback reply_callback(base::Bind(&ChildProcessLauncher::DidLaunch,
461 weak_factory_.GetWeakPtr(),
462 terminate_child_on_shutdown_,
463 base::Passed(&server_handle)));
464 BrowserThread::PostTask(
465 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
466 base::Bind(&LaunchOnLauncherThread, reply_callback, client_thread_id_,
467 child_process_id, base::Passed(&delegate),
468 base::Passed(&client_handle), base::Passed(&cmd_line)));
469 }
470
471 void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) {
472 DCHECK(CalledOnValidThread());
473 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
474 if (zygote_) {
475 termination_status_ = zygote_->GetTerminationStatus(
476 process_.Handle(), known_dead, &exit_code_);
477 } else if (known_dead) {
478 termination_status_ =
479 base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_);
480 } else {
481 #elif defined(OS_MACOSX)
482 if (known_dead) {
483 termination_status_ =
484 base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_);
485 } else {
486 #elif defined(OS_ANDROID)
487 if (IsChildProcessOomProtected(process_.Handle())) {
488 termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED;
489 } else {
490 #else
491 {
492 #endif
493 termination_status_ =
494 base::GetTerminationStatus(process_.Handle(), &exit_code_);
495 }
496 } 75 }
497 76
498 void ChildProcessLauncher::SetProcessBackgrounded(bool background) { 77 void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
499 DCHECK(CalledOnValidThread()); 78 DCHECK(CalledOnValidThread());
500 base::Process to_pass = process_.Duplicate(); 79 base::Process to_pass = process_.Duplicate();
501 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 80 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
502 base::Bind(&SetProcessBackgroundedOnLauncherThread, 81 base::Bind(&SetProcessBackgroundedOnLauncherThread,
503 base::Passed(&to_pass), background)); 82 base::Passed(&to_pass), background));
504 } 83 }
505 84
506 void ChildProcessLauncher::DidLaunch(
507 base::WeakPtr<ChildProcessLauncher> instance,
508 bool terminate_on_shutdown,
509 mojo::edk::ScopedPlatformHandle server_handle,
510 ZygoteHandle zygote,
511 #if defined(OS_ANDROID)
512 base::ScopedFD mojo_fd,
513 #endif
514 base::Process process,
515 int error_code) {
516 if (!process.IsValid())
517 LOG(ERROR) << "Failed to launch child process";
518
519 if (instance.get()) {
520 instance->Notify(zygote, std::move(server_handle),
521 std::move(process), error_code);
522 } else {
523 if (process.IsValid() && terminate_on_shutdown) {
524 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
525 // don't this on the UI/IO threads.
526 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
527 base::Bind(&TerminateOnLauncherThread, zygote,
528 base::Passed(&process)));
529 }
530 }
531 }
532
533 void ChildProcessLauncher::Notify(ZygoteHandle zygote, 85 void ChildProcessLauncher::Notify(ZygoteHandle zygote,
534 mojo::edk::ScopedPlatformHandle server_handle, 86 mojo::edk::ScopedPlatformHandle server_handle,
535 base::Process process, 87 base::Process process,
536 int error_code) { 88 int error_code) {
537 DCHECK(CalledOnValidThread()); 89 DCHECK(CalledOnValidThread());
538 starting_ = false; 90 starting_ = false;
539 process_ = std::move(process); 91 process_ = std::move(process);
540 92
541 if (process_.IsValid()) { 93 if (process_.IsValid()) {
542 // Set up Mojo IPC to the new process. 94 // Set up Mojo IPC to the new process.
543 mojo::edk::ChildProcessLaunched(process_.Handle(), std::move(server_handle), 95 mojo::edk::ChildProcessLaunched(process_.Handle(), std::move(server_handle),
544 mojo_child_token_, process_error_callback_); 96 mojo_child_token_, process_error_callback_);
545 } 97 }
546 98
547 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
548 zygote_ = zygote; 99 zygote_ = zygote;
549 #endif
550 if (process_.IsValid()) { 100 if (process_.IsValid()) {
551 client_->OnProcessLaunched(); 101 client_->OnProcessLaunched();
552 } else { 102 } else {
553 mojo::edk::ChildProcessLaunchFailed(mojo_child_token_); 103 mojo::edk::ChildProcessLaunchFailed(mojo_child_token_);
554 termination_status_ = base::TERMINATION_STATUS_LAUNCH_FAILED; 104 termination_status_ = base::TERMINATION_STATUS_LAUNCH_FAILED;
555 client_->OnProcessLaunchFailed(error_code); 105 client_->OnProcessLaunchFailed(error_code);
556 } 106 }
557 } 107 }
558 108
559 bool ChildProcessLauncher::IsStarting() { 109 bool ChildProcessLauncher::IsStarting() {
560 // TODO(crbug.com/469248): This fails in some tests. 110 // TODO(crbug.com/469248): This fails in some tests.
561 // DCHECK(CalledOnValidThread()); 111 // DCHECK(CalledOnValidThread());
562 return starting_; 112 return starting_;
563 } 113 }
564 114
565 const base::Process& ChildProcessLauncher::GetProcess() const { 115 const base::Process& ChildProcessLauncher::GetProcess() const {
566 // TODO(crbug.com/469248): This fails in some tests. 116 // TODO(crbug.com/469248): This fails in some tests.
567 // DCHECK(CalledOnValidThread()); 117 // DCHECK(CalledOnValidThread());
boliu 2017/01/13 19:00:55 nit: empty space change
Jay Civelli 2017/01/17 17:50:02 Done.
568 return process_; 118 return process_;
569 } 119 }
570 120
571 base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus( 121 base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
572 bool known_dead, 122 bool known_dead,
573 int* exit_code) { 123 int* exit_code) {
574 DCHECK(CalledOnValidThread()); 124 DCHECK(CalledOnValidThread());
575 if (!process_.IsValid()) { 125 if (!process_.IsValid()) {
576 // Process is already gone, so return the cached termination status. 126 // Process is already gone, so return the cached termination status.
577 if (exit_code) 127 if (exit_code)
578 *exit_code = exit_code_; 128 *exit_code = exit_code_;
579 return termination_status_; 129 return termination_status_;
580 } 130 }
581 131
582 UpdateTerminationStatus(known_dead); 132 UpdateTerminationStatus(known_dead);
583 if (exit_code) 133 if (exit_code)
584 *exit_code = exit_code_; 134 *exit_code = exit_code_;
585 135
586 // POSIX: If the process crashed, then the kernel closed the socket 136 // POSIX: If the process crashed, then the kernel closed the socket for it and
587 // for it and so the child has already died by the time we get 137 // so the child has already died by the time we get here. Since
588 // here. Since GetTerminationStatus called waitpid with WNOHANG, 138 // GetTerminationStatus called waitpid with WNOHANG, it'll reap the process.
589 // it'll reap the process. However, if GetTerminationStatus didn't 139 // However, if GetTerminationStatus didn't reap the child (because it was
590 // reap the child (because it was still running), we'll need to 140 // still running), we'll need to Terminate via ProcessWatcher. So we can't
591 // Terminate via ProcessWatcher. So we can't close the handle here. 141 // close the handle here.
592 if (termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING) 142 if (termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
593 process_.Close(); 143 process_.Close();
594 144
595 return termination_status_; 145 return termination_status_;
596 } 146 }
597 147
148 bool ChildProcessLauncher::Terminate(int exit_code, bool wait) {
149 return IsStarting() ? false : TerminateProcess(GetProcess(), exit_code, wait);
150 }
151
598 ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest( 152 ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest(
599 Client* client) { 153 Client* client) {
600 Client* ret = client_; 154 Client* ret = client_;
601 client_ = client; 155 client_ = client;
602 return ret; 156 return ret;
603 } 157 }
604 158
159 // static
160 void ChildProcessLauncher::Terminate(
161 ZygoteHandle zygote, base::Process process) {
162 if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) {
163 ChildProcessLauncher::TerminateOnLauncherThread(zygote, std::move(process));
164 return;
165 }
166 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So don't
167 // this on the UI/IO threads.
168 BrowserThread::PostTask(
169 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
170 base::Bind(&ChildProcessLauncher::TerminateOnLauncherThread,
171 zygote, base::Passed(&process)));
172 }
173
174 ChildProcessLauncher::Helper::Helper(
175 int child_process_id,
176 BrowserThread::ID client_thread_id,
177 std::unique_ptr<base::CommandLine> command_line,
178 std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
179 const base::WeakPtr<ChildProcessLauncher>& child_process_launcher,
180 bool terminate_on_shutdown)
181 : child_process_id_(child_process_id),
182 client_thread_id_(client_thread_id),
183 command_line_(std::move(command_line)),
184 delegate_(std::move(delegate)),
185 child_process_launcher_(child_process_launcher),
186 terminate_on_shutdown_(terminate_on_shutdown) {
187 }
188
189 ChildProcessLauncher::Helper::~Helper() {
190 }
191
192 void ChildProcessLauncher::Helper::StartLaunchOnClientThread() {
193 DCHECK_CURRENTLY_ON(client_thread_id_);
194
195 BeforeLaunchOnClientThread();
196
197 mojo_server_handle_ = PrepareMojoPipeHandlesOnClientThread();
198 if (!mojo_server_handle_.is_valid()) {
199 mojo::edk::PlatformChannelPair channel_pair;
200 mojo_server_handle_ = channel_pair.PassServerHandle();
201 mojo_client_handle_ = channel_pair.PassClientHandle();
202 }
203
204 BrowserThread::PostTask(
205 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
206 base::Bind(&Helper::LaunchOnLauncherThread, this));
207 }
208
209 void ChildProcessLauncher::Helper::LaunchOnLauncherThread() {
210 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
211
212 begin_launch_time_ = base::TimeTicks::Now();
213
214 std::unique_ptr<FileMappedForLaunch> files_to_register = GetFilesToMap();
215
216 base::Process process;
217 ZygoteHandle zygote = nullptr;
218 bool is_synchronous_launch = true;
219 int launch_result = LAUNCH_RESULT_FAILURE;
220 if (ShouldForkAsZygote()) {
221 zygote = ForkAsZygote(std::move(files_to_register), &process);
222 launch_result = LAUNCH_RESULT_SUCCESS;
223 } else {
224 base::LaunchOptions options;
225 BeforeLaunchOnLauncherThread(*files_to_register, &options);
226
227 process = LaunchProcessOnLauncherThread(options,
228 files_to_register.get(),
229 &is_synchronous_launch,
230 &launch_result);
231
232 AfterLaunchOnLauncherThread(process, options);
233 }
234
235 if (is_synchronous_launch) {
236 PostLaunchOnLauncherThread(
237 std::move(process), zygote, launch_result, false);
238 }
239 }
240
241 void ChildProcessLauncher::Helper::PostLaunchOnLauncherThread(
242 base::Process process,
243 ZygoteHandle zygote,
244 int launch_result,
245 bool post_launch_on_client_thread_called) {
246 // Release the client handle now that the process has been started (the pipe
247 // may not signal when the process dies otherwise and we would not detect the
248 // child process died).
249 mojo_client_handle_.reset();
250
251 if (process.IsValid()) {
252 RecordHistogramsOnLauncherThread(
253 base::TimeTicks::Now() - begin_launch_time_);
254 }
255
256 if (!post_launch_on_client_thread_called) {
257 BrowserThread::PostTask(
258 client_thread_id_, FROM_HERE,
259 base::Bind(&Helper::PostLaunchOnClientThread,
260 this, base::Passed(&process), zygote, launch_result));
261 }
262 }
263
264 void ChildProcessLauncher::Helper::PostLaunchOnClientThread(
265 base::Process process, ZygoteHandle zygote, int error_code) {
266 if (child_process_launcher_) {
267 child_process_launcher_->Notify(zygote, std::move(mojo_server_handle_),
268 std::move(process), error_code);
269 } else if (process.IsValid() && terminate_on_shutdown_) {
270 ChildProcessLauncher::Terminate(zygote, std::move(process));
271 }
272 }
273
274 std::string ChildProcessLauncher::Helper::GetProcessType() {
275 return command_line()->GetSwitchValueASCII(switches::kProcessType);
276 }
277
605 } // namespace content 278 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698