|
| 1 | +//! Support for capturing a stack backtrace of an OS thread |
| 2 | +//! |
| 3 | +//! This module contains the support necessary to capture a stack backtrace of a |
| 4 | +//! running OS thread from the OS thread itself. The `Backtrace` type supports |
| 5 | +//! capturing a stack trace via the `Backtrace::capture` and |
| 6 | +//! `Backtrace::force_capture` functions. |
| 7 | +//! |
| 8 | +//! A backtrace is typically quite handy to attach to errors (e.g. types |
| 9 | +//! implementing `std::error::Error`) to get a causal chain of where an error |
| 10 | +//! was generated. |
| 11 | +//! |
| 12 | +//! > **Note**: this module is unstable and is designed in [RFC 2504], and you |
| 13 | +//! > can learn more about its status in the [tracking issue]. |
| 14 | +//! |
| 15 | +//! [RFC 2504]: https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md |
| 16 | +//! [tracking issue]: https://github.com/rust-lang/rust/issues/53487 |
| 17 | +//! |
| 18 | +//! ## Accuracy |
| 19 | +//! |
| 20 | +//! Backtraces are attempted to be as accurate as possible, but no guarantees |
| 21 | +//! are provided about the exact accuracy of a backtrace. Instruction pointers, |
| 22 | +//! symbol names, filenames, line numbers, etc, may all be incorrect when |
| 23 | +//! reported. Accuracy is attempted on a best-effort basis, however, and bugs |
| 24 | +//! are always welcome to indicate areas of improvement! |
| 25 | +//! |
| 26 | +//! For most platforms a backtrace with a filename/line number requires that |
| 27 | +//! programs be compiled with debug information. Without debug information |
| 28 | +//! filenames/line numbers will not be reported. |
| 29 | +//! |
| 30 | +//! ## Platform support |
| 31 | +//! |
| 32 | +//! Not all platforms that libstd compiles for support capturing backtraces. |
| 33 | +//! Some platforms simply do nothing when capturing a backtrace. To check |
| 34 | +//! whether the platform supports capturing backtraces you can consult the |
| 35 | +//! `BacktraceStatus` enum as a result of `Backtrace::status`. |
| 36 | +//! |
| 37 | +//! Like above with accuracy platform support is done on a best effort basis. |
| 38 | +//! Sometimes libraries may not be available at runtime or something may go |
| 39 | +//! wrong which would cause a backtrace to not be captured. Please feel free to |
| 40 | +//! report issues with platforms where a backtrace cannot be captured though! |
| 41 | +//! |
| 42 | +//! ## Environment Variables |
| 43 | +//! |
| 44 | +//! The `Backtrace::capture` function may not actually capture a backtrace by |
| 45 | +//! default. Its behavior is governed by two environment variables: |
| 46 | +//! |
| 47 | +//! * `RUST_LIB_BACKTRACE` - if this is set to `0` then `Backtrace::capture` |
| 48 | +//! will never capture a backtrace. Any other value this is set to will enable |
| 49 | +//! `Backtrace::capture`. |
| 50 | +//! |
| 51 | +//! * `RUST_BACKTRACE` - if `RUST_LIB_BACKTRACE` is not set, then this variable |
| 52 | +//! is consulted with the same rules of `RUST_LIB_BACKTRACE`. |
| 53 | +//! |
| 54 | +//! * If neither of the above env vars are set, then `Backtrace::capture` will |
| 55 | +//! be disabled. |
| 56 | +//! |
| 57 | +//! Capturing a backtrace can be a quite expensive runtime operation, so the |
| 58 | +//! environment variables allow either forcibly disabling this runtime |
| 59 | +//! performance hit or allow selectively enabling it in some programs. |
| 60 | +//! |
| 61 | +//! Note that the `Backtrace::force_capture` function can be used to ignore |
| 62 | +//! these environment variables. Also note that the state of environment |
| 63 | +//! variables is cached once the first backtrace is created, so altering |
| 64 | +//! `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` at runtime may not actually change |
| 65 | +//! how backtraces are captured. |
| 66 | +
|
| 67 | +#![unstable(feature = "backtrace", issue = "53487")] |
| 68 | + |
| 69 | +// NB: A note on resolution of a backtrace: |
| 70 | +// |
| 71 | +// Backtraces primarily happen in two steps, one is where we actually capture |
| 72 | +// the stack backtrace, giving us a list of instruction pointers corresponding |
| 73 | +// to stack frames. Next we take these instruction pointers and, one-by-one, |
| 74 | +// turn them into a human readable name (like `main`). |
| 75 | +// |
| 76 | +// The first phase can be somewhat expensive (walking the stack), especially |
| 77 | +// on MSVC where debug information is consulted to return inline frames each as |
| 78 | +// their own frame. The second phase, however, is almost always extremely |
| 79 | +// expensive (on the order of milliseconds sometimes) when it's consulting debug |
| 80 | +// information. |
| 81 | +// |
| 82 | +// We attempt to amortize this cost as much as possible by delaying resolution |
| 83 | +// of an address to a human readable name for as long as possible. When |
| 84 | +// `Backtrace::create` is called to capture a backtrace it doesn't actually |
| 85 | +// perform any symbol resolution, but rather we lazily resolve symbols only just |
| 86 | +// before they're needed for printing. This way we can make capturing a |
| 87 | +// backtrace and throwing it away much cheaper, but actually printing a |
| 88 | +// backtrace is still basically the same cost. |
| 89 | +// |
| 90 | +// This strategy comes at the cost of some synchronization required inside of a |
| 91 | +// `Backtrace`, but that's a relatively small price to pay relative to capturing |
| 92 | +// a backtrace or actually symbolizing it. |
| 93 | + |
| 94 | +use crate::env; |
| 95 | +use crate::fmt; |
| 96 | +use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; |
| 97 | +use crate::sync::Mutex; |
| 98 | +use crate::sys_common::backtrace::{output_filename, lock}; |
| 99 | +use crate::vec::Vec; |
| 100 | +use backtrace::BytesOrWideString; |
| 101 | + |
| 102 | +/// A captured OS thread stack backtrace. |
| 103 | +/// |
| 104 | +/// This type represents a stack backtrace for an OS thread captured at a |
| 105 | +/// previous point in time. In some instances the `Backtrace` type may |
| 106 | +/// internally be empty due to configuration. For more information see |
| 107 | +/// `Backtrace::capture`. |
| 108 | +pub struct Backtrace { |
| 109 | + inner: Inner, |
| 110 | +} |
| 111 | + |
| 112 | +/// The current status of a backtrace, indicating whether it was captured or |
| 113 | +/// whether it is empty for some other reason. |
| 114 | +#[non_exhaustive] |
| 115 | +#[derive(Debug)] |
| 116 | +pub enum BacktraceStatus { |
| 117 | + /// Capturing a backtrace is not supported, likely because it's not |
| 118 | + /// implemented for the current platform. |
| 119 | + Unsupported, |
| 120 | + /// Capturing a backtrace has been disabled through either the |
| 121 | + /// `RUST_LIB_BACKTRACE` or `RUST_BACKTRACE` environment variables. |
| 122 | + Disabled, |
| 123 | + /// A backtrace has been captured and the `Backtrace` should print |
| 124 | + /// reasonable information when rendered. |
| 125 | + Captured, |
| 126 | +} |
| 127 | + |
| 128 | +enum Inner { |
| 129 | + Unsupported, |
| 130 | + Disabled, |
| 131 | + Captured(Mutex<Capture>), |
| 132 | +} |
| 133 | + |
| 134 | +struct Capture { |
| 135 | + actual_start: usize, |
| 136 | + resolved: bool, |
| 137 | + frames: Vec<BacktraceFrame>, |
| 138 | +} |
| 139 | + |
| 140 | +fn _assert_send_sync() { |
| 141 | + fn _assert<T: Send + Sync>() {} |
| 142 | + _assert::<Backtrace>(); |
| 143 | +} |
| 144 | + |
| 145 | +struct BacktraceFrame { |
| 146 | + frame: backtrace::Frame, |
| 147 | + symbols: Vec<BacktraceSymbol>, |
| 148 | +} |
| 149 | + |
| 150 | +struct BacktraceSymbol { |
| 151 | + name: Option<Vec<u8>>, |
| 152 | + filename: Option<BytesOrWide>, |
| 153 | + lineno: Option<u32>, |
| 154 | +} |
| 155 | + |
| 156 | +enum BytesOrWide { |
| 157 | + Bytes(Vec<u8>), |
| 158 | + Wide(Vec<u16>), |
| 159 | +} |
| 160 | + |
| 161 | +impl Backtrace { |
| 162 | + /// Returns whether backtrace captures are enabled through environment |
| 163 | + /// variables. |
| 164 | + fn enabled() -> bool { |
| 165 | + // Cache the result of reading the environment variables to make |
| 166 | + // backtrace captures speedy, because otherwise reading environment |
| 167 | + // variables every time can be somewhat slow. |
| 168 | + static ENABLED: AtomicUsize = AtomicUsize::new(0); |
| 169 | + match ENABLED.load(SeqCst) { |
| 170 | + 0 => {} |
| 171 | + 1 => return false, |
| 172 | + _ => return true, |
| 173 | + } |
| 174 | + let enabled = match env::var("RUST_LIB_BACKTRACE") { |
| 175 | + Ok(s) => s != "0", |
| 176 | + Err(_) => match env::var("RUST_BACKTRACE") { |
| 177 | + Ok(s) => s != "0", |
| 178 | + Err(_) => false, |
| 179 | + }, |
| 180 | + }; |
| 181 | + ENABLED.store(enabled as usize + 1, SeqCst); |
| 182 | + return enabled; |
| 183 | + } |
| 184 | + |
| 185 | + /// Capture a stack backtrace of the current thread. |
| 186 | + /// |
| 187 | + /// This function will capture a stack backtrace of the current OS thread of |
| 188 | + /// execution, returning a `Backtrace` type which can be later used to print |
| 189 | + /// the entire stack trace or render it to a string. |
| 190 | + /// |
| 191 | + /// This function will be a noop if the `RUST_BACKTRACE` or |
| 192 | + /// `RUST_LIB_BACKTRACE` backtrace variables are both not set. If either |
| 193 | + /// environment variable is set and enabled then this function will actually |
| 194 | + /// capture a backtrace. Capturing a backtrace can be both memory intensive |
| 195 | + /// and slow, so these environment variables allow liberally using |
| 196 | + /// `Backtrace::capture` and only incurring a slowdown when the environment |
| 197 | + /// variables are set. |
| 198 | + /// |
| 199 | + /// To forcibly capture a backtrace regardless of environment variables, use |
| 200 | + /// the `Backtrace::force_capture` function. |
| 201 | + #[inline(never)] // want to make sure there's a frame here to remove |
| 202 | + pub fn capture() -> Backtrace { |
| 203 | + if !Backtrace::enabled() { |
| 204 | + return Backtrace { inner: Inner::Disabled }; |
| 205 | + } |
| 206 | + Backtrace::create(Backtrace::capture as usize) |
| 207 | + } |
| 208 | + |
| 209 | + /// Forcibly captures a full backtrace, regardless of environment variable |
| 210 | + /// configuration. |
| 211 | + /// |
| 212 | + /// This function behaves the same as `capture` except that it ignores the |
| 213 | + /// values of the `RUST_BACKTRACE` and `RUST_LIB_BACKTRACE` environment |
| 214 | + /// variables, always capturing a backtrace. |
| 215 | + /// |
| 216 | + /// Note that capturing a backtrace can be an expensive operation on some |
| 217 | + /// platforms, so this should be used with caution in performance-sensitive |
| 218 | + /// parts of code. |
| 219 | + #[inline(never)] // want to make sure there's a frame here to remove |
| 220 | + pub fn force_capture() -> Backtrace { |
| 221 | + Backtrace::create(Backtrace::force_capture as usize) |
| 222 | + } |
| 223 | + |
| 224 | + // Capture a backtrace which start just before the function addressed by |
| 225 | + // `ip` |
| 226 | + fn create(ip: usize) -> Backtrace { |
| 227 | + let _lock = lock(); |
| 228 | + let mut frames = Vec::new(); |
| 229 | + let mut actual_start = None; |
| 230 | + unsafe { |
| 231 | + backtrace::trace_unsynchronized(|frame| { |
| 232 | + frames.push(BacktraceFrame { frame: frame.clone(), symbols: Vec::new() }); |
| 233 | + if frame.symbol_address() as usize == ip && actual_start.is_none() { |
| 234 | + actual_start = Some(frames.len()); |
| 235 | + } |
| 236 | + true |
| 237 | + }); |
| 238 | + } |
| 239 | + |
| 240 | + // If no frames came out assume that this is an unsupported platform |
| 241 | + // since `backtrace` doesn't provide a way of learning this right now, |
| 242 | + // and this should be a good enough approximation. |
| 243 | + let inner = if frames.len() == 0 { |
| 244 | + Inner::Unsupported |
| 245 | + } else { |
| 246 | + Inner::Captured(Mutex::new(Capture { |
| 247 | + actual_start: actual_start.unwrap_or(0), |
| 248 | + frames, |
| 249 | + resolved: false, |
| 250 | + })) |
| 251 | + }; |
| 252 | + |
| 253 | + Backtrace { inner } |
| 254 | + } |
| 255 | + |
| 256 | + /// Returns the status of this backtrace, indicating whether this backtrace |
| 257 | + /// request was unsupported, disabled, or a stack trace was actually |
| 258 | + /// captured. |
| 259 | + pub fn status(&self) -> BacktraceStatus { |
| 260 | + match self.inner { |
| 261 | + Inner::Unsupported => BacktraceStatus::Unsupported, |
| 262 | + Inner::Disabled => BacktraceStatus::Disabled, |
| 263 | + Inner::Captured(_) => BacktraceStatus::Captured, |
| 264 | + } |
| 265 | + } |
| 266 | +} |
| 267 | + |
| 268 | +impl fmt::Display for Backtrace { |
| 269 | + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 270 | + fmt::Debug::fmt(self, fmt) |
| 271 | + } |
| 272 | +} |
| 273 | + |
| 274 | +impl fmt::Debug for Backtrace { |
| 275 | + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 276 | + let mut capture = match &self.inner { |
| 277 | + Inner::Unsupported => return fmt.write_str("unsupported backtrace"), |
| 278 | + Inner::Disabled => return fmt.write_str("disabled backtrace"), |
| 279 | + Inner::Captured(c) => c.lock().unwrap(), |
| 280 | + }; |
| 281 | + capture.resolve(); |
| 282 | + |
| 283 | + let full = fmt.alternate(); |
| 284 | + let (frames, style) = if full { |
| 285 | + (&capture.frames[..], backtrace::PrintFmt::Full) |
| 286 | + } else { |
| 287 | + (&capture.frames[capture.actual_start..], backtrace::PrintFmt::Short) |
| 288 | + }; |
| 289 | + |
| 290 | + // When printing paths we try to strip the cwd if it exists, otherwise |
| 291 | + // we just print the path as-is. Note that we also only do this for the |
| 292 | + // short format, because if it's full we presumably want to print |
| 293 | + // everything. |
| 294 | + let cwd = crate::env::current_dir(); |
| 295 | + let mut print_path = move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| { |
| 296 | + output_filename(fmt, path, style, cwd.as_ref().ok()) |
| 297 | + }; |
| 298 | + |
| 299 | + let mut f = backtrace::BacktraceFmt::new(fmt, style, &mut print_path); |
| 300 | + f.add_context()?; |
| 301 | + for frame in frames { |
| 302 | + let mut f = f.frame(); |
| 303 | + if frame.symbols.is_empty() { |
| 304 | + f.print_raw(frame.frame.ip(), None, None, None)?; |
| 305 | + } else { |
| 306 | + for symbol in frame.symbols.iter() { |
| 307 | + f.print_raw( |
| 308 | + frame.frame.ip(), |
| 309 | + symbol.name.as_ref().map(|b| backtrace::SymbolName::new(b)), |
| 310 | + symbol.filename.as_ref().map(|b| match b { |
| 311 | + BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w), |
| 312 | + BytesOrWide::Wide(w) => BytesOrWideString::Wide(w), |
| 313 | + }), |
| 314 | + symbol.lineno, |
| 315 | + )?; |
| 316 | + } |
| 317 | + } |
| 318 | + } |
| 319 | + f.finish()?; |
| 320 | + Ok(()) |
| 321 | + } |
| 322 | +} |
| 323 | + |
| 324 | +impl Capture { |
| 325 | + fn resolve(&mut self) { |
| 326 | + // If we're already resolved, nothing to do! |
| 327 | + if self.resolved { |
| 328 | + return; |
| 329 | + } |
| 330 | + self.resolved = true; |
| 331 | + |
| 332 | + // Use the global backtrace lock to synchronize this as it's a |
| 333 | + // requirement of the `backtrace` crate, and then actually resolve |
| 334 | + // everything. |
| 335 | + let _lock = lock(); |
| 336 | + for frame in self.frames.iter_mut() { |
| 337 | + let symbols = &mut frame.symbols; |
| 338 | + unsafe { |
| 339 | + backtrace::resolve_frame_unsynchronized(&frame.frame, |symbol| { |
| 340 | + symbols.push(BacktraceSymbol { |
| 341 | + name: symbol.name().map(|m| m.as_bytes().to_vec()), |
| 342 | + filename: symbol.filename_raw().map(|b| match b { |
| 343 | + BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()), |
| 344 | + BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()), |
| 345 | + }), |
| 346 | + lineno: symbol.lineno(), |
| 347 | + }); |
| 348 | + }); |
| 349 | + } |
| 350 | + } |
| 351 | + } |
| 352 | +} |
0 commit comments