Next Generation WASM Microkernel Operating System
at main 185 lines 6.8 kB view raw
1// Copyright 2025 Jonas Kruckenberg 2// 3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or 4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or 5// http://opensource.org/licenses/MIT>, at your option. This file may not be 6// copied, modified, or distributed except according to those terms. 7 8//! Printing of backtraces. This is adapted from the standard library. 9 10use core::fmt; 11 12use crate::backtrace::BacktraceStyle; 13use crate::backtrace::symbolize::{Symbol, SymbolName}; 14 15const HEX_WIDTH: usize = 2 + 2 * size_of::<usize>(); 16 17/// A formatter for backtraces. 18/// 19/// This type can be used to print a backtrace regardless of where the backtrace 20/// itself comes from. If you have a `Backtrace` type then its `Debug` 21/// implementation already uses this printing format. 22pub struct BacktraceFmt<'a, 'b> { 23 fmt: &'a mut fmt::Formatter<'b>, 24 frame_index: usize, 25 format: BacktraceStyle, 26} 27impl<'a, 'b> BacktraceFmt<'a, 'b> { 28 /// Create a new `BacktraceFmt` which will write output to the provided 29 /// `fmt`. 30 /// 31 /// The `format` argument will control the style in which the backtrace is 32 /// printed, and the `print_path` argument will be used to print the 33 /// `BytesOrWideString` instances of filenames. This type itself doesn't do 34 /// any printing of filenames, but this callback is required to do so. 35 pub fn new(fmt: &'a mut fmt::Formatter<'b>, format: BacktraceStyle) -> Self { 36 BacktraceFmt { 37 fmt, 38 frame_index: 0, 39 format, 40 } 41 } 42 43 /// Adds a frame to the backtrace output. 44 /// 45 /// This commit returns an RAII instance of a `BacktraceFrameFmt` which can be used 46 /// to actually print a frame, and on destruction it will increment the 47 /// frame counter. 48 pub fn frame(&mut self) -> BacktraceFrameFmt<'_, 'a, 'b> { 49 BacktraceFrameFmt { 50 fmt: self, 51 symbol_index: 0, 52 } 53 } 54 55 /// Return the inner formatter. 56 /// 57 /// This is used for writing custom information between frames with `write!` and `writeln!`, 58 /// and won't increment the `frame_index` unlike the `frame` method. 59 pub fn formatter(&mut self) -> &mut fmt::Formatter<'b> { 60 self.fmt 61 } 62} 63 64/// A formatter for just one frame of a backtrace. 65/// 66/// This type is created by the `BacktraceFmt::frame` function. 67pub struct BacktraceFrameFmt<'fmt, 'a, 'b> { 68 fmt: &'fmt mut BacktraceFmt<'a, 'b>, 69 symbol_index: usize, 70} 71 72impl BacktraceFrameFmt<'_, '_, '_> { 73 /// Prints a symbol with this frame formatter. 74 pub fn print_symbol(&mut self, frame_ip: usize, sym: Symbol<'_>) -> fmt::Result { 75 self.print_raw_with_column( 76 frame_ip, 77 sym.name(), 78 sym.filename(), 79 sym.lineno(), 80 sym.colno(), 81 ) 82 } 83 84 /// Print a raw (read un-symbolized) address with this frame formatter. A raw address 85 /// will just show up as the address and `<unknown>` and should be used in places where we 86 /// couldn't find any symbol information for an address. 87 pub fn print_raw(&mut self, frame_ip: usize) -> fmt::Result { 88 self.print_raw_with_column(frame_ip, None, None, None, None) 89 } 90 91 fn print_raw_with_column( 92 &mut self, 93 frame_ip: usize, 94 symbol_name: Option<SymbolName<'_>>, 95 filename: Option<&str>, 96 lineno: Option<u32>, 97 colno: Option<u32>, 98 ) -> fmt::Result { 99 self.print_raw_generic(frame_ip, symbol_name, filename, lineno, colno)?; 100 self.symbol_index += 1; 101 Ok(()) 102 } 103 104 // #[allow(unused_mut)] 105 fn print_raw_generic( 106 &mut self, 107 frame_ip: usize, 108 symbol_name: Option<SymbolName<'_>>, 109 filename: Option<&str>, 110 lineno: Option<u32>, 111 colno: Option<u32>, 112 ) -> fmt::Result { 113 // No need to print "null" frames, it basically just means that the 114 // system backtrace was a bit eager to trace back super far. 115 if let BacktraceStyle::Short = self.fmt.format 116 && frame_ip == 0 117 { 118 return Ok(()); 119 } 120 121 // Print the index of the frame as well as the optional instruction 122 // pointer of the frame. If we're beyond the first symbol of this frame 123 // though we just print appropriate whitespace. 124 if self.symbol_index == 0 { 125 write!(self.fmt.fmt, "{:4}: ", self.fmt.frame_index)?; 126 127 // Print the instruction pointer. If the symbol name is None we always print 128 // the address. Those weird frames don't happen that often and are always the most 129 // interesting in a backtrace, so we need to make sure to print at least the address so 130 // we have somewhere to start investigating! 131 if self.fmt.format == BacktraceStyle::Full || symbol_name.is_some() { 132 write!(self.fmt.fmt, "{frame_ip:HEX_WIDTH$x?} - ")?; 133 } 134 } else { 135 write!(self.fmt.fmt, " ")?; 136 if let BacktraceStyle::Full = self.fmt.format { 137 write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH + 3)?; 138 } 139 } 140 141 // Next up write out the symbol name, using the alternate formatting for 142 // more information if we're a full backtrace. Here we also handle 143 // symbols which don't have a name, 144 match (symbol_name, &self.fmt.format) { 145 (Some(name), BacktraceStyle::Short) => write!(self.fmt.fmt, "{name:#}")?, 146 (Some(name), BacktraceStyle::Full) => write!(self.fmt.fmt, "{name}")?, 147 (None, _) => write!(self.fmt.fmt, "<unknown>")?, 148 } 149 self.fmt.fmt.write_str("\n")?; 150 151 // And last up, print out the filename/line number if they're available. 152 if let (Some(file), Some(line)) = (filename, lineno) { 153 self.print_fileline(file, line, colno)?; 154 } 155 156 Ok(()) 157 } 158 159 fn print_fileline(&mut self, file: &str, line: u32, colno: Option<u32>) -> fmt::Result { 160 // Filename/line are printed on lines under the symbol name, so print 161 // some appropriate whitespace to sort of right-align ourselves. 162 if let BacktraceStyle::Full = self.fmt.format { 163 write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH)?; 164 } 165 write!(self.fmt.fmt, " at ")?; 166 167 // Print the filename and line number. 168 write!(self.fmt.fmt, "at {file}")?; 169 write!(self.fmt.fmt, ":{line}")?; 170 171 // Add column number, if available. 172 if let Some(colno) = colno { 173 write!(self.fmt.fmt, ":{colno}")?; 174 } 175 176 writeln!(self.fmt.fmt)?; 177 Ok(()) 178 } 179} 180 181impl Drop for BacktraceFrameFmt<'_, '_, '_> { 182 fn drop(&mut self) { 183 self.fmt.frame_index += 1; 184 } 185}