Provide the way to override the panic handler.

This commit is contained in:
Zhang Junyang
2024-09-25 18:04:44 +08:00
committed by Tate, Hongliang Tian
parent 3c857d746e
commit 131a25c15c
12 changed files with 129 additions and 74 deletions

View File

@ -68,7 +68,7 @@ pub struct CallbackElement {
impl CallbackElement {
pub fn call(&self, element: &TrapFrame) {
self.function.call((element,));
(self.function)(element);
}
}

View File

@ -118,12 +118,18 @@ pub fn init() {
///
/// This function should be only called from the bootloader-specific module.
pub(crate) fn call_ostd_main() -> ! {
// The entry point of kernel code, which should be defined by the package that
// uses OSTD.
extern "Rust" {
fn __ostd_main() -> !;
}
// SAFETY: The function is called only once on the BSP.
unsafe { crate::init() };
// SAFETY: This external function is defined by the package that uses OSTD,
// which should be generated by the `ostd::main` macro. So it is safe.
unsafe {
// The entry point of kernel code, which should be defined by the package that
// uses OSTD.
extern "Rust" {
fn __ostd_main() -> !;
}
__ostd_main();
}
}

View File

@ -11,6 +11,7 @@
#![feature(generic_const_exprs)]
#![feature(iter_from_coroutine)]
#![feature(let_chains)]
#![feature(linkage)]
#![feature(min_specialization)]
#![feature(negative_impls)]
#![feature(ptr_sub_ptr)]
@ -36,7 +37,7 @@ mod error;
pub mod io_mem;
pub mod logger;
pub mod mm;
pub mod panicking;
pub mod panic;
pub mod prelude;
pub mod smp;
pub mod sync;
@ -47,7 +48,7 @@ pub mod user;
use core::sync::atomic::AtomicBool;
pub use ostd_macros::main;
pub use ostd_macros::{main, panic_handler};
pub use ostd_pod::Pod;
pub use self::{error::Error, prelude::Result};
@ -65,7 +66,7 @@ pub use self::{error::Error, prelude::Result};
// make inter-initialization-dependencies more clear and reduce usages of
// boot stage only global variables.
#[doc(hidden)]
pub unsafe fn init() {
unsafe fn init() {
arch::enable_cpu_features();
arch::serial::init();
@ -153,6 +154,6 @@ pub mod ktest {
//! It is rather discouraged to use the definitions here directly. The
//! `ktest` attribute is sufficient for all normal use cases.
pub use ostd_macros::test_main as main;
pub use ostd_macros::{test_main as main, test_panic_handler as panic_handler};
pub use ostd_test::*;
}

View File

@ -4,60 +4,45 @@
use core::ffi::c_void;
pub use unwinding::panic::{begin_panic, catch_unwind};
use crate::{
arch::qemu::{exit_qemu, QemuExitCode},
cpu_local_cell, early_print, early_println,
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,
};
cpu_local_cell! {
static IN_PANIC: bool = false;
}
/// The asterinas panic handler.
/// The default panic handler for OSTD based kernels.
///
/// The panic handler must be defined in the binary crate or in the crate that the binary
/// crate explicitly declares by `extern crate`. We cannot let the base crate depend on OSTD
/// due to prismatic dependencies. That's why we export this symbol and state the
/// panic handler in the binary crate.
#[export_name = "__aster_panic_handler"]
pub fn panic_handler(info: &core::panic::PanicInfo) -> ! {
/// 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!("{}", info);
early_println!("The panic handler panicked when processing the above panic. Aborting.");
early_println!("The panic handler panicked {:#?}", info);
abort();
}
// If in ktest, we would like to catch the panics and resume the test.
#[cfg(ktest)]
{
use alloc::{boxed::Box, string::ToString};
IN_PANIC.store(true);
use unwinding::panic::begin_panic;
early_println!("Non-resettable panic! {:#?}", info);
let throw_info = ostd_test::PanicInfo {
message: info.message().to_string(),
file: info.location().unwrap().file().to_string(),
line: info.location().unwrap().line() as usize,
col: info.location().unwrap().column() as usize,
resolve_panic: || {
IN_PANIC.store(false);
},
};
// Throw an exception and expecting it to be caught.
begin_panic(Box::new(throw_info.clone()));
}
early_println!("{}", info);
print_stack_trace();
abort();
}
@ -67,7 +52,10 @@ pub fn abort() -> ! {
exit_qemu(QemuExitCode::Failed);
}
fn print_stack_trace() {
/// 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(());
@ -98,6 +86,10 @@ fn print_stack_trace() {
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";
}

View File

@ -15,5 +15,5 @@ pub use ostd_macros::ktest;
pub use crate::{
early_print as print, early_println as println,
mm::{Paddr, Vaddr},
panicking::abort,
panic::abort,
};

View File

@ -170,7 +170,7 @@ impl TaskOptions {
extern "C" fn kernel_task_entry() {
let current_task = current_task()
.expect("no current task, it should have current task in kernel task entry");
current_task.func.call(());
(current_task.func)();
current_task.exit();
}