Skip to content

Windows: stop full process-table CPU sampling on every session (fixes DPC_WATCHDOG_VIOLATION) - #13405

Open
npapano42 wants to merge 1 commit into
warpdotdev:masterfrom
npapano42:npapano42/fix-windows-process-enumeration-dpc-watchdog
Open

Windows: stop full process-table CPU sampling on every session (fixes DPC_WATCHDOG_VIOLATION)#13405
npapano42 wants to merge 1 commit into
warpdotdev:masterfrom
npapano42:npapano42/fix-windows-process-enumeration-dpc-watchdog

Conversation

@npapano42

@npapano42 npapano42 commented Jul 6, 2026

Copy link
Copy Markdown

Description

On Windows, is_kaspersky_running() enumerated the entire system process table with per-process CPU cycle-time sampling on every terminal session bootstrap (every tab/pane/subshell/SSH session), with no cache — solely to check whether a process named avp (Kaspersky) exists.

On Windows, per-process CPU sampling makes the kernel issue NtQueryInformationProcess(ProcessCycleTime) for every process, and the full enumeration issues NtQuerySystemInformation(SystemProcessInformation). Both force KeFlushProcessWriteBuffers, which broadcasts a synchronous inter-processor interrupt to every logical core. Across the whole process table, in bursts of session inits, on high-core-count machines (worse with HVCI, where each IPI is a hypercall) this keeps all cores spinning at DISPATCH_LEVEL long enough to trip the DPC watchdog and bugcheck the system (DPC_WATCHDOG_VIOLATION 0x133). Confirmed via a full kernel dump on a 24-thread Ryzen 9 5900X with Memory Integrity/HVCI enabled — the faulting thread was in warp.exe, in exactly this call path.

Fix

Two small, behavior-preserving changes:

  1. app/src/system/info.rsrefresh_all_processes() now uses a dedicated name-only ProcessRefreshKind (all_processes_refresh_kind()ProcessRefreshKind::nothing()) instead of the CPU+memory refresh_kind() used by the single-PID self-poll. This drops the per-process ProcessCycleTime syscalls; process names are still populated, so processes_by_name("avp") is unchanged. The 5-second self-poll still samples CPU/memory (cheap — one PID).
  2. app/src/util/windows.rsis_kaspersky_running() is cached in a OnceLock<bool>, so the full sweep runs at most once per launch instead of on every session bootstrap.

Net effect: an N-process cycle-time storm on every session → one names-only enumeration, once per launch.

Trade-off: antivirus started mid-session is no longer re-detected. Impact is negligible — the cached flag only decides whether to attempt an async_fs fallback for reading PowerShell history, which already degrades gracefully on error — and it's well worth eliminating a whole-system crash. (If a reviewer prefers, the cache can instead live as an Option<bool> on the SystemInfo singleton rather than a module-level OnceLock.)

Linked Issue

Fixes #13404 — see also #7771 and #7561, which this likely also addresses on Windows.

Testing

Built and verified on Windows 11, AMD Ryzen 9 5900X (24 logical cores), Memory Integrity/HVCI ON, Rust 1.92.0 (MSVC).

  • Regression test (app/src/system/info_tests.rs::all_processes_refresh_kind_does_not_sample_cpu_or_memory) asserting the full-table sweep samples neither CPU nor memory, while the single-PID self-poll still samples both. Passes; would have failed on the previous code (the All sweep used the CPU-sampling refresh_kind()).

    test system::info::tests::all_processes_refresh_kind_does_not_sample_cpu_or_memory ... ok
    test result: ok. 1 passed; 0 failed
    
  • cargo clippy -p warp --all-targets --tests -- -D warnings → clean. ./script/format --check and ./script/check_no_inline_test_modules → clean.

  • Behavioral verification with a standalone probe using the same sysinfo 0.37 API/features Warp uses, reproducing refresh_all_processes's ProcessesToUpdate::All sweep (364 processes):

    • Process names are still populated under the name-only (nothing()) refresh — all 364 processes have non-empty names, identical to the old .with_cpu().with_memory() path — so processes_by_name("avp") is unaffected. processes_by_name("explorer") resolves true under the name-only refresh.
    • The name-only sweep is ~1.6× faster in plain wall-clock (5,969 µs vs 9,773 µs avg over 15 iterations). This understates the real-world win, which also removes (a) the per-process ProcessCycleTime all-core IPI cost that scales with core count and HVCI, and (b) the per-session repetition (now cached to once per launch).
  • I have manually tested my changes locally with ./script/run

    • Built the gui binary on Windows (cargo build --bin warp-oss --features gui, the target ./script/run runs on this platform) — compiles clean, and launched it as a full GUI: the app comes up, spawns terminal sessions, and runs without panic. This exercises the modified path directly — is_kaspersky_running()refresh_all_processes() runs during session bootstrap — confirming the name-only refresh + OnceLock cache work end-to-end in the real app, not just in tests.

Screenshots / Videos

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

CHANGELOG-BUG-FIX: Windows — stopped enumerating the full process table with per-process CPU sampling on every terminal session, which could cause DPC_WATCHDOG_VIOLATION / high CPU on machines with many cores.

@cla-bot

cla-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown

Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have the users @npapano42 on file. In order for us to review and merge your code, each contributor must visit https://cla.warp.dev to read and agree to our CLA. Once you have done so, please comment @cla-bot check to trigger another check.

@github-actions github-actions Bot added the external-contributor Indicates that a PR has been opened by someone outside the Warp team. label Jul 6, 2026
@npapano42

Copy link
Copy Markdown
Author

@cla-bot check

@cla-bot cla-bot Bot added the cla-signed label Jul 6, 2026
@cla-bot

cla-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown

The cla-bot has been summoned, and re-checked this pull request!

@npapano42
npapano42 marked this pull request as ready for review July 6, 2026 18:54
@oz-for-oss

oz-for-oss Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

@npapano42

Every PR must be linked to a same-repo issue before Oz can review it.

This PR is linked to #13404, but no linked issue is marked ready-to-implement yet. Only repository maintainers apply that label, so please wait for a maintainer to mark the issue. Once it is marked, push a new commit or comment /oz-review to re-trigger review.

See the contribution guidelines for the full readiness model.

Powered by Oz

oz-for-oss[bot]
oz-for-oss Bot previously requested changes Jul 6, 2026

@oz-for-oss oz-for-oss Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@npapano42

Every PR must be linked to a same-repo issue before Oz can review it.

This PR is linked to #13404, but no linked issue is marked ready-to-implement yet. Only repository maintainers apply that label, so please wait for a maintainer to mark the issue. Once it is marked, push a new commit or comment /oz-review to re-trigger review.

See the contribution guidelines for the full readiness model.

Powered by Oz

@aminya aminya left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I faced this DPC issue as well. Thanks

@github-actions

Copy link
Copy Markdown
Contributor

Hi @npapano42 — a reviewer requested changes on this PR and it hasn't had activity from you in 7 days. When you get a chance, please push updates or reply to the review so a reviewer can take another look. Without activity, this PR will be automatically closed after 14 days of inactivity.

@github-actions

Copy link
Copy Markdown
Contributor

Hi @npapano42 — final reminder: a reviewer requested changes on this PR and it has been inactive for 10 days. It will be automatically closed in about 4 day(s) unless you push updates or reply. Maintainers can apply the no-autoclose label to keep it open.

@aminya

aminya commented Jul 17, 2026

Copy link
Copy Markdown

The reviewer says the maintainers need to add a tag. This doesn't look like something the contributors can address.

@npapano42

Copy link
Copy Markdown
Author

Yeah, I messaged in the slack channel to see if I can get it added, otherwise when the attached issue gets some traction I'll reopen the PR

@bholmesdev

Copy link
Copy Markdown
Contributor

/oz-review

@oz-for-oss

oz-for-oss Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

@bholmesdev

I'm re-reviewing this pull request in response to a review request.

You can view the conversation on Warp.

I reviewed this pull request and requested human review from: @acarl005.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss
oz-for-oss Bot dismissed their stale review July 20, 2026 15:28

Oz no longer requests changes for this pull request after the latest automated review.

@oz-for-oss oz-for-oss Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR changes Windows process enumeration used for Kaspersky detection so the full process-table refresh no longer samples per-process CPU/memory, and caches the Kaspersky check once per process launch to avoid repeating the sweep for every session bootstrap. It also adds a regression test asserting the full-table refresh kind remains name-only while the single-PID self-poll still samples CPU and memory.

Concerns

  • No blocking correctness, security, or spec-alignment concerns found in the annotated diff.

Verdict

Found: 0 critical, 0 important, 0 suggestions

Approve

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss
oz-for-oss Bot requested a review from acarl005 July 20, 2026 15:28
@npapano42

Copy link
Copy Markdown
Author

Bumping for review @acarl005

