#include <stddef.h>
#include <stdlib.h>
#include <memory>
#include <tuple>
#include <utility>
#include "base/allocator/partition_alloc_support.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/scoped_file.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/message_loop/message_pump_type.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/clamped_math.h"
#include "base/process/current_process.h"
#include "base/process/process_metrics.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/system/sys_info.h"
#include "base/task/current_thread.h"
#include "base/task/single_thread_task_executor.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/threading/hang_watcher.h"
#include "base/threading/platform_thread.h"
#include "base/threading/platform_thread_metrics.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "base/timer/hi_res_timer_manager.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/viz/common/features.h"
#include "components/viz/service/gl/gpu_log_message_manager.h"
#include "components/viz/service/main/viz_main_impl.h"
#include "content/child/child_process.h"
#include "content/common/content_constants_internal.h"
#include "content/common/content_switches_internal.h"
#include "content/common/features.h"
#include "content/common/skia_utils.h"
#include "content/gpu/gpu_child_thread.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.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/common/zygote/zygote_buildflags.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_finch_features.h"
#include "gpu/config/gpu_info_collector.h"
#include "gpu/config/gpu_preferences.h"
#include "gpu/config/gpu_switches.h"
#include "gpu/config/gpu_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 "mojo/public/cpp/bindings/direct_receiver.h"
#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
#include "services/tracing/public/cpp/trace_startup.h"
#include "services/tracing/public/cpp/trace_startup_config.h"
#include "third_party/angle/src/gpu_info_util/SystemInfo.h"
#include "ui/base/ui_base_features.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/gfx/switches.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_features.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 BUILDFLAG(IS_WIN)
#include <windows.h>
#include <dwmapi.h>
#endif
#if BUILDFLAG(IS_ANDROID)
#include "base/android/meminfo_dump_provider.h"
#include "base/posix/eintr_wrapper.h"
#include "base/trace_event/memory_dump_manager.h"
#include "components/tracing/common/graphics_memory_dump_provider_android.h"
#include "sandbox/linux/services/thread_helpers.h"
#include "sandbox/policy/features.h"
#include "sandbox/policy/linux/landlock_gpu_policy_android.h"
#include "sandbox/policy/sandbox_type.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "base/win/scoped_com_initializer.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "media/base/win/mf_initializer.h"
#include "sandbox/policy/win/sandbox_warmup.h"
#include "sandbox/win/src/sandbox.h"
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "content/child/sandboxed_process_thread_type_handler.h"
#include "content/common/gpu_pre_sandbox_hook_linux.h"
#include "sandbox/policy/linux/sandbox_linux.h"
#include "sandbox/policy/sandbox_type.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "base/message_loop/message_pump_apple.h"
#include "components/metal_util/device_removal.h"
#include "sandbox/mac/seatbelt.h"
#endif
#if BUILDFLAG(USE_VAAPI)
#include "media/gpu/vaapi/vaapi_wrapper.h"
#endif
#if BUILDFLAG(IS_OHOS)
#include "content/common/gpu_pre_sandbox_hook_linux.h"
#include "sandbox/policy/linux/sandbox_linux.h"
#include "sandbox/policy/sandbox_type.h"
#endif
#if BUILDFLAG(IS_ARKWEB)
#include "arkweb/chromium_ext/content/gpu/gpu_main_ext.h"
#endif
namespace content {
namespace {
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
bool StartSandboxLinux(gpu::GpuWatchdogThread*,
const gpu::GPUInfo*,
const gpu::GpuPreferences&);
#elif BUILDFLAG(IS_ANDROID)
bool StartSandboxAndroid(gpu::GpuWatchdogThread*);
#elif BUILDFLAG(IS_WIN)
bool StartSandboxWindows(const sandbox::SandboxInterfaceInfo*);
#elif BUILDFLAG(IS_OHOS)
bool StartSandboxOHOS(gpu::GpuWatchdogThread*);
#endif
class ContentSandboxHelper : public gpu::GpuSandboxHelper {
public:
ContentSandboxHelper() {}
ContentSandboxHelper(const ContentSandboxHelper&) = delete;
ContentSandboxHelper& operator=(const ContentSandboxHelper&) = delete;
~ContentSandboxHelper() override {}
#if BUILDFLAG(IS_WIN)
void set_sandbox_info(const sandbox::SandboxInterfaceInfo* info) {
sandbox_info_ = info;
}
#endif
private:
void PreSandboxStartup(const gpu::GpuPreferences& gpu_prefs) override {
TRACE_EVENT("gpu,startup", "gpu_main::PreSandboxStartup");
{
TRACE_EVENT0("gpu", "Warm up rand");
#if BUILDFLAG(IS_WIN)
sandbox::policy::WarmupRandomnessInfrastructure();
#else
std::ignore = base::RandUint64();
#endif
}
#if BUILDFLAG(USE_VAAPI)
#if BUILDFLAG(IS_CHROMEOS)
media::VaapiWrapper::PreSandboxInitialization();
#else
if (!gpu_prefs.disable_accelerated_video_decode)
media::VaapiWrapper::PreSandboxInitialization();
#endif
#endif
#if BUILDFLAG(IS_WIN)
media::PreSandboxMediaFoundationInitialization();
#endif
base::SysInfo::AmountOfPhysicalMemory();
}
bool EnsureSandboxInitialized(gpu::GpuWatchdogThread* watchdog_thread,
const gpu::GPUInfo* gpu_info,
const gpu::GpuPreferences& gpu_prefs) override {
TRACE_EVENT("gpu,startup", "gpu_main::EnsureSandboxInitialized");
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
return StartSandboxLinux(watchdog_thread, gpu_info, gpu_prefs);
#elif BUILDFLAG(IS_WIN)
return StartSandboxWindows(sandbox_info_);
#elif BUILDFLAG(IS_MAC)
return sandbox::Seatbelt::IsSandboxed();
#elif BUILDFLAG(IS_OHOS)
#if BUILDFLAG(ARKWEB_OOP_GPU_PROCESS)
return false;
#else
return StartSandboxOHOS(watchdog_thread);
#endif
#elif BUILDFLAG(IS_ANDROID)
if (base::FeatureList::IsEnabled(
sandbox::policy::features::kAndroidGpuSandbox)) {
return StartSandboxAndroid(watchdog_thread);
}
return false;
#else
return false;
#endif
}
#if BUILDFLAG(IS_WIN)
raw_ptr<const sandbox::SandboxInterfaceInfo> sandbox_info_ = nullptr;
#endif
};
}
int GpuMain(MainFunctionParams parameters) {
TRACE_EVENT("gpu,startup", "GpuMain");
base::CurrentProcess::GetInstance().SetProcessType(
base::CurrentProcessType::PROCESS_GPU);
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_preferences.FromSwitchValue(value);
CHECK(success);
}
mojo::SyncCallRestrictions::DisallowSyncCall();
if (gpu_preferences.gpu_startup_dialog)
WaitForDebugger("Gpu");
base::TimeTicks start_time = base::TimeTicks::Now();
#if BUILDFLAG(IS_WIN)
base::win::EnableHighDPISupport();
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX);
base::win::SetProcessTimerThrottleState(
base::GetCurrentProcessHandle(), base::win::ProcessPowerState::kDisabled);
base::win::ScopedCOMInitializer com_initializer(
base::win::ScopedCOMInitializer::kMTA);
::SetPriorityClass(::GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
#endif
viz::GpuLogMessageManager::GetInstance()->InstallPreInitializeLogHandler();
std::unique_ptr<base::SingleThreadTaskExecutor> main_thread_task_executor;
std::unique_ptr<ui::PlatformEventSource> event_source;
if (command_line.HasSwitch(switches::kHeadless)) {
#if BUILDFLAG(IS_MAC)
main_thread_task_executor =
std::make_unique<base::SingleThreadTaskExecutor>(
base::MessagePumpType::NS_RUNLOOP, true);
main_thread_task_executor->SetWorkBatchSize(2);
#else
main_thread_task_executor =
std::make_unique<base::SingleThreadTaskExecutor>(
base::MessagePumpType::DEFAULT, true);
#endif
} else {
#if BUILDFLAG(IS_WIN)
main_thread_task_executor =
std::make_unique<base::SingleThreadTaskExecutor>(
base::MessagePumpType::DEFAULT, true);
#elif BUILDFLAG(IS_OZONE)
if (!main_thread_task_executor) {
main_thread_task_executor =
std::make_unique<base::SingleThreadTaskExecutor>(
gpu_preferences.message_pump_type, true);
}
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#error "Unsupported Linux platform."
#elif BUILDFLAG(IS_MAC)
main_thread_task_executor =
std::make_unique<base::SingleThreadTaskExecutor>(
base::MessagePumpType::NS_RUNLOOP, true);
main_thread_task_executor->SetWorkBatchSize(2);
#else
main_thread_task_executor =
std::make_unique<base::SingleThreadTaskExecutor>(
base::MessagePumpType::DEFAULT, true);
#endif
}
base::PlatformThread::SetName("CrGpuMain");
mojo::InterfaceEndpointClient::SetThreadNameSuffixForMetrics("GpuMain");
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
SandboxedProcessThreadTypeHandler::Create();
#endif
base::PlatformThread::SetCurrentThreadType(
base::ThreadType::kDisplayCritical);
auto gpu_init = std::make_unique<gpu::GpuInit>();
ContentSandboxHelper sandbox_helper;
#if BUILDFLAG(IS_WIN)
sandbox_helper.set_sandbox_info(parameters.sandbox_info);
#endif
gpu_init->set_sandbox_helper(&sandbox_helper);
{
TRACE_EVENT("gpu,startup", "gpu_main::InitializeSkia");
InitializeSkia();
}
DCHECK(base::ThreadPoolInstance::Get());
const bool init_success = gpu_init->InitializeAndStartSandbox(
const_cast<base::CommandLine*>(&command_line), gpu_preferences);
#if BUILDFLAG(IS_CHROMEOS)
LOG(WARNING) << "gpu initialization completed init_success:" << init_success;
#endif
const bool dead_on_arrival = !init_success;
auto* client = GetContentClient()->gpu();
if (client) {
client->PostSandboxInitialized();
}
if (base::HangWatcher::IsEnabled() &&
!base::HangWatcher::GetInstance()->IsStarted()) {
DCHECK(parameters.hang_watcher_not_started_time.has_value());
base::TimeDelta uncovered_hang_watcher_time =
base::TimeTicks::Now() -
parameters.hang_watcher_not_started_time.value();
base::UmaHistogramTimes("HangWatcher.GpuProcess.UncoveredStartupTime",
uncovered_hang_watcher_time);
base::HangWatcher::GetInstance()->Start();
}
#if BUILDFLAG(IS_ANDROID)
base::PlatformThreadPriorityMonitor::Get().RegisterCurrentThread("GpuMain");
base::PlatformThreadPriorityMonitor::Get().Start();
#endif
if (parameters.needs_startup_tracing_after_sandbox_init) {
tracing::InitTracingPostFeatureList(false,
false);
}
GetContentClient()->SetGpuInfo(gpu_init->gpu_info());
base::ThreadType io_thread_type = base::ThreadType::kDisplayCritical;
ChildProcess gpu_process(io_thread_type);
DCHECK(base::ThreadPoolInstance::Get()->WasStarted());
if (client) {
client->PostIOThreadCreated(gpu_process.io_task_runner());
}
base::RunLoop run_loop;
GpuChildThread* child_thread =
new GpuChildThread(run_loop.QuitClosure(), std::move(gpu_init));
child_thread->Init(start_time, main_thread_task_executor->sequence_manager());
gpu_process.set_main_thread(child_thread);
#if BUILDFLAG(IS_MAC)
metal::RegisterGracefulExitOnDeviceRemoval();
#endif
#if BUILDFLAG(IS_ANDROID)
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
tracing::GraphicsMemoryDumpProvider::GetInstance(), "AndroidGraphics",
nullptr);
base::android::MeminfoDumpProvider::Initialize();
#endif
base::allocator::PartitionAllocSupport::Get()->ReconfigureAfterTaskRunnerInit(
switches::kGpuProcess);
#if BUILDFLAG(ARKWEB_PERFORMANCE_SCHEDULING)
retry_times = 0;
TryForReportThread();
#endif
base::HighResolutionTimerManager hi_res_timer_manager;
if (command_line.HasSwitch(switches::kEnableGpuMainTimeKeeperMetrics)) {
base::CurrentThread::Get()->EnableMessagePumpTimeKeeperMetrics(
"CrGpuMain",
true);
}
DCHECK(tracing::IsTracingInitialized());
{
TRACE_EVENT0("gpu", "Run Message Loop");
run_loop.Run();
}
return dead_on_arrival ? RESULT_CODE_GPU_DEAD_ON_ARRIVAL : 0;
}
namespace {
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
bool StartSandboxLinux(gpu::GpuWatchdogThread* watchdog_thread,
const gpu::GPUInfo* gpu_info,
const gpu::GpuPreferences& gpu_prefs) {
TRACE_EVENT("gpu,startup", "Initialize sandbox");
if (watchdog_thread) {
sandbox::policy::SandboxLinux::GetInstance()->StopThread(watchdog_thread);
}
base::Thread* trace_thread =
tracing::IsTracingInitialized()
? tracing::PerfettoTracedProcess::GetTraceThread()
: nullptr;
if (trace_thread) {
sandbox::policy::SandboxLinux::GetInstance()->StopThread(trace_thread);
}
sandbox::policy::SandboxLinux::Options sandbox_options;
if (gpu_info) {
sandbox_options.use_amd_specific_policies =
angle::IsAMD(gpu_info->active_gpu().vendor_id);
sandbox_options.use_intel_specific_policies =
angle::IsIntel(gpu_info->active_gpu().vendor_id);
sandbox_options.use_virtio_specific_policies =
angle::IsVirtIO(gpu_info->active_gpu().vendor_id);
sandbox_options.use_nvidia_specific_policies =
angle::IsNVIDIA(gpu_info->active_gpu().vendor_id);
for (const auto& gpu : gpu_info->secondary_gpus) {
if (angle::IsAMD(gpu.vendor_id))
sandbox_options.use_amd_specific_policies = true;
else if (angle::IsIntel(gpu.vendor_id))
sandbox_options.use_intel_specific_policies = true;
else if (angle::IsNVIDIA(gpu.vendor_id))
sandbox_options.use_nvidia_specific_policies = true;
}
}
sandbox_options.accelerated_video_decode_enabled =
!gpu_prefs.disable_accelerated_video_decode;
sandbox_options.accelerated_video_encode_enabled =
!gpu_prefs.disable_accelerated_video_encode;
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
const auto current_max_fds =
base::saturated_cast<unsigned int>(base::GetMaxFds());
constexpr unsigned int kMaxFDsDelta = 1u << 14;
const auto new_max_fds =
static_cast<unsigned int>(base::ClampMax(current_max_fds, kMaxFDsDelta));
base::IncreaseFdLimitTo(new_max_fds);
#endif
bool res = sandbox::policy::SandboxLinux::GetInstance()->InitializeSandbox(
sandbox::policy::SandboxTypeFromCommandLine(
*base::CommandLine::ForCurrentProcess()),
base::BindOnce(GpuPreSandboxHook), sandbox_options);
if (watchdog_thread) {
watchdog_thread->Start();
}
if (trace_thread) {
tracing::PerfettoTracedProcess::Get().RestartThreadInSandbox();
}
return res;
}
#endif
#if BUILDFLAG(IS_ANDROID)
bool StartSandboxAndroid(gpu::GpuWatchdogThread* watchdog_thread) {
if (watchdog_thread) {
base::ScopedFD proc_fd(
HANDLE_EINTR(open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC)));
sandbox::ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.get(),
watchdog_thread);
}
bool res = sandbox::landlock::ApplyLandlock(
sandbox::policy::SandboxTypeFromCommandLine(
*base::CommandLine::ForCurrentProcess()));
if (watchdog_thread) {
watchdog_thread->Start();
}
return res;
}
#endif
#if BUILDFLAG(IS_WIN)
bool StartSandboxWindows(const sandbox::SandboxInterfaceInfo* sandbox_info) {
TRACE_EVENT("gpu,startup", "Lower token");
const bool should_init_transport =
features::IsVizDirectCompositorThreadIpcNonRootEnabled() ||
features::IsVizDirectCompositorThreadIpcFrameSinkManagerEnabled();
if (should_init_transport) {
mojo::CreateDirectReceiverTransportBeforeSandbox();
}
sandbox::TargetServices* target_services = sandbox_info->target_services;
if (target_services) {
target_services->LowerToken();
return true;
}
return false;
}
#endif
#if BUILDFLAG(IS_OHOS)
bool StartSandboxOHOS(gpu::GpuWatchdogThread* watchdog_thread) {
TRACE_EVENT0("gpu,startup", "Initialize sandbox");
if (watchdog_thread) {
sandbox::policy::SandboxLinux::GetInstance()->StopThread(watchdog_thread);
}
sandbox::policy::SandboxLinux::Options sandbox_options;
bool res = sandbox::policy::SandboxLinux::GetInstance()->InitializeSandbox(
sandbox::policy::SandboxTypeFromCommandLine(
*base::CommandLine::ForCurrentProcess()),
base::BindOnce(GpuPreSandboxHook), sandbox_options);
if (watchdog_thread) {
watchdog_thread->Start();
}
return res;
}
#endif
}
}