Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sysdecay

Find out why a Windows machine gets slower the longer it stays on — without installing a kernel driver.

Two tools for diagnosing the failure mode where a PC is fine after boot and unusable three days later:

Tool Answers
sysdecay.py Is the system stalling, and what is accumulating?
mousetrace.py Is the cursor misbehaving, and is it the machine or the mouse?

Pure Python, standard library plus psutil. No kernel driver, no signed binary, no install.


Why this exists

The usual answer to "my PC has micro-stutters" is LatencyMon. But its kernel driver (rspLLL64.sys) is increasingly blocked on modern Windows: Microsoft has been removing trust for legacy cross-signed drivers, and the block is rolling out through Windows Update. On an affected machine LatencyMon either refuses to run or silently collects nothing.

These tools measure from user mode only. Nothing to sign, nothing to block.

They also target a different question. Most tools give you a snapshot. Decay is a trend — you need to see what climbs over hours, and what it correlates with.


Install

pip install psutil

Download sysdecay.py and mousetrace.py. That's it — each is standalone, no shared modules.

Run from an Administrator prompt. Without elevation, per-process stats for system-owned processes come back blank, which is often exactly where a leak hides.


sysdecay.py

python sysdecay.py                     # live dashboard, Ctrl+C for report
python sysdecay.py --log run1.csv      # CSV timeline
python sysdecay.py --headless          # alerts only
python sysdecay.py --duty 1.0          # spend a full core; catches more

What it measures

Stall detection. A thread pinned at time-critical priority spins on perf_counter() and records every gap between consecutive reads. If something blocks the kernel for 30 ms, that gap shows up.

This is deliberately not a sleep()-based loop. Sleep cannot resolve finer than the system timer tick, and when that tick is the 15.6 ms default, every stall snaps onto a 15.6 ms grid — your histogram then describes the instrument, not the machine. The giveaway is a near-empty 5–10 ms bucket beside an overflowing 10–25 ms one. Spinning has sub-microsecond resolution and no dependence on timer granularity, at the cost of CPU. The loop is duty-cycled, and all rates are computed against time actually observed.

Resource outliers. Individual processes hoarding handles or threads. This is the highest-signal thing the tool produces, and it needs no trend data. A dev box legitimately runs 20,000 threads across 900 processes, so aggregate alerts fire constantly and mean nothing — but one ordinary process sitting on 100,000 handles is never legitimate.

Leak trends. Kernel nonpaged/paged pool, system handle and thread counts, process count, and commit charge, each with a least-squares slope so you get MB per hour rather than a number with no context.

Multiple instances. Executables accumulating copies of themselves. Browsers are expected here; a photo viewer running fourteen times is not.

Memory pressure. Hard page-in rate via PDH, qualified by available RAM. High page-in with plenty of free memory is ordinary file-backed I/O, not thrashing — the tool distinguishes them rather than crying wolf.

CSV timeline. Every sample keyed by uptime. Chart stalls_per_min against nonpaged_mb and see which curve bends first: that one is the cause, the other is the symptom.

Reading the output

Work top-down and stop at the first thing that's obviously wrong:

  1. RESOURCE OUTLIERS — if anything is here, start here. Restart that process and watch whether the count returns.
  2. Stall cadence — regular means timer-driven (polling service, driver watchdog). Bursty means contention.
  3. Stall magnitude — sub-millisecond is scheduler noise. 1–5 ms suggests DPCs. A floor at 10 ms or above suggests I/O or something below the OS.
  4. Growth rates — nonpaged pool is allocated almost exclusively by kernel drivers, so a climb there points at a driver rather than an app.

mousetrace.py

python mousetrace.py                   # 30s capture
python mousetrace.py --seconds 60

Move the mouse in slow, steady circles for the whole capture.

Separates four failure modes that feel identical to the user but have completely different causes:

Finding Means Look at
Freezes Cursor stops mid-motion The system, USB stack
Jumps Position teleports Dropped reports, wireless interference, USB controller
Reversals Direction flips during steady motion The sensor — surface, dust, hardware
Low rate Effective Hz below spec Polling config, USB port, cable

The reversal test matters most. If the cursor changes direction while you move it smoothly, that is a sensor fault, and no amount of driver or OS work will fix it. Knowing that saves you from debugging the wrong machine entirely.


A worked example

A machine reported micro-stutters that worsened over roughly a day and cleared on reboot. Six days of uptime.

sysdecay reported 25,000 threads, 650,000 handles, 3.7 GB nonpaged pool, 1,083 processes. Every one of those is far outside normal — and every one was a red herring. That's just what a busy developer machine with 237 Chrome processes, WSL2, Docker and two GPUs looks like.

The actual cause was a vendor utility holding 100,000 handles — 15% of every handle on the system — and growing about 950/hour. It repeatedly enumerated HID devices looking for hardware, leaking a handle each pass. As its handle table grew, each enumeration took longer, and mouse input reports were dropped in the gaps. Fine at boot, unbearable after a day.

After ending that process, mousetrace reported 0 reversals, 921 Hz effective rate, 1.00 ms typical update interval — a 1000 Hz mouse behaving exactly to spec.

(A note on rigour: the raw jump counts from that session were unreliable. An early version of the jump detector used an absolute distance threshold, which false-positives on any fast movement — at speed it flagged essentially every update. The reversal count, update rate and interval were unaffected, and those are what actually confirmed the fix. The detector now compares against recent velocity instead, so a flick is not mistaken for a teleport.)

LatencyMon would never have found it. It isn't a driver stalling the kernel — it's userland hammering the input stack.

The lesson is baked into the tool: the outlier panel now fires on a single hoarding process on the first sample, before any aggregate alert. Aggregate numbers on a busy machine are mostly noise.


Limitations

Read these before trusting a result.

  • Windows only. Both use Win32 APIs. sysdecay degrades gracefully elsewhere for development; mousetrace requires GetCursorPos.
  • Stalls are detected, not attributed. These tools prove that the system was blocked and when. They cannot name the driver — that genuinely requires kernel access. For attribution, use Windows Performance Recorder. Think of sysdecay as the thing that catches the fault in the act and tells you when to record.
  • Thresholds are heuristics. "Normal is 2,000–4,000 threads" is a rough guide, not a measurement. Heavy dev machines legitimately exceed every aggregate threshold here. Treat outliers and trends as signal; treat totals as context.
  • Spinning costs CPU. Default duty is 40% of one core. Lower it with --duty if that matters.
  • Duty cycling creates blind spots. Rates are correct because they're computed against observed time, but a short-lived event can be missed.
  • PDH counters need a stable interval. Reads closer together than one second are refused rather than reported, because a rate counter with a near-zero denominator produces fiction.

Contributing

Useful directions:

  • Validation of the threshold heuristics across varied hardware
  • ETW integration for actual stall attribution without a driver
  • Raw Input (WM_INPUT) capture in mousetrace for true device-level timing
  • Tests — there are currently none worth the name

License

MIT. See LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages