| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 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 "chrome/gpu/gpu_thread.h" | |
| 6 | |
| 7 #include <string> | |
| 8 #include <vector> | |
| 9 | |
| 10 #include "app/gfx/gl/gl_context.h" | |
| 11 #include "app/gfx/gl/gl_implementation.h" | |
| 12 #include "app/win/scoped_com_initializer.h" | |
| 13 #include "base/command_line.h" | |
| 14 #include "base/threading/worker_pool.h" | |
| 15 #include "build/build_config.h" | |
| 16 #include "chrome/gpu/gpu_info_collector.h" | |
| 17 #include "chrome/gpu/gpu_watchdog_thread.h" | |
| 18 #include "content/common/child_process.h" | |
| 19 #include "content/common/content_client.h" | |
| 20 #include "content/common/content_switches.h" | |
| 21 #include "content/common/gpu_messages.h" | |
| 22 #include "ipc/ipc_channel_handle.h" | |
| 23 | |
| 24 #if defined(OS_MACOSX) | |
| 25 #include "content/common/sandbox_init_wrapper.h" | |
| 26 #include "content/common/sandbox_mac.h" | |
| 27 #elif defined(OS_WIN) | |
| 28 #include "sandbox/src/sandbox.h" | |
| 29 #endif | |
| 30 | |
| 31 const int kGpuTimeout = 10000; | |
| 32 | |
| 33 namespace { | |
| 34 | |
| 35 bool InitializeGpuSandbox() { | |
| 36 #if defined(OS_MACOSX) | |
| 37 CommandLine* parsed_command_line = CommandLine::ForCurrentProcess(); | |
| 38 SandboxInitWrapper sandbox_wrapper; | |
| 39 return sandbox_wrapper.InitializeSandbox(*parsed_command_line, | |
| 40 switches::kGpuProcess); | |
| 41 #else | |
| 42 // TODO(port): Create GPU sandbox for linux. | |
| 43 return true; | |
| 44 #endif | |
| 45 } | |
| 46 | |
| 47 } // namespace | |
| 48 | |
| 49 #if defined(OS_WIN) | |
| 50 GpuThread::GpuThread(sandbox::TargetServices* target_services) | |
| 51 : target_services_(target_services) { | |
| 52 } | |
| 53 #else | |
| 54 GpuThread::GpuThread() { | |
| 55 } | |
| 56 #endif | |
| 57 | |
| 58 GpuThread::GpuThread(const std::string& channel_id) | |
| 59 : ChildThread(channel_id) { | |
| 60 #if defined(OS_WIN) | |
| 61 target_services_ = NULL; | |
| 62 #endif | |
| 63 } | |
| 64 | |
| 65 | |
| 66 GpuThread::~GpuThread() { | |
| 67 logging::SetLogMessageHandler(NULL); | |
| 68 } | |
| 69 | |
| 70 void GpuThread::Init(const base::Time& process_start_time) { | |
| 71 process_start_time_ = process_start_time; | |
| 72 } | |
| 73 | |
| 74 void GpuThread::RemoveChannel(int renderer_id) { | |
| 75 gpu_channels_.erase(renderer_id); | |
| 76 } | |
| 77 | |
| 78 bool GpuThread::OnControlMessageReceived(const IPC::Message& msg) { | |
| 79 bool msg_is_ok = true; | |
| 80 bool handled = true; | |
| 81 IPC_BEGIN_MESSAGE_MAP_EX(GpuThread, msg, msg_is_ok) | |
| 82 IPC_MESSAGE_HANDLER(GpuMsg_Initialize, OnInitialize) | |
| 83 IPC_MESSAGE_HANDLER(GpuMsg_EstablishChannel, OnEstablishChannel) | |
| 84 IPC_MESSAGE_HANDLER(GpuMsg_CloseChannel, OnCloseChannel) | |
| 85 IPC_MESSAGE_HANDLER(GpuMsg_CreateViewCommandBuffer, | |
| 86 OnCreateViewCommandBuffer); | |
| 87 IPC_MESSAGE_HANDLER(GpuMsg_Synchronize, OnSynchronize) | |
| 88 IPC_MESSAGE_HANDLER(GpuMsg_CollectGraphicsInfo, OnCollectGraphicsInfo) | |
| 89 #if defined(OS_MACOSX) | |
| 90 IPC_MESSAGE_HANDLER(GpuMsg_AcceleratedSurfaceBuffersSwappedACK, | |
| 91 OnAcceleratedSurfaceBuffersSwappedACK) | |
| 92 IPC_MESSAGE_HANDLER(GpuMsg_DidDestroyAcceleratedSurface, | |
| 93 OnDidDestroyAcceleratedSurface) | |
| 94 #endif | |
| 95 IPC_MESSAGE_HANDLER(GpuMsg_Crash, OnCrash) | |
| 96 IPC_MESSAGE_HANDLER(GpuMsg_Hang, OnHang) | |
| 97 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 98 IPC_END_MESSAGE_MAP_EX() | |
| 99 return handled; | |
| 100 } | |
| 101 | |
| 102 namespace { | |
| 103 | |
| 104 bool GpuProcessLogMessageHandler(int severity, | |
| 105 const char* file, int line, | |
| 106 size_t message_start, | |
| 107 const std::string& str) { | |
| 108 std::string header = str.substr(0, message_start); | |
| 109 std::string message = str.substr(message_start); | |
| 110 ChildThread::current()->Send( | |
| 111 new GpuHostMsg_OnLogMessage(severity, header, message)); | |
| 112 return false; | |
| 113 } | |
| 114 | |
| 115 } // namespace | |
| 116 | |
| 117 void GpuThread::OnInitialize() { | |
| 118 // Redirect LOG messages to the GpuProcessHost | |
| 119 bool single_process = CommandLine::ForCurrentProcess()->HasSwitch( | |
| 120 switches::kSingleProcess); | |
| 121 if (!single_process) | |
| 122 logging::SetLogMessageHandler(GpuProcessLogMessageHandler); | |
| 123 | |
| 124 // Load the GL implementation and locate the bindings before starting the GPU | |
| 125 // watchdog because this can take a lot of time and the GPU watchdog might | |
| 126 // terminate the GPU process. | |
| 127 if (!gfx::GLContext::InitializeOneOff()) { | |
| 128 LOG(INFO) << "GLContext::InitializeOneOff failed"; | |
| 129 MessageLoop::current()->Quit(); | |
| 130 return; | |
| 131 } | |
| 132 bool gpu_info_result = gpu_info_collector::CollectGraphicsInfo(&gpu_info_); | |
| 133 if (!gpu_info_result) { | |
| 134 gpu_info_.collection_error = true; | |
| 135 LOG(ERROR) << "gpu_info_collector::CollectGraphicsInfo() failed"; | |
| 136 } | |
| 137 | |
| 138 content::GetContentClient()->SetGpuInfo(gpu_info_); | |
| 139 LOG(INFO) << "gpu_info_collector::CollectGraphicsInfo complete"; | |
| 140 | |
| 141 // Record initialization only after collecting the GPU info because that can | |
| 142 // take a significant amount of time. | |
| 143 gpu_info_.initialization_time = base::Time::Now() - process_start_time_; | |
| 144 | |
| 145 #if defined (OS_MACOSX) | |
| 146 // Note that kNoSandbox will also disable the GPU sandbox. | |
| 147 bool no_gpu_sandbox = CommandLine::ForCurrentProcess()->HasSwitch( | |
| 148 switches::kNoGpuSandbox); | |
| 149 if (!no_gpu_sandbox) { | |
| 150 if (!InitializeGpuSandbox()) { | |
| 151 LOG(ERROR) << "Failed to initialize the GPU sandbox"; | |
| 152 MessageLoop::current()->Quit(); | |
| 153 return; | |
| 154 } | |
| 155 } else { | |
| 156 LOG(ERROR) << "Running without GPU sandbox"; | |
| 157 } | |
| 158 #elif defined(OS_WIN) | |
| 159 // For windows, if the target_services interface is not zero, the process | |
| 160 // is sandboxed and we must call LowerToken() before rendering untrusted | |
| 161 // content. | |
| 162 if (target_services_) | |
| 163 target_services_->LowerToken(); | |
| 164 #endif | |
| 165 | |
| 166 // In addition to disabling the watchdog if the command line switch is | |
| 167 // present, disable it in two other cases. OSMesa is expected to run very | |
| 168 // slowly. Also disable the watchdog on valgrind because the code is expected | |
| 169 // to run slowly in that case. | |
| 170 bool enable_watchdog = | |
| 171 !CommandLine::ForCurrentProcess()->HasSwitch( | |
| 172 switches::kDisableGpuWatchdog) && | |
| 173 gfx::GetGLImplementation() != gfx::kGLImplementationOSMesaGL && | |
| 174 !RunningOnValgrind(); | |
| 175 | |
| 176 // Disable the watchdog in debug builds because they tend to only be run by | |
| 177 // developers who will not appreciate the watchdog killing the GPU process. | |
| 178 #ifndef NDEBUG | |
| 179 enable_watchdog = false; | |
| 180 #endif | |
| 181 | |
| 182 // Disable the watchdog for Windows. It tends to abort when the GPU process | |
| 183 // is not hung but still taking a long time to do something. Instead, the | |
| 184 // browser process displays a dialog when it notices that the child window | |
| 185 // is hung giving the user an opportunity to terminate it. This is the | |
| 186 // same mechanism used to abort hung plugins. | |
| 187 #if defined(OS_WIN) | |
| 188 enable_watchdog = false; | |
| 189 #endif | |
| 190 | |
| 191 // Start the GPU watchdog only after anything that is expected to be time | |
| 192 // consuming has completed, otherwise the process is liable to be aborted. | |
| 193 if (enable_watchdog) { | |
| 194 watchdog_thread_ = new GpuWatchdogThread(kGpuTimeout); | |
| 195 watchdog_thread_->Start(); | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 void GpuThread::StopWatchdog() { | |
| 200 if (watchdog_thread_.get()) { | |
| 201 watchdog_thread_->Stop(); | |
| 202 } | |
| 203 } | |
| 204 | |
| 205 void GpuThread::OnEstablishChannel(int renderer_id) { | |
| 206 scoped_refptr<GpuChannel> channel; | |
| 207 IPC::ChannelHandle channel_handle; | |
| 208 GPUInfo gpu_info; | |
| 209 | |
| 210 GpuChannelMap::const_iterator iter = gpu_channels_.find(renderer_id); | |
| 211 if (iter == gpu_channels_.end()) | |
| 212 channel = new GpuChannel(this, renderer_id); | |
| 213 else | |
| 214 channel = iter->second; | |
| 215 | |
| 216 DCHECK(channel != NULL); | |
| 217 | |
| 218 if (channel->Init()) | |
| 219 gpu_channels_[renderer_id] = channel; | |
| 220 else | |
| 221 channel = NULL; | |
| 222 | |
| 223 if (channel.get()) { | |
| 224 channel_handle.name = channel->GetChannelName(); | |
| 225 #if defined(OS_POSIX) | |
| 226 // On POSIX, pass the renderer-side FD. Also mark it as auto-close so | |
| 227 // that it gets closed after it has been sent. | |
| 228 int renderer_fd = channel->GetRendererFileDescriptor(); | |
| 229 channel_handle.socket = base::FileDescriptor(renderer_fd, false); | |
| 230 #endif | |
| 231 } | |
| 232 | |
| 233 Send(new GpuHostMsg_ChannelEstablished(channel_handle, gpu_info_)); | |
| 234 } | |
| 235 | |
| 236 void GpuThread::OnCloseChannel(const IPC::ChannelHandle& channel_handle) { | |
| 237 for (GpuChannelMap::iterator iter = gpu_channels_.begin(); | |
| 238 iter != gpu_channels_.end(); ++iter) { | |
| 239 if (iter->second->GetChannelName() == channel_handle.name) { | |
| 240 gpu_channels_.erase(iter); | |
| 241 return; | |
| 242 } | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 void GpuThread::OnSynchronize() { | |
| 247 Send(new GpuHostMsg_SynchronizeReply()); | |
| 248 } | |
| 249 | |
| 250 void GpuThread::OnCollectGraphicsInfo(GPUInfo::Level level) { | |
| 251 #if defined(OS_WIN) | |
| 252 if (level == GPUInfo::kComplete && gpu_info_.level <= GPUInfo::kPartial) { | |
| 253 // Prevent concurrent collection of DirectX diagnostics. | |
| 254 gpu_info_.level = GPUInfo::kCompleting; | |
| 255 | |
| 256 // Asynchronously collect the DirectX diagnostics because this can take a | |
| 257 // couple of seconds. | |
| 258 if (!base::WorkerPool::PostTask( | |
| 259 FROM_HERE, | |
| 260 NewRunnableFunction(&GpuThread::CollectDxDiagnostics, this), | |
| 261 true)) { | |
| 262 | |
| 263 // Flag GPU info as complete if the DirectX diagnostics cannot be | |
| 264 // collected. | |
| 265 gpu_info_.level = GPUInfo::kComplete; | |
| 266 } else { | |
| 267 // Do not send response if we are still completing the GPUInfo struct | |
| 268 return; | |
| 269 } | |
| 270 } | |
| 271 #endif | |
| 272 Send(new GpuHostMsg_GraphicsInfoCollected(gpu_info_)); | |
| 273 } | |
| 274 | |
| 275 void GpuThread::OnCreateViewCommandBuffer( | |
| 276 gfx::PluginWindowHandle window, | |
| 277 int32 render_view_id, | |
| 278 int32 renderer_id, | |
| 279 const GPUCreateCommandBufferConfig& init_params) { | |
| 280 int32 route_id = MSG_ROUTING_NONE; | |
| 281 | |
| 282 GpuChannelMap::const_iterator iter = gpu_channels_.find(renderer_id); | |
| 283 if (iter != gpu_channels_.end()) { | |
| 284 iter->second->CreateViewCommandBuffer( | |
| 285 window, render_view_id, init_params, &route_id); | |
| 286 } | |
| 287 | |
| 288 Send(new GpuHostMsg_CommandBufferCreated(route_id)); | |
| 289 } | |
| 290 | |
| 291 #if defined(OS_MACOSX) | |
| 292 void GpuThread::OnAcceleratedSurfaceBuffersSwappedACK( | |
| 293 int renderer_id, int32 route_id, uint64 swap_buffers_count) { | |
| 294 GpuChannelMap::const_iterator iter = gpu_channels_.find(renderer_id); | |
| 295 if (iter == gpu_channels_.end()) | |
| 296 return; | |
| 297 scoped_refptr<GpuChannel> channel = iter->second; | |
| 298 channel->AcceleratedSurfaceBuffersSwapped(route_id, swap_buffers_count); | |
| 299 } | |
| 300 void GpuThread::OnDidDestroyAcceleratedSurface( | |
| 301 int renderer_id, int32 renderer_route_id) { | |
| 302 GpuChannelMap::const_iterator iter = gpu_channels_.find(renderer_id); | |
| 303 if (iter == gpu_channels_.end()) | |
| 304 return; | |
| 305 scoped_refptr<GpuChannel> channel = iter->second; | |
| 306 channel->DidDestroySurface(renderer_route_id); | |
| 307 } | |
| 308 #endif | |
| 309 | |
| 310 void GpuThread::OnCrash() { | |
| 311 LOG(INFO) << "GPU: Simulating GPU crash"; | |
| 312 // Good bye, cruel world. | |
| 313 volatile int* it_s_the_end_of_the_world_as_we_know_it = NULL; | |
| 314 *it_s_the_end_of_the_world_as_we_know_it = 0xdead; | |
| 315 } | |
| 316 | |
| 317 void GpuThread::OnHang() { | |
| 318 LOG(INFO) << "GPU: Simulating GPU hang"; | |
| 319 for (;;) { | |
| 320 // Do not sleep here. The GPU watchdog timer tracks the amount of user | |
| 321 // time this thread is using and it doesn't use much while calling Sleep. | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 #if defined(OS_WIN) | |
| 326 | |
| 327 // Runs on a worker thread. The GpuThread never terminates voluntarily so it is | |
| 328 // safe to assume that its message loop is valid. | |
| 329 void GpuThread::CollectDxDiagnostics(GpuThread* thread) { | |
| 330 app::win::ScopedCOMInitializer com_initializer; | |
| 331 | |
| 332 DxDiagNode node; | |
| 333 gpu_info_collector::GetDxDiagnostics(&node); | |
| 334 | |
| 335 thread->message_loop()->PostTask( | |
| 336 FROM_HERE, | |
| 337 NewRunnableFunction(&GpuThread::SetDxDiagnostics, thread, node)); | |
| 338 } | |
| 339 | |
| 340 // Runs on the GPU thread. | |
| 341 void GpuThread::SetDxDiagnostics(GpuThread* thread, const DxDiagNode& node) { | |
| 342 thread->gpu_info_.dx_diagnostics = node; | |
| 343 thread->gpu_info_.level = GPUInfo::kComplete; | |
| 344 thread->Send(new GpuHostMsg_GraphicsInfoCollected(thread->gpu_info_)); | |
| 345 } | |
| 346 | |
| 347 #endif | |
| OLD | NEW |