1 #[cfg(feature = "std")] 2 use super::{BacktraceFrame, BacktraceSymbol}; 3 use super::{BytesOrWideString, Frame, SymbolName}; 4 use core::ffi::c_void; 5 use core::fmt; 6 7 const HEX_WIDTH: usize = 2 + 2 * core::mem::size_of::<usize>(); 8 9 #[cfg(target_os = "fuchsia")] 10 mod fuchsia; 11 12 /// A formatter for backtraces. 13 /// 14 /// This type can be used to print a backtrace regardless of where the backtrace 15 /// itself comes from. If you have a `Backtrace` type then its `Debug` 16 /// implementation already uses this printing format. 17 pub struct BacktraceFmt<'a, 'b> { 18 fmt: &'a mut fmt::Formatter<'b>, 19 frame_index: usize, 20 format: PrintFmt, 21 print_path: 22 &'a mut (dyn FnMut(&mut fmt::Formatter<'_>, BytesOrWideString<'_>) -> fmt::Result + 'b), 23 } 24 25 /// The styles of printing that we can print 26 #[derive(Copy, Clone, Eq, PartialEq)] 27 pub enum PrintFmt { 28 /// Prints a terser backtrace which ideally only contains relevant information 29 Short, 30 /// Prints a backtrace that contains all possible information 31 Full, 32 #[doc(hidden)] 33 __Nonexhaustive, 34 } 35 36 impl<'a, 'b> BacktraceFmt<'a, 'b> { 37 /// Create a new `BacktraceFmt` which will write output to the provided 38 /// `fmt`. 39 /// 40 /// The `format` argument will control the style in which the backtrace is 41 /// printed, and the `print_path` argument will be used to print the 42 /// `BytesOrWideString` instances of filenames. This type itself doesn't do 43 /// any printing of filenames, but this callback is required to do so. new( fmt: &'a mut fmt::Formatter<'b>, format: PrintFmt, print_path: &'a mut (dyn FnMut(&mut fmt::Formatter<'_>, BytesOrWideString<'_>) -> fmt::Result + 'b), ) -> Self44 pub fn new( 45 fmt: &'a mut fmt::Formatter<'b>, 46 format: PrintFmt, 47 print_path: &'a mut (dyn FnMut(&mut fmt::Formatter<'_>, BytesOrWideString<'_>) -> fmt::Result 48 + 'b), 49 ) -> Self { 50 BacktraceFmt { 51 fmt, 52 frame_index: 0, 53 format, 54 print_path, 55 } 56 } 57 58 /// Prints a preamble for the backtrace about to be printed. 59 /// 60 /// This is required on some platforms for backtraces to be fully 61 /// symbolicated later, and otherwise this should just be the first method 62 /// you call after creating a `BacktraceFmt`. add_context(&mut self) -> fmt::Result63 pub fn add_context(&mut self) -> fmt::Result { 64 #[cfg(target_os = "fuchsia")] 65 fuchsia::print_dso_context(self.fmt)?; 66 Ok(()) 67 } 68 69 /// Adds a frame to the backtrace output. 70 /// 71 /// This commit returns an RAII instance of a `BacktraceFrameFmt` which can be used 72 /// to actually print a frame, and on destruction it will increment the 73 /// frame counter. frame(&mut self) -> BacktraceFrameFmt<'_, 'a, 'b>74 pub fn frame(&mut self) -> BacktraceFrameFmt<'_, 'a, 'b> { 75 BacktraceFrameFmt { 76 fmt: self, 77 symbol_index: 0, 78 } 79 } 80 81 /// Completes the backtrace output. 82 /// 83 /// This is currently a no-op but is added for future compatibility with 84 /// backtrace formats. finish(&mut self) -> fmt::Result85 pub fn finish(&mut self) -> fmt::Result { 86 // Currently a no-op-- including this hook to allow for future additions. 87 Ok(()) 88 } 89 } 90 91 /// A formatter for just one frame of a backtrace. 92 /// 93 /// This type is created by the `BacktraceFmt::frame` function. 94 pub struct BacktraceFrameFmt<'fmt, 'a, 'b> { 95 fmt: &'fmt mut BacktraceFmt<'a, 'b>, 96 symbol_index: usize, 97 } 98 99 impl BacktraceFrameFmt<'_, '_, '_> { 100 /// Prints a `BacktraceFrame` with this frame formatter. 101 /// 102 /// This will recursively print all `BacktraceSymbol` instances within the 103 /// `BacktraceFrame`. 104 /// 105 /// # Required features 106 /// 107 /// This function requires the `std` feature of the `backtrace` crate to be 108 /// enabled, and the `std` feature is enabled by default. 109 #[cfg(feature = "std")] backtrace_frame(&mut self, frame: &BacktraceFrame) -> fmt::Result110 pub fn backtrace_frame(&mut self, frame: &BacktraceFrame) -> fmt::Result { 111 let symbols = frame.symbols(); 112 for symbol in symbols { 113 self.backtrace_symbol(frame, symbol)?; 114 } 115 if symbols.is_empty() { 116 self.print_raw(frame.ip(), None, None, None)?; 117 } 118 Ok(()) 119 } 120 121 /// Prints a `BacktraceSymbol` within a `BacktraceFrame`. 122 /// 123 /// # Required features 124 /// 125 /// This function requires the `std` feature of the `backtrace` crate to be 126 /// enabled, and the `std` feature is enabled by default. 127 #[cfg(feature = "std")] backtrace_symbol( &mut self, frame: &BacktraceFrame, symbol: &BacktraceSymbol, ) -> fmt::Result128 pub fn backtrace_symbol( 129 &mut self, 130 frame: &BacktraceFrame, 131 symbol: &BacktraceSymbol, 132 ) -> fmt::Result { 133 self.print_raw_with_column( 134 frame.ip(), 135 symbol.name(), 136 // TODO: this isn't great that we don't end up printing anything 137 // with non-utf8 filenames. Thankfully almost everything is utf8 so 138 // this shouldn't be too too bad. 139 symbol 140 .filename() 141 .and_then(|p| Some(BytesOrWideString::Bytes(p.to_str()?.as_bytes()))), 142 symbol.lineno(), 143 symbol.colno(), 144 )?; 145 Ok(()) 146 } 147 148 /// Prints a raw traced `Frame` and `Symbol`, typically from within the raw 149 /// callbacks of this crate. symbol(&mut self, frame: &Frame, symbol: &super::Symbol) -> fmt::Result150 pub fn symbol(&mut self, frame: &Frame, symbol: &super::Symbol) -> fmt::Result { 151 self.print_raw_with_column( 152 frame.ip(), 153 symbol.name(), 154 symbol.filename_raw(), 155 symbol.lineno(), 156 symbol.colno(), 157 )?; 158 Ok(()) 159 } 160 161 /// Adds a raw frame to the backtrace output. 162 /// 163 /// This method, unlike the previous, takes the raw arguments in case 164 /// they're being source from different locations. Note that this may be 165 /// called multiple times for one frame. print_raw( &mut self, frame_ip: *mut c_void, symbol_name: Option<SymbolName<'_>>, filename: Option<BytesOrWideString<'_>>, lineno: Option<u32>, ) -> fmt::Result166 pub fn print_raw( 167 &mut self, 168 frame_ip: *mut c_void, 169 symbol_name: Option<SymbolName<'_>>, 170 filename: Option<BytesOrWideString<'_>>, 171 lineno: Option<u32>, 172 ) -> fmt::Result { 173 self.print_raw_with_column(frame_ip, symbol_name, filename, lineno, None) 174 } 175 176 /// Adds a raw frame to the backtrace output, including column information. 177 /// 178 /// This method, like the previous, takes the raw arguments in case 179 /// they're being source from different locations. Note that this may be 180 /// called multiple times for one frame. print_raw_with_column( &mut self, frame_ip: *mut c_void, symbol_name: Option<SymbolName<'_>>, filename: Option<BytesOrWideString<'_>>, lineno: Option<u32>, colno: Option<u32>, ) -> fmt::Result181 pub fn print_raw_with_column( 182 &mut self, 183 frame_ip: *mut c_void, 184 symbol_name: Option<SymbolName<'_>>, 185 filename: Option<BytesOrWideString<'_>>, 186 lineno: Option<u32>, 187 colno: Option<u32>, 188 ) -> fmt::Result { 189 // Fuchsia is unable to symbolize within a process so it has a special 190 // format which can be used to symbolize later. Print that instead of 191 // printing addresses in our own format here. 192 if cfg!(target_os = "fuchsia") { 193 self.print_raw_fuchsia(frame_ip)?; 194 } else { 195 self.print_raw_generic(frame_ip, symbol_name, filename, lineno, colno)?; 196 } 197 self.symbol_index += 1; 198 Ok(()) 199 } 200 201 #[allow(unused_mut)] print_raw_generic( &mut self, mut frame_ip: *mut c_void, symbol_name: Option<SymbolName<'_>>, filename: Option<BytesOrWideString<'_>>, lineno: Option<u32>, colno: Option<u32>, ) -> fmt::Result202 fn print_raw_generic( 203 &mut self, 204 mut frame_ip: *mut c_void, 205 symbol_name: Option<SymbolName<'_>>, 206 filename: Option<BytesOrWideString<'_>>, 207 lineno: Option<u32>, 208 colno: Option<u32>, 209 ) -> fmt::Result { 210 // No need to print "null" frames, it basically just means that the 211 // system backtrace was a bit eager to trace back super far. 212 if let PrintFmt::Short = self.fmt.format { 213 if frame_ip.is_null() { 214 return Ok(()); 215 } 216 } 217 218 // To reduce TCB size in Sgx enclave, we do not want to implement symbol 219 // resolution functionality. Rather, we can print the offset of the 220 // address here, which could be later mapped to correct function. 221 #[cfg(all(feature = "std", target_env = "sgx", target_vendor = "fortanix"))] 222 { 223 let image_base = std::os::fortanix_sgx::mem::image_base(); 224 frame_ip = usize::wrapping_sub(frame_ip as usize, image_base as _) as _; 225 } 226 227 // Print the index of the frame as well as the optional instruction 228 // pointer of the frame. If we're beyond the first symbol of this frame 229 // though we just print appropriate whitespace. 230 if self.symbol_index == 0 { 231 write!(self.fmt.fmt, "{:4}: ", self.fmt.frame_index)?; 232 if let PrintFmt::Full = self.fmt.format { 233 write!(self.fmt.fmt, "{:1$?} - ", frame_ip, HEX_WIDTH)?; 234 } 235 } else { 236 write!(self.fmt.fmt, " ")?; 237 if let PrintFmt::Full = self.fmt.format { 238 write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH + 3)?; 239 } 240 } 241 242 // Next up write out the symbol name, using the alternate formatting for 243 // more information if we're a full backtrace. Here we also handle 244 // symbols which don't have a name, 245 match (symbol_name, &self.fmt.format) { 246 (Some(name), PrintFmt::Short) => write!(self.fmt.fmt, "{:#}", name)?, 247 (Some(name), PrintFmt::Full) => write!(self.fmt.fmt, "{}", name)?, 248 (None, _) | (_, PrintFmt::__Nonexhaustive) => write!(self.fmt.fmt, "<unknown>")?, 249 } 250 self.fmt.fmt.write_str("\n")?; 251 252 // And last up, print out the filename/line number if they're available. 253 if let (Some(file), Some(line)) = (filename, lineno) { 254 self.print_fileline(file, line, colno)?; 255 } 256 257 Ok(()) 258 } 259 print_fileline( &mut self, file: BytesOrWideString<'_>, line: u32, colno: Option<u32>, ) -> fmt::Result260 fn print_fileline( 261 &mut self, 262 file: BytesOrWideString<'_>, 263 line: u32, 264 colno: Option<u32>, 265 ) -> fmt::Result { 266 // Filename/line are printed on lines under the symbol name, so print 267 // some appropriate whitespace to sort of right-align ourselves. 268 if let PrintFmt::Full = self.fmt.format { 269 write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH)?; 270 } 271 write!(self.fmt.fmt, " at ")?; 272 273 // Delegate to our internal callback to print the filename and then 274 // print out the line number. 275 (self.fmt.print_path)(self.fmt.fmt, file)?; 276 write!(self.fmt.fmt, ":{}", line)?; 277 278 // Add column number, if available. 279 if let Some(colno) = colno { 280 write!(self.fmt.fmt, ":{}", colno)?; 281 } 282 283 write!(self.fmt.fmt, "\n")?; 284 Ok(()) 285 } 286 print_raw_fuchsia(&mut self, frame_ip: *mut c_void) -> fmt::Result287 fn print_raw_fuchsia(&mut self, frame_ip: *mut c_void) -> fmt::Result { 288 // We only care about the first symbol of a frame 289 if self.symbol_index == 0 { 290 self.fmt.fmt.write_str("{{{bt:")?; 291 write!(self.fmt.fmt, "{}:{:?}", self.fmt.frame_index, frame_ip)?; 292 self.fmt.fmt.write_str("}}}\n")?; 293 } 294 Ok(()) 295 } 296 } 297 298 impl Drop for BacktraceFrameFmt<'_, '_, '_> { drop(&mut self)299 fn drop(&mut self) { 300 self.fmt.frame_index += 1; 301 } 302 } 303