@npapano42

Copy link
Copy Markdown
Author

@acarl005 bumping again for review

@acarl005

Copy link
Copy Markdown
Contributor

Ah thanks for bumping. I'll have a look

acarl005 pushed a commit that referenced this pull request Aug 11, 2026
…NT (#14907)

## Description
Companion to warpdotdev/common-skills#50. A minimal, repo-specific
addition to `.agents/skills/review-pr-local/SKILL.md`: two short clauses
appended to the existing testing bullet and the existing commenting
bullet (the latter added by #14902) under `## Repo-specific style and
recurring review patterns`, noting that a confirmed violation of either
should be treated as `⚠️ [IMPORTANT]`, not a nit. Nothing else changed —
no new sections, no changes to
`.agents/skills/rust-unit-tests/SKILL.md`, and #14902's original
comment-audit bullet is otherwise untouched.

`git diff origin/master --stat`: 1 file changed, 2 insertions(+), 2
deletions(-).

Prompted by two real issues that slipped through a `review-pr` +
`review-pr-local` pass on PR #13405: a transformation-comment violation
in `app/src/util/windows.rs` (plus other `AGENTS.md` comment issues),
and a "regression test" in `app/src/system/info_tests.rs` that only
exercised a private helper instead of the call site where the historical
bug lived.

Linear: QUALITY-1563

## Linked Issue
- [x] N/A — skill-only documentation change, not tied to a GitHub issue.
- [x] Not user-visible; no screenshots/video applicable.

## Testing
Doc-only change — no code changed, so no automated/manual app testing
applies.

Validated together with warpdotdev/common-skills#50's
specialization-contract repair via fresh subagents with no prior
knowledge of the expected findings, run repeatedly with no scripted
loading of this companion skill (each reviewer had to discover it on its
own). Result across 3 final independent runs: the companion was
discovered unprompted in all 3, confirming the core-skill specialization
mechanism works; of the three original PR #13405 findings, the
transformation-comment violation surfaced in 2 of 3 runs (⚠️ IMPORTANT),
while the redundant/duplicated comment and the regression-test call-site
gap did not surface in any of the 3 runs at this final state. The
regression-test gap specifically has no corresponding content anywhere
in this file or in `rust-unit-tests/SKILL.md` right now, so it isn't
expected to surface until/unless that content is added here — see
warpdotdev/common-skills#50's description for the full tallies, verbatim
`review.json`, and validator output.

### Screenshots / Videos
N/A — skill documentation change only.

## Agent Mode
- [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode

_Conversation:
https://staging.warp.dev/conversation/ed29a745-bc15-4653-8e60-7ae1d25ec9da_
_Run:
https://oz.staging.warp.dev/runs/019fedeb-6452-7cb8-b1a5-08c41f3f37c6_

_This PR was generated with [Oz](https://warp.dev/oz)._

---------

Co-authored-by: Oz <oz-agent@warp.dev>
Co-authored-by: Warp Agent <agent@warp.dev>
@acarl005

Copy link
Copy Markdown
Contributor

@npapano42 Sorry for the delays. We've changed our review bot guidelines a bit so I'm going to run that again.

@acarl005

Copy link
Copy Markdown
Contributor

/oz-review

@oz-for-oss

oz-for-oss Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

@acarl005

I'm re-reviewing this pull request in response to a review request.

You can view the conversation on Warp.

I reviewed this pull request and requested human review from: @acarl005.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss oz-for-oss Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR reduces Windows session-bootstrap process enumeration cost by separating the full process-table refresh from the current-process CPU/memory refresh and caching the Kaspersky process check for the lifetime of the Warp process.

Concerns

  • No blocking correctness, security, comment-quality, test-quality, or spec-alignment concerns found.

Verdict

Found: 0 critical, 0 important, 0 suggestions

Approve

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@acarl005

Copy link
Copy Markdown
Contributor

@oz-agent rebase this onto master so we can review with the latest version of the skills

@oz-by-warp

oz-by-warp Bot commented Aug 11, 2026

Copy link
Copy Markdown

Oz run started.

View conversation · View run in Oz

@oz-for-oss

oz-for-oss Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

@acarl005

I ran into an unexpected error while working on this.

Powered by Oz

@oz-by-warp

oz-by-warp Bot commented Aug 11, 2026

Copy link
Copy Markdown

I rebased this onto current master — it applied cleanly with no conflicts, and the resulting diff is identical to the original commit's diff. However, I could not force-push it onto this PR's branch: the head branch lives on the fork npapano42/warp, and GitHub rejected the push with

refusing to allow a GitHub App to create or update workflow .github/workflows/changelog_draft.yml without workflows permission

Rebasing onto today's master unavoidably carries updated .github/workflows/* files, and my app token lacks the workflows permission, so any rebase/merge push to the fork branch is blocked.

So I pushed the rebased commit to a branch in warpdotdev/warp and opened a draft PR for it instead: #14955 (branch oz/13405-rebase-onto-master). It's the same single commit, just replayed on current master, so a review there will run against the latest skills.

Verification on the rebased branch:

  • ./script/format --check → clean
  • cargo clippy -p warp --all-targets --tests -- -D warnings → clean
  • cargo check -p warp --all-targets → clean
  • I couldn't execute the unit test in the sandbox (linking the warp lib test binary got OOM-killed by the memory limit), but it's a pure assertion over ProcessRefreshKind flags and it type-checks under clippy --all-targets.

If you'd prefer the rebase to land on #13405 itself rather than a mirror PR, someone with the workflows permission (or the contributor) needs to do the force-push — I can't from here.

Pull Requests:

Remote branches:

  • oz/13405-rebase-onto-master

@acarl005 acarl005 self-assigned this Aug 11, 2026
@acarl005

Copy link
Copy Markdown
Contributor

/oz-review

@oz-for-oss

oz-for-oss Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

@acarl005

I'm re-reviewing this pull request in response to a review request.

You can view the conversation on Warp.

I reviewed this pull request and requested human review from: @acarl005.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss oz-for-oss Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR changes the Windows Kaspersky detection path so the full process-table sweep uses a name-only sysinfo::ProcessRefreshKind, then caches the Kaspersky result once per Warp process. The added regression test checks that the full-table sweep no longer requests CPU or memory while the single-PID self-poll still does.

Concerns

  • No blocking concerns found.
  • The added comments explain the non-obvious Windows syscall/IPI rationale and cache trade-off rather than narrating implementation mechanics.
  • No approved spec context was provided, and I found no material spec drift to evaluate.
  • No security findings.
  • Screenshots or video are not meaningful for this non-visual Windows syscall/crash-prevention change; the described Windows build, GUI bring-up, regression test, and sysinfo probe are the relevant evidence.

Verdict

Found: 0 critical, 0 important, 0 suggestions

Approve

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@acarl005 acarl005 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code comments don't follow the project guidelines. Please ask your agent to read the commenting guidelines in AGENTS.md and edit the comments on this PR according to those. Make sure you merge/rebase the latest master before doing so.

I'll approve after these changes.

Comment thread app/src/system/info_tests.rs Outdated
/// forcing an all-core `KeFlushProcessWriteBuffers` IPI. The single-PID
/// self-poll ([`SystemInfo::refresh_kind`]) legitimately still samples both.
#[test]
fn all_processes_refresh_kind_does_not_sample_cpu_or_memory() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a useful test. you can delete this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about the long comment - fixed and test removed

is_kaspersky_running() enumerated the entire process table with per-process
CPU cycle-time sampling on every terminal session bootstrap, just to check
whether "avp" (Kaspersky) is running. On Windows each per-process CPU sample
issues NtQueryInformationProcess(ProcessCycleTime), which forces
KeFlushProcessWriteBuffers -- a synchronous inter-processor interrupt to every
logical core. Across the whole process table, in bursts of session inits, on
high-core-count machines this keeps all cores spinning at DISPATCH_LEVEL long
enough to trip the DPC watchdog and bugcheck the system
(DPC_WATCHDOG_VIOLATION 0x133). Confirmed via a kernel dump on a 24-thread
Ryzen 9 5900X with Memory Integrity/HVCI enabled.

Fix:
- refresh_all_processes() now uses a name-only ProcessRefreshKind
  (all_processes_refresh_kind) instead of the CPU+memory self-poll kind,
  eliminating the per-process ProcessCycleTime syscalls. Names are still
  populated, so the Kaspersky name check is unchanged.
- Cache is_kaspersky_running() in a OnceLock so the full sweep runs at most
  once per launch instead of on every session bootstrap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@npapano42
npapano42 force-pushed the npapano42/fix-windows-process-enumeration-dpc-watchdog branch from 3fdabdb to 5eecb87 Compare August 12, 2026 01:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed external-contributor Indicates that a PR has been opened by someone outside the Warp team.

Projects

None yet

4 participants