1#![forbid(unsafe_op_in_unsafe_fn)]
3
4use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
5use crate::borrow::Cow;
6use crate::io::prelude::*;
7use crate::path::{self, Path, PathBuf};
8use crate::sync::{Mutex, MutexGuard, PoisonError};
9use crate::{env, fmt, io};
10
11const MAX_NB_FRAMES: usize = 100;
13
14pub(crate) const FULL_BACKTRACE_DEFAULT: bool = cfg_select! {
15 target_os = "fuchsia" => true,
17 _ => false,
18};
19
20pub(crate) struct BacktraceLock<'a>(#[allow(dead_code)] MutexGuard<'a, ()>);
21
22pub(crate) fn lock<'a>() -> BacktraceLock<'a> {
23 static LOCK: Mutex<()> = Mutex::new(());
24 BacktraceLock(LOCK.lock().unwrap_or_else(PoisonError::into_inner))
25}
26
27impl BacktraceLock<'_> {
28 pub(crate) fn print(&mut self, w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
30 if cfg!(test) {
35 return Ok(());
36 }
37
38 struct DisplayBacktrace {
39 format: PrintFmt,
40 }
41 impl fmt::Display for DisplayBacktrace {
42 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
43 unsafe { _print_fmt(fmt, self.format) }
45 }
46 }
47 write!(w, "{}", DisplayBacktrace { format })
48 }
49}
50
51unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
55 let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None };
58
59 let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
60 output_filename(fmt, bows, print_fmt, cwd.as_ref())
61 };
62 writeln!(fmt, "stack backtrace:")?;
63 let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path);
64 bt_fmt.add_context()?;
65 let mut idx = 0;
66 let mut res = Ok(());
67 let mut omitted_count: usize = 0;
68 let mut first_omit = true;
69 let mut print = print_fmt != PrintFmt::Short;
71 set_image_base();
72 unsafe {
74 backtrace_rs::trace_unsynchronized(|frame| {
75 if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
76 return false;
77 }
78
79 if cfg!(feature = "backtrace-trace-only") {
80 const HEX_WIDTH: usize = 2 + 2 * size_of::<usize>();
81 let frame_ip = frame.ip();
82 res = writeln!(bt_fmt.formatter(), "{idx:4}: {frame_ip:HEX_WIDTH$?}");
83 } else {
84 let mut hit = false;
85 backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
86 hit = true;
87
88 if print_fmt == PrintFmt::Short {
91 if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
92 if sym.contains("__rust_end_short_backtrace") {
93 print = true;
94 return;
95 }
96 if print && sym.contains("__rust_begin_short_backtrace") {
97 print = false;
98 return;
99 }
100 if !print {
101 omitted_count += 1;
102 }
103 }
104 }
105
106 if print {
107 if omitted_count > 0 {
108 debug_assert!(print_fmt == PrintFmt::Short);
109 if !first_omit {
111 let _ = writeln!(
112 bt_fmt.formatter(),
113 " [... omitted {} frame{} ...]",
114 omitted_count,
115 if omitted_count > 1 { "s" } else { "" }
116 );
117 }
118 first_omit = false;
119 omitted_count = 0;
120 }
121 res = bt_fmt.frame().symbol(frame, symbol);
122 }
123 });
124 #[cfg(all(target_os = "nto", any(target_env = "nto70", target_env = "nto71")))]
125 if libc::__my_thread_exit as *mut libc::c_void == frame.ip() {
126 if !hit && print {
127 use crate::backtrace_rs::SymbolName;
128 res = bt_fmt.frame().print_raw(
129 frame.ip(),
130 Some(SymbolName::new("__my_thread_exit".as_bytes())),
131 None,
132 None,
133 );
134 }
135 return false;
136 }
137 if !hit && print {
138 res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
139 }
140 }
141
142 idx += 1;
143 res.is_ok()
144 })
145 };
146 res?;
147 bt_fmt.finish()?;
148 if print_fmt == PrintFmt::Short {
149 writeln!(
150 fmt,
151 "note: Some details are omitted, \
152 run with `RUST_BACKTRACE=full` for a verbose backtrace."
153 )?;
154 }
155 Ok(())
156}
157
158#[cfg_attr(feature = "backtrace", inline(never))]
162pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
163where
164 F: FnOnce() -> T,
165{
166 let result = f();
167
168 crate::hint::black_box(());
170
171 result
172}
173
174#[cfg_attr(feature = "backtrace", inline(never))]
178pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
179where
180 F: FnOnce() -> T,
181{
182 let result = f();
183
184 crate::hint::black_box(());
186
187 result
188}
189
190pub fn output_filename(
194 fmt: &mut fmt::Formatter<'_>,
195 bows: BytesOrWideString<'_>,
196 print_fmt: PrintFmt,
197 cwd: Option<&PathBuf>,
198) -> fmt::Result {
199 let file: Cow<'_, Path> = match bows {
200 #[cfg(unix)]
201 BytesOrWideString::Bytes(bytes) => {
202 use crate::os::unix::prelude::*;
203 Path::new(crate::ffi::OsStr::from_bytes(bytes)).into()
204 }
205 #[cfg(not(unix))]
206 BytesOrWideString::Bytes(bytes) => {
207 Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")).into()
208 }
209 #[cfg(windows)]
210 BytesOrWideString::Wide(wide) => {
211 use crate::os::windows::prelude::*;
212 Cow::Owned(crate::ffi::OsString::from_wide(wide).into())
213 }
214 #[cfg(not(windows))]
215 BytesOrWideString::Wide(_wide) => Path::new("<unknown>").into(),
216 };
217 if print_fmt == PrintFmt::Short && file.is_absolute() {
218 if let Some(cwd) = cwd {
219 if let Ok(stripped) = file.strip_prefix(&cwd) {
220 if let Some(s) = stripped.to_str() {
221 return write!(fmt, ".{}{s}", path::MAIN_SEPARATOR);
222 }
223 }
224 }
225 }
226 fmt::Display::fmt(&file.display(), fmt)
227}
228
229#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
230pub fn set_image_base() {
231 let image_base = crate::os::fortanix_sgx::mem::image_base();
232 backtrace_rs::set_image_base(crate::ptr::without_provenance_mut(image_base as _));
233}
234
235#[cfg(not(all(target_vendor = "fortanix", target_env = "sgx")))]
236pub fn set_image_base() {
237 }