Inspect

final class ntoseye.Inspect

System-wide reports and helpers that decode an object at an address (dbg.inspect). The results are typed records. On a user-mode minidump, which has no kernel, only version, time, triage, bugcheck, exception_record, and context_record apply; the others raise NtoseyeError.

acl(address: int | str | Process | Thread | Module | Driver | Device | Heap) → Acl

Decode an ACL and its ACEs.

The optional 1 adds the names of well-known SIDs.

alpc_message(address: int | str | Process | Thread | Module | Driver | Device | Heap) → AlpcMessage

Decode an ALPC message, a _KALPC_MESSAGE (!alpc /m).

alpc_port(address: int | str | Process | Thread | Module | Driver | Device | Heap) → AlpcPort

Decode an ALPC port (!alpc /p). The result has the port kind, owner, connection, state, and queues, and for a connection port also its connections. address is the body or the header of the port object.

alpc_process_ports(process: Process | None = None) → AlpcProcessPorts

Get the ALPC ports to which a process has handles (!alpc /lpp). The result has the connection ports that the process owns, with their connections, and the client ports through which the process is connected. process defaults to the current process.

apcs(target: Process | Thread | int | None = None) → ApcQueues

Decode the kernel and user APC queues of all threads, of a process, or of a thread (!apc): target is a Process, a Thread, or an _ETHREAD address, and all threads by default.

blackbox_bsd() → BootStatus

Show the boot status data (bootstat.dat) the crash dump recorded.

Whether the last boot succeeded and shut down, the boot IDs of the last successful and abnormal shutdowns, the power transition and power button state, and the feature configuration state, as WinDbg’s !blackboxbsd shows them.

blackbox_ntfs() → list[NtfsRecord]

Show the NTFS slow I/O and oplock break timeouts the crash dump recorded.

Each record shows its type and length; a slow I/O timeout its IRP, SCB, and waiting thread, and an oplock break timeout its reason (the NTFS routine), IRP, SCB, and the owning and breaking processes, as WinDbg’s !blackboxntfs shows them.

blackbox_pci() → list[PciRecord]

List the PCI functions pci.sys recorded in the crash dump, with their command and status.

Each function shows its bus, device, and function, its vendor and device IDs and revision, and its command and status registers with their flags as WinDbg’s !blackboxpci shows them: i (I/O), m (memory), b (bus master), v (VGA palette snoop), p (parity), s (SERR) for the command, and c (capability list), 6 (66 MHz), P (master data parity error), A (signaled target abort), S (signaled system error) for the status. WinDbg prints the command register’s value as the status value; ntoseye prints the status register’s.

blackbox_pnp() → PnpBlackbox

Show the PnP event the crash dump recorded.

The PnP activity, its time, the event, whether it was in progress, the device’s problem code, the veto, and the device instance ID, as WinDbg’s !blackboxpnp shows them.

blackbox_winlogon() → WinlogonBlackbox

Show what winlogon was doing when the crash dump was written.

bugcheck() → BugcheckAnalysis | None

Show the bugcheck code and its four parameters.

Uses the bugcheck that the stop reported. If there is none, it uses the bugcheck in nt!KiBugCheckData, as $bug_code and $bug_param1-4 do, and if there is none there, the bugcheck in the crash dump header. !analyze uses the same order.

callbacks() → list[NotifyCallback]

List the process, thread, and image notification callbacks.

class_device(address: int | str | Process | Thread | Module | Driver | Device | Heap) → ClassDeviceDetail

Read the class device whose FDO, device extension, or private data is at address, with its requests in flight and its error log (!storagekd.storclass <address>).

class_devices() → ClassDevices

List the storage class devices on classpnp!AllFdosList (!storagekd.storclass).

context_record(address: int | str | Process | Thread | Module | Driver | Device | Heap) → Frame

Decode a CONTEXT record and return its register set as a Frame (.cxr).

control_area(address: int | str | Process | Thread | Module | Driver | Device | Heap) → ControlAreaDetail

Decode the control area of a section, with its segment and subsections.

Shows the section, PFN, mapped-view, and user reference counts, the _MMSECTION_FLAGS that are set, and the backing file object and its name. It also shows the segment (PTE count, size, committed pages, and prototype PTEs) and, for each subsection, the base PTE, the PTE count, the starting sector, the sector count, and the protection. The command reads up to 1,024 subsections along NextSubsection, starting from the subsection after the control area, and stops at a subsection that points to a different control area. To find a control area, use the SectionObjectPointer of a file object or the Subsection of a VAD.

decode_bugcheck(code: int, parameters: Sequence[int] = ...) → BugcheckDecoding

Decode a bugcheck code and its parameters as if the target had stopped with it (!analyze -show), and for a DRIVER_POWER_STATE_FAILURE (0x9F), read what its parameters point to. The target must be a kernel.

device(address: int | str | Process | Thread | Module | Driver | Device | Heap) → Device

Return a handle for the _DEVICE_OBJECT at address (!devobj).

device_power(device: int | str | Process | Thread | Module | Driver | Device | Heap) → DevicePower

Show the power state of a device object.

Shows the device’s driver and power flags, the system and device power states its driver last set with PoSetPowerState (from the PowerFlags of its DEVOBJ_EXTENSION), the idle detection the driver registered, if any, the device state PnP records in the device node of the stack, whether the driver registered with the power framework and the device power IRP that it has outstanding, and the power IRPs on nt!PopIrpList sent to any device of the stack.

device_stack(device_or_node: int | str | Process | Thread | Module | Driver | Device | Heap) → DeviceStackDetail

Show the device stack for a DEVICE_OBJECT or device node.

The argument can be any DEVICE_OBJECT in the stack, or the DEVICE_NODE of the stack. The command shows the stack from the top filter down to the PDO, with the !DevObj, !DrvObj, !DevExt, and object name of each device, and a > marks the argument. After the stack, the command shows the device node of the PDO.

devnode(node: int | str | Process | Thread | Module | Driver | Device | Heap | None = None, *, recurse: bool | None = None) → DevNodeDetail

Show a PnP device node and, optionally, its subtree.

