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