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