// 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/rand_util.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/sys_info.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/platform_thread.h" #include "base/timer/hi_res_timer_manager.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "components/viz/service/main/viz_main_impl.h" #include "content/common/content_constants_internal.h" #include "content/common/content_switches_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 "content/public/gpu/content_gpu_client.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/common/gpu_preferences_util.h" #include "gpu/ipc/service/gpu_config.h" #include "gpu/ipc/service/gpu_init.h" #include "gpu/ipc/service/gpu_watchdog_thread.h" #include "media/gpu/buildflags.h" #include "third_party/angle/src/gpu_info_util/SystemInfo.h" #include "third_party/skia/include/core/SkGraphics.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/windows/dxva_video_decode_accelerator_win.h" #include "media/gpu/windows/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_connection.h" // nogncheck #include "ui/gfx/x/x11_switches.h" // nogncheck #endif #if defined(OS_LINUX) #include "content/common/font_config_ipc_linux.h" #include "content/gpu/gpu_sandbox_hook_linux.h" #include "content/public/common/common_sandbox_support_linux.h" #include "content/public/common/sandbox_init.h" #include "services/service_manager/sandbox/linux/sandbox_linux.h" #include "third_party/skia/include/ports/SkFontConfigInterface.h" #endif #if defined(OS_MACOSX) #include "base/message_loop/message_pump_mac.h" #include "sandbox/mac/seatbelt.h" #include "services/service_manager/sandbox/mac/sandbox_mac.h" #endif #if defined(USE_OZONE) #include "ui/ozone/public/ozone_platform.h" #endif #if BUILDFLAG(USE_VAAPI) #include "media/gpu/vaapi/vaapi_wrapper.h" #endif #if defined(OS_MACOSX) extern "C" { void _LSSetApplicationLaunchServicesServerConnectionStatus( uint64_t flags, bool (^connection_allowed)(CFDictionaryRef)); }; #endif // defined(OS_MACOSX) namespace content { namespace { #if defined(OS_LINUX) bool StartSandboxLinux(gpu::GpuWatchdogThread*, const gpu::GPUInfo*, const gpu::GpuPreferences&); #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) { viz::VizMainImpl::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 BUILDFLAG(USE_VAAPI) media::VaapiWrapper::PreSandboxInitialization(); #endif #if defined(OS_WIN) media::DXVAVideoDecodeAccelerator::PreSandboxInitialization(); media::MediaFoundationVideoEncodeAccelerator::PreSandboxInitialization(); #endif // On Linux, reading system memory doesn't work through the GPU sandbox. // This value is cached, so access it here to populate the cache. base::SysInfo::AmountOfPhysicalMemory(); } bool EnsureSandboxInitialized(gpu::GpuWatchdogThread* watchdog_thread, const gpu::GPUInfo* gpu_info, const gpu::GpuPreferences& gpu_prefs) override { #if defined(OS_LINUX) return StartSandboxLinux(watchdog_thread, gpu_info, gpu_prefs); #elif defined(OS_WIN) return StartSandboxWindows(sandbox_info_); #elif defined(OS_MACOSX) return sandbox::Seatbelt::IsSandboxed(); #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()->set_process_name("GPU Process"); base::trace_event::TraceLog::GetInstance()->SetProcessSortIndex( kTraceEventGpuProcessSortIndex); const base::CommandLine& command_line = parameters.command_line; gpu::GpuPreferences gpu_preferences; if (command_line.HasSwitch(switches::kGpuPreferences)) { std::string value = command_line.GetSwitchValueASCII(switches::kGpuPreferences); bool success = gpu::SwitchValueToGpuPreferences(value, &gpu_preferences); CHECK(success); } if (gpu_preferences.gpu_startup_dialog) 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); // COM is used by some Windows Media Foundation calls made on this thread and // must be MTA so we don't have to worry about pumping messages to handle // COM callbacks. base::win::ScopedCOMInitializer com_initializer( base::win::ScopedCOMInitializer::kMTA); #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) // The GpuMain thread should not be pumping Windows messages because no UI // is expected to run on this thread. main_message_loop.reset( new base::MessageLoop(base::MessageLoop::TYPE_DEFAULT)); #elif defined(USE_X11) // Depending on how Chrome is running there are multiple threads that can // make Xlib function calls. Call XInitThreads() here to be safe, even if // some configurations don't strictly need it. gfx::InitializeThreadedX11(); // 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) // Cross-process CoreAnimation requires a CFRunLoop to function at all, and // requires a NSRunLoop to not starve under heavy load. See: // https://crbug.com/312462#c51 and https://crbug.com/783298 std::unique_ptr pump(new base::MessagePumpNSRunLoop()); main_message_loop.reset(new base::MessageLoop(std::move(pump))); // Tell LaunchServices to continue without a connection to the daemon. _LSSetApplicationLaunchServicesServerConnectionStatus(0, nullptr); #else main_message_loop.reset( new base::MessageLoop(base::MessageLoop::TYPE_DEFAULT)); #endif } base::PlatformThread::SetName("CrGpuMain"); #if defined(OS_ANDROID) || defined(OS_CHROMEOS) // Set thread priority before sandbox initialization. base::PlatformThread::SetCurrentThreadPriority(base::ThreadPriority::DISPLAY); #endif auto gpu_init = std::make_unique(); 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::VizMain::CreateGpuService()). const bool init_success = gpu_init->InitializeAndStartSandbox( const_cast(&command_line), gpu_preferences); const bool dead_on_arrival = !init_success; logging::SetLogMessageHandler(nullptr); 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); auto* client = GetContentClient()->gpu(); if (client) client->PostIOThreadCreated(gpu_process.io_task_runner()); GpuChildThread* child_thread = new GpuChildThread( std::move(gpu_init), 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 if (command_line.HasSwitch(switches::kEnableOOPRasterization)) { SkGraphics::Init(); #if defined(OS_LINUX) // Set up the font IPC so that the GPU process can create typefaces. SkFontConfigInterface::SetGlobal(new FontConfigIPC(GetSandboxFD())) ->unref(); #endif } base::HighResolutionTimerManager hi_res_timer_manager; { 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, const gpu::GPUInfo* gpu_info, const gpu::GpuPreferences& gpu_prefs) { TRACE_EVENT0("gpu,startup", "Initialize sandbox"); if (watchdog_thread) { // SandboxLinux needs to be able to ensure that the thread // has really been stopped. service_manager::SandboxLinux::GetInstance()->StopThread(watchdog_thread); } // SandboxLinux::InitializeSandbox() must always be called // with only one thread. service_manager::SandboxLinux::Options sandbox_options; sandbox_options.use_amd_specific_policies = gpu_info && angle::IsAMD(gpu_info->active_gpu().vendor_id); sandbox_options.accelerated_video_decode_enabled = !gpu_prefs.disable_accelerated_video_decode; sandbox_options.accelerated_video_encode_enabled = !gpu_prefs.disable_accelerated_video_encode; bool res = service_manager::SandboxLinux::GetInstance()->InitializeSandbox( service_manager::SandboxTypeFromCommandLine( *base::CommandLine::ForCurrentProcess()), base::BindOnce(GpuProcessPreSandboxHook), sandbox_options); if (watchdog_thread) { base::Thread::Options thread_options; thread_options.timer_slack = base::TIMER_SLACK_MAXIMUM; watchdog_thread->StartWithOptions(thread_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