EVENT_TRACE_SPINLOCK_INFORMATION

The EVENT_TRACE_SPINLOCK_INFORMATION structure is one of many that the ZwQuerySystemInformation (or NtQuerySystemInformation) and ZwSetSystemInformation (or NtSetSystemInformation) functions expect in their information buffer when given the information class SystemPerformanceTraceInformation (0x1F). This particular structure is selected when the first dword in the information buffer on input is EventTraceSpinlockInformation (0x05).

The remaining members of the structure are parameters that govern the tracing of events that sample the acquisition and release of spin locks. This tracing is enabled for NT Kernel Logger sessions for which the group mask PERF_SPINLOCK (0x20010000) is set. The events have the Hook ID PERFINFO_LOG_TYPE_SPINLOCK (0x0529), with event-specific data in the form of a WMI_SPINLOCK structure. While this tracing is enabled, the kernel tracks the acquisition and release of spin locks, and writes an event whenever a lock’s release satisfies the sampling conditions that are described by this structure’s parameters.

Documentation Status

The EVENT_TRACE_SPINLOCK_INFORMATION structure is not documented but a C-language definition is published in the NTETW.H from the Enterprise edition of the Windows Driver Kit (WDK) for Windows 10 version 1511.

Were it not for this relatively recent and possibly unintended disclosure, much would anyway be known from type information in symbol files. Curiously though, type information for this structure has never appeared in any public symbol files for the kernel or for the obvious low-level user-mode DLLs. In the whole of Microsoft’s packages of public symbol files, at least to the original Windows 10, relevant type information is unknown before Windows 8 and appears in symbol files only for AppXDeploymentClient.dll, CertEnroll.dll (before Windows 10) and Windows.Storage.ApplicationData.dll.

Layout

The EVENT_TRACE_SPINLOCK_INFORMATION is originally 0x10 bytes in both 32-bit and 64-bit Windows. An addition for version 6.3 lengthens the structure to 0x14 bytes.

Offset Definition Versions
0x00
EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass;
6.1 and higher (x64);
6.2 and higher (x86)
0x04
ULONG SpinLockSpinThreshold;
6.1 and higher (x64);
6.2 and higher (x86)
0x08
ULONG SpinLockAcquireSampleRate;
6.1 and higher (x64);
6.2 and higher (x86)
0x0C
ULONG SpinLockContentionSampleRate;
6.1 and higher (x64);
6.2 and higher (x86)
0x10
ULONG SpinLockHoldThreshold;
6.3 and higher

The original structure up to but not including SpinLockHoldThreshold is defined separately as EVENT_TRACE_SPINLOCK_INFORMATION_V1 in version 6.3 and higher.

Plausibly the EVENT_TRACE_SPINLOCK_INFORMATION structure is defined for 32-bit Windows 7 but just isn’t used. Though the 64-bit kernel’s code for spin locks had been in C (or C++) from the start, i.e., for Windows Server 2003 SP1, the corresponding code in the 32-bit kernel is still in assembly language in Windows 7. Its evolution from Windows NT 3.1 had gone as far as adding hypervisor notifications and, for Windows 7, the maintenance of performance counters in the KPRCB, but there it was left. Not until Windows 8 does 32-bit Windows trace events for spin locks.

Behaviour

To set these members in version 6.2 and higher, the caller must have TRACELOG_GUID_ENABLE access to the NT Kernel Logger.

Constraints on the parameters are enforced when setting information, else the function returns STATUS_INVALID_PARAMETER:

For each, the least permitted value is anyway what the kernel starts with and is thus the default.

A PERFINFO_LOG_TYPE_SPINLOCK event is written whenever a spin lock is released, if PERF_SPINLOCK is enabled and any of the following conditions are met:

Uncontended acquisition of spin locks is highly desirable, hopefully ordinary and correspondingly uninteresting, such that relatively few (if any) are usefully sampled: hence the high minimum for the SpinLockAcquireSampleRate. The default is, in effect, to trace roughly every thousandth uncontended acquisition. See that the rate is an inverse: increasing it means that uncontended acquisitions are sampled less frequently.

The default for contended acquisitions is to trace them all. For some purposes this may be excessive. Occasional contention is expected and ordinary. Tracing them all can generate very many events very quickly. One way is to reduce the volume is to increase the SpinLockContentionSampleRate and thus trace only a roughly random sample of contended acquisitions. Another is to raise SpinLockSpinThreshold while keeping the SpinLockContentionSampleRate at 1. This selectively ignores the acquisitions that proceeded with only a few spins in favour of tracing those whose wait was more problematic.

Whether a lock’s acquisition was immediate or contended, and whatever filtering is applied by the original three parameters, it is a serious problem if the time from (finally) acquiring the lock until releasing it is too long. The SpinLockHoldThreshold is in the same processor cycles that are counted by the rdtsc instruction. For processors with clock speeds of a few GHz, the default is an order of magnitude larger than Microsoft’s long-standing warnings that “No routine should hold a spin lock for longer than 25 microseconds.”