1 use crate::borrow::Cow;
2 /// Common code for printing the backtrace in the same way across the different
3 /// supported platforms.
7 use crate::io::prelude::*;
8 use crate::path::{self, Path, PathBuf};
9 use crate::sync::atomic::{self, Ordering};
10 use crate::sys::mutex::Mutex;
12 use backtrace_rs::{BacktraceFmt, BytesOrWideString, PrintFmt};
14 /// Max number of frames to print.
15 const MAX_NB_FRAMES: usize = 100;
17 pub fn lock() -> impl Drop {
19 static LOCK: Mutex = Mutex::new();
35 /// Prints the current backtrace.
36 pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
37 // There are issues currently linking libbacktrace into tests, and in
38 // general during libstd's own unit tests we're not testing this path. In
39 // test mode immediately return here to optimize away any references to the
40 // libbacktrace symbols
45 // Use a lock to prevent mixed output in multithreading context.
46 // Some platforms also requires it, like `SymFromAddr` on Windows.
53 unsafe fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
54 struct DisplayBacktrace {
57 impl fmt::Display for DisplayBacktrace {
58 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
59 unsafe { _print_fmt(fmt, self.format) }
62 write!(w, "{}", DisplayBacktrace { format })
65 unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result {
66 // Always 'fail' to get the cwd when running under Miri -
67 // this allows Miri to display backtraces in isolation mode
68 let cwd = if !cfg!(miri) { env::current_dir().ok() } else { None };
70 let mut print_path = move |fmt: &mut fmt::Formatter<'_>, bows: BytesOrWideString<'_>| {
71 output_filename(fmt, bows, print_fmt, cwd.as_ref())
73 let mut bt_fmt = BacktraceFmt::new(fmt, print_fmt, &mut print_path);
74 bt_fmt.add_context()?;
77 backtrace_rs::trace_unsynchronized(|frame| {
78 if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
84 backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
86 if print_fmt == PrintFmt::Short {
87 if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
88 if sym.contains("__rust_begin_short_backtrace") {
95 res = bt_fmt.frame().symbol(frame, symbol);
101 res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
109 if print_fmt == PrintFmt::Short {
112 "note: Some details are omitted, \
113 run with `RUST_BACKTRACE=full` for a verbose backtrace."
119 /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
120 /// this is only inline(never) when backtraces in libstd are enabled, otherwise
121 /// it's fine to optimize away.
122 #[cfg_attr(feature = "backtrace", inline(never))]
123 pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
132 pub enum RustBacktrace {
138 // For now logging is turned off by default, and this function checks to see
139 // whether the magical environment variable is present to see if it's turned on.
140 pub fn rust_backtrace_env() -> RustBacktrace {
141 // If the `backtrace` feature of this crate isn't enabled quickly return
142 // `None` so this can be constant propagated all over the place to turn
143 // optimize away callers.
144 if !cfg!(feature = "backtrace") {
145 return RustBacktrace::Disabled;
148 // Setting environment variables for Fuchsia components isn't a standard
149 // or easily supported workflow. For now, always display backtraces.
150 if cfg!(target_os = "fuchsia") {
151 return RustBacktrace::Print(PrintFmt::Full);
154 static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
155 match ENABLED.load(Ordering::SeqCst) {
157 1 => return RustBacktrace::RuntimeDisabled,
158 2 => return RustBacktrace::Print(PrintFmt::Short),
159 _ => return RustBacktrace::Print(PrintFmt::Full),
162 let (format, cache) = env::var_os("RUST_BACKTRACE")
165 (RustBacktrace::RuntimeDisabled, 1)
166 } else if &x == "full" {
167 (RustBacktrace::Print(PrintFmt::Full), 3)
169 (RustBacktrace::Print(PrintFmt::Short), 2)
172 .unwrap_or((RustBacktrace::RuntimeDisabled, 1));
173 ENABLED.store(cache, Ordering::SeqCst);
177 /// Prints the filename of the backtrace frame.
179 /// See also `output`.
180 pub fn output_filename(
181 fmt: &mut fmt::Formatter<'_>,
182 bows: BytesOrWideString<'_>,
184 cwd: Option<&PathBuf>,
186 let file: Cow<'_, Path> = match bows {
188 BytesOrWideString::Bytes(bytes) => {
189 use crate::os::unix::prelude::*;
190 Path::new(crate::ffi::OsStr::from_bytes(bytes)).into()
193 BytesOrWideString::Bytes(bytes) => {
194 Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")).into()
197 BytesOrWideString::Wide(wide) => {
198 use crate::os::windows::prelude::*;
199 Cow::Owned(crate::ffi::OsString::from_wide(wide).into())
202 BytesOrWideString::Wide(_wide) => Path::new("<unknown>").into(),
204 if print_fmt == PrintFmt::Short && file.is_absolute() {
205 if let Some(cwd) = cwd {
206 if let Ok(stripped) = file.strip_prefix(&cwd) {
207 if let Some(s) = stripped.to_str() {
208 return write!(fmt, ".{}{}", path::MAIN_SEPARATOR, s);
213 fmt::Display::fmt(&file.display(), fmt)