Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions .github/workflows/smoke-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,78 @@ jobs:
shell: pwsh
run: cargo build -p windbg-tool

- name: Agent command smoke tests
shell: pwsh
timeout-minutes: 5
run: |
$ErrorActionPreference = 'Stop'

$tool = Join-Path $PWD 'target\debug\windbg-tool.exe'

Write-Host 'Checking canonical debug capability matrix'
$debugCapabilities = & $tool --compact debug capabilities | ConvertFrom-Json
if ($debugCapabilities.canonical_command -ne 'debug capabilities') {
throw "Unexpected canonical command: $($debugCapabilities.canonical_command)"
}
$matrixBackends = @($debugCapabilities.backend_matrix | ForEach-Object { $_.backend })
foreach ($backend in @('ttd_cursor', 'dbgeng_live', 'dbgeng_dump', 'dbgeng_remote_plan')) {
if ($matrixBackends -notcontains $backend) {
throw "debug capabilities missing backend '$backend'"
}
}

Write-Host 'Checking remote doctor/status/plan'
$remoteDoctor = & $tool --compact remote doctor --transport tcp:port=0 | ConvertFrom-Json
if ($remoteDoctor.schema_version -ne 1 -or $remoteDoctor.status.parsed_tcp_port -ne 0) {
throw "remote doctor returned unexpected payload: $($remoteDoctor | ConvertTo-Json -Compress -Depth 20)"
}
if (-not (@($remoteDoctor.diagnostics) | Where-Object { $_.id -eq 'remote.probe.opt_in' })) {
throw 'remote doctor should report that TCP probing is opt-in'
}

$remoteStatus = & $tool --compact remote status --transport tcp:port=0 | ConvertFrom-Json
if ($remoteStatus.schema_version -ne 1 -or $null -ne $remoteStatus.probe) {
throw "remote status should not probe unless requested: $($remoteStatus | ConvertTo-Json -Compress -Depth 20)"
}

$remotePlan = & $tool --compact remote plan --server target01 | ConvertFrom-Json
$stepIds = @($remotePlan.steps | ForEach-Object { $_.id })
foreach ($step in @('target_start_server', 'host_connect', 'verify')) {
if ($stepIds -notcontains $step) {
throw "remote plan missing step '$step'"
}
}

Write-Host 'Checking breakpoint planner and CLI schema'
$breakpointPlan = & $tool --compact breakpoint plan --target 1 --address 0x1000 | ConvertFrom-Json
if (-not $breakpointPlan.supported -or $breakpointPlan.safety -ne 'mutating') {
throw "breakpoint plan returned unexpected payload: $($breakpointPlan | ConvertTo-Json -Compress -Depth 20)"
}

$debugSchema = & $tool --compact cli-schema debug snapshot | ConvertFrom-Json
if ($debugSchema.command.path -ne 'debug snapshot' -or $debugSchema.command.metadata.command -ne 'debug snapshot') {
throw "cli-schema did not include debug snapshot metadata: $($debugSchema | ConvertTo-Json -Compress -Depth 20)"
}

Write-Host 'Checking optional action log'
$logPath = Join-Path $env:RUNNER_TEMP 'windbg-tool-action-log.jsonl'
Remove-Item -Path $logPath -Force -ErrorAction SilentlyContinue
$env:WINDBG_TOOL_ACTION_LOG = $logPath
try {
& $tool --compact debug capabilities | Out-Null
$logSummary = & $tool --compact debug log summarize --path $logPath | ConvertFrom-Json
}
finally {
Remove-Item Env:WINDBG_TOOL_ACTION_LOG -ErrorAction SilentlyContinue
}
if ($logSummary.total_entries -lt 1 -or $logSummary.failed_entries -ne 0) {
throw "action log summary returned unexpected payload: $($logSummary | ConvertTo-Json -Compress -Depth 20)"
}
$loggedPath = @($logSummary.recent[0].command_path)
if ($loggedPath.Count -ne 2 -or $loggedPath[0] -ne 'debug' -or $loggedPath[1] -ne 'capabilities') {
throw "action log did not redact to the expected command path: $($logSummary | ConvertTo-Json -Compress -Depth 20)"
}

