# Boot Schunix, crash it on purpose, and capture the crash screen as an image. # # The crash screen is the one part of the system you cannot look at by using # the system, so it is the one part that quietly rots. This drives it through # /proc/sysrq and takes a picture, which turns "does the panic screen still # render" into something answerable in thirty seconds. # # QEMU's monitor is on its own socket so the serial console stays free for the # shell. screendump writes a PPM; the PNG conversion is optional and skipped if # ImageMagick is not around. # # tools\crashshot.ps1 crash the ramdisk-root build # tools\crashshot.ps1 -SchFS with a SchFS volume attached param( [switch]$SchFS, [int]$BootSeconds = 20, [string]$Out = "build\crashscreen.ppm", [string]$Qemu = "$env:USERPROFILE\scoop\apps\qemu\current\qemu-system-x86_64.exe" ) $ErrorActionPreference = 'Stop' $root = Split-Path $PSScriptRoot -Parent $hda = Join-Path $root 'schunix.img' $hdb = if ($SchFS) { Join-Path $root 'build\schfs.img' } else { Join-Path $root 'build\fs.img' } $log = Join-Path $root 'build\crashshot.log' $ppm = Join-Path $root $Out $mon = Join-Path $root 'build\qemu-mon.sock' Remove-Item -Force $log, $ppm -ErrorAction SilentlyContinue # The monitor goes to a TCP port rather than a unix socket: PowerShell can open # a TCP client without any extra machinery, and QEMU on Windows has no unix # sockets anyway. $monPort = 55555 $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.FileName = $Qemu $psi.Arguments = @( "-drive file=`"$hda`",format=raw,index=0,media=disk" "-drive file=`"$hdb`",format=raw,index=1,media=disk" '-m 1G -smp 1 -cpu qemu64,+rdtscp -machine pc-i440fx-9.2' '-display none' "-chardev stdio,id=c0,logfile=`"$log`"" '-serial chardev:c0' "-monitor tcp:127.0.0.1:$monPort,server,nowait" ) -join ' ' $psi.RedirectStandardInput = $true $psi.UseShellExecute = $false Write-Host "==> booting" -ForegroundColor Cyan $p = [System.Diagnostics.Process]::Start($psi) function Send-Line($text) { foreach ($ch in ($text + "`n").ToCharArray()) { $p.StandardInput.Write($ch) $p.StandardInput.Flush() Start-Sleep -Milliseconds 30 # serial has no flow control } } try { Start-Sleep -Seconds $BootSeconds # Put something on the display first. The whole design of the crash screen # is that it dims what was there rather than clearing it, so a blank screen # underneath would prove nothing. Write-Host " > filling the screen with normal output" -ForegroundColor DarkGray Send-Line 'ls /bin' Start-Sleep -Seconds 3 Send-Line 'cat /proc/cpuinfo' Start-Sleep -Seconds 2 Write-Host " > echo c > /proc/sysrq" -ForegroundColor DarkGray Send-Line 'echo c > /proc/sysrq' Start-Sleep -Seconds 4 Write-Host "==> capturing the framebuffer" -ForegroundColor Cyan $client = New-Object System.Net.Sockets.TcpClient('127.0.0.1', $monPort) $stream = $client.GetStream() $writer = New-Object System.IO.StreamWriter($stream) $writer.AutoFlush = $true Start-Sleep -Milliseconds 500 # QEMU on Windows wants a native path here, not the one PowerShell shows. $writer.WriteLine("screendump $ppm") Start-Sleep -Seconds 2 $writer.WriteLine('quit') Start-Sleep -Milliseconds 500 $client.Close() } finally { if (-not $p.HasExited) { $p.Kill() } Start-Sleep -Milliseconds 800 } if (Test-Path $log) { $text = Get-Content $log -Raw Write-Host "==> serial transcript" -ForegroundColor Cyan # Only the crash report, not the whole boot. $idx = $text.IndexOf('Schunix stopped') if ($idx -ge 0) { Write-Host $text.Substring($idx) } else { Write-Host $text } } if (Test-Path $ppm) { $size = (Get-Item $ppm).Length Write-Host "==> $ppm ($size bytes)" -ForegroundColor Green $png = [System.IO.Path]::ChangeExtension($ppm, '.png') $magick = Get-Command magick -ErrorAction SilentlyContinue if ($magick) { & magick $ppm $png Write-Host "==> $png" -ForegroundColor Green } } else { Write-Host "FAIL: no screendump was produced" -ForegroundColor Red exit 1 }