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.
5 #include "content/common/child_thread.h"
7 #include "base/allocator/allocator_extension.h"
8 #include "base/command_line.h"
9 #include "base/message_loop.h"
10 #include "base/process.h"
11 #include "base/process_util.h"
12 #include "base/string_util.h"
13 #include "base/tracked_objects.h"
14 #include "content/common/child_histogram_message_filter.h"
15 #include "content/common/child_process.h"
16 #include "content/common/child_process_messages.h"
17 #include "content/common/fileapi/file_system_dispatcher.h"
18 #include "content/common/quota_dispatcher.h"
19 #include "content/common/resource_dispatcher.h"
20 #include "content/common/socket_stream_dispatcher.h"
21 #include "content/components/tracing/child_trace_message_filter.h"
22 #include "content/public/common/content_switches.h"
23 #include "ipc/ipc_logging.h"
24 #include "ipc/ipc_switches.h"
25 #include "ipc/ipc_sync_channel.h"
26 #include "ipc/ipc_sync_message_filter.h"
27 #include "webkit/glue/webkit_glue.h"
30 #include "content/common/handle_enumerator_win.h"
33 using tracked_objects::ThreadData
;
38 // How long to wait for a connection to the browser process before giving up.
39 const int kConnectionTimeoutS
= 15;
41 // This isn't needed on Windows because there the sandbox's job object
42 // terminates child processes automatically. For unsandboxed processes (i.e.
43 // plugins), PluginThread has EnsureTerminateMessageFilter.
46 class SuicideOnChannelErrorFilter
: public IPC::ChannelProxy::MessageFilter
{
48 // IPC::ChannelProxy::MessageFilter
49 virtual void OnChannelError() OVERRIDE
{
50 // For renderer/worker processes:
51 // On POSIX, at least, one can install an unload handler which loops
52 // forever and leave behind a renderer process which eats 100% CPU forever.
54 // This is because the terminate signals (ViewMsg_ShouldClose and the error
55 // from the IPC channel) are routed to the main message loop but never
56 // processed (because that message loop is stuck in V8).
58 // One could make the browser SIGKILL the renderers, but that leaves open a
59 // large window where a browser failure (or a user, manually terminating
60 // the browser because "it's stuck") will leave behind a process eating all
63 // So, we install a filter on the channel so that we can process this event
64 // here and kill the process.
66 // We want to kill this process after giving it 30 seconds to run the exit
67 // handlers. SIGALRM has a default disposition of terminating the
69 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kChildCleanExit
))
76 virtual ~SuicideOnChannelErrorFilter() {}
83 ChildThread::ChildThread()
84 : ALLOW_THIS_IN_INITIALIZER_LIST(channel_connected_factory_(this)) {
85 channel_name_
= CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
86 switches::kProcessChannelID
);
90 ChildThread::ChildThread(const std::string
& channel_name
)
91 : channel_name_(channel_name
),
92 ALLOW_THIS_IN_INITIALIZER_LIST(channel_connected_factory_(this)) {
96 void ChildThread::Init() {
97 on_channel_error_called_
= false;
98 message_loop_
= MessageLoop::current();
99 channel_
.reset(new IPC::SyncChannel(channel_name_
,
100 IPC::Channel::MODE_CLIENT
, this,
101 ChildProcess::current()->io_message_loop_proxy(), true,
102 ChildProcess::current()->GetShutDownEvent()));
103 #ifdef IPC_MESSAGE_LOG_ENABLED
104 IPC::Logging::GetInstance()->SetIPCSender(this);
107 resource_dispatcher_
.reset(new ResourceDispatcher(this));
108 socket_stream_dispatcher_
.reset(new SocketStreamDispatcher());
109 file_system_dispatcher_
.reset(new FileSystemDispatcher());
110 quota_dispatcher_
.reset(new QuotaDispatcher());
112 sync_message_filter_
=
113 new IPC::SyncMessageFilter(ChildProcess::current()->GetShutDownEvent());
114 histogram_message_filter_
= new ChildHistogramMessageFilter();
116 channel_
->AddFilter(histogram_message_filter_
.get());
117 channel_
->AddFilter(sync_message_filter_
.get());
118 channel_
->AddFilter(new ChildTraceMessageFilter(
119 ChildProcess::current()->io_message_loop_proxy()));
121 #if defined(OS_POSIX)
122 // Check that --process-type is specified so we don't do this in unit tests
123 // and single-process mode.
124 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessType
))
125 channel_
->AddFilter(new SuicideOnChannelErrorFilter());
128 MessageLoop::current()->PostDelayedTask(
130 base::Bind(&ChildThread::EnsureConnected
,
131 channel_connected_factory_
.GetWeakPtr()),
132 base::TimeDelta::FromSeconds(kConnectionTimeoutS
));
135 ChildThread::~ChildThread() {
136 #ifdef IPC_MESSAGE_LOG_ENABLED
137 IPC::Logging::GetInstance()->SetIPCSender(NULL
);
140 channel_
->RemoveFilter(histogram_message_filter_
.get());
141 channel_
->RemoveFilter(sync_message_filter_
.get());
143 // The ChannelProxy object caches a pointer to the IPC thread, so need to
144 // reset it as it's not guaranteed to outlive this object.
145 // NOTE: this also has the side-effect of not closing the main IPC channel to
146 // the browser process. This is needed because this is the signal that the
147 // browser uses to know that this process has died, so we need it to be alive
148 // until this process is shut down, and the OS closes the handle
149 // automatically. We used to watch the object handle on Windows to do this,
150 // but it wasn't possible to do so on POSIX.
151 channel_
->ClearIPCTaskRunner();
154 void ChildThread::OnChannelConnected(int32 peer_pid
) {
155 channel_connected_factory_
.InvalidateWeakPtrs();
158 void ChildThread::OnChannelError() {
159 set_on_channel_error_called(true);
160 MessageLoop::current()->Quit();
163 bool ChildThread::Send(IPC::Message
* msg
) {
164 DCHECK(MessageLoop::current() == message_loop());
165 if (!channel_
.get()) {
170 return channel_
->Send(msg
);
173 void ChildThread::AddRoute(int32 routing_id
, IPC::Listener
* listener
) {
174 DCHECK(MessageLoop::current() == message_loop());
176 router_
.AddRoute(routing_id
, listener
);
179 void ChildThread::RemoveRoute(int32 routing_id
) {
180 DCHECK(MessageLoop::current() == message_loop());
182 router_
.RemoveRoute(routing_id
);
185 IPC::Listener
* ChildThread::ResolveRoute(int32 routing_id
) {
186 DCHECK(MessageLoop::current() == message_loop());
188 return router_
.ResolveRoute(routing_id
);
191 webkit_glue::ResourceLoaderBridge
* ChildThread::CreateBridge(
192 const webkit_glue::ResourceLoaderBridge::RequestInfo
& request_info
) {
193 return resource_dispatcher()->CreateBridge(request_info
);
196 base::SharedMemory
* ChildThread::AllocateSharedMemory(
198 scoped_ptr
<base::SharedMemory
> shared_buf
;
200 shared_buf
.reset(new base::SharedMemory
);
201 if (!shared_buf
->CreateAndMapAnonymous(buf_size
)) {
206 // On POSIX, we need to ask the browser to create the shared memory for us,
207 // since this is blocked by the sandbox.
208 base::SharedMemoryHandle shared_mem_handle
;
209 if (Send(new ChildProcessHostMsg_SyncAllocateSharedMemory(
210 buf_size
, &shared_mem_handle
))) {
211 if (base::SharedMemory::IsHandleValid(shared_mem_handle
)) {
212 shared_buf
.reset(new base::SharedMemory(shared_mem_handle
, false));
213 if (!shared_buf
->Map(buf_size
)) {
214 NOTREACHED() << "Map failed";
218 NOTREACHED() << "Browser failed to allocate shared memory";
222 NOTREACHED() << "Browser allocation request message failed";
226 return shared_buf
.release();
229 ResourceDispatcher
* ChildThread::resource_dispatcher() {
230 return resource_dispatcher_
.get();
233 IPC::SyncMessageFilter
* ChildThread::sync_message_filter() {
234 return sync_message_filter_
;
237 MessageLoop
* ChildThread::message_loop() {
238 return message_loop_
;
241 bool ChildThread::OnMessageReceived(const IPC::Message
& msg
) {
242 // Resource responses are sent to the resource dispatcher.
243 if (resource_dispatcher_
->OnMessageReceived(msg
))
245 if (socket_stream_dispatcher_
->OnMessageReceived(msg
))
247 if (file_system_dispatcher_
->OnMessageReceived(msg
))
249 if (quota_dispatcher_
->OnMessageReceived(msg
))
253 IPC_BEGIN_MESSAGE_MAP(ChildThread
, msg
)
254 IPC_MESSAGE_HANDLER(ChildProcessMsg_Shutdown
, OnShutdown
)
255 #if defined(IPC_MESSAGE_LOG_ENABLED)
256 IPC_MESSAGE_HANDLER(ChildProcessMsg_SetIPCLoggingEnabled
,
257 OnSetIPCLoggingEnabled
)
259 IPC_MESSAGE_HANDLER(ChildProcessMsg_SetProfilerStatus
,
261 IPC_MESSAGE_HANDLER(ChildProcessMsg_GetChildProfilerData
,
262 OnGetChildProfilerData
)
263 IPC_MESSAGE_HANDLER(ChildProcessMsg_DumpHandles
, OnDumpHandles
)
264 #if defined(USE_TCMALLOC)
265 IPC_MESSAGE_HANDLER(ChildProcessMsg_GetTcmallocStats
, OnGetTcmallocStats
)
267 IPC_MESSAGE_UNHANDLED(handled
= false)
268 IPC_END_MESSAGE_MAP()
273 if (msg
.routing_id() == MSG_ROUTING_CONTROL
)
274 return OnControlMessageReceived(msg
);
276 return router_
.OnMessageReceived(msg
);
279 bool ChildThread::OnControlMessageReceived(const IPC::Message
& msg
) {
283 void ChildThread::OnShutdown() {
284 MessageLoop::current()->Quit();
287 #if defined(IPC_MESSAGE_LOG_ENABLED)
288 void ChildThread::OnSetIPCLoggingEnabled(bool enable
) {
290 IPC::Logging::GetInstance()->Enable();
292 IPC::Logging::GetInstance()->Disable();
294 #endif // IPC_MESSAGE_LOG_ENABLED
296 void ChildThread::OnSetProfilerStatus(ThreadData::Status status
) {
297 ThreadData::InitializeAndSetTrackingStatus(status
);
300 void ChildThread::OnGetChildProfilerData(int sequence_number
) {
301 tracked_objects::ProcessDataSnapshot process_data
;
302 ThreadData::Snapshot(false, &process_data
);
304 Send(new ChildProcessHostMsg_ChildProfilerData(sequence_number
,
308 void ChildThread::OnDumpHandles() {
310 scoped_refptr
<HandleEnumerator
> handle_enum(
311 new HandleEnumerator(
312 CommandLine::ForCurrentProcess()->HasSwitch(
313 switches::kAuditAllHandles
)));
314 handle_enum
->EnumerateHandles();
315 Send(new ChildProcessHostMsg_DumpHandlesDone
);
322 #if defined(USE_TCMALLOC)
323 void ChildThread::OnGetTcmallocStats() {
325 char buffer
[1024 * 32];
326 base::allocator::GetStats(buffer
, sizeof(buffer
));
327 result
.append(buffer
);
328 Send(new ChildProcessHostMsg_TcmallocStats(result
));
332 ChildThread
* ChildThread::current() {
333 return ChildProcess::current()->main_thread();
336 bool ChildThread::IsWebFrameValid(WebKit::WebFrame
* frame
) {
337 // Return false so that it is overridden in any process in which it is used.
341 void ChildThread::OnProcessFinalRelease() {
342 if (on_channel_error_called_
) {
343 MessageLoop::current()->Quit();
347 // The child process shutdown sequence is a request response based mechanism,
348 // where we send out an initial feeler request to the child process host
349 // instance in the browser to verify if it's ok to shutdown the child process.
350 // The browser then sends back a response if it's ok to shutdown. This avoids
351 // race conditions if the process refcount is 0 but there's an IPC message
352 // inflight that would addref it.
353 Send(new ChildProcessHostMsg_ShutdownRequest
);
356 void ChildThread::EnsureConnected() {
357 LOG(INFO
) << "ChildThread::EnsureConnected()";
358 base::KillProcess(base::GetCurrentProcessHandle(), 0, false);
361 } // namespace content