Improve backtraces when hovering widgets with modifiers pressed (#4696)

A useful debug-feature in egui is pressing down all modifiers keys and
hovering any widget to see its backtrace (only in `dev` builds).
Unfortunately this is incompatible with `panic="abort"`, something I
just now discovered.

So I removed `panic="abort"` from `Cargo.toml` for `dev` builds. If the
backtrace returns empty-handed, I also suggests this as a fix to the
user. Finally, I cleaned up the backtraces a bit, making them slightly
shorter and more readable.
This commit is contained in:
Emil Ernerfeldt 2024-06-23 17:03:42 +02:00 committed by GitHub
parent b1dc059ef3
commit db8db50bf3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 46 additions and 12 deletions

View File

@ -43,7 +43,10 @@ panic = "abort" # This leads to better optimizations and smaller binaries (and i
# split-debuginfo = "unpacked" # faster debug builds on mac
# opt-level = 1 # Make debug builds run faster
panic = "abort" # This leads to better optimizations and smaller binaries (and is the default in Wasm anyways).
# panic = "abort" leads to better optimizations and smaller binaries (and is the default in Wasm anyways),
# but it also means backtraces don't work with the `backtrace` library (https://github.com/rust-lang/backtrace-rs/issues/397).
# egui has a feature where if you hold down all modifiers keys on your keyboard and hover any UI widget,
# you will see the backtrace to that widget, and we don't want to break that feature in dev builds.
[profile.dev.package."*"]
# Optimize all dependencies even in debug builds (does not affect workspace packages):

View File

@ -10,6 +10,7 @@ struct Frame {
///
/// In particular: slips everything before `egui::Context::run`,
/// and skipping all frames in the `egui::` namespace.
#[inline(never)]
pub fn capture() -> String {
let mut frames = vec![];
let mut depth = 0;
@ -28,7 +29,7 @@ pub fn capture() -> String {
let name = symbol
.name()
.map(|name| name.to_string())
.map(|name| clean_symbol_name(name.to_string()))
.unwrap_or_default();
frames.push(Frame {
@ -44,12 +45,13 @@ pub fn capture() -> String {
});
if frames.is_empty() {
return Default::default();
return
"Failed to capture a backtrace. A common cause of this is compiling with panic=\"abort\" (https://github.com/rust-lang/backtrace-rs/issues/397)".to_owned();
}
// Inclusive:
let mut min_depth = 0;
let mut max_depth = frames.len() - 1;
let mut max_depth = usize::MAX;
for frame in &frames {
if frame.name.starts_with("egui::callstack::capture") {
@ -60,14 +62,23 @@ pub fn capture() -> String {
}
}
/// Is this the name of some sort of useful entry point?
fn is_start_name(name: &str) -> bool {
name == "main"
|| name == "_main"
|| name.starts_with("eframe::run_native")
|| name.starts_with("egui::context::Context::run")
}
let mut has_kept_any_start_names = false;
frames.reverse(); // main on top, i.e. chronological order. Same as Python.
// Remove frames that are uninteresting:
frames.retain(|frame| {
// Keep some special frames to give the user a sense of chronology:
if frame.name == "main"
|| frame.name == "_main"
|| frame.name.starts_with("egui::context::Context::run")
|| frame.name.starts_with("eframe::run_native")
{
// Keep the first "start" frame we can detect (e.g. `main`) to give the user a sense of chronology:
if is_start_name(&frame.name) && !has_kept_any_start_names {
has_kept_any_start_names = true;
return true;
}
@ -96,8 +107,6 @@ pub fn capture() -> String {
true
});
frames.reverse(); // main on top, i.e. chronological order. Same as Python.
let mut deepest_depth = 0;
let mut widest_file_line = 0;
for frame in &frames {
@ -135,6 +144,28 @@ pub fn capture() -> String {
formatted
}
fn clean_symbol_name(mut s: String) -> String {
// We get a hex suffix (at least on macOS) which is quite unhelpful,
// e.g. `my_crate::my_function::h3bedd97b1e03baa5`.
// Let's strip that.
if let Some(h) = s.rfind("::h") {
let hex = &s[h + 3..];
if hex.len() == 16 && hex.chars().all(|c| c.is_ascii_hexdigit()) {
s.truncate(h);
}
}
s
}
#[test]
fn test_clean_symbol_name() {
assert_eq!(
clean_symbol_name("my_crate::my_function::h3bedd97b1e03baa5".to_owned()),
"my_crate::my_function"
);
}
/// Shorten a path to a Rust source file from a callstack.
///
/// Example input: