use alloc::vec::Vec;
use core::fmt::Display;
#[cfg(all(target_os = "vexos", feature = "backtrace"))]
use vex_libunwind::{UnwindContext, UnwindCursor, UnwindError, registers};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Backtrace {
frames: Vec<*const ()>,
}
impl Backtrace {
#[allow(clippy::inline_always)]
#[inline(always)] #[allow(clippy::missing_const_for_fn)]
#[must_use]
pub fn capture() -> Self {
#[cfg(all(target_os = "vexos", feature = "backtrace"))]
return Self::try_capture().unwrap_or(Self { frames: Vec::new() });
#[cfg(not(all(target_os = "vexos", feature = "backtrace")))]
return Self { frames: Vec::new() };
}
#[must_use]
pub const fn frames(&self) -> &[*const ()] {
self.frames.as_slice()
}
#[inline(never)] #[cfg(all(target_os = "vexos", feature = "backtrace"))]
fn try_capture() -> Result<Self, UnwindError> {
UnwindContext::capture(|context| {
let mut cursor = UnwindCursor::new(&context)?;
let mut frames = Vec::new();
loop {
let mut instruction_pointer = cursor.register(registers::UNW_REG_IP)?;
if !cursor.is_signal_frame()? {
instruction_pointer -= 1;
}
frames.push(instruction_pointer as *const ());
if !cursor.step()? {
break;
}
}
Ok(Self { frames })
})
}
}
impl Display for Backtrace {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
writeln!(f, "stack backtrace:")?;
for (i, frame) in self.frames.iter().enumerate() {
writeln!(f, "{i:>3}: 0x{:x}", *frame as usize)?;
}
write!(
f,
"note: Use a symbolizer to convert stack frames to human-readable function names."
)?;
Ok(())
}
}