bootcheck: reap a QEMU left behind by a killed run
The `finally` stops QEMU on every path the script controls, but not when the script itself is killed -- a CI timeout, Ctrl-C at the wrong moment, pkill. QEMU then keeps running with no parent. Because these tests run under TCG rather than KVM, a spare emulator pinning a core slows every later run, and one was found still executing the guest at 86% of a core eight minutes after its parent was gone. That is the bad kind of failure: the suite does not break, it gets slower, and commands drift past Wait-Prompt's timeout until the next one is typed into a busy shell and the transcript interleaves. The check that then fails is unrelated to whatever actually changed. Record the pid on startup and reap the previous run's leftover before starting a new one, the same self-healing approach already used for the monitor port. The name is checked before killing, because pids are recycled and killing an unrelated process would be worse than the leak. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
0750e7cbf4
commit
4e0478c21a
1 changed files with 34 additions and 0 deletions
|
|
@ -85,9 +85,39 @@ $hda = Join-Path $root 'schos.img'
|
|||
# choice -- a kernel handed anything else on device 1 panics in forkret().
|
||||
$hdb = Join-Path $root 'build/schfs.img'
|
||||
$log = Join-Path $root 'build/bootcheck.log'
|
||||
# Where the running QEMU's pid is recorded, so the *next* run can clean up after
|
||||
# one that never got to its `finally` -- see reap_stale_qemu below.
|
||||
$pidfile = Join-Path $root 'build/bootcheck-qemu.pid'
|
||||
|
||||
Remove-Item -Force $log -ErrorAction SilentlyContinue
|
||||
|
||||
# Kill a QEMU left over from a previous run.
|
||||
#
|
||||
# The `finally` at the bottom stops QEMU on every path the script controls, but
|
||||
# not when the script itself is killed -- a CI timeout, Ctrl-C at the wrong
|
||||
# moment, `pkill pwsh`. QEMU then keeps running with no parent, and because
|
||||
# these tests run under TCG rather than KVM, a spare emulator pinning a core
|
||||
# makes every *later* run slower. Commands that normally answer in a second
|
||||
# drift past Wait-Prompt's timeout, the next command is typed into a busy
|
||||
# shell, and the transcript interleaves -- which surfaces as a failed check
|
||||
# somewhere unrelated to whatever actually changed. A test environment that
|
||||
# silently degrades is worse than one that fails loudly, so each run reaps the
|
||||
# last one's leftovers before starting.
|
||||
function Reap-StaleQemu {
|
||||
if (-not (Test-Path $pidfile)) { return }
|
||||
$stale = (Get-Content $pidfile -Raw -ErrorAction SilentlyContinue).Trim()
|
||||
Remove-Item -Force $pidfile -ErrorAction SilentlyContinue
|
||||
if (-not $stale) { return }
|
||||
$proc = Get-Process -Id ([int]$stale) -ErrorAction SilentlyContinue
|
||||
# Check the name too: pids are recycled, and killing an unrelated process
|
||||
# because it inherited the number would be a far worse bug than the leak.
|
||||
if ($proc -and $proc.ProcessName -like 'qemu*') {
|
||||
Write-Host " reaping leaked QEMU from a previous run (pid $stale)" -ForegroundColor Yellow
|
||||
try { $proc.Kill() } catch { }
|
||||
}
|
||||
}
|
||||
Reap-StaleQemu
|
||||
|
||||
# The default run exercises the *root* filesystem: create a file, read it back,
|
||||
# grow it, list a directory, seek around it. Booting to a prompt proves nothing
|
||||
# on its own, and every one of these has broken at some point while the boot
|
||||
|
|
@ -274,6 +304,9 @@ $psi.UseShellExecute = $false
|
|||
$what = if ($Iso) { 'schos.iso, no hard disk' } else { "hdb = $(Split-Path $hdb -Leaf)" }
|
||||
Write-Host "==> booting ($what)" -ForegroundColor Cyan
|
||||
$p = [System.Diagnostics.Process]::Start($psi)
|
||||
# Recorded immediately, so that a run killed one instruction later is still
|
||||
# cleaned up by the next one.
|
||||
Set-Content -Path $pidfile -Value $p.Id
|
||||
|
||||
try {
|
||||
Start-Sleep -Seconds $BootSeconds # wait for the shell prompt
|
||||
|
|
@ -401,6 +434,7 @@ try {
|
|||
} finally {
|
||||
if (-not $p.HasExited) { $p.Kill() }
|
||||
Start-Sleep -Milliseconds 800
|
||||
Remove-Item -Force $pidfile -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
if (-not (Test-Path $log)) { throw "no serial log produced at $log" }
|
||||
|
|
|
|||
Loading…
Reference in a new issue