Reading memory over KD

With --memory-source kd, or on a target whose memory is not on this host, every read of guest memory goes through the Windows kernel debugger transport. Every read is a request/reply round trip. Over KDCOM on an emulated UART the request alone costs about 2 ms before the target sees it: QEMU’s 16550 hands the guest one byte per main-loop iteration at the FIFO trigger level KDCOM programs, and a KD request plus its ACK is ~90 bytes host-to-guest. The reply then costs about 5 µs per byte (a 2 KiB read is ~12 ms), and KDNET has neither floor. When host memory is not available, the session is shaped to need few, short reads:

  • Virtual reads through the target are served from a cache of 512-byte lines for as long as the target stays halted. A miss fetches from its line to the end of the request, so the fields of one structure cost a single request between them while a large read still costs the same 2 KiB requests it always did (a KDNET datagram carries 1 KiB of data, and fills adapt to that after the first short reply). The cache is dropped when the target runs and whenever the debugger writes memory or installs or removes a breakpoint.

  • The host page walk (user space of a process other than the halted processor’s) reads page-table entries in the same 512-byte lines, so adjacent pages share their upper-level entries and their run of PTEs; other physical reads get no read-ahead, since they may touch a device. A process’s loader list is walked once per halt, however many of its threads are unwound.

  • Module images are never copied whole. Attach reads each module’s headers with one probe; the unwinder fetches 2 KiB blocks of .pdata/.rdata as its lookups touch them and keeps them for the session, and a stack walk reads the stack a page at a time.

  • A module’s PDB is remembered by the image identity the symbol server uses (~/.ntoseye/symbols/identities, keyed by file name, TimeDateStamp, and SizeOfImage, all of which the loader’s module list already carries). A module seen in an earlier session is identified with no reads from the target; lm shows such modules’ symbol source as cached. A remembered PDB that fails to load is forgotten and the module rediscovered from the target.

Process, kernel-module, and driver-object lists are walked only when something needs them (a listing command, a tab completion, a break context in a user-mode process) and the first walk per halt serves every later use until the target runs again. The process walk reads one span per _EPROCESS and consults the PEB only for names the kernel’s 15-byte ImageFileName may have truncated, so the prompt after attach and each stop does not wait on a full process walk.