Shows the instance path, the service, the state and state history, the flags, the problem code, and the pending IRP of a device node. If you do not give a node, or give 0, the command shows the root device node. Use -r, or a trailing 1 as in WinDbg, to walk the subtree of the node, with one node on each line.

dpcs() → DpcQueues

List the deferred procedure calls that are queued on each processor.

The command walks the two _KPRCB DpcData queues. Each walk detects cycles and has an entry limit.

etw_buffers(logger: int | str) → EtwLoggerBuffers

List the trace buffers on the GlobalList of an ETW trace session (!wmitrace.strdump logger).

etw_crash_events(logger: int | str, *, count: int | None = None) → EtwEventDump

Decode the events of an ETW trace session that Windows saved in a crash dump, oldest first (!wmitrace.logdump in a minidump). logger is the logger ID or the session name, and count keeps only the most recent events.

etw_crash_save(logger: int | str, path: str) → EtwLogSave

Save the buffers of an ETW trace session that Windows saved in a crash dump as an .etl file at path on the host (!wmitrace.logsave in a minidump). logger is the logger ID or the session name.

etw_crash_session(logger: int | str) → EtwDumpLogger

Show one ETW trace session that Windows saved in a crash dump, with its buffers (!wmitrace.logger and !wmitrace.strdump logger in a minidump). logger is the logger ID or the session name.

etw_crash_sessions() → EtwDumpData

List the ETW trace sessions that Windows saved as it wrote a crash dump, and their buffers (!wmitrace.strdump in a minidump): the sessions started with EVENT_TRACE_ADDTO_TRIAGE_DUMP, such as EventLog-System. A minidump has no other copy of them; in a kernel or full dump, etw_loggers() reads every session from memory.

etw_events(logger: int | str, *, count: int | None = None) → EtwEventDump

Decode the events that are still in the buffers of an ETW trace session, oldest first (!wmitrace.logdump). count keeps only the most recent events. For a WPP message, message_format.text is the message rendered from the TMF that a loaded PDB declares, and the raw record.payload is always kept.

etw_logger(logger: int | str) → EtwLogger

Decode the _WMI_LOGGER_CONTEXT of one ETW trace session (!wmitrace.logger). logger is the logger ID, the context address, or the session name.

etw_loggers() → EtwLoggerTable

List the active ETW trace sessions (!wmitrace.strdump).

etw_save(logger: int | str, path: str) → EtwLogSave

Save the in-memory buffers of an ETW trace session as an .etl file at path on the host (!wmitrace.logsave), which ETW consumers such as tracerpt open. logger is the logger ID, the context address, or the session name.

exception_record(address: int | str | Process | Thread | Module | Driver | Device | Heap | None = None) → ExceptionRecord

Show an EXCEPTION_RECORD64.

file_cache() → FileCacheDetail

Show the views that the cache manager has mapped, for each file.

Walks the VACB arrays (CcVacbArrays) to find the views in use, and groups the views by shared cache map. For each file, the command shows its name, the mapped 256 KB views, the pages of these views that are present in memory, the dirty pages, the file size, and the open count, with the file that has the most present data first. The summary shows the VACBs in use and free, and the bytes mapped and present. The command lists up to 1,024 files, and you can interrupt the walk.

file_object(address: int | str | Process | Thread | Module | Driver | Device | Heap) → FileObject

Decode a FILE_OBJECT and its relations to its device and name.

findstack(symbol: str, *, level: int = 1) → FindStack

List the threads with a stack frame that matches a symbol or module.

The command walks the stack of each Windows thread, up to 64 frames, as !stacks 2 does. module!name matches frames in that module whose function starts with name, as in WinDbg, so nt!KeWait matches KeWaitForSingleObject and KeWaitForMultipleObjects, and nt! matches all frames in nt. A bare word matches a module by name or a function by prefix. With * or ?, the module and function are globs (nt!*Wait*). Matches ignore case. Display level 0 lists the matching threads and the number of frames that match, level 1 (the default) adds the matching frames, and level 2 shows the whole stack and marks the matching frames with *. The command cannot search a thread whose stack does not walk, such as a thread that runs on a processor while the target runs, and lists these threads after the matches.

flt_filters() → FltFilters

List the registered minifilters and their instances.

Walks each filter manager frame in fltmgr!FltGlobals and its registered filters. For each filter, the command shows its altitude and its attached instances, with their names and altitudes. The command uses the types from the PDB of fltmgr.

flt_instances(filter: int | str | None = None) → FltInstances

List minifilter instances with their filter and volume.

Lists each instance of each registered filter. To list the instances of one filter, give its name (WdFilter) or its _FLT_FILTER address. For each instance, the command shows its name, altitude, filter, and the volume that it is attached to.

flt_volumes() → FltVolumes

List the volumes that the filter manager is attached to, with their instances.

Walks the attached volumes of each filter manager frame. For each volume, the command shows the device name, the file-system type, and the attached instances with their names and altitudes.

global_flags() → GlobalFlags

Decode nt!NtGlobalFlag and the _PEB.NtGlobalFlag of the current process (!gflag): the value of each and the bits by their GFlags names.

hang() → HangReport

Find what a hung system is doing (!analyze -hang): the thread on each processor with its stack, and the waiting threads with the most pending IRPs. The target must be a kernel.

interrupt_controllers() → HalControllers

Show the I/O APIC and PIC lines as the HAL set them up.

Walks the HAL’s registered interrupt controllers (nt!HalpRegisteredInterruptControllers) and shows each one, an I/O APIC or a legacy PIC, with its unit ID, ACPI resource ID, line range, and any problem the HAL recorded, then each line the HAL set up, from its own record of the line (_INTERRUPT_LINE_STATE): the line and its global system interrupt, the vector, the IRQL, edge or level trigger, polarity, and the processor target. This is what the HAL programmed into the redirection entries, read from kernel memory, so it works on every backend and in a kernel dump; it does not read the I/O APIC’s registers.

ipi(processor: int | None = None) → IpiState

Show the interprocessor interrupt (IPI) state of one processor or of all processors.

