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/child/child_thread.h" 6 7#include "base/allocator/allocator_extension.h" 8#include "base/base_switches.h" 9#include "base/command_line.h" 10#include "base/lazy_instance.h" 11#include "base/message_loop/message_loop.h" 12#include "base/process/kill.h" 13#include "base/process/process_handle.h" 14#include "base/strings/string_util.h" 15#include "base/threading/thread_local.h" 16#include "base/tracked_objects.h" 17#include "components/tracing/child_trace_message_filter.h" 18#include "content/child/child_histogram_message_filter.h" 19#include "content/child/child_process.h" 20#include "content/child/child_resource_message_filter.h" 21#include "content/child/fileapi/file_system_dispatcher.h" 22#include "content/child/power_monitor_broadcast_source.h" 23#include "content/child/quota_dispatcher.h" 24#include "content/child/quota_message_filter.h" 25#include "content/child/resource_dispatcher.h" 26#include "content/child/socket_stream_dispatcher.h" 27#include "content/child/thread_safe_sender.h" 28#include "content/common/child_process_messages.h" 29#include "content/public/common/content_switches.h" 30#include "ipc/ipc_logging.h" 31#include "ipc/ipc_switches.h" 32#include "ipc/ipc_sync_channel.h" 33#include "ipc/ipc_sync_message_filter.h" 34#include "webkit/glue/webkit_glue.h" 35 36#if defined(OS_WIN) 37#include "content/common/handle_enumerator_win.h" 38#endif 39 40#if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED) 41#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" 42#endif 43 44using tracked_objects::ThreadData; 45 46namespace content { 47namespace { 48 49// How long to wait for a connection to the browser process before giving up. 50const int kConnectionTimeoutS = 15; 51 52base::LazyInstance<base::ThreadLocalPointer<ChildThread> > g_lazy_tls = 53 LAZY_INSTANCE_INITIALIZER; 54 55// This isn't needed on Windows because there the sandbox's job object 56// terminates child processes automatically. For unsandboxed processes (i.e. 57// plugins), PluginThread has EnsureTerminateMessageFilter. 58#if defined(OS_POSIX) 59 60class SuicideOnChannelErrorFilter : public IPC::ChannelProxy::MessageFilter { 61 public: 62 // IPC::ChannelProxy::MessageFilter 63 virtual void OnChannelError() OVERRIDE { 64 // For renderer/worker processes: 65 // On POSIX, at least, one can install an unload handler which loops 66 // forever and leave behind a renderer process which eats 100% CPU forever. 67 // 68 // This is because the terminate signals (ViewMsg_ShouldClose and the error 69 // from the IPC channel) are routed to the main message loop but never 70 // processed (because that message loop is stuck in V8). 71 // 72 // One could make the browser SIGKILL the renderers, but that leaves open a 73 // large window where a browser failure (or a user, manually terminating 74 // the browser because "it's stuck") will leave behind a process eating all 75 // the CPU. 76 // 77 // So, we install a filter on the channel so that we can process this event 78 // here and kill the process. 79 if (CommandLine::ForCurrentProcess()-> 80 HasSwitch(switches::kChildCleanExit)) { 81 // If clean exit is requested, we want to kill this process after giving 82 // it 60 seconds to run exit handlers. Exit handlers may including ones 83 // that write profile data to disk (which happens under profile collection 84 // mode). 85 alarm(60); 86 } else { 87 _exit(0); 88 } 89 } 90 91 protected: 92 virtual ~SuicideOnChannelErrorFilter() {} 93}; 94 95#endif // OS(POSIX) 96 97#if defined(OS_ANDROID) 98ChildThread* g_child_thread; 99 100void QuitMainThreadMessageLoop() { 101 base::MessageLoop::current()->Quit(); 102} 103 104#endif 105 106} // namespace 107 108ChildThread::ChildThread() 109 : channel_connected_factory_(this) { 110 channel_name_ = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 111 switches::kProcessChannelID); 112 Init(); 113} 114 115ChildThread::ChildThread(const std::string& channel_name) 116 : channel_name_(channel_name), 117 channel_connected_factory_(this) { 118 Init(); 119} 120 121void ChildThread::Init() { 122 g_lazy_tls.Pointer()->Set(this); 123 on_channel_error_called_ = false; 124 message_loop_ = base::MessageLoop::current(); 125 channel_.reset( 126 new IPC::SyncChannel(channel_name_, 127 IPC::Channel::MODE_CLIENT, 128 this, 129 ChildProcess::current()->io_message_loop_proxy(), 130 true, 131 ChildProcess::current()->GetShutDownEvent())); 132#ifdef IPC_MESSAGE_LOG_ENABLED 133 IPC::Logging::GetInstance()->SetIPCSender(this); 134#endif 135 136 sync_message_filter_ = 137 new IPC::SyncMessageFilter(ChildProcess::current()->GetShutDownEvent()); 138 thread_safe_sender_ = new ThreadSafeSender( 139 base::MessageLoopProxy::current().get(), sync_message_filter_.get()); 140 141 resource_dispatcher_.reset(new ResourceDispatcher(this)); 142 socket_stream_dispatcher_.reset(new SocketStreamDispatcher()); 143 file_system_dispatcher_.reset(new FileSystemDispatcher()); 144 145 histogram_message_filter_ = new ChildHistogramMessageFilter(); 146 resource_message_filter_ = 147 new ChildResourceMessageFilter(resource_dispatcher()); 148 149 quota_message_filter_ = 150 new QuotaMessageFilter(thread_safe_sender_.get()); 151 quota_dispatcher_.reset(new QuotaDispatcher(thread_safe_sender_.get(), 152 quota_message_filter_.get())); 153 154 channel_->AddFilter(histogram_message_filter_.get()); 155 channel_->AddFilter(sync_message_filter_.get()); 156 channel_->AddFilter(new tracing::ChildTraceMessageFilter( 157 ChildProcess::current()->io_message_loop_proxy())); 158 channel_->AddFilter(resource_message_filter_.get()); 159 channel_->AddFilter(quota_message_filter_.get()); 160 161 // In single process mode we may already have a power monitor 162 if (!base::PowerMonitor::Get()) { 163 scoped_ptr<PowerMonitorBroadcastSource> power_monitor_source( 164 new PowerMonitorBroadcastSource()); 165 channel_->AddFilter(power_monitor_source->GetMessageFilter()); 166 167 power_monitor_.reset(new base::PowerMonitor( 168 power_monitor_source.PassAs<base::PowerMonitorSource>())); 169 } 170 171#if defined(OS_POSIX) 172 // Check that --process-type is specified so we don't do this in unit tests 173 // and single-process mode. 174 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessType)) 175 channel_->AddFilter(new SuicideOnChannelErrorFilter()); 176#endif 177 178 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTraceToConsole)) { 179 std::string category_string = 180 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 181 switches::kTraceToConsole); 182 183 if (!category_string.size()) 184 category_string = "*"; 185 186 base::debug::TraceLog::GetInstance()->SetEnabled( 187 base::debug::CategoryFilter(category_string), 188 base::debug::TraceLog::ECHO_TO_CONSOLE); 189 } 190 191 base::MessageLoop::current()->PostDelayedTask( 192 FROM_HERE, 193 base::Bind(&ChildThread::EnsureConnected, 194 channel_connected_factory_.GetWeakPtr()), 195 base::TimeDelta::FromSeconds(kConnectionTimeoutS)); 196 197#if defined(OS_ANDROID) 198 g_child_thread = this; 199#endif 200 201#if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED) 202 trace_memory_controller_.reset(new base::debug::TraceMemoryController( 203 message_loop_->message_loop_proxy(), 204 ::HeapProfilerWithPseudoStackStart, 205 ::HeapProfilerStop, 206 ::GetHeapProfile)); 207#endif 208} 209 210ChildThread::~ChildThread() { 211#ifdef IPC_MESSAGE_LOG_ENABLED 212 IPC::Logging::GetInstance()->SetIPCSender(NULL); 213#endif 214 215 channel_->RemoveFilter(quota_message_filter_.get()); 216 channel_->RemoveFilter(histogram_message_filter_.get()); 217 channel_->RemoveFilter(sync_message_filter_.get()); 218 219 // The ChannelProxy object caches a pointer to the IPC thread, so need to 220 // reset it as it's not guaranteed to outlive this object. 221 // NOTE: this also has the side-effect of not closing the main IPC channel to 222 // the browser process. This is needed because this is the signal that the 223 // browser uses to know that this process has died, so we need it to be alive 224 // until this process is shut down, and the OS closes the handle 225 // automatically. We used to watch the object handle on Windows to do this, 226 // but it wasn't possible to do so on POSIX. 227 channel_->ClearIPCTaskRunner(); 228 g_lazy_tls.Pointer()->Set(NULL); 229} 230 231void ChildThread::Shutdown() { 232 // Delete objects that hold references to blink so derived classes can 233 // safely shutdown blink in their Shutdown implementation. 234 file_system_dispatcher_.reset(); 235} 236 237void ChildThread::OnChannelConnected(int32 peer_pid) { 238 channel_connected_factory_.InvalidateWeakPtrs(); 239} 240 241void ChildThread::OnChannelError() { 242 set_on_channel_error_called(true); 243 base::MessageLoop::current()->Quit(); 244} 245 246bool ChildThread::Send(IPC::Message* msg) { 247 DCHECK(base::MessageLoop::current() == message_loop()); 248 if (!channel_) { 249 delete msg; 250 return false; 251 } 252 253 return channel_->Send(msg); 254} 255 256void ChildThread::AddRoute(int32 routing_id, IPC::Listener* listener) { 257 DCHECK(base::MessageLoop::current() == message_loop()); 258 259 router_.AddRoute(routing_id, listener); 260} 261 262void ChildThread::RemoveRoute(int32 routing_id) { 263 DCHECK(base::MessageLoop::current() == message_loop()); 264 265 router_.RemoveRoute(routing_id); 266} 267 268webkit_glue::ResourceLoaderBridge* ChildThread::CreateBridge( 269 const webkit_glue::ResourceLoaderBridge::RequestInfo& request_info) { 270 return resource_dispatcher()->CreateBridge(request_info); 271} 272 273base::SharedMemory* ChildThread::AllocateSharedMemory(size_t buf_size) { 274 return AllocateSharedMemory(buf_size, this); 275} 276 277// static 278base::SharedMemory* ChildThread::AllocateSharedMemory( 279 size_t buf_size, 280 IPC::Sender* sender) { 281 scoped_ptr<base::SharedMemory> shared_buf; 282#if defined(OS_WIN) 283 shared_buf.reset(new base::SharedMemory); 284 if (!shared_buf->CreateAndMapAnonymous(buf_size)) { 285 NOTREACHED(); 286 return NULL; 287 } 288#else 289 // On POSIX, we need to ask the browser to create the shared memory for us, 290 // since this is blocked by the sandbox. 291 base::SharedMemoryHandle shared_mem_handle; 292 if (sender->Send(new ChildProcessHostMsg_SyncAllocateSharedMemory( 293 buf_size, &shared_mem_handle))) { 294 if (base::SharedMemory::IsHandleValid(shared_mem_handle)) { 295 shared_buf.reset(new base::SharedMemory(shared_mem_handle, false)); 296 if (!shared_buf->Map(buf_size)) { 297 NOTREACHED() << "Map failed"; 298 return NULL; 299 } 300 } else { 301 NOTREACHED() << "Browser failed to allocate shared memory"; 302 return NULL; 303 } 304 } else { 305 NOTREACHED() << "Browser allocation request message failed"; 306 return NULL; 307 } 308#endif 309 return shared_buf.release(); 310} 311 312bool ChildThread::OnMessageReceived(const IPC::Message& msg) { 313 // Resource responses are sent to the resource dispatcher. 314 if (resource_dispatcher_->OnMessageReceived(msg)) 315 return true; 316 if (socket_stream_dispatcher_->OnMessageReceived(msg)) 317 return true; 318 if (file_system_dispatcher_->OnMessageReceived(msg)) 319 return true; 320 321 bool handled = true; 322 IPC_BEGIN_MESSAGE_MAP(ChildThread, msg) 323 IPC_MESSAGE_HANDLER(ChildProcessMsg_Shutdown, OnShutdown) 324#if defined(IPC_MESSAGE_LOG_ENABLED) 325 IPC_MESSAGE_HANDLER(ChildProcessMsg_SetIPCLoggingEnabled, 326 OnSetIPCLoggingEnabled) 327#endif 328 IPC_MESSAGE_HANDLER(ChildProcessMsg_SetProfilerStatus, 329 OnSetProfilerStatus) 330 IPC_MESSAGE_HANDLER(ChildProcessMsg_GetChildProfilerData, 331 OnGetChildProfilerData) 332 IPC_MESSAGE_HANDLER(ChildProcessMsg_DumpHandles, OnDumpHandles) 333#if defined(USE_TCMALLOC) 334 IPC_MESSAGE_HANDLER(ChildProcessMsg_GetTcmallocStats, OnGetTcmallocStats) 335#endif 336 IPC_MESSAGE_UNHANDLED(handled = false) 337 IPC_END_MESSAGE_MAP() 338 339 if (handled) 340 return true; 341 342 if (msg.routing_id() == MSG_ROUTING_CONTROL) 343 return OnControlMessageReceived(msg); 344 345 return router_.OnMessageReceived(msg); 346} 347 348bool ChildThread::OnControlMessageReceived(const IPC::Message& msg) { 349 return false; 350} 351 352void ChildThread::OnShutdown() { 353 base::MessageLoop::current()->Quit(); 354} 355 356#if defined(IPC_MESSAGE_LOG_ENABLED) 357void ChildThread::OnSetIPCLoggingEnabled(bool enable) { 358 if (enable) 359 IPC::Logging::GetInstance()->Enable(); 360 else 361 IPC::Logging::GetInstance()->Disable(); 362} 363#endif // IPC_MESSAGE_LOG_ENABLED 364 365void ChildThread::OnSetProfilerStatus(ThreadData::Status status) { 366 ThreadData::InitializeAndSetTrackingStatus(status); 367} 368 369void ChildThread::OnGetChildProfilerData(int sequence_number) { 370 tracked_objects::ProcessDataSnapshot process_data; 371 ThreadData::Snapshot(false, &process_data); 372 373 Send(new ChildProcessHostMsg_ChildProfilerData(sequence_number, 374 process_data)); 375} 376 377void ChildThread::OnDumpHandles() { 378#if defined(OS_WIN) 379 scoped_refptr<HandleEnumerator> handle_enum( 380 new HandleEnumerator( 381 CommandLine::ForCurrentProcess()->HasSwitch( 382 switches::kAuditAllHandles))); 383 handle_enum->EnumerateHandles(); 384 Send(new ChildProcessHostMsg_DumpHandlesDone); 385 return; 386#endif 387 388 NOTIMPLEMENTED(); 389} 390 391#if defined(USE_TCMALLOC) 392void ChildThread::OnGetTcmallocStats() { 393 std::string result; 394 char buffer[1024 * 32]; 395 base::allocator::GetStats(buffer, sizeof(buffer)); 396 result.append(buffer); 397 Send(new ChildProcessHostMsg_TcmallocStats(result)); 398} 399#endif 400 401ChildThread* ChildThread::current() { 402 return g_lazy_tls.Pointer()->Get(); 403} 404 405#if defined(OS_ANDROID) 406void ChildThread::ShutdownThread() { 407 DCHECK_NE(base::MessageLoop::current(), g_child_thread->message_loop()); 408 g_child_thread->message_loop()->PostTask( 409 FROM_HERE, base::Bind(&QuitMainThreadMessageLoop)); 410} 411 412#endif 413 414void ChildThread::OnProcessFinalRelease() { 415 if (on_channel_error_called_) { 416 base::MessageLoop::current()->Quit(); 417 return; 418 } 419 420 // The child process shutdown sequence is a request response based mechanism, 421 // where we send out an initial feeler request to the child process host 422 // instance in the browser to verify if it's ok to shutdown the child process. 423 // The browser then sends back a response if it's ok to shutdown. This avoids 424 // race conditions if the process refcount is 0 but there's an IPC message 425 // inflight that would addref it. 426 Send(new ChildProcessHostMsg_ShutdownRequest); 427} 428 429void ChildThread::EnsureConnected() { 430 LOG(INFO) << "ChildThread::EnsureConnected()"; 431 base::KillProcess(base::GetCurrentProcessHandle(), 0, false); 432} 433 434} // namespace content 435