Back to Blog

Capturing Windows Memory Leak Events with ETW VirtualAlloc and Microsoft-Windows-Kernel-Memory

Christopher 5 min read
etwwindowsmemorydiagnostics

This post documents how memory events are captured with ETW on Windows. It covers which signals come from the classic kernel-provider keywords, which of those keywords silently require the NT Kernel Logger session, what the Microsoft-Windows-Kernel-Memory manifest provider covers instead, and which combination the ET Ducky agent's memory-growth profile settled on for diagnosing leaks with per-process attribution. Most of these constraints are undocumented or documented only in TraceEvent source comments, and we hit each of them while building the profile.

Three routes to memory events

Windows exposes memory telemetry through three distinct mechanisms that are easy to conflate:

  1. Classic kernel keywords on a kernel trace session. These include VirtualAlloc, Memory (page faults), MemoryHardFaults, and ReferenceSet, among others, enabled as flag bits when the session starts.
  2. The Microsoft-Windows-Kernel-Memory manifest provider, enabled by GUID like any user-mode provider, which emits periodic memory-info snapshots and working-set data rather than per-operation events.
  3. Heap snapshots (the xperf HeapSnapshot mechanism), which have no kernel keyword at all and require a separate capture workflow.

The choice between them is constrained. Several of the classic keywords only function on one specific session, and the manifest provider answers a different question than the keywords do.

The custom-session constraint

Since Windows 8, kernel providers can be enabled on a custom-named trace session rather than the single system-wide NT Kernel Logger. A custom session name is the default for an agent because it cannot collide with another tool claiming the NT Kernel Logger, and multiple diagnostic sessions can coexist. The ET Ducky engine creates its kernel session as ETDucky_Kernel for that reason.

Not every kernel keyword works on a custom session. In our engine the following are detected and skipped with a logged warning rather than enabled:

Setting these flags on a custom session produces no error, and the events are not delivered. A further set of memory-adjacent capabilities have no kernel keyword at all and can only be reached by enabling a manifest provider by GUID. These are memory compression state, Microsoft-Windows-Kernel-Power, Microsoft-Windows-Kernel-Session, job objects, and object-manager tracking beyond what Handle covers.

What the VirtualAlloc keyword captures

The VirtualAlloc keyword is the core leak signal. It emits an event per virtual-memory operation, covering reserve, commit, and free, with the allocating process attributable from the event. The data is page-granular. You see committed memory climb and whether it is ever released, which is the pattern most native leaks produce. Heap-allocation detail inside those pages is not included. A process that commits one large region and leaks small heap blocks inside it shows a single commit. Heap snapshots cover that case, and they sit outside the kernel-keyword system.

The adjacent keyword is Memory, which enables full page-fault tracing. It is unusable for multi-minute captures. Page faults on a busy machine arrive at very high volume and add little leak signal over the VirtualAlloc stream, because a leaking process faults constantly whether or not each fault is recorded. The ET Ducky memory-growth profile leaves it off. MemoryHardFaults is the better pressure signal. Hard faults, meaning paging from disk, are orders of magnitude rarer than soft faults, so the keyword is cheap. A rising hard-fault rate indicates memory pressure that is causing disk I/O.

Attribution requires more than the memory keywords

A VirtualAlloc event carries a process ID. Turning that into a per-process account over a multi-minute window requires three more keywords running alongside:

The complete keyword set for the ET Ducky memory-growth profile is therefore: Process + ImageLoad + ProcessCounters + VirtualAlloc + MemoryHardFaults, with page-fault tracing off and heap snapshots documented as out of scope.

Capture duration in the memory-growth profile

A leak shows up as a trend over time. A sub-minute capture cannot distinguish a leak from ordinary allocation churn, because most processes allocate and free in bursts that are much larger than a slow leak's slope. The profile runs three minutes by default. A three-minute kernel capture is heavier than a snapshot, so it is gated behind an explicit operator opt-in rather than started automatically by the diagnostic chat flow. The agent also pauses its baseline monitoring for the capture window and resumes afterward, so the capture does not measure the agent's own monitoring overhead.

What the Microsoft-Windows-Kernel-Memory provider covers

The manifest provider (Microsoft-Windows-Kernel-Memory) answers the question of what the memory state of the system is over time, rather than which process allocated what. It emits periodic memory-information snapshots, both system-wide and per-process working-set data, on an interval instead of a per-operation event stream. That makes it cheap enough to leave running continuously, which is how resource-monitoring products use it. It does not attribute individual allocations, so it cannot answer the leak question by itself. The VirtualAlloc keyword cannot cheaply answer the trend question at fleet scale. The two are used together. As a manifest provider it is enabled by GUID on an ordinary non-kernel session, so none of the custom-session keyword caveats above apply to it.

Summary table

SignalMechanismCustom session?Use for
VirtualAlloc (reserve/commit/free)Kernel keywordYesNative leak attribution
Page faults (all)Kernel keyword MemoryYes, but volume-prohibitiveShort targeted captures only
Hard faultsKernel keyword MemoryHardFaultsYesMemory-pressure signal, low volume
Working-set reference trackingKernel keyword ReferenceSetNo, NT Kernel Logger onlyDeep working-set analysis
Periodic memory-info snapshotsMicrosoft-Windows-Kernel-Memory by GUIDN/A (manifest provider)Continuous trend monitoring
Heap allocationsHeap snapshot workflowN/A (no kernel keyword)Small-object leaks inside committed regions
Memory compressionGUID provider onlyN/ACompression-store behavior

When a memory capability appears to be missing from a custom kernel session, check whether it is an NT-Kernel-Logger-only keyword, a GUID-only manifest provider, or a separate workflow before concluding the events do not exist. All three of these cases fail without an error.

How this is used in practice

In ET Ducky this profile backs the memory-leak investigation that an operator launches from an agent's properties panel. The agent runs the three-minute capture, the correlated event stream goes through root-cause analysis, and the result is rendered as an explanation plus a per-process allocate/free table. The same profile definitions are used by the AI investigation planner when a memory question arrives in a diagnostic session. The capture engine, profile definitions, and session-keyword handling described here are in the agent codebase. The desktop app's local monitoring uses the same engine.

ET Ducky

Documentation and pricing are available on this site.

View Pricing