Back to Blog

Finding the Filter Driver Behind High CPU in Unrelated Windows Processes

Christopher 6 min read
windowsperformancetroubleshooting

When a monitoring agent, powershell.exe, and Process Explorer all show high CPU at the same time, the work often isn't theirs. A kernel filter driver that inspects their file activity can be using that CPU inside their own threads. This post covers how to confirm that with a built-in counter, list the filter drivers on the machine, and find the one in the thread stacks.

A recent r/sysadmin thread traced high CPU on monitoring agents, powershell.exe, and Process Explorer to a tamper protection driver from the endpoint security product on those machines. The poster asked for ways to find this kind of cause without WPA and ETW traces. The first three steps below need neither.

Why the wrong process shows the CPU

Microsoft's minifilter documentation says most pre-operation callbacks are called in the context of the thread that originated the I/O request. When a filter driver spends time on each file open, that time is counted against the process that opened the file. The processes with the most file activity then look like the cause, and the driver doesn't show up as a process at all.

Compare kernel time with user time

Time a thread spends in a driver is kernel-mode time, which Windows counts as privileged time. Sample both counters for every process for 30 seconds while the CPU is high.

Get-Counter '\Process(*)\% Privileged Time','\Process(*)\% User Time' -SampleInterval 5 -MaxSamples 6

A process that is busy with its own work mostly shows user time. When several unrelated processes show mostly privileged time at once, a driver is the likely source. Run the same command on a machine of the same build that doesn't have the problem, so you have a baseline to compare against.

List the filter drivers

Fltmc.exe is the system-supplied utility for managing minifilter drivers. From an elevated prompt, list the loaded filters with their altitudes, then the volumes each one is attached to.

fltmc filters
fltmc instances

Microsoft assigns each minifilter an altitude, and the altitude ranges follow load order groups. Antivirus filters are in the 320000 to 329999 range, and activity monitors that observe and report on file I/O are in 360000 to 389999. Check the filters in those ranges first, starting with any that belong to a product installed or updated around the time the CPU problem started.

Find the driver file and the product that installed it.

Get-CimInstance Win32_SystemDriver -Filter "Name='<filter name>'" | Select-Object Name,DisplayName,PathName,State

Open the properties of the file in PathName and read the company, product name, and file version on the Details tab. Write the version down for the vendor ticket.

Read the thread stacks

Process Explorer from Sysinternals shows the call stack of each thread. Install the Debugging Tools for Windows, then under Options and Configure Symbols set the dbghelp.dll path to the copy in the Debuggers folder of the Windows Kits and set the symbols path to the Microsoft symbol server.

srv*C:\symbols*https://msdl.microsoft.com/download/symbols

Run Process Explorer as administrator. Open the properties of one of the busy processes, go to the Threads tab, select the thread with the most CPU, and click Stack. The stack is a snapshot, so click Refresh a few times while the CPU is high.

A frame from a third-party driver shows as the .sys file name with an offset, because the Microsoft symbol server has no symbols for it. Do the same in a second process that has nothing to do with the first. The same driver in the stacks of both is the evidence to send to the vendor.

Confirm it with a trace

When you need a record of the whole machine, Windows Performance Recorder captures CPU samples with stacks. WPR and Windows Performance Analyzer come with the Windows Performance Toolkit in the Windows ADK. Record for about a minute while the CPU is high.

mkdir C:\Traces
wpr -start CPU -filemode
wpr -stop C:\Traces\highcpu.etl

Open C:\Traces\highcpu.etl in WPA. On the Trace menu, click Load Symbols. Open the CPU Usage (Sampled) graph, add the Thread ID and Stack columns, and expand the stacks of the busy processes. The filter driver's .sys module appears in the stacks of each process it is slowing down, and the Weight column shows how much sampled CPU falls under it.

Fix the cause

Send the stacks, the driver file version, and the counter output from both machines to the vendor of the product that installed the driver. Check that vendor's release notes for a newer agent version that addresses CPU use. Don't unload a security product's filter with fltmc to test the theory on a production machine, because that removes the protection it provides.

I'd test any exclusion or setting change the vendor suggests on one machine first. Run the Get-Counter command again afterward and compare privileged time with the numbers you recorded before the change.

Where ET Ducky shows it

I build ET Ducky, and its Windows agent reads the kernel event stream through ETW on every managed endpoint. You can open a Live Session shell from the browser without the need for a VPN and run the Get-Counter and fltmc commands above on the machine with the high CPU. A Live Session also runs an AI-driven investigation in which the AI picks the ETW providers for each round. Only counts-only summaries leave the host, and the raw kernel events stay on it.

You'd start a Multi-Agent Session across a machine with the high CPU and one without it. It compares the kernel events from both to find the outlier. A CPU alert on that machine also gets an automatic root-cause analysis with the cause, impact, mitigation, prevention, and a confidence level.

Try it on one server

One Windows or Linux server on the Free query tier with your own AI key costs $5 a month.

See pricing