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