-
Notifications
You must be signed in to change notification settings - Fork 270
/
Copy pathetwprof.cpp
406 lines (341 loc) · 12.8 KB
/
etwprof.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
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