diff --git a/.gitignore b/.gitignore index 57407a2399a2f..b44c65acb3d52 100644 --- a/.gitignore +++ b/.gitignore @@ -101,7 +101,6 @@ version.ml version.texi .cargo !src/vendor/** -/src/target/ no_llvm_build diff --git a/src/libstd/sys_common/backtrace.rs b/src/libstd/sys_common/backtrace.rs index 1955f3ec9a28f..08e6d5a62ab64 100644 --- a/src/libstd/sys_common/backtrace.rs +++ b/src/libstd/sys_common/backtrace.rs @@ -69,16 +69,14 @@ fn _print(w: &mut Write, format: PrintFormat) -> io::Result<()> { inline_context: 0, }; MAX_NB_FRAMES]; let (nb_frames, context) = unwind_backtrace(&mut frames)?; - let (skipped_before, skipped_after) = - filter_frames(&frames[..nb_frames], format, &context); - if skipped_before + skipped_after > 0 { + let filtered_frames = filter_frames(&frames[..nb_frames], &context, format); + if format != PrintFormat::Full { writeln!(w, "note: Some details are omitted, \ run with `RUST_BACKTRACE=full` for a verbose backtrace.")?; } writeln!(w, "stack backtrace:")?; - let filtered_frames = &frames[..nb_frames - skipped_after]; - for (index, frame) in filtered_frames.iter().skip(skipped_before).enumerate() { + for (index, frame) in filtered_frames { resolve_symname(*frame, |symname| { output(w, index, *frame, symname, format) }, &context)?; @@ -93,40 +91,110 @@ 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) +fn should_show_frame(frame: &Frame, context: &BacktraceContext) -> bool { + const FILTERED_SYMBOLS: &[&str] = &[ + "main", + "rust_begin_unwind", + "__rust_maybe_catch_panic" + ]; + const FILTERED_SYMBOL_PARTS: &[&str] = &[ + "_ZN4core9panicking", + "_ZN3std9panicking", + "_ZN3std3sys", + "_ZN3std10sys_common", + "_ZN3std2rt", + ]; + let mut should_show = true; + let _ = resolve_symname(*frame, |symname| { + if let Some(mangled_symbol_name) = symname { + for filtered_symbol in FILTERED_SYMBOLS { + if mangled_symbol_name == *filtered_symbol { + should_show = false; + return Ok(()); + } + } + for filtered_symbol_part in FILTERED_SYMBOL_PARTS { + if mangled_symbol_name.starts_with(filtered_symbol_part) { + should_show = false; + return Ok(()); + } + } + } + Ok(()) + }, context); + should_show +} + +/// Returns the frames to show as a Vec. +/// If the bool is true the frame is on the edge between showing and not showing. +fn filter_frames<'a>(frames: &'a [Frame], + context: &'a BacktraceContext, + format: PrintFormat) -> impl Iterator + 'a { if format == PrintFormat::Full { - return (0, 0); + return FilterFrames::Pass(frames.iter().enumerate()); } - let skipped_before = 0; - - 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; + let frames_iter = frames + .iter() + .take_while(move |frame| { + let mut is_after_begin_short_backtrace = 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_after_begin_short_backtrace = true; + } } - } - Ok(()) - }, context); - is_marker - }).unwrap_or(frames.len()); - - if skipped_before + skipped_after >= frames.len() { - // Avoid showing completely empty backtraces - return (0, 0); + Ok(()) + }, context); + !is_after_begin_short_backtrace + }) + .enumerate() + .peekable(); + + FilterFrames::Filter { + show_prev_frame: false, + context: context, + iter: frames_iter, } +} - (skipped_before, skipped_after) +enum FilterFrames<'a, I: ::iter::Iterator> { + Filter { + show_prev_frame: bool, + context: &'a BacktraceContext, + iter: ::iter::Peekable, + }, + Pass(::iter::Enumerate<::slice::Iter<'a, Frame>>), } +impl<'a, I: ::iter::Iterator> ::iter::Iterator for FilterFrames<'a, I> { + type Item = (usize, &'a Frame); + + fn next(&mut self) -> Option<(usize, &'a Frame)> { + match *self { + FilterFrames::Filter { ref mut show_prev_frame, ref mut context, ref mut iter } => { + while let Some((i, frame)) = iter.next() { + let show_cur_frame = should_show_frame(frame, context); + let show_next_frame = iter + .peek() + .map(|&(_, frame)| should_show_frame(frame, context)) + .unwrap_or(false); + if *show_prev_frame || show_cur_frame || show_next_frame { + *show_prev_frame = show_cur_frame; + return Some((i, frame)); + } + *show_prev_frame = show_cur_frame; + } + None + } + FilterFrames::Pass(ref mut iter) => { + iter.next() + } + } + } +} /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. #[inline(never)] diff --git a/src/test/run-make/filter_backtrace/Makefile b/src/test/run-make/filter_backtrace/Makefile new file mode 100644 index 0000000000000..ecfd3d4da857a --- /dev/null +++ b/src/test/run-make/filter_backtrace/Makefile @@ -0,0 +1,23 @@ +-include ../tools.mk + +# Test that short backtraces don't include several internal symbols + +all: + $(RUSTC) main.rs -o $(TMPDIR)/main + # with short backtrace + RUST_BACKTRACE=1 $(TMPDIR)/main 2> $(TMPDIR)/short_bt.stderr || exit 0 && exit 1 + cat $(TMPDIR)/short_bt.stderr # FIXME: remove this debug cat + $(CGREP) "panicked at" < $(TMPDIR)/short_bt.stderr + $(CGREP) -v "std::panicking" < $(TMPDIR)/short_bt.stderr + $(CGREP) -v "std::sys" < $(TMPDIR)/short_bt.stderr + $(CGREP) -v "__rust_maybe_catch_panic" < $(TMPDIR)/short_bt.stderr + $(CGREP) -v "__rust_begin_short_backtrace" < $(TMPDIR)/short_bt.stderr + # with long backtrace + RUST_BACKTRACE=full $(TMPDIR)/main 2> $(TMPDIR)/long_bt.stderr || exit 0 && exit 1 + cat $(TMPDIR)/long_bt.stderr # FIXME: remove this debug cat + $(CGREP) "panicked at" < $(TMPDIR)/long_bt.stderr + $(CGREP) "std::panicking" < $(TMPDIR)/long_bt.stderr + $(CGREP) "std::sys" < $(TMPDIR)/long_bt.stderr + $(CGREP) "__rust_maybe_catch_panic" < $(TMPDIR)/long_bt.stderr + # FIXME: prevent tail call optimization for this + $(CGREP) -v "__rust_begin_short_backtrace" < $(TMPDIR)/long_bt.stderr diff --git a/src/test/run-make/filter_backtrace/main.rs b/src/test/run-make/filter_backtrace/main.rs new file mode 100644 index 0000000000000..403e1999635f1 --- /dev/null +++ b/src/test/run-make/filter_backtrace/main.rs @@ -0,0 +1,13 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + (None as Option<()>).unwrap(); +} diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs index 3e1ae730e4af3..f5a01d80d1588 100644 --- a/src/test/run-pass/backtrace.rs +++ b/src/test/run-pass/backtrace.rs @@ -53,7 +53,7 @@ fn expected(fn_name: &str) -> String { fn runtest(me: &str) { // Make sure that the stack trace is printed - let p = template(me).arg("fail").env("RUST_BACKTRACE", "1").spawn().unwrap(); + let p = template(me).arg("fail").env("RUST_BACKTRACE", "full").spawn().unwrap(); let out = p.wait_with_output().unwrap(); assert!(!out.status.success()); let s = str::from_utf8(&out.stderr).unwrap();