child_process_launcher.cc revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright (c) 2012 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.h" 6 7#include <utility> // For std::pair. 8 9#include "base/bind.h" 10#include "base/command_line.h" 11#include "base/file_util.h" 12#include "base/logging.h" 13#include "base/memory/scoped_ptr.h" 14#include "base/metrics/histogram.h" 15#include "base/process/process.h" 16#include "base/synchronization/lock.h" 17#include "base/threading/thread.h" 18#include "content/public/browser/browser_thread.h" 19#include "content/public/browser/content_browser_client.h" 20#include "content/public/common/content_descriptors.h" 21#include "content/public/common/content_switches.h" 22#include "content/public/common/result_codes.h" 23 24#if defined(OS_WIN) 25#include "base/files/file_path.h" 26#include "content/common/sandbox_win.h" 27#include "content/public/common/sandbox_init.h" 28#include "content/public/common/sandboxed_process_launcher_delegate.h" 29#elif defined(OS_MACOSX) 30#include "content/browser/mach_broker_mac.h" 31#elif defined(OS_ANDROID) 32#include "base/android/jni_android.h" 33#include "content/browser/android/child_process_launcher_android.h" 34#elif defined(OS_POSIX) 35#include "base/memory/shared_memory.h" 36#include "base/memory/singleton.h" 37#include "content/browser/renderer_host/render_sandbox_host_linux.h" 38#include "content/browser/zygote_host/zygote_host_impl_linux.h" 39#include "content/common/child_process_sandbox_support_impl_linux.h" 40#endif 41 42#if defined(OS_POSIX) 43#include "base/metrics/stats_table.h" 44#include "base/posix/global_descriptors.h" 45#endif 46 47namespace content { 48 49// Having the functionality of ChildProcessLauncher be in an internal 50// ref counted object allows us to automatically terminate the process when the 51// parent class destructs, while still holding on to state that we need. 52class ChildProcessLauncher::Context 53 : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> { 54 public: 55 Context() 56 : client_(NULL), 57 client_thread_id_(BrowserThread::UI), 58 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), 59 exit_code_(RESULT_CODE_NORMAL_EXIT), 60 starting_(true) 61#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 62 , zygote_(false) 63#endif 64 { 65#if defined(OS_POSIX) 66 terminate_child_on_shutdown_ = !CommandLine::ForCurrentProcess()-> 67 HasSwitch(switches::kChildCleanExit); 68#else 69 terminate_child_on_shutdown_ = true; 70#endif 71 } 72 73 void Launch( 74#if defined(OS_WIN) 75 SandboxedProcessLauncherDelegate* delegate, 76#elif defined(OS_ANDROID) 77 int ipcfd, 78#elif defined(OS_POSIX) 79 bool use_zygote, 80 const base::EnvironmentMap& environ, 81 int ipcfd, 82#endif 83 CommandLine* cmd_line, 84 int child_process_id, 85 Client* client) { 86 client_ = client; 87 88 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); 89 90#if defined(OS_ANDROID) 91 // We need to close the client end of the IPC channel to reliably detect 92 // child termination. We will close this fd after we create the child 93 // process which is asynchronous on Android. 94 ipcfd_ = ipcfd; 95#endif 96 BrowserThread::PostTask( 97 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 98 base::Bind( 99 &Context::LaunchInternal, 100 make_scoped_refptr(this), 101 client_thread_id_, 102 child_process_id, 103#if defined(OS_WIN) 104 delegate, 105#elif defined(OS_ANDROID) 106 ipcfd, 107#elif defined(OS_POSIX) 108 use_zygote, 109 environ, 110 ipcfd, 111#endif 112 cmd_line)); 113 } 114 115#if defined(OS_ANDROID) 116 static void OnChildProcessStarted( 117 // |this_object| is NOT thread safe. Only use it to post a task back. 118 scoped_refptr<Context> this_object, 119 BrowserThread::ID client_thread_id, 120 const base::TimeTicks begin_launch_time, 121 base::ProcessHandle handle) { 122 RecordHistograms(begin_launch_time); 123 if (BrowserThread::CurrentlyOn(client_thread_id)) { 124 // This is always invoked on the UI thread which is commonly the 125 // |client_thread_id| so we can shortcut one PostTask. 126 this_object->Notify(handle); 127 } else { 128 BrowserThread::PostTask( 129 client_thread_id, FROM_HERE, 130 base::Bind( 131 &ChildProcessLauncher::Context::Notify, 132 this_object, 133 handle)); 134 } 135 } 136#endif 137 138 void ResetClient() { 139 // No need for locking as this function gets called on the same thread that 140 // client_ would be used. 141 CHECK(BrowserThread::CurrentlyOn(client_thread_id_)); 142 client_ = NULL; 143 } 144 145 void set_terminate_child_on_shutdown(bool terminate_on_shutdown) { 146 terminate_child_on_shutdown_ = terminate_on_shutdown; 147 } 148 149 private: 150 friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>; 151 friend class ChildProcessLauncher; 152 153 ~Context() { 154 Terminate(); 155 } 156 157 static void RecordHistograms(const base::TimeTicks begin_launch_time) { 158 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; 159 if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) { 160 RecordLaunchHistograms(launch_time); 161 } else { 162 BrowserThread::PostTask( 163 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 164 base::Bind(&ChildProcessLauncher::Context::RecordLaunchHistograms, 165 launch_time)); 166 } 167 } 168 169 static void RecordLaunchHistograms(const base::TimeDelta launch_time) { 170 // Log the launch time, separating out the first one (which will likely be 171 // slower due to the rest of the browser initializing at the same time). 172 static bool done_first_launch = false; 173 if (done_first_launch) { 174 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time); 175 } else { 176 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time); 177 done_first_launch = true; 178 } 179 } 180 181 static void LaunchInternal( 182 // |this_object| is NOT thread safe. Only use it to post a task back. 183 scoped_refptr<Context> this_object, 184 BrowserThread::ID client_thread_id, 185 int child_process_id, 186#if defined(OS_WIN) 187 SandboxedProcessLauncherDelegate* delegate, 188#elif defined(OS_ANDROID) 189 int ipcfd, 190#elif defined(OS_POSIX) 191 bool use_zygote, 192 const base::EnvironmentMap& env, 193 int ipcfd, 194#endif 195 CommandLine* cmd_line) { 196 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line); 197 base::TimeTicks begin_launch_time = base::TimeTicks::Now(); 198 199#if defined(OS_WIN) 200 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate); 201 base::ProcessHandle handle = StartSandboxedProcess(delegate, cmd_line); 202#elif defined(OS_POSIX) 203 std::string process_type = 204 cmd_line->GetSwitchValueASCII(switches::kProcessType); 205 std::vector<FileDescriptorInfo> files_to_register; 206 files_to_register.push_back( 207 FileDescriptorInfo(kPrimaryIPCChannel, 208 base::FileDescriptor(ipcfd, false))); 209 base::StatsTable* stats_table = base::StatsTable::current(); 210 if (stats_table && 211 base::SharedMemory::IsHandleValid( 212 stats_table->GetSharedMemoryHandle())) { 213 files_to_register.push_back( 214 FileDescriptorInfo(kStatsTableSharedMemFd, 215 stats_table->GetSharedMemoryHandle())); 216 } 217#endif 218 219#if defined(OS_ANDROID) 220 // Android WebView runs in single process, ensure that we never get here 221 // when running in single process mode. 222 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess)); 223 224 GetContentClient()->browser()-> 225 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id, 226 &files_to_register); 227 228 StartChildProcess(cmd_line->argv(), files_to_register, 229 base::Bind(&ChildProcessLauncher::Context::OnChildProcessStarted, 230 this_object, client_thread_id, begin_launch_time)); 231 232#elif defined(OS_POSIX) 233 base::ProcessHandle handle = base::kNullProcessHandle; 234 // We need to close the client end of the IPC channel to reliably detect 235 // child termination. 236 file_util::ScopedFD ipcfd_closer(&ipcfd); 237 238#if !defined(OS_MACOSX) 239 GetContentClient()->browser()-> 240 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id, 241 &files_to_register); 242 if (use_zygote) { 243 handle = ZygoteHostImpl::GetInstance()->ForkRequest(cmd_line->argv(), 244 files_to_register, 245 process_type); 246 } else 247 // Fall through to the normal posix case below when we're not zygoting. 248#endif // !defined(OS_MACOSX) 249 { 250 // Convert FD mapping to FileHandleMappingVector 251 base::FileHandleMappingVector fds_to_map; 252 for (size_t i = 0; i < files_to_register.size(); ++i) { 253 fds_to_map.push_back(std::make_pair( 254 files_to_register[i].fd.fd, 255 files_to_register[i].id + 256 base::GlobalDescriptors::kBaseDescriptor)); 257 } 258 259#if !defined(OS_MACOSX) 260 if (process_type == switches::kRendererProcess) { 261 const int sandbox_fd = 262 RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); 263 fds_to_map.push_back(std::make_pair( 264 sandbox_fd, 265 GetSandboxFD())); 266 } 267#endif // defined(OS_MACOSX) 268 269 // Actually launch the app. 270 base::LaunchOptions options; 271 options.environ = env; 272 options.fds_to_remap = &fds_to_map; 273 274#if defined(OS_MACOSX) 275 // Hold the MachBroker lock for the duration of LaunchProcess. The child 276 // will send its task port to the parent almost immediately after startup. 277 // The Mach message will be delivered to the parent, but updating the 278 // record of the launch will wait until after the placeholder PID is 279 // inserted below. This ensures that while the child process may send its 280 // port to the parent prior to the parent leaving LaunchProcess, the 281 // order in which the record in MachBroker is updated is correct. 282 MachBroker* broker = MachBroker::GetInstance(); 283 broker->GetLock().Acquire(); 284 285 // Make sure the MachBroker is running, and inform it to expect a 286 // check-in from the new process. 287 broker->EnsureRunning(); 288#endif // defined(OS_MACOSX) 289 290 bool launched = base::LaunchProcess(*cmd_line, options, &handle); 291 292#if defined(OS_MACOSX) 293 if (launched) 294 broker->AddPlaceholderForPid(handle); 295 296 // After updating the broker, release the lock and let the child's 297 // messasge be processed on the broker's thread. 298 broker->GetLock().Release(); 299#endif // defined(OS_MACOSX) 300 301 if (!launched) 302 handle = base::kNullProcessHandle; 303 } 304#endif // else defined(OS_POSIX) 305#if !defined(OS_ANDROID) 306 if (handle) 307 RecordHistograms(begin_launch_time); 308 BrowserThread::PostTask( 309 client_thread_id, FROM_HERE, 310 base::Bind( 311 &Context::Notify, 312 this_object.get(), 313#if defined(OS_POSIX) && !defined(OS_MACOSX) 314 use_zygote, 315#endif 316 handle)); 317#endif // !defined(OS_ANDROID) 318 } 319 320 void Notify( 321#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 322 bool zygote, 323#endif 324 base::ProcessHandle handle) { 325#if defined(OS_ANDROID) 326 // Finally close the ipcfd 327 file_util::ScopedFD ipcfd_closer(&ipcfd_); 328#endif 329 starting_ = false; 330 process_.set_handle(handle); 331 if (!handle) 332 LOG(ERROR) << "Failed to launch child process"; 333 334#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 335 zygote_ = zygote; 336#endif 337 if (client_) { 338 client_->OnProcessLaunched(); 339 } else { 340 Terminate(); 341 } 342 } 343 344 void Terminate() { 345 if (!process_.handle()) 346 return; 347 348 if (!terminate_child_on_shutdown_) 349 return; 350 351 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So 352 // don't this on the UI/IO threads. 353 BrowserThread::PostTask( 354 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 355 base::Bind( 356 &Context::TerminateInternal, 357#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 358 zygote_, 359#endif 360 process_.handle())); 361 process_.set_handle(base::kNullProcessHandle); 362 } 363 364 static void SetProcessBackgrounded(base::ProcessHandle handle, 365 bool background) { 366 base::Process process(handle); 367 process.SetProcessBackgrounded(background); 368 } 369 370 static void TerminateInternal( 371#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 372 bool zygote, 373#endif 374 base::ProcessHandle handle) { 375#if defined(OS_ANDROID) 376 VLOG(0) << "ChromeProcess: Stopping process with handle " << handle; 377 StopChildProcess(handle); 378#else 379 base::Process process(handle); 380 // Client has gone away, so just kill the process. Using exit code 0 381 // means that UMA won't treat this as a crash. 382 process.Terminate(RESULT_CODE_NORMAL_EXIT); 383 // On POSIX, we must additionally reap the child. 384#if defined(OS_POSIX) 385#if !defined(OS_MACOSX) 386 if (zygote) { 387 // If the renderer was created via a zygote, we have to proxy the reaping 388 // through the zygote process. 389 ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(handle); 390 } else 391#endif // !OS_MACOSX 392 { 393 base::EnsureProcessTerminated(handle); 394 } 395#endif // OS_POSIX 396 process.Close(); 397#endif // defined(OS_ANDROID) 398 } 399 400 Client* client_; 401 BrowserThread::ID client_thread_id_; 402 base::Process process_; 403 base::TerminationStatus termination_status_; 404 int exit_code_; 405 bool starting_; 406 // Controls whether the child process should be terminated on browser 407 // shutdown. Default behavior is to terminate the child. 408 bool terminate_child_on_shutdown_; 409#if defined(OS_ANDROID) 410 // The fd to close after creating the process. 411 int ipcfd_; 412#elif defined(OS_POSIX) && !defined(OS_MACOSX) 413 bool zygote_; 414#endif 415}; 416 417 418ChildProcessLauncher::ChildProcessLauncher( 419#if defined(OS_WIN) 420 SandboxedProcessLauncherDelegate* delegate, 421#elif defined(OS_POSIX) 422 bool use_zygote, 423 const base::EnvironmentMap& environ, 424 int ipcfd, 425#endif 426 CommandLine* cmd_line, 427 int child_process_id, 428 Client* client) { 429 context_ = new Context(); 430 context_->Launch( 431#if defined(OS_WIN) 432 delegate, 433#elif defined(OS_ANDROID) 434 ipcfd, 435#elif defined(OS_POSIX) 436 use_zygote, 437 environ, 438 ipcfd, 439#endif 440 cmd_line, 441 child_process_id, 442 client); 443} 444 445ChildProcessLauncher::~ChildProcessLauncher() { 446 context_->ResetClient(); 447} 448 449bool ChildProcessLauncher::IsStarting() { 450 return context_->starting_; 451} 452 453base::ProcessHandle ChildProcessLauncher::GetHandle() { 454 DCHECK(!context_->starting_); 455 return context_->process_.handle(); 456} 457 458base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus( 459 bool known_dead, 460 int* exit_code) { 461 base::ProcessHandle handle = context_->process_.handle(); 462 if (handle == base::kNullProcessHandle) { 463 // Process is already gone, so return the cached termination status. 464 if (exit_code) 465 *exit_code = context_->exit_code_; 466 return context_->termination_status_; 467 } 468#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 469 if (context_->zygote_) { 470 context_->termination_status_ = ZygoteHostImpl::GetInstance()-> 471 GetTerminationStatus(handle, known_dead, &context_->exit_code_); 472 } else { 473#elif defined(OS_MACOSX) 474 if (known_dead) { 475 context_->termination_status_ = 476 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_); 477 } else { 478#elif defined(OS_ANDROID) 479 if (IsChildProcessOomProtected(handle)) { 480 context_->termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED; 481 } else { 482#else 483 { 484#endif 485 context_->termination_status_ = 486 base::GetTerminationStatus(handle, &context_->exit_code_); 487 } 488 489 if (exit_code) 490 *exit_code = context_->exit_code_; 491 492 // POSIX: If the process crashed, then the kernel closed the socket 493 // for it and so the child has already died by the time we get 494 // here. Since GetTerminationStatus called waitpid with WNOHANG, 495 // it'll reap the process. However, if GetTerminationStatus didn't 496 // reap the child (because it was still running), we'll need to 497 // Terminate via ProcessWatcher. So we can't close the handle here. 498 if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING) 499 context_->process_.Close(); 500 501 return context_->termination_status_; 502} 503 504void ChildProcessLauncher::SetProcessBackgrounded(bool background) { 505 BrowserThread::PostTask( 506 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, 507 base::Bind( 508 &ChildProcessLauncher::Context::SetProcessBackgrounded, 509 GetHandle(), background)); 510} 511 512void ChildProcessLauncher::SetTerminateChildOnShutdown( 513 bool terminate_on_shutdown) { 514 if (context_.get()) 515 context_->set_terminate_child_on_shutdown(terminate_on_shutdown); 516} 517 518} // namespace content 519