diff --git a/Cargo.lock b/Cargo.lock index dada7d97f8..dda10e2edc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9163,6 +9163,7 @@ name = "remote_server" version = "0.1.0" dependencies = [ "anyhow", + "backtrace", "cargo_toml", "clap", "client", diff --git a/crates/remote_server/Cargo.toml b/crates/remote_server/Cargo.toml index 211b76e091..bd61919151 100644 --- a/crates/remote_server/Cargo.toml +++ b/crates/remote_server/Cargo.toml @@ -22,6 +22,7 @@ test-support = ["fs/test-support"] [dependencies] anyhow.workspace = true +backtrace = "0.3" clap.workspace = true client.workspace = true env_logger.workspace = true diff --git a/crates/remote_server/src/main.rs b/crates/remote_server/src/main.rs index e5582d9b1f..872af42596 100644 --- a/crates/remote_server/src/main.rs +++ b/crates/remote_server/src/main.rs @@ -37,7 +37,7 @@ fn main() { #[cfg(not(windows))] fn main() -> Result<()> { - use remote_server::unix::{execute_proxy, execute_run, init_logging}; + use remote_server::unix::{execute_proxy, execute_run, init}; let cli = Cli::parse(); @@ -48,11 +48,11 @@ fn main() -> Result<()> { stdin_socket, stdout_socket, }) => { - init_logging(Some(log_file))?; + init(Some(log_file))?; execute_run(pid_file, stdin_socket, stdout_socket) } Some(Commands::Proxy { identifier }) => { - init_logging(None)?; + init(None)?; execute_proxy(identifier) } Some(Commands::Version) => { diff --git a/crates/remote_server/src/unix.rs b/crates/remote_server/src/unix.rs index 74b71a2277..049b0a003e 100644 --- a/crates/remote_server/src/unix.rs +++ b/crates/remote_server/src/unix.rs @@ -20,7 +20,13 @@ use std::{ sync::Arc, }; -pub fn init_logging(log_file: Option) -> Result<()> { +pub fn init(log_file: Option) -> Result<()> { + init_logging(log_file)?; + init_panic_hook(); + Ok(()) +} + +fn init_logging(log_file: Option) -> Result<()> { if let Some(log_file) = log_file { let target = Box::new(if log_file.exists() { std::fs::OpenOptions::new() @@ -46,6 +52,45 @@ pub fn init_logging(log_file: Option) -> Result<()> { Ok(()) } +fn init_panic_hook() { + std::panic::set_hook(Box::new(|info| { + let payload = info + .payload() + .downcast_ref::<&str>() + .map(|s| s.to_string()) + .or_else(|| info.payload().downcast_ref::().cloned()) + .unwrap_or_else(|| "Box".to_string()); + + let backtrace = backtrace::Backtrace::new(); + let mut backtrace = backtrace + .frames() + .iter() + .flat_map(|frame| { + frame + .symbols() + .iter() + .filter_map(|frame| Some(format!("{:#}", frame.name()?))) + }) + .collect::>(); + + // Strip out leading stack frames for rust panic-handling. + if let Some(ix) = backtrace + .iter() + .position(|name| name == "rust_begin_unwind") + { + backtrace.drain(0..=ix); + } + + log::error!( + "server: panic occurred: {}\nBacktrace:\n{}", + payload, + backtrace.join("\n") + ); + + std::process::abort(); + })); +} + fn start_server( stdin_listener: UnixListener, stdout_listener: UnixListener,