refactoring ETW kernel session

This commit is contained in:
Marcos Slomp
2025-12-01 16:42:07 -08:00
parent 886a0abec9
commit 0adb0dbc57
2 changed files with 277 additions and 247 deletions

View File

@@ -52,8 +52,6 @@ static int GetSamplingPeriod()
# include <string.h>
# include <windows.h>
# include <dbghelp.h>
# include <evntrace.h>
# include <evntcons.h>
# include <psapi.h>
# include <winternl.h>
@@ -61,25 +59,13 @@ static int GetSamplingPeriod()
# include "../common/TracySystem.hpp"
# include "TracyProfiler.hpp"
# include "TracyThread.hpp"
# include "windows/TracyETW.cpp"
namespace tracy
{
static const GUID PerfInfoGuid = { 0xce1dbfb4, 0x137e, 0x4da6, { 0x87, 0xb0, 0x3f, 0x59, 0xaa, 0x10, 0x2c, 0xbc } };
static const GUID DxgKrnlGuid = { 0x802ec45a, 0x1e99, 0x4b83, { 0x99, 0x20, 0x87, 0xc9, 0x82, 0x77, 0xba, 0x9d } };
static const GUID ThreadV2Guid = { 0x3d6fa8d1, 0xfe05, 0x11d0, { 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c } };
static TRACEHANDLE s_traceHandle;
static TRACEHANDLE s_traceHandle2;
static EVENT_TRACE_PROPERTIES* s_prop;
static DWORD s_pid;
static EVENT_TRACE_PROPERTIES* s_propVsync;
static TRACEHANDLE s_traceHandleVsync;
static TRACEHANDLE s_traceHandleVsync2;
Thread* s_threadVsync = nullptr;
struct CSwitch
{
uint32_t newThreadId;
@@ -163,7 +149,7 @@ void WINAPI EventRecordCallback( PEVENT_RECORD record )
const auto& hdr = record->EventHeader;
switch( hdr.ProviderId.Data1 )
{
case 0x3d6fa8d1: // Thread Guid
case etw::ThreadGuid.Data1:
if( hdr.EventDescriptor.Opcode == 36 )
{
const auto cswitch = (const CSwitch*)record->UserData;
@@ -205,7 +191,7 @@ void WINAPI EventRecordCallback( PEVENT_RECORD record )
TracyLfqCommit;
}
break;
case 0xdef2fe46: // StackWalk Guid
case etw::StackWalkGuid.Data1:
if( hdr.EventDescriptor.Opcode == 32 )
{
const auto sw = (const StackWalkEvent*)record->UserData;
@@ -226,246 +212,53 @@ void WINAPI EventRecordCallback( PEVENT_RECORD record )
}
}
break;
case etw::DxgKrnlGuid.Data1:
assert( hdr.EventDescriptor.Id == 0x0011 );
{
const auto vs = (const VSyncInfo*)record->UserData;
TracyLfqPrepare( QueueType::FrameVsync );
MemWrite( &item->frameVsync.time, hdr.TimeStamp.QuadPart );
MemWrite( &item->frameVsync.id, vs->vidPnTargetId );
TracyLfqCommit;
}
break;
default:
break;
}
}
void WINAPI EventRecordCallbackVsync( PEVENT_RECORD record )
{
#ifdef TRACY_ON_DEMAND
if( !GetProfiler().IsConnected() ) return;
#endif
const auto& hdr = record->EventHeader;
// Check for Lost_Event (6a399ae0-4bc6-4de9-870b-3657f8947e7e)
if( hdr.ProviderId.Data1 == 0x6A399AE0 ) return;
assert( hdr.ProviderId.Data1 == 0x802EC45A );
assert( hdr.EventDescriptor.Id == 0x0011 );
const auto vs = (const VSyncInfo*)record->UserData;
TracyLfqPrepare( QueueType::FrameVsync );
MemWrite( &item->frameVsync.time, hdr.TimeStamp.QuadPart );
MemWrite( &item->frameVsync.id, vs->vidPnTargetId );
TracyLfqCommit;
}
static void SetupVsync()
{
#if _WIN32_WINNT >= _WIN32_WINNT_WINBLUE && !defined(__MINGW32__)
const auto psz = sizeof( EVENT_TRACE_PROPERTIES ) + MAX_PATH;
s_propVsync = (EVENT_TRACE_PROPERTIES*)tracy_malloc( psz );
memset( s_propVsync, 0, sizeof( EVENT_TRACE_PROPERTIES ) );
s_propVsync->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
s_propVsync->Wnode.BufferSize = psz;
#ifdef TRACY_TIMER_QPC
s_propVsync->Wnode.ClientContext = 1;
#else
s_propVsync->Wnode.ClientContext = 3;
#endif
s_propVsync->LoggerNameOffset = sizeof( EVENT_TRACE_PROPERTIES );
strcpy( ((char*)s_propVsync) + sizeof( EVENT_TRACE_PROPERTIES ), "TracyVsync" );
auto backup = tracy_malloc( psz );
memcpy( backup, s_propVsync, psz );
const auto controlStatus = ControlTraceA( 0, "TracyVsync", s_propVsync, EVENT_TRACE_CONTROL_STOP );
if( controlStatus != ERROR_SUCCESS && controlStatus != ERROR_WMI_INSTANCE_NOT_FOUND )
{
tracy_free( backup );
tracy_free( s_propVsync );
return;
}
memcpy( s_propVsync, backup, psz );
tracy_free( backup );
const auto startStatus = StartTraceA( &s_traceHandleVsync, "TracyVsync", s_propVsync );
if( startStatus != ERROR_SUCCESS )
{
tracy_free( s_propVsync );
return;
}
EVENT_FILTER_EVENT_ID fe = {};
fe.FilterIn = TRUE;
fe.Count = 1;
fe.Events[0] = 0x0011; // VSyncDPC_Info
EVENT_FILTER_DESCRIPTOR desc = {};
desc.Ptr = (ULONGLONG)&fe;
desc.Size = sizeof( fe );
desc.Type = EVENT_FILTER_TYPE_EVENT_ID;
ENABLE_TRACE_PARAMETERS params = {};
params.Version = ENABLE_TRACE_PARAMETERS_VERSION_2;
params.EnableProperty = EVENT_ENABLE_PROPERTY_IGNORE_KEYWORD_0;
params.SourceId = s_propVsync->Wnode.Guid;
params.EnableFilterDesc = &desc;
params.FilterDescCount = 1;
uint64_t mask = 0x4000000000000001; // Microsoft_Windows_DxgKrnl_Performance | Base
if( EnableTraceEx2( s_traceHandleVsync, &DxgKrnlGuid, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_INFORMATION, mask, mask, 0, &params ) != ERROR_SUCCESS )
{
tracy_free( s_propVsync );
return;
}
char loggerName[MAX_PATH];
strcpy( loggerName, "TracyVsync" );
EVENT_TRACE_LOGFILEA log = {};
log.LoggerName = loggerName;
log.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
log.EventRecordCallback = EventRecordCallbackVsync;
s_traceHandleVsync2 = OpenTraceA( &log );
if( s_traceHandleVsync2 == (TRACEHANDLE)INVALID_HANDLE_VALUE )
{
CloseTrace( s_traceHandleVsync );
tracy_free( s_propVsync );
return;
}
s_threadVsync = (Thread*)tracy_malloc( sizeof( Thread ) );
new(s_threadVsync) Thread( [] (void*) {
ThreadExitHandler threadExitHandler;
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL );
SetThreadName( "Tracy Vsync" );
ProcessTrace( &s_traceHandleVsync2, 1, nullptr, nullptr );
}, nullptr );
#endif
}
static int GetSamplingInterval()
{
return GetSamplingPeriod() / 100;
}
static etw::Session session = {};
bool SysTraceStart( int64_t& samplingPeriod )
{
if( !_GetThreadDescription ) _GetThreadDescription = (t_GetThreadDescription)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "GetThreadDescription" );
s_pid = GetCurrentProcessId();
#if defined _WIN64
constexpr bool isOs64Bit = true;
#else
BOOL _iswow64;
IsWow64Process( GetCurrentProcess(), &_iswow64 );
const bool isOs64Bit = _iswow64;
#endif
session = etw::StartPrivateKernelSession(TEXT("TracySysTrace"));
if (session.handle == 0)
return false;
TOKEN_PRIVILEGES priv = {};
priv.PrivilegeCount = 1;
priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if( LookupPrivilegeValue( nullptr, SE_SYSTEM_PROFILE_NAME, &priv.Privileges[0].Luid ) == 0 ) return false;
HANDLE pt;
if( OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &pt ) == 0 ) return false;
const auto adjust = AdjustTokenPrivileges( pt, FALSE, &priv, 0, nullptr, nullptr );
CloseHandle( pt );
if( adjust == 0 ) return false;
const auto status = GetLastError();
if( status != ERROR_SUCCESS ) return false;
if( isOs64Bit )
{
TRACE_PROFILE_INTERVAL interval = {};
interval.Interval = GetSamplingInterval();
const auto intervalStatus = TraceSetInformation( 0, TraceSampledProfileIntervalInfo, &interval, sizeof( interval ) );
if( intervalStatus != ERROR_SUCCESS ) return false;
samplingPeriod = GetSamplingPeriod();
}
const auto psz = sizeof( EVENT_TRACE_PROPERTIES ) + sizeof( KERNEL_LOGGER_NAME );
s_prop = (EVENT_TRACE_PROPERTIES*)tracy_malloc( psz );
memset( s_prop, 0, sizeof( EVENT_TRACE_PROPERTIES ) );
ULONG flags = 0;
#ifndef TRACY_NO_CONTEXT_SWITCH
flags = EVENT_TRACE_FLAG_CSWITCH | EVENT_TRACE_FLAG_DISPATCHER | EVENT_TRACE_FLAG_THREAD;
if (etw::EnableContextSwitchMonitoring(session) != ERROR_SUCCESS)
return etw::StopSession(session), false;
#endif
#ifndef TRACY_NO_SAMPLING
if( isOs64Bit ) flags |= EVENT_TRACE_FLAG_PROFILE;
#endif
s_prop->EnableFlags = flags;
s_prop->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
s_prop->Wnode.BufferSize = psz;
s_prop->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
#ifdef TRACY_TIMER_QPC
s_prop->Wnode.ClientContext = 1;
#else
s_prop->Wnode.ClientContext = 3;
#endif
s_prop->Wnode.Guid = SystemTraceControlGuid;
s_prop->BufferSize = 1024;
s_prop->MinimumBuffers = std::thread::hardware_concurrency() * 4;
s_prop->MaximumBuffers = std::thread::hardware_concurrency() * 6;
s_prop->LoggerNameOffset = sizeof( EVENT_TRACE_PROPERTIES );
memcpy( ((char*)s_prop) + sizeof( EVENT_TRACE_PROPERTIES ), KERNEL_LOGGER_NAME, sizeof( KERNEL_LOGGER_NAME ) );
auto backup = tracy_malloc( psz );
memcpy( backup, s_prop, psz );
const auto controlStatus = ControlTrace( 0, KERNEL_LOGGER_NAME, s_prop, EVENT_TRACE_CONTROL_STOP );
if( controlStatus != ERROR_SUCCESS && controlStatus != ERROR_WMI_INSTANCE_NOT_FOUND )
{
tracy_free( backup );
tracy_free( s_prop );
return false;
}
memcpy( s_prop, backup, psz );
tracy_free( backup );
const auto startStatus = StartTrace( &s_traceHandle, KERNEL_LOGGER_NAME, s_prop );
if( startStatus != ERROR_SUCCESS )
{
tracy_free( s_prop );
return false;
}
#ifndef TRACY_NO_SAMPLING
if( isOs64Bit )
{
CLASSIC_EVENT_ID stackId[2] = {};
stackId[0].EventGuid = PerfInfoGuid;
stackId[0].Type = 46;
stackId[1].EventGuid = ThreadV2Guid;
stackId[1].Type = 36;
const auto stackStatus = TraceSetInformation( s_traceHandle, TraceStackTracingInfo, &stackId, sizeof( stackId ) );
if( stackStatus != ERROR_SUCCESS )
{
tracy_free( s_prop );
return false;
}
}
int microseconds = GetSamplingInterval() / 10;
if (etw::EnableCPUProfiling(session, microseconds) != ERROR_SUCCESS)
return etw::StopSession(session), false;
samplingPeriod = GetSamplingPeriod();
#endif
#ifdef UNICODE
WCHAR KernelLoggerName[sizeof( KERNEL_LOGGER_NAME )];
#else
char KernelLoggerName[sizeof( KERNEL_LOGGER_NAME )];
#endif
memcpy( KernelLoggerName, KERNEL_LOGGER_NAME, sizeof( KERNEL_LOGGER_NAME ) );
EVENT_TRACE_LOGFILE log = {};
log.LoggerName = KernelLoggerName;
log.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
log.EventRecordCallback = EventRecordCallback;
s_traceHandle2 = OpenTrace( &log );
if( s_traceHandle2 == (TRACEHANDLE)INVALID_HANDLE_VALUE )
{
CloseTrace( s_traceHandle );
tracy_free( s_prop );
return false;
}
#ifndef TRACY_NO_VSYNC_CAPTURE
SetupVsync();
if (etw::EnableVSyncMonitoring(session) != ERROR_SUCCESS)
return etw::StopSession(session), false;
#endif
return true;
@@ -473,26 +266,15 @@ bool SysTraceStart( int64_t& samplingPeriod )
void SysTraceStop()
{
if( s_threadVsync )
{
CloseTrace( s_traceHandleVsync2 );
CloseTrace( s_traceHandleVsync );
s_threadVsync->~Thread();
tracy_free( s_threadVsync );
}
CloseTrace( s_traceHandle2 );
CloseTrace( s_traceHandle );
etw::StopSession(session);
}
void SysTraceWorker( void* ptr )
{
ThreadExitHandler threadExitHandler;
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL );
SetThreadName( "Tracy SysTrace" );
ProcessTrace( &s_traceHandle2, 1, 0, 0 );
ControlTrace( 0, KERNEL_LOGGER_NAME, s_prop, EVENT_TRACE_CONTROL_STOP );
tracy_free( s_prop );
SetThreadName( "Tracy SysTrace (ETW)" );
etw::EventConsumerLoop(session, EventRecordCallback);
}
void SysTraceGetExternalName( uint64_t thread, const char*& threadName, const char*& name )