For each processor, shows the IPI fields that its _KPRCB has on this Windows build. IpiFrozen is the freeze state that the debugger and KeFreezeExecution control (Running, Frozen, Freeze owner, …). TargetCount and PacketBarrier apply to the processor as a sender and show the targets that did not finish its packet yet. The command also shows SelfIpiRequestSummary and IpiFrame, the trap frame of the IPI that the processor services now. Windows 10 and later builds have one mailbox for each sender, and on these builds the command also shows the requests that are in the queue of the processor and are not taken yet. For each request, it shows the sender, the request type, and for a packet, the worker routine and parameters. The request type (packet, TB flush, or cache flush) comes from RequestSummary. For the processor as a sender, the command also shows the processors whose queues still hold its request.

irp(address: int | str | Process | Thread | Module | Driver | Device | Heap) → IrpInfo

Show an IRP and its I/O stack locations.

Shows the IRP as WinDbg does: which stack location is current, the MDL, system buffer and thread, then each location from the lowest driver’s up, the current one marked with > and a run of locations the IRP has not reached as one unused line. A location shows its major and minor function, flags and control, device and file, its completion routine and context with when the routine runs (Success, Error, Cancel) and whether its driver returned pending, the device’s driver, and four arguments. The driver of the location below it in the list set that completion routine. With a second argument, such as 1, it first shows the IRP’s status, requestor mode, user event and user buffer.

irp_find(*, pool_type: Literal['nonpaged', 'paged'] | None = None, restart: int | str | Process | Thread | Module | Driver | Device | Heap | None = None, criteria: Literal['arg', 'device', 'fileobject', 'mdlprocess', 'thread', 'userevent'] | None = None, value: int = 0) → IrpFindDetail

Scan pool for the IRPs that IoAllocateIrp allocates.

Scans the pool region for 16-byte-aligned _POOL_HEADERs with the tag Irp. On Windows 10 1803 and later, the region comes from MiState.Vs.SystemVaRegions, and otherwise it is MmNonPagedPoolStart/End or MmPagedPoolStart/End. The scan walks the page tables and reads only mapped pages, and big allocations come from PoolBigPageTable. The command counts a block only if its body is a live _IRP. In a live _IRP, Type is 6 (IoFreeIrp clears it), and Size is the header plus whole stack locations and at least IoSizeOfIrp(StackCount). A lookaside IRP keeps its larger packet size. CurrentLocation is at most StackCount + 1. For each IRP, the command shows its thread (Tail.Overlay.Thread), the process of its MDL, and the major function, minor function, device, and owning driver of the current stack location. If CurrentLocation is more than StackCount, the IRP shows as complete. -v also shows the pool header, I/O status, PendingReturned, UserEvent, UserBuffer, and OriginalFileObject, and also the file object and completion routine of the current location. pool-type is 0 (nonpaged, the default) or 1 (paged). The command does not accept 2 (special) or 4 (session), because these pool types have no region of their own on these builds. restart-address continues a scan from that page. The criteria are the same as in WinDbg: arg (Argument1-4 of a stack location), device (DeviceObject of a stack location), fileobject (Tail.Overlay.OriginalFileObject), mdlprocess (MdlAddress->Process), thread (Tail.Overlay.Thread), and userevent (UserEvent). Use 0 as the restart address to scan the whole pool. The scan stops after 4,096 IRPs, or when you press Ctrl-C, and then shows the command that continues the scan, with the criteria. The scan checks the big-pool allocations last. If the scan gets to the limit in the big-pool allocations, the command shows a message. A restart cannot get to the remaining big-pool allocations. A driver can build IRPs in its own allocations (IoInitializeIrp). These IRPs have the tag of that driver, so the scan does not find them. Use irps to list the IRPs in thread IrpLists.

irps(*, filter: str | None = None) → list[IrpHit]

Find the in-flight IRPs in thread IrpLists and device CurrentIrp fields.

job(address: int | str | Process | Thread | Module | Driver | Device | Heap | None = None) → Job

Show a job object and the processes in it.

Shows a job object. The address can be a job, or a process or thread, for which the command shows its job. If you do not give an address, or give 0, the command shows the job of the current process. Flag 1 (the default) shows the accounting, limits, and flags of the job, and also the child jobs, the parent and root job, and the silo state. Flag 2 lists the processes assigned to the job (from ProcessListHead).

lookaside(address: int | str | Process | Thread | Module | Driver | Device | Heap) → LookasideDetail

Decode one GENERAL_LOOKASIDE (!lookaside address).

lookasides() → LookasideListsDetail

List the exported nonpaged and paged GENERAL_LOOKASIDE lists (!lookaside), detecting cycles.

mdl(address: int | str | Process | Thread | Module | Driver | Device | Heap, *, pfn_count: int | None = None) → MdlDetail

Decode a memory descriptor list and the page frames it describes.

The command shows the MDL header and the PFN array that follows it. The header fields are Next, Size, MdlFlags by MDL* name, Process, MappedSystemVa, StartVa, ByteCount, and ByteOffset. By default, the PFN count is the number of pages that ByteCount spans from ByteOffset, and a pfn-count overrides this value. In both cases, the count cannot be more than the slots that Size leaves after the header. If the Size, ByteOffset, or span of the header cannot describe an MDL, the command gives an error. The command does not follow Next, so for a chained MDL, run !mdl on the Next address.

memusage(*, process_limit: int = 64) → SystemMemorySummary

Show the system memory-use counters and the counters of a limited number of processes.

The command uses validated memory-manager globals, KDBG fields, or recognized public getter code, and EPROCESS.Vm counters. It does not scan physical memory, and it does not walk all VADs.

ndis_filter(address: int | str | Process | Thread | Module | Driver | Device | Heap) → NdisFilterDetail

Read the NDIS filter module at address (!ndiskd.filter <address>).

ndis_filter_driver(address: int | str | Process | Thread | Module | Driver | Device | Heap) → NdisFilterDriverDetail

Read the NDIS filter driver block at address and its filter modules (!ndiskd.filterdriver <address>).

ndis_filter_drivers() → NdisFilterDriverList

List the NDIS filter drivers (!ndiskd.filterdriver).

ndis_filters() → NdisFilterList

List the NDIS filter modules of every miniport (!ndiskd.filter).

ndis_minidriver(address: int | str | Process | Thread | Module | Driver | Device | Heap) → NdisMinidriver

Read the NDIS miniport driver block at address and its miniports (!ndiskd.minidriver <address>).

ndis_minidrivers() → NdisDriverList

List the NDIS miniport drivers (!ndiskd.minidriver).

ndis_miniport(address: int | str | Process | Thread | Module | Driver | Device | Heap) → NdisMiniportDetail

Read the NDIS miniport at address with its driver, filter stack, bindings, and pending OID request (!ndiskd.miniport).

ndis_miniports() → NdisMiniportList

List the NDIS miniports (network adapters).

Walks ndis!ndisMiniportList and shows one line per miniport: the address of its _NDIS_MINIPORT_BLOCK, which !ndiskd.miniport takes, the service of its miniport driver, the miniport state (Running, Pausing, Paused, Restarting, Initializing, Halted), the media connect state with the transmit link speed (and the receive speed when it differs), the device power state, the interface index, and the friendly name. Virtual miniports, such as Hyper-V’s adapters, are on the list too. The command needs the PDB for ndis.sys.

ndis_nbl(address: int | str | Process | Thread | Module | Driver | Device | Heap, *, data: bool = False) → NdisNbl

Decode the NET_BUFFER_LIST at address and its NET_BUFFERs (!ndiskd.nbl). With data, each NET_BUFFER’s data holds up to 64 KiB of its frame, read through its MDLs’ PFNs, or data_error why it does not read (!ndiskd.nbl -data).

ndis_nbl_chain(address: int | str | Process | Thread | Module | Driver | Device | Heap, *, data: bool = False) → NdisNblChain

Decode the NET_BUFFER_LISTs on the Next chain from address (!ndiskd.nbl -chain). With data, each NET_BUFFER’s data holds up to 64 KiB of its frame, or data_error why it does not read (!ndiskd.nbl -chain -data).

ndis_oids() → NdisPendingOids

Show the OID requests that miniports and filters have not completed.

Reads the PendingOidRequest of every miniport on ndis!ndisMiniportList and every filter module on ndis!ndisGlobalFilterList, and shows each request with its owner, type, OID by its ntddndis.h name, and buffer. NDIS passes a miniport or filter one OID request at a time, apart from direct OID requests, so a request that stays here holds up the ones behind it. The command needs the PDB for ndis.sys.

ndis_protocol(address: int | str | Process | Thread | Module | Driver | Device | Heap) → NdisProtocolDetail

Read the NDIS protocol block at address and its bindings (!ndiskd.protocol <address>).

ndis_protocols() → NdisProtocolList

List the NDIS protocol drivers (!ndiskd.protocol).

object(object: int | str | Process | Thread | Module | Driver | Device | Heap) → ObjectDetail

Show an executive object header and body, and list the entries of a directory.

A path that starts with \ is the name of an object in the object namespace (!object \, !object \Driver\ACPI). The command looks up the path from the root directory and ignores case. It does not follow symbolic links in the path. For a directory, the command lists the entries and their types.

object_security(object: int | str | Process | Thread | Module | Driver | Device | Heap) → ObjectSecurity

Decode the security descriptor that an object header references.

The command masks the low four bits of the OBJECT_HEADER.SecurityDescriptor fast reference before it decodes the descriptor.

pci(bus: int = 0, device: int | None = None, function: int | None = None, *, last_bus: int | None = None) → PciScan

Read and decode PCI configuration space (!pci) for the functions on bus (through last_bus), or for one device and function. It reads all 4 KiB of each function, with the extended capabilities, as config. This needs a halted target and a backend that can get to configuration space (kd/kdnet, or gdb on QEMU).

pci_tree() → PciTree

Show the PCI bus hierarchy that pci.sys tracks, with the functions on each bus.

Reads the pci.sys segment list (pci!PciSegmentList), the root buses of each segment, and the functions and child buses of each bus. The data comes from the extensions of the driver (pci!_PCI_BUS and pci!_PCI_DEVICE), which exist on Windows 8 and later. Because the command reads only guest memory, it works on all backends. A bus line shows the bus number and the FDO extension. A function line shows the device and function number, the vendor and device ID, the PDO extension (devext), the PDO (devstack, for !devstack), and the class code and name. The secondary bus of a bridge follows the bridge and is indented. The JSON output also has the PnP instance path of each function.

pfn(value: int, *, physical_address: bool = False) → PfnDetail

Decode an _MMPFN entry.

A plain value is a page-frame number. Use -a to read the value as a physical address.

pnp_triage() → PnpTriageDetail

Show the PnP device nodes that have problems, pending IRPs, or incomplete starts.

pool(address: int | str | Process | Thread | Module | Driver | Device | Heap) → PoolPageDetail

Show the pool page that contains an address.

pool_find(tag: str, *, pool_type: Literal['nonpaged', 'paged'] | None = None) → PoolFindDetail

Find pool blocks with a matching tag.

The command reads the mapped pages of the pool ranges and the big-page table. On Windows 10 1803 and later, the pool ranges are the fixed regions in MiState.Vs.SystemVaRegions. On earlier builds, they come from MmNonPagedPoolStart and related symbols. The optional type selects nonpaged (0) or paged (1) pool. The scan stops after 1,024 matches, and you can interrupt it.

pool_usage(tag: str | None = None, *, sort: Literal['tag', 'nonpaged', 'paged'] = ...) → PoolUsageDetail

Show pool tracker usage by tag.

The command adds up the usage from the tag table of each processor, which counts big allocations too; the big-page table is read only when those tables cannot be. Tags with no allocation outstanding are left out. The flags follow WinDbg: bit 1 (2) sorts by nonpaged bytes, bit 2 (4) sorts by paged bytes, and bit 0 (1) shows the alloc/free columns. The tag match is case-sensitive and supports * and ?.

pool_validate(address: int | str | Process | Thread | Module | Driver | Device | Heap) → PoolValidationDetail

