Implement panic logging for better debugging
This commit is contained in:
@@ -122,6 +122,7 @@ impl Drop for SimpleCallOnReturn {
|
||||
}
|
||||
|
||||
pub fn global_init() -> bool {
|
||||
install_panic_hook();
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if !crate::platform::linux::is_x11() {
|
||||
@@ -131,6 +132,69 @@ pub fn global_init() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Capture Rust panics so they survive the windows-subsystem build.
|
||||
///
|
||||
/// In release we have `panic = 'abort'` and `windows_subsystem = "windows"`,
|
||||
/// which means a panic prints to stderr (which is null when no console is
|
||||
/// attached) and immediately calls `abort()`. The flexi_logger file never
|
||||
/// sees the panic, and on Windows the abort doesn't reliably surface in the
|
||||
/// Application event log either, so the process appears to vanish.
|
||||
///
|
||||
/// This hook routes every panic to two places before the abort:
|
||||
/// 1. `log::error!` — works once `init_log` has set up flexi_logger.
|
||||
/// 2. `Config::log_path()/panic.log` — fallback for panics that fire before
|
||||
/// `init_log` runs (e.g. inside `load_custom_client` or `bootstrap`).
|
||||
/// The log dir is per-user/per-app on every platform and is writable by
|
||||
/// the user-session `--server` process; the install dir is not.
|
||||
fn install_panic_hook() {
|
||||
use std::sync::Once;
|
||||
static INIT: Once = Once::new();
|
||||
INIT.call_once(|| {
|
||||
let prev = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
let payload: &str = if let Some(s) = info.payload().downcast_ref::<&'static str>() {
|
||||
s
|
||||
} else if let Some(s) = info.payload().downcast_ref::<String>() {
|
||||
s.as_str()
|
||||
} else {
|
||||
"<non-string panic payload>"
|
||||
};
|
||||
let location = info
|
||||
.location()
|
||||
.map(|l| format!("{}:{}:{}", l.file(), l.line(), l.column()))
|
||||
.unwrap_or_else(|| "<unknown>".into());
|
||||
let backtrace = std::backtrace::Backtrace::force_capture();
|
||||
let exe = std::env::current_exe()
|
||||
.ok()
|
||||
.and_then(|p| p.to_str().map(|s| s.to_owned()))
|
||||
.unwrap_or_default();
|
||||
let pid = std::process::id();
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let header = format!(
|
||||
"PANIC pid={pid} exe={exe} args={args:?}\n at {location}\n payload: {payload}"
|
||||
);
|
||||
log::error!("{header}\n backtrace:\n{backtrace}");
|
||||
let mut path = config::Config::log_path();
|
||||
let _ = std::fs::create_dir_all(&path);
|
||||
path.push("panic.log");
|
||||
if let Ok(mut f) = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&path)
|
||||
{
|
||||
use std::io::Write;
|
||||
let ts = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_secs())
|
||||
.unwrap_or(0);
|
||||
let _ = writeln!(f, "[unix={ts}] {header}\n backtrace:\n{backtrace}\n");
|
||||
let _ = f.flush();
|
||||
}
|
||||
prev(info);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn global_clean() {}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -5,6 +5,7 @@ fn main() {}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn main() {
|
||||
crate::common::global_init();
|
||||
crate::common::load_custom_client();
|
||||
hbb_common::init_log(false, "service");
|
||||
crate::start_os_service();
|
||||
|
||||
Reference in New Issue
Block a user