From e9aec1d67a579eb210cd30b74726fdeb3b381404 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 15 Jun 2023 14:09:42 -0700 Subject: [PATCH] Don't rely on debug symbols for panic reporting (#2615) This fixes a regression introduced in https://github.com/zed-industries/zed/pull/2560, where panic reports did not include backtraces. The problem was that in that PR, I assumed we could retrieve file paths for symbols in our backtraces. But actually, that functionality only works when the app is built locally, and a `.dSYM` file can be magically found by the OS. We don't ship those dSYM files with Zed, so panic symbols do not have file paths available. Panic backtraces will still be more useful and less noisy than before though: we will strip out frames for which we don't have symbol names, and remove leading panic-handling stack frames from the backtraces. Release Notes: - N/A --- crates/zed/src/main.rs | 69 ++++++------------------------------------ 1 file changed, 10 insertions(+), 59 deletions(-) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 73a3346a9adc75b61af3ab4e5f50b4484129bb1b..c58060ae2f657742f0d57643537b4a2054e46d9e 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -31,7 +31,6 @@ use std::{ ffi::OsStr, fs::OpenOptions, io::Write as _, - ops::Not, os::unix::prelude::OsStrExt, panic, path::{Path, PathBuf}, @@ -373,7 +372,6 @@ struct Panic { os_version: Option, architecture: String, panicked_on: u128, - identifying_backtrace: Option>, } #[derive(Serialize)] @@ -401,61 +399,18 @@ fn init_panic_hook(app: &App) { .unwrap_or_else(|| "Box".to_string()); let backtrace = Backtrace::new(); - let backtrace = backtrace + let mut backtrace = backtrace .frames() .iter() - .filter_map(|frame| { - let symbol = frame.symbols().first()?; - let path = symbol.filename()?; - Some((path, symbol.lineno(), format!("{:#}", symbol.name()?))) - }) + .filter_map(|frame| Some(format!("{:#}", frame.symbols().first()?.name()?))) .collect::>(); - let this_file_path = Path::new(file!()); - - // Find the first frame in the backtrace for this panic hook itself. Exclude - // that frame and all frames before it. - let mut start_frame_ix = 0; - let mut codebase_root_path = None; - for (ix, (path, _, _)) in backtrace.iter().enumerate() { - if path.ends_with(this_file_path) { - start_frame_ix = ix + 1; - codebase_root_path = path.ancestors().nth(this_file_path.components().count()); - break; - } - } - - // Exclude any subsequent frames inside of rust's panic handling system. - while let Some((path, _, _)) = backtrace.get(start_frame_ix) { - if path.starts_with("/rustc") { - start_frame_ix += 1; - } else { - break; - } - } - - // Build two backtraces: - // * one for display, which includes symbol names for all frames, and files - // and line numbers for symbols in this codebase - // * one for identification and de-duplication, which only includes symbol - // names for symbols in this codebase. - let mut display_backtrace = Vec::new(); - let mut identifying_backtrace = Vec::new(); - for (path, line, symbol) in &backtrace[start_frame_ix..] { - display_backtrace.push(symbol.clone()); - - if let Some(codebase_root_path) = &codebase_root_path { - if let Ok(suffix) = path.strip_prefix(&codebase_root_path) { - identifying_backtrace.push(symbol.clone()); - - let display_path = suffix.to_string_lossy(); - if let Some(line) = line { - display_backtrace.push(format!(" {display_path}:{line}")); - } else { - display_backtrace.push(format!(" {display_path}")); - } - } - } + // 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); } let panic_data = Panic { @@ -477,11 +432,7 @@ fn init_panic_hook(app: &App) { .duration_since(UNIX_EPOCH) .unwrap() .as_millis(), - backtrace: display_backtrace, - identifying_backtrace: identifying_backtrace - .is_empty() - .not() - .then_some(identifying_backtrace), + backtrace, }; if let Some(panic_data_json) = serde_json::to_string_pretty(&panic_data).log_err() { @@ -498,7 +449,7 @@ fn init_panic_hook(app: &App) { .open(&panic_file_path) .log_err(); if let Some(mut panic_file) = panic_file { - write!(&mut panic_file, "{}", panic_data_json).log_err(); + write!(&mut panic_file, "{}\n", panic_data_json).log_err(); panic_file.flush().log_err(); } }