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