windbg-vscode
A VS Code extension for analyzing Windows crash dumps (.dmp) and, in a future release, TTD traces (.run).
It runs on macOS and Windows. There is no dependency on DbgEng.dll or dbghelp: the dump is parsed
from scratch, and symbol resolution, disassembly and type information are provided by the LLVM tools
installed on your machine.
Table of Contents
Features
- WinDBG-compatible experience
- A classic WinDBG-style command window
- Clickable command-output links for module details, thread selection, disassembly and memory inspection
- Compatibility with the debugger commands
- Analysis of
.dmp crash dumps
- Analysis of
.run traces recorded by TTTracer (planned)
- Symbol resolution through symbol servers
- Invocation from GitHub Copilot for VS Code through seven bounded analysis tools
Requirements
| Tool |
Version |
Purpose |
| Visual Studio Code |
1.129.0 or later |
Runs the extension |
| LLVM |
22 or later |
Symbol resolution, disassembly and type information |
| Azure CLI |
Current |
Optional authentication for runtime-configured private symbol servers |
Install LLVM with brew install llvm on macOS, or use an official LLVM release on Windows.
The extension discovers llvm-symbolizer, llvm-pdbutil and llvm-mc from PATH, from the standard
Homebrew location, and from the standard Windows install locations. You can also point
windbg.llvm.toolPath at the directory that contains them.
Without LLVM you can still open a dump and inspect its structure; commands that need symbols,
disassembly or type information report that the tools are unavailable.
Private symbol server addresses and token audiences are never embedded in the repository or VSIX.
Set endpoints through the machine-scoped windbg.symbols.servers setting or
WINDBG_VSCODE_SYMBOL_SERVERS, then give each private endpoint a profile in the machine-scoped
windbg.symbols.authentication setting (or WINDBG_VSCODE_SYMBOL_AUTHENTICATION at runtime).
Two schemes are supported.
Personal access token, the scheme Azure DevOps symbol servers document:
{ "endpoint": "https://artifacts.dev.azure.com/<organization>/_apis/symbol/symsrv", "kind": "personalAccessToken" }
Create a token with the Symbols (read) scope, then run WinDBG: Set Symbol Server Credential
and paste it. It is held in the operating system keychain through VS Code's secret storage, never in
a setting or a synced file, and is sent as HTTP Basic with an empty user name. WinDBG: Clear Symbol
Server Credential removes it.
Azure CLI, for endpoints that accept a Microsoft Entra token:
{ "endpoint": "https://symbols.example.test/store", "kind": "azureCli", "argument": "scope", "audience": "api://symbols.example.test/.default" }
argument is resource or scope. Sign in with az login first.
az login alone does not authenticate a symbol endpoint: the extension still needs one
windbg.symbols.authentication profile per private endpoint to select the token resource or scope.
In both cases the credential is attached only when the normalized request endpoint exactly matches
its profile, and is never forwarded to the signed blob URL the server redirects to.
Requests to an endpoint without a matching authentication profile are unauthenticated. Public
servers may accept them; private servers may return a sign-in page or a refusal instead of an
artifact. These responses are reported rather than retried: run WinDBG: Show Symbol Diagnostics
to see what each endpoint answered, by position and HTTP status.
The extension imposes no artifact-size limit. Large product PDBs can exceed 5 GB, so automatic
resolution may use substantial network bandwidth, disk space and time. An individual artifact
transfer times out after nine hours. Large authenticated downloads are split into resumable
parts and transient failures are retried while data continues arriving. If a server sends no data
for 60 seconds, its transfer is stopped and the next configured server is tried. Run the command
again to continue from saved parts after a timeout or stalled transfer.
Every requested artifact is queued with at most four active
acquisitions, and simultaneous requests for the same cache entry share its in-flight work. Closing
the panel cancels the downloads it started. The status line shows completed artifacts out of the
batch total and names an artifact still being processed. When its transfer size is known, it also
shows the actual downloaded percentage and byte counts, for example
Downloading (7/8): large.pdb - 50% (3.0 GiB / 6.0 GiB). Resumed parts are included without counting
retries twice; unknown sizes show downloaded bytes without a percentage. The percentage describes
the transferred file (the compressed form when applicable), not the whole batch. Assembly,
expansion, validation and retries have distinct status labels. The command input stays disabled
until the batch and command finish; incomplete acquisition and stalled transfers are reported per
endpoint in WinDBG: Show Symbol Diagnostics.
Getting Started
- Install the extension.
- Open a
.dmp file. It opens in the WinDBG Dump Analyzer editor.
- Type a command in the command window at the bottom and press Enter.
If the file opens as text instead, right-click it and choose Open With… → WinDBG Dump Analyzer.
The command input becomes available after the capture loads successfully. Unsupported files and
failed loads keep it disabled with an explanation; close and reopen the capture to retry.
Usage
Command results contain WinDBG-style links. Click a module name to show its details (lmv a ...),
a thread number to switch threads (~<n>s), a stack call site to disassemble it (u ...), or a
memory address to inspect bytes or pointers (db ... / dps ...). Hover over a link to see its
command, or use Tab and Enter to activate it from the keyboard.
Linked commands use the same history and execution path as typed commands, leave unfinished input
intact, and are disabled while a command or symbol download is running. Output layout and copied
plain text are unchanged.
Open a capture and see what it is
The dump is parsed on a worker thread, so the editor stays responsive even for a capture of several
hundred megabytes. The banner reports the architecture, the module and thread counts, and the
recorded exception. vertarget describes the machine that produced the capture, and lm lists the
loaded modules with the symbol state of each one.

