mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 09:53:24 +00:00
Provide the way to override the panic handler.
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
3c857d746e
commit
131a25c15c
108
ostd/src/panic.rs
Normal file
108
ostd/src/panic.rs
Normal file
@ -0,0 +1,108 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Panic support.
|
||||
|
||||
use core::ffi::c_void;
|
||||
|
||||
pub use unwinding::panic::{begin_panic, catch_unwind};
|
||||
|
||||
use crate::{
|
||||
arch::qemu::{exit_qemu, QemuExitCode},
|
||||
early_print, early_println,
|
||||
sync::SpinLock,
|
||||
};
|
||||
|
||||
extern crate cfg_if;
|
||||
extern crate gimli;
|
||||
|
||||
use gimli::Register;
|
||||
use unwinding::abi::{
|
||||
UnwindContext, UnwindReasonCode, _Unwind_Backtrace, _Unwind_FindEnclosingFunction,
|
||||
_Unwind_GetGR, _Unwind_GetIP,
|
||||
};
|
||||
|
||||
/// The default panic handler for OSTD based kernels.
|
||||
///
|
||||
/// The user can override it by defining their own panic handler with the macro
|
||||
/// `#[ostd::panic_handler]`.
|
||||
#[cfg(not(ktest))]
|
||||
#[no_mangle]
|
||||
pub fn __ostd_panic_handler(info: &core::panic::PanicInfo) -> ! {
|
||||
let _irq_guard = crate::trap::disable_local();
|
||||
|
||||
crate::cpu_local_cell! {
|
||||
static IN_PANIC: bool = false;
|
||||
}
|
||||
|
||||
if IN_PANIC.load() {
|
||||
early_println!("The panic handler panicked {:#?}", info);
|
||||
abort();
|
||||
}
|
||||
|
||||
IN_PANIC.store(true);
|
||||
|
||||
early_println!("Non-resettable panic! {:#?}", info);
|
||||
|
||||
print_stack_trace();
|
||||
abort();
|
||||
}
|
||||
|
||||
/// Aborts the QEMU
|
||||
pub fn abort() -> ! {
|
||||
exit_qemu(QemuExitCode::Failed);
|
||||
}
|
||||
|
||||
/// Prints the stack trace of the current thread to the console.
|
||||
///
|
||||
/// The printing procedure is protected by a spin lock to prevent interleaving.
|
||||
pub fn print_stack_trace() {
|
||||
/// We acquire a global lock to prevent the frames in the stack trace from
|
||||
/// interleaving. The spin lock is used merely for its simplicity.
|
||||
static BACKTRACE_PRINT_LOCK: SpinLock<()> = SpinLock::new(());
|
||||
let _lock = BACKTRACE_PRINT_LOCK.lock();
|
||||
|
||||
early_println!("Printing stack trace:");
|
||||
|
||||
struct CallbackData {
|
||||
counter: usize,
|
||||
}
|
||||
extern "C" fn callback(unwind_ctx: &UnwindContext<'_>, arg: *mut c_void) -> UnwindReasonCode {
|
||||
let data = unsafe { &mut *(arg as *mut CallbackData) };
|
||||
data.counter += 1;
|
||||
let pc = _Unwind_GetIP(unwind_ctx);
|
||||
if pc > 0 {
|
||||
let fde_initial_address = _Unwind_FindEnclosingFunction(pc as *mut c_void) as usize;
|
||||
early_println!(
|
||||
"{:4}: fn {:#18x} - pc {:#18x} / registers:",
|
||||
data.counter,
|
||||
fde_initial_address,
|
||||
pc,
|
||||
);
|
||||
}
|
||||
// Print the first 8 general registers for any architecture. The register number follows
|
||||
// the DWARF standard.
|
||||
for i in 0..8u16 {
|
||||
let reg_i = _Unwind_GetGR(unwind_ctx, i as i32);
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "x86_64")] {
|
||||
let reg_name = gimli::X86_64::register_name(Register(i)).unwrap_or("unknown");
|
||||
} else if #[cfg(target_arch = "riscv64")] {
|
||||
let reg_name = gimli::RiscV::register_name(Register(i)).unwrap_or("unknown");
|
||||
} else if #[cfg(target_arch = "aarch64")] {
|
||||
let reg_name = gimli::AArch64::register_name(Register(i)).unwrap_or("unknown");
|
||||
} else {
|
||||
let reg_name = "unknown";
|
||||
}
|
||||
}
|
||||
if i % 4 == 0 {
|
||||
early_print!("\n ");
|
||||
}
|
||||
early_print!(" {} {:#18x};", reg_name, reg_i);
|
||||
}
|
||||
early_print!("\n\n");
|
||||
UnwindReasonCode::NO_REASON
|
||||
}
|
||||
|
||||
let mut data = CallbackData { counter: 0 };
|
||||
_Unwind_Backtrace(callback, &mut data as *mut _ as _);
|
||||
}
|
Reference in New Issue
Block a user