browser_child_process_host_impl.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
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/browser_child_process_host_impl.h" 6 7#include "base/base_switches.h" 8#include "base/bind.h" 9#include "base/command_line.h" 10#include "base/files/file_path.h" 11#include "base/lazy_instance.h" 12#include "base/logging.h" 13#include "base/metrics/histogram.h" 14#include "base/path_service.h" 15#include "base/stl_util.h" 16#include "base/strings/string_util.h" 17#include "base/synchronization/waitable_event.h" 18#include "content/browser/histogram_message_filter.h" 19#include "content/browser/loader/resource_message_filter.h" 20#include "content/browser/profiler_message_filter.h" 21#include "content/browser/tracing/trace_message_filter.h" 22#include "content/common/child_process_host_impl.h" 23#include "content/public/browser/browser_child_process_host_delegate.h" 24#include "content/public/browser/browser_child_process_observer.h" 25#include "content/public/browser/browser_thread.h" 26#include "content/public/browser/child_process_data.h" 27#include "content/public/browser/content_browser_client.h" 28#include "content/public/common/content_switches.h" 29#include "content/public/common/process_type.h" 30#include "content/public/common/result_codes.h" 31 32#if defined(OS_MACOSX) 33#include "content/browser/mach_broker_mac.h" 34#endif 35 36namespace content { 37namespace { 38 39static base::LazyInstance<BrowserChildProcessHostImpl::BrowserChildProcessList> 40 g_child_process_list = LAZY_INSTANCE_INITIALIZER; 41 42base::LazyInstance<ObserverList<BrowserChildProcessObserver> > 43 g_observers = LAZY_INSTANCE_INITIALIZER; 44 45void NotifyProcessHostConnected(const ChildProcessData& data) { 46 FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(), 47 BrowserChildProcessHostConnected(data)); 48} 49 50void NotifyProcessHostDisconnected(const ChildProcessData& data) { 51 FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(), 52 BrowserChildProcessHostDisconnected(data)); 53} 54 55void NotifyProcessCrashed(const ChildProcessData& data) { 56 FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(), 57 BrowserChildProcessCrashed(data)); 58} 59 60} // namespace 61 62BrowserChildProcessHost* BrowserChildProcessHost::Create( 63 int process_type, 64 BrowserChildProcessHostDelegate* delegate) { 65 return new BrowserChildProcessHostImpl(process_type, delegate); 66} 67 68#if defined(OS_MACOSX) 69base::ProcessMetrics::PortProvider* BrowserChildProcessHost::GetPortProvider() { 70 return MachBroker::GetInstance(); 71} 72#endif 73 74// static 75BrowserChildProcessHostImpl::BrowserChildProcessList* 76 BrowserChildProcessHostImpl::GetIterator() { 77 return g_child_process_list.Pointer(); 78} 79 80// static 81void BrowserChildProcessHostImpl::AddObserver( 82 BrowserChildProcessObserver* observer) { 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 84 g_observers.Get().AddObserver(observer); 85} 86 87// static 88void BrowserChildProcessHostImpl::RemoveObserver( 89 BrowserChildProcessObserver* observer) { 90 // TODO(phajdan.jr): Check thread after fixing http://crbug.com/167126. 91 g_observers.Get().RemoveObserver(observer); 92} 93 94BrowserChildProcessHostImpl::BrowserChildProcessHostImpl( 95 int process_type, 96 BrowserChildProcessHostDelegate* delegate) 97 : data_(process_type), 98 delegate_(delegate), 99 power_monitor_message_broadcaster_(this) { 100 data_.id = ChildProcessHostImpl::GenerateChildProcessUniqueId(); 101 102 child_process_host_.reset(ChildProcessHost::Create(this)); 103 child_process_host_->AddFilter(new TraceMessageFilter); 104 child_process_host_->AddFilter(new ProfilerMessageFilter(process_type)); 105 child_process_host_->AddFilter(new HistogramMessageFilter()); 106 107 g_child_process_list.Get().push_back(this); 108 GetContentClient()->browser()->BrowserChildProcessHostCreated(this); 109} 110 111BrowserChildProcessHostImpl::~BrowserChildProcessHostImpl() { 112 g_child_process_list.Get().remove(this); 113 114#if defined(OS_WIN) 115 DeleteProcessWaitableEvent(early_exit_watcher_.GetWatchedEvent()); 116#endif 117} 118 119// static 120void BrowserChildProcessHostImpl::TerminateAll() { 121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 122 // Make a copy since the BrowserChildProcessHost dtor mutates the original 123 // list. 124 BrowserChildProcessList copy = g_child_process_list.Get(); 125 for (BrowserChildProcessList::iterator it = copy.begin(); 126 it != copy.end(); ++it) { 127 delete (*it)->delegate(); // ~*HostDelegate deletes *HostImpl. 128 } 129} 130 131void BrowserChildProcessHostImpl::Launch( 132#if defined(OS_WIN) 133 SandboxedProcessLauncherDelegate* delegate, 134#elif defined(OS_POSIX) 135 bool use_zygote, 136 const base::EnvironmentVector& environ, 137#endif 138 CommandLine* cmd_line) { 139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 140 141 GetContentClient()->browser()->AppendExtraCommandLineSwitches( 142 cmd_line, data_.id); 143 144 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); 145 static const char* kForwardSwitches[] = { 146 switches::kDisableLogging, 147 switches::kEnableDCHECK, 148 switches::kEnableLogging, 149 switches::kLoggingLevel, 150 switches::kTraceToConsole, 151 switches::kV, 152 switches::kVModule, 153#if defined(OS_POSIX) 154 switches::kChildCleanExit, 155#endif 156 }; 157 cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches, 158 arraysize(kForwardSwitches)); 159 160 child_process_.reset(new ChildProcessLauncher( 161#if defined(OS_WIN) 162 delegate, 163#elif defined(OS_POSIX) 164 use_zygote, 165 environ, 166 child_process_host_->TakeClientFileDescriptor(), 167#endif 168 cmd_line, 169 data_.id, 170 this)); 171} 172 173const ChildProcessData& BrowserChildProcessHostImpl::GetData() const { 174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 175 return data_; 176} 177 178ChildProcessHost* BrowserChildProcessHostImpl::GetHost() const { 179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 180 return child_process_host_.get(); 181} 182 183base::ProcessHandle BrowserChildProcessHostImpl::GetHandle() const { 184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 185 DCHECK(child_process_.get()) 186 << "Requesting a child process handle before launching."; 187 DCHECK(child_process_->GetHandle()) 188 << "Requesting a child process handle before launch has completed OK."; 189 return child_process_->GetHandle(); 190} 191 192void BrowserChildProcessHostImpl::SetName(const string16& name) { 193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 194 data_.name = name; 195} 196 197void BrowserChildProcessHostImpl::SetHandle(base::ProcessHandle handle) { 198 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 199 data_.handle = handle; 200} 201 202void BrowserChildProcessHostImpl::ForceShutdown() { 203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 204 g_child_process_list.Get().remove(this); 205 child_process_host_->ForceShutdown(); 206} 207 208void BrowserChildProcessHostImpl::SetBackgrounded(bool backgrounded) { 209 child_process_->SetProcessBackgrounded(backgrounded); 210} 211 212void BrowserChildProcessHostImpl::SetTerminateChildOnShutdown( 213 bool terminate_on_shutdown) { 214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 215 child_process_->SetTerminateChildOnShutdown(terminate_on_shutdown); 216} 217 218void BrowserChildProcessHostImpl::NotifyProcessInstanceCreated( 219 const ChildProcessData& data) { 220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 221 FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(), 222 BrowserChildProcessInstanceCreated(data)); 223} 224 225base::TerminationStatus BrowserChildProcessHostImpl::GetTerminationStatus( 226 int* exit_code) { 227 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 228 if (!child_process_) // If the delegate doesn't use Launch() helper. 229 return base::GetTerminationStatus(data_.handle, exit_code); 230 return child_process_->GetChildTerminationStatus(false /* known_dead */, 231 exit_code); 232} 233 234bool BrowserChildProcessHostImpl::OnMessageReceived( 235 const IPC::Message& message) { 236 return delegate_->OnMessageReceived(message); 237} 238 239void BrowserChildProcessHostImpl::OnChannelConnected(int32 peer_pid) { 240#if defined(OS_WIN) 241 // From this point onward, the exit of the child process is detected by an 242 // error on the IPC channel. 243 DeleteProcessWaitableEvent(early_exit_watcher_.GetWatchedEvent()); 244 early_exit_watcher_.StopWatching(); 245#endif 246 247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 248 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 249 base::Bind(&NotifyProcessHostConnected, data_)); 250 251 delegate_->OnChannelConnected(peer_pid); 252} 253 254void BrowserChildProcessHostImpl::OnChannelError() { 255 delegate_->OnChannelError(); 256} 257 258bool BrowserChildProcessHostImpl::CanShutdown() { 259 return delegate_->CanShutdown(); 260} 261 262void BrowserChildProcessHostImpl::OnChildDisconnected() { 263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 264 if (child_process_.get() || data_.handle) { 265 DCHECK(data_.handle != base::kNullProcessHandle); 266 int exit_code; 267 base::TerminationStatus status = GetTerminationStatus(&exit_code); 268 switch (status) { 269 case base::TERMINATION_STATUS_PROCESS_CRASHED: 270 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: { 271 delegate_->OnProcessCrashed(exit_code); 272 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 273 base::Bind(&NotifyProcessCrashed, data_)); 274 UMA_HISTOGRAM_ENUMERATION("ChildProcess.Crashed2", 275 data_.process_type, 276 PROCESS_TYPE_MAX); 277 break; 278 } 279 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: { 280 delegate_->OnProcessCrashed(exit_code); 281 // Report that this child process was killed. 282 UMA_HISTOGRAM_ENUMERATION("ChildProcess.Killed2", 283 data_.process_type, 284 PROCESS_TYPE_MAX); 285 break; 286 } 287 case base::TERMINATION_STATUS_STILL_RUNNING: { 288 UMA_HISTOGRAM_ENUMERATION("ChildProcess.DisconnectedAlive2", 289 data_.process_type, 290 PROCESS_TYPE_MAX); 291 } 292 default: 293 break; 294 } 295 UMA_HISTOGRAM_ENUMERATION("ChildProcess.Disconnected2", 296 data_.process_type, 297 PROCESS_TYPE_MAX); 298 } 299 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 300 base::Bind(&NotifyProcessHostDisconnected, data_)); 301 delete delegate_; // Will delete us 302} 303 304bool BrowserChildProcessHostImpl::Send(IPC::Message* message) { 305 return child_process_host_->Send(message); 306} 307 308void BrowserChildProcessHostImpl::OnProcessLaunched() { 309 base::ProcessHandle handle = child_process_->GetHandle(); 310 if (!handle) { 311 delete delegate_; // Will delete us 312 return; 313 } 314 315#if defined(OS_WIN) 316 // Start a WaitableEventWatcher that will invoke OnProcessExitedEarly if the 317 // child process exits. This watcher is stopped once the IPC channel is 318 // connected and the exit of the child process is detecter by an error on the 319 // IPC channel thereafter. 320 DCHECK(!early_exit_watcher_.GetWatchedEvent()); 321 early_exit_watcher_.StartWatching( 322 new base::WaitableEvent(handle), 323 base::Bind(&BrowserChildProcessHostImpl::OnProcessExitedEarly, 324 base::Unretained(this))); 325#endif 326 327 data_.handle = handle; 328 delegate_->OnProcessLaunched(); 329} 330 331#if defined(OS_WIN) 332 333void BrowserChildProcessHostImpl::DeleteProcessWaitableEvent( 334 base::WaitableEvent* event) { 335 if (!event) 336 return; 337 338 // The WaitableEvent does not own the process handle so ensure it does not 339 // close it. 340 event->Release(); 341 342 delete event; 343} 344 345void BrowserChildProcessHostImpl::OnProcessExitedEarly( 346 base::WaitableEvent* event) { 347 DeleteProcessWaitableEvent(event); 348 OnChildDisconnected(); 349} 350 351#endif 352 353} // namespace content 354