Find out why it crashed
!analyze -v classifies the fault, names the faulting module and thread, and prints the stack. It
reports what the capture actually supports: frames recovered by scanning are labelled as such, and
the output says when function names would need symbols.

Work through the threads
~ lists the threads with their names, and k walks the current one. Switch threads with ~<n>s
and walk again. A command that needs symbols fetches them for the modules it touched and then runs
again, so names resolve on the first attempt; .reload loads every module at once, and
windbg.symbols.autoLoad turns the automatic behaviour off.

GitHub Copilot can perform the same triage through the language model tools,
without changing the command window's current thread or starting an unapproved symbol download.
Automatic symbol retry freezes aliases and ENTER history before a download. Only replay-safe
commands are rerun. Commands or chains that change session state or advance display positions run
once, retain their output and show a warning instead; run .reload first when they need symbols.
An explicit .reload, including an alias, ENTER repeat or command chain, acquires symbols before
the prepared command executes.
ThreadEx lists and captured thread stacks are also used when a separate memory stream is absent.
Adjacent captured ranges can be read together, with deterministic handling of overlaps. Context
flags determine which registers are available: CONTROL-only contexts remain usable, while an
uncaptured frame pointer is reported as unavailable rather than zero.
Commands
The command window implements the commands below. Every command in the debugger's
command reference
is recognized. Implemented commands check their switches against the reference, and unavailable
execution, port, MSR and script commands validate their full operand grammar before reporting what
they need. A mistyped switch is therefore a syntax error rather than a silently ignored argument.
Breakpoint and event-filter commands retain non-firing session setup state. Type .help for the
whole set grouped by category, or .help memory for one group.
The 100-entry Microsoft Learn table and the extension-command table are generated directly from the
command registry, so their syntax and availability columns are the same data .help and command
dispatch use.
| Microsoft Learn entry |
Registered syntax |
Availability in this analyzer |
ENTER (Repeat Last Command) |
ENTER |
Implemented; capture-specific limits apply |
$<, $><, $$<, $$><, $$>a< (Run Script File) |
$<File | $><File | $$<File | $$><File | $$>a<File [Args] |
Syntax validated; command programs are not executed |
? (Command Help) |
? [Expression] |
Implemented; capture-specific limits apply |
? (Evaluate Expression) |
? [Expression] |
Implemented; capture-specific limits apply |
?? (Evaluate C++ Expression) |
?? Expression |
Implemented; capture-specific limits apply |
# (Search for Disassembly Pattern) |
# [Pattern] [Address [L Size]] |
Implemented; capture-specific limits apply |
|| (System Status) |
|| [SystemNumber] |
Implemented; capture-specific limits apply |
||s (Set Current System) |
||SystemNumber s |
Implemented; capture-specific limits apply |
| (Process Status) |
| [ProcessNumber] |
Implemented; capture-specific limits apply |
|s (Set Current Process) |
|ProcessNumber s |
Implemented; capture-specific limits apply |
~ (Thread Status) |
~ [ThreadNumber] |
Implemented; capture-specific limits apply |
~e (Thread-Specific Command) |
~Thread e Command |
Implemented; capture-specific limits apply |
~f (Freeze Thread) |
~Thread f |
Implemented; capture-specific limits apply |
~u (Unfreeze Thread) |
~Thread u |
Implemented; capture-specific limits apply |
~n (Suspend Thread) |
~Thread n |
Implemented; capture-specific limits apply |
~m (Resume Thread) |
~Thread m |
Implemented; capture-specific limits apply |
~s (Set Current Thread) |
~Thread s |
Implemented; capture-specific limits apply |
~s (Change Current Processor) |
~Thread s |
Implemented; capture-specific limits apply |
a (Assemble) |
a Address Instruction |
Implemented; capture-specific limits apply |
ad (Delete Alias) |
ad [/q] Name |
Implemented; capture-specific limits apply |
ah (Assertion Handling) |
ah | ah{b|i|d} [Address] | ahc |
Implemented; capture-specific limits apply |
al (List Aliases) |
al |
Implemented; capture-specific limits apply |
as, aS (Set Alias) |
as [Options] Name Value |
Implemented; capture-specific limits apply |
ba (Break on Access) |
ba[ID] Access Size [Options] [Address [Passes]] ["CommandString"] |
Implemented; capture-specific limits apply |
bc (Breakpoint Clear) |
bc Breakpoints |
Implemented; capture-specific limits apply |
bd (Breakpoint Disable) |
bd Breakpoints |
Implemented; capture-specific limits apply |
be (Breakpoint Enable) |
be Breakpoints |
Implemented; capture-specific limits apply |
bl (Breakpoint List) |
bl [/L] [Breakpoints] |
Implemented; capture-specific limits apply |
bp, bu, bm (Set Breakpoint) |
bp[ID] [Options] [Address [Passes]] ["CommandString"] |
Implemented; capture-specific limits apply |
br (Breakpoint Renumber) |
br OldID NewID [OldID2 NewID2 ...] |
Implemented; capture-specific limits apply |
bs (Update Breakpoint Command) |
bs ID ["CommandString"] |
Implemented; capture-specific limits apply |
bsc (Update Conditional Breakpoint) |
bsc ID Condition ["CommandString"] |
Implemented; capture-specific limits apply |
c (Compare Memory) |
c Range Address |
Implemented; capture-specific limits apply |
d, da, db, dc, dd, dD, df, dp, dq, du, dw, dW, dyb, dyd (Display Memory) |
d [Options] [Range]
da [Options] [Range]
db [Options] [Range]
dc [Options] [Range]
dd [Options] [Range]
dD [Options] [Range]
df [Options] [Range]
dp [Options] [Range]
dq [Options] [Range]
du [Options] [Range]
dw [Options] [Range]
dW [Options] [Range]
dyb [Options] [Range]
dyd [Options] [Range] |
Implemented; capture-specific limits apply |
dda, ddp, ddu, dpa, dpp, dpu, dqa, dqp, dqu (Display Referenced Memory) |
dda [Options] [Range]
ddp [Options] [Range]
ddu [Options] [Range]
dpa [Options] [Range]
dpp [Options] [Range]
dpu [Options] [Range]
dqa [Options] [Range]
dqp [Options] [Range]
dqu [Options] [Range] |
Implemented; capture-specific limits apply |
dds, dps, dqs (Display Words and Symbols) |
dps [Options] [Range] |
Implemented; capture-specific limits apply |
dg (Display Selector) |
dg FirstSelector [LastSelector] |
Unavailable; the command reports the specific reason |
dl (Display Linked List) |
dl [Options] ListAddress MaxCount Size |
Implemented; capture-specific limits apply |
ds, dS (Display String) |
ds [/c Width] Address |
Implemented; capture-specific limits apply |
dt (Display Type) |
dt [Options] [Type] [Address] |
Implemented; capture-specific limits apply |
dtx (Display Type - Extended Debugger Object Model Information) |
dtx [-h] [-a] [-r[n]] [Module!]Name Address |
Implemented; capture-specific limits apply |
dv (Display Local Variables) |
dv [Options] [Pattern] |
Implemented; capture-specific limits apply |
dx (Display Debugger Object Model Expression) |
dx [Options] Expression |
Implemented; capture-specific limits apply |
e, ea, eb, ed, eD, ef, ep, eq, eu, ew, eza, ezu (Enter Values) |
e Address Values
ea Address "String"
eb Address Values
ed Address Values
eD Address Values
ef Address Values
ep Address Values
eq Address Values
eu Address "String"
ew Address Values
eza Address "String"
ezu Address "String" |
Implemented; capture-specific limits apply |
f, fp (Fill Memory) |
f Range Pattern
fp Range Pattern |
Implemented; capture-specific limits apply Syntax validated; requires physical memory |
g (Go) |
g[a] [= StartAddress] [BreakAddress ... [; BreakCommands]] |
Syntax validated; requires a live target |
gc (Go from Conditional Breakpoint) |
gc |
Syntax validated; requires a live target |
gh (Go with Exception Handled) |
gh[a] [= StartAddress] [BreakAddress ...] |
Syntax validated; requires a live target |
gn, gN (Go with Exception Not Handled) |
gn[a] [= StartAddress] [BreakAddress ...] |
Syntax validated; requires a live target |
gu (Go Up) |
gu |
Syntax validated; requires a live target |
ib, iw, id (Input from Port) |
ib Port |
Syntax validated; requires a kernel target |
j (Execute If - Else) |
j Expression Command1 ; Command2 |
Syntax validated; command programs are not executed |
k, kb, kc, kd, kp, kP, kv (Display Stack Backtrace) |
k [Options] [FrameCount] |
Implemented; capture-specific limits apply |
l+, l- (Set Source Options) |
l{+|-}[Option] |
Unavailable; the command reports the specific reason |
ld (Load Symbols) |
ld Module [/f Filename] |
Implemented; capture-specific limits apply |
lm (List Loaded Modules) |
lm [Options] [a Address] [m Pattern] |
Implemented; capture-specific limits apply |
ln (List Nearest Symbols) |
ln [/D] Address |
Implemented; capture-specific limits apply |
ls, lsa (List Source Lines) |
ls [.] [First] [, Count]
lsa [.] Address [, First [, Count]] |
Implemented; capture-specific limits apply |
lsc (List Current Source) |
lsc |
Implemented; capture-specific limits apply |
lse (Launch Source Editor) |
lse |
Implemented; capture-specific limits apply |
lsf, lsf- (Load or Unload Source File) |
lsf File
lsf- File |
Implemented; capture-specific limits apply |
lsp (Set Number of Source Lines) |
lsp [-a] [LeadingLines [TrailingLines]] |
Implemented; capture-specific limits apply |
m (Move Memory) |
m Range Address |
Implemented; capture-specific limits apply |
n (Set Number Base) |
n [Radix] |
Implemented; capture-specific limits apply |
ob, ow, od (Output to Port) |
ob Port Value |
Syntax validated; requires a kernel target |
p (Step) |
p[r] [= StartAddress] [Count] ["Command"] |
Syntax validated; requires a live target |
pa (Step to Address) |
pa [r] [= StartAddress] StopAddress ["Command"] |
Syntax validated; requires a live target |
pc (Step to Next Call) |
pc [r] [= StartAddress] [Count] |
Syntax validated; requires a live target |
pct (Step to Next Call or Return) |
pct [r] [= StartAddress] [Count] |
Syntax validated; requires a live target |
ph (Step to Next Branching Instruction) |
ph [r] [= StartAddress] [Count] |
Syntax validated; requires a live target |
pt (Step to Next Return) |
pt [r] [= StartAddress] [Count] ["Command"] |
Syntax validated; requires a live target |
q, qq (Quit) |
q
qq |
Implemented; capture-specific limits apply |
qd (Quit and Detach) |
qd |
Implemented; capture-specific limits apply |
r (Registers) |
r [Options] [Register[=Value]] |
Implemented; capture-specific limits apply |
rdmsr (Read MSR) |
rdmsr MSR |
Syntax validated; requires a kernel target |
rm (Register Mask) |
rm [Mask] |
Implemented; capture-specific limits apply |
s (Search Memory) |
s [Options] Range Pattern |
Implemented; capture-specific limits apply |
so (Set Kernel Debugging Options) |
so [NOEXTWARNING] [NOVERSIONCHECK] |
Implemented; capture-specific limits apply |
sq (Set Quiet Mode) |
sq | sq{e|d} |
Implemented; capture-specific limits apply |
ss (Set Symbol Suffix) |
ss {a|w|n} |
Implemented; capture-specific limits apply |
sx, sxd, sxe, sxi, sxn, sxr, sx- (Set Exceptions) |
sx
sxd [-c Cmd1] [-c2 Cmd2] [-h] Event
sxe [-c Cmd1] [-c2 Cmd2] [-h] Event
sxi [-c Cmd1] [-c2 Cmd2] [-h] Event
sxn [-c Cmd1] [-c2 Cmd2] [-h] Event
sxr
sx- [-c Cmd1] [-c2 Cmd2] Event |
Implemented; capture-specific limits apply |
t (Trace) |
t [r] [= StartAddress] [Count] ["Command"] |
Syntax validated; requires a live target |
ta (Trace to Address) |
ta [r] [= StartAddress] StopAddress |
Syntax validated; requires a live target |
tb (Trace to Next Branch) |
tb [r] [= StartAddress] [Count] |
Syntax validated; requires a live target |
tc (Trace to Next Call) |
tc [r] [= StartAddress] [Count] |
Syntax validated; requires a live target |
tct (Trace to Next Call or Return) |
tct [r] [= StartAddress] [Count] |
Syntax validated; requires a live target |
th (Trace to Next Branching Instruction) |
th [r] [= StartAddress] [Count] |
Syntax validated; requires a live target |
tt (Trace to Next Return) |
tt [r] [= StartAddress] [Count] |
Syntax validated; requires a live target |
u (Unassemble) |
u [Range]
ub [Range]
uu [Range] |
Implemented; capture-specific limits apply |
uf (Unassemble Function) |
uf [Options] Address |
Implemented; capture-specific limits apply |
up (Unassemble from Physical Memory) |
up Range |
Syntax validated; requires physical memory |
ur (Unassemble Real Mode BIOS) |
ur Range |
Unavailable; the command reports the specific reason |
ux (Unassemble x86 BIOS) |
ux Range |
Unavailable; the command reports the specific reason |
vercommand (Show Debugger Command Line) |
vercommand |
Implemented; capture-specific limits apply |
version (Show Debugger Version) |
version |
Implemented; capture-specific limits apply |
vertarget (Show Target Computer Version) |
vertarget |
Implemented; capture-specific limits apply |
wrmsr (Write MSR) |
wrmsr MSR Value |
Syntax validated; requires a kernel target |
wt (Trace and Watch Data) |
wt [WatchOptions] [= StartAddress] [EndAddress] |
Syntax validated; requires a live target |
x (Examine Symbols) |
x [Options] Module!Symbol |
Implemented; capture-specific limits apply |
z (Execute While) |
Command ; [Processor] z(Expression) |
Syntax validated; command programs are not executed |
Extension commands
| Extension command |
Registered syntax |
Availability in this analyzer |
.reload (Reload Symbols) |
.reload |
Implemented; capture-specific limits apply |
.ecxr (Display Exception Context) |
.ecxr |
Implemented; capture-specific limits apply |
.exr (Display Exception Record) |
.exr [Address] |
Implemented; capture-specific limits apply |
.lastevent (Display Last Event) |
.lastevent |
Implemented; capture-specific limits apply |
.dumpdebug (Display Dump Stream Directory) |
.dumpdebug |
Implemented; capture-specific limits apply |
.help, help (Command Help) |
.help [Category] |
Implemented; capture-specific limits apply |
!teb (Display the TEB) |
!teb |
Implemented; capture-specific limits apply |
!peb (Display the PEB) |
!peb |
Implemented; capture-specific limits apply |
!process (Display the Process) |
!process |
Implemented; capture-specific limits apply |
!thread (Display the Current Thread) |
!thread |
Implemented; capture-specific limits apply |
!handle (Display Handles) |
!handle [Type] |
Implemented; capture-specific limits apply |
!analyze (Analyze the Recorded Fault) |
!analyze [-v] |
Implemented; capture-specific limits apply |
Expressions are hexadecimal by default; n changes that, and 0x, 0n, 0t and 0y override it
for a single number. The MASM operator set is supported with its documented precedence — * / mod,
+ -, << >> >>>, the comparisons, and, xor and or — together with not, hi, low, the
poi, by, wo, dwo and qwo dereferences, and $vvalid, $iment, $scmp, $sicmp and
$spat. Registers are reached with @rip and pseudo-registers such as $ip, $teb, $peb, $ra,
$exr_code and $t0 with or without the at sign; one a capture cannot answer for reports what it
needs. A range is written as <address> L<count> or <address> <end address>.
Thread selectors accept an index (~1), raw TID (~~[1a4]) or expression (~[@$tid]);
process selectors likewise accept an index, |~[PID] or |[Expression]. rm defaults to the
basic integer registers. It can enable the segment, floating-point, MMX, debug and SIMD groups when
the selected thread's saved CONTEXT contains them; rM, rF and rX override the mask for one
display without changing that default. r reads or assigns comma-separated named registers,
including x86/x64 subregisters, flags and vector formats; assignments are isolated per thread in
the analyzer session. r. shows the registers referenced by the current decoded instruction.
~f / ~u and ~n / ~m retain synthetic freeze and suspend state for one or all threads. This
state is inspectable but cannot stop or resume the captured process.
lm implements the reference filters, columns and 1m/sm forms. x implements its address/name
columns, quoting, public code/data classification, wildcard filtering and sort modes; type and size
fields that are absent from the public-symbol index are shown as unknown. The k variants retain
their distinct clean/raw/argument/parameter/FPO presentations without inventing private parameter
names. u defaults to eight instructions, uu continues across uncaptured gaps, ub reconstructs
the preceding instruction boundary in one batched LLVM invocation, and uf supports call-only and
instruction-count output. ARM64 encoding lengths are read from LLVM's architecture-specific output.
dt fully parses its documented options and supports the built-in _TEB / _PEB prefixes plus
arbitrary structures from a cached private PDB. Array expansion, field filters, values and recursive
nested pointer or structure views use llvm-pdbutil dump --types; dtx uses the same typed records.
dv reads parameters and locals from dump --symbols, with its documented filters,
location display and sorting options. A record's offset is measured from the register it names, or
from the frame register its procedure declares; when a record declares neither, the location is
reported as unavailable instead of being guessed from another register. Missing or optimized-away
records are reported rather than fabricated.
MASM remains the default expression syntax. ??, @@c++( ) and @@( ) implement decimal C++
precedence, casts, sizeof, logical and bitwise operators, typed pointer arithmetic, dereference,
indexing and built-in or private-PDB structure members. Short-circuited and sizeof operands are
parsed without being evaluated. @@masm( ) selects MASM explicitly.
An alias defined with as or aS is substituted into a later command line wherever its name stands
alone, and ${Alias}, ${/v:Alias}, ${/d:Alias}, ${/f:Alias} and ${/n:Alias} behave as
documented. Aliases belong to the command window: the language model tools run the text they were
given, so an alias cannot change what a command does there.
bp/bu/bm/ba retain IDs, enabled state, pass counts, commands, conditions and documented
restrictions. bl supports ID lists, ranges and quoted symbolic wildcards; bc/bd/be, br,
bs and bsc update that table. Address expressions may contain spaces; a trailing pass count is
separated only after a complete address expression. Processor breakpoints reject duplicates whose
only difference is the command string. bs clears a condition created by bsc, while a /w
condition remains independent. The sx family starts from the documented exception/event defaults
and retains break/handling status plus first- and second-chance commands; sxr restores the defaults.
ah, so and sq retain their corresponding setup. All of this state belongs only to the open
analyzer session: a post-mortem capture cannot execute, hit a breakpoint, decrement a pass counter
or receive an event, and every relevant display says so.
lsf explicitly loads a local UTF-8 source file into the analyzer session; ls, lsa, lsc and
lsp navigate or configure the bounded source view, lsf- unloads it, and lse opens the current
line in VS Code. A session accepts at most 64 source files, 4 MiB each, and displays at most 1,000
source lines per command. Address-to-source lookup uses available symbol line records and reports
when no mapping exists. l+ and l- are recognized, but their source-mode stepping and display
options are not applied; this analyzer uses lsf and lsp for its source view.
The live execution families (g*, p*, t*, wt) and port/MSR commands parse their documented
flags and expressions before returning the live-target or kernel-target limitation. WinDbg can run
j, z and all five $< script-file forms against a crash dump; this analyzer currently validates
their quoted commands, processor prefix, file names and arguments before reporting its scripting
limitation. It does not execute a command program or resume the captured process.
A display command with no range continues from where the last one stopped, and starts at the
instruction pointer when there has been none; /c8 and /c 8 both set how many items go on a row.
A range is <address> <end address>, where the end address is part of the range, or
<address> L<count>, with L-<count> ending at the address and L?<count> lifting the debugger's
own guard. The count is read in the current radix; for du it counts UTF-16 characters. A minidump
holds only part of the address space, so bytes it does not contain are shown as question marks
rather than as zeroes or as a refusal of the whole request, and c treats a byte it cannot read as
a difference rather than a match. STRING and UNICODE_STRING output follows the structure's length,
so an embedded NUL is invisible without ending the value and valid surrogate pairs remain Unicode.
e*, f and m write to a bounded copy-on-write memory overlay, and a assembles one instruction
with llvm-mc into that overlay. Expressions, dumps, disassembly, stack walking and typed views all
read the overlaid bytes. The source dump file is never modified, and closing the analyzer discards
the overlay.
Commands can be chained with ;. An unsupported z segment reports its limitation without
discarding output from ordinary segments before or after it. Editor actions from a chain are applied
in order. Pressing ENTER on an empty line reissues the previous command line.
A thread selector binds to the command that follows it, so ~0 shows a single thread, ~1k walks
thread 1 without making it current, and ~*k walks the stack of every thread.
Command echoes use the current thread's prompt. ENTER repeat also works with history disabled;
IME confirmation does not submit a command. Input exceeding 2,048 characters is reported and kept
for correction. Clipboard failures are reported instead of claiming that copying succeeded.
Large outputs trim scrollback once per received chunk while retaining the configured line limit.
The VS Code command palette also offers:
| Command |
Description |
WinDBG: Open Crash Dump |
Pick a dump file and open it in the analyzer |
WinDBG: Run Debugger Command |
Focus the command window of the active analyzer |
WinDBG: Clear Command Output |
Clear the output log |
WinDBG: Copy Command Output |
Copy the output log to the clipboard |
WinDBG: Clear Symbol Cache |
Delete cached artifacts and their staging files, preserving unrelated neighboring files |
WinDBG: Show Symbol Diagnostics |
Show the detected LLVM tools and symbol server status |
WinDBG: Set Symbol Server Credential |
Store the access token for a symbol server in the operating system keychain |
WinDBG: Clear Symbol Server Credential |
Remove a stored symbol server access token |
GitHub Copilot
The extension contributes seven language model tools, so Copilot can analyze a dump directly:
windbg_openDump, windbg_analyze, windbg_stack, windbg_modules, windbg_threads,
windbg_runCommand and windbg_symbolStatus. Reference them in a prompt with #windbgOpen,
#windbgAnalyze, #windbgStack, #windbgModules, #windbgThreads, #windbgCommand and
#windbgSymbols.
The tools share the same worker as the editor, so a dump opened in both is parsed once. They are
read-only and bounded, and windbg_runCommand accepts only an allowlist of analysis commands.
windbg_openDump requires an absolute path; concurrent opens wait for the actual capture result,
and a failed open can be retried. A supplied windbg_stack thread index must be an integer from
0 to 4,095. Tool output is bounded to 128 KiB of UTF-8, including its truncation notice, without
splitting Unicode characters.
Cancelled tool invocations stop waiting and release their pending results without closing the
user's shared editor.
Commands that return process memory contents (db, da, du, dq, dps, s, u, uf, ub),
the file and object paths recorded in the capture (lm v, lm f, lm p, lm M, !handle), or that would start a
download (.reload) are not reachable from the model. The allowlist reads the command as it was
written, and the tools run without alias substitution, so an alias defined in the command window
cannot change what a command does for the model. ln, rm, expression-based target selectors,
lm a, custom k contexts, expression-sized x /s and expression-valued rM are also excluded
because their numeric expressions can dereference an address with poi; literal stack counts,
symbol sizes and register masks remain available. Breakpoint, event-filter, assertion, kernel-option,
quiet-mode, symbol-suffix and thread selection/freeze/suspend commands are also excluded because
that mutable state belongs to the user's command-window session. The dedicated stack tool scopes a
requested thread to one stack walk and restores the user's current thread.
A ready-made WinDBG Analyst agent ships with the extension at
agents/windbg-analyst.agent.md. Copy it into your workspace's .github/agents/ folder to use it.
Settings
| Setting |
Default |
Description |
windbg.symbols.servers |
[] |
Ordered symbol server endpoints. Each entry must be an absolute https:// URL without credentials, query or fragment. Microsoft Public Symbols is always appended last. Endpoints named in an authentication profile use its selected authentication method |
windbg.symbols.authentication |
[] |
Authentication profiles for private symbol servers, each { endpoint, kind, argument, audience }. kind is azureCli (default) or personalAccessToken. A credential is attached only when the request endpoint matches a profile exactly, and never reaches the signed blob URL. No credential is stored here |
windbg.symbols.cachePath |
"" |
Directory used to cache downloaded PE and PDB artifacts. Must be an absolute path at least two levels below the filesystem root. Empty, or rejected, uses the extension's global storage |
windbg.symbols.offline |
false |
Resolve symbols only from the verified local cache and never start a network request |
windbg.symbols.autoLoad |
true |
Fetch symbols for replay-safe commands automatically. Stateful and continuation-dependent commands are not rerun; use .reload first |
windbg.llvm.toolPath |
"" |
Directory containing llvm-symbolizer, llvm-pdbutil and llvm-mc. Empty auto-detects |
windbg.command.historySize |
200 |
Number of entered commands kept in the session-only history. Zero disables recall, not ENTER repeat |
windbg.output.maxLines |
10000 |
Maximum number of output lines retained per command |
windbg.disassembly.syntax |
intel |
Assembly syntax used by the disassembly commands |
Supported Files
| Extension |
Status |
.dmp, .mdmp, .hdmp |
User-mode minidump. Supported |
.run |
TTD trace. Recognized, analysis planned |
File associations accept every casing of these extensions, including .DMP, .MDMP, .HDMP
and .RuN.
Limitations
- Read-only capture file. The extension never writes to the dump and does not support live
debugging. Memory and register edits, thread freeze/suspend state, breakpoints and event filters
exist only in the analyzer session; they never change or resume the captured process.
- User-mode dumps only. Kernel crash dumps are planned for a later release.
- No TTD replay. Replaying a
.run trace requires the Windows-only TTD replay engine, so it
cannot run on macOS. A future release will extract trace metadata instead.
- No managed (.NET) analysis. SOS-style commands are planned for a later release.
- No debugger command-program execution.
j, z and the $< script-file forms validate their
syntax but do not execute scripts from a capture.
- ARM64 stack unwinding is derived from the specification and is still waiting for verification
against a real ARM64 capture. Results are labelled accordingly.
- Stack frames recovered by scanning the captured stack are labelled as such. A minidump
normally captures thread stacks but not the module images. Once
.reload has fetched an image,
frames inside it are unwound exactly from its exception directory; everything past that is
recovered by scanning and is marked so the two are never confused.
Scanning stays within the selected thread's stack. x64 prologue, epilogue and saved-frame-register
recovery uses only captured data; unsupported unwind forms retain the scan label.
Cached image payloads have a 128 MiB budget per worker. Larger images still provide bounded
metadata and symbol lookup, but unwinding that needs unretained image bytes falls back to labelled scanning.
- Private typed views depend on PDB records.
_TEB and _PEB have stable built-in prefixes;
other dt / dtx types and dv locals require a cached private PDB. A local whose record names
no base register, and one the compiler optimized away, are reported as unavailable.
Roadmap
- [x] Read-only custom editor and command window shell
- [x] Minidump stream parsing and the
lm / ~ / r / d* command family
- [x] Stack walking (
k), ln and expression evaluation
- [x] Symbol server configuration, LLVM tool discovery and
.reload
- [x] Disassembly (
u) and symbol-annotated pointer dumps (dps)
- [x] Type-aware commands (
dt, dx, !peb, !teb, !process, !thread, !handle)
- [x]
!analyze -v
- [x] Session-only breakpoint, assertion and exception-filter setup
- [x] Session source-file loading and opening the current line in VS Code
- [x] GitHub Copilot integration
- [ ] A detachable memory and disassembly window
- [ ] TTD
.run trace metadata extraction
- [ ] Kernel crash dump analysis
- [ ] SOS-style managed .NET analysis
Privacy
The capture file is strictly read-only. Symbol loading is automatic by default when a command
needs it; enable windbg.symbols.offline to prevent network requests. Language model tools never
start symbol downloads. Full file paths, memory contents, command lines, symbol paths and LLVM tool
paths are never written to logs, to persistent state, or to test and benchmark output. Symbol
downloads use configured endpoints followed by Microsoft Public Symbols, and a downloaded image
or PDB is cached only when its own identity matches the one recorded in the
capture. Direct responses must deliver their declared byte count, and a PDB must contain every block
its MSF superblock declares before its GUID is accepted.
Cache hits are revalidated; unreadable or inconclusive entries are not reported as verified.
Resumable parts are tied to the source and representation rather than reused by length alone, and
compressed artifacts are expanded with bounded reads instead of loading the whole archive.
Local LLVM symbolization explicitly disables debuginfod downloads.
License
MIT