diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /tier0/etwprof.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'tier0/etwprof.cpp')
| -rw-r--r-- | tier0/etwprof.cpp | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/tier0/etwprof.cpp b/tier0/etwprof.cpp new file mode 100644 index 0000000..6f8a54c --- /dev/null +++ b/tier0/etwprof.cpp @@ -0,0 +1,406 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// ETW (Event Tracing for Windows) profiling helpers. +// This allows easy insertion of Generic Event markers into ETW/xperf tracing +// which then aids in analyzing the traces and finding performance problems. +// +//=============================================================================== + +#include "pch_tier0.h" +#include "tier0/etwprof.h" + +#ifdef ETW_MARKS_ENABLED + +#include <memory> + +// After building the DLL if it has never been registered on this machine or +// if the providers have changed you need to go: +// xcopy /y %vgame%\bin\tier0.dll %temp% +// wevtutil um %vgame%\..\src\tier0\ValveETWProvider.man +// wevtutil im %vgame%\..\src\tier0\ValveETWProvider.man + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +// These are defined in evntrace.h but you need a Vista+ Windows +// SDK to have them available, so I define them here. +#define EVENT_CONTROL_CODE_DISABLE_PROVIDER 0 +#define EVENT_CONTROL_CODE_ENABLE_PROVIDER 1 +#define EVENT_CONTROL_CODE_CAPTURE_STATE 2 + +// EVNTAPI is used in evntprov.h which is included by ValveETWProviderEvents.h +// We define EVNTAPI without the DECLSPEC_IMPORT specifier so that +// we can implement these functions locally instead of using the import library, +// and can therefore still run on Windows XP. +#define EVNTAPI __stdcall +// Include the event register/write/unregister macros compiled from the manifest file. +// Note that this includes evntprov.h which requires a Vista+ Windows SDK +// which we don't currently have, so evntprov.h is checked in. +#include "ValveETWProviderEvents.h" + +// Typedefs for use with GetProcAddress +typedef ULONG (__stdcall *tEventRegister)( LPCGUID ProviderId, PENABLECALLBACK EnableCallback, PVOID CallbackContext, PREGHANDLE RegHandle); +typedef ULONG (__stdcall *tEventWrite)( REGHANDLE RegHandle, PCEVENT_DESCRIPTOR EventDescriptor, ULONG UserDataCount, PEVENT_DATA_DESCRIPTOR UserData); +typedef ULONG (__stdcall *tEventUnregister)( REGHANDLE RegHandle ); + +// Helper class to dynamically load Advapi32.dll, find the ETW functions, +// register the providers if possible, and get the performance counter frequency. +class CETWRegister +{ +public: + CETWRegister() + { + QueryPerformanceFrequency( &m_frequency ); + + // Find Advapi32.dll. This should always succeed. + HMODULE pAdvapiDLL = LoadLibraryW( L"Advapi32.dll" ); + if ( pAdvapiDLL ) + { + // Try to find the ETW functions. This will fail on XP. + m_pEventRegister = ( tEventRegister )GetProcAddress( pAdvapiDLL, "EventRegister" ); + m_pEventWrite = ( tEventWrite )GetProcAddress( pAdvapiDLL, "EventWrite" ); + m_pEventUnregister = ( tEventUnregister )GetProcAddress( pAdvapiDLL, "EventUnregister" ); + + // Register two ETW providers. If registration fails then the event logging calls will fail. + // On XP these calls will do nothing. + // On Vista and above, if these providers have been enabled by xperf or logman then + // the VALVE_FRAMERATE_Context and VALVE_MAIN_Context globals will be modified + // like this: + // MatchAnyKeyword: 0xffffffffffffffff + // IsEnabled: 1 + // Level: 255 + // In other words, fully enabled. + + EventRegisterValve_FrameRate(); + EventRegisterValve_ServerFrameRate(); + EventRegisterValve_Main(); + EventRegisterValve_Input(); + EventRegisterValve_Network(); + + // Emit the thread ID for the main thread. This also indicates that + // the main provider is initialized. + EventWriteThread_ID( GetCurrentThreadId(), "Main thread" ); + // Emit an input system event so we know that it is active. + EventWriteKey_down( "Valve input provider initialized.", 0, 0 ); + } + } + ~CETWRegister() + { + // Unregister our providers. + EventUnregisterValve_Network(); + EventUnregisterValve_Input(); + EventUnregisterValve_Main(); + EventUnregisterValve_ServerFrameRate(); + EventUnregisterValve_FrameRate(); + } + + tEventRegister m_pEventRegister; + tEventWrite m_pEventWrite; + tEventUnregister m_pEventUnregister; + + // QPC frequency + LARGE_INTEGER m_frequency; + +} g_ETWRegister; + +// Redirector function for EventRegister. Called by macros in ValveETWProviderEvents.h +ULONG EVNTAPI EventRegister( LPCGUID ProviderId, PENABLECALLBACK EnableCallback, PVOID CallbackContext, PREGHANDLE RegHandle ) +{ + if ( g_ETWRegister.m_pEventRegister ) + return g_ETWRegister.m_pEventRegister( ProviderId, EnableCallback, CallbackContext, RegHandle ); + + // RegHandle is an _Out_ parameter and must always be initialized. + *RegHandle = 0; + return 0; +} + +// Redirector function for EventWrite. Called by macros in ValveETWProviderEvents.h +ULONG EVNTAPI EventWrite( REGHANDLE RegHandle, PCEVENT_DESCRIPTOR EventDescriptor, ULONG UserDataCount, PEVENT_DATA_DESCRIPTOR UserData ) +{ + if ( g_ETWRegister.m_pEventWrite ) + return g_ETWRegister.m_pEventWrite( RegHandle, EventDescriptor, UserDataCount, UserData ); + return 0; +} + +// Redirector function for EventUnregister. Called by macros in ValveETWProviderEvents.h +ULONG EVNTAPI EventUnregister( REGHANDLE RegHandle ) +{ + if ( g_ETWRegister.m_pEventUnregister ) + return g_ETWRegister.m_pEventUnregister( RegHandle ); + return 0; +} + +// Call QueryPerformanceCounter +static int64 GetQPCTime() +{ + LARGE_INTEGER time; + + QueryPerformanceCounter( &time ); + return time.QuadPart; +} + +// Convert a QueryPerformanceCounter delta into milliseconds +static float QPCToMS( int64 nDelta ) +{ + // Convert from a QPC delta to seconds. + float flSeconds = ( float )( nDelta / double( g_ETWRegister.m_frequency.QuadPart ) ); + + // Convert from seconds to milliseconds + return flSeconds * 1000; +} + +// Public functions for emitting ETW events. + +int64 ETWMark( const char *pMessage ) +{ + int64 nTime = GetQPCTime(); + EventWriteMark( pMessage ); + return nTime; +} + +void ETWMarkPrintf( const char *pMessage, ... ) +{ + // If we are running on Windows XP or if our providers have not been enabled + // (by xperf or other) then this will be false and we can early out. + // Be sure to check the appropriate context for the event. This is only + // worth checking if there is some cost beyond the EventWrite that we can + // avoid -- the redirectors in this file guarantee that EventWrite is always + // safe to call. + if ( !VALVE_MAIN_Context.IsEnabled ) + { + return; + } + + char buffer[1000]; + va_list args; + va_start( args, pMessage ); + vsprintf_s( buffer, pMessage, args ); + va_end( args ); + + EventWriteMark( buffer ); +} + +void ETWMark1F( const char *pMessage, float data1 ) +{ + EventWriteMark1F( pMessage, data1 ); +} + +void ETWMark2F( const char *pMessage, float data1, float data2 ) +{ + EventWriteMark2F( pMessage, data1, data2 ); +} + +void ETWMark3F( const char *pMessage, float data1, float data2, float data3 ) +{ + EventWriteMark3F( pMessage, data1, data2, data3 ); +} + +void ETWMark4F( const char *pMessage, float data1, float data2, float data3, float data4 ) +{ + EventWriteMark4F( pMessage, data1, data2, data3, data4 ); +} + +void ETWMark1I( const char *pMessage, int data1 ) +{ + EventWriteMark1I( pMessage, data1 ); +} + +void ETWMark2I( const char *pMessage, int data1, int data2 ) +{ + EventWriteMark2I( pMessage, data1, data2 ); +} + +void ETWMark3I( const char *pMessage, int data1, int data2, int data3 ) +{ + EventWriteMark3I( pMessage, data1, data2, data3 ); +} + +void ETWMark4I( const char *pMessage, int data1, int data2, int data3, int data4 ) +{ + EventWriteMark4I( pMessage, data1, data2, data3, data4 ); +} + +void ETWMark1S( const char *pMessage, const char* data1 ) +{ + EventWriteMark1S( pMessage, data1 ); +} + +void ETWMark2S( const char *pMessage, const char* data1, const char* data2 ) +{ + EventWriteMark2S( pMessage, data1, data2 ); +} + +// Track the depth of ETW Begin/End pairs. This needs to be per-thread +// if we start emitting marks on multiple threads. Using __declspec(thread) +// has some problems on Windows XP, but since these ETW functions only work +// on Vista+ that doesn't matter. +static __declspec( thread ) int s_nDepth; + +int64 ETWBegin( const char *pMessage ) +{ + // If we are running on Windows XP or if our providers have not been enabled + // (by xperf or other) then this will be false and we can early out. + // Be sure to check the appropriate context for the event. This is only + // worth checking if there is some cost beyond the EventWrite that we can + // avoid -- the redirectors in this file guarantee that EventWrite is always + // safe to call. + // In this case we also avoid the potentially unreliable TLS implementation + // (for dynamically loaded DLLs) on Windows XP. + if ( !VALVE_MAIN_Context.IsEnabled ) + { + return 0; + } + + int64 nTime = GetQPCTime(); + EventWriteStart( pMessage, s_nDepth++ ); + return nTime; +} + +int64 ETWEnd( const char *pMessage, int64 nStartTime ) +{ + // If we are running on Windows XP or if our providers have not been enabled + // (by xperf or other) then this will be false and we can early out. + // Be sure to check the appropriate context for the event. This is only + // worth checking if there is some cost beyond the EventWrite that we can + // avoid -- the redirectors in this file guarantee that EventWrite is always + // safe to call. + // In this case we also avoid the potentially unreliable TLS implementation + // (for dynamically loaded DLLs) on Windows XP. + if ( !VALVE_MAIN_Context.IsEnabled ) + { + return 0; + } + + int64 nTime = GetQPCTime(); + EventWriteStop( pMessage, --s_nDepth, QPCToMS( nTime - nStartTime ) ); + return nTime; +} + +// Record server and client frame counts separately, in case they are +// in the same process. +static int s_nRenderFrameCount[2]; + +int ETWGetRenderFrameNumber() +{ + return s_nRenderFrameCount[0]; +} + +// Insert a render frame marker using the Valve-FrameRate provider. Automatically +// count the frame number and frame time. +void ETWRenderFrameMark( bool bIsServerProcess ) +{ + Assert( bIsServerProcess == false || bIsServerProcess == true ); + // Record server and client frame counts separately, in case they are + // in the same process. + static int64 s_lastFrameTime[2]; + + int64 nCurrentFrameTime = GetQPCTime(); + float flElapsedFrameTime = 0.0f; + if ( s_nRenderFrameCount[bIsServerProcess] ) + { + flElapsedFrameTime = QPCToMS( nCurrentFrameTime - s_lastFrameTime[bIsServerProcess] ); + } + + if ( bIsServerProcess ) + { + EventWriteServerRenderFrameMark( s_nRenderFrameCount[bIsServerProcess], flElapsedFrameTime ); + } + else + { + EventWriteRenderFrameMark( s_nRenderFrameCount[bIsServerProcess], flElapsedFrameTime ); + } + + ++s_nRenderFrameCount[bIsServerProcess]; + s_lastFrameTime[bIsServerProcess] = nCurrentFrameTime; +} + +// Insert a simulation frame marker using the Valve-FrameRate provider. Automatically +// count the frame number and frame time. +void ETWSimFrameMark( bool bIsServerProcess ) +{ + Assert( bIsServerProcess == false || bIsServerProcess == true ); + // Record server and client frame counts separately, in case they are + // in the same process. + static int s_nFrameCount[2]; + static int64 s_lastFrameTime[2]; + + int64 nCurrentFrameTime = GetQPCTime(); + float flElapsedFrameTime = 0.0f; + if ( s_nFrameCount[bIsServerProcess] ) + { + flElapsedFrameTime = QPCToMS( nCurrentFrameTime - s_lastFrameTime[bIsServerProcess] ); + } + + if ( bIsServerProcess ) + { + EventWriteServerSimFrameMark( s_nFrameCount[bIsServerProcess], flElapsedFrameTime ); + } + else + { + EventWriteSimFrameMark( s_nFrameCount[bIsServerProcess], flElapsedFrameTime ); + } + + ++s_nFrameCount[bIsServerProcess]; + s_lastFrameTime[bIsServerProcess] = nCurrentFrameTime; +} + +void ETWMouseDown( int whichButton, int x, int y ) +{ + // Always have x/y first to make the summary tables easier to read. + EventWriteMouse_down( x, y, whichButton ); +} + +void ETWMouseUp( int whichButton, int x, int y ) +{ + // Always have x/y first to make the summary tables easier to read. + EventWriteMouse_up( x, y, whichButton ); +} + +void ETWMouseMove( int nX, int nY ) +{ + static int lastX, lastY; + + // Only emit mouse-move events if the mouse position has changed, since + // otherwise source2 emits a continous stream of events which makes it + // harder to find 'real' mouse-move events. + if ( lastX != nX || lastY != nY ) + { + lastX = nX; + lastY = nY; + // Always have x/y first to make the summary tables easier to read. + EventWriteMouse_Move( nX, nY ); + } +} + +void ETWMouseWheel( int nWheelDelta, int nX, int nY ) +{ + // Always have x/y first to make the summary tables easier to read. + EventWriteMouse_Wheel( nX, nY, nWheelDelta ); +} + +void ETWKeyDown( int nScanCode, int nVirtualCode, const char *pChar ) +{ + EventWriteKey_down( pChar, nScanCode, nVirtualCode ); +} + +void ETWSendPacket( const char *pTo, int nWireSize, int nOutSequenceNR, int nOutSequenceNrAck ) +{ + static int s_nCumulativeWireSize; + s_nCumulativeWireSize += nWireSize; + + EventWriteSendPacket( pTo, nWireSize, nOutSequenceNR, nOutSequenceNrAck, s_nCumulativeWireSize ); +} + +void ETWThrottled() +{ + EventWriteThrottled(); +} + +void ETWReadPacket( const char *pFrom, int nWireSize, int nInSequenceNR, int nOutSequenceNRAck ) +{ + static int s_nCumulativeWireSize; + s_nCumulativeWireSize += nWireSize; + + EventWriteReadPacket( pFrom, nWireSize, nInSequenceNR, nOutSequenceNRAck, s_nCumulativeWireSize ); +} + +#endif // ETW_MARKS_ENABLED |