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