Next Generation WASM Microkernel Operating System
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#![no_std] // this is crate is fully incompatible with `std` due to clashing lang item definitions
9#![cfg(target_os = "none")]
10
11mod symbolize;
12
13use arrayvec::ArrayVec;
14use core::fmt;
15use core::fmt::Formatter;
16use fallible_iterator::FallibleIterator;
17use unwind2::FrameIter;
18
19pub use crate::symbolize::SymbolizeContext;
20
21#[derive(Clone)]
22pub struct Backtrace<'a, 'data, const MAX_FRAMES: usize> {
23 symbolize_ctx: &'a SymbolizeContext<'data>,
24 pub frames: ArrayVec<usize, MAX_FRAMES>,
25 pub frames_omitted: usize,
26}
27
28impl<'a, 'data, const MAX_FRAMES: usize> Backtrace<'a, 'data, MAX_FRAMES> {
29 /// Captures a backtrace at the callsite of this function, returning an owned representation.
30 ///
31 /// The returned object is almost entirely self-contained. It can be cloned, or send to other threads.
32 ///
33 /// Note that this step is quite cheap, contrary to the `Backtrace` implementation in the standard
34 /// library this resolves the symbols (the expensive step) lazily, so this struct can be constructed
35 /// in performance sensitive codepaths and only later resolved.
36 ///
37 /// # Errors
38 ///
39 /// Returns the underlying [`unwind2::Error`] if walking the stack fails.
40 #[inline]
41 pub fn capture(ctx: &'a SymbolizeContext<'data>) -> Result<Self, unwind2::Error> {
42 Self::new_inner(ctx, FrameIter::new())
43 }
44
45 /// Constructs a backtrace from the provided register context, returning an owned representation.
46 ///
47 /// The returned object is almost entirely self-contained. It can be cloned, or send to other threads.
48 ///
49 /// Note that this step is quite cheap, contrary to the `Backtrace` implementation in the standard
50 /// library this resolves the symbols (the expensive step) lazily, so this struct can be constructed
51 /// in performance sensitive codepaths and only later resolved.
52 ///
53 /// # Errors
54 ///
55 /// Returns the underlying [`unwind2::Error`] if walking the stack fails.
56 #[inline]
57 pub fn from_registers(
58 ctx: &'a SymbolizeContext<'data>,
59 regs: unwind2::Registers,
60 ip: usize,
61 ) -> Result<Self, unwind2::Error> {
62 let iter = FrameIter::from_registers(regs, ip);
63 Self::new_inner(ctx, iter)
64 }
65
66 fn new_inner(
67 ctx: &'a SymbolizeContext<'data>,
68 mut iter: FrameIter,
69 ) -> Result<Self, unwind2::Error> {
70 let mut frames = ArrayVec::new();
71 let mut frames_omitted: usize = 0;
72
73 while let Some(frame) = iter.next()? {
74 if frames.try_push(frame.ip()).is_err() {
75 frames_omitted += 1;
76 }
77 }
78
79 Ok(Self {
80 symbolize_ctx: ctx,
81 frames,
82 frames_omitted,
83 })
84 }
85}
86
87impl<const MAX_FRAMES: usize> fmt::Display for Backtrace<'_, '_, MAX_FRAMES> {
88 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
89 writeln!(f, "stack backtrace:")?;
90
91 let mut frame_idx: i32 = 0;
92 for ip in &self.frames {
93 let mut syms = self
94 .symbolize_ctx
95 .resolve_unsynchronized(*ip as u64)
96 .unwrap();
97
98 while let Some(sym) = syms.next().unwrap() {
99 write!(f, "{frame_idx}: {address:#x} -", address = ip)?;
100 if let Some(name) = sym.name() {
101 writeln!(f, " {name}")?;
102 } else {
103 writeln!(f, " <unknown>")?;
104 }
105 if let Some(filename) = sym.filename() {
106 write!(f, " at {filename}")?;
107 if let Some(lineno) = sym.lineno() {
108 write!(f, ":{lineno}")?;
109 } else {
110 write!(f, "??")?;
111 }
112 if let Some(colno) = sym.colno() {
113 writeln!(f, ":{colno}")?;
114 } else {
115 writeln!(f, "??")?;
116 }
117 }
118
119 frame_idx += 1i32;
120 }
121 }
122
123 Ok(())
124 }
125}
126
127/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
128/// this is only inline(never) when backtraces in std are enabled, otherwise
129/// it's fine to optimize away.
130#[inline(never)]
131pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
132where
133 F: FnOnce() -> T,
134{
135 let result = f();
136
137 // prevent this frame from being tail-call optimised away
138 core::hint::black_box(());
139
140 result
141}
142
143/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
144/// this is only inline(never) when backtraces in std are enabled, otherwise
145/// it's fine to optimize away.
146#[inline(never)]
147pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
148where
149 F: FnOnce() -> T,
150{
151 let result = f();
152
153 // prevent this frame from being tail-call optimised away
154 core::hint::black_box(());
155
156 result
157}