Each event in a buffer starts with a header,
and there are several kinds of header: one for the kernel’s own events, one for
performance events, one for classic providers and one for modern manifest-based
providers. The first four bytes say which kind it is and how long the event is. This
page follows Geoff Chappell’s study of NTWMI.H and the SDK headers, checked
against Airbus CERT’s etl-parser (etl/system.py, perf.py,
trace.py, event.py), whose header layouts agree; Microsoft
does not document the layout of events inside a buffer.
Telling headers apart
Read the first four bytes of an event. The byte at offset 3 has its top bit set in
every trace header; for the header types below its top two bits are set, so it is
0xC0. The byte at offset 2 is the header type. Events are aligned on
8-byte boundaries, so a reader rounds each event’s size up to a multiple of 8 to find
the next one.
| Type | Name | Header | Size of header |
|---|---|---|---|
0x01 | TRACE_HEADER_TYPE_SYSTEM32 | System header, 32-bit event data follows | 0x20 |
0x02 | TRACE_HEADER_TYPE_SYSTEM64 | System header, 64-bit event data follows | 0x20 |
0x03 | TRACE_HEADER_TYPE_COMPACT32 | Compact system header, 32-bit data (Windows 8 and later) | 0x18 |
0x04 | TRACE_HEADER_TYPE_COMPACT64 | Compact system header, 64-bit data (Windows 8 and later) | 0x18 |
0x0A | TRACE_HEADER_TYPE_FULL_HEADER32 | EVENT_TRACE_HEADER, 32-bit data | 0x30 |
0x0B | TRACE_HEADER_TYPE_INSTANCE32 | EVENT_INSTANCE_GUID_HEADER (in Windows 2000 and XP, EVENT_INSTANCE_HEADER), 32-bit data | not established |
0x0C | TRACE_HEADER_TYPE_TIMED | Long out of use | – |
0x0D | TRACE_HEADER_TYPE_ERROR | Long out of use | – |
0x0E | TRACE_HEADER_TYPE_WNODE_HEADER | Long out of use | – |
0x0F | TRACE_HEADER_TYPE_MESSAGE | MESSAGE_TRACE_HEADER, then message arguments (WPP software tracing) | not established |
0x10 | TRACE_HEADER_TYPE_PERFINFO32 | Performance header, 32-bit data | 0x10 |
0x11 | TRACE_HEADER_TYPE_PERFINFO64 | Performance header, 64-bit data | 0x10 |
0x12 | TRACE_HEADER_TYPE_EVENT_HEADER32 | EVENT_HEADER, 32-bit data (Windows Vista and later) | 0x50 |
0x13 | TRACE_HEADER_TYPE_EVENT_HEADER64 | EVENT_HEADER, 64-bit data | 0x50 |
0x14 | TRACE_HEADER_TYPE_FULL_HEADER64 | EVENT_TRACE_HEADER, 64-bit data | 0x30 |
0x15 | TRACE_HEADER_TYPE_INSTANCE64 | EVENT_INSTANCE_GUID_HEADER, 64-bit data | not established |
“32-bit” and “64-bit” describe the event data, not the header: a 32-bit provider on 64-bit Windows writes 32-bit data, with 4-byte pointers, and a reader must know which to decode it.
System header
SYSTEM_TRACE_HEADER, used by the kernel’s own events (processes,
threads, disk, network, and so on). 0x20 bytes; the compact form stops after
SystemTime, at 0x18.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0x00 | 2 | uint16 | Version | The event’s version; the kernel uses only the low 8 bits. 2 and 3 are seen in Windows 10. |
0x02 | 1 | uint8 | HeaderType | 1 to 4, as above. |
0x03 | 1 | uint8 | Flags | 0xC0. The whole first four bytes, without the version, are the marker 0xC0010000, 0xC0020000, 0xC0030000 or 0xC0040000. |
0x04 | 2 | uint16 | Size | Bytes of header plus event data. |
0x06 | 1 | uint8 | Type | The event type within its group. With Group it forms the 16-bit HookId, which says what the event is and how to read its data. |
0x07 | 1 | uint8 | Group | The event group. |
0x08 | 4 | uint32 | ThreadId | The thread that logged the event. |
0x0C | 4 | uint32 | ProcessId | Its process. |
0x10 | 8 | int64 | SystemTime | The timestamp, in the file’s clock type. |
0x18 | 4 | uint32 | KernelTime | Kernel-mode time of the thread (full form only). |
0x1C | 4 | uint32 | UserTime | User-mode time of the thread (full form only). |
The meanings of the many group and type values (the hook IDs) are only partly published; Microsoft’s Kernel Trace classes name some of them.
Performance header
PERFINFO_TRACE_HEADER, used for the kernel’s high-rate performance
events such as context switches. The event data starts at 0x10.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0x00 | 2 | uint16 | Version | Low 8 bits: the version. Bits 8 to 10 (0x0700): how many performance-counter values follow the header, 0 to 7 (at most 4 in practice). Bit 15 (0x8000): a PEBS index follows. |
0x02 | 1 | uint8 | HeaderType | 0x10 or 0x11. |
0x03 | 1 | uint8 | Flags | 0xC0; marker 0xC0100000 or 0xC0110000. |
0x04 | 2 | uint16 | Size | Bytes of header plus event data. |
0x06 | 2 | uint16 | HookId | The event type (Type at 0x06, Group at 0x07). For example 0x0524 is a context switch. |
0x08 | 8 | int64 | SystemTime | The timestamp. |
0x10 | varies | – | Counters, PEBS index, Data | Any 64-bit counter values, then any 64-bit PEBS index, then the event data. |
Geoff Chappell’s notes give the structure’s size as 0x18 bytes in one place but put the data at 0x10; the offsets above follow the layout.
Classic header
EVENT_TRACE_HEADER, documented in the Windows SDK, used by classic (MOF)
providers. 0x30 bytes.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0x00 | 2 | uint16 | Size | Bytes of header plus event data. |
0x02 | 1 | uint8 | HeaderType | 0x0A or 0x14. |
0x03 | 1 | uint8 | MarkerFlags | 0xC0; marker 0xC00A0000 or 0xC0140000 plus the size. |
0x04 | 1 | uint8 | Class.Type | The event type, such as 1 for start and 2 for end (EVENT_TRACE_TYPE_*). |
0x05 | 1 | uint8 | Class.Level | Importance: 1 critical to 5 verbose. |
0x06 | 2 | uint16 | Class.Version | Version of the event’s data layout. |
0x08 | 4 | uint32 | ThreadId | – |
0x0C | 4 | uint32 | ProcessId | – |
0x10 | 8 | int64 | TimeStamp | The timestamp. |
0x18 | 16 | GUID | Guid | The event class (provider) GUID. |
0x28 | 4 | uint32 | KernelTime | Kernel-mode time, or with UserTime a 64-bit ProcessorTime. |
0x2C | 4 | uint32 | UserTime | User-mode time. Where the header is used as a WNODE_HEADER, these 4 bytes are its Flags: 0x00000200 use timestamp, 0x00020000 traced GUID, 0x00040000 log WNODE, 0x00080000 GUID pointer, 0x00100000 MOF pointer, 0x00200000 no header. |
Modern header
EVENT_HEADER, documented in the Windows SDK, used by manifest-based and
TraceLogging providers since Windows Vista. 0x50 bytes.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0x00 | 2 | uint16 | Size | Bytes of header plus everything after it. |
0x02 | 2 | uint16 | HeaderType | 0xC012 or 0xC013 in a buffer. |
0x04 | 2 | uint16 | Flags | See flags. In a buffer, mainly 0x0001: extended data items follow the header. |
0x06 | 2 | uint16 | EventProperty | 0x0001 XML, 0x0002 forwarded XML, 0x0004 legacy event log, 0x0008 can be relogged. |
0x08 | 4 | uint32 | ThreadId | – |
0x0C | 4 | uint32 | ProcessId | – |
0x10 | 8 | int64 | TimeStamp | The timestamp. |
0x18 | 16 | GUID | ProviderId | The provider’s GUID. |
0x28 | 16 | struct | EventDescriptor | See descriptor. |
0x38 | 8 | 2 × uint32 | KernelTime, UserTime | Thread times, or one 64-bit ProcessorTime. |
0x40 | 16 | GUID | ActivityId | Groups related events. |
Flags
| Value | Name |
|---|---|
0x0001 | EVENT_HEADER_FLAG_EXTENDED_INFO |
0x0002 | EVENT_HEADER_FLAG_PRIVATE_SESSION |
0x0004 | EVENT_HEADER_FLAG_STRING_ONLY |
0x0008 | EVENT_HEADER_FLAG_TRACE_MESSAGE |
0x0010 | EVENT_HEADER_FLAG_NO_CPUTIME |
0x0020 | EVENT_HEADER_FLAG_32_BIT_HEADER |
0x0040 | EVENT_HEADER_FLAG_64_BIT_HEADER |
0x0080 | EVENT_HEADER_FLAG_DECODE_GUID (Windows 10 1709 and later) |
0x0100 | EVENT_HEADER_FLAG_CLASSIC_HEADER |
0x0200 | EVENT_HEADER_FLAG_PROCESSOR_INDEX |
Event descriptor
EVENT_DESCRIPTOR, 16 bytes, identifies the event within its provider’s
manifest.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0 | 2 | uint16 | Id | The event ID. |
2 | 1 | uint8 | Version | The event’s version. |
3 | 1 | uint8 | Channel | The channel it is aimed at. |
4 | 1 | uint8 | Level | 1 critical, 2 error, 3 warning, 4 information, 5 verbose. |
5 | 1 | uint8 | Opcode | The operation, such as 1 start or 2 stop. |
6 | 2 | uint16 | Task | The task the event belongs to. |
8 | 8 | uint64 | Keyword | Bit mask of categories. |
Microsoft does not publish how the extended data items that follow an
EVENT_HEADER are stored in a buffer when flag 0x0001 is set.
Airbus CERT’s etl-parser (etl/event.py) reads them as a run of items,
each aligned to 8 bytes, until one has bit 0 of its third field clear:
| Offset | Size | Type | Name (etl-parser) | Description |
|---|---|---|---|---|
0 | 2 | uint16 | reserved1 | – |
2 | 2 | uint16 | ext_type | The item’s type. |
4 | 2 | uint16 | reserved2 | Bit 0 set: another item follows. |
6 | 2 | uint16 | data_size | Bytes of data that follow. |
8 | varies | bytes | Data | The item’s data. |
The event’s own data follows the last item. This layout is etl-parser’s reading, not a published one.