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