Next Generation WASM Microkernel Operating System
at trap_handler 298 lines 10 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 8mod print; 9mod symbolize; 10 11use crate::backtrace::print::BacktraceFmt; 12use crate::mem::VirtualAddress; 13use arrayvec::ArrayVec; 14use core::str::FromStr; 15use core::{fmt, slice}; 16use fallible_iterator::FallibleIterator; 17use loader_api::BootInfo; 18use spin::OnceLock; 19use symbolize::SymbolizeContext; 20use unwind2::FrameIter; 21 22static BACKTRACE_INFO: OnceLock<BacktraceInfo> = OnceLock::new(); 23 24#[cold] 25pub fn init(boot_info: &'static BootInfo, backtrace_style: BacktraceStyle) { 26 BACKTRACE_INFO.get_or_init(|| BacktraceInfo::new(boot_info, backtrace_style)); 27} 28 29/// Information about the kernel required to build a backtrace 30struct BacktraceInfo { 31 /// The base virtual address of the kernel ELF. ELF debug info expects zero-based addresses, 32 /// but the kernel is located at some address in the higher half. This offset is used to convert 33 /// between the two. 34 kernel_virt_base: u64, 35 /// The memory of our own ELF 36 elf: &'static [u8], 37 /// The actual state required for converting addresses into symbols. This is *very* heavy to 38 /// compute though, so we only construct it lazily in [`BacktraceInfo::symbolize_context`]. 39 symbolize_context: OnceLock<SymbolizeContext<'static>>, 40 backtrace_style: BacktraceStyle, 41} 42 43#[derive(Debug)] 44pub struct UnknownBacktraceStyleError; 45 46#[derive(Debug, Default, Copy, Clone, PartialEq)] 47pub enum BacktraceStyle { 48 #[default] 49 Short, 50 Full, 51} 52 53#[derive(Clone)] 54pub struct Backtrace<'a, const MAX_FRAMES: usize> { 55 symbolize_ctx: Option<&'a SymbolizeContext<'static>>, 56 pub frames: ArrayVec<usize, MAX_FRAMES>, 57 pub frames_omitted: bool, 58 style: BacktraceStyle, 59} 60 61// === impl BacktraceInfo === 62 63impl BacktraceInfo { 64 fn new(boot_info: &'static BootInfo, backtrace_style: BacktraceStyle) -> Self { 65 BacktraceInfo { 66 kernel_virt_base: boot_info.kernel_virt.start as u64, 67 // Safety: we have to trust the loaders BootInfo here 68 elf: unsafe { 69 let base = boot_info 70 .physical_address_offset 71 .checked_add(boot_info.kernel_phys.start) 72 .unwrap() as *const u8; 73 74 slice::from_raw_parts( 75 base, 76 boot_info 77 .kernel_phys 78 .end 79 .checked_sub(boot_info.kernel_phys.start) 80 .unwrap(), 81 ) 82 }, 83 symbolize_context: OnceLock::new(), 84 backtrace_style, 85 } 86 } 87 88 fn symbolize_context(&self) -> &SymbolizeContext<'static> { 89 self.symbolize_context.get_or_init(|| { 90 tracing::debug!("Setting up symbolize context..."); 91 92 let elf = xmas_elf::ElfFile::new(self.elf).unwrap(); 93 SymbolizeContext::new(elf, self.kernel_virt_base).unwrap() 94 }) 95 } 96} 97 98// === impl Backtrace === 99 100impl<const MAX_FRAMES: usize> Backtrace<'_, MAX_FRAMES> { 101 /// Captures a backtrace at the callsite of this function, returning an owned representation. 102 /// 103 /// The returned object is almost entirely self-contained. It can be cloned, or send to other threads. 104 /// 105 /// Note that this step is quite cheap, contrary to the `Backtrace` implementation in the standard 106 /// library this resolves the symbols (the expensive step) lazily, so this struct can be constructed 107 /// in performance sensitive codepaths and only later resolved. 108 /// 109 /// # Errors 110 /// 111 /// Returns the underlying [`unwind2::Error`] if walking the stack fails. 112 #[inline] 113 pub fn capture() -> Result<Self, unwind2::Error> { 114 Self::new_inner(FrameIter::new()) 115 } 116 117 /// Constructs a backtrace from the provided register context, returning an owned representation. 118 /// 119 /// The returned object is almost entirely self-contained. It can be cloned, or send to other threads. 120 /// 121 /// Note that this step is quite cheap, contrary to the `Backtrace` implementation in the standard 122 /// library this resolves the symbols (the expensive step) lazily, so this struct can be constructed 123 /// in performance sensitive codepaths and only later resolved. 124 /// 125 /// # Errors 126 /// 127 /// Returns the underlying [`unwind2::Error`] if walking the stack fails. 128 #[inline] 129 pub fn from_registers( 130 regs: unwind2::Registers, 131 pc: VirtualAddress, 132 ) -> Result<Self, unwind2::Error> { 133 let iter = FrameIter::from_registers(regs, pc.get()); 134 Self::new_inner(iter) 135 } 136 137 fn new_inner(iter: FrameIter) -> Result<Self, unwind2::Error> { 138 let mut frames = ArrayVec::new(); 139 140 let mut iter = iter.take(MAX_FRAMES); 141 142 while let Some(frame) = iter.next()? { 143 frames.try_push(frame.ip()).unwrap(); 144 } 145 let frames_omitted = iter.next()?.is_some(); 146 147 Ok(Self { 148 symbolize_ctx: BACKTRACE_INFO.get().map(|info| info.symbolize_context()), 149 frames, 150 frames_omitted, 151 style: BACKTRACE_INFO 152 .get() 153 .map(|info| info.backtrace_style) 154 .unwrap_or_default(), 155 }) 156 } 157} 158 159impl<const MAX_FRAMES: usize> fmt::Display for Backtrace<'_, MAX_FRAMES> { 160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 161 writeln!(f, "stack backtrace:")?; 162 163 let style = if f.alternate() { 164 BacktraceStyle::Full 165 } else { 166 self.style 167 }; 168 169 let mut bt_fmt = BacktraceFmt::new(f, style); 170 171 let mut omitted_count: usize = 0; 172 let mut first_omit = true; 173 // If we're using a short backtrace, ignore all frames until we're told to start printing. 174 let mut print = style != BacktraceStyle::Short; 175 176 for ip in &self.frames { 177 let mut any = false; // did we print any symbols? 178 179 // if the symbolication state isn't setup, yet we can't print symbols the addresses will have 180 // to suffice... 181 if let Some(symbolize_ctx) = self.symbolize_ctx { 182 let mut syms = symbolize_ctx.resolve_unsynchronized(*ip as u64).unwrap(); 183 184 while let Some(sym) = syms.next().unwrap() { 185 any = true; 186 // `__rust_end_short_backtrace` means we are done hiding symbols 187 // for now. Print until we see `__rust_begin_short_backtrace`. 188 if style == BacktraceStyle::Short { 189 if let Some(sym) = sym.name().map(|s| s.as_raw_str()) { 190 if sym.contains("__rust_end_short_backtrace") { 191 print = true; 192 break; 193 } 194 if print && sym.contains("__rust_begin_short_backtrace") { 195 print = false; 196 break; 197 } 198 if !print { 199 omitted_count += 1; 200 } 201 } 202 } 203 204 if print { 205 if omitted_count > 0 { 206 debug_assert!(style == BacktraceStyle::Short); 207 // only print the message between the middle of frames 208 if !first_omit { 209 let _ = writeln!( 210 bt_fmt.formatter(), 211 " [... omitted {} frame{} ...]", 212 omitted_count, 213 if omitted_count > 1 { "s" } else { "" } 214 ); 215 } 216 first_omit = false; 217 omitted_count = 0; 218 } 219 bt_fmt.frame().print_symbol(*ip, sym)?; 220 } 221 } 222 } else { 223 // no symbolize context always means no symbols 224 any = false; 225 } 226 227 if !any && print { 228 bt_fmt.frame().print_raw(*ip)?; 229 } 230 } 231 232 if style == BacktraceStyle::Short { 233 writeln!( 234 f, 235 "note: Some details are omitted, \ 236 run with `backtrace=full` bootarg for a verbose backtrace." 237 )?; 238 } 239 if self.symbolize_ctx.is_none() { 240 writeln!( 241 f, 242 "note: backtrace subsystem wasn't initialized, no symbols were printed." 243 )?; 244 } 245 246 Ok(()) 247 } 248} 249 250// === impl BacktraceStyle === 251 252impl FromStr for BacktraceStyle { 253 type Err = UnknownBacktraceStyleError; 254 255 fn from_str(s: &str) -> Result<Self, Self::Err> { 256 match s { 257 "short" => Ok(BacktraceStyle::Short), 258 "full" => Ok(BacktraceStyle::Full), 259 _ => Err(UnknownBacktraceStyleError), 260 } 261 } 262} 263 264impl fmt::Display for UnknownBacktraceStyleError { 265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 266 writeln!(f, "unknown backtrace style") 267 } 268} 269 270impl core::error::Error for UnknownBacktraceStyleError {} 271 272/// Fixed frame used to clean the backtrace with `backtrace=short`. 273#[inline(never)] 274pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T 275where 276 F: FnOnce() -> T, 277{ 278 let result = f(); 279 280 // prevent this frame from being tail-call optimised away 281 core::hint::black_box(()); 282 283 result 284} 285 286/// Fixed frame used to clean the backtrace with `backtrace=short`. 287#[inline(never)] 288pub fn __rust_end_short_backtrace<F, T>(f: F) -> T 289where 290 F: FnOnce() -> T, 291{ 292 let result = f(); 293 294 // prevent this frame from being tail-call optimised away 295 core::hint::black_box(()); 296 297 result 298}