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 44 using tracked_objects::ThreadData; 45 46 namespace content { 47 namespace { 48 49 // How long to wait for a connection to the browser process before giving up. 50 const int kConnectionTimeoutS = 15; 51 52 base::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 60 class 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) 98 ChildThread* g_child_thread; 99 100 void QuitMainThreadMessageLoop() { 101 base::MessageLoop::current()->Quit(); 102 } 103 104 #endif 105 106 } // namespace 107 108 ChildThread::ChildThread() 109 : channel_connected_factory_(this) { 110 channel_name_ = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 111 switches::kProcessChannelID); 112 Init(); 113 } 114 115 ChildThread::ChildThread(const std::string& channel_name) 116 : channel_name_(channel_name), 117 channel_connected_factory_(this) { 118 Init(); 119 } 120 121 void 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 210 ChildThread::~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 231 void 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 237 void ChildThread::OnChannelConnected(int32 peer_pid) { 238 channel_connected_factory_.InvalidateWeakPtrs(); 239 } 240 241 void ChildThread::OnChannelError() { 242 set_on_channel_error_called(true); 243 base::MessageLoop::current()->Quit(); 244 } 245 246 bool 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 256 void ChildThread::AddRoute(int32 routing_id, IPC::Listener* listener) { 257 DCHECK(base::MessageLoop::current() == message_loop()); 258 259 router_.AddRoute(routing_id, listener); 260 } 261 262 void ChildThread::RemoveRoute(int32 routing_id) { 263 DCHECK(base::MessageLoop::current() == message_loop()); 264 265 router_.RemoveRoute(routing_id); 266 } 267 268 webkit_glue::ResourceLoaderBridge* ChildThread::CreateBridge( 269 const webkit_glue::ResourceLoaderBridge::RequestInfo& request_info) { 270 return resource_dispatcher()->CreateBridge(request_info); 271 } 272 273 base::SharedMemory* ChildThread::AllocateSharedMemory(size_t buf_size) { 274 return AllocateSharedMemory(buf_size, this); 275 } 276 277 // static 278 base::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 312 bool 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 348 bool ChildThread::OnControlMessageReceived(const IPC::Message& msg) { 349 return false; 350 } 351 352 void ChildThread::OnShutdown() { 353 base::MessageLoop::current()->Quit(); 354 } 355 356 #if defined(IPC_MESSAGE_LOG_ENABLED) 357 void 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 365 void ChildThread::OnSetProfilerStatus(ThreadData::Status status) { 366 ThreadData::InitializeAndSetTrackingStatus(status); 367 } 368 369 void 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 377 void 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) 392 void 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 401 ChildThread* ChildThread::current() { 402 return g_lazy_tls.Pointer()->Get(); 403 } 404 405 #if defined(OS_ANDROID) 406 void 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 414 void 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 429 void ChildThread::EnsureConnected() { 430 LOG(INFO) << "ChildThread::EnsureConnected()"; 431 base::KillProcess(base::GetCurrentProcessHandle(), 0, false); 432 } 433 434 } // namespace content 435