From 07d7fbfa0799066ced25f911669621e24d0df550 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Tue, 6 Oct 2026 07:19:20 -0400 Subject: [PATCH 1/6] Windows on ARM64: release builds, and CI that installs and runs them on ARM64 (#61) - package.ps1 takes -Arch arm64 (aarch64-pc-windows-msvc, WiX -arch arm64) and now checks each binary's PE Machine as well as its Subsystem, so an x64 build can never ship labelled arm64. - release.yml builds, signs and packages a Windows ARM64 MSI and portable zip next to x64 and x86, cross-compiled on the x64 runner so signing and WiX run exactly as they do for x64. - New windows-arm64.yml (PRs touching Cargo/packaging, main, weekly): packages the ARM64 MSI the same way (unsigned), installs it on a Windows 11 ARM64 runner, checks both installed binaries are ARM64, opens/reads/edits/ renders/saves a PDF with the installed CLI, uninstalls; and runs the whole test suite natively on ARM64. The MSI is kept as an artifact for 7 days. - README: Windows builds for x64, x86 and ARM64. Requested by @maxffarrell. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/release.yml | 3 + .github/workflows/windows-arm64.yml | 126 ++++++++++++++++++++++++++++ README.md | 2 +- packaging/windows/package.ps1 | 23 +++-- 4 files changed, 144 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/windows-arm64.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 349088ce2..59bda0650 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,6 +112,9 @@ jobs: include: - { arch: x64, target: x86_64-pc-windows-msvc } - { arch: x86, target: i686-pc-windows-msvc } + # Windows on ARM (#61): cross-compiled on the x64 runner, so signing and WiX run as above. + # .github/workflows/windows-arm64.yml installs and runs it on ARM64 hardware. + - { arch: arm64, target: aarch64-pc-windows-msvc } env: PRINTCRAFT_VERSION: ${{ needs.version.outputs.version }} PRINTCRAFT_BUILD_DATE: ${{ needs.version.outputs.date }} diff --git a/.github/workflows/windows-arm64.yml b/.github/workflows/windows-arm64.yml new file mode 100644 index 000000000..bb05a3eb2 --- /dev/null +++ b/.github/workflows/windows-arm64.yml @@ -0,0 +1,126 @@ +# Windows on ARM (#61): package the ARM64 release the way release.yml does (cross-compiled on an +# x64 runner, unsigned here), then install that MSI on a Windows 11 ARM64 runner and run it, and +# run the test suite natively on ARM64. +name: Windows ARM64 + +on: + push: + branches: [main] + pull_request: + paths: + - Cargo.toml + - Cargo.lock + - "**/Cargo.toml" + - "**/build.rs" + - .github/workflows/windows-arm64.yml + - packaging/windows/** + schedule: + - cron: "41 4 * * 1" # weekly, so drift in the runner images shows up + workflow_dispatch: + +concurrency: + group: windows-arm64-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: 0 + RUST_BACKTRACE: 1 + +jobs: + package: + name: package (cross-compiled on x64) + runs-on: windows-latest + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + targets: aarch64-pc-windows-msvc + - uses: Swatinem/rust-cache@v2 + with: + key: windows-arm64-package + - name: Install WiX + shell: pwsh + run: dotnet tool install --global wix --version 5.0.2 + - name: Build and package (unsigned) + shell: pwsh + env: + DIST: ${{ github.workspace }}\dist\arm64 + run: ./packaging/windows/package.ps1 -Arch arm64 + - uses: actions/upload-artifact@v4 + with: + name: printcraft-windows-arm64 + path: dist/arm64/* + if-no-files-found: error + retention-days: 7 + + install: + name: install and run on ARM64 + needs: package + runs-on: windows-11-arm + timeout-minutes: 30 + steps: + - uses: actions/download-artifact@v4 + with: + name: printcraft-windows-arm64 + path: dist + - name: Install the MSI, check it, run it, uninstall it + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + $msi = (Get-ChildItem dist/*.msi | Select-Object -First 1).FullName + $p = Start-Process msiexec.exe -ArgumentList '/i', "`"$msi`"", '/qn', '/l*v', 'install.log' -Wait -PassThru + if ($p.ExitCode -ne 0) { Get-Content install.log -Tail 60; throw "msiexec /i exited $($p.ExitCode)" } + $dir = Join-Path $env:ProgramFiles 'PrintCraft' + foreach ($exe in 'printcraft.exe', 'printcraft-cli.exe') { + $path = Join-Path $dir $exe + if (-not (Test-Path $path)) { Get-ChildItem -Recurse $env:ProgramFiles -Filter $exe | Select-Object -First 3; throw "$exe not installed in $dir" } + $bytes = [System.IO.File]::ReadAllBytes($path) + $machine = [BitConverter]::ToUInt16($bytes, [BitConverter]::ToInt32($bytes, 0x3C) + 4) + if ($machine -ne 0xAA64) { throw "$exe is for machine 0x$('{0:X}' -f $machine), not ARM64" } + Write-Output "ok $path (ARM64)" + } + # The installed CLI runs natively: open, render and edit a small PDF through the engine. + $cli = Join-Path $dir 'printcraft-cli.exe' + & $cli tools | Out-Null + if ($LASTEXITCODE -ne 0) { throw "printcraft-cli tools exited $LASTEXITCODE" } + $body = 'BT /F1 24 Tf 20 150 Td (Hello ARM64) Tj ET' + $objs = @('<< /Type /Catalog /Pages 2 0 R >>', '<< /Type /Pages /Kids [3 0 R] /Count 1 >>', + '<< /Type /Page /Parent 2 0 R /MediaBox [0 0 300 200] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>', + "<< /Length $($body.Length) >>`nstream`n$body`nendstream", '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>') + $sb = [System.Text.StringBuilder]::new("%PDF-1.7`n"); $offsets = @() + for ($i = 0; $i -lt $objs.Count; $i++) { $offsets += $sb.Length; [void]$sb.Append("$($i + 1) 0 obj`n$($objs[$i])`nendobj`n") } + $xref = $sb.Length + [void]$sb.Append("xref`n0 $($objs.Count + 1)`n0000000000 65535 f `n") + foreach ($o in $offsets) { [void]$sb.Append(('{0:D10} 00000 n `n' -f $o)) } + [void]$sb.Append("trailer`n<< /Size $($objs.Count + 1) /Root 1 0 R >>`nstartxref`n$xref`n%%EOF`n") + New-Item -ItemType Directory -Force smoke | Out-Null + [System.IO.File]::WriteAllText("$PWD\smoke\hello.pdf", $sb.ToString(), [System.Text.Encoding]::ASCII) + $steps = '[{"tool":"doc_open","args":{"path":"hello.pdf"}},{"tool":"text_paragraphs","args":{"doc":1,"page":1}},{"tool":"text_edit","args":{"doc":1,"page":1,"paragraph":1,"text":"Edited on ARM64"}},{"tool":"page_render","args":{"doc":1,"page":1,"dpi":72},"out":"smoke/page.png"},{"tool":"doc_save","args":{"doc":1,"path":"out.pdf"}}]' + [System.IO.File]::WriteAllText("$PWD\smoke\steps.json", $steps) + $out = & $cli run --script smoke\steps.json --root smoke 2>&1 | Out-String + if ($LASTEXITCODE -ne 0) { Write-Output $out; throw "printcraft-cli run exited $LASTEXITCODE" } + if ($out -notmatch 'Hello ARM64') { Write-Output $out; throw 'text_paragraphs did not read the page' } + foreach ($f in 'smoke\page.png', 'smoke\out.pdf') { if (-not (Test-Path $f) -or (Get-Item $f).Length -eq 0) { Write-Output $out; throw "$f was not written" } } + Write-Output 'ok printcraft-cli: opened, read, edited, rendered and saved a PDF on ARM64' + $p = Start-Process msiexec.exe -ArgumentList '/x', "`"$msi`"", '/qn' -Wait -PassThru + if ($p.ExitCode -ne 0) { throw "msiexec /x exited $($p.ExitCode)" } + if (Test-Path (Join-Path $dir 'printcraft.exe')) { throw 'uninstall left printcraft.exe behind' } + Write-Output 'ok uninstalled' + + test: + name: test (native ARM64) + runs-on: windows-11-arm + timeout-minutes: 120 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + key: windows-arm64-test + - name: Test + run: cargo test --workspace --locked diff --git a/README.md b/README.md index 86d9ef598..9a6640954 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,7 @@ Press ⌘K to search every tool and command, or browse the **All tool ## Runs everywhere, stays yours -- **Native on macOS, Windows, Linux and FreeBSD**, and **in the browser** through WebAssembly, from the same Rust codebase. +- **Native on macOS, Windows, Linux and FreeBSD**, and **in the browser** through WebAssembly, from the same Rust codebase. Windows builds come for x64, x86 and ARM64 (Windows on ARM, no emulation); every ARM64 change is tested on ARM64 hardware in CI. - **Private by design.** Documents never leave your machine. There's no account, no telemetry and no cloud processing. - **Engine first.** Parsing, rendering and editing live in reusable library crates. The interface is one swappable layer on top. - **Scriptable.** The `printcraft-cli` tool (see [Built for agents, too](#built-for-agents-too)) covers inspecting, rendering, extracting text, editing, combining, extracting pages and splitting. Robustness sweeps run on the same engine as the app. diff --git a/packaging/windows/package.ps1 b/packaging/windows/package.ps1 index 71b23c72c..014363f66 100644 --- a/packaging/windows/package.ps1 +++ b/packaging/windows/package.ps1 @@ -17,9 +17,10 @@ .EXAMPLE pwsh packaging/windows/package.ps1 -Arch x64 pwsh packaging/windows/package.ps1 -Arch x86 -SkipBuild + pwsh packaging/windows/package.ps1 -Arch arm64 # cross-compiled; needs the MSVC ARM64 build tools #> param( - [ValidateSet('x64', 'x86')] [string] $Arch = 'x64', + [ValidateSet('x64', 'x86', 'arm64')] [string] $Arch = 'x64', [switch] $SkipBuild ) $ErrorActionPreference = 'Stop' @@ -44,7 +45,7 @@ if (-not $Version) { throw 'could not read [workspace.package] version from Carg # MSI ProductVersion is numeric (major.minor.build); pre-release tags are dropped there. $MsiVersion = ($Version -split '-')[0] -$Target = if ($Arch -eq 'x64') { 'x86_64-pc-windows-msvc' } else { 'i686-pc-windows-msvc' } +$Target = switch ($Arch) { 'x64' { 'x86_64-pc-windows-msvc' } 'x86' { 'i686-pc-windows-msvc' } 'arm64' { 'aarch64-pc-windows-msvc' } } $Dist = if ($env:DIST) { $env:DIST } else { Join-Path $Root 'dist\release' } $TargetDir = if ($env:CARGO_TARGET_DIR) { $env:CARGO_TARGET_DIR } else { Join-Path $Root 'target' } New-Item -ItemType Directory -Force -Path $Dist | Out-Null @@ -66,17 +67,21 @@ if (-not $SkipBuild) { $Bin = Join-Path $TargetDir "$Target\release" -# The PE optional header's Subsystem field: 2 = Windows GUI, 3 = console. The app must be GUI (no -# console window opens with it, #57); the CLI must stay console so its output reaches the terminal. -function Get-PeSubsystem([string] $Path) { +# Check both binaries' PE headers before packaging. Machine (COFF header) must match -Arch, so an +# x64 build can never ship labelled arm64 (#61). Subsystem (optional header): 2 = Windows GUI, +# 3 = console. The app must be GUI (no console window opens with it, #57); the CLI must stay +# console so its output reaches the terminal. +function Get-PeHeader([string] $Path) { $bytes = [System.IO.File]::ReadAllBytes($Path) $pe = [BitConverter]::ToInt32($bytes, 0x3C) - return [BitConverter]::ToUInt16($bytes, $pe + 0x5C) + return @{ Machine = [BitConverter]::ToUInt16($bytes, $pe + 4); Subsystem = [BitConverter]::ToUInt16($bytes, $pe + 0x5C) } } +$Machine = switch ($Arch) { 'x64' { 0x8664 } 'x86' { 0x14C } 'arm64' { 0xAA64 } } foreach ($check in @(@('printcraft.exe', 2), @('printcraft-cli.exe', 3))) { - $subsystem = Get-PeSubsystem (Join-Path $Bin $check[0]) - if ($subsystem -ne $check[1]) { throw "$($check[0]) has PE subsystem $subsystem, expected $($check[1])" } - Write-Output "ok $($check[0]): PE subsystem $subsystem" + $h = Get-PeHeader (Join-Path $Bin $check[0]) + if ($h.Machine -ne $Machine) { throw "$($check[0]) is for machine 0x$('{0:X}' -f $h.Machine), expected 0x$('{0:X}' -f $Machine) ($Arch)" } + if ($h.Subsystem -ne $check[1]) { throw "$($check[0]) has PE subsystem $($h.Subsystem), expected $($check[1])" } + Write-Output "ok $($check[0]): $Arch, PE subsystem $($h.Subsystem)" } $Stage = Join-Path $TargetDir "windows-package\$Arch" Remove-Item -Recurse -Force $Stage -ErrorAction SilentlyContinue From 3feea57cd7754bef6a5eec5a1fae88d051c59835 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Tue, 6 Oct 2026 07:41:47 -0400 Subject: [PATCH 2/6] package.ps1: skip the CLI smoke test for an ARM64 build on an x64 machine The cross-compiled ARM64 binary can't run on the x64 packaging runner; the windows-arm64 workflow installs and runs it on ARM64 instead. (x86 builds still run there: x64 Windows runs them.) Co-Authored-By: Claude Opus 5.5 --- packaging/windows/package.ps1 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packaging/windows/package.ps1 b/packaging/windows/package.ps1 index 014363f66..d75f8a4c9 100644 --- a/packaging/windows/package.ps1 +++ b/packaging/windows/package.ps1 @@ -114,5 +114,12 @@ $Zip = Join-Path $Dist "printcraft-$Version-windows-$Arch-portable.zip" Remove-Item -Force $Zip -ErrorAction SilentlyContinue Compress-Archive -Path $Portable -DestinationPath $Zip -Invoke-Native 'printcraft-cli --version' { & (Join-Path $Stage 'printcraft-cli.exe') --version } +# Smoke-test the CLI when this machine can run it. An ARM64 build made on an x64 runner can't run +# here; .github/workflows/windows-arm64.yml installs and runs it on ARM64 instead. +$HostArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant() +if ($Arch -ne 'arm64' -or $HostArch -eq 'arm64') { + Invoke-Native 'printcraft-cli --version' { & (Join-Path $Stage 'printcraft-cli.exe') --version } +} else { + Write-Output "skipping printcraft-cli --version: an $Arch build doesn't run on this $HostArch machine" +} Get-Item $Msi, $Zip | Format-Table Name, Length From 4ad43487ee86e5dca9c520bce69d8fba8a21a988 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Tue, 6 Oct 2026 08:23:15 -0400 Subject: [PATCH 3/6] TEMP: diagnose the intermittent ARM64 access violation in tests/view.rs --- .github/workflows/windows-arm64.yml | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/.github/workflows/windows-arm64.yml b/.github/workflows/windows-arm64.yml index bb05a3eb2..eddedf96c 100644 --- a/.github/workflows/windows-arm64.yml +++ b/.github/workflows/windows-arm64.yml @@ -124,3 +124,38 @@ jobs: key: windows-arm64-test - name: Test run: cargo test --workspace --locked + + # TEMPORARY (remove before merge): find the test in tests/view.rs that intermittently dies with + # STATUS_ACCESS_VIOLATION on ARM64. Single-threaded, so the last "test … " line names it. + diag-view: + name: diag view.rs crash (temporary) + runs-on: windows-11-arm + timeout-minutes: 120 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + key: windows-arm64-test + - name: Build the view tests + run: cargo test -p printcraft-ui-egui --test view --no-run + - name: Run them 30 times, single-threaded + shell: bash + run: | + crashes=0 + for i in $(seq 1 30); do + if ! cargo test -q -p printcraft-ui-egui --test view -- --test-threads=1 --nocapture > run.log 2>&1; then + crashes=$((crashes+1)) + echo "::warning::run $i failed; last tests started:" + grep -E "^test |STATUS_|panicked" run.log | tail -4 + fi + done + echo "crashes: $crashes / 30" + - name: Each test alone, 15 times + shell: bash + run: | + for t in $(cargo test -q -p printcraft-ui-egui --test view -- --list 2>/dev/null | sed -n 's/: test$//p'); do + n=0 + for i in $(seq 1 15); do cargo test -q -p printcraft-ui-egui --test view -- --exact "$t" > one.log 2>&1 || n=$((n+1)); done + echo "$t: $n / 15 failed" + done From 7afb93b06bd556eeb6964781ad4ecf78fb306c55 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Tue, 6 Oct 2026 08:41:24 -0400 Subject: [PATCH 4/6] TEMP: A/B the ARM64 crash: parallel vs parallel with big thread stacks --- .github/workflows/windows-arm64.yml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/.github/workflows/windows-arm64.yml b/.github/workflows/windows-arm64.yml index eddedf96c..dde218f12 100644 --- a/.github/workflows/windows-arm64.yml +++ b/.github/workflows/windows-arm64.yml @@ -139,23 +139,21 @@ jobs: key: windows-arm64-test - name: Build the view tests run: cargo test -p printcraft-ui-egui --test view --no-run - - name: Run them 30 times, single-threaded + - name: "A: parallel (the default), 30 runs" shell: bash run: | crashes=0 for i in $(seq 1 30); do - if ! cargo test -q -p printcraft-ui-egui --test view -- --test-threads=1 --nocapture > run.log 2>&1; then - crashes=$((crashes+1)) - echo "::warning::run $i failed; last tests started:" - grep -E "^test |STATUS_|panicked" run.log | tail -4 - fi + cargo test -q -p printcraft-ui-egui --test view > run.log 2>&1 || { crashes=$((crashes+1)); grep -E "STATUS_|panicked" run.log | head -2; } done - echo "crashes: $crashes / 30" - - name: Each test alone, 15 times + echo "A parallel: crashes $crashes / 30" + - name: "B: parallel with 16 MB test-thread stacks, 30 runs" shell: bash + env: + RUST_MIN_STACK: "16777216" run: | - for t in $(cargo test -q -p printcraft-ui-egui --test view -- --list 2>/dev/null | sed -n 's/: test$//p'); do - n=0 - for i in $(seq 1 15); do cargo test -q -p printcraft-ui-egui --test view -- --exact "$t" > one.log 2>&1 || n=$((n+1)); done - echo "$t: $n / 15 failed" + crashes=0 + for i in $(seq 1 30); do + cargo test -q -p printcraft-ui-egui --test view > run.log 2>&1 || { crashes=$((crashes+1)); grep -E "STATUS_|panicked" run.log | head -2; } done + echo "B big stacks: crashes $crashes / 30" From c3007277ef373ebd10771ae9d0ce7253dbbe404e Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Tue, 6 Oct 2026 08:55:59 -0400 Subject: [PATCH 5/6] TEMP: capture a crash dump of the ARM64 view-test crash and print its stack --- .github/workflows/windows-arm64.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/windows-arm64.yml b/.github/workflows/windows-arm64.yml index dde218f12..d6acf271d 100644 --- a/.github/workflows/windows-arm64.yml +++ b/.github/workflows/windows-arm64.yml @@ -139,6 +139,25 @@ jobs: key: windows-arm64-test - name: Build the view tests run: cargo test -p printcraft-ui-egui --test view --no-run + - name: Crash dump of the view tests, with the crashing stack + shell: pwsh + run: | + $dumps = "$env:RUNNER_TEMP\dumps"; New-Item -ItemType Directory -Force $dumps | Out-Null + $key = 'HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps' + New-Item -Force $key | Out-Null + Set-ItemProperty $key DumpFolder $dumps; Set-ItemProperty $key DumpType 2; Set-ItemProperty $key DumpCount 3 + $exe = (Get-ChildItem target\debug\deps\view-*.exe | Sort-Object LastWriteTime | Select-Object -Last 1).FullName + Write-Output "binary: $exe" + for ($i = 1; $i -le 60; $i++) { + & $exe --quiet *> $null + if ($LASTEXITCODE -ne 0) { Write-Output "crashed on run $i (exit $LASTEXITCODE)"; Start-Sleep 5; if (Get-ChildItem $dumps -Filter *.dmp) { break } } + } + $dump = Get-ChildItem $dumps -Filter *.dmp | Select-Object -First 1 + if (-not $dump) { Write-Output 'no dump written'; exit 0 } + $cdb = Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\Debuggers' -Recurse -Filter cdb.exe -ErrorAction SilentlyContinue | Sort-Object { $_.FullName -notmatch 'arm64' } | Select-Object -First 1 + if (-not $cdb) { Write-Output 'cdb not found'; Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10' -Directory | Out-String; exit 0 } + Write-Output "cdb: $($cdb.FullName)" + & $cdb.FullName -z $dump.FullName -y "srv*;$(Split-Path $exe)" -c ".ecxr; kn 40; ~* kn 12; q" 2>&1 | Select-Object -Last 220 - name: "A: parallel (the default), 30 runs" shell: bash run: | From 5112e34f371bcc3c4e5948963bf21c97e228d899 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Tue, 6 Oct 2026 09:08:36 -0400 Subject: [PATCH 6/6] TEMP: print the faulting thread and the loaded graphics modules --- .github/workflows/windows-arm64.yml | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/.github/workflows/windows-arm64.yml b/.github/workflows/windows-arm64.yml index d6acf271d..b019fc730 100644 --- a/.github/workflows/windows-arm64.yml +++ b/.github/workflows/windows-arm64.yml @@ -157,22 +157,4 @@ jobs: $cdb = Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\Debuggers' -Recurse -Filter cdb.exe -ErrorAction SilentlyContinue | Sort-Object { $_.FullName -notmatch 'arm64' } | Select-Object -First 1 if (-not $cdb) { Write-Output 'cdb not found'; Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10' -Directory | Out-String; exit 0 } Write-Output "cdb: $($cdb.FullName)" - & $cdb.FullName -z $dump.FullName -y "srv*;$(Split-Path $exe)" -c ".ecxr; kn 40; ~* kn 12; q" 2>&1 | Select-Object -Last 220 - - name: "A: parallel (the default), 30 runs" - shell: bash - run: | - crashes=0 - for i in $(seq 1 30); do - cargo test -q -p printcraft-ui-egui --test view > run.log 2>&1 || { crashes=$((crashes+1)); grep -E "STATUS_|panicked" run.log | head -2; } - done - echo "A parallel: crashes $crashes / 30" - - name: "B: parallel with 16 MB test-thread stacks, 30 runs" - shell: bash - env: - RUST_MIN_STACK: "16777216" - run: | - crashes=0 - for i in $(seq 1 30); do - cargo test -q -p printcraft-ui-egui --test view > run.log 2>&1 || { crashes=$((crashes+1)); grep -E "STATUS_|panicked" run.log | head -2; } - done - echo "B big stacks: crashes $crashes / 30" + & $cdb.FullName -z $dump.FullName -y "srv*;$(Split-Path $exe)" -c ".lines -d; .echo ===FAULT===; .ecxr; kn 30; .echo ===MODULES===; lm m d3d*; lm m dxgi*; lm m *warp*; lm m nv*; lm m qc*; .echo ===END===; q" 2>&1 | Select-String -Pattern '===|!|ExceptionCode|Attempt|d3d|dxgi|warp|start' | Select-Object -First 120