Check the block headers of the pool page that contains an address.

The command reports the page as VALID or INVALID, with the first inconsistency. The blocks of a classic pool page must make a chain from the start of the page to its end, in which the PreviousSize of each block matches the BlockSize of the block before it. Segment-heap pages (Windows 10 1903 and later) have no PreviousSize chain, so on these pages the inconsistency is a block whose BlockSize runs over the header of another block. The PoolType of a classic block must also identify the pool (paged or nonpaged) that holds the page. A level of 1 or more also lists all block headers. The command does not do the single-bit-error scan of WinDbg.

power_action() → PowerActionDetail

Show the system power action and the power IRPs in flight.

Shows nt!PopAction: the power action in progress, its state and flags, and the system state being entered, with the device notification pass when one runs. Then lists every power IRP the power manager has allocated, from nt!PopIrpList: its PDO, the device it was sent to, its minor function and the power state it asks for, where it is pending now (the driver of its current stack location), and how long the drivers have had it when the power IRP watchdog runs. Last come the threads that pass power IRPs to drivers, from nt!PopIrpThreadList, with the IRP and device each is working on.

queued_locks() → QueuedLocks

Show which processors own or wait for each numbered queued spinlock.

Reads the _KPRCB.LockQueue entries of each processor, which hold one _KSPIN_LOCK_QUEUE for each _KSPIN_LOCK_QUEUE_NUMBER lock. The table has the same layout as in WinDbg, with one row for each lock and one column for each processor. O marks the owner, and 1-n marks the wait order, which the command finds by following the queue links from the owner. C marks an entry whose owner or wait bits do not agree with the links. The table shows only the numbered locks (KeAcquireQueuedSpinLock). In-stack queued spinlocks are on the stacks of the callers that acquire them.

rcdr_drivers() → RcdrDriverList

List the drivers the WPP recorder keeps logs for (!rcdrkd.rcdrloglist).

rcdr_log(driver: str, *, log: int | str | Process | Thread | Module | Driver | Device | Heap | None = None) → RcdrLogDump

Show a driver’s WPP recorder log records, oldest first.

Walks each of the driver’s recorder logs, both the normal and the error partition, back from the newest record, and shows the records of all of them merged in the order the driver logged them (their sequence numbers come from one counter per driver). -a shows only the log whose _WPP_AUTOLOG_HEADER you give, as !rcdrkd.rcdrloglist lists it. Each record shows its sequence number, its time when the log keeps timestamps, the log it is in, and its message formatted from the trace message format (TMF) annotations in a loaded PDB, or else the message GUID and number and the argument bytes. Microsoft’s public PDBs carry no TMF annotations; add your driver’s private PDB to the symbol path for its messages. The walk is !wdfkd.wdflogdump’s: the records share KMDF’s IFR format.

rcdr_logs(driver: str) → RcdrLogs

List the WPP recorder logs of driver, by its name with or without .sys or its recorder context (!rcdrkd.rcdrloglist <driver>).

read_tagged(tag: str) → bytes

Read the data of the crash dump’s block tagged tag (.enumtag), a GUID with or without braces, such as the one a driver passes to KeRegisterBugCheckReasonCallback for its secondary dump data. target.dump.tagged_blocks lists the blocks.

ready(processor: int | None = None) → ReadyQueues

List the dispatcher ready queues of all processors or of one processor.

The command reads DispatcherReadyListHead from each _KPRCB, or ReadyListHead on newer builds. Each queue walk has an entry limit and, like dt -l, reports null links, cycles, unreadable links, and when it reaches its entry limit.

registry_cell(hive: int | str | Process | Thread | Module | Driver | Device | Heap, cell: int) → CellLocation

Find where cell index cell of the hive at hive is, through the hive’s cell map (!reg cellindex).

registry_hives() → HiveList

List the registry hives loaded in kernel memory, with where each is mounted and the file it was loaded from (!reg hivelist).

registry_key(path: str) → RegKey

Read the registry key at path, a full path such as HKLM\SYSTEM\CurrentControlSet\Services\Tcpip or \REGISTRY\MACHINE\..., with its subkeys and its values, each value’s data typed by its type (!reg querykey).

registry_key_at(hive: int | str | Process | Thread | Module | Driver | Device | Heap, node: int | str | Process | Thread | Module | Driver | Device | Heap) → RegKey

Read the registry key whose key node (_CM_KEY_NODE) is at node in the hive at hive (!reg keyinfo).

registry_value(path: str, name: str) → RegValue

Read the value name of the registry key at path; "" names the key’s default value. Its data is a str, a list[str], an int, or bytes, by the value’s type (!reg querykey <path> -v <name>).

resource(address: int | str | Process | Thread | Module | Driver | Device | Heap) → ExecutiveResource

Decode an executive resource (!locks address).

resources(*, limit: int = 256) → ResourceList

List the entries of the symbol-backed executive-resource list (!locks), at most limit of them.

running(*, include_idle: bool = False, include_stacks: bool = False) → RunningProcessors

Show the thread that runs on each processor.

-i includes idle threads, and -t adds a short kernel stack, with a frame limit, for each processor.

security_descriptor(address: int | str | Process | Thread | Module | Driver | Device | Heap) → SecurityDescriptor

Decode a SECURITY_DESCRIPTOR and its ACLs.

Decodes a self-relative or absolute security descriptor, and decodes each SID and each ACE separately. The optional 1 adds the names of well-known SIDs.

session_processes(session: int | None = None, *, image: str | None = None) → SessionProcesses

List the processes in a session.

The session is a signed decimal number. -1 and -2 select the current session, and -4 selects all sessions. If you do not give a session, the command uses the session of the attached process, if it is known. Flags default to 0 (brief output), and any non-zero value selects detailed output. The optional image argument is a glob that ignores case.

sessions(session: int | None = None) → Sessions

List the sessions and the processes in each session.

The command reads session IDs from _EPROCESS.Session and _MM_SESSION_SPACE, or from the primary token if session space is opaque. Use -s -1 for the current session. The process walk stops at 4096 entries.

