WMI_CONTEXTSWAP

The WMI_CONTEXTSWAP is one of many types of fixed-size header that begin the data for an event as held in the trace buffers or flushed to an Event Trace Log (ETL) file for an NT Kernel Logger session. The event is specifically PERFINFO_LOG_TYPE_CONTEXTSWAP (0x0524).

Usage

The PERFINFO_LOG_TYPE_CONTEXTSWAP event traces context swaps, of course. Here, context swap means a change of thread: a processor switches from running an old thread to a new thread.

For any particular NT Kernel Logger session to be sent this event, the group mask PERF_CONTEXT_SWITCH (0x20000004) must be enabled. For compatibility with the EnableFlags in the EVENT_TRACE_PROPERTIES that is input to StartTrace and ControlTrace, this group mask maps to EVENT_TRACE_FLAG_CSWITCH (0x00000010).

Documentation Status

Microsoft has long documented the structure in a syntax that is “simplified from MOF code” as the CSwitch class. As found online today (21st December 2016), this documentation has two members labelled spare or reserved, which they have not been since at least Windows Vista.

The WMI_CONTEXTSWAP structure itself, however, is not documented but a C-language definition is published in the NTWMI.H header from some editions of the Windows Driver Kit (WDK) for Windows 10.

Layout

Data for the PERFINFO_LOG_TYPE_CONTEXTSWAP event (as it exists in the trace buffers) comprises:

Trace Header

In the PERFINFO_TRACE_HEADER, the Size is the total in bytes of the trace header and all the event data. The HookId is PERFINFO_LOG_TYPE_CONTEXTSWAP, which identifies the event.

The Marker is, at its most basic, 0xC0100002 (32-bit or 0xC0110002 (64-bit). Additional flags may be set to indicate that extended data items are inserted between the trace header and the event data. Ordinarily, however, the event data follows as the trace header’s Data array.

Event Data

The event data is just the one fixed-size structure. This WMI_CONTEXTSWAP is 0x18 bytes in both 32-bit and 64-bit Windows:

Offset Definition
0x00
ULONG NewThreadId;
0x04
ULONG OldThreadId;
0x08
CHAR NewThreadPriority;
0x09
CHAR OldThreadPriority;
0x0A
union {
    UCHAR PreviousCState;
    UCHAR OldThreadRank;
};
0x0B
union {
    CHAR NewThreadPriorityDecrement;
    CHAR SpareByte;
};
0x0C
UCHAR OldThreadWaitReason;
0x0D
CHAR OldThreadWaitMode;
0x0E
UCHAR OldThreadState;
0x0F
UCHAR OldThreadIdealProcessor;
0x10
ULONG NewThreadWaitTime;
0x14
LONG OldThreadRemainingQuantum;

Most of the members are read straightforwardly from similarly named members of the old or new thread’s ETHREAD. Not obvious from the types is that the OldThreadWaitReason, OldThreadWaitMode and OldThreadState take their values from the documented KWAIT_REASON and KPROCESSOR_MODE and the undocumented KTHREAD_STATE enumerations, respectively. Note also the truncation of the ideal processor from a ULONG: the OldThreadIdealProcessor is unreliable for computers with more than 256 processors.

The OldThreadRemainingQuantum is the old thread’s QuantumTarget less its CycleTime, divided by 1024.

In the union at offset 0x0A, the PreviousCState applies if the old thread is the idle thread for its processor.