]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys_common/backtrace.rs
New upstream version 1.36.0+dfsg1
[rustc.git] / src / libstd / sys_common / backtrace.rs
CommitLineData
8bb4bdeb
XL
1/// Common code for printing the backtrace in the same way across the different
2/// supported platforms.
3
532ac7d7
XL
4use crate::env;
5use crate::io::prelude::*;
6use crate::io;
7use crate::path::{self, Path};
8use crate::ptr;
9use crate::str;
10use crate::sync::atomic::{self, Ordering};
11use crate::sys::mutex::Mutex;
12
0731742a 13use rustc_demangle::demangle;
e9174d1e 14
532ac7d7 15pub use crate::sys::backtrace::{
8bb4bdeb
XL
16 unwind_backtrace,
17 resolve_symname,
18 foreach_symbol_fileline,
19 BacktraceContext
20};
1a4d82fc 21
85aaf69f 22#[cfg(target_pointer_width = "64")]
c34b1796 23pub const HEX_WIDTH: usize = 18;
1a4d82fc 24
85aaf69f 25#[cfg(target_pointer_width = "32")]
c34b1796 26pub const HEX_WIDTH: usize = 10;
1a4d82fc 27
8bb4bdeb
XL
28/// Represents an item in the backtrace list. See `unwind_backtrace` for how
29/// it is created.
30#[derive(Debug, Copy, Clone)]
31pub struct Frame {
32 /// Exact address of the call that failed.
abe05a73 33 pub exact_position: *const u8,
8bb4bdeb 34 /// Address of the enclosing function.
abe05a73 35 pub symbol_addr: *const u8,
2c00a5a8
XL
36 /// Which inlined function is this frame referring to
37 pub inline_context: u32,
8bb4bdeb
XL
38}
39
40/// Max number of frames to print.
41const MAX_NB_FRAMES: usize = 100;
42
43/// Prints the current backtrace.
8faf50e0 44pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
8bb4bdeb
XL
45 static LOCK: Mutex = Mutex::new();
46
0731742a
XL
47 // There are issues currently linking libbacktrace into tests, and in
48 // general during libstd's own unit tests we're not testing this path. In
49 // test mode immediately return here to optimize away any references to the
50 // libbacktrace symbols
51 if cfg!(test) {
52 return Ok(())
53 }
54
8bb4bdeb
XL
55 // Use a lock to prevent mixed output in multithreading context.
56 // Some platforms also requires it, like `SymFromAddr` on Windows.
57 unsafe {
58 LOCK.lock();
59 let res = _print(w, format);
60 LOCK.unlock();
61 res
62 }
63}
64
8faf50e0 65fn _print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
8bb4bdeb
XL
66 let mut frames = [Frame {
67 exact_position: ptr::null(),
68 symbol_addr: ptr::null(),
2c00a5a8 69 inline_context: 0,
8bb4bdeb
XL
70 }; MAX_NB_FRAMES];
71 let (nb_frames, context) = unwind_backtrace(&mut frames)?;
72 let (skipped_before, skipped_after) =
73 filter_frames(&frames[..nb_frames], format, &context);
74 if skipped_before + skipped_after > 0 {
75 writeln!(w, "note: Some details are omitted, \
76 run with `RUST_BACKTRACE=full` for a verbose backtrace.")?;
77 }
78 writeln!(w, "stack backtrace:")?;
79
80 let filtered_frames = &frames[..nb_frames - skipped_after];
81 for (index, frame) in filtered_frames.iter().skip(skipped_before).enumerate() {
82 resolve_symname(*frame, |symname| {
83 output(w, index, *frame, symname, format)
84 }, &context)?;
85 let has_more_filenames = foreach_symbol_fileline(*frame, |file, line| {
86 output_fileline(w, file, line, format)
87 }, &context)?;
88 if has_more_filenames {
89 w.write_all(b" <... and possibly more>")?;
90 }
91 }
92
93 Ok(())
94}
95
7cac9316
XL
96/// Returns a number of frames to remove at the beginning and at the end of the
97/// backtrace, according to the backtrace format.
98fn filter_frames(frames: &[Frame],
99 format: PrintFormat,
100 context: &BacktraceContext) -> (usize, usize)
8bb4bdeb 101{
7cac9316
XL
102 if format == PrintFormat::Full {
103 return (0, 0);
104 }
105
106 let skipped_before = 0;
107
108 let skipped_after = frames.len() - frames.iter().position(|frame| {
109 let mut is_marker = false;
110 let _ = resolve_symname(*frame, |symname| {
111 if let Some(mangled_symbol_name) = symname {
112 // Use grep to find the concerned functions
113 if mangled_symbol_name.contains("__rust_begin_short_backtrace") {
114 is_marker = true;
115 }
116 }
117 Ok(())
118 }, context);
119 is_marker
120 }).unwrap_or(frames.len());
121
122 if skipped_before + skipped_after >= frames.len() {
123 // Avoid showing completely empty backtraces
124 return (0, 0);
125 }
126
127 (skipped_before, skipped_after)
128}
129
130
131/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
132#[inline(never)]
133pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
ff7c6d11 134 where F: FnOnce() -> T, F: Send, T: Send
7cac9316
XL
135{
136 f()
8bb4bdeb
XL
137}
138
0531ce1d 139/// Controls how the backtrace should be formatted.
8bb4bdeb
XL
140#[derive(Debug, Copy, Clone, Eq, PartialEq)]
141pub enum PrintFormat {
8bb4bdeb 142 /// Show only relevant data from the backtrace.
83c7162d
XL
143 Short = 2,
144 /// Show all the frames with absolute path for files.
145 Full = 3,
8bb4bdeb
XL
146}
147
e9174d1e
SL
148// For now logging is turned off by default, and this function checks to see
149// whether the magical environment variable is present to see if it's turned on.
8bb4bdeb 150pub fn log_enabled() -> Option<PrintFormat> {
e9174d1e
SL
151 static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
152 match ENABLED.load(Ordering::SeqCst) {
83c7162d 153 0 => {}
8bb4bdeb 154 1 => return None,
83c7162d
XL
155 2 => return Some(PrintFormat::Short),
156 _ => return Some(PrintFormat::Full),
e9174d1e
SL
157 }
158
8faf50e0
XL
159 let val = env::var_os("RUST_BACKTRACE").and_then(|x|
160 if &x == "0" {
8bb4bdeb
XL
161 None
162 } else if &x == "full" {
163 Some(PrintFormat::Full)
164 } else {
165 Some(PrintFormat::Short)
8faf50e0
XL
166 }
167 );
8bb4bdeb
XL
168 ENABLED.store(match val {
169 Some(v) => v as isize,
170 None => 1,
171 }, Ordering::SeqCst);
172 val
e9174d1e
SL
173}
174
9fa01778 175/// Prints the symbol of the backtrace frame.
8bb4bdeb
XL
176///
177/// These output functions should now be used everywhere to ensure consistency.
178/// You may want to also use `output_fileline`.
8faf50e0 179fn output(w: &mut dyn Write, idx: usize, frame: Frame,
8bb4bdeb
XL
180 s: Option<&str>, format: PrintFormat) -> io::Result<()> {
181 // Remove the `17: 0x0 - <unknown>` line.
182 if format == PrintFormat::Short && frame.exact_position == ptr::null() {
183 return Ok(());
184 }
185 match format {
186 PrintFormat::Full => write!(w,
187 " {:2}: {:2$?} - ",
188 idx,
189 frame.exact_position,
190 HEX_WIDTH)?,
191 PrintFormat::Short => write!(w, " {:2}: ", idx)?,
192 }
193 match s {
0731742a
XL
194 Some(string) => {
195 let symbol = demangle(string);
196 match format {
197 PrintFormat::Full => write!(w, "{}", symbol)?,
198 // strip the trailing hash if short mode
199 PrintFormat::Short => write!(w, "{:#}", symbol)?,
200 }
201 }
8bb4bdeb 202 None => w.write_all(b"<unknown>")?,
e9174d1e 203 }
8bb4bdeb 204 w.write_all(b"\n")
e9174d1e
SL
205}
206
9fa01778 207/// Prints the filename and line number of the backtrace frame.
8bb4bdeb
XL
208///
209/// See also `output`.
e9174d1e 210#[allow(dead_code)]
8faf50e0 211fn output_fileline(w: &mut dyn Write,
abe05a73
XL
212 file: &[u8],
213 line: u32,
214 format: PrintFormat) -> io::Result<()> {
e9174d1e 215 // prior line: " ##: {:2$} - func"
8bb4bdeb
XL
216 w.write_all(b"")?;
217 match format {
218 PrintFormat::Full => write!(w,
219 " {:1$}",
220 "",
221 HEX_WIDTH)?,
222 PrintFormat::Short => write!(w, " ")?,
e9174d1e 223 }
8bb4bdeb
XL
224
225 let file = str::from_utf8(file).unwrap_or("<unknown>");
226 let file_path = Path::new(file);
227 let mut already_printed = false;
228 if format == PrintFormat::Short && file_path.is_absolute() {
229 if let Ok(cwd) = env::current_dir() {
230 if let Ok(stripped) = file_path.strip_prefix(&cwd) {
231 if let Some(s) = stripped.to_str() {
cc61c64b 232 write!(w, " at .{}{}:{}", path::MAIN_SEPARATOR, s, line)?;
8bb4bdeb
XL
233 already_printed = true;
234 }
235 }
236 }
237 }
238 if !already_printed {
239 write!(w, " at {}:{}", file, line)?;
240 }
241
242 w.write_all(b"\n")
e9174d1e 243}