summaryrefslogtreecommitdiffstats
path: root/external/optick/optick_core.win.h
diff options
context:
space:
mode:
Diffstat (limited to 'external/optick/optick_core.win.h')
-rw-r--r--external/optick/optick_core.win.h1664
1 files changed, 1664 insertions, 0 deletions
diff --git a/external/optick/optick_core.win.h b/external/optick/optick_core.win.h
new file mode 100644
index 0000000..0d8a11a
--- /dev/null
+++ b/external/optick/optick_core.win.h
@@ -0,0 +1,1664 @@
+#pragma once
+#if defined(_MSC_VER)
+
+#include "optick.config.h"
+
+#if USE_OPTICK
+
+#include "optick_core.platform.h"
+
+namespace Optick
+{
+ const char* Platform::GetName()
+ {
+ #if defined(OPTICK_PC)
+ return "Windows";
+ #else
+ return "XBox";
+ #endif
+ }
+
+ ThreadID Platform::GetThreadID()
+ {
+ return GetCurrentThreadId();
+ }
+
+ ProcessID Platform::GetProcessID()
+ {
+ return GetCurrentProcessId();
+ }
+
+ int64 Platform::GetFrequency()
+ {
+ LARGE_INTEGER frequency;
+ QueryPerformanceFrequency(&frequency);
+ return frequency.QuadPart;
+ }
+
+ int64 Platform::GetTime()
+ {
+ LARGE_INTEGER largeInteger;
+ QueryPerformanceCounter(&largeInteger);
+ return largeInteger.QuadPart;
+ }
+}
+
+#if OPTICK_ENABLE_TRACING
+#include <psapi.h>
+#include "optick_core.h"
+
+/*
+Event Tracing Functions - API
+https://msdn.microsoft.com/en-us/library/windows/desktop/aa363795(v=vs.85).aspx
+*/
+
+#define DECLARE_ETW (!OPTICK_PC)
+
+#if DECLARE_ETW
+// Copied from Windows SDK
+#ifndef WMIAPI
+#ifndef MIDL_PASS
+#ifdef _WMI_SOURCE_
+#define WMIAPI __stdcall
+#else
+#define WMIAPI DECLSPEC_IMPORT __stdcall
+#endif // _WMI_SOURCE
+#endif // MIDL_PASS
+#endif // WMIAPI
+#define INITGUID
+#include <guiddef.h>
+#if defined(_NTDDK_) || defined(_NTIFS_) || defined(_WMIKM_)
+#define _EVNTRACE_KERNEL_MODE
+#endif
+#if !defined(_EVNTRACE_KERNEL_MODE)
+#include <wmistr.h>
+#endif
+
+#if _MSC_VER <= 1600
+#define EVENT_DESCRIPTOR_DEF
+#define EVENT_HEADER_DEF
+#define EVENT_HEADER_EXTENDED_DATA_ITEM_DEF
+#define EVENT_RECORD_DEF
+#endif
+
+#ifndef _TRACEHANDLE_DEFINED
+#define _TRACEHANDLE_DEFINED
+typedef ULONG64 TRACEHANDLE, *PTRACEHANDLE;
+#endif
+
+//
+// EventTraceGuid is used to identify a event tracing session
+//
+DEFINE_GUID( /* 68fdd900-4a3e-11d1-84f4-0000f80464e3 */
+ EventTraceGuid,
+ 0x68fdd900,
+ 0x4a3e,
+ 0x11d1,
+ 0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3
+);
+
+//
+// SystemTraceControlGuid. Used to specify event tracing for kernel
+//
+DEFINE_GUID( /* 9e814aad-3204-11d2-9a82-006008a86939 */
+ SystemTraceControlGuid,
+ 0x9e814aad,
+ 0x3204,
+ 0x11d2,
+ 0x9a, 0x82, 0x00, 0x60, 0x08, 0xa8, 0x69, 0x39
+);
+
+//
+// EventTraceConfigGuid. Used to report system configuration records
+//
+DEFINE_GUID( /* 01853a65-418f-4f36-aefc-dc0f1d2fd235 */
+ EventTraceConfigGuid,
+ 0x01853a65,
+ 0x418f,
+ 0x4f36,
+ 0xae, 0xfc, 0xdc, 0x0f, 0x1d, 0x2f, 0xd2, 0x35
+);
+
+//
+// DefaultTraceSecurityGuid. Specifies the default event tracing security
+//
+DEFINE_GUID( /* 0811c1af-7a07-4a06-82ed-869455cdf713 */
+ DefaultTraceSecurityGuid,
+ 0x0811c1af,
+ 0x7a07,
+ 0x4a06,
+ 0x82, 0xed, 0x86, 0x94, 0x55, 0xcd, 0xf7, 0x13
+);
+
+
+///////////////////////////////////////////////////////////////////////////////
+#define PROCESS_TRACE_MODE_REAL_TIME 0x00000100
+#define PROCESS_TRACE_MODE_RAW_TIMESTAMP 0x00001000
+#define PROCESS_TRACE_MODE_EVENT_RECORD 0x10000000
+///////////////////////////////////////////////////////////////////////////////
+#define EVENT_HEADER_FLAG_EXTENDED_INFO 0x0001
+#define EVENT_HEADER_FLAG_PRIVATE_SESSION 0x0002
+#define EVENT_HEADER_FLAG_STRING_ONLY 0x0004
+#define EVENT_HEADER_FLAG_TRACE_MESSAGE 0x0008
+#define EVENT_HEADER_FLAG_NO_CPUTIME 0x0010
+#define EVENT_HEADER_FLAG_32_BIT_HEADER 0x0020
+#define EVENT_HEADER_FLAG_64_BIT_HEADER 0x0040
+#define EVENT_HEADER_FLAG_CLASSIC_HEADER 0x0100
+#define EVENT_HEADER_FLAG_PROCESSOR_INDEX 0x0200
+///////////////////////////////////////////////////////////////////////////////
+#define KERNEL_LOGGER_NAMEW L"NT Kernel Logger"
+///////////////////////////////////////////////////////////////////////////////
+#define EVENT_TRACE_REAL_TIME_MODE 0x00000100 // Real time mode on
+///////////////////////////////////////////////////////////////////////////////
+#define EVENT_TRACE_CONTROL_STOP 1
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Enable flags for Kernel Events
+//
+#define EVENT_TRACE_FLAG_PROCESS 0x00000001 // process start & end
+#define EVENT_TRACE_FLAG_THREAD 0x00000002 // thread start & end
+#define EVENT_TRACE_FLAG_IMAGE_LOAD 0x00000004 // image load
+
+#define EVENT_TRACE_FLAG_DISK_IO 0x00000100 // physical disk IO
+#define EVENT_TRACE_FLAG_DISK_FILE_IO 0x00000200 // requires disk IO
+
+#define EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS 0x00001000 // all page faults
+#define EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS 0x00002000 // hard faults only
+
+#define EVENT_TRACE_FLAG_NETWORK_TCPIP 0x00010000 // tcpip send & receive
+
+#define EVENT_TRACE_FLAG_REGISTRY 0x00020000 // registry calls
+#define EVENT_TRACE_FLAG_DBGPRINT 0x00040000 // DbgPrint(ex) Calls
+
+//
+// Enable flags for Kernel Events on Vista and above
+//
+#define EVENT_TRACE_FLAG_PROCESS_COUNTERS 0x00000008 // process perf counters
+#define EVENT_TRACE_FLAG_CSWITCH 0x00000010 // context switches
+#define EVENT_TRACE_FLAG_DPC 0x00000020 // deffered procedure calls
+#define EVENT_TRACE_FLAG_INTERRUPT 0x00000040 // interrupts
+#define EVENT_TRACE_FLAG_SYSTEMCALL 0x00000080 // system calls
+
+#define EVENT_TRACE_FLAG_DISK_IO_INIT 0x00000400 // physical disk IO initiation
+#define EVENT_TRACE_FLAG_ALPC 0x00100000 // ALPC traces
+#define EVENT_TRACE_FLAG_SPLIT_IO 0x00200000 // split io traces (VolumeManager)
+
+#define EVENT_TRACE_FLAG_DRIVER 0x00800000 // driver delays
+#define EVENT_TRACE_FLAG_PROFILE 0x01000000 // sample based profiling
+#define EVENT_TRACE_FLAG_FILE_IO 0x02000000 // file IO
+#define EVENT_TRACE_FLAG_FILE_IO_INIT 0x04000000 // file IO initiation
+
+#define EVENT_TRACE_FLAG_PMC_PROFILE 0x80000000 // sample based profiling (PMC) - NOT CONFIRMED!
+
+//
+// Enable flags for Kernel Events on Win7 and above
+//
+#define EVENT_TRACE_FLAG_DISPATCHER 0x00000800 // scheduler (ReadyThread)
+#define EVENT_TRACE_FLAG_VIRTUAL_ALLOC 0x00004000 // VM operations
+
+//
+// Enable flags for Kernel Events on Win8 and above
+//
+#define EVENT_TRACE_FLAG_VAMAP 0x00008000 // map/unmap (excluding images)
+#define EVENT_TRACE_FLAG_NO_SYSCONFIG 0x10000000 // Do not do sys config rundown
+
+///////////////////////////////////////////////////////////////////////////////
+
+#pragma warning(push)
+#pragma warning (disable:4201)
+
+#ifndef EVENT_DESCRIPTOR_DEF
+#define EVENT_DESCRIPTOR_DEF
+typedef struct _EVENT_DESCRIPTOR {
+
+ USHORT Id;
+ UCHAR Version;
+ UCHAR Channel;
+ UCHAR Level;
+ UCHAR Opcode;
+ USHORT Task;
+ ULONGLONG Keyword;
+
+} EVENT_DESCRIPTOR, *PEVENT_DESCRIPTOR;
+typedef const EVENT_DESCRIPTOR *PCEVENT_DESCRIPTOR;
+#endif
+///////////////////////////////////////////////////////////////////////////////
+#ifndef EVENT_HEADER_DEF
+#define EVENT_HEADER_DEF
+typedef struct _EVENT_HEADER {
+
+ USHORT Size;
+ USHORT HeaderType;
+ USHORT Flags;
+ USHORT EventProperty;
+ ULONG ThreadId;
+ ULONG ProcessId;
+ LARGE_INTEGER TimeStamp;
+ GUID ProviderId;
+ EVENT_DESCRIPTOR EventDescriptor;
+ union {
+ struct {
+ ULONG KernelTime;
+ ULONG UserTime;
+ } DUMMYSTRUCTNAME;
+ ULONG64 ProcessorTime;
+
+ } DUMMYUNIONNAME;
+ GUID ActivityId;
+
+} EVENT_HEADER, *PEVENT_HEADER;
+#endif
+///////////////////////////////////////////////////////////////////////////////
+#ifndef EVENT_HEADER_EXTENDED_DATA_ITEM_DEF
+#define EVENT_HEADER_EXTENDED_DATA_ITEM_DEF
+typedef struct _EVENT_HEADER_EXTENDED_DATA_ITEM {
+
+ USHORT Reserved1; // Reserved for internal use
+ USHORT ExtType; // Extended info type
+ struct {
+ USHORT Linkage : 1; // Indicates additional extended
+ // data item
+ USHORT Reserved2 : 15;
+ };
+ USHORT DataSize; // Size of extended info data
+ ULONGLONG DataPtr; // Pointer to extended info data
+
+} EVENT_HEADER_EXTENDED_DATA_ITEM, *PEVENT_HEADER_EXTENDED_DATA_ITEM;
+#endif
+///////////////////////////////////////////////////////////////////////////////
+#ifndef ETW_BUFFER_CONTEXT_DEF
+#define ETW_BUFFER_CONTEXT_DEF
+typedef struct _ETW_BUFFER_CONTEXT {
+ union {
+ struct {
+ UCHAR ProcessorNumber;
+ UCHAR Alignment;
+ } DUMMYSTRUCTNAME;
+ USHORT ProcessorIndex;
+ } DUMMYUNIONNAME;
+ USHORT LoggerId;
+} ETW_BUFFER_CONTEXT, *PETW_BUFFER_CONTEXT;
+#endif
+///////////////////////////////////////////////////////////////////////////////
+#ifndef EVENT_RECORD_DEF
+#define EVENT_RECORD_DEF
+typedef struct _EVENT_RECORD {
+ EVENT_HEADER EventHeader;
+ ETW_BUFFER_CONTEXT BufferContext;
+ USHORT ExtendedDataCount;
+
+ USHORT UserDataLength;
+ PEVENT_HEADER_EXTENDED_DATA_ITEM ExtendedData;
+ PVOID UserData;
+ PVOID UserContext;
+} EVENT_RECORD, *PEVENT_RECORD;
+#endif
+///////////////////////////////////////////////////////////////////////////////
+typedef struct _EVENT_TRACE_PROPERTIES {
+ WNODE_HEADER Wnode;
+ //
+ // data provided by caller
+ ULONG BufferSize; // buffer size for logging (kbytes)
+ ULONG MinimumBuffers; // minimum to preallocate
+ ULONG MaximumBuffers; // maximum buffers allowed
+ ULONG MaximumFileSize; // maximum logfile size (in MBytes)
+ ULONG LogFileMode; // sequential, circular
+ ULONG FlushTimer; // buffer flush timer, in seconds
+ ULONG EnableFlags; // trace enable flags
+ union {
+ LONG AgeLimit; // unused
+ LONG FlushThreshold; // Number of buffers to fill before flushing
+ } DUMMYUNIONNAME;
+
+ // data returned to caller
+ ULONG NumberOfBuffers; // no of buffers in use
+ ULONG FreeBuffers; // no of buffers free
+ ULONG EventsLost; // event records lost
+ ULONG BuffersWritten; // no of buffers written to file
+ ULONG LogBuffersLost; // no of logfile write failures
+ ULONG RealTimeBuffersLost; // no of rt delivery failures
+ HANDLE LoggerThreadId; // thread id of Logger
+ ULONG LogFileNameOffset; // Offset to LogFileName
+ ULONG LoggerNameOffset; // Offset to LoggerName
+} EVENT_TRACE_PROPERTIES, *PEVENT_TRACE_PROPERTIES;
+
+typedef struct _EVENT_TRACE_HEADER { // overlays WNODE_HEADER
+ USHORT Size; // Size of entire record
+ union {
+ USHORT FieldTypeFlags; // Indicates valid fields
+ struct {
+ UCHAR HeaderType; // Header type - internal use only
+ UCHAR MarkerFlags; // Marker - internal use only
+ } DUMMYSTRUCTNAME;
+ } DUMMYUNIONNAME;
+ union {
+ ULONG Version;
+ struct {
+ UCHAR Type; // event type
+ UCHAR Level; // trace instrumentation level
+ USHORT Version; // version of trace record
+ } Class;
+ } DUMMYUNIONNAME2;
+ ULONG ThreadId; // Thread Id
+ ULONG ProcessId; // Process Id
+ LARGE_INTEGER TimeStamp; // time when event happens
+ union {
+ GUID Guid; // Guid that identifies event
+ ULONGLONG GuidPtr; // use with WNODE_FLAG_USE_GUID_PTR
+ } DUMMYUNIONNAME3;
+ union {
+ struct {
+ ULONG KernelTime; // Kernel Mode CPU ticks
+ ULONG UserTime; // User mode CPU ticks
+ } DUMMYSTRUCTNAME;
+ ULONG64 ProcessorTime; // Processor Clock
+ struct {
+ ULONG ClientContext; // Reserved
+ ULONG Flags; // Event Flags
+ } DUMMYSTRUCTNAME2;
+ } DUMMYUNIONNAME4;
+} EVENT_TRACE_HEADER, *PEVENT_TRACE_HEADER;
+
+typedef struct _EVENT_TRACE {
+ EVENT_TRACE_HEADER Header; // Event trace header
+ ULONG InstanceId; // Instance Id of this event
+ ULONG ParentInstanceId; // Parent Instance Id.
+ GUID ParentGuid; // Parent Guid;
+ PVOID MofData; // Pointer to Variable Data
+ ULONG MofLength; // Variable Datablock Length
+ union {
+ ULONG ClientContext;
+ ETW_BUFFER_CONTEXT BufferContext;
+ } DUMMYUNIONNAME;
+} EVENT_TRACE, *PEVENT_TRACE;
+
+typedef struct _TRACE_LOGFILE_HEADER {
+ ULONG BufferSize; // Logger buffer size in Kbytes
+ union {
+ ULONG Version; // Logger version
+ struct {
+ UCHAR MajorVersion;
+ UCHAR MinorVersion;
+ UCHAR SubVersion;
+ UCHAR SubMinorVersion;
+ } VersionDetail;
+ } DUMMYUNIONNAME;
+ ULONG ProviderVersion; // defaults to NT version
+ ULONG NumberOfProcessors; // Number of Processors
+ LARGE_INTEGER EndTime; // Time when logger stops
+ ULONG TimerResolution; // assumes timer is constant!!!
+ ULONG MaximumFileSize; // Maximum in Mbytes
+ ULONG LogFileMode; // specify logfile mode
+ ULONG BuffersWritten; // used to file start of Circular File
+ union {
+ GUID LogInstanceGuid; // For RealTime Buffer Delivery
+ struct {
+ ULONG StartBuffers; // Count of buffers written at start.
+ ULONG PointerSize; // Size of pointer type in bits
+ ULONG EventsLost; // Events losts during log session
+ ULONG CpuSpeedInMHz; // Cpu Speed in MHz
+ } DUMMYSTRUCTNAME;
+ } DUMMYUNIONNAME2;
+#if defined(_WMIKM_)
+ PWCHAR LoggerName;
+ PWCHAR LogFileName;
+ RTL_TIME_ZONE_INFORMATION TimeZone;
+#else
+ LPWSTR LoggerName;
+ LPWSTR LogFileName;
+ TIME_ZONE_INFORMATION TimeZone;
+#endif
+ LARGE_INTEGER BootTime;
+ LARGE_INTEGER PerfFreq; // Reserved
+ LARGE_INTEGER StartTime; // Reserved
+ ULONG ReservedFlags; // ClockType
+ ULONG BuffersLost;
+} TRACE_LOGFILE_HEADER, *PTRACE_LOGFILE_HEADER;
+
+typedef enum _TRACE_QUERY_INFO_CLASS {
+ TraceGuidQueryList,
+ TraceGuidQueryInfo,
+ TraceGuidQueryProcess,
+ TraceStackTracingInfo, // Win7
+ TraceSystemTraceEnableFlagsInfo,
+ TraceSampledProfileIntervalInfo,
+ TraceProfileSourceConfigInfo,
+ TraceProfileSourceListInfo,
+ TracePmcEventListInfo,
+ TracePmcCounterListInfo,
+ MaxTraceSetInfoClass
+} TRACE_QUERY_INFO_CLASS, TRACE_INFO_CLASS;
+
+typedef struct _CLASSIC_EVENT_ID {
+ GUID EventGuid;
+ UCHAR Type;
+ UCHAR Reserved[7];
+} CLASSIC_EVENT_ID, *PCLASSIC_EVENT_ID;
+
+typedef struct _TRACE_PROFILE_INTERVAL {
+ ULONG Source;
+ ULONG Interval;
+} TRACE_PROFILE_INTERVAL, *PTRACE_PROFILE_INTERVAL;
+
+typedef struct _EVENT_TRACE_LOGFILEW
+EVENT_TRACE_LOGFILEW, *PEVENT_TRACE_LOGFILEW;
+
+typedef ULONG(WINAPI * PEVENT_TRACE_BUFFER_CALLBACKW)
+(PEVENT_TRACE_LOGFILEW Logfile);
+
+typedef VOID(WINAPI *PEVENT_CALLBACK)(PEVENT_TRACE pEvent);
+
+typedef struct _EVENT_RECORD
+EVENT_RECORD, *PEVENT_RECORD;
+
+typedef VOID(WINAPI *PEVENT_RECORD_CALLBACK) (PEVENT_RECORD EventRecord);
+
+struct _EVENT_TRACE_LOGFILEW {
+ LPWSTR LogFileName; // Logfile Name
+ LPWSTR LoggerName; // LoggerName
+ LONGLONG CurrentTime; // timestamp of last event
+ ULONG BuffersRead; // buffers read to date
+ union {
+ // Mode of the logfile
+ ULONG LogFileMode;
+ // Processing flags used on Vista and above
+ ULONG ProcessTraceMode;
+ } DUMMYUNIONNAME;
+ EVENT_TRACE CurrentEvent; // Current Event from this stream.
+ TRACE_LOGFILE_HEADER LogfileHeader; // logfile header structure
+ PEVENT_TRACE_BUFFER_CALLBACKW // callback before each buffer
+ BufferCallback; // is read
+ //
+ // following variables are filled for BufferCallback.
+ //
+ ULONG BufferSize;
+ ULONG Filled;
+ ULONG EventsLost;
+ //
+ // following needs to be propaged to each buffer
+ //
+ union {
+ // Callback with EVENT_TRACE
+ PEVENT_CALLBACK EventCallback;
+ // Callback with EVENT_RECORD on Vista and above
+ PEVENT_RECORD_CALLBACK EventRecordCallback;
+ } DUMMYUNIONNAME2;
+
+ ULONG IsKernelTrace; // TRUE for kernel logfile
+
+ PVOID Context; // reserved for internal use
+};
+
+#pragma warning(pop)
+
+#define PEVENT_TRACE_BUFFER_CALLBACK PEVENT_TRACE_BUFFER_CALLBACKW
+#define EVENT_TRACE_LOGFILE EVENT_TRACE_LOGFILEW
+#define PEVENT_TRACE_LOGFILE PEVENT_TRACE_LOGFILEW
+#define KERNEL_LOGGER_NAME KERNEL_LOGGER_NAMEW
+#define GLOBAL_LOGGER_NAME GLOBAL_LOGGER_NAMEW
+#define EVENT_LOGGER_NAME EVENT_LOGGER_NAMEW
+
+EXTERN_C
+ULONG
+WMIAPI
+ProcessTrace(
+ _In_reads_(HandleCount) PTRACEHANDLE HandleArray,
+ _In_ ULONG HandleCount,
+ _In_opt_ LPFILETIME StartTime,
+ _In_opt_ LPFILETIME EndTime
+);
+
+EXTERN_C
+ULONG
+WMIAPI
+StartTraceW(
+ _Out_ PTRACEHANDLE TraceHandle,
+ _In_ LPCWSTR InstanceName,
+ _Inout_ PEVENT_TRACE_PROPERTIES Properties
+);
+
+EXTERN_C
+ULONG
+WMIAPI
+ControlTraceW(
+ _In_ TRACEHANDLE TraceHandle,
+ _In_opt_ LPCWSTR InstanceName,
+ _Inout_ PEVENT_TRACE_PROPERTIES Properties,
+ _In_ ULONG ControlCode
+);
+
+EXTERN_C
+TRACEHANDLE
+WMIAPI
+OpenTraceW(
+ _Inout_ PEVENT_TRACE_LOGFILEW Logfile
+);
+
+EXTERN_C
+ULONG
+WMIAPI
+CloseTrace(
+ _In_ TRACEHANDLE TraceHandle
+);
+
+EXTERN_C
+ULONG
+WMIAPI
+TraceSetInformation(
+ _In_ TRACEHANDLE SessionHandle,
+ _In_ TRACE_INFO_CLASS InformationClass,
+ _In_reads_bytes_(InformationLength) PVOID TraceInformation,
+ _In_ ULONG InformationLength
+);
+
+EXTERN_C
+ULONG
+WMIAPI
+TraceQueryInformation(
+ _In_ TRACEHANDLE SessionHandle,
+ _In_ TRACE_INFO_CLASS InformationClass,
+ _Out_writes_bytes_(InformationLength) PVOID TraceInformation,
+ _In_ ULONG InformationLength,
+ _Out_opt_ PULONG ReturnLength
+);
+
+//////////////////////////////////////////////////////////////////////////
+#define RegisterTraceGuids RegisterTraceGuidsW
+#define StartTrace StartTraceW
+#define ControlTrace ControlTraceW
+#define StopTrace StopTraceW
+#define QueryTrace QueryTraceW
+#define UpdateTrace UpdateTraceW
+#define FlushTrace FlushTraceW
+#define QueryAllTraces QueryAllTracesW
+#define OpenTrace OpenTraceW
+//////////////////////////////////////////////////////////////////////////
+#else
+#define INITGUID // Causes definition of SystemTraceControlGuid in evntrace.h.
+#include <wmistr.h>
+#include <evntrace.h>
+#include <strsafe.h>
+#include <evntcons.h>
+#endif //DECLARE_ETW
+
+namespace Optick
+{
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+class ETW : public Trace
+{
+ static const int ETW_BUFFER_SIZE = 1024 << 10; // 1Mb
+ static const int ETW_BUFFER_COUNT = 32;
+ static const int ETW_MAXIMUM_SESSION_NAME = 1024;
+
+ EVENT_TRACE_PROPERTIES *traceProperties;
+ EVENT_TRACE_LOGFILE logFile;
+ TRACEHANDLE traceSessionHandle;
+ TRACEHANDLE openedHandle;
+
+ HANDLE processThreadHandle;
+ DWORD currentProcessId;
+
+ bool isActive;
+
+ static DWORD WINAPI RunProcessTraceThreadFunction(LPVOID parameter);
+ static void AdjustPrivileges();
+
+ unordered_map<uint64_t, const EventDescription*> syscallDescriptions;
+
+ void ResolveSysCalls();
+public:
+
+ unordered_set<uint64> activeThreadsIDs;
+
+ ETW();
+ ~ETW();
+
+ virtual CaptureStatus::Type Start(Mode::Type mode, int frequency, const ThreadList& threads) override;
+ virtual bool Stop() override;
+
+ DWORD GetProcessID() const { return currentProcessId; }
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+struct CSwitch
+{
+ // New thread ID after the switch.
+ uint32 NewThreadId;
+
+ // Previous thread ID.
+ uint32 OldThreadId;
+
+ // Thread priority of the new thread.
+ int8 NewThreadPriority;
+
+ // Thread priority of the previous thread.
+ int8 OldThreadPriority;
+
+ //The index of the C-state that was last used by the processor. A value of 0 represents the lightest idle state with higher values representing deeper C-states.
+ uint8 PreviousCState;
+
+ // Not used.
+ int8 SpareByte;
+
+ // Wait reason for the previous thread. The following are the possible values:
+ // 0 Executive
+ // 1 FreePage
+ // 2 PageIn
+ // 3 PoolAllocation
+ // 4 DelayExecution
+ // 5 Suspended
+ // 6 UserRequest
+ // 7 WrExecutive
+ // 8 WrFreePage
+ // 9 WrPageIn
+ // 10 WrPoolAllocation
+ // 11 WrDelayExecution
+ // 12 WrSuspended
+ // 13 WrUserRequest
+ // 14 WrEventPair
+ // 15 WrQueue
+ // 16 WrLpcReceive
+ // 17 WrLpcReply
+ // 18 WrVirtualMemory
+ // 19 WrPageOut
+ // 20 WrRendezvous
+ // 21 WrKeyedEvent
+ // 22 WrTerminated
+ // 23 WrProcessInSwap
+ // 24 WrCpuRateControl
+ // 25 WrCalloutStack
+ // 26 WrKernel
+ // 27 WrResource
+ // 28 WrPushLock
+ // 29 WrMutex
+ // 30 WrQuantumEnd
+ // 31 WrDispatchInt
+ // 32 WrPreempted
+ // 33 WrYieldExecution
+ // 34 WrFastMutex
+ // 35 WrGuardedMutex
+ // 36 WrRundown
+ // 37 MaximumWaitReason
+ int8 OldThreadWaitReason;
+
+ // Wait mode for the previous thread. The following are the possible values:
+ // 0 KernelMode
+ // 1 UserMode
+ int8 OldThreadWaitMode;
+
+ // State of the previous thread. The following are the possible state values:
+ // 0 Initialized
+ // 1 Ready
+ // 2 Running
+ // 3 Standby
+ // 4 Terminated
+ // 5 Waiting
+ // 6 Transition
+ // 7 DeferredReady (added for Windows Server 2003)
+ int8 OldThreadState;
+
+ // Ideal wait time of the previous thread.
+ int8 OldThreadWaitIdealProcessor;
+
+ // Wait time for the new thread.
+ uint32 NewThreadWaitTime;
+
+ // Reserved.
+ uint32 Reserved;
+
+ static const byte OPCODE = 36;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+struct StackWalk_Event
+{
+ // Original event time stamp from the event header
+ uint64 EventTimeStamp;
+
+ // The process identifier of the original event
+ uint32 StackProcess;
+
+ // The thread identifier of the original event
+ uint32 StackThread;
+
+ // Callstack head
+ uint64 Stack0;
+
+ static const byte OPCODE = 32;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+struct Thread_TypeGroup1
+{
+ // Process identifier of the thread involved in the event.
+ uint32 ProcessId;
+ // Thread identifier of the thread involved in the event.
+ uint32 TThreadId;
+ // Base address of the thread's stack.
+ uint64 StackBase;
+ // Limit of the thread's stack.
+ uint64 StackLimit;
+ // Base address of the thread's user-mode stack.
+ uint64 UserStackBase;
+ // Limit of the thread's user-mode stack.
+ uint64 UserStackLimit;
+ // The set of processors on which the thread is allowed to run.
+ uint32 Affinity;
+ // Starting address of the function to be executed by this thread.
+ uint64 Win32StartAddr;
+ // Thread environment block base address.
+ uint64 TebBase;
+ // Identifies the service if the thread is owned by a service; otherwise, zero.
+ uint32 SubProcessTag;
+ // The scheduler priority of the thread
+ uint8 BasePriority;
+ // A memory page priority hint for memory pages accessed by the thread.
+ uint8 PagePriority;
+ // An IO priority hint for scheduling IOs generated by the thread.
+ uint8 IoPriority;
+ // Not used.
+ uint8 ThreadFlags;
+
+ enum struct Opcode : uint8
+ {
+ Start = 1,
+ End = 2,
+ DCStart = 3,
+ DCEnd = 4,
+ };
+};
+
+size_t GetSIDSize(uint8* ptr)
+{
+ size_t result = 0;
+
+ int sid = *((int*)ptr);
+
+ if (sid != 0)
+ {
+ size_t tokenSize = 16;
+ ptr += tokenSize;
+ result += tokenSize;
+ result += 8 + (4 * ((SID*)ptr)->SubAuthorityCount);
+ }
+ else
+ {
+ result = 4;
+ }
+
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// https://github.com/Microsoft/perfview/blob/688a8564062d51321bbab53cd71d9e174a77d2ce/src/TraceEvent/TraceEvent.cs
+struct Process_TypeGroup1
+{
+ // The address of the process object in the kernel.
+ uint64 UniqueProcessKey;
+ // Global process identifier that you can use to identify a process.
+ uint32 ProcessId;
+ // Unique identifier of the process that creates this process.
+ uint32 ParentId;
+ // Unique identifier that an operating system generates when it creates a new session.
+ uint32 SessionId;
+ // Exit status of the stopped process.
+ int32 ExitStatus;
+ // The physical address of the page table of the process.
+ uint64 DirectoryTableBase;
+ // (?) uint8 Flags;
+ // object UserSID;
+ // string ImageFileName;
+ // wstring CommandLine;
+
+ static size_t GetSIDOffset(PEVENT_RECORD pEvent)
+ {
+ if (pEvent->EventHeader.EventDescriptor.Version >= 4)
+ return 36;
+
+ if (pEvent->EventHeader.EventDescriptor.Version == 3)
+ return 32;
+
+ return 24;
+ }
+
+ const char* GetProcessName(PEVENT_RECORD pEvent) const
+ {
+ OPTICK_ASSERT((pEvent->EventHeader.Flags & EVENT_HEADER_FLAG_64_BIT_HEADER) != 0, "32-bit is not supported! Disable OPTICK_ENABLE_TRACING on 32-bit platform if needed!");
+ size_t sidOffset = GetSIDOffset(pEvent);
+ size_t sidSize = GetSIDSize((uint8*)this + sidOffset);
+ return (char*)this + sidOffset + sidSize;
+ }
+
+ enum struct Opcode
+ {
+ Start = 1,
+ End = 2,
+ DCStart = 3,
+ DCEnd = 4,
+ Defunct = 39,
+ };
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+struct SampledProfile
+{
+ uint32 InstructionPointer;
+ uint32 ThreadId;
+ uint32 Count;
+
+ static const byte OPCODE = 46;
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+struct SysCallEnter
+{
+ uintptr_t SysCallAddress;
+
+ static const byte OPCODE = 51;
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+struct SysCallExit
+{
+ uint32 SysCallNtStatus;
+
+ static const byte OPCODE = 52;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ce1dbfb4-137e-4da6-87b0-3f59aa102cbc
+DEFINE_GUID(SampledProfileGuid, 0xce1dbfb4, 0x137e, 0x4da6, 0x87, 0xb0, 0x3f, 0x59, 0xaa, 0x10, 0x2c, 0xbc);
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 3d6fa8d1-fe05-11d0-9dda-00c04fd7ba7c
+// https://docs.microsoft.com/en-us/windows/desktop/etw/thread
+DEFINE_GUID(ThreadGuid, 0x3d6fa8d1, 0xfe05, 0x11d0, 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c);
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 3d6fa8d0-fe05-11d0-9dda-00c04fd7ba7c
+// https://docs.microsoft.com/en-us/windows/desktop/etw/process
+DEFINE_GUID(ProcessGuid, 0x3d6fa8d0, 0xfe05, 0x11d0, 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c);
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const int MAX_CPU_CORES = 256;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+struct ETWRuntime
+{
+ array<ThreadID, MAX_CPU_CORES> activeCores;
+ vector<std::pair<uint8_t, SysCallData*>> activeSyscalls;
+
+ ETWRuntime()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ activeCores.fill(INVALID_THREAD_ID);
+ activeSyscalls.resize(0);;
+ }
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ETWRuntime g_ETWRuntime;
+ETW g_ETW;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void WINAPI OnRecordEvent(PEVENT_RECORD eventRecord)
+{
+ //static uint8 cpuCoreIsExecutingThreadFromOurProcess[256] = { 0 };
+
+ const byte opcode = eventRecord->EventHeader.EventDescriptor.Opcode;
+
+ if (opcode == CSwitch::OPCODE)
+ {
+ if (sizeof(CSwitch) == eventRecord->UserDataLength)
+ {
+ CSwitch* pSwitchEvent = (CSwitch*)eventRecord->UserData;
+
+ SwitchContextDesc desc;
+ desc.reason = pSwitchEvent->OldThreadWaitReason;
+ desc.cpuId = eventRecord->BufferContext.ProcessorNumber;
+ desc.oldThreadId = (uint64)pSwitchEvent->OldThreadId;
+ desc.newThreadId = (uint64)pSwitchEvent->NewThreadId;
+ desc.timestamp = eventRecord->EventHeader.TimeStamp.QuadPart;
+ Core::Get().ReportSwitchContext(desc);
+
+ // Assign ThreadID to the cores
+ if (g_ETW.activeThreadsIDs.find(desc.newThreadId) != g_ETW.activeThreadsIDs.end())
+ {
+ g_ETWRuntime.activeCores[desc.cpuId] = desc.newThreadId;
+ }
+ else if (g_ETW.activeThreadsIDs.find(desc.oldThreadId) != g_ETW.activeThreadsIDs.end())
+ {
+ g_ETWRuntime.activeCores[desc.cpuId] = INVALID_THREAD_ID;
+ }
+ }
+ }
+ else if (opcode == StackWalk_Event::OPCODE)
+ {
+ if (eventRecord->UserData && eventRecord->UserDataLength >= sizeof(StackWalk_Event))
+ {
+ //TODO: Support x86 windows kernels
+ const size_t osKernelPtrSize = sizeof(uint64);
+
+ StackWalk_Event* pStackWalkEvent = (StackWalk_Event*)eventRecord->UserData;
+ uint32 count = 1 + (eventRecord->UserDataLength - sizeof(StackWalk_Event)) / osKernelPtrSize;
+
+ if (count && pStackWalkEvent->StackThread != 0)
+ {
+ if (pStackWalkEvent->StackProcess == g_ETW.GetProcessID())
+ {
+ CallstackDesc desc;
+ desc.threadID = pStackWalkEvent->StackThread;
+ desc.timestamp = pStackWalkEvent->EventTimeStamp;
+
+ static_assert(osKernelPtrSize == sizeof(uint64), "Incompatible types!");
+ desc.callstack = &pStackWalkEvent->Stack0;
+
+ desc.count = (uint8)count;
+ Core::Get().ReportStackWalk(desc);
+ }
+ }
+ }
+ }
+ else if (opcode == SampledProfile::OPCODE)
+ {
+ SampledProfile* pEvent = (SampledProfile*)eventRecord->UserData;
+ OPTICK_UNUSED(pEvent);
+ }
+ else if (opcode == SysCallEnter::OPCODE)
+ {
+ if (eventRecord->UserDataLength >= sizeof(SysCallEnter))
+ {
+ uint8_t cpuId = eventRecord->BufferContext.ProcessorNumber;
+ uint64_t threadId = g_ETWRuntime.activeCores[cpuId];
+
+ if (threadId != INVALID_THREAD_ID)
+ {
+ SysCallEnter* pEventEnter = (SysCallEnter*)eventRecord->UserData;
+
+ SysCallData& sysCall = Core::Get().syscallCollector.Add();
+ sysCall.start = eventRecord->EventHeader.TimeStamp.QuadPart;
+ sysCall.finish = EventTime::INVALID_TIMESTAMP;
+ sysCall.threadID = threadId;
+ sysCall.id = pEventEnter->SysCallAddress;
+ sysCall.description = nullptr;
+
+ g_ETWRuntime.activeSyscalls.push_back(std::make_pair(cpuId, &sysCall));
+ }
+ }
+ }
+ else if (opcode == SysCallExit::OPCODE)
+ {
+ if (eventRecord->UserDataLength >= sizeof(SysCallExit))
+ {
+ uint8_t cpuId = eventRecord->BufferContext.ProcessorNumber;
+ if (g_ETWRuntime.activeCores[cpuId] != INVALID_THREAD_ID)
+ {
+ for (int i = (int)g_ETWRuntime.activeSyscalls.size() - 1; i >= 0; --i)
+ {
+ if (g_ETWRuntime.activeSyscalls[i].first == cpuId)
+ {
+ g_ETWRuntime.activeSyscalls[i].second->finish = eventRecord->EventHeader.TimeStamp.QuadPart;
+ g_ETWRuntime.activeSyscalls.erase(g_ETWRuntime.activeSyscalls.begin() + i);
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // VS TODO: We might have a situation where a thread was deleted and the new thread was created with the same threadID
+ // Ignoring for now - profiling sessions are quite short - not critical
+ if (IsEqualGUID(eventRecord->EventHeader.ProviderId, ThreadGuid))
+ {
+ if (eventRecord->UserDataLength >= sizeof(Thread_TypeGroup1))
+ {
+ const Thread_TypeGroup1* pThreadEvent = (const Thread_TypeGroup1*)eventRecord->UserData;
+ Core::Get().RegisterThreadDescription(ThreadDescription("", pThreadEvent->TThreadId, pThreadEvent->ProcessId, 1, pThreadEvent->BasePriority));
+ }
+
+ }
+ else if (IsEqualGUID(eventRecord->EventHeader.ProviderId, ProcessGuid))
+ {
+ if (eventRecord->UserDataLength >= sizeof(Process_TypeGroup1))
+ {
+ const Process_TypeGroup1* pProcessEvent = (const Process_TypeGroup1*)eventRecord->UserData;
+ Core::Get().RegisterProcessDescription(ProcessDescription(pProcessEvent->GetProcessName(eventRecord), pProcessEvent->ProcessId, pProcessEvent->UniqueProcessKey));
+ }
+ }
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static ULONG WINAPI OnBufferRecord(_In_ PEVENT_TRACE_LOGFILE Buffer)
+{
+ OPTICK_UNUSED(Buffer);
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const TRACEHANDLE INVALID_TRACEHANDLE = (TRACEHANDLE)-1;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+DWORD WINAPI ETW::RunProcessTraceThreadFunction(LPVOID parameter)
+{
+ Core::Get().RegisterThreadDescription(ThreadDescription("[Optick] ETW", GetCurrentThreadId(), GetCurrentProcessId()));
+ ETW* etw = (ETW*)parameter;
+ ULONG status = ProcessTrace(&etw->openedHandle, 1, 0, 0);
+ OPTICK_UNUSED(status);
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void ETW::AdjustPrivileges()
+{
+#if OPTICK_PC
+ HANDLE token = 0;
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
+ {
+ TOKEN_PRIVILEGES tokenPrivileges;
+ memset(&tokenPrivileges, 0, sizeof(tokenPrivileges));
+ tokenPrivileges.PrivilegeCount = 1;
+ tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ LookupPrivilegeValue(NULL, SE_SYSTEM_PROFILE_NAME, &tokenPrivileges.Privileges[0].Luid);
+
+ AdjustTokenPrivileges(token, FALSE, &tokenPrivileges, 0, (PTOKEN_PRIVILEGES)NULL, 0);
+ CloseHandle(token);
+ }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void ETW::ResolveSysCalls()
+{
+ if (SymbolEngine* symEngine = Platform::GetSymbolEngine())
+ {
+ Core::Get().syscallCollector.syscallPool.ForEach([this, symEngine](SysCallData& data)
+ {
+ auto it = syscallDescriptions.find(data.id);
+ if (it == syscallDescriptions.end())
+ {
+ const Symbol* symbol = symEngine->GetSymbol(data.id);
+ if (symbol != nullptr)
+ {
+ string name(symbol->function.begin(), symbol->function.end());
+
+ data.description = EventDescription::CreateShared(name.c_str(), "SysCall", (long)data.id);
+ syscallDescriptions.insert(std::pair<const uint64_t, const EventDescription *>(data.id, data.description));
+ }
+ }
+ else
+ {
+ data.description = it->second;
+ }
+ });
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ETW::ETW()
+ : isActive(false)
+ , traceSessionHandle(INVALID_TRACEHANDLE)
+ , openedHandle(INVALID_TRACEHANDLE)
+ , processThreadHandle(INVALID_HANDLE_VALUE)
+ , traceProperties(nullptr)
+{
+ currentProcessId = GetCurrentProcessId();
+}
+
+CaptureStatus::Type ETW::Start(Mode::Type mode, int frequency, const ThreadList& threads)
+{
+ if (!isActive)
+ {
+ AdjustPrivileges();
+
+ g_ETWRuntime.Reset();
+
+ activeThreadsIDs.clear();
+ for (auto it = threads.begin(); it != threads.end(); ++it)
+ {
+ ThreadEntry* entry = *it;
+ if (entry->isAlive)
+ {
+ activeThreadsIDs.insert(entry->description.threadID);
+ }
+ }
+
+
+ ULONG bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + (ETW_MAXIMUM_SESSION_NAME + MAX_PATH) * sizeof(WCHAR);
+ if (traceProperties == nullptr)
+ traceProperties = (EVENT_TRACE_PROPERTIES*)Memory::Alloc(bufferSize);
+ ZeroMemory(traceProperties, bufferSize);
+ traceProperties->Wnode.BufferSize = bufferSize;
+ traceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
+ StringCchCopyW((LPWSTR)((PCHAR)traceProperties + traceProperties->LoggerNameOffset), ETW_MAXIMUM_SESSION_NAME, KERNEL_LOGGER_NAMEW);
+ traceProperties->EnableFlags = 0;
+
+ traceProperties->BufferSize = ETW_BUFFER_SIZE;
+ traceProperties->MinimumBuffers = ETW_BUFFER_COUNT;
+
+ if (mode & Mode::SWITCH_CONTEXT)
+ {
+ traceProperties->EnableFlags |= EVENT_TRACE_FLAG_CSWITCH;
+ }
+
+ if (mode & Mode::AUTOSAMPLING)
+ {
+ traceProperties->EnableFlags |= EVENT_TRACE_FLAG_PROFILE;
+ }
+
+ if (mode & Mode::SYS_CALLS)
+ {
+ traceProperties->EnableFlags |= EVENT_TRACE_FLAG_SYSTEMCALL;
+ }
+
+ if (mode & Mode::OTHER_PROCESSES)
+ {
+ traceProperties->EnableFlags |= EVENT_TRACE_FLAG_PROCESS;
+ traceProperties->EnableFlags |= EVENT_TRACE_FLAG_THREAD;
+ }
+
+ traceProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
+ traceProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
+ //
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364160(v=vs.85).aspx
+ // Clock resolution = QPC
+ traceProperties->Wnode.ClientContext = 1;
+ traceProperties->Wnode.Guid = SystemTraceControlGuid;
+
+ // ERROR_BAD_LENGTH(24): The Wnode.BufferSize member of Properties specifies an incorrect size. Properties does not have sufficient space allocated to hold a copy of SessionName.
+ // ERROR_ALREADY_EXISTS(183): A session with the same name or GUID is already running.
+ // ERROR_ACCESS_DENIED(5): Only users with administrative privileges, users in the Performance Log Users group, and services running as LocalSystem, LocalService, NetworkService can control event tracing sessions.
+ // ERROR_INVALID_PARAMETER(87)
+ // ERROR_BAD_PATHNAME(161)
+ // ERROR_DISK_FULL(112)
+ // ERROR_NO_SUCH_PRIVILEGE(1313)
+ int retryCount = 4;
+ ULONG status = CaptureStatus::OK;
+
+ while (--retryCount >= 0)
+ {
+ status = StartTrace(&traceSessionHandle, KERNEL_LOGGER_NAME, traceProperties);
+
+ switch (status)
+ {
+ case ERROR_NO_SUCH_PRIVILEGE:
+ AdjustPrivileges();
+ break;
+
+ case ERROR_ALREADY_EXISTS:
+ ControlTrace(0, KERNEL_LOGGER_NAME, traceProperties, EVENT_TRACE_CONTROL_STOP);
+ break;
+
+ case ERROR_ACCESS_DENIED:
+ return CaptureStatus::ERR_TRACER_ACCESS_DENIED;
+
+ case ERROR_SUCCESS:
+ retryCount = 0;
+ break;
+
+ default:
+ return CaptureStatus::ERR_TRACER_FAILED;
+ }
+ }
+
+ if (status != ERROR_SUCCESS)
+ {
+ return CaptureStatus::ERR_TRACER_FAILED;
+ }
+
+ CLASSIC_EVENT_ID callstackSamples[4];
+ int callstackCountSamplesCount = 0;
+
+ if (mode & Mode::AUTOSAMPLING)
+ {
+ callstackSamples[callstackCountSamplesCount].EventGuid = SampledProfileGuid;
+ callstackSamples[callstackCountSamplesCount].Type = SampledProfile::OPCODE;
+ ++callstackCountSamplesCount;
+ }
+
+ if (mode & Mode::SYS_CALLS)
+ {
+ callstackSamples[callstackCountSamplesCount].EventGuid = SampledProfileGuid;
+ callstackSamples[callstackCountSamplesCount].Type = SysCallEnter::OPCODE;
+ ++callstackCountSamplesCount;
+ }
+
+ /*
+ callstackSamples[callstackCountSamplesCount].EventGuid = CSwitchProfileGuid;
+ callstackSamples[callstackCountSamplesCount].Type = CSwitch::OPCODE;
+ ++callstackCountSamplesCount;
+ */
+
+
+ /*
+ https://msdn.microsoft.com/en-us/library/windows/desktop/dd392328%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
+ Typically, on 64-bit computers, you cannot capture the kernel stack in certain contexts when page faults are not allowed. To enable walking the kernel stack on x64, set
+ the DisablePagingExecutive Memory Management registry value to 1. The DisablePagingExecutive registry value is located under the following registry key:
+ HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Memory Management
+ */
+ if (callstackCountSamplesCount > 0)
+ {
+ status = TraceSetInformation(traceSessionHandle, TraceStackTracingInfo, &callstackSamples[0], sizeof(CLASSIC_EVENT_ID) * callstackCountSamplesCount);
+ if (status != ERROR_SUCCESS)
+ {
+ OPTICK_FAILED("TraceSetInformation - failed");
+ return CaptureStatus::ERR_TRACER_FAILED;
+ }
+ }
+
+ if (mode & Mode::AUTOSAMPLING)
+ {
+ TRACE_PROFILE_INTERVAL itnerval = { 0 };
+ memset(&itnerval, 0, sizeof(TRACE_PROFILE_INTERVAL));
+ int step = 10000 * 1000 / frequency; // 1ms = 10000 steps
+ itnerval.Interval = step; // std::max(1221, std::min(step, 10000));
+ // The SessionHandle is irrelevant for this information class and must be zero, else the function returns ERROR_INVALID_PARAMETER.
+ status = TraceSetInformation(0, TraceSampledProfileIntervalInfo, &itnerval, sizeof(TRACE_PROFILE_INTERVAL));
+ OPTICK_ASSERT(status == ERROR_SUCCESS, "TraceSetInformation - failed");
+ }
+
+ ZeroMemory(&logFile, sizeof(EVENT_TRACE_LOGFILE));
+ logFile.LoggerName = KERNEL_LOGGER_NAME;
+ logFile.ProcessTraceMode = (PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP);
+ logFile.EventRecordCallback = OnRecordEvent;
+ logFile.BufferCallback = OnBufferRecord;
+ openedHandle = OpenTrace(&logFile);
+ if (openedHandle == INVALID_TRACEHANDLE)
+ {
+ OPTICK_FAILED("OpenTrace - failed");
+ return CaptureStatus::ERR_TRACER_FAILED;
+ }
+
+ DWORD threadID;
+ processThreadHandle = CreateThread(0, 0, RunProcessTraceThreadFunction, this, 0, &threadID);
+
+ isActive = true;
+ }
+
+ return CaptureStatus::OK;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool ETW::Stop()
+{
+ if (!isActive)
+ {
+ return false;
+ }
+
+ ULONG controlTraceResult = ControlTrace(openedHandle, KERNEL_LOGGER_NAME, traceProperties, EVENT_TRACE_CONTROL_STOP);
+
+ // ERROR_CTX_CLOSE_PENDING(7007L): The call was successful. The ProcessTrace function will stop after it has processed all real-time events in its buffers (it will not receive any new events).
+ // ERROR_BUSY(170L): Prior to Windows Vista, you cannot close the trace until the ProcessTrace function completes.
+ // ERROR_INVALID_HANDLE(6L): One of the following is true: TraceHandle is NULL. TraceHandle is INVALID_HANDLE_VALUE.
+ ULONG closeTraceStatus = CloseTrace(openedHandle);
+
+ // Wait for ProcessThread to finish
+ WaitForSingleObject(processThreadHandle, INFINITE);
+ BOOL wasThreadClosed = CloseHandle(processThreadHandle);
+
+ isActive = false;
+
+ //VS TODO: Disabling resolving of the syscalls - we can't use then as EventDescriptions at the moment
+ //ResolveSysCalls();
+
+ activeThreadsIDs.clear();
+
+ return wasThreadClosed && (closeTraceStatus == ERROR_SUCCESS) && (controlTraceResult == ERROR_SUCCESS);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ETW::~ETW()
+{
+ Stop();
+ Memory::Free(traceProperties);
+ traceProperties = nullptr;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Trace* Platform::GetTrace()
+{
+ return &g_ETW;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Symbol Resolving
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#define USE_DBG_HELP (OPTICK_PC)
+
+#if USE_DBG_HELP
+#include <DbgHelp.h>
+#pragma comment( lib, "DbgHelp.Lib" )
+#endif
+
+#include "optick_serialization.h"
+
+#if OPTICK_PC
+#include <psapi.h>
+#else
+// Forward declare kernel functions
+#pragma pack(push,8)
+typedef struct _MODULEINFO {
+ LPVOID lpBaseOfDll;
+ DWORD SizeOfImage;
+ LPVOID EntryPoint;
+} MODULEINFO, *LPMODULEINFO;
+#pragma pack(pop)
+#ifndef EnumProcessModulesEx
+#define EnumProcessModulesEx K32EnumProcessModulesEx
+EXTERN_C DWORD WINAPI K32EnumProcessModulesEx(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded, DWORD dwFilterFlag);
+#endif
+#ifndef GetModuleInformation
+#define GetModuleInformation K32GetModuleInformation
+EXTERN_C DWORD WINAPI K32GetModuleInformation(HANDLE hProcess, HMODULE hModule, LPMODULEINFO lpmodinfo, DWORD cb);
+#endif
+
+#ifndef GetModuleFileNameExA
+#define GetModuleFileNameExA K32GetModuleFileNameExA
+EXTERN_C DWORD WINAPI K32GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize);
+#endif
+#endif
+
+namespace Optick
+{
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//void ReportLastError()
+//{
+// LPVOID lpMsgBuf;
+// DWORD dw = GetLastError();
+//
+// FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+// NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+// (LPTSTR)&lpMsgBuf, 0, NULL);
+//
+// MessageBox(NULL, (LPCTSTR)lpMsgBuf, TEXT("Error"), MB_OK);
+// LocalFree(lpMsgBuf);
+//}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+typedef array<uintptr_t, 512> CallStackBuffer;
+typedef unordered_map<uint64, Symbol> SymbolCache;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+class WinSymbolEngine : public SymbolEngine
+{
+ HANDLE hProcess;
+
+ bool isInitialized;
+
+ bool needRestorePreviousSettings;
+ uint32 previousOptions;
+ static const size_t MAX_SEARCH_PATH_LENGTH = 2048;
+ char previousSearchPath[MAX_SEARCH_PATH_LENGTH];
+
+ SymbolCache cache;
+ vector<Module> modules;
+
+ void InitSystemModules();
+ void InitApplicationModules();
+public:
+ WinSymbolEngine();
+ ~WinSymbolEngine();
+
+ void Init();
+ void Close();
+
+ // Get Symbol from PDB file
+ virtual const Symbol * GetSymbol(uint64 dwAddress) override;
+ virtual const vector<Module>& GetModules() override;
+};
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+WinSymbolEngine::WinSymbolEngine() : isInitialized(false), hProcess(GetCurrentProcess()), needRestorePreviousSettings(false), previousOptions(0)
+{
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+WinSymbolEngine::~WinSymbolEngine()
+{
+ Close();
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const Symbol* WinSymbolEngine::GetSymbol(uint64 address)
+{
+ if (address == 0)
+ return nullptr;
+
+ Init();
+
+ Symbol& symbol = cache[address];
+
+ if (symbol.address != 0)
+ return &symbol;
+
+ if (!isInitialized)
+ return nullptr;
+
+ symbol.address = address;
+
+#if USE_DBG_HELP
+ DWORD64 dwAddress = static_cast<DWORD64>(address);
+
+ // FileName and Line
+ IMAGEHLP_LINEW64 lineInfo;
+ memset(&lineInfo, 0, sizeof(IMAGEHLP_LINEW64));
+ lineInfo.SizeOfStruct = sizeof(lineInfo);
+ DWORD dwDisp;
+ if (SymGetLineFromAddrW64(hProcess, dwAddress, &dwDisp, &lineInfo))
+ {
+ symbol.file = lineInfo.FileName;
+ symbol.line = lineInfo.LineNumber;
+ }
+
+ const size_t length = (sizeof(SYMBOL_INFOW) + MAX_SYM_NAME * sizeof(WCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64) + 1;
+
+ // Function Name
+ ULONG64 buffer[length];
+ PSYMBOL_INFOW dbgSymbol = (PSYMBOL_INFOW)buffer;
+ memset(dbgSymbol, 0, sizeof(buffer));
+ dbgSymbol->SizeOfStruct = sizeof(SYMBOL_INFOW);
+ dbgSymbol->MaxNameLen = MAX_SYM_NAME;
+
+ DWORD64 offset = 0;
+ if (SymFromAddrW(hProcess, dwAddress, &offset, dbgSymbol))
+ {
+ symbol.function.resize(dbgSymbol->NameLen);
+ memcpy(&symbol.function[0], &dbgSymbol->Name[0], sizeof(WCHAR) * dbgSymbol->NameLen);
+ }
+
+ symbol.offset = static_cast<uintptr_t>(offset);
+#endif
+
+ return &symbol;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const vector<Module>& WinSymbolEngine::GetModules()
+{
+ if (modules.empty())
+ {
+ InitSystemModules();
+ InitApplicationModules();
+ }
+ return modules;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// const char* USER_SYMBOL_SEARCH_PATH = "http://msdl.microsoft.com/download/symbols";
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void WinSymbolEngine::Init()
+{
+ if (!isInitialized)
+ {
+#if USE_DBG_HELP
+ previousOptions = SymGetOptions();
+
+ memset(previousSearchPath, 0, MAX_SEARCH_PATH_LENGTH);
+ SymGetSearchPath(hProcess, previousSearchPath, MAX_SEARCH_PATH_LENGTH);
+
+ SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_LOAD_ANYTHING);
+ if (!SymInitialize(hProcess, NULL, TRUE))
+ {
+ needRestorePreviousSettings = true;
+ SymCleanup(hProcess);
+
+ if (SymInitialize(hProcess, NULL, TRUE))
+ isInitialized = true;
+ }
+ else
+ {
+ isInitialized = true;
+ }
+
+ const vector<Module>& loadedModules = GetModules();
+ for (size_t i = 0; i < loadedModules.size(); ++i)
+ {
+ const Module& module = loadedModules[i];
+ SymLoadModule64(hProcess, NULL, module.path.c_str(), NULL, (DWORD64)module.address, (DWORD)module.size);
+ }
+
+#else
+ isInitialized = true;
+#endif
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+typedef DWORD(__stdcall *pZwQuerySystemInformation)(DWORD, LPVOID, DWORD, DWORD*);
+#define SystemModuleInformation 11 // SYSTEMINFOCLASS
+#define MAXIMUM_FILENAME_LENGTH 256
+
+struct SYSTEM_MODULE_INFORMATION
+{
+ DWORD reserved1;
+ DWORD reserved2;
+ PVOID mappedBase;
+ PVOID imageBase;
+ DWORD imageSize;
+ DWORD flags;
+ WORD loadOrderIndex;
+ WORD initOrderIndex;
+ WORD loadCount;
+ WORD moduleNameOffset;
+ CHAR imageName[MAXIMUM_FILENAME_LENGTH];
+};
+
+#pragma warning (push)
+#pragma warning(disable : 4200)
+struct MODULE_LIST
+{
+ DWORD dwModules;
+ SYSTEM_MODULE_INFORMATION pModulesInfo[];
+};
+#pragma warning (pop)
+
+void WinSymbolEngine::InitSystemModules()
+{
+ ULONG returnLength = 0;
+ ULONG systemInformationLength = 0;
+ MODULE_LIST* pModuleList = nullptr;
+
+#pragma warning (push)
+#pragma warning(disable : 4191)
+ pZwQuerySystemInformation ZwQuerySystemInformation = (pZwQuerySystemInformation)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "ZwQuerySystemInformation");
+#pragma warning (pop)
+
+ ZwQuerySystemInformation(SystemModuleInformation, pModuleList, systemInformationLength, &returnLength);
+ systemInformationLength = returnLength;
+ pModuleList = (MODULE_LIST*)Memory::Alloc(systemInformationLength);
+ DWORD status = ZwQuerySystemInformation(SystemModuleInformation, pModuleList, systemInformationLength, &returnLength);
+ if (status == ERROR_SUCCESS)
+ {
+ char systemRootPath[MAXIMUM_FILENAME_LENGTH] = { 0 };
+#if OPTICK_PC
+ ExpandEnvironmentStringsA("%SystemRoot%", systemRootPath, MAXIMUM_FILENAME_LENGTH);
+#else
+ strcpy_s(systemRootPath, "C:\\Windows");
+#endif
+
+ const char* systemRootPattern = "\\SystemRoot";
+
+ modules.reserve(modules.size() + pModuleList->dwModules);
+
+ for (uint32_t i = 0; i < pModuleList->dwModules; ++i)
+ {
+ SYSTEM_MODULE_INFORMATION& module = pModuleList->pModulesInfo[i];
+
+ char path[MAXIMUM_FILENAME_LENGTH] = { 0 };
+
+ if (strstr(module.imageName, systemRootPattern) == module.imageName)
+ {
+ strcpy_s(path, systemRootPath);
+ strcat_s(path, module.imageName + strlen(systemRootPattern));
+ }
+ else
+ {
+ strcpy_s(path, module.imageName);
+ }
+
+ modules.push_back(Module(path, (void*)module.imageBase, module.imageSize));
+ }
+ }
+ else
+ {
+ OPTICK_FAILED("Can't query System Module Information!");
+ }
+
+ if (pModuleList)
+ {
+ Memory::Free(pModuleList);
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void WinSymbolEngine::InitApplicationModules()
+{
+ HANDLE processHandle = GetCurrentProcess();
+ HMODULE hModules[256];
+ DWORD modulesSize = 0;
+ EnumProcessModulesEx(processHandle, hModules, sizeof(hModules), &modulesSize, 0);
+
+ int moduleCount = modulesSize / sizeof(HMODULE);
+
+ modules.reserve(modules.size() + moduleCount);
+
+ for (int i = 0; i < moduleCount; ++i)
+ {
+ MODULEINFO info = { 0 };
+ if (GetModuleInformation(processHandle, hModules[i], &info, sizeof(MODULEINFO)))
+ {
+ char name[MAX_PATH] = "UnknownModule";
+ GetModuleFileNameExA(processHandle, hModules[i], name, MAX_PATH);
+
+ modules.push_back(Module(name, info.lpBaseOfDll, info.SizeOfImage));
+ }
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void WinSymbolEngine::Close()
+{
+ if (isInitialized)
+ {
+#if USE_DBG_HELP
+ SymCleanup(hProcess);
+ if (needRestorePreviousSettings)
+ {
+ HANDLE currentProcess = GetCurrentProcess();
+
+ SymSetOptions(previousOptions);
+ SymSetSearchPath(currentProcess, previousSearchPath);
+ SymInitialize(currentProcess, NULL, TRUE);
+
+ needRestorePreviousSettings = false;
+ }
+#endif
+ modules.clear();
+ isInitialized = false;
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+SymbolEngine* Platform::GetSymbolEngine()
+{
+ static WinSymbolEngine pdbSymbolEngine;
+ return &pdbSymbolEngine;
+}
+//////////////////////////////////////////////////////////////////////////
+}
+#endif //OPTICK_ENABLE_TRACING
+#endif //USE_OPTICK
+#endif //_MSC_VER \ No newline at end of file