View File

@@ -0,0 +1,248 @@
#include <windows.h>
#include <evntrace.h>
#include <evntcons.h>
#include <thread>
#include <stdio.h>
namespace tracy { namespace etw {
constexpr GUID NullGuid = {};
constexpr GUID ThreadGuid = { 0x3D6FA8D1, 0xFE05, 0x11D0, { 0x9D, 0xDA, 0x00, 0xC0, 0x4F, 0xD7, 0xBA, 0x7C } };
constexpr GUID PerfInfoGuid = { 0xCE1DBFB4, 0x137E, 0x4DA6, { 0x87, 0xB0, 0x3F, 0x59, 0xAA, 0x10, 0x2C, 0xBC } };
constexpr GUID StackWalkGuid = { 0xDEF2FE46, 0x7BD6, 0x4B80, { 0xBD, 0x94, 0xF5, 0x7F, 0xE2, 0x0D, 0x0C, 0xE3 } };
constexpr GUID DxgKrnlGuid = { 0x802EC45A, 0x1E99, 0x4B83, { 0x99, 0x20, 0x87, 0xC9, 0x82, 0x77, 0xBA, 0x9D } };
constexpr GUID LostEventGuid = { 0x6A399AE0, 0x4BC6, 0x4DE9, { 0x87, 0x0B, 0x36, 0x57, 0xF8, 0x94, 0x7E, 0x7E } };
struct Session {
EVENT_TRACE_PROPERTIES properties = {};
TCHAR name[64] = {};
CONTROLTRACE_ID handle = 0;
};
static ULONG ETWError(ULONG result) {
if (result == ERROR_SUCCESS)
return result;
ZoneScopedC(tracy::Color::Red4);
char message [128] = {};
size_t written = snprintf(message, sizeof(message), "ETW Error %u (0x%x): ", result, result);
written += FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
result,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&message[written],
sizeof(message)-written,
NULL
);
tracy::InitCallstackCritical();
TracyMessageCS(message, written, tracy::Color::Red4, 60);
#ifdef __cpp_exceptions
// TODO: should we throw an exception?
#endif
return result;
}
static DWORD ElevatePrivilege(LPCTSTR PrivilegeName) {
TOKEN_PRIVILEGES tp = {};
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (LookupPrivilegeValue(nullptr, PrivilegeName, &tp.Privileges[0].Luid) == FALSE) {
return ETWError(GetLastError());
}
HANDLE hToken = {};
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken) == FALSE) {
return ETWError(GetLastError());
}
BOOL adjusted = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, nullptr, nullptr);
DWORD status = GetLastError();
CloseHandle(hToken); // NOTE: skipping error handling for CloseHandle()
return ETWError(status);
}
static ULONG StartSession(Session& session) {
ULONG status = StartTrace(&session.handle, session.name, &session.properties);
if (status == ERROR_ALREADY_EXISTS) {
// Session is already running (likely from a previous run that did not terminate
// gracefully). There are two options: take control of the existing session with
// ControlSession(UPDATE), or stop the session and start fresh again. The latter
// is better because it also resets the event providers.
status = ControlTrace(session.handle, session.name, &session.properties, EVENT_TRACE_CONTROL_STOP);
if (status != ERROR_SUCCESS)
return ETWError(status);
status = StartTrace(&session.handle, session.name, &session.properties);
}
return ETWError(status);
}
static ULONG StopSession(Session& session) {
ULONG status = ControlTrace(session.handle, session.name, &session.properties, EVENT_TRACE_CONTROL_STOP);
if (status != ERROR_SUCCESS)
return ETWError(status);
// once stopped, the session handle becomes invalid
session.handle = 0;
return ERROR_SUCCESS;
}
static ULONG EnableProvider(
Session& session,
const GUID& ProviderId,
ULONG ControlCode = EVENT_CONTROL_CODE_ENABLE_PROVIDER,
UCHAR Level = TRACE_LEVEL_INFORMATION,
ULONGLONG MatchAnyKeyword = 0,
ULONGLONG MatchAllKeyword = 0,
ULONG Timeout = 0,
PENABLE_TRACE_PARAMETERS EnableParameters = NULL) {
ULONG status = EnableTraceEx2(session.handle, &ProviderId, ControlCode, Level, MatchAnyKeyword, MatchAllKeyword, Timeout, EnableParameters);
return ETWError(status);
}
static ULONG EnableStackWalk(Session& session, GUID EventGuid, UCHAR Opcode) {
#if defined _WIN64
constexpr bool isOs64Bit = true;
#else
BOOL _iswow64;
IsWow64Process( GetCurrentProcess(), &_iswow64 );
const bool isOs64Bit = _iswow64;
#endif
if (!isOs64Bit)
return 0 /* ERROR_SUCCESS */; // TODO: return error instead?
CLASSIC_EVENT_ID stackId[1] = {};
stackId[0].EventGuid = EventGuid;
stackId[0].Type = Opcode;
ULONG status = TraceSetInformation(session.handle, TraceStackTracingInfo, &stackId, sizeof(stackId));
return ETWError(status);
}
static Session StartPrivateKernelSession(const TCHAR* name) {
Session session = {};
size_t maxlen = std::size(session.name);
for (size_t i = 0; i < maxlen && name[i] != 0; ++i) {
session.name[i] = name[i];
}
auto& props = session.properties;
props.LoggerNameOffset = offsetof(Session, name);
props.Wnode.BufferSize = sizeof(Session);
props.Wnode.Guid = NullGuid;
#ifdef TRACY_TIMER_QPC
props.Wnode.ClientContext = 1; // 1: QueryPerformanceCounter
#else
props.Wnode.ClientContext = 3; // 3: CPU Ticks (e.g., rdtsc)
#endif
props.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
props.LogFileMode |= EVENT_TRACE_SYSTEM_LOGGER_MODE;
props.LogFileMode |= EVENT_TRACE_REAL_TIME_MODE;
// TODO: should we really be tweaking the buffering parameters?
props.BufferSize = 1024;
props.MinimumBuffers = std::thread::hardware_concurrency() * 4;
props.MaximumBuffers = std::thread::hardware_concurrency() * 6;
ULONG status = StartSession(session);
if (status != ERROR_SUCCESS)
return {};
return session;
}
static ULONG EnableCPUProfiling(Session& session, int microseconds = 125 /* 8KHz = 125us */) {
// CPU Profiling requires special privileges on top of admin privileges
DWORD access = ElevatePrivilege(SE_SYSTEM_PROFILE_NAME);
if (access != ERROR_SUCCESS)
return access;
ULONG status = EnableProvider(session, SystemProfileProviderGuid);
if (status != ERROR_SUCCESS)
return status;
TRACE_PROFILE_INTERVAL interval = {};
interval.Source = 0; // 0: ProfileTime
interval.Interval = (microseconds * 1000) / 100; // in 100's of nanoseconds
CONTROLTRACE_ID TraceId = 0; // must be zero for TraceSampledProfileIntervalInfo
status = TraceSetInformation(TraceId, TraceSampledProfileIntervalInfo, &interval, sizeof(interval));
if (status != ERROR_SUCCESS)
return status;
status = EnableStackWalk(session, PerfInfoGuid, 46); // PerfInfoGuid Opcode 46: SampledProfile event
return status;
}
static ULONG EnableContextSwitchMonitoring(Session& session) {
ULONGLONG MatchAnyKeyword = SYSTEM_SCHEDULER_KW_CONTEXT_SWITCH;
ULONG status = EnableProvider(session, SystemSchedulerProviderGuid,
EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_INFORMATION, MatchAnyKeyword);
if (status != ERROR_SUCCESS)
return status;
status = EnableStackWalk(session, ThreadGuid, 36); // ThreadGuid Opcode 36: CSwitch event
return status;
}
static ULONG EnableVSyncMonitoring(Session& session) {
// TODO: is this correct?
#if (_WIN32_WINNT < _WIN32_WINNT_WINBLUE) || defined(__MINGW32__)
return ETWError(ERROR_NOT_SUPPORTED);
#endif
enum Keyword : ULONGLONG {
DxgKrnlBase = 0x0000'0000'0000'0001, // Microsoft-Windows-DxgKrnl: Base
DxgKrnlPresent = 0x0000'0000'0800'0000, // Microsoft-Windows-DxgKrnl: Present
MSFTReserved62 = 0x4000'0000'0000'0000 // winmeta.h: WINEVENT_KEYWORD_RESERVED_62
};
ULONGLONG MatchAnyKeyword = Keyword::MSFTReserved62 | Keyword::DxgKrnlPresent | Keyword::DxgKrnlBase;
ULONGLONG MatchAllKeyword = MatchAnyKeyword;
EVENT_FILTER_EVENT_ID fe = {};
fe.FilterIn = TRUE;
fe.Count = 1;
fe.Events[0] = 0x0011; // 0x11 = 17 : VSyncDPC_Info
EVENT_FILTER_DESCRIPTOR desc = {};
desc.Ptr = (ULONGLONG)&fe;
desc.Size = sizeof(fe);
desc.Type = EVENT_FILTER_TYPE_EVENT_ID;
ENABLE_TRACE_PARAMETERS EnableParamaters = {};
EnableParamaters.Version = ENABLE_TRACE_PARAMETERS_VERSION_2;
EnableParamaters.EnableProperty = EVENT_ENABLE_PROPERTY_IGNORE_KEYWORD_0;
EnableParamaters.SourceId = DxgKrnlGuid; // or NullGuid? Does it even matter?
EnableParamaters.EnableFilterDesc = &desc;
EnableParamaters.FilterDescCount = 1;
ULONG status = EnableProvider(session, DxgKrnlGuid,
EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_INFORMATION,
MatchAnyKeyword, MatchAllKeyword, 0, &EnableParamaters);
return status;
}
static ULONG WINAPI OnBufferComplete(PEVENT_TRACE_LOGFILE Buffer) {
if (Buffer->EventsLost > 0) {
fprintf(stderr, "WARNING: %u events have been lost...\n", Buffer->EventsLost);
}
return TRUE; // or FALSE to break out of ProcessTrace()
}
static ULONG EventConsumerLoop(const Session& session, PEVENT_RECORD_CALLBACK callback = NULL)
{
EVENT_TRACE_LOGFILE trace = {};
trace.LoggerName = (LPTSTR)session.name;
trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME;
trace.ProcessTraceMode |= PROCESS_TRACE_MODE_EVENT_RECORD; // request EVENT_RECORD, not EVENT_TRACE (legacy)
trace.ProcessTraceMode |= PROCESS_TRACE_MODE_RAW_TIMESTAMP; // no timestamp conversions (use whatever the session is using)
trace.EventRecordCallback = callback;
trace.BufferCallback = OnBufferComplete;
PROCESSTRACE_HANDLE hConsumer = OpenTrace(&trace);
if (hConsumer == INVALID_PROCESSTRACE_HANDLE)
return ETWError(GetLastError());
ULONG status = ProcessTrace(&hConsumer, 1, NULL, NULL);
if (status != ERROR_SUCCESS && status != ERROR_CANCELLED)
return ETWError(status);
status = CloseTrace(hConsumer);
ETWError(status);
return status;
}
} }