]>
Commit | Line | Data |
---|---|---|
3dfed10e | 1 | use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt}; |
dfeec247 | 2 | use crate::borrow::Cow; |
8bb4bdeb XL |
3 | /// Common code for printing the backtrace in the same way across the different |
4 | /// supported platforms. | |
532ac7d7 | 5 | use crate::env; |
e1599b0c | 6 | use crate::fmt; |
532ac7d7 | 7 | use crate::io; |
dc9dc135 | 8 | use crate::io::prelude::*; |
e1599b0c | 9 | use crate::path::{self, Path, PathBuf}; |
2b03887a | 10 | use crate::sync::{Mutex, PoisonError}; |
532ac7d7 | 11 | |
8bb4bdeb XL |
12 | /// Max number of frames to print. |
13 | const MAX_NB_FRAMES: usize = 100; | |
14 | ||
2b03887a FG |
15 | pub fn lock() -> impl Drop { |
16 | static LOCK: Mutex<()> = Mutex::new(()); | |
17 | LOCK.lock().unwrap_or_else(PoisonError::into_inner) | |
e1599b0c XL |
18 | } |
19 | ||
20 | /// Prints the current backtrace. | |
21 | pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> { | |
0731742a | 22 | // There are issues currently linking libbacktrace into tests, and in |
f25598a0 | 23 | // general during std's own unit tests we're not testing this path. In |
0731742a XL |
24 | // test mode immediately return here to optimize away any references to the |
25 | // libbacktrace symbols | |
26 | if cfg!(test) { | |
dc9dc135 | 27 | return Ok(()); |
0731742a XL |
28 | } |
29 | ||
8bb4bdeb XL |
30 | // Use a lock to prevent mixed output in multithreading context. |
31 | // Some platforms also requires it, like `SymFromAddr` on Windows. | |
32 | unsafe { | |
e1599b0c XL |
33 | let _lock = lock(); |
34 | _print(w, format) | |
35 | } | |
36 | } | |
37 | ||
38 | unsafe fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> { | |
39 | struct DisplayBacktrace { | |
40 | format: PrintFmt, | |
8bb4bdeb | 41 | } |
e1599b0c XL |
42 | impl fmt::Display for DisplayBacktrace { |
43 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | |
dfeec247 | 44 | unsafe { _print_fmt(fmt, self.format) } |
e1599b0c XL |
45 | } |
46 | } | |
47 | write!(w, "{}", DisplayBacktrace { format }) | |
8bb4bdeb XL |
48 | } |
49 | ||
e1599b0c | 50 | unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result { |
60c5eb7d XL |
51 | // Always 'fail' to get the cwd when running under Miri - |
52 | // this allows Miri to display backtraces in isolation mode | |
dfeec247 | 53 | let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None }; |
60c5eb7d | 54 | |
e1599b0c XL |
55 | let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| { |
56 | output_filename(fmt, bows, print_fmt, cwd.as_ref()) | |
57 | }; | |
ba9703b0 | 58 | writeln!(fmt, "stack backtrace:")?; |
e1599b0c XL |
59 | let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path); |
60 | bt_fmt.add_context()?; | |
61 | let mut idx = 0; | |
62 | let mut res = Ok(()); | |
3dfed10e XL |
63 | // Start immediately if we're not using a short backtrace. |
64 | let mut start = print_fmt != PrintFmt::Short; | |
e1599b0c XL |
65 | backtrace_rs::trace_unsynchronized(|frame| { |
66 | if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES { | |
67 | return false; | |
68 | } | |
8bb4bdeb | 69 | |
e1599b0c XL |
70 | let mut hit = false; |
71 | let mut stop = false; | |
72 | backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { | |
73 | hit = true; | |
74 | if print_fmt == PrintFmt::Short { | |
75 | if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { | |
94222f64 | 76 | if start && sym.contains("__rust_begin_short_backtrace") { |
e1599b0c XL |
77 | stop = true; |
78 | return; | |
79 | } | |
3dfed10e XL |
80 | if sym.contains("__rust_end_short_backtrace") { |
81 | start = true; | |
82 | return; | |
83 | } | |
e1599b0c | 84 | } |
7cac9316 | 85 | } |
e1599b0c | 86 | |
3dfed10e XL |
87 | if start { |
88 | res = bt_fmt.frame().symbol(frame, symbol); | |
89 | } | |
dc9dc135 | 90 | }); |
e1599b0c XL |
91 | if stop { |
92 | return false; | |
93 | } | |
c295e0f8 XL |
94 | if !hit && start { |
95 | res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); | |
e1599b0c XL |
96 | } |
97 | ||
98 | idx += 1; | |
99 | res.is_ok() | |
100 | }); | |
101 | res?; | |
102 | bt_fmt.finish()?; | |
103 | if print_fmt == PrintFmt::Short { | |
dc9dc135 | 104 | writeln!( |
e1599b0c | 105 | fmt, |
dc9dc135 XL |
106 | "note: Some details are omitted, \ |
107 | run with `RUST_BACKTRACE=full` for a verbose backtrace." | |
108 | )?; | |
109 | } | |
110 | Ok(()) | |
7cac9316 XL |
111 | } |
112 | ||
e74abb32 | 113 | /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that |
f25598a0 | 114 | /// this is only inline(never) when backtraces in std are enabled, otherwise |
e74abb32 XL |
115 | /// it's fine to optimize away. |
116 | #[cfg_attr(feature = "backtrace", inline(never))] | |
7cac9316 | 117 | pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T |
dc9dc135 XL |
118 | where |
119 | F: FnOnce() -> T, | |
7cac9316 | 120 | { |
3dfed10e XL |
121 | let result = f(); |
122 | ||
123 | // prevent this frame from being tail-call optimised away | |
124 | crate::hint::black_box(()); | |
125 | ||
126 | result | |
127 | } | |
128 | ||
129 | /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that | |
f25598a0 | 130 | /// this is only inline(never) when backtraces in std are enabled, otherwise |
3dfed10e XL |
131 | /// it's fine to optimize away. |
132 | #[cfg_attr(feature = "backtrace", inline(never))] | |
133 | pub fn __rust_end_short_backtrace<F, T>(f: F) -> T | |
134 | where | |
135 | F: FnOnce() -> T, | |
136 | { | |
137 | let result = f(); | |
138 | ||
139 | // prevent this frame from being tail-call optimised away | |
140 | crate::hint::black_box(()); | |
141 | ||
142 | result | |
8bb4bdeb XL |
143 | } |
144 | ||
e1599b0c XL |
145 | /// Prints the filename of the backtrace frame. |
146 | /// | |
147 | /// See also `output`. | |
148 | pub fn output_filename( | |
149 | fmt: &mut fmt::Formatter<'_>, | |
150 | bows: BytesOrWideString<'_>, | |
151 | print_fmt: PrintFmt, | |
152 | cwd: Option<&PathBuf>, | |
153 | ) -> fmt::Result { | |
154 | let file: Cow<'_, Path> = match bows { | |
155 | #[cfg(unix)] | |
156 | BytesOrWideString::Bytes(bytes) => { | |
157 | use crate::os::unix::prelude::*; | |
158 | Path::new(crate::ffi::OsStr::from_bytes(bytes)).into() | |
8bb4bdeb | 159 | } |
e1599b0c XL |
160 | #[cfg(not(unix))] |
161 | BytesOrWideString::Bytes(bytes) => { | |
162 | Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")).into() | |
dc9dc135 | 163 | } |
dc9dc135 | 164 | #[cfg(windows)] |
e1599b0c XL |
165 | BytesOrWideString::Wide(wide) => { |
166 | use crate::os::windows::prelude::*; | |
167 | Cow::Owned(crate::ffi::OsString::from_wide(wide).into()) | |
dc9dc135 | 168 | } |
e1599b0c | 169 | #[cfg(not(windows))] |
dfeec247 | 170 | BytesOrWideString::Wide(_wide) => Path::new("<unknown>").into(), |
e1599b0c XL |
171 | }; |
172 | if print_fmt == PrintFmt::Short && file.is_absolute() { | |
173 | if let Some(cwd) = cwd { | |
174 | if let Ok(stripped) = file.strip_prefix(&cwd) { | |
175 | if let Some(s) = stripped.to_str() { | |
5e7ed085 | 176 | return write!(fmt, ".{}{s}", path::MAIN_SEPARATOR); |
dc9dc135 XL |
177 | } |
178 | } | |
179 | } | |
dc9dc135 | 180 | } |
e1599b0c | 181 | fmt::Display::fmt(&file.display(), fmt) |
e9174d1e | 182 | } |