PROCESSOR_POWER_STATE

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.

Original (Before Windows Vista)

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

Appended for Windows XP

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.

Windows Vista

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

Windows 7 and Higher

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