- name: Create and inspect ping dump
shell: pwsh
timeout-minutes: 10
Expand Down Expand Up @@ -108,6 +180,87 @@ jobs:
throw "Expected at least one stack frame in the dump"
}

Write-Host 'Checking daemon-backed agent commands on the dump target'
$pipe = "\\.\pipe\windbg-tool-ci-$PID"
$daemon = Start-Process `
-FilePath $tool `
-ArgumentList @('--pipe', $pipe, 'daemon', 'start') `
-PassThru
$daemonReady = $false
for ($attempt = 0; $attempt -lt 50; $attempt++) {
Start-Sleep -Milliseconds 100
$statusOutput = & $tool --pipe $pipe daemon status 2>$null
if ($LASTEXITCODE -eq 0) {
$statusOutput | ConvertFrom-Json | Out-Null
$daemonReady = $true
break
}
if ($daemon.HasExited) {
throw "Daemon exited before becoming ready with code $($daemon.ExitCode)"
}
}
if (-not $daemonReady) {
throw "Daemon did not become ready on pipe $pipe"
}

$targetId = $null
try {
$opened = & $tool --pipe $pipe dump open $dumpPath | ConvertFrom-Json
$targetId = $opened.target_id
if (-not $targetId) {
throw "dump open did not return a target_id: $($opened | ConvertTo-Json -Compress -Depth 20)"
}

$debugCapabilities = & $tool --pipe $pipe --compact debug capabilities --target $targetId | ConvertFrom-Json
if ($debugCapabilities.selected.subject.kind -ne 'target' -or -not $debugCapabilities.selected.matrix.can_stack) {
throw "debug capabilities did not describe the dump target: $($debugCapabilities | ConvertTo-Json -Compress -Depth 20)"
}

$snapshot = & $tool --pipe $pipe --compact debug snapshot `
--target $targetId `
--include status `
--include stack `
--include modules `
--max-frames 8 `
--max-modules 32 | ConvertFrom-Json
if (
$snapshot.canonical_command -ne 'debug snapshot' -or
$snapshot.subject.kind -ne 'target' -or
-not $snapshot.sections.status.status -or
-not $snapshot.sections.stack.status -or
-not $snapshot.sections.modules.status
) {
throw "debug snapshot returned unexpected dump-target sections: $($snapshot | ConvertTo-Json -Compress -Depth 20)"
}

$symbolsDoctor = & $tool --pipe $pipe --compact symbols doctor --target $targetId | ConvertFrom-Json
if ($symbolsDoctor.schema_version -ne 1 -or $symbolsDoctor.subject.kind -ne 'target' -or $symbolsDoctor.doctor.status -ne 'ok') {
throw "symbols doctor returned unexpected dump-target payload: $($symbolsDoctor | ConvertTo-Json -Compress -Depth 20)"
}

$triage = & $tool --pipe $pipe --compact triage symbol-health --target $targetId | ConvertFrom-Json
if ($triage.schema_version -ne 1 -or $triage.kind -ne 'symbol_health' -or $triage.evidence.snapshot.canonical_command -ne 'debug snapshot') {
throw "triage did not include debug snapshot evidence: $($triage | ConvertTo-Json -Compress -Depth 20)"
}

$breakpointPlan = & $tool --pipe $pipe --compact breakpoint plan --target $targetId --address 0x1000 | ConvertFrom-Json
if (-not $breakpointPlan.supported -or $breakpointPlan.safety -ne 'mutating') {
throw "breakpoint plan returned unexpected dump-target payload: $($breakpointPlan | ConvertTo-Json -Compress -Depth 20)"
}
}
finally {
if ($targetId) {
& $tool --pipe $pipe target close --target $targetId | Out-Null
}
& $tool --pipe $pipe daemon shutdown | Out-Null
if ($daemon -and -not $daemon.HasExited) {
if (-not $daemon.WaitForExit(5000)) {
Stop-Process -Id $daemon.Id -Force
$daemon.WaitForExit()
}
}
}

- name: Upload dump on failure
if: failure()
uses: actions/upload-artifact@v6
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
- Run a stdio MCP server for TTD replay workflows.
- Keep long-lived replay sessions in a local daemon and drive them from the CLI.
- Open traces, create cursors, seek positions, inspect threads/modules/registers/memory, and replay to watchpoints.
- Use higher-level helpers such as `discover`, `recipes`, `context snapshot`, `symbols diagnose`, `disasm`, `stack backtrace`, and `memory chase`.
- Start a DbgEng process server and install, update, or launch WinDbg.
- Use higher-level helpers such as `discover`, `recipes`, `debug capabilities`, `debug snapshot`, `triage`, `symbols doctor`, `disasm`, `stack backtrace`, and `memory chase`.
- Plan and diagnose local/remote DbgEng workflows with `remote doctor`, `remote plan`, process-server commands, and WinDbg install/update/launch helpers.

## Repository layout

Expand Down Expand Up @@ -51,6 +51,7 @@ Some commands work without loading a trace or starting the daemon:

```powershell
target\debug\windbg-tool.exe discover
target\debug\windbg-tool.exe cli-schema
target\debug\windbg-tool.exe recipes
target\debug\windbg-tool.exe tools
```
Expand All @@ -61,18 +62,23 @@ For trace-driven work, the common flow is:
target\debug\windbg-tool.exe daemon ensure
target\debug\windbg-tool.exe open C:\path\to\trace.run --binary-path C:\path\to\binary.exe
target\debug\windbg-tool.exe sessions
target\debug\windbg-tool.exe context snapshot --session 1 --cursor 1
target\debug\windbg-tool.exe debug capabilities --session 1 --cursor 1
target\debug\windbg-tool.exe debug snapshot --session 1 --cursor 1
target\debug\windbg-tool.exe disasm --session 1 --cursor 1
```

