#include "gpu/ipc/service/gpu_watchdog_thread.h"
#include <memory>
#include <string>
#include <utility>
#include "base/bit_cast.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/debug/alias.h"
#include "base/debug/dump_without_crashing.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/native_library.h"
#include "base/numerics/safe_conversions.h"
#include "base/power_monitor/power_monitor.h"
#include "base/process/process.h"
#include "base/strings/string_number_conversions.h"
#include "base/system/sys_info.h"
#include "base/task/current_thread.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "gpu/config/gpu_crash_keys.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/config/gpu_switches.h"
#include "gpu/ipc/common/result_codes.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#endif
#if BUILDFLAG(IS_ARKWEB)
#include "arkweb/chromium_ext/gpu/ipc/service/gpu_hang_adapter.h"
#endif
namespace gpu {
base::TimeDelta GetGpuWatchdogTimeout(bool software_rendering) {
if (base::FeatureList::IsEnabled(features::kConfigurableGPUWatchdogTimeout)) {
int seconds = features::kConfigurableGPUWatchdogTimeoutSeconds.Get();
if (seconds > 0) {
return base::Seconds(seconds);
}
LOG(WARNING) << "Invalid GPU watchdog timeout seconds: " << seconds
<< ". Using default timeout.";
}
std::string timeout_str =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kGpuWatchdogTimeoutSeconds);
if (!timeout_str.empty()) {
size_t timeout_seconds;
if (base::StringToSizeT(timeout_str, &timeout_seconds))
return base::Seconds(timeout_seconds);
LOG(WARNING) << "Invalid --" << switches::kGpuWatchdogTimeoutSeconds << ": "
<< timeout_str;
}
base::TimeDelta timeout = kGpuWatchdogTimeout;
#if BUILDFLAG(IS_WIN)
int num_of_processors = base::SysInfo::NumberOfProcessors();
if (num_of_processors > 8) {
timeout -= base::Seconds(10);
} else if (num_of_processors <= 4) {
timeout += base::Seconds(5);
}
#endif
if (software_rendering) {
timeout *= kSoftwareRenderingFactor;
}
return timeout;
}
GpuWatchdogThread::GpuWatchdogThread(base::TimeDelta timeout,
int restart_factor,
bool is_test_mode,
const std::string& thread_name)
: base::Thread(thread_name),
watchdog_timeout_(timeout),
watchdog_restart_factor_(restart_factor),
is_test_mode_(is_test_mode) {
base::CurrentThread::Get()->AddTaskObserver(this);
if (thread_name == "GpuWatchdog_Compositor")
watched_thread_name_str_uma_ = ".compositor";
else
watched_thread_name_str_uma_ = ".main";
watched_thread_id_str_ =
base::NumberToString(base::PlatformThread::CurrentId().raw());
#if BUILDFLAG(IS_ARKWEB) && !defined(COMPONENT_BUILD)
bool gpumain_test = gpu::IsHangTestEnabled("gpu_main");
hang_test_ = gpumain_test && thread_name == "GpuWatchdog";
#endif
#if BUILDFLAG(IS_WIN)
if (!::DuplicateHandle(::GetCurrentProcess(), ::GetCurrentThread(),
::GetCurrentProcess(), &watched_thread_handle_,
THREAD_QUERY_INFORMATION, FALSE, 0)) {
watched_thread_handle_ = nullptr;
}
#endif
#if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
tty_file_.reset(base::OpenFile(
base::FilePath(FILE_PATH_LITERAL("/sys/class/tty/tty0/active")), "r"));
UpdateActiveTTY();
host_tty_ = active_tty_;
#endif
Arm();
}
GpuWatchdogThread::~GpuWatchdogThread() {
DCHECK_CALLED_ON_VALID_SEQUENCE(watched_thread_sequence_checker_);
if (IsArmed())
Disarm();
PauseWatchdog();
Stop();
base::CurrentThread::Get()->RemoveTaskObserver(this);
base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
GpuWatchdogThreadEventHistogram(GpuWatchdogThreadEvent::kGpuWatchdogEnd);
#if BUILDFLAG(IS_WIN)
if (watched_thread_handle_)
CloseHandle(watched_thread_handle_);
#endif
}
std::unique_ptr<GpuWatchdogThread> GpuWatchdogThread::Create(
bool start_backgrounded,
base::TimeDelta timeout,
int restart_factor,
bool is_test_mode,
const std::string& thread_name) {
auto watchdog_thread = base::WrapUnique(new GpuWatchdogThread(
timeout, restart_factor, is_test_mode, thread_name));
watchdog_thread->Start();
if (start_backgrounded)
watchdog_thread->OnBackgrounded();
return watchdog_thread;
}
std::unique_ptr<GpuWatchdogThread> GpuWatchdogThread::Create(
bool start_backgrounded,
bool software_rendering,
const std::string& thread_name) {
return Create(start_backgrounded, GetGpuWatchdogTimeout(software_rendering),
kRestartFactor, false, thread_name);
}
std::unique_ptr<GpuWatchdogThread> GpuWatchdogThread::Create(
bool start_backgrounded,
const GpuWatchdogThread* existing_watchdog,
const std::string& thread_name) {
DCHECK(existing_watchdog);
return Create(start_backgrounded, existing_watchdog->watchdog_timeout_,
existing_watchdog->watchdog_restart_factor_,
false, thread_name);
}
void GpuWatchdogThread::OnBackgrounded() {
InProgress();
task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&GpuWatchdogThread::StopWatchdogTimeoutTask,
base::Unretained(this), kAndroidBackgroundForeground));
}
void GpuWatchdogThread::OnForegrounded() {
task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&GpuWatchdogThread::RestartWatchdogTimeoutTask,
base::Unretained(this), kAndroidBackgroundForeground));
}
void GpuWatchdogThread::OnInitComplete() {
DCHECK_CALLED_ON_VALID_SEQUENCE(watched_thread_sequence_checker_);
task_runner()->PostTask(
FROM_HERE, base::BindOnce(&GpuWatchdogThread::UpdateInitializationFlag,
base::Unretained(this)));
Disarm();
task_runner()->PostTask(FROM_HERE,
base::BindOnce(&GpuWatchdogThread::AddPowerObserver,
base::Unretained(this)));
}
void GpuWatchdogThread::OnGpuProcessTearDown() {
DCHECK_CALLED_ON_VALID_SEQUENCE(watched_thread_sequence_checker_);
in_gpu_process_teardown_ = true;
if (!IsArmed())
Arm();
}
void GpuWatchdogThread::PauseWatchdog() {
DCHECK_CALLED_ON_VALID_SEQUENCE(watched_thread_sequence_checker_);
InProgress();
task_runner()->PostTask(
FROM_HERE, base::BindOnce(&GpuWatchdogThread::StopWatchdogTimeoutTask,
base::Unretained(this), kGeneralGpuFlow));
}
void GpuWatchdogThread::ResumeWatchdog() {
DCHECK_CALLED_ON_VALID_SEQUENCE(watched_thread_sequence_checker_);
task_runner()->PostTask(
FROM_HERE, base::BindOnce(&GpuWatchdogThread::RestartWatchdogTimeoutTask,
base::Unretained(this), kGeneralGpuFlow));
}
void GpuWatchdogThread::Init() {
weak_ptr_ = weak_factory_.GetWeakPtr();
task_runner()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&GpuWatchdogThread::OnWatchdogTimeout, weak_ptr_),
watchdog_timeout_);
last_arm_disarm_counter_ = ReadArmDisarmCounter();
watchdog_start_timeticks_ = base::TimeTicks::Now();
last_on_watchdog_timeout_timeticks_ = watchdog_start_timeticks_;
next_on_watchdog_timeout_time_ = base::Time::Now() + watchdog_timeout_;
in_gpu_initialization_ = true;
#if BUILDFLAG(IS_WIN)
if (watched_thread_handle_) {
if (base::ThreadTicks::IsSupported())
base::ThreadTicks::WaitUntilInitialized();
last_on_watchdog_timeout_thread_ticks_ = GetWatchedThreadTime();
remaining_watched_thread_ticks_ = watchdog_timeout_;
}
#endif
}
void GpuWatchdogThread::CleanUp() {
DCHECK(task_runner()->RunsTasksInCurrentSequence());
weak_factory_.InvalidateWeakPtrs();
}
void GpuWatchdogThread::ReportProgress() {
InProgress();
}
void GpuWatchdogThread::WillProcessTask(const base::PendingTask& pending_task,
bool was_blocked_or_low_priority) {
DCHECK_CALLED_ON_VALID_SEQUENCE(watched_thread_sequence_checker_);
if (in_gpu_process_teardown_)
DCHECK(IsArmed());
else
Arm();
#if BUILDFLAG(IS_ARKWEB)
static int32_t count = 0;
gpu::SimulateHangForTesting(count, hang_test_, "gpu_main");
#endif
}
void GpuWatchdogThread::DidProcessTask(const base::PendingTask& pending_task) {
DCHECK_CALLED_ON_VALID_SEQUENCE(watched_thread_sequence_checker_);
if (in_gpu_process_teardown_)
InProgress();
else
Disarm();
}
void GpuWatchdogThread::OnSuspend() {
DCHECK(task_runner()->RunsTasksInCurrentSequence());
InProgress();
StopWatchdogTimeoutTask(kPowerSuspendResume);
}
void GpuWatchdogThread::OnResume() {
DCHECK(task_runner()->RunsTasksInCurrentSequence());
RestartWatchdogTimeoutTask(kPowerSuspendResume);
}
void GpuWatchdogThread::AddPowerObserver() {
DCHECK(task_runner()->RunsTasksInCurrentSequence());
bool is_system_suspended =
base::PowerMonitor::GetInstance()
->AddPowerSuspendObserverAndReturnSuspendedState(this);
if (is_system_suspended)
StopWatchdogTimeoutTask(kPowerSuspendResume);
}
void GpuWatchdogThread::RestartWatchdogTimeoutTask(
PauseResumeSource source_of_request) {
DCHECK(task_runner()->RunsTasksInCurrentSequence());
base::TimeDelta timeout;
switch (source_of_request) {
case kAndroidBackgroundForeground:
if (!is_backgrounded_)
return;
is_backgrounded_ = false;
timeout = watchdog_timeout_ * watchdog_restart_factor_;
foregrounded_timeticks_ = base::TimeTicks::Now();
foregrounded_event_ = true;
num_of_timeout_after_foregrounded_ = 0;
break;
case kPowerSuspendResume:
if (!in_power_suspension_)
return;
in_power_suspension_ = false;
timeout = watchdog_timeout_ * watchdog_restart_factor_;
power_resume_timeticks_ = base::TimeTicks::Now();
power_resumed_event_ = true;
num_of_timeout_after_power_resume_ = 0;
break;
case kGeneralGpuFlow:
if (!is_paused_)
return;
is_paused_ = false;
timeout = watchdog_timeout_;
watchdog_resume_timeticks_ = base::TimeTicks::Now();
break;
}
if (!is_backgrounded_ && !in_power_suspension_ && !is_paused_) {
weak_ptr_ = weak_factory_.GetWeakPtr();
task_runner()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&GpuWatchdogThread::OnWatchdogTimeout, weak_ptr_),
timeout);
last_on_watchdog_timeout_timeticks_ = base::TimeTicks::Now();
next_on_watchdog_timeout_time_ = base::Time::Now() + timeout;
last_arm_disarm_counter_ = ReadArmDisarmCounter();
#if BUILDFLAG(IS_WIN)
if (watched_thread_handle_) {
last_on_watchdog_timeout_thread_ticks_ = GetWatchedThreadTime();
remaining_watched_thread_ticks_ = timeout;
}
#endif
}
}
void GpuWatchdogThread::StopWatchdogTimeoutTask(
PauseResumeSource source_of_request) {
DCHECK(task_runner()->RunsTasksInCurrentSequence());
switch (source_of_request) {
case kAndroidBackgroundForeground:
if (is_backgrounded_)
return;
is_backgrounded_ = true;
backgrounded_timeticks_ = base::TimeTicks::Now();
foregrounded_event_ = false;
break;
case kPowerSuspendResume:
if (in_power_suspension_)
return;
in_power_suspension_ = true;
power_suspend_timeticks_ = base::TimeTicks::Now();
power_resumed_event_ = false;
break;
case kGeneralGpuFlow:
if (is_paused_)
return;
is_paused_ = true;
watchdog_pause_timeticks_ = base::TimeTicks::Now();
break;
}
weak_factory_.InvalidateWeakPtrs();
}
void GpuWatchdogThread::UpdateInitializationFlag() {
DCHECK(task_runner()->RunsTasksInCurrentSequence());
in_gpu_initialization_ = false;
}
void GpuWatchdogThread::Arm() {
DCHECK_CALLED_ON_VALID_SEQUENCE(watched_thread_sequence_checker_);
arm_disarm_counter_.fetch_add(1, std::memory_order_relaxed);
DCHECK(IsArmed());
}
void GpuWatchdogThread::Disarm() {
DCHECK_CALLED_ON_VALID_SEQUENCE(watched_thread_sequence_checker_);
arm_disarm_counter_.fetch_add(1, std::memory_order_relaxed);
DCHECK(!IsArmed());
}
void GpuWatchdogThread::InProgress() {
arm_disarm_counter_.fetch_add(2, std::memory_order_relaxed);
}
bool GpuWatchdogThread::IsArmed() {
return arm_disarm_counter_.load(std::memory_order_relaxed) & 1;
}
int GpuWatchdogThread::ReadArmDisarmCounter() {
return arm_disarm_counter_.load(std::memory_order_relaxed);
}
void GpuWatchdogThread::OnWatchdogTimeout() {
DCHECK(task_runner()->RunsTasksInCurrentSequence());
DCHECK(!is_backgrounded_);
DCHECK(!in_power_suspension_);
DCHECK(!is_paused_);
if (!is_watchdog_start_histogram_recorded_) {
is_watchdog_start_histogram_recorded_ = true;
GpuWatchdogThreadEventHistogram(GpuWatchdogThreadEvent::kGpuWatchdogStart);
}
GpuWatchdogTimeoutHistogram(GpuWatchdogTimeoutEvent::kTimeout);
if (power_resumed_event_)
num_of_timeout_after_power_resume_++;
if (foregrounded_event_)
num_of_timeout_after_foregrounded_++;
#if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
UpdateActiveTTY();
#endif
int arm_disarm_counter = ReadArmDisarmCounter();
bool disarmed = arm_disarm_counter % 2 == 0;
bool gpu_makes_progress = arm_disarm_counter != last_arm_disarm_counter_;
bool no_gpu_hang = disarmed || gpu_makes_progress || SlowWatchdogThread();
bool watched_thread_needs_more_time =
WatchedThreadNeedsMoreThreadTime(no_gpu_hang);
no_gpu_hang = no_gpu_hang || watched_thread_needs_more_time ||
ContinueOnNonHostX11ServerTty();
if (no_gpu_hang) {
ContinueWithNextWatchdogTimeoutTask();
return;
}
TRACE_EVENT1("gpu,startup", "OnWatchdogTimeout", "timeoutMs",
watchdog_timeout_.InMilliseconds());
DeliberatelyTerminateToRecoverFromHang();
}
void GpuWatchdogThread::ContinueWithNextWatchdogTimeoutTask() {
last_on_watchdog_timeout_timeticks_ = base::TimeTicks::Now();
next_on_watchdog_timeout_time_ = base::Time::Now() + watchdog_timeout_;
last_arm_disarm_counter_ = ReadArmDisarmCounter();
task_runner()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&GpuWatchdogThread::OnWatchdogTimeout, weak_ptr_),
watchdog_timeout_);
}
bool GpuWatchdogThread::SlowWatchdogThread() {
bool slow_watchdog_thread =
(base::Time::Now() - next_on_watchdog_timeout_time_) >=
kUnreasonableTimeoutDelay;
if (slow_watchdog_thread)
GpuWatchdogTimeoutHistogram(GpuWatchdogTimeoutEvent::kSlowWatchdogThread);
return slow_watchdog_thread;
}
bool GpuWatchdogThread::WatchedThreadNeedsMoreThreadTime(
bool no_gpu_hang_detected) {
#if BUILDFLAG(IS_WIN)
if (!watched_thread_handle_)
return false;
WatchedThreadNeedsMoreThreadTimeHistogram(
no_gpu_hang_detected,
false);
if (!no_gpu_hang_detected && count_of_more_gpu_thread_time_allowed_ >=
kMaxCountOfMoreGpuThreadTimeAllowed) {
less_than_full_thread_time_after_capped_ = true;
} else {
less_than_full_thread_time_after_capped_ = false;
}
base::ThreadTicks now = GetWatchedThreadTime();
base::TimeDelta thread_time_elapsed =
now - last_on_watchdog_timeout_thread_ticks_;
last_on_watchdog_timeout_thread_ticks_ = now;
remaining_watched_thread_ticks_ -= thread_time_elapsed;
if (no_gpu_hang_detected ||
count_of_more_gpu_thread_time_allowed_ >=
kMaxCountOfMoreGpuThreadTimeAllowed ||
thread_time_elapsed.is_negative() ||
remaining_watched_thread_ticks_ <= base::TimeDelta()) {
remaining_watched_thread_ticks_ = watchdog_timeout_;
count_of_more_gpu_thread_time_allowed_ = 0;
return false;
} else {
if (count_of_more_gpu_thread_time_allowed_ == 0) {
WatchedThreadNeedsMoreThreadTimeHistogram(
no_gpu_hang_detected, true);
}
count_of_more_gpu_thread_time_allowed_++;
return true;
}
#else
return false;
#endif
}
#if BUILDFLAG(IS_WIN)
base::ThreadTicks GpuWatchdogThread::GetWatchedThreadTime() {
DCHECK(watched_thread_handle_);
if (base::ThreadTicks::IsSupported()) {
return base::ThreadTicks::GetForThread(
base::PlatformThreadHandle(watched_thread_handle_));
} else {
FILETIME creation_time;
FILETIME exit_time;
FILETIME kernel_time;
FILETIME user_time;
BOOL result = GetThreadTimes(watched_thread_handle_, &creation_time,
&exit_time, &kernel_time, &user_time);
if (!result)
return base::ThreadTicks();
int64_t user_time_us = base::bit_cast<int64_t, FILETIME>(user_time) / 10;
int64_t kernel_time_us =
base::bit_cast<int64_t, FILETIME>(kernel_time) / 10;
return base::ThreadTicks() +
base::Microseconds(user_time_us + kernel_time_us);
}
}
#endif
void GpuWatchdogThread::DeliberatelyTerminateToRecoverFromHang() {
DCHECK(task_runner()->RunsTasksInCurrentSequence());
if (is_test_mode_) {
test_result_timeout_and_gpu_hang_.Set();
return;
}
#if BUILDFLAG(IS_WIN)
if (IsDebuggerPresent())
return;
#endif
base::TimeTicks function_begin_timeticks = base::TimeTicks::Now();
base::debug::Alias(&in_gpu_initialization_);
base::debug::Alias(&num_of_timeout_after_power_resume_);
base::debug::Alias(&num_of_timeout_after_foregrounded_);
base::debug::Alias(&function_begin_timeticks);
base::debug::Alias(&watchdog_start_timeticks_);
base::debug::Alias(&power_suspend_timeticks_);
base::debug::Alias(&power_resume_timeticks_);
base::debug::Alias(&backgrounded_timeticks_);
base::debug::Alias(&foregrounded_timeticks_);
base::debug::Alias(&watchdog_pause_timeticks_);
base::debug::Alias(&watchdog_resume_timeticks_);
base::debug::Alias(&in_power_suspension_);
base::debug::Alias(&in_gpu_process_teardown_);
base::debug::Alias(&is_backgrounded_);
base::debug::Alias(&last_on_watchdog_timeout_timeticks_);
base::TimeDelta timeticks_elapses =
function_begin_timeticks - last_on_watchdog_timeout_timeticks_;
base::debug::Alias(&timeticks_elapses);
#if BUILDFLAG(IS_WIN)
base::debug::Alias(&remaining_watched_thread_ticks_);
base::debug::Alias(&less_than_full_thread_time_after_capped_);
#endif
crash_keys::list_of_hung_threads.Set(watched_thread_id_str_);
crash_keys::gpu_watchdog_crashed_in_gpu_init.Set(
in_gpu_initialization_ ? "1" : "0");
crash_keys::gpu_watchdog_kill_after_power_resume.Set(
WithinOneMinFromPowerResumed() ? "1" : "0");
const int num_of_processors = base::SysInfo::NumberOfProcessors();
crash_keys::num_of_processors.Set(base::NumberToString(num_of_processors));
crash_keys::gpu_thread.Set(watched_thread_name_str_uma_);
auto last_arm_disarm_counter = ReadArmDisarmCounter();
base::debug::Alias(&last_arm_disarm_counter);
#if BUILDFLAG(IS_ARKWEB)
LOG(ERROR) << "ArkWeb report gpu freeze";
#if !defined(COMPONENT_BUILD)
gpu::ReportGpuFreeze();
#endif
#else
base::debug::DumpWithoutCrashing();
#endif
GpuWatchdogTimeoutHistogram(GpuWatchdogTimeoutEvent::kKill);
bool gpu_hang = IsArmed();
if (gpu_hang) {
GpuWatchdogThreadEventHistogram(GpuWatchdogThreadEvent::kGpuWatchdogKill);
#if BUILDFLAG(IS_WIN)
if (less_than_full_thread_time_after_capped_)
GpuWatchdogTimeoutHistogram(
GpuWatchdogTimeoutEvent::kKillOnLessThreadTime);
#endif
base::Process::TerminateCurrentProcessImmediately(RESULT_CODE_HUNG);
} else {
crash_keys::list_of_hung_threads.Clear();
crash_keys::gpu_watchdog_crashed_in_gpu_init.Clear();
crash_keys::gpu_watchdog_kill_after_power_resume.Clear();
crash_keys::num_of_processors.Clear();
crash_keys::gpu_thread.Clear();
GpuWatchdogTimeoutHistogram(
GpuWatchdogTimeoutEvent::kNoKillForGpuProgressDuringCrashDumping);
#if BUILDFLAG(IS_WIN)
remaining_watched_thread_ticks_ = watchdog_timeout_;
count_of_more_gpu_thread_time_allowed_ = 0;
#endif
ContinueWithNextWatchdogTimeoutTask();
}
}
void GpuWatchdogThread::GpuWatchdogThreadEventHistogram(
GpuWatchdogThreadEvent thread_event) {
base::UmaHistogramEnumeration("GPU.WatchdogThread.Event", thread_event);
base::UmaHistogramEnumeration(
"GPU.WatchdogThread.Event" + watched_thread_name_str_uma_, thread_event);
}
void GpuWatchdogThread::GpuWatchdogTimeoutHistogram(
GpuWatchdogTimeoutEvent timeout_event) {
base::UmaHistogramEnumeration("GPU.WatchdogThread.Timeout", timeout_event);
base::UmaHistogramEnumeration(
"GPU.WatchdogThread.Timeout" + watched_thread_name_str_uma_,
timeout_event);
bool recorded = false;
if (in_gpu_initialization_) {
base::UmaHistogramEnumeration("GPU.WatchdogThread.Timeout.Init",
timeout_event);
base::UmaHistogramEnumeration(
"GPU.WatchdogThread.Timeout.Init" + watched_thread_name_str_uma_,
timeout_event);
recorded = true;
}
if (WithinOneMinFromPowerResumed()) {
base::UmaHistogramEnumeration("GPU.WatchdogThread.Timeout.PowerResume",
timeout_event);
base::UmaHistogramEnumeration(
"GPU.WatchdogThread.Timeout.PowerResume" + watched_thread_name_str_uma_,
timeout_event);
recorded = true;
}
if (WithinOneMinFromForegrounded()) {
base::UmaHistogramEnumeration("GPU.WatchdogThread.Timeout.Foregrounded",
timeout_event);
base::UmaHistogramEnumeration("GPU.WatchdogThread.Timeout.Foregrounded" +
watched_thread_name_str_uma_,
timeout_event);
recorded = true;
}
if (!recorded) {
base::UmaHistogramEnumeration("GPU.WatchdogThread.Timeout.Normal",
timeout_event);
base::UmaHistogramEnumeration(
"GPU.WatchdogThread.Timeout.Normal" + watched_thread_name_str_uma_,
timeout_event);
}
}
#if BUILDFLAG(IS_WIN)
void GpuWatchdogThread::WatchedThreadNeedsMoreThreadTimeHistogram(
bool no_gpu_hang_detected,
bool start_of_more_thread_time) {
if (start_of_more_thread_time) {
GpuWatchdogTimeoutHistogram(GpuWatchdogTimeoutEvent::kMoreThreadTime);
} else {
if (count_of_more_gpu_thread_time_allowed_ > 0) {
if (no_gpu_hang_detected) {
GpuWatchdogTimeoutHistogram(
GpuWatchdogTimeoutEvent::kProgressAfterMoreThreadTime);
} else if (count_of_more_gpu_thread_time_allowed_ >=
kMaxCountOfMoreGpuThreadTimeAllowed) {
GpuWatchdogTimeoutHistogram(
GpuWatchdogTimeoutEvent::kLessThanFullThreadTimeAfterCapped);
}
}
}
}
#endif
bool GpuWatchdogThread::WithinOneMinFromPowerResumed() {
size_t count = base::ClampFloor<size_t>(base::Minutes(1) / watchdog_timeout_);
return power_resumed_event_ && num_of_timeout_after_power_resume_ <= count;
}
bool GpuWatchdogThread::WithinOneMinFromForegrounded() {
size_t count = base::ClampFloor<size_t>(base::Minutes(1) / watchdog_timeout_);
return foregrounded_event_ && num_of_timeout_after_foregrounded_ <= count;
}
#if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
void GpuWatchdogThread::UpdateActiveTTY() {
last_active_tty_ = active_tty_;
active_tty_ = -1;
char tty_string[8] = {};
if (tty_file_ && !fseek(tty_file_.get(), 0, SEEK_SET) &&
UNSAFE_TODO(fread(tty_string, 1, 7, tty_file_.get()))) {
int tty_number;
if (UNSAFE_TODO(sscanf(tty_string, "tty%d\n", &tty_number)) == 1) {
active_tty_ = tty_number;
}
}
}
#endif
bool GpuWatchdogThread::ContinueOnNonHostX11ServerTty() {
#if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
if (host_tty_ == -1 || active_tty_ == -1)
return false;
if (active_tty_ != host_tty_) {
if (last_active_tty_ == active_tty_) {
GpuWatchdogTimeoutHistogram(
GpuWatchdogTimeoutEvent::kContinueOnNonHostServerTty);
}
return true;
}
#endif
return false;
}
bool GpuWatchdogThread::IsGpuHangDetectedForTesting() {
DCHECK(is_test_mode_);
return test_result_timeout_and_gpu_hang_.IsSet();
}
}