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
172 changes: 172 additions & 0 deletions .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
name: Windows packages

on:
push:
pull_request:
workflow_dispatch:

jobs:
package:
name: Package ${{ matrix.label }}
runs-on: windows-latest
timeout-minutes: 60

strategy:
fail-fast: false
matrix:
include:
- label: windows-x64
rust_target: x86_64-pc-windows-msvc
nuget_arch: amd64
vcvars_arch: x64
artifact: windbg-tool-windows-x64
can_run: true
- label: windows-arm64
rust_target: aarch64-pc-windows-msvc
nuget_arch: arm64
vcvars_arch: x64_arm64
artifact: windbg-tool-windows-arm64
can_run: false

env:
CARGO_TERM_COLOR: always
RUSTFLAGS: -C target-feature=+crt-static

steps:
- name: Checkout
uses: actions/checkout@v5

- name: Select stable Rust
shell: pwsh
run: |
rustup default stable
rustup target add ${{ matrix.rust_target }}

- name: Restore debugger dependencies
shell: pwsh
run: cargo xtask deps --arch ${{ matrix.nuget_arch }}

- name: Ensure ARM64 MSVC toolset
if: matrix.nuget_arch == 'arm64'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe'
$installer = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vs_installer.exe'
if (-not (Test-Path $vswhere)) {
throw "vswhere.exe was not found at $vswhere"
}
if (-not (Test-Path $installer)) {
throw "vs_installer.exe was not found at $installer"
}
$installationPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
if (-not $installationPath) {
throw 'Could not find a Visual Studio installation with the MSVC x64 toolset'
}
& $installer modify `
--installPath $installationPath `
--add Microsoft.VisualStudio.Component.VC.Tools.ARM64 `
--quiet `
--norestart `
--nocache `
--wait
if (($LASTEXITCODE -ne 0) -and ($LASTEXITCODE -ne 3010)) {
exit $LASTEXITCODE
}

- name: Build native bridge
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe'
$installationPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
if (-not $installationPath) {
throw 'Could not find a Visual Studio installation with the MSVC x64 toolset'
}
$vcvars = Join-Path $installationPath 'VC\Auxiliary\Build\vcvarsall.bat'
cmd /c "`"$vcvars`" ${{ matrix.vcvars_arch }} && cargo xtask native-build --arch ${{ matrix.nuget_arch }} --static-crt"

- name: Build windbg-tool
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe'
$installationPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
if (-not $installationPath) {
throw 'Could not find a Visual Studio installation with the MSVC x64 toolset'
}
$vcvars = Join-Path $installationPath 'VC\Auxiliary\Build\vcvarsall.bat'
cmd /c "`"$vcvars`" ${{ matrix.vcvars_arch }} && cargo build -p windbg-tool --release --target ${{ matrix.rust_target }}"

- name: Package runtime files
shell: pwsh
run: |
cargo xtask package `
--arch ${{ matrix.nuget_arch }} `
--target ${{ matrix.rust_target }} `
--profile release `
--out "target\package\${{ matrix.artifact }}"

- name: Validate package and create ZIP
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'

$packageDir = Join-Path $PWD 'target\package\${{ matrix.artifact }}'
$zipPath = Join-Path $PWD 'target\package\${{ matrix.artifact }}.zip'
$requiredFiles = @(
'windbg-tool.exe',
'ttd_replay_bridge.dll',
'TTDReplay.dll',
'TTDReplayCPU.dll',
'dbgeng.dll',
'dbgcore.dll',
'dbghelp.dll',
'dbgmodel.dll',
'msdia140.dll',
'symsrv.dll',
'srcsrv.dll'
)

foreach ($file in $requiredFiles) {
$path = Join-Path $packageDir $file
if (-not (Test-Path $path)) {
throw "Package is missing required file: $file"
}
}

if ('${{ matrix.can_run }}' -eq 'true') {
Push-Location $packageDir
try {
& '.\windbg-tool.exe' discover | Out-Null
}
finally {
Pop-Location
}
}

if (Test-Path $zipPath) {
Remove-Item $zipPath -Force
}
Compress-Archive -Path (Join-Path $packageDir '*') -DestinationPath $zipPath -Force

