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