Flags in the ETW_REG_ENTRY

The small Index, Flags and EnableMask members of the ETW_REG_ENTRY leave 3 bytes of unused alignment space in both 32-bit and 64-bit Windows versions 6.0 and 6.1 The Flags were then a USHORT with bits presumably defined by macro. Only seven bits are known to have been in use, but apparently in two sets: 0x0001 to 0x0010 and then 0x1000 to 0x2000. When Windows 8 moved all three members further into the structure, it not only defined the flags formally as bit fields but redefined two of them so that all fit into an 8-bit Flags. The concurrent addition of DbgModernRegistration meant that the 8-bit Flags started full. Inevitably, more bits got defined and so the Flags have since been restored to 16 bits:

Mask Definition Versions Remarks
0x01 (6.2 to 1511);
0x0001
UCHAR DbgKernelRegistration : 1;
6.2 to 1511 same bit in Flags in 6.0 to 6.1
USHORT DbgKernelRegistration : 1;
1607 and higher  
0x02 (6.2 to 1511);
0x0002
UCHAR DbgUserRegistration : 1;
6.2 to 1511 same bit in Flags in 6.0 to 6.1
USHORT DbgUserRegistration : 1;
1607 and higher  
0x04 (6.2 to 1511);
0x0004
UCHAR DbgReplyRegistration : 1;
6.2 to 1511 same bit in Flags in 6.0 to 6.1
USHORT DbgReplyRegistration : 1;
1607 and higher  
0x08 (6.2 to 1511);
0x0008
UCHAR DbgClassicRegistration : 1;
6.2 to 1511 same bit in Flags in 6.0 to 6.1
USHORT DbgClassicRegistration : 1;
1607 and higher  
0x10 (6.2 to 1511);
0x0010
UCHAR DbgSessionSpaceRegistration : 1;
6.2 to 1511 same bit in Flags in 6.1
USHORT DbgSessionSpaceRegistration : 1;
1607 and higher  
0x20 (6.2 to 1511);
0x0020
UCHAR DbgModernRegistration : 1;
6.2 to 1511  
USHORT DbgModernRegistration : 1;
1607 and higher  
0x40 (6.2 to 1511);
0x0040
UCHAR DbgClosed : 1;
6.2 to 1511 0x1000 bit in Flags in 6.0 to 6.1
USHORT DbgClosed : 1;
1607 and higher  
0x80 (6.2 to 1511);
0x0080
UCHAR DbgInserted : 1;
6.2 to 1511 0x2000 bit in Flags in 6.0 to 6.1
USHORT DbgInserted : 1;
1607 and higher  
0x0100
USHORT DbgWow64 : 1;
1607 and higher  
0x0200
USHORT DbgUseDescriptorType;
1709 and higher previously as BOOLEAN
0x0400
USHORT DbgDropProviderTraits;
1709 and higher  

The DbgKernelRegistration and DbgUserRegistration bits of course distinguish whether the ETW_REG_ENTRY was created because a registration was made by a kernel-mode or user-mode caller. A set DbgReplyRegistration marks that the ETW_REG_ENTRY exists not to record any sort of registration of any sort of event provider but to help with an event provider’s reply to a notification. These mutually exclusive bits affect the interpretation of some ETW_REG_ENTRY members.

Kernel Registration

Kernel-mode registrations can have a Callback routine that executes in kernel mode, such that its address and whatever Context is to be passed as an argument are kept in the ETW_REG_ENTRY. That the Callback is formally a pointer to void is here thought to allow for the routine’s two different prototypes. The pointer is in fact a PETWENABLECALLBACK or a PETW_CLASSIC_CALLBACK, depending on whether DbgClassicRegistration is clear or set.

The first of these pointer types is defined in WDM.H and appears in documentation of the EtwRegister function. The corresponding routine has its own page of documentation, as ETWENABLECALLBACK, which is odd since this name appears nowhere in any headers for the Windows Driver Kit (WDK). Still, documented it plainly is.

The second pointer type has the same relationship to the EtwRegisterClassicProvider function as does the first to EtwRegister, but the means of registering a classic provider has never been formally documented. The PETW_CLASSIC_CALLBACK type has no C-language definition in any WDK header except for the NTOSP.H which Microsoft published, plausibly by oversight, in the original and 1511 editions of the WDK for Windows 10. The headers are not everything, however. A C-language definition has instead been in the configuration files for Windows Pre-Processor (WPP) Tracing, starting with the WDK for Windows Vista SP1.

User Registration

User-mode registrations can have callback routines too, but although their execution in user mode may occur because of kernel-mode action, the calling back is not itself the kernel’s to arrange. The Callback and Context have no meaning for an ETW_REG_ENTRY that was created for a user-mode registration. Their place is taken instead by the Process from which the registration was made and in which any user-mode callback would execute. Within that process, NTDLL has an ETW_REGISTRATION_ENTRY and the user-mode callback and context are there. The kernel never knows of them.

Reply Registration

The NtTraceControl function provides for user-mode software to send notifications to the user-mode registrations of an event provider. These notifications can request replies. The sender is then given a handle to poll for replies. The object that’s referenced by this handle is a highly specialised ETW_REG_ENTRY. There may be no more reason than some programmer’s economy with object types: Event Tracing for Windows (ETW) already makes a formal object, the EtwRegistration, of each ETW_REG_ENTRY for a user-mode registration, so why not just adapt that? Further study looks to be required.

In an ETW_REG_ENTRY that is created as a reply object, with DbgReplyRegistration set, very few of the defined members are meaningful. The most important is that where the ETW_REG_ENTRY for each user-mode registration that receives the notification has a ReplySlot array, the ETW_REG_ENTRY that is created for receiving the replies from these registrations has a ReplyQueue pointer.