Add-Type -AssemblyName System.IO.Compression.FileSystem
$zip = [System.IO.Compression.ZipFile]::OpenRead($zipPath)
try {
$entries = @($zip.Entries | ForEach-Object { $_.FullName.Replace('/', '\') })
foreach ($file in $requiredFiles) {
if ($entries -notcontains $file) {
throw "ZIP is missing required file: $file"
}
}
}
finally {
$zip.Dispose()
}

- name: Upload package
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.artifact }}
path: target\package\${{ matrix.artifact }}.zip
if-no-files-found: error
118 changes: 118 additions & 0 deletions .github/workflows/smoke-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
name: Smoke tests

on:
push:
pull_request:
workflow_dispatch:

jobs:
dump-smoke:
name: Process dump smoke test
runs-on: windows-latest
timeout-minutes: 30

steps:
- name: Checkout
uses: actions/checkout@v5

- name: Select stable Rust
shell: pwsh
run: rustup default stable

- name: Restore debugger dependencies
shell: pwsh
run: cargo xtask deps

- name: Build windbg-tool
shell: pwsh
run: cargo build -p windbg-tool

- name: Create and inspect ping dump
shell: pwsh
timeout-minutes: 10
run: |
$ErrorActionPreference = 'Stop'

$tool = Join-Path $PWD 'target\debug\windbg-tool.exe'
$dumpDir = Join-Path $env:RUNNER_TEMP 'windbg-tool-smoke'
New-Item -ItemType Directory -Path $dumpDir -Force | Out-Null
$dumpPath = Join-Path $dumpDir 'ping.dmp'
Remove-Item -Path $dumpPath -Force -ErrorAction SilentlyContinue

Write-Host 'Launching ping.exe'
$ping = Start-Process `
-FilePath 'C:\Windows\System32\ping.exe' `
-ArgumentList @('127.0.0.1', '-n', '10') `
-PassThru

try {
Start-Sleep -Seconds 1

Write-Host 'Creating process dump'
$created = & $tool dump create `
--process-id $ping.Id `
--output $dumpPath `
--kind mini `
--overwrite | Tee-Object -Variable createOutput
$createOutput | ConvertFrom-Json | Out-Null

if (-not (Test-Path $dumpPath)) {
throw "Expected dump file was not created: $dumpPath"
}

$dumpFile = Get-Item $dumpPath
if ($dumpFile.Length -le 0) {
throw "Dump file is empty: $dumpPath"
}
}
finally {
if (-not $ping.HasExited) {
Write-Host 'Terminating ping.exe'
Stop-Process -Id $ping.Id -Force
$ping.WaitForExit()
}
}

Write-Host 'Opening and inspecting dump'
$inspection = & $tool dump inspect $dumpPath --max-frames 8 | ConvertFrom-Json
if ($inspection.target.kind -ne 'dump') {
throw "Expected dump target kind, got '$($inspection.target.kind)'"
}

Write-Host 'Checking modules'
$pingModule = $inspection.modules | Where-Object {
($_.module_name -like '*ping*') -or
($_.image_name -like '*ping.exe*') -or
($_.loaded_image_name -like '*ping.exe*')
} | Select-Object -First 1
if (-not $pingModule) {
throw "Expected loaded modules to include ping.exe"
}

Write-Host 'Checking threads'
if (-not $inspection.threads -or $inspection.threads.Count -lt 1) {
throw "Expected at least one thread in the dump"
}

Write-Host 'Checking registers'
if (
-not $inspection.registers.instruction_offset -and
-not $inspection.registers.stack_offset -and
-not $inspection.registers.frame_offset
) {
throw "Expected at least one current-thread register offset in the dump"
}

Write-Host 'Checking stack'
if (-not $inspection.frames -or $inspection.frames.Count -lt 1) {
throw "Expected at least one stack frame in the dump"
}

- name: Upload dump on failure
if: failure()
uses: actions/upload-artifact@v6
with:
name: ping-dump-smoke
path: ${{ runner.temp }}\windbg-tool-smoke\ping.dmp
if-no-files-found: ignore
retention-days: 3
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ The built executable is:
target\debug\windbg-tool.exe
```

Release ZIP artifacts are built by the Windows packaging workflow. The local equivalent uses `cargo xtask deps --arch <amd64|arm64>`, `cargo xtask native-build --arch <amd64|arm64> --static-crt`, a release build for the matching MSVC Rust target, and `cargo xtask package --profile release`.

For deeper setup, test commands, runtime details, and workspace notes, see [the development guide](docs/development.md).

## CLI quick start
Expand Down Expand Up @@ -70,7 +72,7 @@ Representative command areas:
- Discovery: `discover`, `recipes`, `tools`, `schema`
- Session and replay: `open`, `load`, `sessions`, `info`, `position set`, `step`, `replay to`
- Analysis: `symbols diagnose`, `disasm`, `memory dump`, `memory strings`, `memory chase`, `stack recover`, `stack backtrace`
- Platform helpers: `remote explain`, `dbgeng server`, `live launch`, `windbg status`
- Platform helpers: `remote explain`, `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
2 changes: 2 additions & 0 deletions crates/windbg-dbgeng/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ serde.workspace = true
[target.'cfg(windows)'.dependencies]
windows = { workspace = true, features = [
"Win32_Foundation",
"Win32_Storage_FileSystem",
"Win32_System_Diagnostics_Debug",
"Win32_System_Diagnostics_Debug_Extensions",
"Win32_System_Kernel",
"Win32_System_Memory",
"Win32_System_ProcessStatus",
"Win32_System_SystemInformation",
Expand Down
Loading