Basic implementation of backtraces on panic

This commit is contained in:
Gwilym Inzani 2024-04-01 13:05:39 +01:00
parent 6fdd961b61
commit 7708398981
3 changed files with 109 additions and 6 deletions

View file

@ -6,9 +6,17 @@ build-std-features = ["compiler-builtins-mem"]
target = "thumbv4t-none-eabi"
[target.thumbv4t-none-eabi]
rustflags = ["-Clink-arg=-Tgba.ld", "-Ctarget-cpu=arm7tdmi"]
rustflags = [
"-Clink-arg=-Tgba.ld",
"-Ctarget-cpu=arm7tdmi",
"-Cforce-frame-pointers=yes",
]
runner = "mgba-test-runner"
[target.armv4t-none-eabi]
rustflags = ["-Clink-arg=-Tgba.ld", "-Ctarget-cpu=arm7tdmi"]
rustflags = [
"-Clink-arg=-Tgba.ld",
"-Ctarget-cpu=arm7tdmi",
"-Cforce-frame-pointers=yes",
]
runner = "mgba-test-runner"

86
agb/src/backtrace.rs Normal file
View file

@ -0,0 +1,86 @@
use core::{arch::asm, ops::Index};
use alloc::vec::Vec;
// only works for code compiled as THUMB
#[repr(C)]
#[derive(Clone, Default, Debug)]
struct Context {
registers: [u32; 11],
}
pub struct Frame {
pub address: u32,
}
#[allow(unused)]
enum Register {
R0,
R1,
R2,
R3,
R4,
R5,
R6,
FP,
SP,
LR,
PC,
}
impl Index<Register> for Context {
type Output = u32;
fn index(&self, index: Register) -> &Self::Output {
&self.registers[index as usize]
}
}
#[inline(never)]
pub(crate) fn unwind_exception() -> Vec<Frame> {
let mut context = Context::default();
unsafe {
let context_ptr = (&mut context) as *mut _;
asm!(
"
str r0, [r0, #0x00]
str r1, [r0, #0x04]
str r2, [r0, #0x08]
str r3, [r0, #0x0C]
str r4, [r0, #0x10]
str r5, [r0, #0x14]
str r6, [r0, #0x18]
str r7, [r0, #0x1C]
mov r7, sp
str r7, [r0, #0x20]
mov r7, lr
str r7, [r0, #0x24]
mov r7, pc
str r7, [r0, #0x28]
ldr r7, [r0, #0x1C]
",
in("r0") context_ptr
);
}
let mut frame_pointer = context[Register::FP];
let mut frames = Vec::new();
loop {
let sp = unsafe { *(frame_pointer as *const u32) };
let lr = unsafe { *((frame_pointer as *const u32).add(1)) };
if sp == 0 {
break;
}
frames.push(Frame { address: lr });
frame_pointer = sp;
}
frames
}

View file

@ -150,6 +150,7 @@ extern crate alloc;
mod agb_alloc;
mod agbabi;
mod backtrace;
mod bitarray;
/// Implements everything relating to things that are displayed on screen.
pub mod display;
@ -317,11 +318,19 @@ pub mod test_runner {
#[panic_handler]
fn panic_implementation(info: &core::panic::PanicInfo) -> ! {
let frames = backtrace::unwind_exception();
if let Some(mut mgba) = mgba::Mgba::new() {
mgba.print(format_args!("[failed]"), mgba::DebugLevel::Error)
.unwrap();
mgba.print(format_args!("Error: {info}"), mgba::DebugLevel::Fatal)
.unwrap();
let _ = mgba.print(format_args!("[failed]"), mgba::DebugLevel::Error);
for frame in frames {
let _ = mgba.print(
format_args!("{:#08x}", frame.address),
mgba::DebugLevel::Error,
);
}
let _ = mgba.print(format_args!("Error: {info}"), mgba::DebugLevel::Fatal);
}
loop {}