1#[cfg(feature = "std")]
2use super::{BacktraceFrame, BacktraceSymbol};
3use super::{BytesOrWideString, Frame, SymbolName};
4use core::ffi::c_void;
5use core::fmt;
67const HEX_WIDTH: usize = 2 + 2 * core::mem::size_of::<usize>();
89#[cfg(target_os = "fuchsia")]
10mod fuchsia;
1112/// 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}
2425/// The styles of printing that we can print
26#[derive(#[automatically_derived]
impl ::core::marker::Copy for PrintFmt { }Copy, #[automatically_derived]
impl ::core::clone::Clone for PrintFmt {
#[inline]
fn clone(&self) -> PrintFmt { *self }
}Clone, #[automatically_derived]
impl ::core::cmp::Eq for PrintFmt {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::cmp::PartialEq for PrintFmt {
#[inline]
fn eq(&self, other: &PrintFmt) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq)]
27#[non_exhaustive]
28pub enum PrintFmt {
29/// Prints a terser backtrace which ideally only contains relevant information
30Short,
31/// Prints a backtrace that contains all possible information
32Full,
33}
3435impl<'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.
43pub fn new(
44 fmt: &'a mut fmt::Formatter<'b>,
45 format: PrintFmt,
46 print_path: &'a mut (
47dyn FnMut(&mut fmt::Formatter<'_>, BytesOrWideString<'_>) -> fmt::Result + 'b
48),
49 ) -> Self {
50BacktraceFmt {
51fmt,
52 frame_index: 0,
53format,
54print_path,
55 }
56 }
5758/// 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`.
63pub fn add_context(&mut self) -> fmt::Result {
64#[cfg(target_os = "fuchsia")]
65fuchsia::print_dso_context(self.fmt)?;
66Ok(())
67 }
6869/// 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.
74pub fn frame(&mut self) -> BacktraceFrameFmt<'_, 'a, 'b> {
75BacktraceFrameFmt {
76 fmt: self,
77 symbol_index: 0,
78 }
79 }
8081/// Completes the backtrace output.
82 ///
83 /// This is currently a no-op but is added for future compatibility with
84 /// backtrace formats.
85pub fn finish(&mut self) -> fmt::Result {
86#[cfg(target_os = "fuchsia")]
87fuchsia::finish_context(self.fmt)?;
88Ok(())
89 }
9091/// Inserts a message in the backtrace output.
92 ///
93 /// This allows information to be inserted between frames,
94 /// and won't increment the `frame_index` unlike the `frame`
95 /// method.
96pub fn message(&mut self, msg: &str) -> fmt::Result {
97self.fmt.write_str(msg)
98 }
99100/// Return the inner formatter.
101 ///
102 /// This is used for writing custom information between frames with `write!` and `writeln!`,
103 /// and won't increment the `frame_index` unlike the `frame` method.
104pub fn formatter(&mut self) -> &mut fmt::Formatter<'b> {
105self.fmt
106 }
107}
108109/// A formatter for just one frame of a backtrace.
110///
111/// This type is created by the `BacktraceFmt::frame` function.
112pub struct BacktraceFrameFmt<'fmt, 'a, 'b> {
113 fmt: &'fmt mut BacktraceFmt<'a, 'b>,
114 symbol_index: usize,
115}
116117impl BacktraceFrameFmt<'_, '_, '_> {
118/// Prints a `BacktraceFrame` with this frame formatter.
119 ///
120 /// This will recursively print all `BacktraceSymbol` instances within the
121 /// `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")]
128pub fn backtrace_frame(&mut self, frame: &BacktraceFrame) -> fmt::Result {
129let symbols = frame.symbols();
130for symbol in symbols {
131self.backtrace_symbol(frame, symbol)?;
132 }
133if symbols.is_empty() {
134self.print_raw(frame.ip(), None, None, None)?;
135 }
136Ok(())
137 }
138139/// Prints a `BacktraceSymbol` within a `BacktraceFrame`.
140 ///
141 /// # Required features
142 ///
143 /// This function requires the `std` feature of the `backtrace` crate to be
144 /// enabled, and the `std` feature is enabled by default.
145#[cfg(feature = "std")]
146pub fn backtrace_symbol(
147&mut self,
148 frame: &BacktraceFrame,
149 symbol: &BacktraceSymbol,
150 ) -> fmt::Result {
151self.print_raw_with_column(
152 frame.ip(),
153 symbol.name(),
154// TODO: this isn't great that we don't end up printing anything
155 // with non-utf8 filenames. Thankfully almost everything is utf8 so
156 // this shouldn't be too bad.
157symbol
158 .filename()
159 .and_then(|p| Some(BytesOrWideString::Bytes(p.to_str()?.as_bytes()))),
160 symbol.lineno(),
161 symbol.colno(),
162 )?;
163Ok(())
164 }
165166/// Prints a raw traced `Frame` and `Symbol`, typically from within the raw
167 /// callbacks of this crate.
168pub fn symbol(&mut self, frame: &Frame, symbol: &super::Symbol) -> fmt::Result {
169self.print_raw_with_column(
170 frame.ip(),
171 symbol.name(),
172 symbol.filename_raw(),
173 symbol.lineno(),
174 symbol.colno(),
175 )?;
176Ok(())
177 }
178179/// Adds a raw frame to the backtrace output.
180 ///
181 /// This method, unlike the previous, takes the raw arguments in case
182 /// they're being source from different locations. Note that this may be
183 /// called multiple times for one frame.
184pub fn print_raw(
185&mut self,
186 frame_ip: *mut c_void,
187 symbol_name: Option<SymbolName<'_>>,
188 filename: Option<BytesOrWideString<'_>>,
189 lineno: Option<u32>,
190 ) -> fmt::Result {
191self.print_raw_with_column(frame_ip, symbol_name, filename, lineno, None)
192 }
193194/// Adds a raw frame to the backtrace output, including column information.
195 ///
196 /// This method, like the previous, takes the raw arguments in case
197 /// they're being source from different locations. Note that this may be
198 /// called multiple times for one frame.
199pub fn print_raw_with_column(
200&mut self,
201 frame_ip: *mut c_void,
202 symbol_name: Option<SymbolName<'_>>,
203 filename: Option<BytesOrWideString<'_>>,
204 lineno: Option<u32>,
205 colno: Option<u32>,
206 ) -> fmt::Result {
207// Fuchsia is unable to symbolize within a process so it has a special
208 // format which can be used to symbolize later. Print that instead of
209 // printing addresses in our own format here.
210if falsecfg!(target_os = "fuchsia") {
211self.print_raw_fuchsia(frame_ip)?;
212 } else {
213self.print_raw_generic(frame_ip, symbol_name, filename, lineno, colno)?;
214 }
215self.symbol_index += 1;
216Ok(())
217 }
218219#[allow(unused_mut)]
220fn print_raw_generic(
221&mut self,
222 frame_ip: *mut c_void,
223 symbol_name: Option<SymbolName<'_>>,
224 filename: Option<BytesOrWideString<'_>>,
225 lineno: Option<u32>,
226 colno: Option<u32>,
227 ) -> fmt::Result {
228// No need to print "null" frames, it basically just means that the
229 // system backtrace was a bit eager to trace back super far.
230if let PrintFmt::Short = self.fmt.format {
231if frame_ip.is_null() {
232return Ok(());
233 }
234 }
235236// Print the index of the frame as well as the optional instruction
237 // pointer of the frame. If we're beyond the first symbol of this frame
238 // though we just print appropriate whitespace.
239if self.symbol_index == 0 {
240self.fmt.fmt.write_fmt(format_args!("{0:4}: ", self.fmt.frame_index))write!(self.fmt.fmt, "{:4}: ", self.fmt.frame_index)?;
241if let PrintFmt::Full = self.fmt.format {
242self.fmt.fmt.write_fmt(format_args!("{0:1$?} - ", frame_ip, HEX_WIDTH))write!(self.fmt.fmt, "{frame_ip:HEX_WIDTH$?} - ")?;
243 }
244 } else {
245self.fmt.fmt.write_fmt(format_args!(" "))write!(self.fmt.fmt, " ")?;
246if let PrintFmt::Full = self.fmt.format {
247self.fmt.fmt.write_fmt(format_args!("{0:1$}", "", HEX_WIDTH + 3))write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH + 3)?;
248 }
249 }
250251// Next up write out the symbol name, using the alternate formatting for
252 // more information if we're a full backtrace. Here we also handle
253 // symbols which don't have a name,
254match (symbol_name, &self.fmt.format) {
255 (Some(name), PrintFmt::Short) => self.fmt.fmt.write_fmt(format_args!("{0:#}", name))write!(self.fmt.fmt, "{name:#}")?,
256 (Some(name), PrintFmt::Full) => self.fmt.fmt.write_fmt(format_args!("{0}", name))write!(self.fmt.fmt, "{name}")?,
257 (None, _) => self.fmt.fmt.write_fmt(format_args!("<unknown>"))write!(self.fmt.fmt, "<unknown>")?,
258 }
259self.fmt.fmt.write_str("\n")?;
260261// And last up, print out the filename/line number if they're available.
262if let (Some(file), Some(line)) = (filename, lineno) {
263self.print_fileline(file, line, colno)?;
264 }
265266Ok(())
267 }
268269fn print_fileline(
270&mut self,
271 file: BytesOrWideString<'_>,
272 line: u32,
273 colno: Option<u32>,
274 ) -> fmt::Result {
275// Filename/line are printed on lines under the symbol name, so print
276 // some appropriate whitespace to sort of right-align ourselves.
277if let PrintFmt::Full = self.fmt.format {
278self.fmt.fmt.write_fmt(format_args!("{0:1$}", "", HEX_WIDTH))write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH)?;
279 }
280self.fmt.fmt.write_fmt(format_args!(" at "))write!(self.fmt.fmt, " at ")?;
281282// Delegate to our internal callback to print the filename and then
283 // print out the line number.
284(self.fmt.print_path)(self.fmt.fmt, file)?;
285self.fmt.fmt.write_fmt(format_args!(":{0}", line))write!(self.fmt.fmt, ":{line}")?;
286287// Add column number, if available.
288if let Some(colno) = colno {
289self.fmt.fmt.write_fmt(format_args!(":{0}", colno))write!(self.fmt.fmt, ":{colno}")?;
290 }
291292self.fmt.fmt.write_fmt(format_args!("\n"))writeln!(self.fmt.fmt)?;
293Ok(())
294 }
295296fn print_raw_fuchsia(&mut self, frame_ip: *mut c_void) -> fmt::Result {
297// We only care about the first symbol of a frame
298if self.symbol_index == 0 {
299self.fmt.fmt.write_str("{{{bt:")?;
300self.fmt.fmt.write_fmt(format_args!("{0}:{1:?}", self.fmt.frame_index,
frame_ip))write!(self.fmt.fmt, "{}:{:?}", self.fmt.frame_index, frame_ip)?;
301self.fmt.fmt.write_str("}}}\n")?;
302 }
303Ok(())
304 }
305}
306307impl Dropfor BacktraceFrameFmt<'_, '_, '_> {
308fn drop(&mut self) {
309self.fmt.frame_index += 1;
310 }
311}