mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 08:11:33 +11:00
gwilym_encode the stack trace and generate a qr code for debugging
This commit is contained in:
parent
71bdb085be
commit
e36145552f
|
@ -48,13 +48,8 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
let ctx = addr2line::Context::new(&object)?;
|
let ctx = addr2line::Context::new(&object)?;
|
||||||
|
|
||||||
for (i, address) in cli.dump.split('-').enumerate() {
|
for (i, address) in gwilym_encoding::decode(&cli.dump).into_iter().enumerate() {
|
||||||
let mut address = u64::from_str_radix(address, 16)?;
|
print_address(&ctx, i, address.into(), modification_time)?;
|
||||||
if address <= 0xFFFF {
|
|
||||||
address += 0x0800_0000;
|
|
||||||
}
|
|
||||||
|
|
||||||
print_address(&ctx, i, address, modification_time)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -184,3 +179,72 @@ fn is_interesting_function(function_name: &str, path: &str) -> bool {
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod gwilym_encoding {
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
const ALPHABET: &[u8] = b"-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
|
// pub fn encode_16(input: u16) -> [u8; 3] {
|
||||||
|
// let input = input as usize;
|
||||||
|
// [
|
||||||
|
// ALPHABET[input >> (16 - 5)],
|
||||||
|
// ALPHABET[(input >> (16 - 10)) & 0b11111],
|
||||||
|
// ALPHABET[input & 0b111111],
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn encode_32(input: u32) -> [u8; 6] {
|
||||||
|
// let input = input as usize;
|
||||||
|
// let output_16 = encode_16(input as u16);
|
||||||
|
// [
|
||||||
|
// ALPHABET[(input >> (32 - 5)) | 0b100000],
|
||||||
|
// ALPHABET[(input >> (32 - 10)) & 0b11111],
|
||||||
|
// ALPHABET[(input >> (32 - 16)) & 0b111111],
|
||||||
|
// output_16[0],
|
||||||
|
// output_16[1],
|
||||||
|
// output_16[2],
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn decode(input: &str) -> Vec<u32> {
|
||||||
|
let mut result = vec![];
|
||||||
|
|
||||||
|
let mut previous_value = None;
|
||||||
|
for chunk in input.as_bytes().chunks_exact(3) {
|
||||||
|
let value = decode_chunk(chunk);
|
||||||
|
|
||||||
|
if value & (1 << 17) != 0 {
|
||||||
|
previous_value = Some(value << 16);
|
||||||
|
} else if let Some(upper_bits) = previous_value {
|
||||||
|
result.push(upper_bits | value);
|
||||||
|
previous_value = None;
|
||||||
|
} else {
|
||||||
|
result.push(value | 0x0800_0000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_chunk(chunk: &[u8]) -> u32 {
|
||||||
|
let a = get_value_for_char(chunk[0]);
|
||||||
|
let b = get_value_for_char(chunk[1]);
|
||||||
|
let c = get_value_for_char(chunk[2]);
|
||||||
|
|
||||||
|
(a << (16 - 5)) | (b << (16 - 10)) | c
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_value_for_char(input: u8) -> u32 {
|
||||||
|
static REVERSE_ALHPABET: OnceLock<[u8; 128]> = OnceLock::new();
|
||||||
|
|
||||||
|
REVERSE_ALHPABET.get_or_init(|| {
|
||||||
|
let mut result = [0; 128];
|
||||||
|
for (i, &c) in ALPHABET.iter().enumerate() {
|
||||||
|
result[c as usize] = i as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
})[input as usize] as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ agb_fixnum = { version = "0.19.1", path = "../agb-fixnum" }
|
||||||
agb_hashmap = { version = "0.19.1", path = "../agb-hashmap" }
|
agb_hashmap = { version = "0.19.1", path = "../agb-hashmap" }
|
||||||
bare-metal = "1"
|
bare-metal = "1"
|
||||||
bilge = "0.2"
|
bilge = "0.2"
|
||||||
|
qrcodegen-no-heap = "1.8"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
default-target = "thumbv4t-none-eabi"
|
default-target = "thumbv4t-none-eabi"
|
||||||
|
|
|
@ -87,23 +87,47 @@ pub(crate) fn unwind_exception() -> Frames {
|
||||||
|
|
||||||
impl core::fmt::Display for Frames {
|
impl core::fmt::Display for Frames {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
let mut is_first = true;
|
|
||||||
|
|
||||||
for frame in &self.frames {
|
for frame in &self.frames {
|
||||||
if !is_first {
|
|
||||||
write!(f, "-")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if frame & 0xFFFF_0000 == 0x0800_0000 {
|
if frame & 0xFFFF_0000 == 0x0800_0000 {
|
||||||
let frame = frame & 0xFFFF;
|
let frame = *frame as u16; // intentionally truncate
|
||||||
write!(f, "{frame:x}")?;
|
let frame_encoded = gwilym_encoding::encode_16(frame);
|
||||||
} else {
|
let frame_str = unsafe { core::str::from_utf8_unchecked(&frame_encoded) };
|
||||||
write!(f, "{frame:x}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
is_first = false;
|
write!(f, "{frame_str}")?;
|
||||||
|
} else {
|
||||||
|
let frame_encoded = gwilym_encoding::encode_32(*frame);
|
||||||
|
let frame_str = unsafe { core::str::from_utf8_unchecked(&frame_encoded) };
|
||||||
|
|
||||||
|
write!(f, "{frame_str}")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod gwilym_encoding {
|
||||||
|
const ALPHABET: &[u8] = b"-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
|
pub fn encode_16(input: u16) -> [u8; 3] {
|
||||||
|
let input = input as usize;
|
||||||
|
[
|
||||||
|
ALPHABET[input >> (16 - 5)],
|
||||||
|
ALPHABET[(input >> (16 - 10)) & 0b11111],
|
||||||
|
ALPHABET[input & 0b111111],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode_32(input: u32) -> [u8; 6] {
|
||||||
|
let input = input as usize;
|
||||||
|
let output_16 = encode_16(input as u16);
|
||||||
|
[
|
||||||
|
ALPHABET[(input >> (32 - 5)) | 0b100000],
|
||||||
|
ALPHABET[(input >> (32 - 10)) & 0b11111],
|
||||||
|
ALPHABET[(input >> (32 - 16)) & 0b111111],
|
||||||
|
output_16[0],
|
||||||
|
output_16[1],
|
||||||
|
output_16[2],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ impl Dma {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn disable(&mut self) {
|
pub(crate) fn disable(&mut self) {
|
||||||
unsafe { MemoryMapped::new(dma_control_addr(self.number)) }.set(0);
|
unsafe { MemoryMapped::new(dma_control_addr(self.number)) }.set(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,7 @@ pub mod mgba;
|
||||||
pub use agb_fixnum as fixnum;
|
pub use agb_fixnum as fixnum;
|
||||||
/// Contains an implementation of a hashmap which suits the gameboy advance's hardware.
|
/// Contains an implementation of a hashmap which suits the gameboy advance's hardware.
|
||||||
pub use agb_hashmap as hash_map;
|
pub use agb_hashmap as hash_map;
|
||||||
|
mod panics_render;
|
||||||
/// Simple random number generator
|
/// Simple random number generator
|
||||||
pub mod rng;
|
pub mod rng;
|
||||||
pub mod save;
|
pub mod save;
|
||||||
|
@ -289,6 +290,8 @@ impl Gba {
|
||||||
/// You can run the tests using `cargo test`, but it will work better through `mgba-test-runner` by
|
/// You can run the tests using `cargo test`, but it will work better through `mgba-test-runner` by
|
||||||
/// running something along the lines of `CARGO_TARGET_THUMBV4T_NONE_EABI_RUNNER=mgba-test-runner cargo test`.
|
/// running something along the lines of `CARGO_TARGET_THUMBV4T_NONE_EABI_RUNNER=mgba-test-runner cargo test`.
|
||||||
pub mod test_runner {
|
pub mod test_runner {
|
||||||
|
use self::panics_render::render_backtrace;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -327,11 +330,9 @@ pub mod test_runner {
|
||||||
format_args!("debug data: {frames}"),
|
format_args!("debug data: {frames}"),
|
||||||
mgba::DebugLevel::Error,
|
mgba::DebugLevel::Error,
|
||||||
);
|
);
|
||||||
|
|
||||||
let _ = mgba.print(format_args!("Error: {info}"), mgba::DebugLevel::Fatal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {}
|
render_backtrace(&frames, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static mut TEST_GBA: Option<Gba> = None;
|
static mut TEST_GBA: Option<Gba> = None;
|
||||||
|
|
70
agb/src/panics_render.rs
Normal file
70
agb/src/panics_render.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
|
use alloc::{format, vec};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
backtrace,
|
||||||
|
display::{busy_wait_for_vblank, HEIGHT, WIDTH},
|
||||||
|
dma::dma3_exclusive,
|
||||||
|
interrupt, mgba, syscall,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn render_backtrace(trace: &backtrace::Frames, info: &PanicInfo) -> ! {
|
||||||
|
interrupt::free(|_cs| {
|
||||||
|
dma3_exclusive(|| {
|
||||||
|
// SAFETY: This is not fine, but we're crashing anyway. The loop at the end should stop anything bad happening
|
||||||
|
let mut gba = unsafe { crate::Gba::new_in_entry() };
|
||||||
|
|
||||||
|
gba.dma.dma().dma3.disable();
|
||||||
|
draw_qr_code(&mut gba, trace);
|
||||||
|
|
||||||
|
busy_wait_for_vblank();
|
||||||
|
|
||||||
|
if let Some(mut mgba) = mgba::Mgba::new() {
|
||||||
|
let _ = mgba.print(format_args!("Error: {info}"), mgba::DebugLevel::Fatal);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
syscall::halt();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_qr_code(gba: &mut crate::Gba, trace: &backtrace::Frames) {
|
||||||
|
let mut gfx = gba.display.video.bitmap3();
|
||||||
|
|
||||||
|
let qrcode_string_data = format!("https://agbrs.dev/crash#v1-{trace}");
|
||||||
|
const MAX_VERSION: qrcodegen_no_heap::Version = qrcodegen_no_heap::Version::new(6);
|
||||||
|
|
||||||
|
let mut temp_buffer = vec![0; MAX_VERSION.buffer_len()];
|
||||||
|
let mut out_buffer = vec![0; MAX_VERSION.buffer_len()];
|
||||||
|
|
||||||
|
let qr_code = match qrcodegen_no_heap::QrCode::encode_text(
|
||||||
|
&qrcode_string_data,
|
||||||
|
&mut temp_buffer,
|
||||||
|
&mut out_buffer,
|
||||||
|
qrcodegen_no_heap::QrCodeEcc::Medium,
|
||||||
|
qrcodegen_no_heap::Version::MIN,
|
||||||
|
MAX_VERSION,
|
||||||
|
Some(qrcodegen_no_heap::Mask::new(0)),
|
||||||
|
true,
|
||||||
|
) {
|
||||||
|
Ok(qr_code) => qr_code,
|
||||||
|
Err(e) => {
|
||||||
|
crate::println!("Error generating qr code: {e:?}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for y in 0..HEIGHT {
|
||||||
|
for x in 0..WIDTH {
|
||||||
|
let colour = if qr_code.get_module(x / 2 - 4, y / 2 - 4) {
|
||||||
|
0x0000
|
||||||
|
} else {
|
||||||
|
0xFFFF
|
||||||
|
};
|
||||||
|
gfx.draw_point(x, y, colour);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue