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