// 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 #include #include #include #include "base/lazy_instance.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/statistics_recorder.h" #include "base/rand_util.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/platform_thread.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "content/child/child_process.h" #include "content/common/content_constants_internal.h" #include "content/gpu/gpu_child_thread.h" #include "content/gpu/gpu_process.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" #include "content/public/common/result_codes.h" #include "gpu/command_buffer/service/gpu_switches.h" #include "gpu/config/gpu_driver_bug_list.h" #include "gpu/config/gpu_info_collector.h" #include "gpu/config/gpu_switches.h" #include "gpu/config/gpu_util.h" #include "gpu/ipc/common/gpu_memory_buffer_support.h" #include "gpu/ipc/service/gpu_config.h" #include "gpu/ipc/service/gpu_init.h" #include "gpu/ipc/service/gpu_watchdog_thread.h" #include "ui/events/platform/platform_event_source.h" #include "ui/gfx/switches.h" #include "ui/gl/gl_context.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" #include "ui/gl/init/gl_factory.h" #if defined(OS_WIN) #include #include #endif #if defined(OS_ANDROID) #include "base/trace_event/memory_dump_manager.h" #include "components/tracing/common/graphics_memory_dump_provider_android.h" #endif #if defined(OS_WIN) #include "base/win/scoped_com_initializer.h" #include "base/win/windows_version.h" #include "media/gpu/dxva_video_decode_accelerator_win.h" #include "media/gpu/media_foundation_video_encode_accelerator_win.h" #include "sandbox/win/src/sandbox.h" #endif #if defined(USE_X11) #include "ui/base/x/x11_util.h" // nogncheck #include "ui/gfx/x/x11_switches.h" // nogncheck #endif #if defined(OS_LINUX) #include "content/common/sandbox_linux/sandbox_linux.h" #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(USE_OZONE) #include "ui/ozone/public/ozone_platform.h" #endif #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) #include "media/gpu/vaapi_wrapper.h" #endif #if defined(SANITIZER_COVERAGE) #include #include #endif namespace content { namespace { #if defined(OS_LINUX) bool StartSandboxLinux(gpu::GpuWatchdogThread*); #elif defined(OS_WIN) bool StartSandboxWindows(const sandbox::SandboxInterfaceInfo*); #endif base::LazyInstance::DestructorAtExit deferred_messages = LAZY_INSTANCE_INITIALIZER; bool GpuProcessLogMessageHandler(int severity, const char* file, int line, size_t message_start, const std::string& str) { GpuChildThread::LogMessage log; log.severity = severity; log.header = str.substr(0, message_start); log.message = str.substr(message_start); deferred_messages.Get().push_back(std::move(log)); return false; } class ContentSandboxHelper : public gpu::GpuSandboxHelper { public: ContentSandboxHelper() {} ~ContentSandboxHelper() override {} #if defined(OS_WIN) void set_sandbox_info(const sandbox::SandboxInterfaceInfo* info) { sandbox_info_ = info; } #endif private: // SandboxHelper: void PreSandboxStartup() override { // Warm up resources that don't need access to GPUInfo. { TRACE_EVENT0("gpu", "Warm up rand"); // Warm up the random subsystem, which needs to be done pre-sandbox on all // platforms. (void)base::RandUint64(); } #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) media::VaapiWrapper::PreSandboxInitialization(); #endif #if defined(OS_WIN) media::DXVAVideoDecodeAccelerator::PreSandboxInitialization(); media::MediaFoundationVideoEncodeAccelerator::PreSandboxInitialization(); #endif } bool EnsureSandboxInitialized( gpu::GpuWatchdogThread* watchdog_thread) override { #if defined(OS_LINUX) return StartSandboxLinux(watchdog_thread); #elif defined(OS_WIN) return StartSandboxWindows(sandbox_info_); #elif defined(OS_MACOSX) return Sandbox::SandboxIsCurrentlyActive(); #else return false; #endif } #if defined(OS_WIN) const sandbox::SandboxInterfaceInfo* sandbox_info_ = nullptr; #endif DISALLOW_COPY_AND_ASSIGN(ContentSandboxHelper); }; } // namespace // Main function for starting the Gpu process. int GpuMain(const MainFunctionParams& parameters) { TRACE_EVENT0("gpu", "GpuMain"); base::trace_event::TraceLog::GetInstance()->SetProcessName("GPU Process"); base::trace_event::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); #endif logging::SetLogMessageHandler(GpuProcessLogMessageHandler); // We are experiencing what appear to be memory-stomp issues in the GPU // process. These issues seem to be impacting the message loop and listeners // registered to it. Create the message loop on the heap to guard against // this. // TODO(ericrk): Revisit this once we assess its impact on crbug.com/662802 // and crbug.com/609252. std::unique_ptr main_message_loop; std::unique_ptr event_source; if (command_line.HasSwitch(switches::kHeadless)) { main_message_loop.reset( new base::MessageLoop(base::MessageLoop::TYPE_DEFAULT)); } else { #if defined(OS_WIN) // OK to use default non-UI message loop because all GPU windows run on // dedicated thread. main_message_loop.reset( new base::MessageLoop(base::MessageLoop::TYPE_DEFAULT)); #elif defined(USE_X11) // We need a UI loop so that we can grab the Expose events. See GLSurfaceGLX // and https://crbug.com/326995. #if !defined(TOOLKIT_QT) ui::SetDefaultX11ErrorHandlers(); if (!gfx::GetXDisplay()) return RESULT_CODE_GPU_DEAD_ON_ARRIVAL; #endif main_message_loop.reset(new base::MessageLoop(base::MessageLoop::TYPE_UI)); event_source = ui::PlatformEventSource::CreateDefault(); #elif defined(USE_OZONE) // The MessageLoop type required depends on the Ozone platform selected at // runtime. main_message_loop.reset(new base::MessageLoop( ui::OzonePlatform::EnsureInstance()->GetMessageLoopTypeForGpu())); #elif defined(OS_LINUX) #error "Unsupported Linux platform." #elif defined(OS_MACOSX) // This is necessary for CoreAnimation layers hosted in the GPU process to // be drawn. See http://crbug.com/312462. std::unique_ptr pump(new base::MessagePumpCFRunLoop()); main_message_loop.reset(new base::MessageLoop(std::move(pump))); #else main_message_loop.reset( new base::MessageLoop(base::MessageLoop::TYPE_DEFAULT)); #endif } base::PlatformThread::SetName("CrGpuMain"); // Initializes StatisticsRecorder which tracks UMA histograms. base::StatisticsRecorder::Initialize(); #if defined(OS_ANDROID) || defined(OS_CHROMEOS) // Set thread priority before sandbox initialization. base::PlatformThread::SetCurrentThreadPriority(base::ThreadPriority::DISPLAY); #endif gpu::GpuInit gpu_init; ContentSandboxHelper sandbox_helper; #if defined(OS_WIN) sandbox_helper.set_sandbox_info(parameters.sandbox_info); #endif gpu_init.set_sandbox_helper(&sandbox_helper); // Gpu initialization may fail for various reasons, 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 initialization // message from the browser (through mojom::GpuMain::CreateGpuService()). const bool init_success = gpu_init.InitializeAndStartSandbox(command_line); const bool dead_on_arrival = !init_success; logging::SetLogMessageHandler(NULL); GetContentClient()->SetGpuInfo(gpu_init.gpu_info()); base::ThreadPriority io_thread_priority = base::ThreadPriority::NORMAL; #if defined(OS_ANDROID) || defined(OS_CHROMEOS) io_thread_priority = base::ThreadPriority::DISPLAY; #endif GpuProcess gpu_process(io_thread_priority); GpuChildThread* child_thread = new GpuChildThread( gpu_init.TakeWatchdogThread(), dead_on_arrival, gpu_init.gpu_info(), gpu_init.gpu_feature_info(), std::move(deferred_messages.Get())); deferred_messages.Get().clear(); child_thread->Init(start_time); gpu_process.set_main_thread(child_thread); #if defined(OS_ANDROID) base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( tracing::GraphicsMemoryDumpProvider::GetInstance(), "AndroidGraphics", nullptr); #endif { TRACE_EVENT0("gpu", "Run Message Loop"); base::RunLoop().Run(); } return dead_on_arrival ? RESULT_CODE_GPU_DEAD_ON_ARRIVAL : 0; } namespace { #if defined(OS_LINUX) bool StartSandboxLinux(gpu::GpuWatchdogThread* watchdog_thread) { TRACE_EVENT0("gpu,startup", "Initialize sandbox"); bool res = false; if (watchdog_thread) { // LinuxSandbox needs to be able to ensure that the thread // has really been stopped. LinuxSandbox::StopThread(watchdog_thread); } #if defined(SANITIZER_COVERAGE) 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,startup", "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