1use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
2use crate::borrow::Cow;
3/// Common code for printing the backtrace in the same way across the different
4/// supported platforms.
5use crate::env;
6use crate::fmt;
7use crate::io;
8use crate::io::prelude::*;
9use crate::path::{self, Path, PathBuf};
10use crate::sync::{Mutex, PoisonError};
11
12/// Max number of frames to print.
13const MAX_NB_FRAMES: usize = 100;
14
15pub fn lock() -> impl Drop {
16 static LOCK: Mutex<()> = Mutex::new(());
17 LOCK.lock().unwrap_or_else(op:PoisonError::into_inner)
18}
19
20/// Prints the current backtrace.
21pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
22 // There are issues currently linking libbacktrace into tests, and in
23 // general during std's own unit tests we're not testing this path. In
24 // test mode immediately return here to optimize away any references to the
25 // libbacktrace symbols
26 if cfg!(test) {
27 return Ok(());
28 }
29
30 // Use a lock to prevent mixed output in multithreading context.
31 // Some platforms also requires it, like `SymFromAddr` on Windows.
32 unsafe {
33 let _lock: impl Sized = lock();
34 _print(w, format)
35 }
36}
37
38unsafe fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
39 struct DisplayBacktrace {
40 format: PrintFmt,
41 }
42 impl fmt::Display for DisplayBacktrace {
43 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
44 unsafe { _print_fmt(fmt, self.format) }
45 }
46 }
47 write!(w, "{}", DisplayBacktrace { format })
48}
49
50unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
51 // Always 'fail' to get the cwd when running under Miri -
52 // this allows Miri to display backtraces in isolation mode
53 let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None };
54
55 let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
56 output_filename(fmt, bows, print_fmt, cwd.as_ref())
57 };
58 writeln!(fmt, "stack backtrace:")?;
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(());
63 let mut omitted_count: usize = 0;
64 let mut first_omit = true;
65 // Start immediately if we're not using a short backtrace.
66 let mut start = print_fmt != PrintFmt::Short;
67 set_image_base();
68 backtrace_rs::trace_unsynchronized(|frame| {
69 if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
70 return false;
71 }
72
73 let mut hit = false;
74 backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
75 hit = true;
76
77 // Any frames between `__rust_begin_short_backtrace` and `__rust_end_short_backtrace`
78 // are omitted from the backtrace in short mode, `__rust_end_short_backtrace` will be
79 // called before the panic hook, so we won't ignore any frames if there is no
80 // invoke of `__rust_begin_short_backtrace`.
81 if print_fmt == PrintFmt::Short {
82 if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
83 if start && sym.contains("__rust_begin_short_backtrace") {
84 start = false;
85 return;
86 }
87 if sym.contains("__rust_end_short_backtrace") {
88 start = true;
89 return;
90 }
91 if !start {
92 omitted_count += 1;
93 }
94 }
95 }
96
97 if start {
98 if omitted_count > 0 {
99 debug_assert!(print_fmt == PrintFmt::Short);
100 // only print the message between the middle of frames
101 if !first_omit {
102 let _ = writeln!(
103 bt_fmt.formatter(),
104 " [... omitted {} frame{} ...]",
105 omitted_count,
106 if omitted_count > 1 { "s" } else { "" }
107 );
108 }
109 first_omit = false;
110 omitted_count = 0;
111 }
112 res = bt_fmt.frame().symbol(frame, symbol);
113 }
114 });
115 #[cfg(target_os = "nto")]
116 if libc::__my_thread_exit as *mut libc::c_void == frame.ip() {
117 if !hit && start {
118 use crate::backtrace_rs::SymbolName;
119 res = bt_fmt.frame().print_raw(
120 frame.ip(),
121 Some(SymbolName::new("__my_thread_exit".as_bytes())),
122 None,
123 None,
124 );
125 }
126 return false;
127 }
128 if !hit && start {
129 res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
130 }
131
132 idx += 1;
133 res.is_ok()
134 });
135 res?;
136 bt_fmt.finish()?;
137 if print_fmt == PrintFmt::Short {
138 writeln!(
139 fmt,
140 "note: Some details are omitted, \
141 run with `RUST_BACKTRACE=full` for a verbose backtrace."
142 )?;
143 }
144 Ok(())
145}
146
147/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
148/// this is only inline(never) when backtraces in std are enabled, otherwise
149/// it's fine to optimize away.
150#[cfg_attr(feature = "backtrace", inline(never))]
151pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
152where
153 F: FnOnce() -> T,
154{
155 let result: T = f();
156
157 // prevent this frame from being tail-call optimised away
158 crate::hint::black_box(());
159
160 result
161}
162
163/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
164/// this is only inline(never) when backtraces in std are enabled, otherwise
165/// it's fine to optimize away.
166#[cfg_attr(feature = "backtrace", inline(never))]
167pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
168where
169 F: FnOnce() -> T,
170{
171 let result: T = f();
172
173 // prevent this frame from being tail-call optimised away
174 crate::hint::black_box(());
175
176 result
177}
178
179/// Prints the filename of the backtrace frame.
180///
181/// See also `output`.
182pub fn output_filename(
183 fmt: &mut fmt::Formatter<'_>,
184 bows: BytesOrWideString<'_>,
185 print_fmt: PrintFmt,
186 cwd: Option<&PathBuf>,
187) -> fmt::Result {
188 let file: Cow<'_, Path> = match bows {
189 #[cfg(unix)]
190 BytesOrWideString::Bytes(bytes) => {
191 use crate::os::unix::prelude::*;
192 Path::new(crate::ffi::OsStr::from_bytes(bytes)).into()
193 }
194 #[cfg(not(unix))]
195 BytesOrWideString::Bytes(bytes) => {
196 Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")).into()
197 }
198 #[cfg(windows)]
199 BytesOrWideString::Wide(wide) => {
200 use crate::os::windows::prelude::*;
201 Cow::Owned(crate::ffi::OsString::from_wide(wide).into())
202 }
203 #[cfg(not(windows))]
204 BytesOrWideString::Wide(_wide) => Path::new("<unknown>").into(),
205 };
206 if print_fmt == PrintFmt::Short && file.is_absolute() {
207 if let Some(cwd) = cwd {
208 if let Ok(stripped) = file.strip_prefix(&cwd) {
209 if let Some(s) = stripped.to_str() {
210 return write!(fmt, ".{}{s}", path::MAIN_SEPARATOR);
211 }
212 }
213 }
214 }
215 fmt::Display::fmt(&file.display(), fmt)
216}
217
218#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
219pub fn set_image_base() {
220 let image_base = crate::os::fortanix_sgx::mem::image_base();
221 backtrace_rs::set_image_base(crate::ptr::invalid_mut(image_base as _));
222}
223
224#[cfg(not(all(target_vendor = "fortanix", target_env = "sgx")))]
225pub fn set_image_base() {
226 // nothing to do for platforms other than SGX
227}
228