Geoff Chappell - Software Analyst
The ETW_GUID_ENTRY structure is the kernel’s record of an event provider.
The ETW_GUID_ENTRY structure is not documented.
For a non-trivial structure that is plainly very much internal to the kernel, the ETW_GUID_ENTRY has been very stable. In the following table of sizes, different builds of Windows Vista are distinguished as early and late because they are known to vary the layout even though they don’t change the size.
Version | Size (x86) | Size (x64) |
---|---|---|
early 6.0 (before SP1); late 6.0 |
0x0158 | 0x0170 |
6.1 | 0x0178 | 0x01B0 |
6.2 to 6.3 | 0x0160 | 0x0178 |
10.0 | 0x0160 | 0x0180 |
1511 to 1903 | 0x0168 | 0x0190 |
2004 | 0x0178 | 0x01A8 |
The preceding sizes, and the offsets, types and names in the table below are from Microsoft’s symbol files for the kernel starting with Windows Vista.
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x00 | 0x00 |
LIST_ENTRY GuidList; |
6.0 and higher |
0x08 | 0x10 |
LIST_ENTRY SiloGuidList; |
2004 and higher |
0x08 (6.0 to 1903); 0x10 |
0x10 (6.0 to 1903); 0x20 |
LONG_PTR volatile RefCount; |
6.0 and higher |
0x0C (6.0 to 1903); 0x14 |
0x18 (6.0 to 1903); 0x28 |
GUID Guid; |
6.0 and higher |
0x1C (6.0 to 1903); 0x24 |
0x28 (6.0 to 1903); 0x38 |
LIST_ENTRY RegListHead; |
6.0 and higher |
0x24 (6.0 to 1903); 0x2C |
0x38 (6.0 to 1903); 0x48 |
PSECURITY_DESCRIPTOR SecurityDescriptor; |
6.0 and higher |
0x28 (early 6.0) | 0x40 (early 6.0) |
TRACE_ENABLE_CONTEXT LegacyEnableContext; |
early 6.0 only |
0x30 (early 6.0) | 0x48 (early 6.0) |
ULONG LegacyProviderEnabled; |
early 6.0 only |
0x28 (late 6.0 to 1903); 0x30 |
0x40 (late 6.0 to 1903); 0x50 |
ETW_LAST_ENABLE_INFO LastEnable; |
late 6.0 only |
union { ETW_LAST_ENABLE_INFO LastEnable; ULONGLONG MatchId; }; |
6.1 and higher | ||
0x38 (6.0 to 1903); 0x40 |
0x50 (6.0 to 1903); 0x60 |
TRACE_ENABLE_INFO ProviderEnableInfo; |
6.0 and higher |
0x58 (6.0 to 1903); 0x60 |
0x70 (6.0 to 1903); 0x80 |
TRACE_ENABLE_INFO EnableInfo [8]; |
6.0 and higher |
0x0158 (6.1 to 1903); 0x0160 |
0x0170 (6.1 to 1903); 0x0180 |
EVENT_FILTER_HEADER *FilterData [8]; |
6.1 only |
EVENT_FILTER_HEADER **FilterData; |
6.2 only | ||
ETW_FILTER_HEADER *FilterData; |
6.3 and higher | ||
0x015C (10.0 to 1903); 0x0164 |
0x0178 (10.0 to 1903); 0x0188 |
ESILO *ServerSilo; |
10.0 only |
UCHAR HostSilo; |
1511 only | ||
ETW_SILODRIVERSTATE *SiloState; |
1607 and higher | ||
0x0168 | 0x0190 |
ETW_GUID_ENTRY *HostEntry; |
2004 and higher |
0x0160 (1511 to 1903); 0x016C |
0x0180 (1511 to 1903); 0x0198 |
EX_PUSH_LOCK Lock; |
1511 and higher |
0x0164 (1511 to 1903); 0x0170 |
0x0188 (1511 to 1903); 0x01A0 |
ETHREAD *LockOwner; |
1511 and higher |
The Guid is not unique to an ETW_GUID_ENTRY. This is because event providers come in different types: trace provider; notification provider; and provider group (this last being new for Windows 10). Providers that have a different ETW_GUID_TYPE can have the same Guid because the corresponding ETW_GUID_ENTRY instances go into different lists. Windows 10 not only adds a list but also allows for multiplicity within each list: providers that register in multiple silos get a different instance for each silo. Whichever list an ETW_GUID_ENTRY goes into, it’s linked through its GuidList member.
Each registration of an event provider creates an ETW_REG_ENTRY structure. To register an event provider is in effect to open it with the intention of writing events through it. Registration from user mode formalises this by opening an Object Manager handle to the ETW_REG_ENTRY structure. An event provider can have concurrent registrations. The most notable purpose to this in practice is that an event provider can be distributed across multiple executable modules, including to mix kernel and user modes, which each write their selection of events through the one provider. The ETW_REG_ENTRY structures for the possibly many registrations of a provider are kept in the provider’s RegListHead, linking through their RegList member.
A provider can be concurrently enabled for at most eight tracing sesions, also known as loggers. The EnableInfo array records what the provider knows about these loggers. This includes, of course, the 16-bit LoggerId, but also parameters such as the Level, MatchAnyKeyword and MatchAllKeyword that the logger has specified for matching against event definitions to decide which events the logger is and is not interested in receiving. (The TRACE_ENABLE_INFO structure is declared in Microsoft’s EVNTRACE.H and is documented, though for reasons that are far removed from its use in the ETW_GUID_ENTRY.)
The ProviderEnableInfo member is an aggregate over all loggers, so that the provider can see quickly not to proceed with writing an event that is not enabled for any logger. It has the highest of any logger’s Level, a bit-wise OR of all loggers’ MatchAnyKeyword and a bit-wise AND of all loggers’ MatchAllKeyword.
Schematized event filters—also called provider-side filters—were added at kernel level for Windows 7. Loggers can specify one filter each. The original implementation has FilterData as an array of eight pointers to EVENT_FILTER_HEADER structures. The filter data for each logger is this header plus variable-size data, for a total size given by the header’s Size member. Version 6.2 changed to having one pointer to an array of eight pointers, each again to an EVENT_FILTER_HEADER and variable-size data. In version 6.3 and higher, the pointer is to an array of eight ETW_FILTER_HEADER structures, for each of which the EVENT_FILTER_HEADER and variable-size data is pointed to by the ProviderSideFilter member.
The new Lock for the 1511 release of Windows 10 allows that operations on one provider, such as adding an ETW_REG_ENTRY, do not delay operations on another. Earlier versions have a mutex in the kernel’s data.