WMI_LOGGER_INFORMATION

The WMI_LOGGER_INFORMATION structure describes an event logger across the NtTraceControl interface between user mode and kernel mode. It is exchanged when starting, stopping, querying, updating or flushing a logger. It thus provides the input for what the kernel builds as the WMI_LOGGER_CONTEXT and is the means of returning information about a logger. To the documented user-mode functions such as StartTrace and ControlTrace that go through NtTraceControl to reach the kernel-mode implementation for these operations, the WMI_LOGGER_INFORMATION is a slight repackaging of the documented EVENT_TRACE_PROPERTIES.

Windows versions before 6.0 do not have NtTraceControl. Versions before 5.0, which predate Event Tracing for Windows (ETW), have nothing similar, but all the versions in-between have the WMI_LOGGER_INFORMATION as the input and output for Device I/O Control requests that are sent to the kernel’s built-in WMI support device. Versions 5.1 and 5.2 also have the WMI_LOGGER_INFORMATION as input and output for the undocumented kernel exports WmiStartTrace, WmiStopTrace, WmiQueryTrace, WmiUpdateTrace and WmiFlushTrace.

Documentation Status

The WMI_LOGGER_INFORMATION structure is not documented, but Microsoft has published a C-language definition in the NTWMI.H from the Enterprise edition of the Windows Driver Kit (WDK) for Windows 10 version 1511.

Were it not for this relatively recent and possibly unintended disclosure, much would anyway be known from type information in symbol files. Curiously though, type information for this structure has never appeared in any public symbol files for the kernel or for the obvious low-level user-mode DLLs. In the whole of Microsoft’s packages of public symbol files, relevant type information is unknown before Windows 8 and appears in symbol files only for appxdeploymentclient.dll, certenroll.dll (before Windows 10) and windows.storage.applicationdata.dll.

Layout

Though the WMI_LOGGER_INFORMATION is shared between kernel and user modes, it took some time to settle. The following changes of size are known:

Version Size
5.0 0x88
5.1 0xA8
5.2 and higher 0xB0

The WMI_LOGGER_INFORMATION is the same in 32-bit and 64-bit Windows because from as early as version 5.1, members that are pointers or handles or that contain pointers are defined in union with artificial members that widen the 32-bit definition to the 64-bit. Other members are defined in unions because the different branches of the union are intended for different operations, or because one is for input to the kernel and the other for output. It is not yet certain when some of this differentiation started.

Offset Definition Versions
0x00
WNODE_HEADER Wnode;
5.0 and higher
0x30
ULONG BufferSize;
5.0 and higher
0x34
ULONG MinimumBuffers;
5.0 and higher
0x38
ULONG MaximumBuffers;
5.0 and higher
0x3C
ULONG MaximumFileSize;
5.0 and higher
0x40
ULONG LogFileMode;
5.0 and higher
0x44
ULONG FlushTimer;
5.0 and higher
0x48
ULONG EnableFlags;
5.0 and higher
0x4C
LONG AgeLimit;
 
union {
    LONG AgeLimit;
    LONG FlushThreshold;
};
 
0x50
ULONG Wow;
5.2 and higher
0x50 (5.0 to 5.1);
0x58
HANDLE LogFileHandle;
5.0 only
union {
    HANDLE LogFileHandle;
    ULONG64 LogFileHandle64;
};
5.1 and higher
0x54 (5.0);
0x58 (5.1);
0x60
ULONG NumberOfBuffers;
 
union {
    ULONG NumberOfBuffers;
    ULONG InstanceCount;
};
 
0x58 (5.0);
0x5C (5.1);
0x64
ULONG FreeBuffers;
 
union {
    ULONG FreeBuffers;
    ULONG InstanceId;
};
 
0x5C (5.0);
0x60 (5.1);
0x68
ULONG EventsLost;
 
union {
    ULONG EventsLost;
    ULONG NumberOfProcessors;
};
 
0x60 (5.0);
0x64 (5.1);
0x6C
ULONG BuffersWritten;
5.0 and higher
0x64 (5.0);
0x68 (5.1);
0x70
ULONG LogBuffersLost;
5.0 to 5.2
union {
    ULONG LogBuffersLost;
    ULONG Flags;
};
6.0 and higher
0x68 (5.0);
0x6C (5.1);
0x74
ULONG RealTimeBuffersLost;
5.0 and higher
0x6C (5.0);
0x70 (5.1);
0x78
HANDLE LoggerThreadId;
5.0 only
union {
    HANDLE LoggerThreadId;
    ULONG64 LoggerThreadId64;
};
5.1 and higher
0x70 (5.0);
0x78 (5.1);
0x80
UNICODE_STRING LogFileName;
5.0 only
union {
    UNICODE_STRING LogFileName;
    UNICODE_STRING64 LogFileName64;
};
5.1 and higher
0x78 (5.0);
0x88 (5.1);
0x90
UNICODE_STRING LoggerName;
5.0
union {
    UNICODE_STRING LoggerName;
    UNICODE_STRING64 LoggerName64;
};
5.1 and higher
0x80 (5.0);
0x98 (5.1);
0xA0 (5.2)
unknown PVOID 5.0 only
unknown PVOID and ULONG64 5.1 to 5.2
0xA0
ULONG RealTimeConsumerCount;
5.2 and higher
0xA4
ULONG SpareUlong;
5.2 and higher
0xA0 (5.1);
0xA8
union {
    PVOID LoggerExtension;
    ULONG64 LoggerExtension64;
} DUMMYUNIONNAME10;
5.1 and higher

All the remaining remarks are from very old notes and are reproduced solely to record something of this barely known structure’s early development. Take or leave with more than usual caution.

Some requirements of the Wnode apply to all operations:

The Guid in the Wnode is vital input for identifying the logger. Some identifiers are intrinsic to the ETW machinery:

In version 5.2 and higher, SystemTraceControlGuid is invalid if EVENT_TRACE_FILE_MODE_NEWFILE is set. In all versions, SystemTraceControlGuid requires the TRACELOG_ACCESS_KERNEL_LOGGER permission.

A null Guid stands for whatever the kernel wants to use. In versions 5.2 and higher, the kernel creates a GUID. Before then, the kernel uses EventTraceGuid.

Also vital as input in version 5.1 and higher in the Wnode is the ClientContext. It selects the type of clock. Microsoft’s names are known from NTWMI.H:

Value Name Versions Remarks
0 EVENT_TRACE_CLOCK_RAW    
1 EVENT_TRACE_CLOCK_PERFCOUNTER 5.1 and higher default in 5.1 and early 5.2
2 EVENT_TRACE_CLOCK_SYSTEMTIME 5.2 and higher default in late 5.2 and higher
3 EVENT_TRACE_CLOCK_CPUCYCLE late 5.2 and higher  
  EVENT_TRACE_CLOCK_MAX    

All versions support at least the performance counter and system time, but using the ClientContext for each logger is not known to version 5.0, which is instead configured from the registry. In version 5.1 and in version 5.2 before Windows Server 2003 SP1, all values other than 2 are treated as 1 (and the ClientContext is updated correspondingly on output). In later versions, all values other than 1 and 3 are treated as 2.

The BufferSize is in KB. Zero stands for 4KB, presumably as the CPU page size. Anything greater than 0x0400 is treated as 0x0400, thus accounting for the documented maximum of 1MB.

The MaximumBuffers and MinimumBuffers interact. Each is defaulted if less than two. The minimum is defaulted if it exceeds the maximum. The defaults depend on the number of processors:

Version Default MinimumBuffers Default MaximumBuffers
5.0 only numproc plus 1 25
5.1 to 5.2 numproc plus 2 numproc plus 22

A MaximumFileSize is required if EVENT_TRACE_FILE_MODE_NEWFILE is set.

The FlushTimer is counted in seconds.

The EnableFlags are ordinarily a collection of bits that specify types of event for the kernel logger. To allow for more types than there are bits in a dword, a very different interpretation applies if EVENT_TRACE_FLAG_EXTENSION is set. The EnableFlags is then not a dword but a TRACE_ENABLE_FLAG_EXTENSION which locates an array of dwords that act as an extended set of flags.

The AgeLimit is counted in minutes. A non-zero count less than 15 is treated as 15 in versions 5.1 and higher. Note that the AgeLimit is signed: all versions treat a negative count as zero.

A LogFileHandle is required if

A LogFileHandle is invalid without the TRACELOG_CREATE_ONDISK access right. For a user-mode request, a LogFileHandle must have the FILE_WRITE_DATA permission.

A LogFileName is required if

The unknown pointer near the end of the structure for versions before 6.0 provides a WNODE_HEADER and TRACE_LOGFILE_HEADER to use for validation when opening an Event Tracing Log (ETL) file. The kernel saves a copy of the input to paged memory, keeping the address as the LoggerHeader member of the WMI_LOGGER_CONTEXT. Every ETL file begins with a WMI_BUFFER_HEADER, a SYSTEM_TRACE_HEADER and a TRACE_LOGFILE_HEADER. In these versions, the WMI_BUFFER_HEADER begins with a WNODE_HEADER, which must match what’s saved. In the TRACE_LOGFILE_HEADER, the first 0x38 bytes must match.

Version 5.0 assumes that the LoggerName and LogFileName each have a Buffer that is one WCHAR larger (presumably expected to be a null-terminator) than the Length.