`open` is the best starting command for the CLI because it loads the trace, creates a cursor, and returns both `session_id` and `cursor_id`. Most replay commands then use `--session` and `--cursor` (or the short forms `-s` and `-c`).

For AI agents and robust scripts, add `--envelope` or set `WINDBG_TOOL_ENVELOPE=1` to get stable `{ schema_version, ok, data|error }` JSON responses and structured error codes.
Set `WINDBG_TOOL_ACTION_LOG=<path>` to append redacted JSONL command outcomes for handoff/resume workflows, then inspect them with `debug log summarize`.

Representative command areas:

- Discovery: `discover`, `recipes`, `tools`, `schema`
- Discovery: `discover`, `cli-schema`, `recipes`, `tools`, `schema`
- Session and replay: `open`, `load`, `sessions`, `info`, `position set`, `step`, `replay to`
- Agent workflow: `debug capabilities`, `debug snapshot`, `triage crash`, `symbols doctor`, `breakpoint plan`, `debug log summarize`
- Analysis: `symbols diagnose`, `disasm`, `memory dump`, `memory strings`, `memory chase`, `stack recover`, `stack backtrace`
- Platform helpers: `remote explain`, `dbgeng server`, `live launch`, `dump create`, `dump inspect`, `windbg status`
- Platform helpers: `remote explain`, `remote doctor`, `remote plan`, `dbgeng server`, `live launch`, `dump create`, `dump inspect`, `windbg status`

For a fuller CLI walkthrough, output-shaping flags, and command map, see [the CLI guide](docs/cli.md).

Expand Down
1 change: 1 addition & 0 deletions crates/windbg-tool/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ anyhow.workspace = true
clap.workspace = true
iced-x86 = "1.21"
rmcp.workspace = true
serde.workspace = true
serde_json.workspace = true
tokio.workspace = true
tracing-subscriber.workspace = true
Expand Down
Loading
Loading