set_global_flags(change: str) → GlobalFlags

Change nt!NtGlobalFlag and decode the flags as they now are (!gflag +ust). change is + or - and a value or a flag’s three-letter abbreviation, which sets or clears those bits, or a value without a sign, which replaces nt!NtGlobalFlag. It is one 4-byte write to nt!NtGlobalFlag; the PEB copy does not change. The result’s change has the value before and after.

sid(address: int | str | Process | Thread | Module | Driver | Device | Heap) → Sid

Decode a SID in guest memory.

smbios() → SmbiosTable

Read the SMBIOS table the kernel found at boot (nt!WmipSMBiosTablePhysicalAddress) from physical memory, with every structure it holds (!sysinfo smbios; !sysinfo machineid shows the identity strings of the same table).

srb(address: int | str | Process | Thread | Module | Driver | Device | Heap) → Srb

Decode an SRB: its function, status, command, address, data, and sense.

Decodes a STORAGE_REQUEST_BLOCK (an extended SRB, which StorPort, classpnp, and miniports use on Windows 8 and later) or a legacy SCSI_REQUEST_BLOCK with storport’s public PDB: the SRB function, the SRB status with QUEUE_FROZEN and AUTOSENSE_VALID, the SCSI status, the command its CDB holds with its LBA and block count, the port, path, target, and LUN, the data buffer and length, the IRP (OriginalRequest), the flags by SRB_FLAGS_ name, the timeout, tag, and priority, the sense data when AUTOSENSE_VALID says it is valid, the class, port, and miniport contexts, and an extended SRB’s extended data blocks. An address whose Function and Signature, or Length, are not an SRB’s is refused.

ssdt() → list[SsdtTable]

Show the SSDT and the shadow SSDT.

stacks(*, level: int = 0, filter: str | None = None) → ThreadStacks

Show the state, wait reason, and top stack symbol of each thread.

Level 0 shows one frame. Levels 1 and 2 add the full stacks, with a frame limit. The optional filter matches process or stack symbols.

storport_adapter(address: int | str | Process | Thread | Module | Driver | Device | Heap) → StorAdapter

Read the StorPort adapter whose extension or FDO is at address (!storagekd.storadapter <address>).

storport_adapters() → StorDrivers

List StorPort’s drivers, their adapters, and their units (!storagekd.storadapter).

storport_log(address: int | str | Process | Thread | Module | Driver | Device | Heap) → StorLog

Show a StorPort adapter’s internal log of requests, pauses, and resumes.

Give the address of an adapter extension or of its FDO, as !storagekd.storadapter lists them. StorPort keeps a ring of its last 256 events for each adapter (RaidLogList): requests it builds, starts, and sees completed, pauses and resumes of the adapter and its units, busy and ready notifications, timeouts, resets, and PnP and power IRPs. Each entry shows its number, its time (UTC), the event, and for a request its IRP, SRB, command, and SRB status, or else the four parameters StorPort logged, a code address by symbol. Without a range, the command shows the last 50 entries; shows entries from that number, that range, and L how many.

storport_unit(address: int | str | Process | Thread | Module | Driver | Device | Heap) → StorUnit

Show a StorPort logical unit, its queue, and the requests the miniport holds.

Give the address of a unit extension (_RAID_UNIT_EXTENSION), as !storagekd.storadapter lists them, or of the unit’s PDO. Shows the unit’s bus/target/LUN, the inquiry vendor, product and revision, the adapter, the PnP and power state, the flags, the miniport’s per-unit extension (StorPortGetLogicalUnit), and its device queue: the depth, whether it is frozen or locked, the pause and busy counts, and the requests waiting in storport. Then it lists each request that storport handed to the miniport and has not seen completed, from the unit’s per-processor pending queues, with its _EXTENDED_REQUEST_BLOCK, IRP and SRB; use !irp on the IRP. It lists at most 256 requests and says why a list stopped early.

system_ptes(*, free_runs: bool = False) → SystemPtesDetail

Show system PTE usage from the memory manager’s bitmap allocators.

Windows 10 and later allocate system PTEs from _MI_SYSTEM_PTE_TYPE bitmap allocators in MiState. These allocators are Vs.SystemPteInfo (the SystemPtes region) and the system-view, non-cached-mapping, and kernel-stack allocators of SystemPtes. For each allocator, the command shows the VA range that it serves, TotalSystemPtes, TotalFreeSystemPtes, the PTEs in use, and PteFailures, and from the bitmap, the number of free blocks and the largest free block. The command reads all of the bitmap, up to 2^27 bits, a limit that only a corrupt SizeOfBitMap exceeds. If the free count from the bitmap differs from the counter, the target made an allocation between the reads or a processor holds PTEs in the allocator’s cache; the free count comes from the bitmap. Where the counters cannot be true of the bitmap (a total beyond it, a free count below zero or above the total, as on Windows 11 22631 ARM64), the allocator’s total and use are shown as unknown and left out of the summary, and !vm shows no system-PTE totals. All counts are in PTEs, and each bit of the system-view bitmap covers 16 PTEs. The flags follow WinDbg. 0x1 lists each free block with its first PTE, the address that it maps, and its length, up to 256 free blocks for each allocator. 0x4 (PTEs that map locked pages) needs the TrackPtes tracking of the kernel, and shows which allocators track but does not list the tracked mappings. The command ignores 0x2, 0x8, and 0x10, which select Windows 2000/XP/Vista lists that these allocators replaced. On a build without these allocators, the command gives an error.

time() → TargetTime

Show the target UTC time and the system uptime.

timer(address: int | str | Process | Thread | Module | Driver | Device | Heap) → KernelTimer

Decode a _KTIMER and its DPC (!timer address).

timers() → TimerTable

Read the kernel timer-table entries, up to a limit, and their DPCs (!timer).

trap_frame(address: int | str | Process | Thread | Module | Driver | Device | Heap) → KtrapFrame

Decode a _KTRAP_FRAME at address (.trap).

triage() → TriageReport

Make the structured one-shot crash and debug report (!analyze).

uniqstack(process: Process | None = None) → UniqStacks

