Geoff Chappell, Software Analyst
Starting with Windows 2000, a PROCESSOR_POWER_STATE structure is nested within the KPRCB for each logical processor. It is a highly variable implementation detail for the kernel, but it may be worth public attention not just as an aid to debugging but for the curious point that Microsoft seems initially not to have thought of it as internal.
A C-language definition appeared in NTPOAPI.H from the Device Driver Kit (DDK) for Windows XP, albeit in a directory of headers that are specific to targetting Windows 2000 (and another for Windows Me, which is a different operating system of no concern here). What’s noteworthy is not just that this definition for Windows 2000 is the only definition that Microsoft has disclosed but also that Microsoft keeps disclosing it. Even as late as the Windows Driver Kit (WDK) for Windows 10, which to a large extent forces programmers to target nothing older than Windows 7, NTPOAPI.H has the C-language definition of PROCESSOR_POWER_STATE for Windows 2000. It’s in a conditional compilation block with a comment “win2k only”, but why does it remain? Why, even, did Microsoft ever publish it?
Whatever wider use may have been intended for the PROCESSOR_POWER_STATE in Windows 2000, or even while preparing the DDK for Windows XP, the structure was evidently soon regarded as internal in the sense that nothing of its layout needed to be preserved from one build to another. In the following table which summarises the variability just from its changing size, different builds of the same version are distinguished as early and late because they are known to vary the structure even if they don’t change the size. These descriptions, as early and late, are then used throughout the article as a shorthand.
Version | Size (x86) | Size (x64) |
---|---|---|
5.0 | 0x88 | |
early 5.1 (before Windows XP SP2); late 5.1 (Windows XP SP2); very late 5.1 (Windows XP SP3); early 5.2 (before Windows Server 2003 SP1); late 5.2 (Windows Server 2003 SP1); very late 5.2 (Windows Server 2003 SP2) |
0x0120 | 0x0170 |
early 6.0 (before Windows Vista SP1) | 0xE0 | 0x0138 |
late 6.0 (Windows Vista SP1 and higher) | 0xC8 | 0x0118 |
6.1 | 0xC8 | 0x0100 |
6.2 | 0x0180 | 0x01C8 |
6.3 | 0x0190 | 0x01E0 |
10.0 | 0x0180 | 0x01D0 |
Except that the structure sometimes gets smaller for a new version, the changes in size do not convey the scale of reorganisation within. It seems best to regard the PROCESSOR_POWER_STATE as having been completely redesigned for Windows Vista and then again for Windows 7.
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x00 | 0x00 |
PPROCESSOR_IDLE_FUNCTION IdleFunction; |
5.0 to 5.2 |
0x04 | 0x08 |
ULONG Idle0KernelTimeLimit; |
5.0 to 5.2 |
0x08 | 0x0C |
ULONG Idle0LastTime; |
5.0 to 5.2 |
0x0C | 0x10 |
PVOID IdleHandlers; |
5.1 to 5.2 |
0x0C (5.0); 0x10 |
0x18 |
PVOID IdleState; |
5.0 to 5.2 |
0x14 | 0x20 |
ULONG IdleHandlersCount; |
5.1 to 5.2 |
0x10 (5.0); 0x18 |
0x28 |
ULONGLONG LastCheck; |
5.0 to 5.2 |
0x18 (5.0); 0x20 |
0x30 |
PROCESSOR_IDLE_TIMES IdleTimes; |
5.0 to 5.2 |
0x38 (5.0); 0x40 |
0x50 |
ULONG IdleTime1; |
5.0 to 5.2 |
0x3C (5.0); 0x44 |
0x54 |
ULONG PromotionCheck; |
5.0 to 5.2 |
0x40 (5.0); 0x48 |
0x58 |
ULONG IdleTime2; |
5.0 to 5.2 |
0x44 (5.0); 0x4C |
0x5C |
UCHAR CurrentThrottle; |
5.0 to 5.2 |
0x45 (5.0); 0x4D |
UCHAR ThrottleLimit; |
5.0 only | |
0x5D |
UCHAR ThermalThrottleLimit; |
5.1 to 5.2 | |
0x46 (5.0); 0x4E |
0x5E |
UCHAR Spare1 [2]; |
5.0 only |
UCHAR CurrentThrottleIndex; |
5.1 to 5.2 | ||
0x4F | 0x5F |
UCHAR ThermalThrottleIndex; |
5.1 to 5.2 |
0x48 (5.0); 0x50 |
ULONG SetMember; |
5.0 only | |
ULONG PerfSystemTime; |
5.1 before Windows XP SP2; 5.2 before Windows Server 2003 SP1 |
||
0x60 |
ULONG LastKernelUserTime; |
5.1 from Windows XP SP2 and higher; 5.2 from Windows Server 2003 SP1 and higher |
|
0x54 |
PVOID AbortThrottle; |
5.0 only | |
0x64 |
ULONG PerfIdleTime; |
5.1 before Windows XP SP3; 5.2 before Windows Server 2003 SP2 |
|
ULONG LastIdleThreadKernelTime; |
5.1 from Windows XP SP3; 5.2 from Windows Server 2003 SP2 |
||
0x50 (5.0); 0x58 |
0x68 |
ULONGLONG DebugDelta; |
5.0; 5.1 before Windows XP SP3; 5.2 before Windows Server 2003 SP2 |
ULONG PackageIdleStartTime; |
5.1 from Windows XP SP3; 5.2 from Windows Server 2003 SP2 |
||
0x5C | 0x6C |
ULONG PackageIdleTime; |
5.1 from Windows XP SP3; 5.2 from Windows Server 2003 SP2 |
0x58 (5.0); 0x60 |
0x70 |
ULONG DebugCount; |
5.0 to 5.2 |
0x5C (5.0); 0x64 |
0x74 |
ULONG LastSysTime; |
5.0 to 5.2 |
The spare 0x28 bytes that end the structure for version 5.0 turns out to have been far too small an area for version 5.1 to keep to for its expansion.
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x60 (5.0); 0x68 |
ULONG Spare2 [10]; |
5.0 only | |
0x78 |
ULONGLONG TotalIdleStateTime [3]; |
5.1 to 5.2 | |
0x80 | 0x90 |
ULONG TotalIdleTransitions [3]; |
5.1 to 5.2 |
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x90 | 0xA0 |
ULONGLONG PreviousC3StateTime; |
5.1 to 5.2 |
0x98 | 0xA8 |
UCHAR KneeThrottleIndex; |
5.1 to 5.2 |
0x99 | 0xA9 |
UCHAR ThrottleLimitIndex; |
5.1 to 5.2 |
0x9A | 0xAA |
UCHAR PerfStatesCount; |
5.1 to 5.2 |
0x9B | 0xAB |
UCHAR ProcessorMinThrottle; |
5.1 to 5.2 |
0x9C | 0xAC |
UCHAR ProcessorMaxThrottle; |
5.1 to 5.2 |
0x9D | 0xAD |
UCHAR LastBusyPercentage; |
5.1 before Windows XP SP3; 5.2 before Windows Server 2003 SP2 |
UCHAR EnableIdleAccounting; |
5.1 from Windows XP SP3; 5.2 from Windows Server 2003 SP2 |
||
0x9E | 0xAE |
UCHAR LastC3Percentage; |
5.1 to 5.2 |
0x9F | 0xAF |
UCHAR LastAdjustedBusyPercentage; |
5.1 to 5.2 |
0xA0 | 0xB0 |
ULONG PromotionCount; |
5.1 to 5.2 |
0xA4 | 0xB4 |
ULONG DemotionCount; |
5.1 to 5.2 |
0xA8 | 0xB8 |
ULONG ErrorCount; |
5.1 to 5.2 |
0xAC | 0xBC |
ULONG RetryCount; |
5.1 to 5.2 |
0xB0 | 0xC0 |
ULONG Flags; |
5.1 to 5.2 |
0xB8 | 0xC8 |
LARGE_INTEGER PerfCounterFrequency; |
5.1 to 5.2 |
0xC0 | 0xD0 |
ULONG PerfTickCount; |
5.1 to 5.2 |
0xC8 | 0xD8 |
KTIMER PerfTimer; |
5.1 to 5.2 |
0xF0 | 0x0118 |
KDPC PerfDpc; |
5.1 to 5.2 |
0x0110 | 0x0158 |
PROCESSOR_PERF_STATE *PerfStates; |
5.1 to 5.2 |
0x0114 | 0x0160 |
PSET_PROCESSOR_THROTTLE PerfSetThrottle; |
5.1 to 5.2 |
0x0118 | 0x0168 |
ULONG Spare1 [2]; |
5.1 before Windows XP SP2; 5.2 before Windows Server 2003 SP1 |
ULONG LastC3KernelUserTime; |
5.1 from Windows XP SP2 and higher; 5.2 from Windows Server 2003 SP1 and higher |
||
0x011C | 0x016C |
ULONG Spare1 [1]; |
5.1 from Windows XP SP2; 5.2 from Windows Server 2003 SP1 |
ULONG LastPackageIdleTime; |
5.1 from Windows XP SP3; 5.2 from Windows Server 2003 SP2 |
All non-obvious types in these tables are structures or enumerations except for the following function pointers:
typedef VOID (FASTCALL *PPROCESSOR_IDLE_FUNCTION) (PROCESSOR_POWER_STATE *);
typedef VOID (FASTCALL *PSET_PROCESSOR_THROTTLE) (UCHAR);
The IdleFunction is called whenever the kernel is in its idle loop but has no thread to run.
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x00 (early 6.0) | 0x00 (early 6.0) |
PPROCESSOR_IDLE_FUNCTION IdleFunction; |
early 6.0 only |
0x04 (early (6.0); 0x00 |
0x08 (early 6.0); 0x00 |
PPM_IDLE_STATES *IdleStates; |
6.0 only |
0x08 | 0x10 (early 6.0); 0x08 |
ULONGLONG LastTimeCheck; |
6.0 only |
0x10 | 0x18 (early 6.0); 0x10 |
ULONGLONG LastIdleTime; |
early 6.0 only |
ULONGLONG IdleTimeAccumulated; |
late 6.0 only | ||
0x18 | 0x20 (early 6.0); 0x18 |
PROCESSOR_IDLE_TIMES IdleTimes; |
early 6.0 only |
union { struct { ULONGLONG IdleTransitionTime; } Native; struct { ULONGLONG LastIdleCheck; } Hv; }; |
late 6.0 only | ||
0x38 (early 6.0); 0x20 |
0x40 (early 6.0); 0x20 |
PPM_IDLE_ACCOUNTING *IdleAccounting; |
6.0 only |
0x3C (early 6.0); 0x24 |
0x48 (early 6.0); 0x28 |
PPM_PERF_STATES *PerfStates; |
6.0 only |
0x40 (early 6.0); 0x28 |
0x50 (early 6.0); 0x30 |
ULONG LastKernelUserTime; |
6.0 only |
0x44 (early 6.0); 0x2C |
0x54 (early 6.0); 0x34 |
ULONG LastIdleThreadKTime; |
6.0 only |
0x48 (early 6.0); 0x30 |
0x58 (early 6.0); 0x38 |
ULONGLONG LastGlobalTimeHv; |
6.0 only |
0x50 (early 6.0); 0x38 |
0x60 (early 6.0); 0x40 |
ULONGLONG LastProcessorTimeHv; |
6.0 only |
0x58 (early 6.0); 0x40 |
0x68 (early 6.0); 0x48 |
UCHAR ThermalConstraint; |
6.0 only |
0x59 (early 6.0); 0x41 |
0x69 (early 6.0); 0x49 |
UCHAR LastBusyPercentage; |
6.0 only |
0x5A (early 6.0); 0x42 |
0x6A (early 6.0); 0x4A |
union { USHORT AsUSHORT; struct { USHORT PStateDomain : 1; // 0x0001 USHORT PStartDomainIdleAccounting : 1; // 0x0002 USHORT Reserved : 14; }; } Flags; |
6.0 only |
0x60 (early 6.0); 0x48 |
0x70 (early 6.0); 0x50 |
KTIMER PerfTimer; |
6.0 only |
0x88 (early 6.0); 0x70 |
0xB0 (early 6.0); 0x90 |
KDPC PerfDpc; |
6.0 only |
0xA8 (early 6.0); 0x90 |
0xF0 (early 6.0); 0xD0 |
ULONG LastSysTime; |
6.0 only |
0xAC (early 6.0); 0x94 |
0xF8 (early 6.0); 0xD8 |
KPRCB *PStateMaster; |
6.0 only |
0xB0 (early 6.0); 0x98 |
0x0100 (early 6.0); 0xE0 |
ULONG_PTR PStateSet; |
6.0 only |
0xB4 (early 6.0); 0x9C |
0x0108 (early 6.0); 0xE8 |
ULONG CurrentPState; |
6.0 only |
0xB8 (early 6.0) | 0x010C |
ULONG Reserved0; |
early 6.0 only |
0xBC (early 6.0); 0xA0 |
0x0110 (early 6.0); 0xEC |
ULONG DesiredPState; |
6.0 only |
0xC0 (early 6.0) | 0x0114 |
ULONG Reserved1; |
early 6.0 only |
0xC4 (early 6.0); 0xA4 |
0x0118 (early 6.0); 0xF0 |
ULONG volatile PStateIdleStartTime; |
6.0 only |
0xC8 (early 6.0); 0xA8 |
0x011C (early 6.0); 0xF4 |
ULONG PStateIdleTime; |
6.0 only |
0xCC (early 6.0); 0xAC |
0x0120 (early 6.0); 0xF8 |
ULONG LastPStateIdleTime; |
6.0 only |
0xD0 (early 6.0); 0xB0 |
0x0124 (early 6.0); 0xFC |
ULONG PStateStartTime; |
6.0 only |
0xB4 | 0x0100 |
ULONG DiaIndex; |
late 6.0 only |
0xB8 | 0x0104 |
ULONG Reserved0; |
late 6.0 only |
0xD4 (early 6.0); 0xBC |
0x0128 (early 6.0); 0x0108 |
ULONG_PTR WmiDispatchPtr; |
6.0 only |
0xD8 (early 6.0); 0xC0 |
0x0130 (early 6.0); 0x0110 |
LONG WmiInterfaceEnabled; |
6.0 only |
Changes since Windows 7 have mostly been a matter of inserting, redefining or appending. There has been some reordering, however: see that IdleAccounting got moved forward for Windows 8.
Offset (x86) | Offset (x64) | Definition | Versions | Remarks |
---|---|---|---|---|
0x00 | 0x00 |
PPM_IDLE_STATES *IdleStates; |
6.1 and higher | |
0x04 | 0x08 |
PROC_IDLE_ACCOUNTING *IdleAccounting; |
6.2 and higher | previously at 0x20 |
0x08 (6.2) | 0x10 (6.2) |
PLATFORM_IDLE_ACCOUNTING *PlatformIdleAccounting; |
6.2 only | |
0x08 (6.1); 0x10 (6.2); 0x08 |
0x08 (6.1); 0x18 (6.2); 0x10 |
ULONGLONG IdleTimeLast; |
6.1 and higher | |
0x10 (6.1); 0x18 (6.2); 0x10 |
0x10 (6.1); 0x20 (6.2); 0x18 |
ULONGLONG IdleTimeTotal; |
6.1 and higher | |
0x18 (6.1); 0x20 (6.2); 0x18 |
0x18 (6.1); 0x28 (6.2); 0x20 |
ULONGLONG IdleTimeEntry; |
6.1 and higher | |
0x20 (6.1) | 0x20 (6.1) |
PROC_IDLE_ACCOUNTING *IdleAccounting; |
6.1 only | next at 0x04 and 0x08 |
0x28 (6.2); 0x20 |
0x30 (6.2); 0x28 |
ULONGLONG Reserved; |
6.2 to 6.3 | |
ULONGLONG IdleTimeExpiration; |
10.0 and higher | |||
0x28 | 0x30 |
UCHAR NoInterruptibleTransition; |
10.0 and higher | |
0x29 | 0x31 |
UCHAR PepWokenTransition; |
10.0 and higher | |
0x2A | 0x32 |
UCHAR Class; |
10.0 and higher | |
0x2C | 0x34 |
ULONG TargetIdleState; |
10.0 and higher | |
0x30 (6.2); 0x28 (6.3); 0x30 |
0x38 (6.2); 0x30 (6.3); 0x38 |
PROC_IDLE_POLICY IdlePolicy; |
6.2 and higher | |
0x38 (6.2); 0x30 (6.3); 0x38 |
0x40 (6.2); 0x38 (6.3); 0x40 |
PPM_IDLE_SYNCHRONIZATION_STATE volatile Synchronization; |
6.2 and higher | |
0x40 (6.2); 0x38 (6.3); 0x40 |
0x48 (6.2); 0x40 (6.3); 0x48 |
PROC_FEEDBACK PerfFeedback; |
6.2 and higher | |
0x24 (6.1); 0xA8 (6.2); 0xA0 (6.3); 0xC8 |
0x28 (6.1); 0xB8 (6.2); 0xB0 (6.3); 0xD8 |
PROC_HYPERVISOR_STATE Hypervisor; |
6.1 and higher | |
0x28 (6.1) | 0x2C (6.1) |
ULONG PerfHistoryTotal; |
6.1 only | |
0x2C (6.1) | 0x30 (6.1) |
UCHAR ThermalConstraint; |
6.1 only | |
0x2D (6.1) | 0x31 (6.1) |
UCHAR PerfHistoryCount; |
6.1 only | |
0x2E (6.1) | 0x32 (6.1) |
UCHAR PerfHistorySlot; |
6.1 only | |
0x2F (6.1) | 0x33 (6.1) |
UCHAR Reserved; |
6.1 only | |
0x30 (6.1); 0xAC (6.2); 0xA4 (6.3); 0xCC |
0x34 (6.1); 0xBC (6.2); 0xB4 (6.3); 0xDC |
ULONG LastSysTime; |
6.1 and higher | |
0x34 (6.1); 0xB0 (6.2); 0xA8 (6.3); 0xD0 |
0x38 (6.1); 0xC0 (6.2); 0xB8 (6.3); 0xE0 |
ULONG_PTR WmiDispatchPtr; |
6.1 and higher | |
0x38 (6.1); 0xB4 (6.2); 0xAC (6.3); 0xD4 |
0x40 (6.1); 0xC8 (6.2); 0xC0 (6.3); 0xE8 |
LONG WmiInterfaceEnabled; |
6.1 and higher | |
0x40 (6.1); 0xB8 (6.2); 0xB0 (6.3); 0xD8 |
0x48 (6.1); 0xD0 (6.2); 0xC8 (6.3); 0xF0 |
PPM_FFH_THROTTLE_STATE_INFO FFHThrottleStateInfo; |
6.1 and higher | |
0x60 (6.1); 0xD8 (6.2); 0xD0 (6.3); 0xF8 |
0x68 (6.1); 0xF0 (6.2); 0xE8 (6.3); 0x0110 |
KDPC PerfActionDpc; |
6.1 and higher | |
0x80 (6.1); 0xF8 (6.2); 0xF0 (6.3); 0x0118 |
0xA8 (6.1); 0x0130 (6.2); 0x0128 (6.3); 0x0150 |
LONG volatile PerfActionMask; |
6.1 and higher | |
0x88 (6.1); 0x0100 (6.2); 0xF8 (6.3); 0x0120 |
0xB0 (6.1); 0x0138 (6.2); 0x0130 (6.3); 0x0158 |
PROC_IDLE_SNAP IdleCheck; |
6.1 only | |
PROC_IDLE_SNAP HvIdleCheck; |
6.2 and higher | |||
0x98 (6.1); 0x0110 (6.2); 0x0108 (6.3); 0x0130 |
0xC0 (6.1); 0x0148 (6.2); 0x0140 (6.3); 0x0168 |
PROC_IDLE_SNAP PerfCheck; |
6.1 only | |
PROC_PERF_SNAP PerfCheck; |
6.2 to 6.3 | |||
PROC_PERF_CHECK *PerfCheck; |
10.0 and higher | |||
0xA8 (6.1); 0x0150 (6.2); 0x0148 (6.3); 0x0134 |
0xD0 (6.1); 0x0188 (6.2); 0x0180 (6.3); 0x0170 |
PROC_PERF_DOMAIN *Domain; |
6.1 and higher | |
0xAC (6.1); 0x0154 (6.2); 0x014C (6.3); 0x0138 |
0xD8 (6.1); 0x0190 (6.2); 0x0188 (6.3); 0x0178 |
PROC_PERF_CONSTRAINT *PerfConstraint; |
6.1 and higher | |
0x0158 (6.2); 0x0150 (6.3); 0x013C |
0x0198 (6.2); 0x0190 (6.3); 0x0180 |
PPM_CONCURRENCY_ACCOUNTING *Concurrency; |
6.2 and higher | |
0xB0 (6.1); 0x015C (6.2); 0x0154 (6.3); 0x0140 |
0xE0 (6.1); 0x01A0 (6.2); 0x0198 (6.3); 0x0188 |
PROC_PERF_LOAD *Load; |
6.1 and higher | |
0xB4 (6.1); 0x0160 (6.2); 0x0158 (6.3); 0x0144 |
0xE8 (6.1); 0x01A8 (6.2); 0x01A0 (6.3); 0x0190 |
PROC_HISTORY_ENTRY *PerfHistory; |
6.1 and higher | |
0x0164 (6.2); 0x015C (6.3); 0x0148 |
0x01B0 (6.2); 0x01A8 (6.3); 0x0198 |
UCHAR GuaranteedPerformancePercent; |
6.2 and higher | |
0x0165 (6.2); 0x015D (6.3); 0x0149 |
0x01B1 (6.2); 0x01A9 (6.3); 0x0199 |
UCHAR HvTargetState; |
6.2 and higher | |
0x0166 (6.2); 0x015E (6.3); 0x014A |
0x01B2 (6.2); 0x01AA (6.3); 0x019A |
UCHAR Parked; |
6.2 and higher | |
0x0167 (6.2); 0x015F (6.3) |
0x01B3 (6.2); 0x01AB (6.3) |
UCHAR OverUtilitized; |
6.2 to 6.3 | |
0x0168 (6.2); 0x0160 (6.3); 0x014C |
0x01B4 (6.2); 0x01AC (6.3); 0x019C |
ULONG LatestPerformancePercent; |
6.2 and higher | |
0x016C (6.2); 0x0150 |
0x01B8 (6.2); 0x01A0 |
ULONG AveragePerformancePercent; |
6.2; not 6.3; 10.0 and higher |
|
0x0170 (6.2); 0x0164 (6.3); 0x0154 |
0x01BC (6.2); 0x01B0 (6.3); 0x01A4 |
ULONG LatestAffinitizedPercent; |
6.2 and higher | |
0x0168 (6.3); 0x0158 |
0x01B4 (6.3); 0x01A8 |
ULONG ExpectedUtility; |
6.3 only | |
ULONG RelativePerformance; |
10.0 and higher | |||
0xB8 (6.1); 0x0174 (6.2); 0x016C (6.3); 0x015C |
0xF0 (6.1); 0x01C0 (6.2); 0x01B8 (6.3); 0x01AC |
ULONG Utility; |
6.1 to 6.2 | |
PROC_PERF_UTILITY Utility [3]; |
6.3 only | last member in 6.3 | ||
ULONG Utility; |
10.0 and higher | |||
0xBC (6.1) | 0xF4 (6.1) |
ULONG OverUtilizedHistory; |
6.1 only | |
0xC0 (6.1) | 0xF8 (6.1) |
ULONG volatile AffinityCount; |
6.1 only | |
0xC4 (6.1) | 0xFC (6.1) |
ULONG AffinityHistory; |
6.1 only | last member in 6.1 |
0x0178 (6.2); 0x0160 |
0x01C4 (6.2); 0x01B0 |
ULONG AffinitizedUtility; |
6.2; not 6.3; 10.0 and higher |
last member in 6.2 |
0x0168 | 0x01B8 |
union { ULONGLONG SnapTimeLast; ULONGLONG EnergyConsumed; }; |
10.0 and higher | |
0x0170 | 0x01C0 |
ULONGLONG ActiveTime; |
10.0 and higher | |
0x0178 | 0x01C8 |
ULONGLONG TotalTime; |
10.0 and higher | last member in 10.0 |