Home | History | Annotate | Download | only in browser
      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 
     36 namespace content {
     37 namespace {
     38 
     39 static base::LazyInstance<BrowserChildProcessHostImpl::BrowserChildProcessList>
     40     g_child_process_list = LAZY_INSTANCE_INITIALIZER;
     41 
     42 base::LazyInstance<ObserverList<BrowserChildProcessObserver> >
     43     g_observers = LAZY_INSTANCE_INITIALIZER;
     44 
     45 void NotifyProcessHostConnected(const ChildProcessData& data) {
     46   FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(),
     47                     BrowserChildProcessHostConnected(data));
     48 }
     49 
     50 void NotifyProcessHostDisconnected(const ChildProcessData& data) {
     51   FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(),
     52                     BrowserChildProcessHostDisconnected(data));
     53 }
     54 
     55 void NotifyProcessCrashed(const ChildProcessData& data) {
     56   FOR_EACH_OBSERVER(BrowserChildProcessObserver, g_observers.Get(),
     57                     BrowserChildProcessCrashed(data));
     58 }
     59 
     60 }  // namespace
     61 
     62 BrowserChildProcessHost* BrowserChildProcessHost::Create(
     63     int process_type,
     64     BrowserChildProcessHostDelegate* delegate) {
     65   return new BrowserChildProcessHostImpl(process_type, delegate);
     66 }
     67 
     68 #if defined(OS_MACOSX)
     69 base::ProcessMetrics::PortProvider* BrowserChildProcessHost::GetPortProvider() {
     70   return MachBroker::GetInstance();
     71 }
     72 #endif
     73 
     74 // static
     75 BrowserChildProcessHostImpl::BrowserChildProcessList*
     76     BrowserChildProcessHostImpl::GetIterator() {
     77   return g_child_process_list.Pointer();
     78 }
     79 
     80 // static
     81 void BrowserChildProcessHostImpl::AddObserver(
     82     BrowserChildProcessObserver* observer) {
     83   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     84   g_observers.Get().AddObserver(observer);
     85 }
     86 
     87 // static
     88 void 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 
     94 BrowserChildProcessHostImpl::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 
    111 BrowserChildProcessHostImpl::~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
    120 void 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 
    131 void 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 
    173 const ChildProcessData& BrowserChildProcessHostImpl::GetData() const {
    174   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    175   return data_;
    176 }
    177 
    178 ChildProcessHost* BrowserChildProcessHostImpl::GetHost() const {
    179   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    180   return child_process_host_.get();
    181 }
    182 
    183 base::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 
    192 void BrowserChildProcessHostImpl::SetName(const string16& name) {
    193   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    194   data_.name = name;
    195 }
    196 
    197 void BrowserChildProcessHostImpl::SetHandle(base::ProcessHandle handle) {
    198   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    199   data_.handle = handle;
    200 }
    201 
    202 void BrowserChildProcessHostImpl::ForceShutdown() {
    203   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    204   g_child_process_list.Get().remove(this);
    205   child_process_host_->ForceShutdown();
    206 }
    207 
    208 void BrowserChildProcessHostImpl::SetBackgrounded(bool backgrounded) {
    209   child_process_->SetProcessBackgrounded(backgrounded);
    210 }
    211 
    212 void BrowserChildProcessHostImpl::SetTerminateChildOnShutdown(
    213     bool terminate_on_shutdown) {
    214   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    215   child_process_->SetTerminateChildOnShutdown(terminate_on_shutdown);
    216 }
    217 
    218 void 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 
    225 base::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 
    234 bool BrowserChildProcessHostImpl::OnMessageReceived(
    235     const IPC::Message& message) {
    236   return delegate_->OnMessageReceived(message);
    237 }
    238 
    239 void 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 
    254 void BrowserChildProcessHostImpl::OnChannelError() {
    255   delegate_->OnChannelError();
    256 }
    257 
    258 bool BrowserChildProcessHostImpl::CanShutdown() {
    259   return delegate_->CanShutdown();
    260 }
    261 
    262 void 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 
    304 bool BrowserChildProcessHostImpl::Send(IPC::Message* message) {
    305   return child_process_host_->Send(message);
    306 }
    307 
    308 void 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 
    333 void 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 
    345 void BrowserChildProcessHostImpl::OnProcessExitedEarly(
    346     base::WaitableEvent* event) {
    347   DeleteProcessWaitableEvent(event);
    348   OnChildDisconnected();
    349 }
    350 
    351 #endif
    352 
    353 }  // namespace content
    354