The SYSTEM_PROCESSOR_PROFILE_CONTROL_AREA structure both provides input to and output from ZwSetSystemInformation or NtSetSystemInformation when given the information class SystemProcessorProfileControlArea (0x81).


The structure and information class date from version 6.2 and its introduction of Processor Event Based Sampling (PEBS) to Event Tracing for Windows (ETW).

Documentation Status

Neither the SYSTEM_PROCESSOR_PROFILE_CONTROL_AREA structure nor the SystemProcessorProfileControlArea information class is documented.

Microsoft does publish the practical equivalent of a C-language definition as type information in public symbol files, though not for the kernel, where the structure is prepared, nor even for low-level user-mode DLLs that interpret the structure, but for various higher-level user-mode DLLs such as URLMON.DLL and only then starting with version 6.2.


The SYSTEM_PROCESSOR_PROFILE_CONTROL_AREA is 0x08 or 0x10 bytes in 32-bit and 64-bit Windows, respectively.

Offset (x86) Offset (x64) Definition Remarks
0x00 0x00
0x04 0x08
BOOLEAN Allocate;

The structure is designed so that NtSetSystemInformation both sets information and receives it. A non-zero Allocate on input tells the function to create a suitable PROCESSOR_PROFILE_CONTROL_AREA for the current processor and return its address in ProcessorProfileControlArea as output. The reverse is to call with zero for Allocate on input. The function then clears ProcessorProfileControlArea as output.


If Allocate is non-zero and the function cannot get memory for a new control area (cache-aligned in non-paged pool and zero-initialised), it sets ProcessorProfileControlArea to NULL and returns STATUS_INSUFFICIENT_RESOURCES.

If the current processor does not support Processor Based Event Sampling (PEBS), the function returns STATUS_NOT_SUPPORTED. Support requires all the following:

If Allocate is non-zero and the current processor already has a control area, the function puts the address of that control area in ProcessorProfileControlArea as its output, frees the newly allocated control area, and returns STATUS_ADDRESS_ALREADY_EXISTS. Otherwise, the newly allocated control area is accepted for the processor, and the function succeeds.

If Allocate is zero on input, the function clears ProcessorProfileControlArea to NULL for output. If the current processor does not have a control area, the function returns STATUS_MEMORY_NOT_ALLOCATED. Otherwise, it detaches the control area from the processor, frees it, and succeeds.


See that the function gets the memory before it even knows that the processor is suitable. If you paused at this, and perhaps even more if you immediately thought it inefficient, then pause also at how you would call the function. The call is expected at PASSIVE_LEVEL. The function does the body of its work at DISPATCH_LEVEL to ensure that it acts only on the one processor. If the calling thread is not otherwise restricted from being switched to other processors, the successful function can have prepared the returned PROCESSOR_PROFILE_CONTROL_AREA for some processor other than what the thread was running on before or after the call.