Group threads by identical call stacks (!uniqstack), using all threads unless process limits it to the threads of one process.

verifier() → Verifier

Get the Driver Verifier configuration and statistics (!verifier).

version() → TargetVersion

Show version information for the target, kernel, symbols, processor, and debugger.

virtio() → list[VirtioFunction]

List the virtio PCI functions (!virtio), with the state of each queue where the driver’s private PDB types it. A queue’s progress counters only grow, so two calls show what moved in between.

virtio_driver(address: int | str | Process | Thread | Module | Driver | Device | Heap, *, module: str | None = None) → VirtioDriver

Read the virtio_device at address as its virtio-win driver sees it, with its queues, named when its PCI function is found (!virtio <address> [module]). module names the driver whose PDB types it, by default the one that owns it.

vm(*, include_processes: bool = True) → VmDetail

Show virtual-memory statistics.

The command shows system memory, pool, PTE, page-file, and process usage. The flags follow WinDbg: bit 0 omits the per-process rows, and the command accepts bits 1, 2, and 3 but ignores them. If the command cannot read a counter, it still shows the other counters.

vpb(address: int | str | Process | Thread | Module | Driver | Device | Heap) → VpbDetail

Decode a volume parameter block.

Shows the VPB_* flags, the volume device of the mounted file system, and the storage device below it, with their object names. It also shows the reference count, the serial number, and the volume label. The Vpb field of a device object points to the VPB. If the Type at the address is not IO_TYPE_VPB, the command shows an error.

vring(address: int | str | Process | Thread | Module | Driver | Device | Heap, *, module: str | None = None, kind: Literal['blk', 'scsi', 'scsi_control', 'scsi_event', 'net', 'net_control', 'gpu', 'vsock'] | None = None) → VirtioRing

Read the virtio-win virtqueue at address (!vring): its ring, the buffers the device holds or returned, and the request each carries. module names the driver whose PDB types it, by default the one its add_buf routine is in. kind says what its buffers hold, by default what its device’s type and the queue’s index say.

vring_raw(size: int, desc: int | str | Process | Thread | Module | Driver | Device | Heap, avail: int | str | Process | Thread | Module | Driver | Device | Heap, used: int | str | Process | Thread | Module | Driver | Device | Heap, *, kind: Literal['blk', 'scsi', 'scsi_control', 'scsi_event', 'net', 'net_control', 'gpu', 'vsock'] | None = None) → VirtioRing

Read a split ring from its size and the kernel addresses of its descriptor table, avail ring and used ring (!vring /r), for a driver without a PDB. kind says what its buffers hold, for decoding their requests (/t).

vtop(directory_base: int, address: int | str | Process | Thread | Module | Driver | Device | Heap) → VtopDetail

Translate a virtual address with an explicit directory base.

The command shows each page-table level and the final physical address. A directory base of zero uses the current context, and for a large page, the walk stops at its leaf level.

wdf_crash_drivers() → list[WdfDumpDriver]

List the KMDF client drivers that KMDF recorded in a crash dump, with their version and globals (!wdfkd.wdfcrashdump loader).

wdf_crash_log() → WdfLogDump

Get the In-Flight Recorder log of the one KMDF client driver that KMDF copied into a crash dump (!wdfkd.wdfcrashdump): the driver that the bugcheck parameters point to, or one set to keep its log in minidumps, or else the last one that ran on the processor that crashed. A minidump has no other copy of it.

wdf_device(handle: int) → WdfDeviceDetail

Show a WDFDEVICE and its device objects, state machines, queues, and I/O targets.

Shows the kind of the device (FDO, filter, PDO, or control), its WDM device object, the device object that it is attached to, and the PDO of the stack. It also shows the current states of the PnP, power, and power policy state machines (_WDF_DEVICE_PNP_STATE, _WDF_DEVICE_POWER_STATE, _WDF_DEVICE_POWER_POLICY_STATE), and the device and system power states. For a PDO, it shows the parent device, and for an FDO, the child lists and the default and self I/O targets. For each queue of the I/O package, it shows the dispatch type, the power management, and the counts of pending and driver-owned requests, and it marks the default queue. For each I/O target of the device (m_IoTargetsList), it shows the WDFIOTARGET handle, the kind of target, its state, and the device object that it sends to, and it marks the default target. Pass a WDFQUEUE to !wdfkd.wdfqueue and a WDFIOTARGET to !wdfkd.wdfiotarget.

wdf_driver_info(driver: str) → WdfDriverInfo

Show a KMDF client driver and its WDF devices.

Give the driver name as !wdfkd.wdfldr lists it. The name is not case-sensitive, and .sys is optional. The command shows the DRIVER_OBJECT, the FxDriver, the WDFDRIVER handle, the _FX_DRIVER_GLOBALS, the KMDF version, the registry path, and the image. Then it shows each device object on the DeviceObject/NextDevice chain of the driver object, with the related WDFDEVICE and its kind and PnP state. A device object links to its WDFDEVICE through its DeviceExtension, which KMDF sets to point to the first context of the device. The command checks the context header, the type and owner of the object, and the device object of the FxDevice, and if a check fails, it lists the device object with the reason.

wdf_handle(handle: int) → WdfObject

Decode a WDF handle and show its object.

A handle is the address of its object, XORed with ~7. An offset handle has bit 0 set and points to a WDFOBJECT_OFFSET value inside the object, which the command subtracts from the address that the handle points to. The command shows the FxObject, its type (FX_OBJECT_TYPES), size, reference count, state (FxObjectState), flags, owning driver, and parent, and each context with its type name, size, and address. Then it names the command that shows the object in full: !wdfkd.wdfdevice, !wdfkd.wdfqueue, !wdfkd.wdfrequest, or !wdfkd.wdfiotarget for those types, !wdfkd.wdfdriverinfo for a WDFDRIVER, and !wdfkd.wdfobject, which adds the children of the object, for any other type. It shows an error if the value does not decode to a kernel address, or if the type, state, size, context header, or owning driver of the object is not valid.

wdf_io_target(handle: int) → WdfIoTargetDetail

