From 73f6c66cdefc7c6bc5d11994e3559d4a70f1acc1 Mon Sep 17 00:00:00 2001 From: Daniil Brychikov Date: Fri, 11 Feb 2022 17:01:56 +0300 Subject: [PATCH 1/2] Automatic timer fallback --- client/TracyProfiler.cpp | 89 ++++++++++++++++++++++++++++------------ client/TracyProfiler.hpp | 55 ++++++++++++++++++++----- 2 files changed, 106 insertions(+), 38 deletions(-) diff --git a/client/TracyProfiler.cpp b/client/TracyProfiler.cpp index ff9ce9c7..73384eab 100644 --- a/client/TracyProfiler.cpp +++ b/client/TracyProfiler.cpp @@ -322,24 +322,50 @@ static void InitFailure( const char* msg ) exit( 1 ); } +static bool checkHardwareSupportsInvariantTSC() +{ + const char* noCheck = GetEnvVar( "TRACY_NO_INVARIANT_CHECK" ); + if( noCheck && noCheck[0] == '1' ) + { + return true; + } + + uint32_t regs[4]; + CpuId( regs, 1 ); + if( !( regs[3] & ( 1 << 4 ) ) ) + { +#if !defined TRACY_TIMER_QPC && !defined TRACY_TIMER_FALLBACK + InitFailure( "CPU doesn't support RDTSC instruction." ); +#endif + return false; + } + CpuId( regs, 0x80000007 ); + if( regs[3] & ( 1 << 8 ) ) + { + return true; + } + + return false; +} + +#if defined TRACY_TIMER_FALLBACK && defined TRACY_HW_TIMER +bool hardwareSupportsInvariantTSC() +{ + static bool cachedResult = checkHardwareSupportsInvariantTSC(); + return cachedResult; +} +#endif + static int64_t SetupHwTimer() { #if !defined TRACY_TIMER_QPC && !defined TRACY_TIMER_FALLBACK - uint32_t regs[4]; - CpuId( regs, 1 ); - if( !( regs[3] & ( 1 << 4 ) ) ) InitFailure( "CPU doesn't support RDTSC instruction." ); - CpuId( regs, 0x80000007 ); - if( !( regs[3] & ( 1 << 8 ) ) ) + if(!checkHardwareSupportsInvariantTSC()) { - const char* noCheck = GetEnvVar( "TRACY_NO_INVARIANT_CHECK" ); - if( !noCheck || noCheck[0] != '1' ) - { #if defined _WIN32 - InitFailure( "CPU doesn't support invariant TSC.\nDefine TRACY_NO_INVARIANT_CHECK=1 to ignore this error, *if you know what you are doing*.\nAlternatively you may rebuild the application with the TRACY_TIMER_QPC or TRACY_TIMER_FALLBACK define to use lower resolution timer." ); + InitFailure( "CPU doesn't support invariant TSC.\nDefine TRACY_NO_INVARIANT_CHECK=1 to ignore this error, *if you know what you are doing*.\nAlternatively you may rebuild the application with the TRACY_TIMER_QPC or TRACY_TIMER_FALLBACK define to use lower resolution timer." ); #else - InitFailure( "CPU doesn't support invariant TSC.\nDefine TRACY_NO_INVARIANT_CHECK=1 to ignore this error, *if you know what you are doing*.\nAlternatively you may rebuild the application with the TRACY_TIMER_FALLBACK define to use lower resolution timer." ); + InitFailure( "CPU doesn't support invariant TSC.\nDefine TRACY_NO_INVARIANT_CHECK=1 to ignore this error, *if you know what you are doing*.\nAlternatively you may rebuild the application with the TRACY_TIMER_FALLBACK define to use lower resolution timer." ); #endif - } } #endif @@ -3451,23 +3477,32 @@ void Profiler::HandleDisconnect() void Profiler::CalibrateTimer() { -#ifdef TRACY_HW_TIMER - std::atomic_signal_fence( std::memory_order_acq_rel ); - const auto t0 = std::chrono::high_resolution_clock::now(); - const auto r0 = GetTime(); - std::atomic_signal_fence( std::memory_order_acq_rel ); - std::this_thread::sleep_for( std::chrono::milliseconds( 200 ) ); - std::atomic_signal_fence( std::memory_order_acq_rel ); - const auto t1 = std::chrono::high_resolution_clock::now(); - const auto r1 = GetTime(); - std::atomic_signal_fence( std::memory_order_acq_rel ); - - const auto dt = std::chrono::duration_cast( t1 - t0 ).count(); - const auto dr = r1 - r0; - - m_timerMul = double( dt ) / double( dr ); -#else m_timerMul = 1.; + +#ifdef TRACY_HW_TIMER + +# if !defined TRACY_TIMER_QPC && defined TRACY_TIMER_FALLBACK + const bool needCalibration = hardwareSupportsInvariantTSC(); +# else + const bool needCalibration = true; +# endif + if (needCalibration) + { + std::atomic_signal_fence( std::memory_order_acq_rel ); + const auto t0 = std::chrono::high_resolution_clock::now(); + const auto r0 = GetTime(); + std::atomic_signal_fence( std::memory_order_acq_rel ); + std::this_thread::sleep_for( std::chrono::milliseconds( 200 ) ); + std::atomic_signal_fence( std::memory_order_acq_rel ); + const auto t1 = std::chrono::high_resolution_clock::now(); + const auto r1 = GetTime(); + std::atomic_signal_fence( std::memory_order_acq_rel ); + + const auto dt = std::chrono::duration_cast( t1 - t0 ).count(); + const auto dr = r1 - r0; + + m_timerMul = double( dt ) / double( dr ); + } #endif } diff --git a/client/TracyProfiler.hpp b/client/TracyProfiler.hpp index 005feb6e..ca50dd24 100644 --- a/client/TracyProfiler.hpp +++ b/client/TracyProfiler.hpp @@ -26,11 +26,11 @@ # include #endif -#if !defined TRACY_TIMER_FALLBACK && ( defined _WIN32 || ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) || ( defined TARGET_OS_IOS && TARGET_OS_IOS == 1 ) ) +#if ( defined _WIN32 || ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) || ( defined TARGET_OS_IOS && TARGET_OS_IOS == 1 ) ) # define TRACY_HW_TIMER #endif -#if !defined TRACY_HW_TIMER +#if defined TRACY_TIMER_FALLBACK || !defined TRACY_HW_TIMER # include #endif @@ -67,6 +67,23 @@ TRACY_API uint32_t GetThreadHandle(); TRACY_API bool ProfilerAvailable(); TRACY_API int64_t GetFrequencyQpc(); +#if defined TRACY_TIMER_FALLBACK && defined TRACY_HW_TIMER && ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) +TRACY_API bool hardwareSupportsInvariantTSC(); // check, if we need fallback scenario +#else +# if defined TRACY_HW_TIMER +tracy_force_inline bool hardwareSupportsInvariantTSC() +{ + return true; // this is checked at startup +} +# else +tracy_force_inline bool hardwareSupportsInvariantTSC() +{ + return false; +} +# endif +#endif + + struct SourceLocationData { const char* name; @@ -166,25 +183,39 @@ public: { #ifdef TRACY_HW_TIMER # if defined TARGET_OS_IOS && TARGET_OS_IOS == 1 - return mach_absolute_time(); + if (hardwareSupportsInvariantTSC()) + { + return mach_absolute_time(); + } # elif defined _WIN32 # ifdef TRACY_TIMER_QPC return GetTimeQpc(); # else - return int64_t( __rdtsc() ); + if (hardwareSupportsInvariantTSC()) + { + return int64_t( __rdtsc() ); + } # endif # elif defined __i386 || defined _M_IX86 - uint32_t eax, edx; - asm volatile ( "rdtsc" : "=a" (eax), "=d" (edx) ); - return ( uint64_t( edx ) << 32 ) + uint64_t( eax ); + if (hardwareSupportsInvariantTSC()) + { + uint32_t eax, edx; + asm volatile ( "rdtsc" : "=a" (eax), "=d" (edx) ); + return ( uint64_t( edx ) << 32 ) + uint64_t( eax ); + } # elif defined __x86_64__ || defined _M_X64 - uint64_t rax, rdx; - asm volatile ( "rdtsc" : "=a" (rax), "=d" (rdx) ); - return (int64_t)(( rdx << 32 ) + rax); + if (hardwareSupportsInvariantTSC()) + { + uint64_t rax, rdx; + asm volatile ( "rdtsc" : "=a" (rax), "=d" (rdx) ); + return (int64_t)(( rdx << 32 ) + rax); + } # else # error "TRACY_HW_TIMER detection logic needs fixing" # endif -#else +#endif + +#if !defined TRACY_HW_TIMER || defined TRACY_TIMER_FALLBACK # if defined __linux__ && defined CLOCK_MONOTONIC_RAW struct timespec ts; clock_gettime( CLOCK_MONOTONIC_RAW, &ts ); @@ -193,6 +224,8 @@ public: return std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); # endif #endif + + return 0; // unreacheble branch } tracy_force_inline uint32_t GetNextZoneId() From a9ba1ce6887c1cc09d5277700f421dcf78b56458 Mon Sep 17 00:00:00 2001 From: Daniil Brychikov Date: Sat, 19 Feb 2022 11:44:15 +0300 Subject: [PATCH 2/2] Codestyle fixes --- client/TracyProfiler.cpp | 22 ++++++++-------------- client/TracyProfiler.hpp | 20 +++++++------------- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/client/TracyProfiler.cpp b/client/TracyProfiler.cpp index 73384eab..25b9f27c 100644 --- a/client/TracyProfiler.cpp +++ b/client/TracyProfiler.cpp @@ -322,13 +322,10 @@ static void InitFailure( const char* msg ) exit( 1 ); } -static bool checkHardwareSupportsInvariantTSC() +static bool CheckHardwareSupportsInvariantTSC() { const char* noCheck = GetEnvVar( "TRACY_NO_INVARIANT_CHECK" ); - if( noCheck && noCheck[0] == '1' ) - { - return true; - } + if( noCheck && noCheck[0] == '1' ) return true; uint32_t regs[4]; CpuId( regs, 1 ); @@ -340,18 +337,15 @@ static bool checkHardwareSupportsInvariantTSC() return false; } CpuId( regs, 0x80000007 ); - if( regs[3] & ( 1 << 8 ) ) - { - return true; - } + if( regs[3] & ( 1 << 8 ) ) return true; return false; } #if defined TRACY_TIMER_FALLBACK && defined TRACY_HW_TIMER -bool hardwareSupportsInvariantTSC() +bool HardwareSupportsInvariantTSC() { - static bool cachedResult = checkHardwareSupportsInvariantTSC(); + static bool cachedResult = CheckHardwareSupportsInvariantTSC(); return cachedResult; } #endif @@ -359,7 +353,7 @@ bool hardwareSupportsInvariantTSC() static int64_t SetupHwTimer() { #if !defined TRACY_TIMER_QPC && !defined TRACY_TIMER_FALLBACK - if(!checkHardwareSupportsInvariantTSC()) + if( !CheckHardwareSupportsInvariantTSC() ) { #if defined _WIN32 InitFailure( "CPU doesn't support invariant TSC.\nDefine TRACY_NO_INVARIANT_CHECK=1 to ignore this error, *if you know what you are doing*.\nAlternatively you may rebuild the application with the TRACY_TIMER_QPC or TRACY_TIMER_FALLBACK define to use lower resolution timer." ); @@ -3482,11 +3476,11 @@ void Profiler::CalibrateTimer() #ifdef TRACY_HW_TIMER # if !defined TRACY_TIMER_QPC && defined TRACY_TIMER_FALLBACK - const bool needCalibration = hardwareSupportsInvariantTSC(); + const bool needCalibration = HardwareSupportsInvariantTSC(); # else const bool needCalibration = true; # endif - if (needCalibration) + if( needCalibration ) { std::atomic_signal_fence( std::memory_order_acq_rel ); const auto t0 = std::chrono::high_resolution_clock::now(); diff --git a/client/TracyProfiler.hpp b/client/TracyProfiler.hpp index ca50dd24..1e0b6c8d 100644 --- a/client/TracyProfiler.hpp +++ b/client/TracyProfiler.hpp @@ -68,15 +68,15 @@ TRACY_API bool ProfilerAvailable(); TRACY_API int64_t GetFrequencyQpc(); #if defined TRACY_TIMER_FALLBACK && defined TRACY_HW_TIMER && ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) -TRACY_API bool hardwareSupportsInvariantTSC(); // check, if we need fallback scenario +TRACY_API bool HardwareSupportsInvariantTSC(); // check, if we need fallback scenario #else # if defined TRACY_HW_TIMER -tracy_force_inline bool hardwareSupportsInvariantTSC() +tracy_force_inline bool HardwareSupportsInvariantTSC() { return true; // this is checked at startup } # else -tracy_force_inline bool hardwareSupportsInvariantTSC() +tracy_force_inline bool HardwareSupportsInvariantTSC() { return false; } @@ -183,28 +183,22 @@ public: { #ifdef TRACY_HW_TIMER # if defined TARGET_OS_IOS && TARGET_OS_IOS == 1 - if (hardwareSupportsInvariantTSC()) - { - return mach_absolute_time(); - } + if( HardwareSupportsInvariantTSC() ) return mach_absolute_time(); # elif defined _WIN32 # ifdef TRACY_TIMER_QPC return GetTimeQpc(); # else - if (hardwareSupportsInvariantTSC()) - { - return int64_t( __rdtsc() ); - } + if( HardwareSupportsInvariantTSC() ) return int64_t( __rdtsc() ); # endif # elif defined __i386 || defined _M_IX86 - if (hardwareSupportsInvariantTSC()) + if( HardwareSupportsInvariantTSC() ) { uint32_t eax, edx; asm volatile ( "rdtsc" : "=a" (eax), "=d" (edx) ); return ( uint64_t( edx ) << 32 ) + uint64_t( eax ); } # elif defined __x86_64__ || defined _M_X64 - if (hardwareSupportsInvariantTSC()) + if( HardwareSupportsInvariantTSC() ) { uint64_t rax, rdx; asm volatile ( "rdtsc" : "=a" (rax), "=d" (rdx) );