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 AddFilter(new TraceMessageFilter); 104 AddFilter(new ProfilerMessageFilter(process_type)); 105 AddFilter(new HistogramMessageFilter); 106 107 g_child_process_list.Get().push_back(this); 108 GetContentClient()->browser()->BrowserChildProcessHostCreated(this); 109 110 power_monitor_message_broadcaster_.Init(); 111} 112 113BrowserChildProcessHostImpl::~BrowserChildProcessHostImpl() { 114 g_child_process_list.Get().remove(this); 115 116#if defined(OS_WIN) 117 DeleteProcessWaitableEvent(early_exit_watcher_.GetWatchedEvent()); 118#endif 119} 120 121// static 122void BrowserChildProcessHostImpl::TerminateAll() { 123 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 124 // Make a copy since the BrowserChildProcessHost dtor mutates the original 125 // list. 126 BrowserChildProcessList copy = g_child_process_list.Get(); 127 for (BrowserChildProcessList::iterator it = copy.begin(); 128 it != copy.end(); ++it) { 129 delete (*it)->delegate(); // ~*HostDelegate deletes *HostImpl. 130 } 131} 132 133void BrowserChildProcessHostImpl::Launch( 134 SandboxedProcessLauncherDelegate* delegate, 135 base::CommandLine* cmd_line) { 136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 137 138 GetContentClient()->browser()->AppendExtraCommandLineSwitches( 139 cmd_line, data_.id); 140 141 const base::CommandLine& browser_command_line = 142 *base::CommandLine::ForCurrentProcess(); 143 static const char* kForwardSwitches[] = { 144 switches::kDisableLogging, 145 switches::kEnableLogging, 146 switches::kIPCConnectionTimeout, 147 switches::kLoggingLevel, 148 switches::kTraceToConsole, 149 switches::kV, 150 switches::kVModule, 151 }; 152 cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches, 153 arraysize(kForwardSwitches)); 154 155 child_process_.reset(new ChildProcessLauncher( 156 delegate, 157 cmd_line, 158 data_.id, 159 this)); 160} 161 162const ChildProcessData& BrowserChildProcessHostImpl::GetData() const { 163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 164 return data_; 165} 166 167ChildProcessHost* BrowserChildProcessHostImpl::GetHost() const { 168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 169 return child_process_host_.get(); 170} 171 172base::ProcessHandle BrowserChildProcessHostImpl::GetHandle() const { 173 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 174 DCHECK(child_process_.get()) 175 << "Requesting a child process handle before launching."; 176 DCHECK(child_process_->GetHandle()) 177 << "Requesting a child process handle before launch has completed OK."; 178 return child_process_->GetHandle(); 179} 180 181void BrowserChildProcessHostImpl::SetName(const base::string16& name) { 182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 183 data_.name = name; 184} 185 186void BrowserChildProcessHostImpl::SetHandle(base::ProcessHandle handle) { 187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 188 data_.handle = handle; 189} 190 191void BrowserChildProcessHostImpl::ForceShutdown() { 192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 193 g_child_process_list.Get().remove(this); 194 child_process_host_->ForceShutdown(); 195} 196 197void BrowserChildProcessHostImpl::SetBackgrounded(bool backgrounded) { 198 child_process_->SetProcessBackgrounded(backgrounded); 199} 200 201void BrowserChildProcessHostImpl::SetTerminateChildOnShutdown( 202 bool terminate_on_shutdown) { 203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 204 child_process_->SetTerminateChildOnShutdown(terminate_on_shutdown); 205} 206 207void BrowserChildProcessHostImpl::AddFilter(BrowserMessageFilter* filter) { 208 child_process_host_->AddFilter(filter->GetFilter()); 209} 210 211void BrowserChildProcessHostImpl::NotifyProcessInstanceCreated( 212 const ChildProcessData& data) { 213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 214 FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(), 215 BrowserChildProcessInstanceCreated(data)); 216} 217 218void BrowserChildProcessHostImpl::HistogramBadMessageTerminated( 219 int process_type) { 220 UMA_HISTOGRAM_ENUMERATION("ChildProcess.BadMessgeTerminated", process_type, 221 PROCESS_TYPE_MAX); 222} 223 224base::TerminationStatus BrowserChildProcessHostImpl::GetTerminationStatus( 225 bool known_dead, int* exit_code) { 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 227 if (!child_process_) // If the delegate doesn't use Launch() helper. 228 return base::GetTerminationStatus(data_.handle, exit_code); 229 return child_process_->GetChildTerminationStatus(known_dead, 230 exit_code); 231} 232 233bool BrowserChildProcessHostImpl::OnMessageReceived( 234 const IPC::Message& message) { 235 return delegate_->OnMessageReceived(message); 236} 237 238void BrowserChildProcessHostImpl::OnChannelConnected(int32 peer_pid) { 239#if defined(OS_WIN) 240 // From this point onward, the exit of the child process is detected by an 241 // error on the IPC channel. 242 DeleteProcessWaitableEvent(early_exit_watcher_.GetWatchedEvent()); 243 early_exit_watcher_.StopWatching(); 244#endif 245 246 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 247 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 248 base::Bind(&NotifyProcessHostConnected, data_)); 249 250 delegate_->OnChannelConnected(peer_pid); 251} 252 253void BrowserChildProcessHostImpl::OnChannelError() { 254 delegate_->OnChannelError(); 255} 256 257void BrowserChildProcessHostImpl::OnBadMessageReceived( 258 const IPC::Message& message) { 259 HistogramBadMessageTerminated(data_.process_type); 260 if (CommandLine::ForCurrentProcess()->HasSwitch( 261 switches::kDisableKillAfterBadIPC)) { 262 return; 263 } 264 base::KillProcess(GetHandle(), RESULT_CODE_KILLED_BAD_MESSAGE, false); 265} 266 267bool BrowserChildProcessHostImpl::CanShutdown() { 268 return delegate_->CanShutdown(); 269} 270 271void BrowserChildProcessHostImpl::OnChildDisconnected() { 272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 273 if (child_process_.get() || data_.handle) { 274 DCHECK(data_.handle != base::kNullProcessHandle); 275 int exit_code; 276 base::TerminationStatus status = GetTerminationStatus( 277 true /* known_dead */, &exit_code); 278 switch (status) { 279 case base::TERMINATION_STATUS_PROCESS_CRASHED: 280 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: { 281 delegate_->OnProcessCrashed(exit_code); 282 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 283 base::Bind(&NotifyProcessCrashed, data_)); 284 UMA_HISTOGRAM_ENUMERATION("ChildProcess.Crashed2", 285 data_.process_type, 286 PROCESS_TYPE_MAX); 287 break; 288 } 289 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: { 290 delegate_->OnProcessCrashed(exit_code); 291 // Report that this child process was killed. 292 UMA_HISTOGRAM_ENUMERATION("ChildProcess.Killed2", 293 data_.process_type, 294 PROCESS_TYPE_MAX); 295 break; 296 } 297 case base::TERMINATION_STATUS_STILL_RUNNING: { 298 UMA_HISTOGRAM_ENUMERATION("ChildProcess.DisconnectedAlive2", 299 data_.process_type, 300 PROCESS_TYPE_MAX); 301 } 302 default: 303 break; 304 } 305 UMA_HISTOGRAM_ENUMERATION("ChildProcess.Disconnected2", 306 data_.process_type, 307 PROCESS_TYPE_MAX); 308 } 309 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 310 base::Bind(&NotifyProcessHostDisconnected, data_)); 311 delete delegate_; // Will delete us 312} 313 314bool BrowserChildProcessHostImpl::Send(IPC::Message* message) { 315 return child_process_host_->Send(message); 316} 317 318void BrowserChildProcessHostImpl::OnProcessLaunchFailed() { 319 delegate_->OnProcessLaunchFailed(); 320 delete delegate_; // Will delete us 321} 322 323void BrowserChildProcessHostImpl::OnProcessLaunched() { 324 base::ProcessHandle handle = child_process_->GetHandle(); 325 if (!handle) { 326 delete delegate_; // Will delete us 327 return; 328 } 329 330#if defined(OS_WIN) 331 // Start a WaitableEventWatcher that will invoke OnProcessExitedEarly if the 332 // child process exits. This watcher is stopped once the IPC channel is 333 // connected and the exit of the child process is detecter by an error on the 334 // IPC channel thereafter. 335 DCHECK(!early_exit_watcher_.GetWatchedEvent()); 336 early_exit_watcher_.StartWatching( 337 new base::WaitableEvent(handle), 338 base::Bind(&BrowserChildProcessHostImpl::OnProcessExitedEarly, 339 base::Unretained(this))); 340#endif 341 342 data_.handle = handle; 343 delegate_->OnProcessLaunched(); 344} 345 346#if defined(OS_WIN) 347 348void BrowserChildProcessHostImpl::DeleteProcessWaitableEvent( 349 base::WaitableEvent* event) { 350 if (!event) 351 return; 352 353 // The WaitableEvent does not own the process handle so ensure it does not 354 // close it. 355 event->Release(); 356 357 delete event; 358} 359 360void BrowserChildProcessHostImpl::OnProcessExitedEarly( 361 base::WaitableEvent* event) { 362 DeleteProcessWaitableEvent(event); 363 OnChildDisconnected(); 364} 365 366#endif 367 368} // namespace content 369