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). It was introduced with Windows XP.

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. The event records the identifiers of each thread and a small selection of possibly interesting properties, mostly of the outgoing thread.

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

Documentation Status

Microsoft has long documented the WMI_CONTEXTSWAP structure as an MOF class named CSwitch using a syntax that is “simplified from MOF code”. To say long documented is to take only a modern perspective. This useful diagnostics support seems to have been kept for use only by Microsoft’s tools for roughly its first five years: the structure and event date from Windows XP but the first known documentation is in the Software Development Kit (SDK) for Windows Vista.

For what MOF code the documentation simplified from, the only known candidate is in a file named WMICORE.MOF which is distributed not with the SDK and the documentation but with the Windows Driver KIt (WDK), again starting with Windows Vista. Microsoft apparently had some trouble synchronising the documentation’s simplification with the MOF definition, let alone with the reality of what the kernel actually does write as the event data. What’s documented for Windows Vista mixes old and new formats. The MOF definition, simplified or not, never truly is that of the structure. For instance, even as found online today, 3rd November 2022, the documentation names the last property as Reserved and describes another as “Not used”, which is no more true now than for Windows Vista.

Though the WMI_CONTEXTSWAP structure itself is not documented, a C-language definition was published in the NTWMI.H header from the original and Version 1511 editions of the Windows Driver Kit (WDK) for Windows 10.

Otherwise, the structure is known from type information in a smattering of symbol files, starting with Windows 8. These are all private symbol files that Microsoft has included, possibly by oversight, in the usual downloadable packages of public symbol files for Windows and continues to make available at the public symbol server. A few of these private symbol files show that their corresponding (user-mode) binaries were built with NTWMI.H among the headers that were included by the source files. Thus do these few symbol files have type information for the WMI_CONTEXTSWAP.

Variability

The point to the MOF definition is that the structure is intended for interpretation by user-mode tools for performance monitoring. These may be fed data that was collected from a different computer which ran a different Windows version. Changes to the structure are allowed but are few and orderly. A progression through four distinct formats has changed the size only once:

Version Size
5.1 to 5.2 0x10
6.0 to 2004 0x18

The WMI_CONTEXTSWAP structure itself does not carry an indication of its format. This is instead a byte in the trace header of the event for which the structure is event-specific data.

Layout

As it exists in the trace buffers, a PERFINFO_LOG_TYPE_CONTEXTSWAP event comprises:

Trace Header

As for any PERFINFO_TRACE_HEADER, the four-byte Marker at the start is at its most basic either 0xC0100000 (32-bit) or 0xC0110000 (64-bit). To this may be added bit flags to indicate that extended data items are inserted between the trace header and the event data. The low byte, however, is particular to the event. It tells which version of the event is recorded. Different versions of the kernel write different versions of the event:

Event Version Windows Versions
0x01 5.1 to 5.2
0x02 6.0 to 1607
0x03 1703
0x04 1709 to 2004

After the Marker, the Size is the total in bytes of the trace header and all the event data. That the event records a context swap is indicated by its having PERFINFO_LOG_TYPE_CONTEXTSWAP as its HookId. Except if the Marker indicates the insertion of extended data items, the event data follows as the trace header’s Data array.

Event-Specific Data

The event-specific data for a PERFINFO_LOG_TYPE_CONTEXTSWAP event is just the one fixed-size WMI_CONTEXTSWAP structure. Names, types and offsets are from symbol files, as noted above, for Windows 8 and higher.

Offset Definition Versions
0x00
ULONG NewThreadId;
5.1 and higher
0x04
ULONG OldThreadId;
5.1 and higher
0x08
CHAR NewThreadPriority;
5.1 and higher
0x09
CHAR OldThreadPriority;
5.1 and higher
0x0A
CHAR NewThreadQuantum;
5.1 to 5.2
union {
    UCHAR PreviousCState;
    UCHAR OldThreadRank;
};
6.0 and higher
0x0B
CHAR OldThreadQuantum;
5.1 to 5.2
union {
    CHAR NewThreadPriorityDecrement;
    CHAR SpareByte;
};
6.0 and higher
0x0C
UCHAR OldThreadWaitReason;
5.1 and higher
0x0D
CHAR OldThreadWaitMode;
5.1 and higher
union {
    struct {
        UCHAR OldThreadWaitMode : 1;
        UCHAR OldThreadBamEppImportant : 1;
        UCHAR NewThreadBamEppImportant : 1;
        UCHAR Reserved : 5;
    }
    UCHAR Flags;
};
1703 only
union {
    struct {
        UCHAR OldThreadWaitMode : 1;
        UCHAR OldThreadBamQosLevel : 3;
        UCHAR NewThreadBamQosLevel : 3;
        UCHAR Reserved : 1;
    }
    UCHAR Flags;
};
1709 and higher
0x0E
UCHAR OldThreadState;
5.1 and higher
0x0F
UCHAR OldThreadIdealProcessor;
5.1 and higher
0x10
ULONG NewThreadWaitTime;
6.0 and higher
0x14
LONG OldThreadRemainingQuantum;
6.0 and higher

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 names NewThreadQuantum and OldThreadQuantum are from the MOF definitions of CSwitch_V1.

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

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