Skip to content

feat(engine): read and report the WinDivert queue a session runs behind - #62

Merged
donislawdev merged 1 commit into
masterfrom
feat/report-the-driver-queue-parameters
Jul 28, 2026
Merged

feat(engine): read and report the WinDivert queue a session runs behind#62
donislawdev merged 1 commit into
masterfrom
feat/report-the-driver-queue-parameters

Conversation

@donislawdev

Copy link
Copy Markdown
Owner

Audit finding F10, part a - visibility. The measurement half (part b) follows separately,
because it touches the capture loop and this does not.

pydivert.WinDivert(filt) never received a set_param(), and the package contained no
get_param() at all. So WinDivert's own queue - the one ahead of this tool's - was invisible:
with QUEUE_TIME at its default a packet may be held for up to two seconds, which is latency
this tool adds while being blind to it, and no session artefact said what queue produced its
numbers.

Measured first, on the real driver

An elevated throwaway ICMP handle on the owner's machine (2026-07-28):

QPC frequency  10 MHz          QUEUE_LEN 4096   QUEUE_TIME 2000 ms   QUEUE_SIZE 4 MiB
packet age in the driver queue: 0.049 - 0.163 ms  (median ~0.08 ms, idle machine)

QUEUE_SIZE binds before QUEUE_LEN for full frames - 4 MiB / 1500 B = 2796 packets - so at a
saturated gigabit (83k pkt/s of 1500 B) a frozen capture thread has roughly 34 ms before the
driver starts discarding. And the 2000 ms is a ceiling, not a routine cost: on an idle machine the
queue adds 0.08 ms. Knowing which of those two a session was living in is exactly what part b is for.

Deliberately NOT retuned

The trade is real in both directions, and both sides are invisible today: a large QUEUE_TIME
is latency the tool hides; a small one is loss the tool cannot even count, because a packet the
driver drops never reaches us, so no counter can see it. Picking values before measuring the actual
queue delay is machinery around the wrong primitive (convention 6). Measure first.

What changed

  • _read_driver_queue() reads the three params once the handle is open, session_info() carries
    them into the repro report as session.driver_queue, and one line goes to the log at START.
  • None - not zeroes - on the simulate path. "No driver" and "a queue of nothing" are different
    claims.
  • The param numbers are spelled out rather than imported, and that is the part worth reading.
    Reaching for pydivert.consts.Param here would import a win32-only dependency: the read would
    return None on the Linux half of the CI matrix while passing on Windows - the exact bug shape
    that only shows up on the runner you did not run. A guard test compares the numbers against the
    real enum wherever pydivert is importable, so they cannot drift in silence.
  • --doctor gains a line saying where the values live and why it does not read them itself: they
    need an open handle, and opening one loads the driver - which would falsify the "windivert driver"
    check printed two lines above it, in the same report.

Verification

  • python -m pytest tests -> 703 passed, 0 failed (elevated shell). python smoke_gui.py -> OK.
  • Three new guards, three mutants, every one caught - including swapping two ABI numbers, which
    the pydivert cross-check catches with queue_time is 1 in pydivert (engine says 2).
  • No hot-path change: three calls, once per session.

Still owed: an end-to-end run on the real driver, to confirm the numbers actually reach the log
line and the repro report rather than just the unit test. Asked for separately.

🤖 Generated with Claude Code

`pydivert.WinDivert(filt)` never received a set_param(), and the package had no
get_param() at all, so the driver's own queue was invisible. With QUEUE_TIME at
its default a packet may be held for up to two seconds - latency this tool adds
while being blind to it - and nothing in a session artefact said what queue
produced its numbers.

Measured on the owner's machine before designing anything (elevated, real
driver): QUEUE_LEN=4096, QUEUE_TIME=2000, QUEUE_SIZE=4 MiB. The size limit binds
before the length one for full frames: 4 MiB / 1500 B = 2796 packets, not 4096.

Deliberately NOT retuned. The trade is real both ways and both sides are
invisible today - a large QUEUE_TIME is latency the tool hides, a small one is
loss it cannot even count, because a packet the driver drops never reaches us.
Choosing values before measuring the actual queue delay is machinery around the
wrong primitive (convention 6). The measurement is part b.

The three values are read after the handle opens, logged at START, and stored in
the repro report as session.driver_queue. None - not zeroes - on the simulate
path: "no driver" and "a queue of nothing" are different claims.

The param numbers are spelled out rather than imported from pydivert.consts,
because pydivert is win32-only: importing it here would make the read return None
on the Linux half of the CI matrix while passing on Windows. A guard test
compares them against the real enum wherever pydivert IS importable.

--doctor deliberately does not read them: they need an open handle, and opening
one loads the driver, falsifying the "windivert driver" check printed two lines
above it in the same report.

Three mutants, every one caught. No hot-path change - three calls per session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@donislawdev
donislawdev merged commit 0d4e939 into master Jul 28, 2026
8 checks passed
@donislawdev
donislawdev deleted the feat/report-the-driver-queue-parameters branch July 28, 2026 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant