]> git.proxmox.com Git - rustc.git/blame - vendor/backtrace/src/print.rs
New upstream version 1.42.0+dfsg0+pve1
[rustc.git] / vendor / backtrace / src / print.rs
CommitLineData
e1599b0c
XL
1use crate::BytesOrWideString;
2use core::ffi::c_void;
3use core::fmt;
4
5const HEX_WIDTH: usize = 2 + 2 * core::mem::size_of::<usize>();
6
7#[cfg(target_os = "fuchsia")]
8mod 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.
15pub 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)]
24pub 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
33impl<'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 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.
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.
82 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.
91pub struct BacktraceFrameFmt<'fmt, 'a, 'b> {
92 fmt: &'fmt mut BacktraceFmt<'a, 'b>,
93 symbol_index: usize,
94}
95
96impl 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")]
107 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")]
125 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.
146 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.
161 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)]
181 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"))]
200 {
e74abb32
XL
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 _;
e1599b0c
XL
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
238 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
253 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
264impl Drop for BacktraceFrameFmt<'_, '_, '_> {
265 fn drop(&mut self) {
266 self.fmt.frame_index += 1;
267 }
268}