]> git.proxmox.com Git - rustc.git/blob - vendor/backtrace/src/print.rs
New upstream version 1.43.0+dfsg1
[rustc.git] / vendor / backtrace / src / print.rs
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.
41 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`.
59 pub fn add_context(&mut self) -> fmt::Result {
60 #[cfg(target_os = "fuchsia")]
61 fuchsia::print_dso_context(self.fmt)?;
62 Ok(())
63 }
64
65 /// Adds a frame to the backtrace output.
66 ///
67 /// This commit returns an RAII instance of a `BacktraceFrameFmt` which can be used
68 /// to actually print a frame, and on destruction it will increment the
69 /// frame counter.
70 pub fn frame(&mut self) -> BacktraceFrameFmt<'_, 'a, 'b> {
71 BacktraceFrameFmt {
72 fmt: self,
73 symbol_index: 0,
74 }
75 }
76
77 /// Completes the backtrace output.
78 ///
79 /// This is currently a no-op but is added for future compatibility with
80 /// backtrace formats.
81 pub fn finish(&mut self) -> fmt::Result {
82 // Currently a no-op-- including this hook to allow for future additions.
83 Ok(())
84 }
85 }
86
87 /// A formatter for just one frame of a backtrace.
88 ///
89 /// This type is created by the `BacktraceFmt::frame` function.
90 pub struct BacktraceFrameFmt<'fmt, 'a, 'b> {
91 fmt: &'fmt mut BacktraceFmt<'a, 'b>,
92 symbol_index: usize,
93 }
94
95 impl BacktraceFrameFmt<'_, '_, '_> {
96 /// Prints a `BacktraceFrame` with this frame formatter.
97 ///
98 /// This will recusrively print all `BacktraceSymbol` instances within the
99 /// `BacktraceFrame`.
100 ///
101 /// # Required features
102 ///
103 /// This function requires the `std` feature of the `backtrace` crate to be
104 /// enabled, and the `std` feature is enabled by default.
105 #[cfg(feature = "std")]
106 pub fn backtrace_frame(&mut self, frame: &crate::BacktraceFrame) -> fmt::Result {
107 let symbols = frame.symbols();
108 for symbol in symbols {
109 self.backtrace_symbol(frame, symbol)?;
110 }
111 if symbols.is_empty() {
112 self.print_raw(frame.ip(), None, None, None)?;
113 }
114 Ok(())
115 }
116
117 /// Prints a `BacktraceSymbol` within a `BacktraceFrame`.
118 ///
119 /// # Required features
120 ///
121 /// This function requires the `std` feature of the `backtrace` crate to be
122 /// enabled, and the `std` feature is enabled by default.
123 #[cfg(feature = "std")]
124 pub fn backtrace_symbol(
125 &mut self,
126 frame: &crate::BacktraceFrame,
127 symbol: &crate::BacktraceSymbol,
128 ) -> fmt::Result {
129 self.print_raw(
130 frame.ip(),
131 symbol.name(),
132 // TODO: this isn't great that we don't end up printing anything
133 // with non-utf8 filenames. Thankfully almost everything is utf8 so
134 // this shouldn't be too too bad.
135 symbol
136 .filename()
137 .and_then(|p| Some(BytesOrWideString::Bytes(p.to_str()?.as_bytes()))),
138 symbol.lineno(),
139 )?;
140 Ok(())
141 }
142
143 /// Prints a raw traced `Frame` and `Symbol`, typically from within the raw
144 /// callbacks of this crate.
145 pub fn symbol(&mut self, frame: &crate::Frame, symbol: &crate::Symbol) -> fmt::Result {
146 self.print_raw(
147 frame.ip(),
148 symbol.name(),
149 symbol.filename_raw(),
150 symbol.lineno(),
151 )?;
152 Ok(())
153 }
154
155 /// Adds a raw frame to the backtrace output.
156 ///
157 /// This method, unlike the previous, takes the raw arguments in case
158 /// they're being source from different locations. Note that this may be
159 /// called multiple times for one frame.
160 pub fn print_raw(
161 &mut self,
162 frame_ip: *mut c_void,
163 symbol_name: Option<crate::SymbolName>,
164 filename: Option<BytesOrWideString>,
165 lineno: Option<u32>,
166 ) -> fmt::Result {
167 // Fuchsia is unable to symbolize within a process so it has a special
168 // format which can be used to symbolize later. Print that instead of
169 // printing addresses in our own format here.
170 if cfg!(target_os = "fuchsia") {
171 self.print_raw_fuchsia(frame_ip)?;
172 } else {
173 self.print_raw_generic(frame_ip, symbol_name, filename, lineno)?;
174 }
175 self.symbol_index += 1;
176 Ok(())
177 }
178
179 #[allow(unused_mut)]
180 fn print_raw_generic(
181 &mut self,
182 mut frame_ip: *mut c_void,
183 symbol_name: Option<crate::SymbolName>,
184 filename: Option<BytesOrWideString>,
185 lineno: Option<u32>,
186 ) -> fmt::Result {
187 // No need to print "null" frames, it basically just means that the
188 // system backtrace was a bit eager to trace back super far.
189 if let PrintFmt::Short = self.fmt.format {
190 if frame_ip.is_null() {
191 return Ok(());
192 }
193 }
194
195 // To reduce TCB size in Sgx enclave, we do not want to implement symbol
196 // resolution functionality. Rather, we can print the offset of the
197 // address here, which could be later mapped to correct function.
198 #[cfg(all(feature = "std", target_env = "sgx", target_vendor = "fortanix"))]
199 {
200 let image_base = std::os::fortanix_sgx::mem::image_base();
201 frame_ip = usize::wrapping_sub(frame_ip as usize, image_base as _) as _;
202 }
203
204 // Print the index of the frame as well as the optional instruction
205 // pointer of the frame. If we're beyond the first symbol of this frame
206 // though we just print appropriate whitespace.
207 if self.symbol_index == 0 {
208 write!(self.fmt.fmt, "{:4}: ", self.fmt.frame_index)?;
209 if let PrintFmt::Full = self.fmt.format {
210 write!(self.fmt.fmt, "{:1$?} - ", frame_ip, HEX_WIDTH)?;
211 }
212 } else {
213 write!(self.fmt.fmt, " ")?;
214 if let PrintFmt::Full = self.fmt.format {
215 write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH + 3)?;
216 }
217 }
218
219 // Next up write out the symbol name, using the alternate formatting for
220 // more information if we're a full backtrace. Here we also handle
221 // symbols which don't have a name,
222 match (symbol_name, &self.fmt.format) {
223 (Some(name), PrintFmt::Short) => write!(self.fmt.fmt, "{:#}", name)?,
224 (Some(name), PrintFmt::Full) => write!(self.fmt.fmt, "{}", name)?,
225 (None, _) | (_, PrintFmt::__Nonexhaustive) => write!(self.fmt.fmt, "<unknown>")?,
226 }
227 self.fmt.fmt.write_str("\n")?;
228
229 // And last up, print out the filename/line number if they're available.
230 if let (Some(file), Some(line)) = (filename, lineno) {
231 self.print_fileline(file, line)?;
232 }
233
234 Ok(())
235 }
236
237 fn print_fileline(&mut self, file: BytesOrWideString, line: u32) -> fmt::Result {
238 // Filename/line are printed on lines under the symbol name, so print
239 // some appropriate whitespace to sort of right-align ourselves.
240 if let PrintFmt::Full = self.fmt.format {
241 write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH)?;
242 }
243 write!(self.fmt.fmt, " at ")?;
244
245 // Delegate to our internal callback to print the filename and then
246 // print out the line number.
247 (self.fmt.print_path)(self.fmt.fmt, file)?;
248 write!(self.fmt.fmt, ":{}\n", line)?;
249 Ok(())
250 }
251
252 fn print_raw_fuchsia(&mut self, frame_ip: *mut c_void) -> fmt::Result {
253 // We only care about the first symbol of a frame
254 if self.symbol_index == 0 {
255 self.fmt.fmt.write_str("{{{bt:")?;
256 write!(self.fmt.fmt, "{}:{:?}", self.fmt.frame_index, frame_ip)?;
257 self.fmt.fmt.write_str("}}}\n")?;
258 }
259 Ok(())
260 }
261 }
262
263 impl Drop for BacktraceFrameFmt<'_, '_, '_> {
264 fn drop(&mut self) {
265 self.fmt.frame_index += 1;
266 }
267 }