-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ignore more frames on backtrace unwinding. #40264
Changes from all commits
680e2b7
2183ef6
dfaed07
c40ea76
33e42a3
d54caab
3353a42
deeaa73
7d667e4
00b991e
5205b8d
8a75b20
03f9940
b36446a
0e16333
e1ca626
d72937d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -93,6 +93,8 @@ fn _print(w: &mut Write, format: PrintFormat) -> io::Result<()> { | |
Ok(()) | ||
} | ||
|
||
/// Returns a number of frames to remove at the beginning and at the end of the | ||
/// backtrace, according to the backtrace format. | ||
fn filter_frames(frames: &[Frame], | ||
format: PrintFormat, | ||
context: &BacktraceContext) -> (usize, usize) | ||
|
@@ -101,59 +103,33 @@ fn filter_frames(frames: &[Frame], | |
return (0, 0); | ||
} | ||
|
||
// We want to filter out frames with some prefixes | ||
// from both top and bottom of the call stack. | ||
// Frames to remove from the top of the backtrace. | ||
// | ||
// The raw form is used so that we don't have to demangle the symbol names. | ||
// The `a::b::c` form can show up on Windows/MSVC. | ||
static BAD_PREFIXES_TOP: &'static [&'static str] = &[ | ||
"_ZN3std3sys3imp9backtrace", | ||
"ZN3std3sys3imp9backtrace", | ||
"std::sys::imp::backtrace", | ||
"_ZN3std10sys_common9backtrace", | ||
"ZN3std10sys_common9backtrace", | ||
"ZN3std3sys3imp9backtrace", | ||
|
||
"std::sys_common::backtrace", | ||
"_ZN3std9panicking", | ||
"ZN3std9panicking", | ||
"ZN3std10sys_common9backtrace", | ||
|
||
"std::panicking", | ||
"_ZN4core9panicking", | ||
"ZN4core9panicking", | ||
"ZN3std9panicking", | ||
|
||
"core::panicking", | ||
"_ZN4core6result13unwrap_failed", | ||
"ZN4core6result13unwrap_failed", | ||
"ZN4core9panicking", | ||
|
||
"core::result::unwrap_failed", | ||
"ZN4core6result13unwrap_failed", | ||
|
||
"rust_begin_unwind", | ||
"_ZN4drop", | ||
"mingw_set_invalid_parameter_handler", | ||
]; | ||
static BAD_PREFIXES_BOTTOM: &'static [&'static str] = &[ | ||
"_ZN3std9panicking", | ||
"ZN3std9panicking", | ||
"std::panicking", | ||
"_ZN3std5panic", | ||
"ZN3std5panic", | ||
"std::panic", | ||
"_ZN4core9panicking", | ||
"ZN4core9panicking", | ||
"core::panicking", | ||
"_ZN3std2rt10lang_start", | ||
"ZN3std2rt10lang_start", | ||
"std::rt::lang_start", | ||
"panic_unwind::__rust_maybe_catch_panic", | ||
"__rust_maybe_catch_panic", | ||
"_rust_maybe_catch_panic", | ||
"__libc_start_main", | ||
"__rust_try", | ||
"_start", | ||
"main", | ||
"BaseThreadInitThunk", | ||
"RtlInitializeExceptionChain", | ||
"__scrt_common_main_seh", | ||
"_ZN4drop", | ||
"mingw_set_invalid_parameter_handler", | ||
]; | ||
|
||
let is_good_frame = |frame: Frame, bad_prefixes: &[&str]| { | ||
resolve_symname(frame, |symname| { | ||
if let Some(mangled_symbol_name) = symname { | ||
if !bad_prefixes.iter().any(|s| mangled_symbol_name.starts_with(s)) { | ||
if !bad_prefixes.iter().any(|s| mangled_symbol_name.contains(s)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, some platforms seems to have different behaviours, like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know, but that should be the only cases (no |
||
return Ok(()) | ||
} | ||
} | ||
|
@@ -164,18 +140,38 @@ fn filter_frames(frames: &[Frame], | |
let skipped_before = frames.iter().position(|frame| { | ||
is_good_frame(*frame, BAD_PREFIXES_TOP) | ||
}).unwrap_or(frames.len()); | ||
let skipped_after = frames[skipped_before..].iter().rev().position(|frame| { | ||
is_good_frame(*frame, BAD_PREFIXES_BOTTOM) | ||
}).unwrap_or(frames.len() - skipped_before); | ||
|
||
if skipped_before + skipped_after == frames.len() { | ||
let skipped_after = frames.len() - frames.iter().position(|frame| { | ||
let mut is_marker = false; | ||
let _ = resolve_symname(*frame, |symname| { | ||
if let Some(mangled_symbol_name) = symname { | ||
// Use grep to find the concerned functions | ||
if mangled_symbol_name.contains("__rust_begin_short_backtrace") { | ||
is_marker = true; | ||
} | ||
} | ||
Ok(()) | ||
}, context); | ||
is_marker | ||
}).unwrap_or(frames.len()); | ||
|
||
if skipped_before + skipped_after >= frames.len() { | ||
// Avoid showing completely empty backtraces | ||
return (0, 0); | ||
} | ||
|
||
(skipped_before, skipped_after) | ||
} | ||
|
||
|
||
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. | ||
#[inline(never)] | ||
pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T | ||
where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static | ||
{ | ||
f() | ||
} | ||
|
||
/// Controls how the backtrace should be formated. | ||
#[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
pub enum PrintFormat { | ||
|
@@ -198,7 +194,7 @@ pub fn log_enabled() -> Option<PrintFormat> { | |
} | ||
|
||
let val = match env::var_os("RUST_BACKTRACE") { | ||
Some(x) => if &x == "0" { | ||
Some(x) => if &x == "0" || &x == "" { | ||
None | ||
} else if &x == "full" { | ||
Some(PrintFormat::Full) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1252,12 +1252,16 @@ pub fn convert_benchmarks_to_tests(tests: Vec<TestDescAndFn>) -> Vec<TestDescAnd | |
let testfn = match x.testfn { | ||
DynBenchFn(bench) => { | ||
DynTestFn(Box::new(move |()| { | ||
bench::run_once(|b| bench.run(b)) | ||
bench::run_once(|b| { | ||
__rust_begin_short_backtrace_bench_dyn(&bench, b) | ||
}) | ||
})) | ||
} | ||
StaticBenchFn(benchfn) => { | ||
DynTestFn(Box::new(move |()| { | ||
bench::run_once(|b| benchfn(b)) | ||
bench::run_once(|b| { | ||
__rust_begin_short_backtrace_bench_static(benchfn, b) | ||
}) | ||
})) | ||
} | ||
f => f, | ||
|
@@ -1363,12 +1367,39 @@ pub fn run_test(opts: &TestOpts, | |
monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap(); | ||
return; | ||
} | ||
DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f), | ||
StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, | ||
Box::new(move |()| f())), | ||
DynTestFn(f) => | ||
run_test_inner(desc, monitor_ch, opts.nocapture, | ||
Box::new(move |()| __rust_begin_short_backtrace_test_boxfn(f))), | ||
StaticTestFn(f) => | ||
run_test_inner(desc, monitor_ch, opts.nocapture, | ||
Box::new(move |()| __rust_begin_short_backtrace_test(f))), | ||
} | ||
} | ||
|
||
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. | ||
#[inline(never)] | ||
fn __rust_begin_short_backtrace_test_boxfn(f: Box<FnBox<()>>) { | ||
f.call_box(()) | ||
} | ||
|
||
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. | ||
#[inline(never)] | ||
fn __rust_begin_short_backtrace_test(f: fn()) { | ||
f() | ||
} | ||
|
||
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. | ||
#[inline(never)] | ||
fn __rust_begin_short_backtrace_bench_static(f: fn(&mut Bencher), b: &mut Bencher) { | ||
f(b) | ||
} | ||
|
||
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. | ||
#[inline(never)] | ||
fn __rust_begin_short_backtrace_bench_dyn(bench: &Box<TDynBenchFn>, b: &mut Bencher) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can these be consolidated? There probably only needs to be one implementation here: #[inline(never)
fn name<F: FnOnce()>(f: F) { f() } There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use a generic closure here, the signatures don't have to literally all be the same |
||
bench.run(b) | ||
} | ||
|
||
fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any + Send>>) -> TestResult { | ||
match (&desc.should_panic, task_result) { | ||
(&ShouldPanic::No, Ok(())) | | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove this array entirely? Currently
rust_panic
is the "canonical frame name" of the panic, and if there's a lower frame we should start from then we should just give that a canonical name instead.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What to you think about removing the frames from the
core::panicking::panic*
functions?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine to strive to eliminate them, but we shouldn't have such a long list of what to filter. There should be one frame that we look for and if we find it maybe do a few calculations based off that. Substring searching is basically guarantee to go wrong in the future.