1 /// Common code for printing the backtrace in the same way across the different
2 /// supported platforms.
7 use path::{self, Path};
9 use rustc_demangle::demangle;
11 use sync::atomic::{self, Ordering};
12 use sys::mutex::Mutex;
14 pub use sys::backtrace::{
17 foreach_symbol_fileline,
21 #[cfg(target_pointer_width = "64")]
22 pub const HEX_WIDTH: usize = 18;
24 #[cfg(target_pointer_width = "32")]
25 pub const HEX_WIDTH: usize = 10;
27 /// Represents an item in the backtrace list. See `unwind_backtrace` for how
29 #[derive(Debug, Copy, Clone)]
31 /// Exact address of the call that failed.
32 pub exact_position: *const u8,
33 /// Address of the enclosing function.
34 pub symbol_addr: *const u8,
35 /// Which inlined function is this frame referring to
36 pub inline_context: u32,
39 /// Max number of frames to print.
40 const MAX_NB_FRAMES: usize = 100;
42 /// Prints the current backtrace.
43 pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
44 static LOCK: Mutex = Mutex::new();
46 // There are issues currently linking libbacktrace into tests, and in
47 // general during libstd's own unit tests we're not testing this path. In
48 // test mode immediately return here to optimize away any references to the
49 // libbacktrace symbols
54 // Use a lock to prevent mixed output in multithreading context.
55 // Some platforms also requires it, like `SymFromAddr` on Windows.
58 let res = _print(w, format);
64 fn _print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
65 let mut frames = [Frame {
66 exact_position: ptr::null(),
67 symbol_addr: ptr::null(),
70 let (nb_frames, context) = unwind_backtrace(&mut frames)?;
71 let (skipped_before, skipped_after) =
72 filter_frames(&frames[..nb_frames], format, &context);
73 if skipped_before + skipped_after > 0 {
74 writeln!(w, "note: Some details are omitted, \
75 run with `RUST_BACKTRACE=full` for a verbose backtrace.")?;
77 writeln!(w, "stack backtrace:")?;
79 let filtered_frames = &frames[..nb_frames - skipped_after];
80 for (index, frame) in filtered_frames.iter().skip(skipped_before).enumerate() {
81 resolve_symname(*frame, |symname| {
82 output(w, index, *frame, symname, format)
84 let has_more_filenames = foreach_symbol_fileline(*frame, |file, line| {
85 output_fileline(w, file, line, format)
87 if has_more_filenames {
88 w.write_all(b" <... and possibly more>")?;
95 /// Returns a number of frames to remove at the beginning and at the end of the
96 /// backtrace, according to the backtrace format.
97 fn filter_frames(frames: &[Frame],
99 context: &BacktraceContext) -> (usize, usize)
101 if format == PrintFormat::Full {
105 let skipped_before = 0;
107 let skipped_after = frames.len() - frames.iter().position(|frame| {
108 let mut is_marker = false;
109 let _ = resolve_symname(*frame, |symname| {
110 if let Some(mangled_symbol_name) = symname {
111 // Use grep to find the concerned functions
112 if mangled_symbol_name.contains("__rust_begin_short_backtrace") {
119 }).unwrap_or(frames.len());
121 if skipped_before + skipped_after >= frames.len() {
122 // Avoid showing completely empty backtraces
126 (skipped_before, skipped_after)
130 /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
132 pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
133 where F: FnOnce() -> T, F: Send, T: Send
138 /// Controls how the backtrace should be formatted.
139 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
140 pub enum PrintFormat {
141 /// Show only relevant data from the backtrace.
143 /// Show all the frames with absolute path for files.
147 // For now logging is turned off by default, and this function checks to see
148 // whether the magical environment variable is present to see if it's turned on.
149 pub fn log_enabled() -> Option<PrintFormat> {
150 static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
151 match ENABLED.load(Ordering::SeqCst) {
154 2 => return Some(PrintFormat::Short),
155 _ => return Some(PrintFormat::Full),
158 let val = env::var_os("RUST_BACKTRACE").and_then(|x|
161 } else if &x == "full" {
162 Some(PrintFormat::Full)
164 Some(PrintFormat::Short)
167 ENABLED.store(match val {
168 Some(v) => v as isize,
170 }, Ordering::SeqCst);
174 /// Prints the symbol of the backtrace frame.
176 /// These output functions should now be used everywhere to ensure consistency.
177 /// You may want to also use `output_fileline`.
178 fn output(w: &mut dyn Write, idx: usize, frame: Frame,
179 s: Option<&str>, format: PrintFormat) -> io::Result<()> {
180 // Remove the `17: 0x0 - <unknown>` line.
181 if format == PrintFormat::Short && frame.exact_position == ptr::null() {
185 PrintFormat::Full => write!(w,
188 frame.exact_position,
190 PrintFormat::Short => write!(w, " {:2}: ", idx)?,
194 let symbol = demangle(string);
196 PrintFormat::Full => write!(w, "{}", symbol)?,
197 // strip the trailing hash if short mode
198 PrintFormat::Short => write!(w, "{:#}", symbol)?,
201 None => w.write_all(b"<unknown>")?,
206 /// Prints the filename and line number of the backtrace frame.
208 /// See also `output`.
210 fn output_fileline(w: &mut dyn Write,
213 format: PrintFormat) -> io::Result<()> {
214 // prior line: " ##: {:2$} - func"
217 PrintFormat::Full => write!(w,
221 PrintFormat::Short => write!(w, " ")?,
224 let file = str::from_utf8(file).unwrap_or("<unknown>");
225 let file_path = Path::new(file);
226 let mut already_printed = false;
227 if format == PrintFormat::Short && file_path.is_absolute() {
228 if let Ok(cwd) = env::current_dir() {
229 if let Ok(stripped) = file_path.strip_prefix(&cwd) {
230 if let Some(s) = stripped.to_str() {
231 write!(w, " at .{}{}:{}", path::MAIN_SEPARATOR, s, line)?;
232 already_printed = true;
237 if !already_printed {
238 write!(w, " at {}:{}", file, line)?;