The EVENT_TRACE_PROFILE_COUNTER_INFORMATION structure is one of many that the ZwSetSystemInformation and NtSetSystemInformation functions expect as input when given the information class SystemPerformanceTraceInformation (0x1F). This particular structure is selected when the first dword in the information buffer is EventTraceProfileConfigInformation (0x0C) or EventTraceProfileCounterListInformation (0x0F).

Documentation Status

The EVENT_TRACE_PROFILE_COUNTER_INFORMATION structure is not documented.

A few public disclosures are known from Microsoft, though not as any sort of plain-English documentation. One is that a previously unpublished header named NTETW.H was published in the original and Version 1511 editions of the Windows Driver Kit (WDK) for Windows 10, and this header contains a C-language definition of the structure.

Were it not for this limited and possibly unintended disclosure of NTETW.H, a practical equivalent of the C-language definition (but missing comments, of course) would anyway be known from type information in symbol files. But this too has the look of an oversight. 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. It has instead slipped out in symbol files for a smattering of higher-level user-mode DLLs, starting with Windows 8. For these few, the readily available symbol files actually are private symbol files and show that the unpublished NTETW.H was included when compiling the corresponding binaries.


In user mode, the EVENT_TRACE_PROFILE_COUNTER_INFORMATION structure arguably exists only to support the documented ADVAPI32 (or SECHOST) function TraceSetInformation for its information classes TraceProfileSourceConfigInfo (0x06) and TracePmcCounterListInfo (0x09). Well-behaved user-mode software executing above ADVAPI32 does not call NtSetSystemInformation but prefers TraceSetInformation and therefore has no need of this structure.

Or so might go the theory or principle. Against it is that Microsoft’s documentation of TraceSetInformation, as perused online today (30th November 2016), does not tell programmers what information to provide in what form. For instance, of TracePmcCounterListInfo it says only “Query the list of performance monitoring counters to collect.”


The EVENT_TRACE_PROFILE_COUNTER_INFORMATION is 0x18 bytes in both 32-bit and 64-bit Windows. Offsets, names and types in the table that follows are from type information in symbol files and from the published C-language definition, as described above.

Offset Definition Versions
6.2 and higher
6.2 and higher
6.2 and higher

Though the elements of the ProfileSource array are defined as ULONG, they take values from the KPROFILE_SOURCE enumeration. This is defined in WDM.H and lists the types of profiling data that the HAL may keep about processor performance. It is presumably also defined in headers that Microsoft does not publish but makes available to its own user-mode programmers: how else does type information for it appear in symbol files for such high-level modules as the URLMON.DLL from Internet Explorer?

Though the one EVENT_TRACE_PROFILE_COUNTER_INFORMATION structure serves both information classes EventTraceProfileConfigInformation and EventTraceProfileCounterListInformation, NTETW.H helpfully defines the alias EVENT_TRACE_PROFILE_CONFIG_INFORMATION for the structure’s use with the first of them.


The EVENT_TRACE_PROFILE_COUNTER_INFORMATION structure is meaningful only as input to the ZwSetSystemInformation function in two cases. Their behaviour is as well picked up here. This review takes as understood all the general points and shorthands that are noted in the separate attempt at documenting the function, and takes as granted that the information class is SystemPerformanceTraceInformation and that the information buffer is at least large enough for an EVENT_TRACE_PROFILE_COUNTER_INFORMATION structure up to but not including the ProfileSource array and in which the EventTraceInformationClass is either EventTraceProfileConfigInformation or EventTraceProfileCounterListInformation.




The point to the ProfileSource array is that although it is defined formally as having only one element, it is meant to continue for the remainder of the information buffer. If the information buffer is not exactly right for zero or more whole array elements, the function returns STATUS_INVALID_PARAMETER.

The TraceHandle selects an event logger. Specifically, the low 16 bits are the logger ID or are 0xFFFF to select the NT Kernel Logger. This interpretation of 0xFFFF is formalised by the definition of a macro KERNEL_LOGGER_ID in the NTWMI_X.H header in early editions of the Windows Driver Kit (WDK) for Windows 10. If the logger ID does not select an active logger to which the function can arrange exclusive access, the function returns STATUS_WMI_INSTANCE_NOT_FOUND. If the caller does not have the TRACELOG_GUID_ENABLE permission for the logger, the function fails, typically returning STATUS_ACCESS_DENIED.

The function is to configure the logger for receiving the processor performance monitoring counters that are represented by the given profile sources. The implementation can support only so many counters per logger. The maximum was originally a hard-coded 4 that was extended to 8 for Version 1703. The published NTETW.H looks to have this maximum defined as ETW_MAX_PMC_COUNTERS. Starting with Version 1903, the kernel instead learns the maximum from the HAL while initialising. The HAL’s report is obtained via the HalQuerySystemInformation pointer in the kernel’s HAL_DISPATCH, using the new information class HalQueryProfileNumberOfCounters (0x2C).

If the information buffer supplies either no profile sources or more than the maximum, the function returns STATUS_INVALID_PARAMETER. If the logger is set to use paged memory, as from having EVENT_TRACE_USE_PAGED_MEMORY in its logger mode, then the function returns STATUS_INVALID_PARAMETER.

To profile counters, the logger must have an ETW_PMC_SUPPORT structure. If it does not already have one but the function cannot create one, the function returns STATUS_NO_MEMORY.

Each logger can have profile counters set for it just the once. Whatever sources are set remain set until the logger is stopped. If the logger already has a source set, the function returns STATUS_WMI_ALREADY_ENABLED.

The profiling is actually done by the HAL. For each currently active processor, the given profile sources are passed to the HAL via the HalAllocatePmcCounterSets pointer in the kernel’s HAL_PRIVATE_DISPATCH.. Failure for any processor is failure for the function.