summaryrefslogtreecommitdiff
path: root/tier0/etwprof.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /tier0/etwprof.cpp
downloadarchived-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.cpp406
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