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 <stdlib.h> 6 7#if defined(OS_WIN) 8#include <dwmapi.h> 9#include <windows.h> 10#endif 11 12#include "base/debug/trace_event.h" 13#include "base/lazy_instance.h" 14#include "base/message_loop/message_loop.h" 15#include "base/metrics/histogram.h" 16#include "base/rand_util.h" 17#include "base/strings/string_number_conversions.h" 18#include "base/strings/stringprintf.h" 19#include "base/threading/platform_thread.h" 20#include "build/build_config.h" 21#include "content/child/child_process.h" 22#include "content/common/content_constants_internal.h" 23#include "content/common/gpu/gpu_config.h" 24#include "content/common/gpu/gpu_messages.h" 25#include "content/common/gpu/media/gpu_video_encode_accelerator.h" 26#include "content/common/sandbox_linux/sandbox_linux.h" 27#include "content/gpu/gpu_child_thread.h" 28#include "content/gpu/gpu_process.h" 29#include "content/gpu/gpu_watchdog_thread.h" 30#include "content/public/common/content_client.h" 31#include "content/public/common/content_switches.h" 32#include "content/public/common/main_function_params.h" 33#include "gpu/command_buffer/service/gpu_switches.h" 34#include "gpu/config/gpu_info_collector.h" 35#include "gpu/config/gpu_util.h" 36#include "ui/events/platform/platform_event_source.h" 37#include "ui/gl/gl_implementation.h" 38#include "ui/gl/gl_surface.h" 39#include "ui/gl/gl_switches.h" 40#include "ui/gl/gpu_switching_manager.h" 41 42#if defined(OS_WIN) 43#include "base/win/windows_version.h" 44#include "base/win/scoped_com_initializer.h" 45#include "sandbox/win/src/sandbox.h" 46#endif 47 48#if defined(USE_X11) 49#include "ui/base/x/x11_util.h" 50#endif 51 52#if defined(OS_LINUX) 53#include "content/public/common/sandbox_init.h" 54#endif 55 56#if defined(OS_MACOSX) 57#include "base/message_loop/message_pump_mac.h" 58#include "content/common/sandbox_mac.h" 59#endif 60 61#if defined(ADDRESS_SANITIZER) 62#include <sanitizer/asan_interface.h> 63#endif 64 65const int kGpuTimeout = 10000; 66 67namespace content { 68 69namespace { 70 71void GetGpuInfoFromCommandLine(gpu::GPUInfo& gpu_info, 72 const CommandLine& command_line); 73bool WarmUpSandbox(const CommandLine& command_line); 74 75#if !defined(OS_MACOSX) 76bool CollectGraphicsInfo(gpu::GPUInfo& gpu_info); 77#endif 78 79#if defined(OS_LINUX) 80#if !defined(OS_CHROMEOS) 81bool CanAccessNvidiaDeviceFile(); 82#endif 83bool StartSandboxLinux(const gpu::GPUInfo&, GpuWatchdogThread*, bool); 84#elif defined(OS_WIN) 85bool StartSandboxWindows(const sandbox::SandboxInterfaceInfo*); 86#endif 87 88base::LazyInstance<GpuChildThread::DeferredMessages> deferred_messages = 89 LAZY_INSTANCE_INITIALIZER; 90 91bool GpuProcessLogMessageHandler(int severity, 92 const char* file, int line, 93 size_t message_start, 94 const std::string& str) { 95 std::string header = str.substr(0, message_start); 96 std::string message = str.substr(message_start); 97 deferred_messages.Get().push(new GpuHostMsg_OnLogMessage( 98 severity, header, message)); 99 return false; 100} 101 102} // namespace anonymous 103 104// Main function for starting the Gpu process. 105int GpuMain(const MainFunctionParams& parameters) { 106 TRACE_EVENT0("gpu", "GpuMain"); 107 base::debug::TraceLog::GetInstance()->SetProcessName("GPU Process"); 108 base::debug::TraceLog::GetInstance()->SetProcessSortIndex( 109 kTraceEventGpuProcessSortIndex); 110 111 const CommandLine& command_line = parameters.command_line; 112 if (command_line.HasSwitch(switches::kGpuStartupDialog)) { 113 ChildProcess::WaitForDebugger("Gpu"); 114 } 115 116 base::Time start_time = base::Time::Now(); 117 118#if defined(OS_WIN) 119 // Prevent Windows from displaying a modal dialog on failures like not being 120 // able to load a DLL. 121 SetErrorMode( 122 SEM_FAILCRITICALERRORS | 123 SEM_NOGPFAULTERRORBOX | 124 SEM_NOOPENFILEERRORBOX); 125#elif defined(USE_X11) 126 ui::SetDefaultX11ErrorHandlers(); 127#endif 128 129 logging::SetLogMessageHandler(GpuProcessLogMessageHandler); 130 131 if (command_line.HasSwitch(switches::kSupportsDualGpus)) { 132 std::string types = command_line.GetSwitchValueASCII( 133 switches::kGpuDriverBugWorkarounds); 134 std::set<int> workarounds; 135 gpu::StringToFeatureSet(types, &workarounds); 136 if (workarounds.count(gpu::FORCE_DISCRETE_GPU) == 1) 137 ui::GpuSwitchingManager::GetInstance()->ForceUseOfDiscreteGpu(); 138 else if (workarounds.count(gpu::FORCE_INTEGRATED_GPU) == 1) 139 ui::GpuSwitchingManager::GetInstance()->ForceUseOfIntegratedGpu(); 140 } 141 142 // Initialization of the OpenGL bindings may fail, in which case we 143 // will need to tear down this process. However, we can not do so 144 // safely until the IPC channel is set up, because the detection of 145 // early return of a child process is implemented using an IPC 146 // channel error. If the IPC channel is not fully set up between the 147 // browser and GPU process, and the GPU process crashes or exits 148 // early, the browser process will never detect it. For this reason 149 // we defer tearing down the GPU process until receiving the 150 // GpuMsg_Initialize message from the browser. 151 bool dead_on_arrival = false; 152 153#if defined(OS_WIN) 154 base::MessageLoop::Type message_loop_type = base::MessageLoop::TYPE_IO; 155 // Unless we're running on desktop GL, we don't need a UI message 156 // loop, so avoid its use to work around apparent problems with some 157 // third-party software. 158 if (command_line.HasSwitch(switches::kUseGL) && 159 command_line.GetSwitchValueASCII(switches::kUseGL) == 160 gfx::kGLImplementationDesktopName) { 161 message_loop_type = base::MessageLoop::TYPE_UI; 162 } 163 base::MessageLoop main_message_loop(message_loop_type); 164#elif defined(OS_LINUX) && defined(USE_X11) 165 // We need a UI loop so that we can grab the Expose events. See GLSurfaceGLX 166 // and https://crbug.com/326995. 167 base::MessageLoop main_message_loop(base::MessageLoop::TYPE_UI); 168 scoped_ptr<ui::PlatformEventSource> event_source = 169 ui::PlatformEventSource::CreateDefault(); 170#elif defined(OS_LINUX) 171 base::MessageLoop main_message_loop(base::MessageLoop::TYPE_DEFAULT); 172#elif defined(OS_MACOSX) 173 // This is necessary for CoreAnimation layers hosted in the GPU process to be 174 // drawn. See http://crbug.com/312462. 175 scoped_ptr<base::MessagePump> pump(new base::MessagePumpCFRunLoop()); 176 base::MessageLoop main_message_loop(pump.Pass()); 177#else 178 base::MessageLoop main_message_loop(base::MessageLoop::TYPE_IO); 179#endif 180 181 base::PlatformThread::SetName("CrGpuMain"); 182 183 // In addition to disabling the watchdog if the command line switch is 184 // present, disable the watchdog on valgrind because the code is expected 185 // to run slowly in that case. 186 bool enable_watchdog = 187 !command_line.HasSwitch(switches::kDisableGpuWatchdog) && 188 !RunningOnValgrind(); 189 190 // Disable the watchdog in debug builds because they tend to only be run by 191 // developers who will not appreciate the watchdog killing the GPU process. 192#ifndef NDEBUG 193 enable_watchdog = false; 194#endif 195 196 bool delayed_watchdog_enable = false; 197 198#if defined(OS_CHROMEOS) 199 // Don't start watchdog immediately, to allow developers to switch to VT2 on 200 // startup. 201 delayed_watchdog_enable = true; 202#endif 203 204 scoped_refptr<GpuWatchdogThread> watchdog_thread; 205 206 // Start the GPU watchdog only after anything that is expected to be time 207 // consuming has completed, otherwise the process is liable to be aborted. 208 if (enable_watchdog && !delayed_watchdog_enable) { 209 watchdog_thread = new GpuWatchdogThread(kGpuTimeout); 210 base::Thread::Options options; 211 options.timer_slack = base::TIMER_SLACK_MAXIMUM; 212 watchdog_thread->StartWithOptions(options); 213 } 214 215 gpu::GPUInfo gpu_info; 216 // Get vendor_id, device_id, driver_version from browser process through 217 // commandline switches. 218 GetGpuInfoFromCommandLine(gpu_info, command_line); 219 220 base::TimeDelta collect_context_time; 221 base::TimeDelta initialize_one_off_time; 222 223 // Warm up resources that don't need access to GPUInfo. 224 if (WarmUpSandbox(command_line)) { 225#if defined(OS_LINUX) 226 bool initialized_sandbox = false; 227 bool initialized_gl_context = false; 228 bool should_initialize_gl_context = false; 229 // On Chrome OS ARM Mali, GPU driver userspace creates threads when 230 // initializing a GL context, so start the sandbox early. 231 if (command_line.HasSwitch(switches::kGpuSandboxStartEarly)) { 232 gpu_info.sandboxed = StartSandboxLinux( 233 gpu_info, watchdog_thread.get(), should_initialize_gl_context); 234 initialized_sandbox = true; 235 } 236#endif // defined(OS_LINUX) 237 238 base::TimeTicks before_initialize_one_off = base::TimeTicks::Now(); 239 240 // Determine if we need to initialize GL here or it has already been done. 241 bool gl_already_initialized = false; 242#if defined(OS_MACOSX) 243 if (!command_line.HasSwitch(switches::kNoSandbox)) { 244 // On Mac, if the sandbox is enabled, then GLSurface::InitializeOneOff() 245 // is called from the sandbox warmup code before getting here. 246 gl_already_initialized = true; 247 } 248#endif 249 if (command_line.HasSwitch(switches::kInProcessGPU)) { 250 // With in-process GPU, GLSurface::InitializeOneOff() is called from 251 // GpuChildThread before getting here. 252 gl_already_initialized = true; 253 } 254 255 // Load and initialize the GL implementation and locate the GL entry points. 256 bool gl_initialized = 257 gl_already_initialized 258 ? gfx::GetGLImplementation() != gfx::kGLImplementationNone 259 : gfx::GLSurface::InitializeOneOff(); 260 if (gl_initialized) { 261 // We need to collect GL strings (VENDOR, RENDERER) for blacklisting 262 // purposes. However, on Mac we don't actually use them. As documented in 263 // crbug.com/222934, due to some driver issues, glGetString could take 264 // multiple seconds to finish, which in turn cause the GPU process to 265 // crash. 266 // By skipping the following code on Mac, we don't really lose anything, 267 // because the basic GPU information is passed down from browser process 268 // and we already registered them through SetGpuInfo() above. 269 base::TimeTicks before_collect_context_graphics_info = 270 base::TimeTicks::Now(); 271#if !defined(OS_MACOSX) 272 if (!CollectGraphicsInfo(gpu_info)) 273 dead_on_arrival = true; 274 275#if defined(OS_CHROMEOS) || defined(OS_ANDROID) 276 // Recompute gpu driver bug workarounds - this is specifically useful 277 // on systems where vendor_id/device_id aren't available. 278 if (!command_line.HasSwitch(switches::kDisableGpuDriverBugWorkarounds)) { 279 gpu::ApplyGpuDriverBugWorkarounds( 280 gpu_info, const_cast<CommandLine*>(&command_line)); 281 } 282#endif 283 284#if defined(OS_LINUX) 285 initialized_gl_context = true; 286#if !defined(OS_CHROMEOS) 287 if (gpu_info.gpu.vendor_id == 0x10de && // NVIDIA 288 gpu_info.driver_vendor == "NVIDIA" && 289 !CanAccessNvidiaDeviceFile()) 290 dead_on_arrival = true; 291#endif // !defined(OS_CHROMEOS) 292#endif // defined(OS_LINUX) 293#endif // !defined(OS_MACOSX) 294 collect_context_time = 295 base::TimeTicks::Now() - before_collect_context_graphics_info; 296 } else { // gl_initialized 297 VLOG(1) << "gfx::GLSurface::InitializeOneOff failed"; 298 dead_on_arrival = true; 299 } 300 301 initialize_one_off_time = 302 base::TimeTicks::Now() - before_initialize_one_off; 303 304 if (enable_watchdog && delayed_watchdog_enable) { 305 watchdog_thread = new GpuWatchdogThread(kGpuTimeout); 306 base::Thread::Options options; 307 options.timer_slack = base::TIMER_SLACK_MAXIMUM; 308 watchdog_thread->StartWithOptions(options); 309 } 310 311 // OSMesa is expected to run very slowly, so disable the watchdog in that 312 // case. 313 if (enable_watchdog && 314 gfx::GetGLImplementation() == gfx::kGLImplementationOSMesaGL) { 315 watchdog_thread->Stop(); 316 watchdog_thread = NULL; 317 } 318 319#if defined(OS_LINUX) 320 should_initialize_gl_context = !initialized_gl_context && 321 !dead_on_arrival; 322 323 if (!initialized_sandbox) { 324 gpu_info.sandboxed = StartSandboxLinux(gpu_info, watchdog_thread.get(), 325 should_initialize_gl_context); 326 } 327#elif defined(OS_WIN) 328 gpu_info.sandboxed = StartSandboxWindows(parameters.sandbox_info); 329#elif defined(OS_MACOSX) 330 gpu_info.sandboxed = Sandbox::SandboxIsCurrentlyActive(); 331#endif 332 333 gpu_info.video_encode_accelerator_supported_profiles = 334 content::GpuVideoEncodeAccelerator::GetSupportedProfiles(); 335 } else { 336 dead_on_arrival = true; 337 } 338 339 logging::SetLogMessageHandler(NULL); 340 341 GpuProcess gpu_process; 342 343 // These UMA must be stored after GpuProcess is constructed as it 344 // initializes StatisticsRecorder which tracks the histograms. 345 UMA_HISTOGRAM_TIMES("GPU.CollectContextGraphicsInfo", collect_context_time); 346 UMA_HISTOGRAM_TIMES("GPU.InitializeOneOffTime", initialize_one_off_time); 347 348 GpuChildThread* child_thread = new GpuChildThread(watchdog_thread.get(), 349 dead_on_arrival, 350 gpu_info, 351 deferred_messages.Get()); 352 while (!deferred_messages.Get().empty()) 353 deferred_messages.Get().pop(); 354 355 child_thread->Init(start_time); 356 357 gpu_process.set_main_thread(child_thread); 358 359 if (watchdog_thread.get()) 360 watchdog_thread->AddPowerObserver(); 361 362 { 363 TRACE_EVENT0("gpu", "Run Message Loop"); 364 main_message_loop.Run(); 365 } 366 367 child_thread->StopWatchdog(); 368 369 return 0; 370} 371 372namespace { 373 374void GetGpuInfoFromCommandLine(gpu::GPUInfo& gpu_info, 375 const CommandLine& command_line) { 376 DCHECK(command_line.HasSwitch(switches::kGpuVendorID) && 377 command_line.HasSwitch(switches::kGpuDeviceID) && 378 command_line.HasSwitch(switches::kGpuDriverVersion)); 379 bool success = base::HexStringToUInt( 380 command_line.GetSwitchValueASCII(switches::kGpuVendorID), 381 &gpu_info.gpu.vendor_id); 382 DCHECK(success); 383 success = base::HexStringToUInt( 384 command_line.GetSwitchValueASCII(switches::kGpuDeviceID), 385 &gpu_info.gpu.device_id); 386 DCHECK(success); 387 gpu_info.driver_vendor = 388 command_line.GetSwitchValueASCII(switches::kGpuDriverVendor); 389 gpu_info.driver_version = 390 command_line.GetSwitchValueASCII(switches::kGpuDriverVersion); 391 GetContentClient()->SetGpuInfo(gpu_info); 392} 393 394bool WarmUpSandbox(const CommandLine& command_line) { 395 { 396 TRACE_EVENT0("gpu", "Warm up rand"); 397 // Warm up the random subsystem, which needs to be done pre-sandbox on all 398 // platforms. 399 (void) base::RandUint64(); 400 } 401 return true; 402} 403 404#if !defined(OS_MACOSX) 405bool CollectGraphicsInfo(gpu::GPUInfo& gpu_info) { 406 bool res = true; 407 gpu::CollectInfoResult result = gpu::CollectContextGraphicsInfo(&gpu_info); 408 switch (result) { 409 case gpu::kCollectInfoFatalFailure: 410 LOG(ERROR) << "gpu::CollectGraphicsInfo failed (fatal)."; 411 res = false; 412 break; 413 case gpu::kCollectInfoNonFatalFailure: 414 VLOG(1) << "gpu::CollectGraphicsInfo failed (non-fatal)."; 415 break; 416 case gpu::kCollectInfoNone: 417 NOTREACHED(); 418 break; 419 case gpu::kCollectInfoSuccess: 420 break; 421 } 422 GetContentClient()->SetGpuInfo(gpu_info); 423 return res; 424} 425#endif 426 427#if defined(OS_LINUX) 428#if !defined(OS_CHROMEOS) 429bool CanAccessNvidiaDeviceFile() { 430 bool res = true; 431 base::ThreadRestrictions::AssertIOAllowed(); 432 if (access("/dev/nvidiactl", R_OK) != 0) { 433 VLOG(1) << "NVIDIA device file /dev/nvidiactl access denied"; 434 res = false; 435 } 436 return res; 437} 438#endif 439 440void CreateDummyGlContext() { 441 scoped_refptr<gfx::GLSurface> surface( 442 gfx::GLSurface::CreateOffscreenGLSurface(gfx::Size())); 443 if (!surface.get()) { 444 VLOG(1) << "gfx::GLSurface::CreateOffscreenGLSurface failed"; 445 return; 446 } 447 448 // On Linux, this is needed to make sure /dev/nvidiactl has 449 // been opened and its descriptor cached. 450 scoped_refptr<gfx::GLContext> context(gfx::GLContext::CreateGLContext( 451 NULL, surface.get(), gfx::PreferDiscreteGpu)); 452 if (!context.get()) { 453 VLOG(1) << "gfx::GLContext::CreateGLContext failed"; 454 return; 455 } 456 457 // Similarly, this is needed for /dev/nvidia0. 458 if (context->MakeCurrent(surface.get())) { 459 context->ReleaseCurrent(surface.get()); 460 } else { 461 VLOG(1) << "gfx::GLContext::MakeCurrent failed"; 462 } 463} 464 465void WarmUpSandboxNvidia(const gpu::GPUInfo& gpu_info, 466 bool should_initialize_gl_context) { 467 // We special case Optimus since the vendor_id we see may not be Nvidia. 468 bool uses_nvidia_driver = (gpu_info.gpu.vendor_id == 0x10de && // NVIDIA. 469 gpu_info.driver_vendor == "NVIDIA") || 470 gpu_info.optimus; 471 if (uses_nvidia_driver && should_initialize_gl_context) { 472 // We need this on Nvidia to pre-open /dev/nvidiactl and /dev/nvidia0. 473 CreateDummyGlContext(); 474 } 475} 476 477bool StartSandboxLinux(const gpu::GPUInfo& gpu_info, 478 GpuWatchdogThread* watchdog_thread, 479 bool should_initialize_gl_context) { 480 TRACE_EVENT0("gpu", "Initialize sandbox"); 481 482 bool res = false; 483 484 WarmUpSandboxNvidia(gpu_info, should_initialize_gl_context); 485 486 if (watchdog_thread) { 487 // LinuxSandbox needs to be able to ensure that the thread 488 // has really been stopped. 489 LinuxSandbox::StopThread(watchdog_thread); 490 } 491 492#if defined(ADDRESS_SANITIZER) 493 const std::string sancov_file_name = 494 "gpu." + base::Uint64ToString(base::RandUint64()); 495 LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance(); 496 linux_sandbox->sanitizer_args()->coverage_sandboxed = 1; 497 linux_sandbox->sanitizer_args()->coverage_fd = 498 __sanitizer_maybe_open_cov_file(sancov_file_name.c_str()); 499 linux_sandbox->sanitizer_args()->coverage_max_block_size = 0; 500#endif 501 502 // LinuxSandbox::InitializeSandbox() must always be called 503 // with only one thread. 504 res = LinuxSandbox::InitializeSandbox(); 505 if (watchdog_thread) { 506 base::Thread::Options options; 507 options.timer_slack = base::TIMER_SLACK_MAXIMUM; 508 watchdog_thread->StartWithOptions(options); 509 } 510 511 return res; 512} 513#endif // defined(OS_LINUX) 514 515#if defined(OS_WIN) 516bool StartSandboxWindows(const sandbox::SandboxInterfaceInfo* sandbox_info) { 517 TRACE_EVENT0("gpu", "Lower token"); 518 519 // For Windows, if the target_services interface is not zero, the process 520 // is sandboxed and we must call LowerToken() before rendering untrusted 521 // content. 522 sandbox::TargetServices* target_services = sandbox_info->target_services; 523 if (target_services) { 524 target_services->LowerToken(); 525 return true; 526 } 527 528 return false; 529} 530#endif // defined(OS_WIN) 531 532} // namespace. 533 534} // namespace content 535