OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/command_line.h" | |
9 #include "base/file_path.h" | |
10 #include "base/lazy_instance.h" | |
11 #include "base/logging.h" | |
12 #include "base/metrics/histogram.h" | |
13 #include "base/path_service.h" | |
14 #include "base/process_util.h" | |
15 #include "base/stl_util.h" | |
16 #include "base/string_util.h" | |
17 #include "content/browser/profiler_message_filter.h" | |
18 #include "content/browser/renderer_host/resource_message_filter.h" | |
19 #include "content/browser/trace_message_filter.h" | |
20 #include "content/common/child_process_host_impl.h" | |
21 #include "content/common/plugin_messages.h" | |
22 #include "content/public/browser/browser_thread.h" | |
23 #include "content/public/browser/browser_child_process_host_delegate.h" | |
24 #include "content/public/browser/child_process_data.h" | |
25 #include "content/public/browser/content_browser_client.h" | |
26 #include "content/public/browser/notification_service.h" | |
27 #include "content/public/browser/notification_types.h" | |
28 #include "content/public/common/content_switches.h" | |
29 #include "content/public/common/result_codes.h" | |
30 | |
31 #if defined(OS_WIN) | |
32 #include "base/synchronization/waitable_event.h" | |
33 #else | |
34 #include "base/bind.h" | |
35 #endif | |
36 | |
37 using content::BrowserChildProcessHostDelegate; | |
38 using content::BrowserThread; | |
39 using content::ChildProcessData; | |
40 using content::ChildProcessHost; | |
41 using content::ChildProcessHostImpl; | |
42 | |
43 namespace { | |
44 | |
45 static base::LazyInstance<BrowserChildProcessHost::BrowserChildProcessList> | |
46 g_child_process_list = LAZY_INSTANCE_INITIALIZER; | |
47 | |
48 // Helper functions since the child process related notifications happen on the | |
49 // UI thread. | |
50 void ChildNotificationHelper(int notification_type, | |
51 const ChildProcessData& data) { | |
52 content::NotificationService::current()-> | |
53 Notify(notification_type, content::NotificationService::AllSources(), | |
54 content::Details<const ChildProcessData>(&data)); | |
55 } | |
56 | |
57 } // namespace | |
58 | |
59 namespace content { | |
60 | |
61 BrowserChildProcessHost* BrowserChildProcessHost::Create( | |
62 ProcessType type, | |
63 BrowserChildProcessHostDelegate* delegate) { | |
64 return new ::BrowserChildProcessHost(type, delegate); | |
65 } | |
66 | |
67 } // namespace content | |
68 | |
69 BrowserChildProcessHost::BrowserChildProcessList* | |
70 BrowserChildProcessHost::GetIterator() { | |
71 return g_child_process_list.Pointer(); | |
72 } | |
73 | |
74 BrowserChildProcessHost::BrowserChildProcessHost( | |
75 content::ProcessType type, | |
76 BrowserChildProcessHostDelegate* delegate) | |
77 : data_(type), | |
78 delegate_(delegate), | |
79 #if !defined(OS_WIN) | |
80 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), | |
81 #endif | |
82 disconnect_was_alive_(false) { | |
83 data_.id = ChildProcessHostImpl::GenerateChildProcessUniqueId(); | |
84 | |
85 child_process_host_.reset(ChildProcessHost::Create(this)); | |
86 child_process_host_->AddFilter(new TraceMessageFilter); | |
87 child_process_host_->AddFilter(new ProfilerMessageFilter); | |
88 | |
89 g_child_process_list.Get().push_back(this); | |
90 } | |
91 | |
92 BrowserChildProcessHost::~BrowserChildProcessHost() { | |
93 g_child_process_list.Get().remove(this); | |
94 } | |
95 | |
96 // static | |
97 void BrowserChildProcessHost::TerminateAll() { | |
98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
99 // Make a copy since the BrowserChildProcessHost dtor mutates the original | |
100 // list. | |
101 BrowserChildProcessList copy = g_child_process_list.Get(); | |
102 STLDeleteElements(©); | |
103 } | |
104 | |
105 void BrowserChildProcessHost::Launch( | |
106 #if defined(OS_WIN) | |
107 const FilePath& exposed_dir, | |
108 #elif defined(OS_POSIX) | |
109 bool use_zygote, | |
110 const base::environment_vector& environ, | |
111 #endif | |
112 CommandLine* cmd_line) { | |
113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
114 | |
115 content::GetContentClient()->browser()->AppendExtraCommandLineSwitches( | |
116 cmd_line, data_.id); | |
117 | |
118 child_process_.reset(new ChildProcessLauncher( | |
119 #if defined(OS_WIN) | |
120 exposed_dir, | |
121 #elif defined(OS_POSIX) | |
122 use_zygote, | |
123 environ, | |
124 child_process_host_->TakeClientFileDescriptor(), | |
125 #endif | |
126 cmd_line, | |
127 this)); | |
128 } | |
129 | |
130 const ChildProcessData& BrowserChildProcessHost::GetData() const { | |
131 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
132 return data_; | |
133 } | |
134 | |
135 ChildProcessHost* BrowserChildProcessHost::GetHost() const { | |
136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
137 return child_process_host_.get(); | |
138 } | |
139 | |
140 base::ProcessHandle BrowserChildProcessHost::GetHandle() const { | |
141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
142 DCHECK(child_process_.get()) | |
143 << "Requesting a child process handle before launching."; | |
144 DCHECK(child_process_->GetHandle()) | |
145 << "Requesting a child process handle before launch has completed OK."; | |
146 return child_process_->GetHandle(); | |
147 } | |
148 | |
149 void BrowserChildProcessHost::SetName(const string16& name) { | |
150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
151 data_.name = name; | |
152 } | |
153 | |
154 void BrowserChildProcessHost::SetHandle(base::ProcessHandle handle) { | |
155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
156 data_.handle = handle; | |
157 } | |
158 | |
159 void BrowserChildProcessHost::ForceShutdown() { | |
160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
161 g_child_process_list.Get().remove(this); | |
162 child_process_host_->ForceShutdown(); | |
163 } | |
164 | |
165 void BrowserChildProcessHost::SetTerminateChildOnShutdown( | |
166 bool terminate_on_shutdown) { | |
167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
168 child_process_->SetTerminateChildOnShutdown(terminate_on_shutdown); | |
169 } | |
170 | |
171 void BrowserChildProcessHost::Notify(int type) { | |
172 BrowserThread::PostTask( | |
173 BrowserThread::UI, | |
174 FROM_HERE, | |
175 base::Bind(&ChildNotificationHelper, type, data_)); | |
176 } | |
177 | |
178 base::TerminationStatus BrowserChildProcessHost::GetTerminationStatus( | |
179 int* exit_code) { | |
180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
181 if (!child_process_.get()) // If the delegate doesn't use Launch() helper. | |
182 return base::GetTerminationStatus(data_.handle, exit_code); | |
183 return child_process_->GetChildTerminationStatus(exit_code); | |
184 } | |
185 | |
186 bool BrowserChildProcessHost::OnMessageReceived(const IPC::Message& message) { | |
187 return delegate_->OnMessageReceived(message); | |
188 } | |
189 | |
190 void BrowserChildProcessHost::OnChannelConnected(int32 peer_pid) { | |
191 Notify(content::NOTIFICATION_CHILD_PROCESS_HOST_CONNECTED); | |
192 delegate_->OnChannelConnected(peer_pid); | |
193 } | |
194 | |
195 void BrowserChildProcessHost::OnChannelError() { | |
196 delegate_->OnChannelError(); | |
197 } | |
198 | |
199 bool BrowserChildProcessHost::CanShutdown() { | |
200 return delegate_->CanShutdown(); | |
201 } | |
202 | |
203 // Normally a ChildProcessHostDelegate deletes itself from this callback, but at | |
204 // this layer and below we need to have the final child process exit code to | |
205 // properly bucket crashes vs kills. On Windows we can do this if we wait until | |
206 // the process handle is signaled; on the rest of the platforms, we schedule a | |
207 // delayed task to wait for an exit code. However, this means that this method | |
208 // may be called twice: once from the actual channel error and once from | |
209 // OnWaitableEventSignaled() or the delayed task. | |
210 void BrowserChildProcessHost::OnChildDisconnected() { | |
211 DCHECK(data_.handle != base::kNullProcessHandle); | |
212 int exit_code; | |
213 base::TerminationStatus status = GetTerminationStatus(&exit_code); | |
214 switch (status) { | |
215 case base::TERMINATION_STATUS_PROCESS_CRASHED: | |
216 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: { | |
217 delegate_->OnProcessCrashed(exit_code); | |
218 // Report that this child process crashed. | |
219 Notify(content::NOTIFICATION_CHILD_PROCESS_CRASHED); | |
220 UMA_HISTOGRAM_ENUMERATION("ChildProcess.Crashed", | |
221 data_.type, | |
222 content::PROCESS_TYPE_MAX); | |
223 if (disconnect_was_alive_) { | |
224 UMA_HISTOGRAM_ENUMERATION("ChildProcess.CrashedWasAlive", | |
225 data_.type, | |
226 content::PROCESS_TYPE_MAX); | |
227 } | |
228 break; | |
229 } | |
230 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: { | |
231 // Report that this child process was killed. | |
232 UMA_HISTOGRAM_ENUMERATION("ChildProcess.Killed", | |
233 data_.type, | |
234 content::PROCESS_TYPE_MAX); | |
235 if (disconnect_was_alive_) { | |
236 UMA_HISTOGRAM_ENUMERATION("ChildProcess.KilledWasAlive", | |
237 data_.type, | |
238 content::PROCESS_TYPE_MAX); | |
239 } | |
240 break; | |
241 } | |
242 case base::TERMINATION_STATUS_STILL_RUNNING: { | |
243 // Exit code not yet available. Ensure we don't wait forever for an exit | |
244 // code. | |
245 if (disconnect_was_alive_) { | |
246 UMA_HISTOGRAM_ENUMERATION("ChildProcess.DisconnectedAlive", | |
247 data_.type, | |
248 content::PROCESS_TYPE_MAX); | |
249 break; | |
250 } | |
251 disconnect_was_alive_ = true; | |
252 #if defined(OS_WIN) | |
253 child_watcher_.StartWatching( | |
254 new base::WaitableEvent(data_.handle), this); | |
255 #else | |
256 // On non-Windows platforms, give the child process some time to die after | |
257 // disconnecting the channel so that the exit code and termination status | |
258 // become available. This is best effort -- if the process doesn't die | |
259 // within the time limit, this object gets destroyed. | |
260 const int kExitCodeWaitMs = 250; | |
261 MessageLoop::current()->PostDelayedTask( | |
262 FROM_HERE, | |
263 base::Bind(&BrowserChildProcessHost::OnChildDisconnected, | |
264 task_factory_.GetWeakPtr()), | |
265 kExitCodeWaitMs); | |
266 #endif | |
267 return; | |
268 } | |
269 | |
270 default: | |
271 break; | |
272 } | |
273 UMA_HISTOGRAM_ENUMERATION("ChildProcess.Disconnected", | |
274 data_.type, | |
275 content::PROCESS_TYPE_MAX); | |
276 // Notify in the main loop of the disconnection. | |
277 Notify(content::NOTIFICATION_CHILD_PROCESS_HOST_DISCONNECTED); | |
278 delete delegate_; // Will delete us | |
279 } | |
280 | |
281 // The child process handle has been signaled so the exit code is finally | |
282 // available. Unfortunately STILL_ACTIVE (0x103) is a valid exit code in | |
283 // which case we should not call OnChildDisconnected() or else we will be | |
284 // waiting forever. | |
285 void BrowserChildProcessHost::OnWaitableEventSignaled( | |
286 base::WaitableEvent* waitable_event) { | |
287 #if defined (OS_WIN) | |
288 unsigned long exit_code = 0; | |
289 GetExitCodeProcess(waitable_event->Release(), &exit_code); | |
290 delete waitable_event; | |
291 if (exit_code == STILL_ACTIVE) { | |
292 delete delegate_; // Will delete us | |
293 } else { | |
294 BrowserChildProcessHost::OnChildDisconnected(); | |
295 } | |
296 #endif | |
297 } | |
298 | |
299 bool BrowserChildProcessHost::Send(IPC::Message* message) { | |
300 return child_process_host_->Send(message); | |
301 } | |
302 | |
303 void BrowserChildProcessHost::ShutdownStarted() { | |
304 // Must remove the process from the list now, in case it gets used for a | |
305 // new instance before our watcher tells us that the process terminated. | |
306 g_child_process_list.Get().remove(this); | |
307 } | |
308 | |
309 | |
310 void BrowserChildProcessHost::OnProcessLaunched() { | |
311 if (!child_process_->GetHandle()) { | |
312 delete delegate_; // Will delete us | |
313 return; | |
314 } | |
315 data_.handle = child_process_->GetHandle(); | |
316 delegate_->OnProcessLaunched(); | |
317 } | |
OLD | NEW |