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