fix(host): crash visibility + Windows deliberate-quit teardown; pin outranks quit
Two strands from the "Windows host went Offline after reconnect, zero errors in the logs" investigation. Crash visibility — the field reports kept arriving with nothing in the Logs tab because every way the host can die silently was unlogged: - A panic hook tees each panic (thread, location, backtrace) through `tracing` into the in-memory ring + host.log before the default hook runs — a panicking thread otherwise only hit stderr, absent from the web console and gone when stderr is detached. - A last-resort Windows SEH filter (windows/crash.rs) logs an unhandled native exception with its code, faulting address, and faulting MODULE — the smoking gun that separates our bug from a GPU runtime/driver DLL (amfrt64, the UMD, d3d11) crashing under us. - A 10 s watchdog around the vdisplay monitor teardown logs an ERROR when the driver REMOVE/CCD-restore looks wedged (it runs under the manager state lock, so a hang there silently blocks every future acquire). Deliberate-quit teardown (Windows pf-vdisplay) — the Windows manager never wired the linger skip the Linux registry has: on ⌘D (the QUIT close code) the monitor lingered 10 s, so a quick reconnect hit the Lingering-preempt's back-to-back REMOVE→ADD churn. A per-lease quit flag (VirtualDisplay:: set_quit_flag) now tears the monitor down immediately on a deliberate quit, so the reconnect finds the manager Idle and does a clean fresh ADD. Pin outranks quit (both platforms) — keep_alive=forever (the gaming-rig preset) promises "the screen stays alive", so a deliberate quit must skip only the linger WINDOW, never the pin. Windows release() checks keep_alive_forever() before the quit; the Linux registry had the inverse bug (force_immediate tore down even a pinned display) — fixed via effective_linger() with a unit test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,9 @@ mod discovery;
|
||||
mod wol;
|
||||
// Goal-1 stage 6: top-level platform-only modules live under `src/linux/` and `src/windows/`; `#[path]`
|
||||
// keeps the `crate::*` module names flat (every existing path is unchanged).
|
||||
#[cfg(target_os = "windows")]
|
||||
#[path = "windows/crash.rs"]
|
||||
mod crash;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[path = "linux/dmabuf_fence.rs"]
|
||||
mod dmabuf_fence;
|
||||
@@ -111,6 +114,35 @@ fn main() {
|
||||
.init();
|
||||
}
|
||||
|
||||
// Tee every panic through `tracing` BEFORE the default hook: a panicking thread otherwise
|
||||
// prints only to stderr — absent from the web console's Logs tab (the ring) and gone entirely
|
||||
// when stderr is detached — so a field report reads "host died, zero errors in the logs".
|
||||
// The default hook still runs afterwards for the usual stderr message/abort behavior.
|
||||
let default_panic = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
// Manual payload downcast (`payload_as_str` needs Rust 1.91; workspace MSRV is 1.82).
|
||||
let payload = info
|
||||
.payload()
|
||||
.downcast_ref::<&str>()
|
||||
.copied()
|
||||
.or_else(|| info.payload().downcast_ref::<String>().map(String::as_str))
|
||||
.unwrap_or("<non-string panic payload>");
|
||||
tracing::error!(
|
||||
thread = std::thread::current().name().unwrap_or("<unnamed>"),
|
||||
location = %info
|
||||
.location()
|
||||
.map(ToString::to_string)
|
||||
.unwrap_or_else(|| "<unknown>".into()),
|
||||
backtrace = %std::backtrace::Backtrace::force_capture(),
|
||||
"PANIC: {payload}"
|
||||
);
|
||||
default_panic(info);
|
||||
}));
|
||||
// Native crashes (an access violation inside a GPU runtime/driver DLL) are logged by a
|
||||
// last-resort SEH filter for the same reason — they otherwise kill the host with no trace.
|
||||
#[cfg(target_os = "windows")]
|
||||
crash::install();
|
||||
|
||||
if let Err(e) = real_main() {
|
||||
tracing::error!("{e:#}");
|
||||
std::process::exit(1);
|
||||
|
||||
Reference in New Issue
Block a user