]>
Commit | Line | Data |
---|---|---|
8bb4bdeb XL |
1 | /// Common code for printing the backtrace in the same way across the different |
2 | /// supported platforms. | |
3 | ||
532ac7d7 XL |
4 | use crate::env; |
5 | use crate::io::prelude::*; | |
6 | use crate::io; | |
7 | use crate::path::{self, Path}; | |
8 | use crate::ptr; | |
9 | use crate::str; | |
10 | use crate::sync::atomic::{self, Ordering}; | |
11 | use crate::sys::mutex::Mutex; | |
12 | ||
0731742a | 13 | use rustc_demangle::demangle; |
e9174d1e | 14 | |
532ac7d7 | 15 | pub 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 | 23 | pub const HEX_WIDTH: usize = 18; |
1a4d82fc | 24 | |
85aaf69f | 25 | #[cfg(target_pointer_width = "32")] |
c34b1796 | 26 | pub 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)] | |
31 | pub 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. | |
41 | const MAX_NB_FRAMES: usize = 100; | |
42 | ||
43 | /// Prints the current backtrace. | |
8faf50e0 | 44 | pub 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 | 65 | fn _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. | |
98 | fn 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)] | |
133 | pub 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)] |
141 | pub 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 | 150 | pub 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 | 179 | fn 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 | 211 | fn 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 | } |