More accurate implementation of watched thread time for Gpu Watchdog.
This change leverages an accurate implementation of ThreadTicks to allow GPU Watchdog thread report watched thread specific CPU time deltas to help with GPU hang crash dump analysis. This includes a small refactoring of ThreadTicks class that adds an overload of ThreadTicks::Now that accepts an arbitrary thread handle as an argument. That is limited to Windows OS only but if necessary we could extend it to other platforms. BUG=596190 Review URL: https://codereview.chromium.org/1910063003 Cr-Commit-Position: refs/heads/master@{#389210}
This commit is contained in:
@ -74,12 +74,12 @@
|
||||
// For FILETIME in FromFileTime, until it moves to a new converter class.
|
||||
// See TODO(iyengar) below.
|
||||
#include <windows.h>
|
||||
|
||||
#include "base/gtest_prod_util.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
class PlatformThreadHandle;
|
||||
class TimeDelta;
|
||||
|
||||
// The functions in the time_internal namespace are meant to be used only by the
|
||||
@ -749,11 +749,18 @@ class BASE_EXPORT ThreadTicks : public time_internal::TimeBase<ThreadTicks> {
|
||||
// absolutely needed, call WaitUntilInitialized() before this method.
|
||||
static ThreadTicks Now();
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Similar to Now() above except this returns thread-specific CPU time for an
|
||||
// arbitrary thread. All comments for Now() method above apply apply to this
|
||||
// method as well.
|
||||
static ThreadTicks GetForThread(const PlatformThreadHandle& thread_handle);
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class time_internal::TimeBase<ThreadTicks>;
|
||||
|
||||
// Please use Now() to create a new object. This is for internal use
|
||||
// and testing.
|
||||
// Please use Now() or GetForThread() to create a new object. This is for
|
||||
// internal use and testing.
|
||||
explicit ThreadTicks(int64_t us) : TimeBase(us) {}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "base/lazy_instance.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "base/threading/platform_thread.h"
|
||||
|
||||
using base::ThreadTicks;
|
||||
using base::Time;
|
||||
@ -98,16 +99,6 @@ uint32_t g_high_res_timer_count = 0;
|
||||
base::LazyInstance<base::Lock>::Leaky g_high_res_lock =
|
||||
LAZY_INSTANCE_INITIALIZER;
|
||||
|
||||
// Returns a pointer to the QueryThreadCycleTime() function from Windows.
|
||||
// Can't statically link to it because it is not available on XP.
|
||||
using QueryThreadCycleTimePtr = decltype(::QueryThreadCycleTime)*;
|
||||
QueryThreadCycleTimePtr GetQueryThreadCycleTimeFunction() {
|
||||
static const QueryThreadCycleTimePtr query_thread_cycle_time_fn =
|
||||
reinterpret_cast<QueryThreadCycleTimePtr>(::GetProcAddress(
|
||||
::GetModuleHandle(L"kernel32.dll"), "QueryThreadCycleTime"));
|
||||
return query_thread_cycle_time_fn;
|
||||
}
|
||||
|
||||
// Returns the current value of the performance counter.
|
||||
uint64_t QPCNowRaw() {
|
||||
LARGE_INTEGER perf_counter_now = {};
|
||||
@ -518,11 +509,17 @@ TimeTicks::Clock TimeTicks::GetClock() {
|
||||
|
||||
// static
|
||||
ThreadTicks ThreadTicks::Now() {
|
||||
return ThreadTicks::GetForThread(PlatformThread::CurrentHandle());
|
||||
}
|
||||
|
||||
// static
|
||||
ThreadTicks ThreadTicks::GetForThread(
|
||||
const base::PlatformThreadHandle& thread_handle) {
|
||||
DCHECK(IsSupported());
|
||||
|
||||
// Get the number of TSC ticks used by the current thread.
|
||||
ULONG64 thread_cycle_time = 0;
|
||||
GetQueryThreadCycleTimeFunction()(::GetCurrentThread(), &thread_cycle_time);
|
||||
::QueryThreadCycleTime(thread_handle.platform_handle(), &thread_cycle_time);
|
||||
|
||||
// Get the frequency of the TSC.
|
||||
double tsc_ticks_per_second = TSCTicksPerSecond();
|
||||
@ -537,8 +534,7 @@ ThreadTicks ThreadTicks::Now() {
|
||||
|
||||
// static
|
||||
bool ThreadTicks::IsSupportedWin() {
|
||||
static bool is_supported = GetQueryThreadCycleTimeFunction() &&
|
||||
base::CPU().has_non_stop_time_stamp_counter() &&
|
||||
static bool is_supported = base::CPU().has_non_stop_time_stamp_counter() &&
|
||||
!IsBuggyAthlon(base::CPU());
|
||||
return is_supported;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "base/power_monitor/power_monitor.h"
|
||||
#include "base/process/process.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/threading/platform_thread.h"
|
||||
#include "build/build_config.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "content/public/common/result_codes.h"
|
||||
@ -420,35 +421,40 @@ void GpuWatchdogThread::OnResume() {
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
base::TimeDelta GpuWatchdogThread::GetWatchedThreadTime() {
|
||||
FILETIME creation_time;
|
||||
FILETIME exit_time;
|
||||
FILETIME user_time;
|
||||
FILETIME kernel_time;
|
||||
BOOL result = GetThreadTimes(watched_thread_handle_,
|
||||
&creation_time,
|
||||
&exit_time,
|
||||
&kernel_time,
|
||||
&user_time);
|
||||
DCHECK(result);
|
||||
base::ThreadTicks GpuWatchdogThread::GetWatchedThreadTime() {
|
||||
if (base::ThreadTicks::IsSupported()) {
|
||||
// Convert ThreadTicks::Now() to TimeDelta.
|
||||
return base::ThreadTicks::GetForThread(
|
||||
base::PlatformThreadHandle(watched_thread_handle_));
|
||||
} else {
|
||||
// Use GetThreadTimes as a backup mechanism.
|
||||
FILETIME creation_time;
|
||||
FILETIME exit_time;
|
||||
FILETIME user_time;
|
||||
FILETIME kernel_time;
|
||||
BOOL result = GetThreadTimes(watched_thread_handle_, &creation_time,
|
||||
&exit_time, &kernel_time, &user_time);
|
||||
DCHECK(result);
|
||||
|
||||
ULARGE_INTEGER user_time64;
|
||||
user_time64.HighPart = user_time.dwHighDateTime;
|
||||
user_time64.LowPart = user_time.dwLowDateTime;
|
||||
ULARGE_INTEGER user_time64;
|
||||
user_time64.HighPart = user_time.dwHighDateTime;
|
||||
user_time64.LowPart = user_time.dwLowDateTime;
|
||||
|
||||
ULARGE_INTEGER kernel_time64;
|
||||
kernel_time64.HighPart = kernel_time.dwHighDateTime;
|
||||
kernel_time64.LowPart = kernel_time.dwLowDateTime;
|
||||
ULARGE_INTEGER kernel_time64;
|
||||
kernel_time64.HighPart = kernel_time.dwHighDateTime;
|
||||
kernel_time64.LowPart = kernel_time.dwLowDateTime;
|
||||
|
||||
// Time is reported in units of 100 nanoseconds. Kernel and user time are
|
||||
// summed to deal with to kinds of hangs. One is where the GPU process is
|
||||
// stuck in user level, never calling into the kernel and kernel time is
|
||||
// not increasing. The other is where either the kernel hangs and never
|
||||
// returns to user level or where user level code
|
||||
// calls into kernel level repeatedly, giving up its quanta before it is
|
||||
// tracked, for example a loop that repeatedly Sleeps.
|
||||
return base::TimeDelta::FromMilliseconds(static_cast<int64_t>(
|
||||
(user_time64.QuadPart + kernel_time64.QuadPart) / 10000));
|
||||
// Time is reported in units of 100 nanoseconds. Kernel and user time are
|
||||
// summed to deal with to kinds of hangs. One is where the GPU process is
|
||||
// stuck in user level, never calling into the kernel and kernel time is
|
||||
// not increasing. The other is where either the kernel hangs and never
|
||||
// returns to user level or where user level code
|
||||
// calls into kernel level repeatedly, giving up its quanta before it is
|
||||
// tracked, for example a loop that repeatedly Sleeps.
|
||||
return base::ThreadTicks() +
|
||||
base::TimeDelta::FromMilliseconds(static_cast<int64_t>(
|
||||
(user_time64.QuadPart + kernel_time64.QuadPart) / 10000));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -88,7 +88,7 @@ class GpuWatchdogThread : public base::Thread,
|
||||
void OnResume() override;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
base::TimeDelta GetWatchedThreadTime();
|
||||
base::ThreadTicks GetWatchedThreadTime();
|
||||
#endif
|
||||
|
||||
base::MessageLoop* watched_message_loop_;
|
||||
@ -106,7 +106,7 @@ class GpuWatchdogThread : public base::Thread,
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void* watched_thread_handle_;
|
||||
base::TimeDelta arm_cpu_time_;
|
||||
base::ThreadTicks arm_cpu_time_;
|
||||
|
||||
// This measures the time that the system has been running, in units of 100
|
||||
// ns.
|
||||
|
Reference in New Issue
Block a user