diff --git a/src/server.rs b/src/server.rs index 239c96994..a3a888f3e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -36,6 +36,7 @@ use futures::future::FutureExt; use futures::{future, stream, Sink, SinkExt, Stream, StreamExt, TryFutureExt}; use number_prefix::NumberPrefix; use serde::{Deserialize, Serialize}; +use std::cell::Cell; use std::collections::HashMap; use std::env; use std::ffi::{OsStr, OsString}; @@ -392,12 +393,27 @@ impl DistClientContainer { } } +thread_local! { + /// catch_unwind doesn't provide panic location, so we store that + /// information via a panic hook to be used when catch_unwind + /// catches a panic. + static PANIC_LOCATION: Cell> = Cell::new(None); +} + /// Start an sccache server, listening on `port`. /// /// Spins an event loop handling client connections until a client /// requests a shutdown. pub fn start_server(config: &Config, port: u16) -> Result<()> { info!("start_server: port: {}", port); + let panic_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + PANIC_LOCATION.set( + info.location() + .map(|loc| (loc.file().to_string(), loc.line(), loc.column())), + ); + panic_hook(info) + })); let client = unsafe { Client::new() }; let runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() @@ -1182,20 +1198,35 @@ where let task = async move { let dist_client = me.dist_client.get_client().await; let result = match dist_client { - Ok(client) => { - hasher - .get_cached_or_compile( - client, - creator, - storage, - arguments, - cwd, - env_vars, - cache_control, - pool, + Ok(client) => std::panic::AssertUnwindSafe(hasher.get_cached_or_compile( + client, + creator, + storage, + arguments, + cwd, + env_vars, + cache_control, + pool, + )) + .catch_unwind() + .await + .map_err(|e| { + let panic = e + .downcast_ref::<&str>() + .map(|s| &**s) + .or_else(|| e.downcast_ref::().map(|s| &**s)) + .unwrap_or_else(|| "An unknown panic was caught."); + let thread = std::thread::current(); + let thread_name = thread.name().unwrap_or("unnamed"); + if let Some((file, line, column)) = PANIC_LOCATION.take() { + anyhow!( + "thread '{thread_name}' panicked at {file}:{line}:{column}: {panic}" ) - .await - } + } else { + anyhow!("thread '{thread_name}' panicked: {panic}") + } + }) + .and_then(std::convert::identity), Err(e) => Err(e), }; let mut cache_write = None;