Show a WDFIOTARGET, the device object it sends to, and the requests sent to it.

Shows the kind of the target: local (in the device’s own stack, as the default target is), remote (a target that the driver opened with WdfIoTargetOpen), self, or a USB device or USB pipe, and whether it is the default I/O target of its device. It shows the state (_WDF_IO_TARGET_STATE), the open state of a remote target, the WDFDEVICE that the target belongs to, the device object that the target sends requests to and the driver of that device object, the PDO of that stack, the file object, the I/O count, and the stack size. Then it lists the requests on three lists: the requests sent to the target and not completed (m_SentIoListHead), the requests sent with WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE while the target was not started (m_IgnoredIoListHead), and the requests that the target holds until it starts (m_PendedQueue). For each request, it shows the WDFREQUEST handle, the FxRequest, and the IRP. A request without a handle is the framework’s own, such as the request that a synchronous send builds. A list stops at a request whose m_Target is not this target, with the reason.

wdf_loader() → WdfLoader

List the KMDF client drivers.

Walks the list at Wdf01000!FxLibraryGlobals.FxDriverGlobalsList. For each client driver, it shows the name, the KMDF version that the driver bound to (WdfBindInfo), the _FX_DRIVER_GLOBALS, the WDFDRIVER handle, the DRIVER_OBJECT, and whether the driver has an In-Flight Recorder log. The command needs the PDB for Wdf01000. The walk continues only while the Blink of each entry points back to the entry before it. A client has an empty DriverName if it bound to KMDF but did not create an FxDriver, and the command then lists the client by the name of its DRIVER_OBJECT, which you also use with !wdfkd.wdfdriverinfo and !wdfkd.wdflogdump. When a client name is not printable, or the FxDriver of a client fails the checks of its type, owner, or handle, the command lists the client with the problem.

wdf_log(driver: str) → WdfLogDump

Show the In-Flight Recorder log of a KMDF driver, oldest record first.

Reads the IFR log of the driver (_FX_DRIVER_GLOBALS.WdfLogHeader). The command walks the records back from the newest record along PrevOffset, and stops at the first record written or at records that newer records overwrote. For each record, it shows the sequence number, the UTC time (only when the log keeps timestamps), the function, and the message. The command formats the message from the trace message format (TMF) annotations in the PDB for Wdf01000. For a record with a message that no loaded PDB declares, or with arguments that do not fit the message, it shows the message GUID, the message number, and the argument bytes. The command checks the header (its GUID, base, and size) and each record (signature, length, position, and sequence), and the walk stops at the first item that fails a check, with the reason.

wdf_object(object: int) → WdfObjectDetail

Show a KMDF object’s header, contexts, and children.

Give a handle, as for !wdfkd.wdfhandle, or the address of an FxObject. The command shows what !wdfkd.wdfhandle shows: the handle, the type (FX_OBJECT_TYPES), size, reference count, state (FxObjectState), flags (FXOBJECT_FLAGS), owning driver, and parent, and each context with its type name, size, and address. Then it lists the children of the object (m_ChildListHead), each with its handle and type. The framework’s own children, such as the packages of a device, have no handle. Use the command for an object whose type has no command of its own, such as a WDFMEMORY, WDFTIMER, or WDFWORKITEM. The object must pass the same checks as for !wdfkd.wdfhandle, and the list of children stops at a child whose m_ParentObject is not this object.

wdf_queue(handle: int) → WdfQueueDetail

Show a WDFQUEUE and its configuration, state, callbacks, and requests.

Shows the dispatch type of the queue, the state bits (_FX_IO_QUEUE_STATE), the power state, and the power management. It also shows the execution level, the synchronization scope, the request counts, and the Evt callbacks that the driver set. Then it shows the requests on three lists: the requests that wait in the queue, the requests that the driver marked cancelable, and the requests presented to the driver. For each request, it shows the WDFREQUEST handle, which you pass to !wdfkd.wdfrequest, the FxRequest, and the IRP. If a request is not valid, the command stops that list at the request and shows the reason.

wdf_request(handle: int) → WdfRequestDetail

Show a WDFREQUEST, its IRP, its queue, and the I/O target it was sent to.

Shows the FxRequest, its owning driver, parent, reference count, and object state. For the IRP of the request, it shows the current stack location: the major and minor function, the device object and its driver, and the buffer lengths of a read, a write, or a device control with its control code. It also shows the system buffer, the user buffer, and the MDL of the IRP; pass the IRP to !irp for all of its stack locations. If the IRP was freed or reused, the command shows why it is not a live IRP. The command shows whether the IRP came from the I/O manager or the framework or the driver allocated it (FxRequestAllocationSource), whether the request is completed or canceled, and whether its queue presented it to the driver. It shows the queue that the request came from or waits in (m_IoQueue) and the cancel-safe queue that holds the IRP: the requests that wait in the queue, the requests that the driver marked cancelable with its EvtRequestCancel routine, which the command shows, or the requests that an I/O target holds until it starts. A request that none of these holds cannot be canceled. For a request that the driver sent, it shows the I/O target (m_Target), the completion routine and its context, and the target flags (FxRequestTargetFlags). KMDF sets the verifier flags (FxRequestVerifierFlags) only when its verifier is on for the driver.

work_queues(*, include_stacks: bool = False, queue_types: Sequence[Literal['critical', 'delayed', 'hypercritical']] | None = None) → WorkQueues

Get the executive worker queues, their pending work items, and their worker threads (!exqueue). include_stacks adds the stack of each worker, and queue_types ("critical", "delayed", "hypercritical") limits the listed items to the priorities of those types.

zombies(*, flags: int = 1) → Zombies

List exited processes and threads that still have references.

Scans nonpaged pool for process (Proc) and thread (Thre) objects, and finds the header of each object by its decoded type. The command lists the processes that have ExitTime set and the threads in the Terminated state. For each object, it shows the handle count and the pointer count: whatever still holds these references keeps the object in memory. Flag 1 (the default) lists processes, 2 lists threads, and 3 lists both. The scan stops after 4096 processes or 4096 threads, or when you press Ctrl+C.