| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| StressLab.exe | 2026-07-14 | 44.2 MB | |
| README.md | 2026-07-14 | 88.1 kB | |
| stresslab.py | 2026-07-14 | 1.2 MB | |
| CHANGELOG.md | 2026-07-14 | 236.8 kB | |
| QUICKSTART.md | 2026-05-15 | 17.3 kB | |
| LICENSE | 2026-04-28 | 1.1 kB | |
| THIRD_PARTY_LICENSES | 2026-04-28 | 9.0 kB | |
| requirements.txt | 2026-02-24 | 71 Bytes | |
| Totals: 8 Items | 45.8 MB | 315 |
StressLab v12.78
Internal Hardware Testing Suite for Windows
StressLab is an all-in-one hardware stress testing, diagnostics, and Windows repair tool built for PC builders, repair shops, and QA workflows. It runs sequential stress tests across CPU, RAM, GPU, VRAM, Disk, and combined Power load, with a Transient/Switch load-step test, live temperature monitoring, deep SMART diagnostics, SMART self-tests, Windows system repair (SFC/DISM), and one-click service resets.
What's new in v12.78
Idle-temp warning raised to 70 C (error tier 80 C). Animated startup splash with a spinning ring and status lines while the UI builds. Health-check progress bar with a live status label naming the current check, so long checks read as working, not stuck.
What's new in v12.77
Fixed microcode "unknown" (4-byte values). Some systems store the microcode revision as a 4-byte value rather than the 8-byte MSR image; the reader now handles both, so a 13900K correctly reports 0x12B instead of "unknown".
What's new in v12.76
Fixed microcode "unknown" on AMD. The microcode revision now reads correctly on AMD CPUs (the value lives in the low 32 bits on AMD vs the high 32 bits on Intel; v12.75 only read the Intel half).
What's new in v12.75
Memory channels via raw SMBIOS — dual-channel kits that read as single-channel on some boards are now detected from the SMBIOS Type 17/Type 20 tables (with interleave depth as the authoritative signal). CPU microcode check flags 13th/14th gen Intel on pre-0x12B microcode. AMD fTPM flag warns when the fTPM version (e.g. 3.92.0.5) can break Call of Duty anti-cheat. Power-drop throttling detection on the CPU test tags repeated power dips as thermal (CPU at TjMax) or VRM/power-delivery (CPU cool) throttling.
What's new in v12.74
GPU panel follows the selected GPU. The upper-right load % and the card detail line beneath the VPH load readout now update to reflect the currently selected GPU (any card, not just in multi-GPU "All" mode), so selecting the iGPU vs the discrete card shows each one's own load/power/temp and name.
What's new in v12.73
Fixed: GPU power helper orphaned on window close. Closing the window mid-test now terminates the gpu_power_helper subprocess(es) and any multi-GPU VRAM instances instead of leaving them running.
What's new in v12.72
View follows GPU selection. Choosing a specific GPU to stress now auto-switches the panel/graphs to display that card (instead of staying on "All"/the discrete card); the View initializes to the default stress target too. Hard 100% load cap — utilization is clamped to 0-100% so a sensor reporting 100.11% no longer shows as 101%.
What's new in v12.71
AMD GPU power sensor handling. The per-GPU LHM telemetry now picks the total-board power sensor on AMD cards ("GPU Package" / "GPU PPT") instead of a sub-rail, while NVIDIA/Intel continue to use "GPU Power". AMD utilization and temperature come from the GPU Core load and GPU Core / Hot Spot sensors.
What's new in v12.70
Per-GPU telemetry for Intel/AMD GPUs. Cards nvidia-smi can't read now get their load/power/temp from the LibreHardwareMonitor sensor dump (Intel utilization from the D3D 3D engine load, AMD from the GPU Core load, power from GPU Power), feeding the same per-GPU panel segments, pop-outs, and report charts as NVIDIA cards — marked "(LHM)". Runs only when a non-NVIDIA GPU is present; the NVIDIA path is unchanged.
What's new in v12.69
Battery health detail. The battery status window, completion dialog, and report now report design/full-charge capacity (Wh and mAh), health %, wear %, voltage, charge cycles, and chemistry where the firmware exposes them.
Hybrid-laptop GPU selection. On laptops with an iGPU + discrete GPU, the panel header now shows the discrete card and its VRAM (e.g. "UHD Graphics / RTX 3060 Laptop GPU · 6 GB"), and a GPU selector lets you choose the discrete card (default) or the iGPU — like a desktop multi-GPU system. The iGPU is never auto-loaded by "All".
What's new in v12.68
Per-GPU overlay charts in the detailed-charts modal. GPU temperature, load, and power now plot one labelled line per GPU on a shared axis (legend GPU0/GPU1/…) in the "View detailed charts" view, for direct card-to-card comparison on multi-GPU runs.
What's new in v12.67
Default test length is now 15 minutes. Per-GPU report detail charts now fall back to the live per-card telemetry history if the at-stop snapshot is empty, and the run log records the per-card telemetry point counts so a missing chart can be diagnosed at a glance (data vs rendering).
What's new in v12.66
Report per-GPU selector now switches correctly. The previous CSS-only tab switching was overridden by the report's stylesheet, leaving all panels visible at once. Panel visibility is now driven by inline styles + a small onchange handler, so selecting All / GPU0 / GPU1 shows just that card's results and its own util/power/temp charts.
What's new in v12.65
Fixed the empty per-GPU report selector and added per-GPU charts. The per-GPU data was read from live state that the stop step had already cleared before the report captured it, so the GPU card's selector never appeared. The run data (including each card's util/power/temp history) is now archived at stop and read from there, and each GPU's report tab now shows its own utilization, power, and temperature charts.
What's new in v12.64
Power & Transient tests now multi-GPU. The Power test auto-selects the Vulkan helper and loads all GPUs simultaneously when multiple are detected; the Transient test pulses the primary card (gated shader) while holding secondary cards at steady helper load. The stress report gains a JS-free per-GPU selector (All / GPU0 / GPU1 …) on the GPU, Power, and VRAM result cards to switch between each card's results.
What's new in v12.63
VRAM panel View dropdown (multi-GPU systems): switch the VRAM panel between the all-cards layout and a single memtest instance's full detail (iterations, errors, W/R bandwidth, per-card GB totals), matching the GPU Stress panel's View control.
What's new in v12.62
VRAM test fixed on hybrid iGPU + discrete systems (was completely broken in v12.61 on a UHD + 2080 Ti bench). Single-target systems use the bare memtest launch again — memtest's own default picks the first device of its sorted list, which is the discrete card on hybrids — and argv device labels remain only for multi-discrete simultaneous runs. Also added a loud alert when a memtest instance fails to start (e.g. the pinned exe is missing because its one-time download failed).
What's new in v12.61
Multi-GPU VRAM selection corrected for the pinned memtest v0.5.0. The v12.60 negative-label form is a post-0.5.0 feature; v0.5.0 only accepts a positive index into its displayed, sorted device list. StressLab now replicates v0.5.0's exact sort (discrete first, bus descending) to compute each card's display label and passes it as the argv device argument. The in-log Bus verification still confirms each instance per run.
What's new in v12.60
Multi-GPU VRAM device selection fixed (root-caused from memtest_vulkan source). Its device prompt reads console input — not stdin — so the piped index was silently ignored and both instances tested card 1. The fix uses memtest's undocumented argv device label in its negative form, which selects by raw Vulkan enumeration index during device loading. Each instance now provably lands on its own card; the in-log verification ("confirmed testing Bus 0x.. ✓") confirms it per run.
What's new in v12.59
"Stop on fail" now defaults to checked. Multi-GPU VRAM runs also gained device-selection verification: each memtest instance's log is parsed for the device it actually selected, with a per-card confirmation (or loud alert on mismatch/duplication) logged ~12 s into the run — diagnosing the report that only one card was being stressed.
What's new in v12.58
Integrated graphics no longer counts as a second GPU. Inventory targets discrete cards only (iGPU kept solely as a fallback on systems with no discrete card). Hybrid laptops also now target the discrete card by its real Vulkan device index in Max Thermal Mode and the VRAM test, instead of defaulting onto device 0 (often the iGPU).
What's new in v12.57
Simultaneous multi-GPU VRAM testing. With "All" selected, the VRAM test launches one memtest_vulkan instance per GPU at once (device selected by piping the index to each instance's stdin); per-card iterations/errors/bandwidth show live in the panel, the report gets per-GPU rows, and errors from any card fail the verdict. Also fixed the per-GPU View graphs freezing at 100% after Stop — the per-GPU poller now runs continuously, so graphs and pop-outs track idle state between tests.
What's new in v12.56
Multi-GPU display polish. "VPH" replaces the long helper label; GPU names are compacted in the panel ("RTX 5070" instead of the full GL renderer string); the All view wraps per-card segments onto the second line (replacing the redundant renderer line); and the View selector now switches the panel graphs to the selected card's per-GPU history, not just the numbers.
What's new in v12.55
Per-GPU view + pop-out monitors (multi-GPU systems): a "View:" dropdown in the GPU Stress panel header switches between all-cards and single-card detail, and the ⧉ button opens a live pop-out window per GPU with util/power/temp readouts and rolling graphs. Fixed the GPU metrics freezing at their last value (e.g. 100%) after clicking Stop. Each heat helper now logs to %APPDATA%\StressLab\gpu_helper_devN.log with a watchdog that loudly reports a helper that exits right after launch — for diagnosing one card not loading.
What's new in v12.54
Simultaneous multi-GPU testing. Max Thermal Mode now drives every GPU in the system at once — one Vulkan helper process per card (helper v1.3+ with --list). GPUs are enumerated via Vulkan and correlated with nvidia-smi by PCI bus id, so even two identical cards are tracked correctly. Multi-GPU rigs get a "GPUs:" selector (All simultaneous by default, or a single card), per-card live telemetry in the panel (GPU0 99% 248W 71° │ GPU1 ...), and per-GPU peak-watts rows in the report. Per-card telemetry is NVIDIA-only for now (AMD cards get full load but global-only readings); the error check still runs on the primary GPU only.
What's new in v12.53
Battery test polish. The completion screen now matches the stress-test completion dialog (styled popup with verdict, runtime/discharge summary, wear, and a View Report button). The battery test no longer touches the SSD — the "light" workload's periodic temp-file write was removed (pure CPU now). The setup dialog adds a screen-brightness selector (0/20/50/75/100%, or leave as-is) on laptops whose panel supports WMI brightness control; the chosen level is applied for the run and restored afterward. The report gains a Test Setup & System panel recording the brightness used, full system specs (CPU/RAM/GPU/OS/board/BIOS), and whether the GPU was under load.
What's new in v12.52
The battery test now generates a full HTML report with a discharge curve. When the laptop battery rundown test finishes, it saves and opens a report (in Documents, stresslab_battery_<timestamp>.html) containing: a battery-charge-over-time discharge curve, a discharge-power (W) curve, a results panel (runtime, %/hour, projected full runtime, avg/min/max watts, energy used), and a capacity & health panel (full-charge vs design capacity, wear %, implied usable energy). An "Open report" button is also added to the completion window.
What's new in v12.51
VRAM test stuck-iteration: root cause fixed. Confirmed from a real log: memtest_vulkan right-aligns its written/checked GB values, and once they grow wide enough the pad space after "checked:" disappears ("checked:13048.0GB" vs the earlier "checked: 14.0GB"). The parser required that space, so it stopped matching once the values got large and froze the iteration count (e.g. at 189) while memtest kept running. The space is now optional — parsing continues for the whole run.
What's new in v12.50
VRAM test stuck-iteration: parser tolerance + diagnostic. The parser now accepts both "GB/sec" and "GB/s" bandwidth units (a build using "GB/s" would previously parse nothing). And if the log keeps growing while the iteration count freezes, StressLab now flags "⚠ output not parsing" in the VRAM panel and captures the unrecognized text into the report — so the exact failing format can be identified.
What's new in v12.49
New: laptop battery rundown test. A standalone "Battery Test" button (next to START), separate from the load-test sequence. Pick a workload — Idle/screen-on, Light (web/office approximation), or Full CPU+GPU stress — arm it, and unplug the charger to start. It discharges to a 5% floor and reports measured time on battery, an extrapolated 100→0% runtime, average discharge watts, and battery wear % (full vs. design capacity). Waits for AC-unplug to begin, ends if you plug back in, and suppresses sleep during the run. Detects desktops (no battery) and says so.
What's new in v12.48
GPU Test Max Thermal Mode is now the default. The GPU test drives the card to its real power/thermal ceiling via the Vulkan helper out of the box. Uncheck it for the lighter raymarcher-only test; falls back automatically if the helper isn't bundled.
What's new in v12.47
A batch of reporting/diagnostic fixes:
- Memory channel detection no longer mislabels budget boards (e.g. Chinese X99) as single-channel when their SMBIOS slot labels are generic. It only claims single-channel with positive evidence, otherwise reports the module count.
- UI freeze during RAM allocation fixed: the whole-buffer
memsetheld the GIL and froze the graphs (then fast-forwarded). It's now chunked with GIL yields. - VRAM test iterations stuck (e.g. at 199): the memtest_vulkan filename is now version-pinned so a stale old exe is bypassed, and the iteration parser tolerates both old and new memtest output formats.
- Thermal-throttle stat only shows on CPU/Power/transient tests and only when the CPU actually passed 90 °C (not just a clock dip).
- Quieter logs: the "ran longer than expected" warning fires only at ≥150% of expected duration; the GPU hotspot-vs-core delta section only appears at a ≥30 °C delta.
What's new in v12.46
AMD/Intel GPU utilization restored. The util pollers only queried nvidia-smi, so non-NVIDIA cards showed no utilization. Added a vendor-agnostic fallback via the Windows "GPU Engine" performance counter (same source as Task Manager), so AMD/Intel cards display utilization again.
Useful GPU metric in Max Thermal Mode. Instead of "FPS: 0 | Frames: 0" (the raymarcher is skipped while the Vulkan helper drives the GPU), the panel now shows "Vulkan power helper | Load: N% | NNN W", and the report records the load tool + peak GPU watts.
What's new in v12.45
Crash dump analysis fixes. Prompted by a GPU live dump (0x141) the analyzer mishandled: (1) "Analyze Recent" now finds live dumps that live in LiveKernelReports subdirectories (it was reconstructing paths from the bare filename and missing them); (2) added the missing bugcheck codes including 0x141 VIDEO_ENGINE_TIMEOUT_DETECTED — the GPU-TDR live dump a stress test most often triggers — plus 0x142 and 0x14B, with stress-relevant guidance (heat / GPU overclock / driver / power).
What's new in v12.44
Fix: Power test with the Vulkan helper collapsed after a few seconds (full power, then ~30%/50-60W). The full all-core CPU load was starving the helper's submit thread at normal priority, draining the GPU queue. The helper now runs at High priority — it needs little CPU, just not to be starved — holding full GPU power with the CPU maxed.
Removed the clock-stability percentage from reports (not a useful signal). Thermal-throttle detection stays.
What's new in v12.43
Fix: Power test oscillated when calibration picked the Vulkan helper. The CPU/GPU autotune misread the idle in-process render loop as GPU starvation and cycled CPU workers up/down. Now disabled when the helper supplies GPU load (nothing to balance against a separate process); all cores run flat out.
Fix: Max Thermal Mode left power on the table. The throttled raymarcher was still stealing GPU from the helper (4060 Ti: 165W -> 130W). Since the error check is independent of the raymarcher, the raymarcher is now skipped entirely in Max Thermal Mode, ceding the whole GPU to the helper; the error check still runs on its timer.
What's new in v12.42
Transient test load steps are now sharp and consistent. It uses the overdraw power shader pinned to a fixed load instead of the old raymarcher, whose autoscaler didn't converge within the short on/off windows (this is why a 4060 Ti couldn't reach full load). Every gate-on now slams to an identical, repeatable, heavier load step.
Power test auto-picks the heaviest GPU load per card. Wide-bus cards draw more from the Vulkan helper; narrow-bus cards can draw more from the overdraw shader. The Power test now calibrates once per card ("Calibrating for Maximum Wattage"), measuring ~20s of each and caching the higher-wattage winner, then runs with it. The report shows which tool was chosen and the measured watts.
What's new in v12.41
Max Thermal Mode fix. When Max Thermal Mode runs the heat helper alongside the GPU test, the continuous raymarcher was competing with the helper for the GPU and pulling total power down (340W helper-alone vs 230W together on a 5080). The GPU test now throttles the raymarcher way down in Max Thermal Mode so the helper gets the full card and the card reaches its real thermal load. The error check is unchanged and still runs on schedule — now against a hotter card.
What's new in v12.40
GPU test "Max Thermal Mode" (off by default). A new checkbox beside "GPU error checking". When enabled, the GPU test runs the Vulkan power helper alongside the raymarcher to drive the card to a heavy power/thermal load, surfacing render faults that only show up when the card is hot. The raymarcher and error check are unchanged and unaffected; the helper (a separate process) only supplies heat. Falls back cleanly if the helper isn't installed. (Replaces the v12.39 in-shader heat attempt, which couldn't draw real power on high-end cards.)
What's new in v12.39
GPU test now heats the card. The standalone GPU stability test was running too cool (~172W on a 5080) to catch faults that only appear under heavy power/thermal load. It now runs a heat pass (dense compute + memory traffic) alongside the raymarcher so the card genuinely warms up, while the raymarcher keeps rendering its divergent scene for error variety. The error checking is unchanged — same deterministic integer checksum pass. Memory-integrity testing still belongs to the VRAM test; maximum power draw still belongs to the Power test.
What's new in v12.38
Power test now runs all CPU cores. Fixed the adaptive CPU/GPU controller idling a large fraction of CPU cores during the Power test (a 13900K sat at ~60% utilization). It now runs all cores for maximum combined load and only eases off if the GPU genuinely collapses (sustained <60% utilization), giving meaningfully higher total CPU power and load.
GPU power — what to expect. The GPU and Power tests reliably drive high GPU utilization and heavy sustained load, which is what exposes instability, weak VRMs, inadequate cooling, and marginal PSUs — the actual job of a stress test. They are not tuned to hit a specific card's exact rated TDP wattage: maximum power draw is highly architecture-specific (FurMark achieves it through years of per-GPU tuning, and modern drivers actively detune recognized power-virus patterns), which isn't replicable from a portable shader. For validating a card against its exact rated power draw, run FurMark or a vendor tool alongside StressLab. For stability, thermal, and combined-load validation, StressLab's tests are sufficient on their own.
What's new in v12.19.3
Further tightened the CPU/GPU stress panel spacing so their progress bars match the height of the RAM/VRAM panel bars (graphs trimmed 58px → 52px, padding reduced). Cosmetic only.
Pure Python — rebuild via build_exe.bat.
What's new in v12.19.2
Two fixes: (1) the stress panel progress bars were cut off at the default window size after the error-check line was added — tightened the status-row spacing and shrank the live graphs slightly so the bars stay visible. (2) GPU error checking was sapping a couple percent of load (extra pipeline stalls from a redundant sync + readback every second) — removed the redundant ctx.finish() and reduced check frequency to every 2s, so the load shader keeps the GPU saturated between checks.
Pure Python — rebuild via build_exe.bat.
What's new in v12.19.1
Live GPU error-check counter + self-test. The live status panel now shows the running check count and error count during the GPU test ("✓ err-check: 142 checks, 0 errors"), so you can see the error-checking workflow actively running — not just sitting at 0. To verify detection actually works on healthy hardware (where errors are always 0), set STRESSLAB_GPU_ERRCHECK_SELFTEST=5 and the checker injects a fault at the 5th check so you can watch the counter climb. The report also shows the check count.
Pure Python — rebuild via build_exe.bat.
What's new in v12.19
GPU error checking (OCCT-style). The GPU test now detects compute instability, not just load/thermals. Alongside the load shader, a deterministic integer compute pass runs and its output is checksummed against a self-referencing baseline — any divergence means the GPU computed wrong values (unstable overclock, heat/degradation errors). Integer math is bit-exact across all GPUs, so it can't false-positive from floating-point variation. On by default with an On/Off toggle. Results show in the report ("passed" / error count); a run with confirmed errors fails. Applies to the dedicated GPU test (Power/Transient stay load-only).
Pure Python — rebuild via build_exe.bat.
What's new in v12.18.2
Crash recovery polish: the startup crash notice is now a styled modal (matching the completion dialog, but red "✗ Crashed") with a scanning progress bar — the View Crash Report button stays disabled until the crash-dump scan finishes. The recovery report now correctly shows the test that was running at crash time as failed (red), instead of marking every test passed. Also fixed a thread-safety issue in the background scan's dialog updates.
Pure Python — rebuild via build_exe.bat.
What's new in v12.18.1
Crash recovery now correlates minidumps and auto-exports. On the next launch after a crash, StressLab scans for crash dumps / bugcheck events recorded around the time the run stopped and attaches the matching ones to the recovery report (with bugcheck code, name, and description) — tying "died 42s into the GPU test" to "...with a WHEA hardware bugcheck 90s later." The recovery report also auto-exports to Documents\StressLab Reports automatically (named by crash time, no duplicates on repeat launches), with a JSON sidecar.
Pure Python — rebuild via build_exe.bat.
What's new in v12.18
Crash recovery (OCCT-style). If a stress run ends unexpectedly (system crash, freeze, BSOD, or power loss), the next StressLab startup on that machine offers to show the last recorded state of the interrupted run. A checkpoint is written every 5 seconds during a run and deleted on clean finish/stop/close, so only an actual crash leaves it behind. The recovery view reconstructs a partial report — peaks, charts, results, and exactly which test was running and how far in when it died — which is often the key diagnostic clue. Display-only; can be saved for documentation.
Pure Python — rebuild via build_exe.bat.
What's new in v12.17.2
Idle-thermal yellow-warning threshold raised from 55°C to 60°C (red error stays at 65°C). Applies to both the live Stress Tests tab banner and the health-check idle test.
What's new in v12.17.1
Crash fix: the health-check "CPU Idle Temperature" test referenced a constant (IDLE_HOT_TEMP_C) that was removed in v12.16, throwing an error when the health report ran. The health idle check now uses the same two-tier thresholds as the live banner — yellow warning at idle + >55°C, red error at idle + >65°C.
Pure Python — rebuild via build_exe.bat.
What's new in v12.17
Expanded CPU max-power table and smarter cooling-limited detection:
- Power table now covers Intel 6th–14th gen + Core Ultra Series 2 and Refresh (incl. the new 270K/270KF Plus and 250K/250KF Plus and F variants) and AMD Ryzen 1000–9000, validated against manufacturer specs. Each entry is marked as an enforced limit (10th-gen+ Intel, Zen 3+ AMD) or reference-only TDP (older Intel/Xeon, Ryzen 1000–3000).
- Cooling-limited detection now separates a real cooling fault from a motherboard power cap. It only flags when the CPU actually thermal-throttled (hit TjMax), and the report notes that throttling can come from board power limits rather than a failing cooler. On chips without a reliable power figure (older Intel/Xeon, early Ryzen), it flags on temperature alone instead of citing an untrustworthy wattage ratio.
Pure Python — rebuild via build_exe.bat.
What's new in v12.16
Better CPU cooling detection:
- Idle thermal warning is now more sensitive with two tiers — yellow at idle + >55°C, red at idle + >65°C (was a single >70°C threshold that could miss, e.g., a cooler with the protective film still on). Idle is now defined as <20% load.
- New cooling-limited detection: if the CPU thermal-throttles while drawing under 60% of its rated max power, the report flags it (e.g. "140W / 250W — throttled at 56% of rated power"). Catches a chip being choked by its cooler — hitting TjMax long before it can pull its rated power. Uses a built-in max-power table for common Intel 12th–14th gen / Core Ultra and AMD Ryzen 5000/7000/9000 CPUs; unknown models skip the check rather than guessing.
Pure Python — rebuild via build_exe.bat.
What's new in v12.15.4
Fixes the per-test aggregate zoom charts showing the wrong duration on multi-cycle runs. They spanned from a test's first phase to its last phase — which for a test that runs once per cycle includes the long gaps where other tests ran (a 2-cycle CPU test showed a 35-min axis for 10 min of actual data). The aggregate now stitches the test's own cycles back-to-back, removing the dead gaps, so a 2-cycle test shows its true combined active duration with no misleading gap. Per-cycle panels and the Performance Telemetry whole-run charts are unchanged.
Pure Python — rebuild via build_exe.bat.
What's new in v12.15.3
Completes the chart-timing fix. The per-phase detailed (zoom) charts were fabricating a clean "5:00" span by slicing data on an assumed 1-sample-per-second index, which hid the real sample-rate shortfall under load (and could mis-slice deep-timeline phases). They now slice by real sample timestamps like the aggregate chart does, so per-phase and aggregate charts are consistent and both reflect true wall-clock time. A legacy fallback covers reports from pre-timestamp builds.
Pure Python — rebuild via build_exe.bat.
What's new in v12.15.2
Five report fixes:
- Cycle deviation banner names the metric — instead of "off on a key metric", it now says e.g. "VRAM write speed 30% below median".
- Aggregate charts span the full run — a 1-hour run was showing ~36 min because the 1Hz poll drops samples under load and the chart used sample-count for its time axis. Now every sample is timestamped and positioned by true elapsed time.
- "PCIe ? x?" suppressed — the Max PCIe link card only shows when valid speed/width were detected.
- Total run time shown in the verdict block (e.g. "1h 23m 45s").
- Motherboard + BIOS version/date shown in the report header.
Pure Python — rebuild via build_exe.bat.
What's new in v12.15.1
Transient/Switch test tuning: the load needs ~2s of dwell to actually reach 100%/0% (GPU driver ramp + util polling lag), so the sub-2s periods were removed. Period is now 2s or 5s (default 2s). Asymmetric mode now uses off = period + 2s to guarantee a full spool-down to 0% before each sharp re-load. Every load phase is now ≥2s.
Pure Python — rebuild via build_exe.bat.
What's new in v12.15
New Transient/Switch test. Rapidly slams CPU+GPU from idle to full load and back to stress the power-delivery chain's transient response — catches weak PSUs, aged capacitors, and marginal VRMs that pass steady-state testing but fail on load swings. Selectable period (0.5/1/2 s) and duty profile (Symmetric 50/50 or Asymmetric 25/75 sharp-pulse). Both CPU and GPU pulse together for maximum dI/dt.
Targets PSU/bulk-cap transients (0.5-2 s regime), not microsecond VRM droop (not achievable from Python). Report includes load profile, cycle count, work output, errors during steps, and — only if the board actually exposes it with a plausible value — the minimum +12V rail observed during the load steps.
Safe on healthy hardware: an hour of cycling is within the normal operating envelope, with the same thermal protections as every other test.
Pure Python — rebuild via build_exe.bat.
What's new in v12.14
Critical multi-cycle fix. Long multi-cycle runs could spawn concurrent test-sequence chains from leaked poll timers, causing: a crash, multiple duplicate reports, wrong per-test cycle counts, and the disk test ignoring the write cap (one reported run wrote 804 GB against a 200 GB cap). Diagnosed from field JSON data.
Fixed with a poll-loop generation token (stale timers drop themselves), a re-entrancy guard on the queue advance, and an idempotent finish path. The disk cap is now correctly shared across cycles — cycle 1 writes up to the cap, later cycles skip. Verified via simulation.
Pure Python — rebuild via build_exe.bat.
What's new in v12.13.2
ReBAR text update on non-Arc cards: now warns that enabling can hurt performance in some games, recommends testing thoroughly. Removed the explanatory "ReBAR is a PCIe feature..." paragraph from the detail since it wasn't necessary.
Pure Python — rebuild via build_exe.bat.
What's new in v12.13.1
- ReBAR check fix: now correctly identifies and skips integrated GPUs (Intel UHD Graphics 770 was being checked because it reports 1+ GB "dedicated" memory). iGPU/dGPU classification is now by name pattern instead of by VRAM size, matching the C# helper's logic. On iGPU-only systems, the check returns informational status instead of a misleading reading.
- Max PCIe link state: stress report now includes the max-observed PCIe gen × width sampled during the GPU and power phases. Informational only (no warnings). Useful diagnostic when a 5.0 card seems to be running below capability — you can spot it in the report and follow up.
Pure Python — rebuild via build_exe.bat.
What's new in v12.13
- Resizable BAR health check added to the system health report. Errors on Arc cards if disabled (Intel documents that Arc performance is severely degraded without ReBAR); informational only on AMD/NVIDIA cards if disabled (smaller performance impact, 2-10% typically). Detection works across all three vendors via
Win32_PnPAllocatedResourceBAR1 size inspection. - Capture controls on the overlay.
▶ Start Capturebutton + duration dropdown (1/5/15/30/60 min) at the top of the overlay window. Hidden during active capture (the existing CAP: progress row in the overlay body takes over).
Pure Python — rebuild via build_exe.bat.
What's new in v12.12.2
Fixes the multi-cycle "View detailed charts" bug: clicking the link from any cycle tab now opens the chart for that specific cycle (aggregate, cycle 1, cycle 2, etc.) instead of always showing the same chart. Root cause was two-fold: only one zoom panel was generated per test (the call site found only the first matching phase), and the modal HTML ID was keyed only on test name so all cycles' triggers pointed at the same target. Now generates one zoom panel per cycle plus an aggregate spanning the full window, with cycle-scoped IDs (zoom-cpu-agg, zoom-cpu-1, zoom-cpu-2, ...) and proper trigger/modal placement (triggers inside cycle tabs, modals outside so the CSS :target pattern still works on inactive tabs).
Pure Python change. Rebuild via build_exe.bat.
What's new in v12.12.1
Important upgrade fix. Found a long-standing bug where lhm_reader.exe was only deployed on first launch — subsequent StressLab upgrades silently kept running the old helper from LOCALAPPDATA. This means the sensor matcher fixes shipped in v12.11.2 / .3 / .4 may not have actually been running on machines that had previously run an earlier StressLab.
Now: a lhm_version.txt sidecar tracks which StressLab version deployed the current helper. Version mismatch forces redeploy. Same fix applied to PresentMon via size comparison.
If the AMD/Gigabyte CPU temp fixes haven't seemed to take effect on your test rig, this is probably why. Installing v12.12.1 will force a fresh helper deploy on first launch.
Pure Python change. Rebuild via build_exe.bat.
What's new in v12.12
Two capture-mode additions:
- Capture progress on overlay — when a capture is running, a
CAP: MM:SS/MM:SS NN%row appears at the top of the metrics overlay. Auto-appears on start, auto-disappears on stop. Updates at 1Hz with neutral/orange/green color tiers. - Captured target in report + CSV — capture polling samples the foreground process each second and records the most-common one as the "captured target." Surfaces in the HTML report header (e.g.
Captured target: Cyberpunk2077.exe (94% of capture)) and as a# captured_target:metadata comment at the top of both CSV files. Secondary apps (Discord, browser) shown when they held foreground for ≥10% of capture.
Pure Python — drop in the new stresslab.py and rebuild.
What's new in v12.11.4
Fixes an iGPU-only regression from v12.11.2. AMD Ryzen 7000/9000 iGPU-only systems (where the iGPU exposes only GPU VR SoC and no GPU Core) were getting a blank GPU temp display. Now uses a two-pass evaluation: non-core sensors are stashed as tentative iGPU readings during iteration, and promoted to the displayed GPU temp only if no discrete GPU contributed a real value. Daniel's bug fix is preserved (a discrete GPU still always wins over iGPU readings).
Tested against 10 GPU configurations including Intel iGPU only, AMD iGPU only, NVIDIA dGPU only, AMD RX dGPU only, and various mixed configurations. All pass.
Build note: C# helper changed (SensorReading.cs + SensorMatcher.cs). Rebuild lhm_reader.exe via helper\build.bat.
What's new in v12.11.3
Anti-cheat warning fires at the actual point of use, not at startup. The warning only appears when the user enables the overlay AND has at least one PresentMon-derived metric (FPS, EPS, GPU busy %, display latency, generated frame %) checked, OR when they toggle on a PresentMon metric while the overlay is showing. Sensor-only overlays don't trigger the warning at all — no ETW session is started.
If you accepted the warning in a previous version, the latch is still in %LOCALAPPDATA%\StressLab\config.json so you won't be re-prompted. To test the new flow from scratch, delete that file.
Pure Python change — drop in the new stresslab.py and rebuild.
What's new in v12.11.2
Sensor matching fixes for AMD Ryzen + Gigabyte motherboard systems:
- CPU temp on Ryzen 9 7900X + Gigabyte B650 was showing 40°C instead of the real 93°C. Three combined fixes: (1) tighter CCD-pattern matching so
L3 (CCD1)/(CCD2)cache sensors don't get picked up, (2) explicit blocklist forIOD Hotspotand L-cache prefixes so they never become the headline CPU temp, (3) WMI thermal-zone fallback now only engages when the helper isn't running at all (was masking the real problem with a wrong-looking ACPI motherboard sensor value). - GPU temp was showing iGPU's
GPU VR SoC(44°C) instead of the RTX 5080'sGPU Core(57°C). Added blocklist for non-core GPU temperatures (VR SoC, GPU SoC, VRM, Mainboard, PCB) so voltage-regulator sensors can never become the user-facing "GPU temp" regardless of discrete-vs-integrated logic. - Diagnostic warning when helper is alive for >10s without reporting CPU temp, suggesting the user run the sensor dump tool. Helps surface board-specific matching gaps that need attention.
Build note: this release touches both the C# helper (SensorMatcher.cs) AND Python. Rebuild lhm_reader.exe via helper\build.bat before rebuilding the main exe.
What's new in v12.11.1
Six fixes/additions:
- FPS no longer blinks — cached stale-value fallback handles PresentMon's batched stdout flushes (300ms gaps were causing the avg FPS row to flash between value and
--). - Capture report charts enlarged —
minmax(640px)andmin-height: 240pxfor readable detail. - New PresentMon metrics: GPU busy %, display latency (ms), generated frame % (from
--track_frame_typefor DLSS-FG / FSR-FG / AMD AFMF / Intel XeFG detection). All opt-in via the overlay picker. - Raw CSV export alongside HTML — slow-metric CSV (sensors at 1Hz) and per-frame CSV (PresentMon per-frame data). Ready for Excel / pandas / CapFrameX.
- FPS auto-enabled by default when PresentMon is detected. Anti-cheat warning shown once and persisted to
%LOCALAPPDATA%\StressLab\config.json. Enable button only appears when PresentMon isn't available. - Better EPS description in report — "What it measures" + "How to read it" (4 tier ranges) + inline assessment for the current capture.
What's new in v12.11
Errors per second (EPS) metric based on PresentMon's MsAnimationError column. Measures perceived stutter that frame time alone misses — see GamersNexus's article for background. Counts frames where animation-vs-display timing drift exceeds 1ms, divided by rolling window length.
- New "Errors/sec (anim. error)" overlay row, opt-in via the metric picker. Color tiers: green <1, orange 1-5, red ≥5.
- New Animation Error sub-section in capture reports showing total error frames, average EPS, and peak 1-second EPS.
What's new in v12.10.4
Three bug fixes from field testing:
- Overlay was a blank gray box since v12.10.1. A tuple passed to
tk.Frame(pady=...)was raising a TclError that broke the build mid-way, leaving an emptyToplevelon screen. Fixed. - FPS reading lagged the game's own counter by ~1500ms. Two causes: a 1s rolling window contributed ~500ms phase lag, and overlay-update cadence was only 1Hz so the FPS labels could be up to a full second stale on top of that. Now uses a 300ms window AND a separate 5Hz overlay refresh just for FPS. Lag now ~300-500ms, mostly bounded by ETW delivery latency (RTSS has the same constraint).
- Build script warning about "no PresentMon bundled" now actually shows which files ARE in
assets\(off-version filenames hint at the right fix) and warns that a residual deploy in LOCALAPPDATA could mask the issue on your dev machine.
What's new in v12.10.3
Hotfix: PresentMon validation was rejecting valid binaries. I assumed PresentMon was ~3 MB; the actual console app is ~411 KB. The 142 MB MSI on GitHub is a different (full GUI) thing we don't need.
Replaced the wrong-headed size threshold with proper PE magic-byte validation (MZ + PE header signature). Now correctly accepts any valid Windows exe regardless of size while still cleanly rejecting HTML error pages, empty/truncated downloads, ZIPs, etc.
The 411 KB file already in your assets/ folder is correct — just rebuild with v12.10.3.
What's new in v12.10.2
Diagnostic improvements + deploy target fix for PresentMon bundling.
- build_exe.bat now reports whether PresentMon was found in
assets/(prints size + filename if yes, big warning block + directory listing if no). The final BUILD SUCCESS line explicitly says "Bundled PresentMon" or "NO PresentMon bundled". - Deploy target moved from the exe directory to
%LOCALAPPDATA%\StressLab\presentmon\(matches LHM helper pattern, writable without admin, survives upgrades cleanly). - Runtime diagnostics expanded: failed bundle lookups now show the exact directory checked, the accepted filenames, and what files actually are in that directory — so "I put it there but it didn't work" becomes immediately debuggable.
What's new in v12.10.1
Hotfix: PresentMon now bundles directly with StressLab instead of downloading on first use. v12.10's download path was failing on some networks (corporate proxies, Cloudflare bot detection rejecting python-urllib User-Agent).
Setup: drop PresentMon-2.3.1-x64.exe from Intel's GitHub releases into the assets/ folder. StressLab deploys it silently on first FPS enable. Download fallback still exists for stripped distributions.
PyInstaller spec already updated to include it automatically when packaging.
What's new in v12.10
- FPS tracking via PresentMon — real per-game FPS, 1% low, and 0.1% low (CapFrameX convention). Bundled on-demand (~3MB MIT download from Intel's GitHub releases). Two targeting modes: auto-follows foreground game, or manual pick from process dropdown. Available in the overlay metric picker (opt-in, off by default), and included in capture reports with frametime chart and statistics.
- Anti-cheat note: PresentMon uses ETW (no DLL injection — same mechanism as Steam Overlay) but some kernel anti-cheat systems have at various points flagged ETW capture. First-run dialog warns users. Don't run FPS tracking with Valorant/competitive titles with kernel anti-cheat; most games are fine.
- Overlay UX: bottom padding fixed to look balanced, label column auto-widens to fit long labels like "GPU HS:" without truncation.
- Show Metrics Overlay button moved from Stress Tests page to Metrics Monitoring tab (next to Capture controls). Toggle text flips between Show/Hide to match state.
What's new in v12.9
New Metrics Monitoring tab. Foundation work for the FPS/PresentMon integration coming in v12.10.
- New tab with 12 always-on charts (CPU/GPU temps, utilization, power, clocks, GPU hotspot, GPU memory °C, VRM °C, SSD °C, fans). Charts only redraw when the tab is visible.
- Capture mode — pick a duration (1/5/15/30/60 min), hit Start, get a self-contained HTML capture report with stats table (peak/mean/p99 per metric), zoomable SVG charts, system info, and SHA-256 integrity stamp. Reports save to
Documents/StressLab Reports/. - Overlay metric picker on the new tab — checkbox grid for 14 available metrics. Toggling immediately rebuilds the overlay. Default selection unchanged (CPU/GPU util+temp, RAM, VRAM) so existing users see no difference unless they touch the picker.
- Dynamic overlay sizing — height auto-expands when more metrics are selected, capped at 90% of screen height.
- CaptureHistory is a separate class from MetricHistory, time-stamped per sample so reports have accurate wall-clock x-axes even when polls drift.
What's new in v12.8
- Overlay sizes bumped up. Small is now what Medium used to be (280×162, 11pt), Medium is now what Large used to be (340×200, 13pt), and Large is doubled to 680×400 with 26pt body font — appropriate for 4K displays or reading from across the room.
- Percent utilization color tiers. CPU%, GPU%, RAM%, and VRAM% now color-code: green < 50%, orange 50-89%, red ≥ 90%. Temperature color tiers (≥70°C yellow / ≥80°C amber / ≥90°C red) unchanged.
What's new in v12.7
- Overlay now stays on top of borderless-windowed games. The v12.6 overlay was getting buried when games took focus because they re-shuffle the topmost slot. Fixed via periodic
SetWindowPos(HWND_TOPMOST)reassertion every 1.5s (withSWP_NOACTIVATEso it doesn't steal focus), plus removing theWS_EX_TOOLWINDOWstyle which was competing with games' topmost claims on some configurations. If you still see the overlay buried behind a specific game, the game is likely using DXGI independent-flip or fullscreen optimizations — right-click the game's exe → Properties → Compatibility → check "Disable fullscreen optimizations" to force it into normal borderless windowed. - Configurable text size. Right-click → Size → Small / Medium / Large. Small (240×138, 9pt) for minimal footprint, Medium (280×162, 11pt) default, Large (340×200, 13pt) for 4K displays or reading from a distance.
- RAM and VRAM now show absolute MB/GB alongside percent. Each row shows e.g.
RAM: 7.2/16 GB 45%. RAM comes frompsutil.virtual_memory(). VRAM MB comes fromnvidia-smi memory.used/total(NVIDIA only; AMD/Intel still get percent but not MB). The existing GPU util background thread now does a single multi-field nvidia-smi query so util + memory come from one subprocess. - Right-click menu reorganized. Corner options under Position submenu, size options under Size submenu, Hide overlay at the bottom.
What's new in v12.6
- Metrics overlay — toggleable from the Stress Tests page (new "Overlay" checkbox next to Stop on fail and Auto-export). Small semi-transparent window in the top-right corner (right-click for other corners or to hide) showing CPU%, CPU°C, GPU%, GPU°C, RAM%, VRAM%. Updates 1Hz from the existing telemetry pipeline, color-codes temperatures (yellow ≥70°C, amber ≥80°C, red ≥90°C), sets
WS_EX_NOACTIVATEso clicks don't steal focus from foreground apps. Works over the desktop and borderless-windowed games; does NOT work over true fullscreen exclusive (those need a DirectX/Vulkan hook overlay like NVIDIA Overlay or MSI Afterburner+RTSS, out of scope for StressLab).
What's new in v12.5
- RAM passes counter now shows pattern-position progress — large-RAM systems can take 60-90+ seconds for a single full 10-pattern cycle, so the previous
Passes: 0display lingered far too long. The panel now readsPasses: 0 (8/10)showing the slowest worker is on pattern 8 of 10. Replaces misleading floor-div averaging withmin()across workers. - GPU thermal interface degradation detector — compares the GPU hotspot to GPU core temperature during the run. Sustained delta ≥25°C triggers an alert in the stress log and a red-bordered section in the report recommending repaste (with PTM7950 specifically called out, since it's a phase-change pad that doesn't pump out over time). 20-24°C delta gets a gentler yellow info note. Healthy cards (10-15°C delta) get no card.
- build.bat fixed — was showing "Common causes" troubleshooting after every build, even successful ones, because some .NET SDK versions return non-zero on harmless trim warnings. Canonical success check is now "did the exe get produced?" not dotnet's exit code.
What's new in v12.4
GPU memory temperature monitoring is now significantly more reliable. Requires rebuilding the C# helper (helper/build.bat). See CHANGELOG.md for details.
- Broader memory-junction name matching in
SensorMatcher.cs— covers all the variants NVIDIA, AMD, and Intel Arc expose across driver versions ("GPU Memory Junction", "Memory Junction", plain "Memory", "GPU Memory" etc.). Previously many cards reported a value but StressLab silently missed it. - 255°C sentinel filter for the known LHM upstream bug where RTX 5090 (and possibly other RTX 50 cards) report a sentinel value instead of the real temp. Anything ≥200°C is now rejected as a clearly-wrong reading.
- Discrete-over-integrated priority applied to memory temp too — iGPUs don't have separate VRAM, so any memory reading from an iGPU is meaningless or mis-labelled.
- Peak GPU hotspot and peak GPU memory now tracked across each run and shown as separate cards in the report's Peak Readings dashboard. Both are commonly the actual thermal bottleneck on modern cards (GDDR6X memory junction can hit 100-110°C while the core is only 75°C, and the hotspot is what AMD/NVIDIA's own thermal protection acts on).
- Two new charts in the per-test zoom modal: GPU HOTSPOT and GPU MEMORY, each on a 0-115°C scale so values approaching the GDDR6X throttle threshold are visible. Silently absent on cards that don't expose those sensors.
What's new in v12.3
Three field-reported bug fixes. Requires rebuilding the C# helper (helper/build.bat) since two of the three fixes are in the sensor reader. See CHANGELOG.md for full details.
- Power test phase truncation fixed on slow CPUs (Ryzen 1600 / similar). The chart's time axis now uses real phase timestamps when available, and the 1Hz sensor poll loop is drift-corrected so it catches up after slow iterations instead of permanently lagging behind. A 15-minute Power test now produces a 15-minute-wide phase band even when the CPU is saturated.
- Zen 4 CPU temp reading fixed. Ryzen 7000-series CPUs (7900X, etc.) expose CCD chiplet temperature sensors that read substantially lower than the actual die temperature — a known Zen 4 silicon quirk. The old code let any of those sensors win the headline CPU temp; now Tctl/Tdie/Package always take precedence over CCD readings within the same poll, regardless of LHM iteration order.
- Discrete GPU temp now wins over iGPU. On Ryzen 7000 + NVIDIA discrete systems (e.g. 7900X + RTX 5080), the integrated Radeon Graphics enumerated first through the AMD driver path, hiding the discrete card's real temp. New
IsIntegratedGpu()helper classifies GPUs by hardware name; discrete sensors always take priority. Same fix applied to GPU power, GPU hotspot, and GPU fan.
What's new in v12.2
Bug-fix release. See CHANGELOG.md for details.
- VRAM cumulative totals fixed — the
Total VRAM written/verifiedfigures were showing only the last 30-second window from memtest_vulkan, not the cumulative totals over the whole run. Per-window values are now properly accumulated, with de-duplication by iteration number to handle re-reads of the log file. - VRAM panel now updates smoothly between memtest_vulkan reports. The Written/Checked numbers use the last-known bandwidth to extrapolate forward so the panel doesn't appear frozen for 30+ seconds at a time during the steady-state portion of a run. Live values reset to memtest_vulkan's authoritative numbers at each new report.
- Sensor poll loop made robust to internal exceptions. Previously, a failure in any part of the 1Hz telemetry poll would silently kill the reschedule, halting all sensor capture for the rest of the run — manifesting as short or empty chart phase bands in reports. The poll body is now wrapped in try/finally so the reschedule always fires, with failures logged to the stress log instead of disappearing silently.
- Phase duration diagnostic — the stress log now warns when a test phase ends more than 5% off its expected duration, so future "the report shows a short phase band" reports have a clear paper trail.
What's new in v12.1
- Idle thermal warning — new heuristic that catches CPU cooler problems by their thermal signature (low utilization but high temp). A healthy idle CPU should sit at 30–50°C; sustained 70°C+ at near-zero load means the heatsink isn't removing heat. Surfaces as a live red banner on the Stress page when the condition holds for 8+ consecutive seconds, and as a flag in the health report with detailed cause analysis (loose mounting, dried thermal paste, failing AiO pump, dead fan, dust buildup). Includes a 90s cooldown after stress runs and dismissable mute.
- VRAM panel polling fix — the previous implementation short-circuited on unchanged file size, but memtest_vulkan's stdout is full-buffered (4 KB chunks on Windows) so the file grew in bursts and the UI froze for 5–15 seconds at a time. Now re-parses every 0.25s regardless. Plus a "last update X seconds ago" indicator that explains memtest_vulkan's natural reporting cadence (which grows from every-iteration early in the run to every-10-30+ iterations later — that's by design, not a hang).
What's new in v12.0
A polish-and-correctness release on top of v11's feature additions. See the full v12 entry in CHANGELOG.md for details.
- Zoomable performance charts — drag any chart in a stress report to brush-zoom on a region; double-click or click "Reset Zoom" to restore. Useful for inspecting individual cycles in multi-cycle runs.
- Multi-cycle counting fixed — the Power test was incorrectly inflating CPU and GPU cycle counts. Power now has its own metric bucket, its own cycle list, and its own card in reports.
- Network test accuracy improvements — 8 parallel streams (matching Speedtest.net), TCP_NODELAY on upload, pre-allocated random buffers (5-10× upload speedup vs v11), and 256 KB chunks
- False packet loss fixed — the network test was counting subprocess errors during heavy load as dropped packets. Now uses a three-state ping result that distinguishes real timeouts from tool-side errors.
- Honest accuracy framing in the network test description — explains that Cloudflare's
__upendpoint has a known performance ceiling and recommends Speedtest.net or Waveform for accurate upload measurement above ~600 Mbps. The bufferbloat grade and packet-loss-under-load remain the most diagnostically useful outputs. - Python 3.11 build compatibility fix and Cloudflare bot management bypass (browser-like User-Agent + headers).
What's new in v11
- Network & Bufferbloat Test — measures download/upload speed, idle latency, packet loss, and bufferbloat (latency under load) against Cloudflare's public endpoint. Multi-stream HTTP for accurate measurement on connections above 1 Gbps. Bufferbloat grade follows the Waveform/DSLReports A+/A/B/C/D scale. Live charts with hover tooltips and Wi-Fi detection warning.
- Burn-in / multi-cycle stress testing — run each test 1-5 times in a single run with a cycle dropdown next to Duration. Live runtime estimate label updates as you toggle tests. Per-cycle metrics merging with totals summed, rates recomputed, and per-cycle history preserved on each test card.
- Per-cycle report tabs — each test card in the HTML report has tabs for Aggregate / Cycle 1 / Cycle 2 / etc. Cycles that deviate substantially from the median (>10% warn, >25% bad) get badged on their tab labels with explanatory banners.
- TDR (display-driver crash) detection — automatic Windows Event Log scan at the end of every stress run, looking for TDR events from
nvlddmkm,amdkmdap,igdkmd64, and the kernelDisplayprovider. Yellow-bordered section in the HTML report with timestamps and event details. - Fake-boost / hidden-throttling detection — cross-checks reported CPU clock vs measured throughput across cycles. Flags the fake-boost pattern (clock stays flat while throughput drops) seen on cheaper boards with undersized VRMs. Cross-references peak CPU temperature to distinguish legitimate thermal throttling from clock misreporting.
- Report comparison — side-by-side HTML diff of two stress runs (older "baseline" vs newer "comparison"). Color-coded deltas, direction-aware (lower temp is better, higher throughput is better). Useful for warranty work, before/after diagnostics, BIOS update validation. Reads from JSON sidecar files written alongside every stress report.
- Test profile save/load — save the current test selection + duration + cycles + cap settings as a named profile in the Profiles menu, recall later.
- SMART self-test auto-display — after triggering a short or long self-test, StressLab polls the drive every 30s (short) or 5min (long) and automatically displays results when the test completes.
- Report integrity stamping — SHA-256 content hash in both
.txtand.htmlreports, with a "Verify Report Integrity" utility that re-hashes and confirms the content is unchanged. Honest framing about what the check does and doesn't prove (StressLab is open source, so any HMAC key would be public). - Fan RPM range per test — each test card shows fan range during that phase ("1820 – 2400 RPM (avg 2050)" or "1500 RPM (steady)").
- Test cards aligned at bottom — "View Detailed Charts" buttons line up across all test cards regardless of how many metric rows each has.
What's new in v10
- Bundled .NET 10 sensor reader — the entire LibreHardwareMonitor stack ships pre-compiled inside StressLab.exe via PyInstaller. Customer machines need zero runtime downloads, zero compilation, zero external dependencies. No more "Rebuild Sensors" lock-while-running races.
- PawnIO replaces WinRing0 entirely — Microsoft-signed kernel driver, no Defender false positives. The PawnIO installer is bundled inside StressLab.exe and runs silently on first admin launch. Legacy WinRing0 binaries from previous installs are automatically cleaned up (including locked .sys files via reboot-deletion fallback).
- Per-test detailed metrics in stress reports — both .txt and .html exports now include CPU total ops + ops/sec, RAM passes/errors, GPU frame counts + average FPS, VRAM bandwidth (read/write GB/s) + total verified, disk SEQ1M Q8T1 speeds + total written/verified.
- Full-test HTML graph coverage — graph history expanded from 90 seconds to 30 minutes so the embedded report charts cover the entire stress run, not just the last minute.
- Direct SMART IOCTL reader — CrystalDiskInfo-style direct drive access via
IOCTL_STORAGE_QUERY_PROPERTY(NVMe) andIOCTL_ATA_PASS_THROUGH(SATA). Actual SMART attribute reads on every drive instead of "N/A" defaults. - Drive identity in health check — model, serial number, firmware revision, total host data read/written (TBR/TBW), all read directly from the drive.
- TBW endurance warnings — heuristic warning at 80% of estimated rated TBW (600 TBW per TB), error at 100%.
- SMART self-tests — short and long (extended) self-test triggers and result reading for both SATA (full support) and NVMe (read always works; trigger depends on driver).
- Windows Repair tools — SFC
/scannowand the full DISM trio (CheckHealth/ScanHealth/RestoreHealth) with live streaming output. - Service reset presets — one-click reset for Windows Update, Network Stack, Audio, Print Spooler, Windows Search, and Explorer/Shell, with cache cleanup.
For the full version history, see CHANGELOG.md. For a quick orientation to the app's tabs and buttons, see QUICKSTART.md.
Features
Stress Testing
- Sequential stress testing — run CPU → RAM → GPU → VRAM → Disk in order, or pick individual tests
- Power mode — simultaneous CPU + GPU load for PSU/thermal stress testing, with its own report card and metrics
- Burn-in / multi-cycle — repeat each selected test 1-5 times in a single run, with a live total-runtime estimate and per-cycle metric merging
- CPU stress — multi-process integer + float workload, targets 95%+ utilization across all cores
- RAM verify — multi-threaded pattern testing with VirtualLock (pinned pages), uses
memcmpfor fast in-place verification with 5 test patterns + inversions - GPU stress — adaptive OpenGL raymarching shader that auto-tunes to ~95% GPU utilization
- VRAM verify — integrates memtest_vulkan for Vulkan compute-based memory error detection (NVIDIA, AMD, Intel)
- Disk verify — unbuffered write + readback verification with per-block latency measurement, bus-type-aware speed benchmarking, configurable write cap, plus SEQ1M Q8T1 benchmark for accurate NVMe sequential speeds
- Test profile save/load — save the current selection + duration + cycles + cap settings as a named profile, recall later
Sensor Monitoring
- Live temperature monitoring — CPU, GPU, VRM, and SSD temps via the bundled .NET 10 sensor reader using LibreHardwareMonitorLib v0.9.6 over the Microsoft-signed PawnIO driver
- Live performance graphs — temperature, load%, and power graphs that update in real time during stress runs and embed in the HTML reports for the full duration of the test
- Voltage rail monitoring — +12V, +5V, +3.3V tracking with ATX12V tolerance warnings (where the motherboard SuperIO chip exposes them)
- Fan RPM range per test — each test card shows fan range during its phase
Diagnostic Detection
- Idle thermal warning — flags CPU cooler problems (loose heatsink, dried thermal paste, failing AiO pump, dead fan, dusty fins) by their thermal signature: low CPU utilization but sustained high temperature. Surfaces as a live banner on the Stress page even between test runs, and as a flag in the health report.
- TDR (display-driver crash) detection — automatic Windows Event Log scan at the end of every stress run for TDR events from
nvlddmkm,amdkmdap,igdkmd64, and the kernelDisplayprovider - Fake-boost / hidden-throttling detection — cross-checks reported CPU clock vs measured throughput across cycles. Catches cheaper boards that report inflated clocks while throttling silently.
- Network & bufferbloat test — multi-stream HTTP throughput, idle and loaded latency, packet loss, bufferbloat grade (Waveform/DSLReports A+/A/B/C/D scale)
Storage Diagnostics
- Direct SMART reader — reads NVMe SMART/Health (Log Page 0x02) and SATA SMART attributes directly via IOCTL — works where Windows'
Get-StorageReliabilityCounterreturns nothing - SMART self-tests with auto-display — trigger short/long self-tests on SATA drives, read self-test logs on NVMe drives, automatic result polling and display when the test completes
- TBW endurance tracking — warns when SSDs approach their rated total bytes written
- Speed benchmarking — auto-detects NVMe vs SATA and warns on underperformance (NVMe <1800 MB/s, SATA <400 MB/s)
- Drive identity — model, serial number, firmware revision, total host data read/written (TBR/TBW), all read directly from the drive
Reports
- Comprehensive HTML reports with embedded CSS, inline SVG charts, and dark theme — fully self-contained, no external assets
- Zoomable charts — drag any chart to brush-zoom on a region; double-click or click "Reset Zoom" to restore
- Per-cycle report tabs — multi-cycle runs get tabs for Aggregate / Cycle 1 / Cycle 2 / etc on each test card. Outlier cycles (>10% off median) get badged warnings.
- Per-test detailed metrics — CPU total ops + ops/sec, RAM passes/errors, GPU frame counts + average FPS, VRAM bandwidth (read/write GB/s) + total verified, disk SEQ1M Q8T1 speeds + total written/verified
- Peak readings dashboard — peak temps, peak power, voltage rail minimums, thermal headroom, throttle status
- Performance telemetry charts — CPU/GPU temp, load%, power, plus optional clock and fan charts; phase-banded so you can see which test was running when
- TDR events section — yellow-bordered list of any display-driver TDR events that fired during the run
- Report comparison — side-by-side HTML diff of two stress runs with color-coded deltas. Useful for warranty work, before/after diagnostics, BIOS update validation
- Report integrity — SHA-256 content hash with a Verify Report Integrity utility that re-hashes and confirms the content is unchanged
- JSON sidecar alongside every
.txtand.htmlfor the comparison feature and any future tooling - Auto-export — reports automatically saved to
Documents\StressLab Reports\on completion
Windows Tools
- Windows repair — SFC
/scannowand DISMCheckHealth/ScanHealth/RestoreHealthwith live streaming output, no PowerShell window required - Service reset presets — one-click recovery for Windows Update, Network Stack, Audio, Print Spooler, Search, Explorer/Shell
Health & System Info
- Health checks — driver status, BIOS age, TPM, Secure Boot, Windows activation, BSoD history, and direct-IOCTL SMART
- Memory configuration check — DDR generation detection, XMP/EXPO active vs rated speed, channel layout
- System info top bar — CPU + core/thread count + RAM (with DDR generation and speed) + GPU + total VRAM + Windows version
- Dark themed UI — modern dark interface built with tkinter
Requirements
For end users (running the built exe)
- OS: Windows 10 (1903+) or Windows 11 (x64)
- Privileges: Administrator (the exe self-elevates via UAC manifest)
- Network: Not required. Everything ships inside the exe.
- Runtime: None. .NET 10 is bundled, PawnIO installer is bundled, sensor reader is bundled.
For developers (building from source)
- OS: Windows 10 (1903+) or Windows 11 (x64)
- Python: 3.10 or newer
- .NET 10 SDK: required to build the sensor reader. Install from dotnet.microsoft.com.
- GPU stress: OpenGL 3.3+ capable GPU and drivers (integrated or discrete)
- VRAM test: Vulkan 1.1 capable GPU and drivers (any modern NVIDIA, AMD, or Intel GPU)
Python Dependencies
psutil>=5.9.0
wmi>=1.5.1
pywin32>=306
moderngl>=5.8.0
glcontext>=2.5.0
pyinstaller>=6.0
Install with:
pip install -r requirements.txt
Note:
modernglandglcontextare used only for the GPU stress test (OpenGL shader). They are optional — if not installed, the GPU stress test will be unavailable but everything else works.
Bundled Components
These ship inside StressLab.exe. No runtime downloads, no GitHub access, no external dependencies on customer machines.
| Component | Purpose | Size | License | Source |
|---|---|---|---|---|
| lhm_reader.exe | .NET 10 sensor reader (self-contained) | ~30 MB | MIT (StressLab) | Built locally via helper\build.bat |
| LibreHardwareMonitorLib v0.9.6 | Bundled inside lhm_reader.exe (NuGet) | included | MPL 2.0 | GitHub |
| PawnIO_setup.exe | Microsoft-signed kernel driver installer | ~3 MB | MIT | GitHub |
| memtest_vulkan.exe | VRAM error detection (Vulkan compute) | ~600 KB | zlib | GitHub |
On first launch, StressLab extracts lhm_reader.exe to %LOCALAPPDATA%\StressLab\lhm\ and runs PawnIO_setup.exe -install -silent (when running as Admin) to install the kernel driver. Both happen silently with no user interaction. Subsequent launches reuse the deployed files.
memtest_vulkan is downloaded silently in the background on startup — typically ready by the time the user clicks Start.
Usage
Run the prebuilt EXE
dist\StressLab.exe
The exe self-elevates via UAC manifest, so it always runs as Administrator. No _admin.bat wrapper needed.
Run from source
python stresslab.py
For full functionality (PawnIO install, direct SMART access, self-tests, SFC/DISM, service resets):
stresslab_admin.bat
Or right-click → Run as Administrator.
Build standalone EXE
build_exe.bat
This 5-step script:
- Installs Python dependencies via pip
- Builds the .NET 10 sensor reader (
helper\build.bat) - Stages bundled assets (copies
lhm_reader.exetoassets\, verifiesPawnIO_setup.exeis present) - Runs PyInstaller against
stresslab.spec - Verifies
dist\StressLab.exewas produced
Produces dist\StressLab.exe — a single-file executable with admin elevation built in. Final size is approximately 31 MB (UPX-compressed).
One-time setup before first build: download PawnIO_setup.exe from github.com/namazso/PawnIO.Setup/releases/latest and save it as assets\PawnIO_setup.exe. The build is otherwise fully offline-capable.
Running a test
- Select which tests to run (CPU, RAM, GPU, VRAM, Disk) using the checkboxes
- Set the duration per test (5m, 15m, or 60m)
- Optionally set the disk write cap (200GB, 500GB, or 1TB) — default 200GB
- Click ▶ START
- Tests run sequentially — progress, temperatures, and metrics update live
- When complete, a pass/fail summary is shown in the log
- Reports auto-export to
Documents\StressLab Reports\(uncheck "Auto-export" to disable) - Click Export for manual save-as to a custom location
Test Details
CPU Stress Spawns worker processes (one per logical core) running tight integer and floating-point loops. Targets 95%+ CPU utilization. Temperature is monitored throughout.
RAM Verify
Allocates ~75% of available RAM using VirtualAlloc with VirtualLock (pins pages to physical RAM, preventing swapping). Runs 5 test patterns (Solid 0x00, Solid 0xFF, Checkerboard 0xAA, Checkerboard 0x55, Random) plus their bitwise inversions across multiple worker threads. Verification uses msvcrt.memcmp for fast in-place comparison — only falls back to byte-level inspection on mismatch.
GPU Stress Renders a computationally expensive raymarching shader (SDF scene with reflections, refractions, soft shadows, ambient occlusion, and fractal noise). Adaptive algorithm adjusts shader complexity (ray steps and noise octaves) to maintain ~95% GPU utilization. Uses a background nvidia-smi poller thread for utilization feedback (no CPU impact from process spawning).
VRAM Verify Launches memtest_vulkan as a subprocess — a Vulkan compute-based memory tester that allocates as much VRAM as possible and writes/reads test patterns directly via compute shaders. Uses non-sequential memory access patterns designed to catch address bus errors that linear patterns miss. Output is read by tailing memtest_vulkan's own log file (avoids Windows pipe buffering issues). Reports iteration count, read/write bandwidth, and any bit-level errors detected. Works on any GPU with Vulkan 1.1 support.
Disk Verify
Creates a temporary test file (25% of free space, up to 2GB) and performs unbuffered write + readback verification using the Windows CreateFileW API with FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH — bypasses the entire OS filesystem cache so every write goes to NAND and every read comes from NAND.
Each pass:
- Writes 1MB blocks with a test pattern + position-dependent stamp
- Reads back each block, measures per-block latency, and verifies via
memcmp - Cycles through 5 patterns: Random (incompressible), 0xAA, 0x55, 0xFF, 0x00
Plus a SEQ1M Q8T1 benchmark phase using 8 worker threads with interleaved offsets to saturate NVMe controller queues — catches drives that pass verification but are running below their spec sequential speed.
What it catches that SMART misses:
- Bad NAND cells — write succeeds but readback data differs
- Degrading cells — blocks >10ms read latency (SSD needs internal retries)
- Controller issues — data corruption in the I/O path
- Fake/counterfeit drives — later blocks return garbage
- Speed degradation — auto-detects NVMe vs SATA via WMI and warns if speeds fall below expected thresholds
The write cap (200GB / 500GB / 1TB) limits total bytes written to protect drive endurance. A 200GB test uses ~0.03% of a typical 600 TBW SSD's lifetime. If the cap is reached before the timer expires, the test completes early automatically.
Power Mode Runs CPU stress and GPU stress simultaneously for maximum system-wide thermal and power delivery stress testing.
SMART Health Check
The SMART check on the Health tab uses direct IOCTL reads (CrystalDiskInfo-style) for accurate data on every drive — no more falling back to "N/A" defaults.
For each drive it reports:
- Identity: model, serial number, firmware revision
- Health, wear percentage, power-on hours, power cycles, temperature
- Total host data read and written (TBR / TBW)
- NVMe-specific: critical warnings, available spare percentage
- SATA-specific: pending sectors, uncorrectable sectors, reallocated sectors
Warning thresholds
| Trigger | Status | Threshold |
|---|---|---|
| Wear % over 80 | Error | NVMe Percentage Used > 80, or SATA Wearout (0xE9) value < 20 |
| Wear % over 50 | Warning | (same attributes, intermediate range) |
| Power-on hours over 35,000 | Warning | About 4 years of 24/7 operation |
| Uncorrected read errors | Error | Any non-zero count |
| NVMe media errors | Error | Any non-zero count |
| NVMe critical warning bits set | Warning | Any of bits 0–4 from the SMART log byte 0 |
| NVMe spare below threshold | Warning | Available spare % < drive's reported threshold |
| SATA pending sectors | Error | Any non-zero count |
| SATA uncorrectable sectors | Error | Any non-zero count |
| SATA reallocated sectors over 100 | Warning | Attribute 0x05 raw count |
| Drive Windows HealthStatus not "Healthy" | Error | From Get-PhysicalDisk |
| TBW over rated endurance | Error | Host writes >= estimated 600 TBW per TB capacity |
| TBW approaching rated endurance | Warning | Host writes >= 80% of estimated 600 TBW per TB |
The TBW heuristic of 600 TBW per TB is representative of mid-to-high consumer TLC SSDs (Samsung 970 EVO, WD Black SN850, Crucial MX500). QLC drives are typically rated lower (Samsung 870 QVO 1TB is 360 TBW). The warning surfaces both written total and estimated rating so you can compare against the manufacturer's actual datasheet.
SMART Self-Test (Diagnostic Tools tab)
Triggers the drive's built-in self-test. The drive runs the test in its own firmware so you can keep using your computer normally.
- Short test (~2 min): electrical/mechanical health check
- Long test (1–2+ hours): reads every sector
SATA drives — full support via IOCTL_ATA_PASS_THROUGH with SMART EXECUTE OFF-LINE IMMEDIATE (cmd 0xB0 / feat 0xD4). Trigger and read both work.
NVMe drives — read always works (uses IOCTL_STORAGE_QUERY_PROPERTY with NVMe Log Page 0x06). Trigger requires the NVMe driver to expose IOCTL_STORAGE_PROTOCOL_COMMAND writes, which most consumer NVMe drivers (including Microsoft's stornvme on older Windows builds) don't do. StressLab detects this on click and shows a list of vendor utilities you can use to trigger the test, then come back to View Results to see the outcome.
Windows Repair (Windows Utilities tab)
Live-streamed output for the standard Windows repair toolkit:
- SFC /scannow — scans and repairs system files (typically 5–15 minutes)
- DISM /CheckHealth — quick component store check (no changes)
- DISM /ScanHealth — full integrity scan (5–10 minutes, read-only)
- DISM /RestoreHealth — repair component store from Windows Update (10–30 minutes, downloads if needed)
Output streams in real-time to the panel — no detached PowerShell window. SFC's UTF-16 console output is decoded properly.
Service Reset Presets (Windows Utilities tab)
One-click stop / cache-cleanup / start cycle for commonly broken services. Each preset shows a confirmation dialog with what it will do before running.
| Preset | What it does |
|---|---|
| Windows Update | Stops BITS, wuauserv, appidsvc, cryptsvc; renames SoftwareDistribution and catroot2 with timestamped backup; restarts services |
| Network Stack | Stops Dnscache; runs netsh winsock reset, netsh int ip reset, ipconfig /flushdns /release /renew |
| Audio | Stops/starts Windows Audio + Audio Endpoint Builder |
| Print Spooler | Stops spooler, clears %SystemRoot%\System32\spool\PRINTERS\*, starts spooler |
| Windows Search | Stops/starts WSearch (often fixes Start menu search) |
| Explorer / Shell | Kills and restarts explorer.exe (taskbar, Start menu, desktop) |
Auto-Export
When enabled (default), health check results and stress test certificates are automatically saved to:
%USERPROFILE%\Documents\StressLab Reports\
Filenames include the hostname and timestamp, e.g. StressLab_Stress_DESKTOP-ABC_20260301_143022.txt. Both .txt and .html versions are saved every time. The "Auto-export" checkbox appears on both the Health and Stress tabs. Manual export via the Export button is always available regardless of this setting.
The HTML version of the stress report includes embedded SVG performance charts covering the full test duration, a peak readings dashboard, and per-test detailed metrics (CPU ops/sec, RAM passes, GPU frame counts, VRAM bandwidth, disk MB/s).
Architecture
StressLab is a single-file Python application (~5,500 lines) plus a separate .NET 10 sensor reader (~700 lines of C# across 5 files). Both ship pre-compiled inside StressLab.exe.
Top-level layout
StressLab/
├── stresslab.py # Main application (Python)
├── stresslab.spec # PyInstaller spec - bundles helper + PawnIO into exe
├── stresslab.ico # Application icon
├── requirements.txt # Python dependencies
├── stresslab_admin.bat # Run-as-admin launcher (source mode)
├── build_exe.bat # 5-step build pipeline
├── helper/ # .NET 10 sensor reader source
│ ├── lhm_reader.csproj # Project file (NuGet ref to LibreHardwareMonitorLib 0.9.6)
│ ├── Program.cs # Entry point, poll loop, JSON output
│ ├── SensorReading.cs # Output struct + source-generated JSON context
│ ├── SensorMatcher.cs # Per-SensorType dispatch with vendor-name matching
│ ├── SensorDumper.cs # Diagnostic dump mode
│ ├── StdoutRedirector.cs # Module initializer - silences native lib chatter
│ ├── build.bat # dotnet publish wrapper
│ └── README.md # Helper-specific docs
├── assets/ # Bundled into exe via PyInstaller --add-data
│ ├── lhm_reader.exe # Built by helper/build.bat
│ └── PawnIO_setup.exe # Manual one-time download from namazso/PawnIO.Setup
├── LICENSE # MIT license
├── THIRD_PARTY_LICENSES # Bundled component licenses
├── README.md # This file
├── CHANGELOG.md # Version history
└── QUICKSTART.md # Quick orientation guide for end users
Python dependencies
- tkinter — UI framework (ships with Python)
- psutil — CPU/RAM metrics and process management
- wmi — Windows system information queries
- moderngl — OpenGL context and shader rendering for GPU stress
- ctypes — direct Windows API calls (VirtualAlloc, VirtualLock, CreateFileW, kernel32, msvcrt, IOCTL pass-through, MoveFileEx for reboot-deletion)
- subprocess — manages memtest_vulkan, lhm_reader, PawnIO installer, and SFC/DISM streaming
- multiprocessing — CPU stress worker processes
- threading — RAM verify workers, disk verify worker, nvidia-smi poller, temperature reader, async stop handlers, repair output streamers
Temperature Monitoring Priority Chain
- lhm_reader.exe (bundled) — pre-compiled .NET 10 helper using LibreHardwareMonitorLib v0.9.6 over the PawnIO driver. Best coverage; this is the primary path on every Phase 2 install.
- WMI OHM/LHM namespace — fallback if user runs LHM/OHM GUI separately
- nvidia-smi — fallback for NVIDIA GPU temperature only
- WMI thermal zone — basic CPU fallback
The helper subprocess streams JSON sensor lines on stdout at 1 Hz. Python parses each line and updates the live displays. Native library chatter (Intel Level Zero, NVIDIA driver banners) is silenced via a [ModuleInitializer] that redirects the OS-level stdout handle to NUL before any other code runs.
SMART Reading
Direct IOCTL pass-through, requires Administrator. Two paths:
- NVMe →
IOCTL_STORAGE_QUERY_PROPERTYwithStorageDeviceProtocolSpecificPropertyreading NVMe Log Pages 0x02 (SMART/Health), 0x06 (Self-test log), and CNS 0x01 (Identify Controller). Works on every NVMe driver including Microsoft's stornvme. - SATA →
IOCTL_ATA_PASS_THROUGHsending SMART READ DATA (0xB0/0xD0), SMART READ LOG (0xB0/0xD5), IDENTIFY DEVICE (0xEC), and SMART EXECUTE OFF-LINE IMMEDIATE (0xB0/0xD4) for self-tests.
Runtime data (auto-created)
%LOCALAPPDATA%\StressLab\
├── lhm\
│ └── lhm_reader.exe # Extracted from bundle on first launch (~30 MB)
└── memtest_vulkan.exe # Downloaded on first launch (~600 KB)
PawnIO driver lives at C:\Program Files\PawnIO\PawnIO.sys and is registered as a kernel-mode service named PawnIO. It's installed silently on first admin launch via the bundled PawnIO_setup.exe -install -silent.
Auto-exported reports
%USERPROFILE%\Documents\StressLab Reports\
├── StressLab_Health_HOSTNAME_YYYYMMDD_HHMMSS.txt
├── StressLab_Health_HOSTNAME_YYYYMMDD_HHMMSS.html
├── StressLab_Stress_HOSTNAME_YYYYMMDD_HHMMSS.txt
└── StressLab_Stress_HOSTNAME_YYYYMMDD_HHMMSS.html
Build
The default stresslab.spec produces a single-file --onefile exe with admin elevation and bundled assets. The full pipeline is just build_exe.bat. For better Defender behavior (occasionally an issue with single-file PyInstaller bootstraps), switch to --onedir by editing stresslab.spec:
exe = EXE(pyz, a.scripts, [], exclude_binaries=True, name='StressLab',
debug=False, strip=False, upx=True, console=False,
uac_admin=True, icon='stresslab.ico')
coll = COLLECT(exe, a.binaries, a.datas, strip=False, upx=True,
upx_exclude=[], name='StressLab')
This produces a dist\StressLab\ folder you ship as a zip. Defender heuristics flag the self-extracting --onefile mode much more aggressively than --onedir.
Troubleshooting
Temperatures show as "—" Run as Administrator so PawnIO can install. Once the driver is registered, temperatures will appear after a quick app restart. PawnIO is Microsoft-signed and won't trigger Defender.
"PawnIO needed — restart as Admin to install"
StressLab needs administrator privileges to install the PawnIO kernel driver. The exe self-elevates via UAC, so this typically only appears when running from source — use stresslab_admin.bat or right-click → Run as Administrator.
SMART check shows "100% health" or "N/A" power-on hours
You're running without admin privileges, so the direct IOCTL path is unavailable and StressLab falls back to PowerShell's Get-StorageReliabilityCounter which returns nothing on many drives. Run as Administrator for the full direct-IOCTL SMART data.
NVMe self-test "Cannot trigger" error
Your NVMe driver doesn't expose IOCTL_STORAGE_PROTOCOL_COMMAND for command writes. This is the case for most NVMe drivers including Microsoft's stornvme on older Win10/11 builds. Workaround: trigger the test from the drive vendor's utility (Samsung Magician, WD Dashboard, Crucial Storage Executive, etc.), then come back to StressLab and click "View Results" — the result reading path works on all NVMe drives.
"memtest_vulkan not found" during VRAM test The auto-download may have failed (firewall, no internet). Go to the Windows Utilities tab (Dependencies & Sensors) and click "Download memtest_vulkan" manually. Requires internet access to GitHub.
Windows Defender flags the exe
With v10's switch to PawnIO and pre-compiled bundled helper, the WinRing0 false-positive issue is gone. If your bundled StressLab.exe is still flagged, it's likely the PyInstaller --onefile heuristic. Switch the spec to --onedir mode (see Build section). You can also submit the binary to Microsoft for whitelisting at microsoft.com/wdsi/filesubmission (www.microsoft.com).
Sensor reader not deployed / "Reinstall Sensors" button
On the Windows Utilities tab (Dependencies & Sensors) there is a Reinstall Sensors button that re-extracts the bundled lhm_reader.exe to %LOCALAPPDATA%\StressLab\lhm\ and restarts the helper subprocess. Useful after StressLab updates (auto-detected by helper file size — anything under 1 MB at the helper path is recognized as a legacy stub and replaced automatically on launch). No reboot needed.
GPU stress test unavailable
Install moderngl and glcontext: pip install moderngl glcontext. Requires OpenGL 3.3+ drivers.
GPU utilization settles below target The adaptive algorithm needs nvidia-smi for feedback. Ensure NVIDIA drivers are installed. On AMD/Intel GPUs, the shader runs at fixed high complexity without adaptive tuning.
Disk test shows low speeds on NVMe If the test file is small relative to the drive's SLC cache, speeds may appear normal even on a degrading drive. Use the 500GB or 1TB write cap to push past the cache and measure steady-state performance. Low speeds on pass 1 are ignored (cold start).
UEFI reboot fails the first time, works the second
Fixed in v10. The reboot flow now clears stale shutdown state with shutdown /a and retries automatically before reporting failure.
Windows shows "Not Activated" in health checks Correctly reports Windows activation status. WMI license codes 0 (Unlicensed) and 5 (Notification) are both reported as "Not Activated".
SSD shows 94°C false alert (older builds) Fixed in v10. Earlier builds were reading the SSD's static "Critical Temperature" SMART threshold (a fixed value the drive reports as "this is what hot would be") as the live current temp. The matcher now skips Warning/Critical/Threshold-named sensors and only uses live temp readings.
Build fails: "lhm_reader.exe not found"
Run helper\build.bat first to produce the .NET 10 sensor reader, then re-run build_exe.bat. Or just run build_exe.bat directly — it chains the helper build automatically. Requires the .NET 10 SDK on the build machine.
Build fails: "PawnIO_setup.exe not found"
One-time setup: download PawnIO_setup.exe from github.com/namazso/PawnIO.Setup/releases/latest and save it as assets\PawnIO_setup.exe. The build is otherwise fully offline-capable.
License
StressLab is licensed under the MIT License.
Third-party components have their own licenses — see THIRD_PARTY_LICENSES for details.
Credits
StressLab — Cincy Power Computers, LLC
Built with:
- LibreHardwareMonitor (MPL 2.0) — hardware sensor access
- PawnIO (MIT) — Microsoft-signed kernel driver
- memtest_vulkan by GpuZelenograd (zlib) — Vulkan VRAM error detection
- psutil (BSD 3-Clause) — system metrics
- moderngl (MIT) — OpenGL rendering