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

29
Cargo.lock generated
View File

@ -282,7 +282,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.77", "syn 2.0.79",
] ]
[[package]] [[package]]
@ -344,7 +344,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.77", "syn 2.0.79",
] ]
[[package]] [[package]]
@ -514,7 +514,7 @@ dependencies = [
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.77", "syn 2.0.79",
] ]
[[package]] [[package]]
@ -549,7 +549,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.77", "syn 2.0.79",
"unicode-xid", "unicode-xid",
] ]
@ -655,7 +655,7 @@ checksum = "ba330b70a5341d3bc730b8e205aaee97ddab5d9c448c4f51a7c2d924266fa8f9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.77", "syn 2.0.79",
] ]
[[package]] [[package]]
@ -755,7 +755,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.77", "syn 2.0.79",
] ]
[[package]] [[package]]
@ -1034,7 +1034,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.77", "syn 2.0.79",
] ]
[[package]] [[package]]
@ -1088,7 +1088,6 @@ version = "0.9.3"
dependencies = [ dependencies = [
"ostd", "ostd",
"owo-colors 4.0.0", "owo-colors 4.0.0",
"unwinding",
] ]
[[package]] [[package]]
@ -1141,7 +1140,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rand", "rand",
"syn 2.0.77", "syn 2.0.79",
] ]
[[package]] [[package]]
@ -1369,7 +1368,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.77", "syn 2.0.79",
] ]
[[package]] [[package]]
@ -1456,9 +1455,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.77" version = "2.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1507,7 +1506,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.77", "syn 2.0.79",
] ]
[[package]] [[package]]
@ -1621,7 +1620,7 @@ checksum = "c19ee3a01d435eda42cb9931269b349d28a1762f91ddf01c68d276f74b957cc3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.77", "syn 2.0.79",
] ]
[[package]] [[package]]
@ -1809,5 +1808,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.77", "syn 2.0.79",
] ]

View File

@ -6,7 +6,7 @@ extern crate #TARGET_NAME#;
#[panic_handler] #[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! { fn panic(info: &core::panic::PanicInfo) -> ! {
extern "Rust" { extern "Rust" {
pub fn __aster_panic_handler(info: &core::panic::PanicInfo) -> !; pub fn __ostd_panic_handler(info: &core::panic::PanicInfo) -> !;
} }
unsafe { __aster_panic_handler(info); } unsafe { __ostd_panic_handler(info); }
} }

View File

@ -11,4 +11,3 @@ repository ="https://github.com/asterinas/asterinas"
[dependencies] [dependencies]
ostd = { version = "0.9.3", path = "../../ostd" } ostd = { version = "0.9.3", path = "../../ostd" }
owo-colors = "4.0.0" owo-colors = "4.0.0"
unwinding = { version = "0.2.3", default-features = false, features = ["fde-gnu-eh-frame-hdr", "hide-trace", "panic", "personality", "unwinder"] }

View File

@ -16,7 +16,7 @@ use alloc::{boxed::Box, collections::BTreeSet, string::String, vec::Vec};
use core::{any::Any, format_args}; use core::{any::Any, format_args};
use ostd::{ use ostd::{
early_print, early_print, early_println,
ktest::{ ktest::{
get_ktest_crate_whitelist, get_ktest_test_whitelist, KtestError, KtestItem, KtestIter, get_ktest_crate_whitelist, get_ktest_test_whitelist, KtestError, KtestItem, KtestIter,
}, },
@ -54,11 +54,32 @@ fn main() {
unreachable!("The spawn method will NOT return in the boot context") unreachable!("The spawn method will NOT return in the boot context")
} }
#[ostd::ktest::panic_handler]
fn panic_handler(info: &core::panic::PanicInfo) -> ! {
let _irq_guard = ostd::trap::disable_local();
use alloc::{boxed::Box, string::ToString};
use ostd::panic::begin_panic;
let throw_info = ostd::ktest::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,
};
// Throw an exception and expecting it to be caught.
begin_panic(Box::new(throw_info.clone()));
// If not caught, abort the kernel.
early_println!("An uncaught panic occurred: {:#?}", throw_info);
ostd::prelude::abort();
}
/// Run all the tests registered by `#[ktest]` in the `.ktest_array` section. /// Run all the tests registered by `#[ktest]` in the `.ktest_array` section.
/// ///
/// Need to provide a print function `print` to print the test result, and a `catch_unwind`
/// implementation to catch the panic.
///
/// The `whitelist` argument is optional. If it is `None`, all tests compiled will be run. /// The `whitelist` argument is optional. If it is `None`, all tests compiled will be run.
/// If it is `Some`, only the tests whose test path being the suffix of any paths in the whitelist /// If it is `Some`, only the tests whose test path being the suffix of any paths in the whitelist
/// will be run. /// will be run.
@ -129,7 +150,7 @@ fn run_crate_ktests(crate_: &KtestCrate, whitelist: &Option<SuffixTrie>) -> Ktes
); );
debug_assert_eq!(test.info().package, crate_name); debug_assert_eq!(test.info().package, crate_name);
match test.run( match test.run(
&(unwinding::panic::catch_unwind::<(), fn()> &(ostd::panic::catch_unwind::<(), fn()>
as fn(fn()) -> Result<(), Box<(dyn Any + Send + 'static)>>), as fn(fn()) -> Result<(), Box<(dyn Any + Send + 'static)>>),
) { ) {
Ok(()) => { Ok(()) => {

View File

@ -31,8 +31,6 @@ pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
#[no_mangle] #[no_mangle]
#[linkage = "weak"] #[linkage = "weak"]
extern "Rust" fn __ostd_main() -> ! { extern "Rust" fn __ostd_main() -> ! {
// SAFETY: The function is called only once on the BSP.
unsafe { ostd::init() };
#main_fn_name(); #main_fn_name();
ostd::prelude::abort(); ostd::prelude::abort();
} }
@ -58,8 +56,6 @@ pub fn test_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
quote!( quote!(
#[no_mangle] #[no_mangle]
extern "Rust" fn __ostd_main() -> ! { extern "Rust" fn __ostd_main() -> ! {
// SAFETY: The function is called only once on the BSP.
unsafe { ostd::init() };
#main_fn_name(); #main_fn_name();
ostd::prelude::abort(); ostd::prelude::abort();
} }
@ -69,6 +65,49 @@ pub fn test_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
.into() .into()
} }
/// A macro attribute for the panic handler.
///
/// The attributed function will be used to override OSTD's default
/// implementation of Rust's `#[panic_handler]`. The function takes a single
/// parameter of type `&core::panic::PanicInfo` and does not return.
#[proc_macro_attribute]
pub fn panic_handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
let handler_fn = parse_macro_input!(item as ItemFn);
let handler_fn_name = &handler_fn.sig.ident;
quote!(
#[cfg(not(ktest))]
#[no_mangle]
extern "Rust" fn __ostd_panic_handler(info: &core::panic::PanicInfo) -> ! {
#handler_fn_name(info);
}
#[cfg(not(ktest))]
#handler_fn
)
.into()
}
/// A macro attribute for the panic handler.
///
/// This macro is used for internal OSDK implementation. Do not use it
/// directly.
#[proc_macro_attribute]
pub fn test_panic_handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
let handler_fn = parse_macro_input!(item as ItemFn);
let handler_fn_name = &handler_fn.sig.ident;
quote!(
#[no_mangle]
extern "Rust" fn __ostd_panic_handler(info: &core::panic::PanicInfo) -> ! {
#handler_fn_name(info);
}
#handler_fn
)
.into()
}
/// The test attribute macro to mark a test function. /// The test attribute macro to mark a test function.
/// ///
/// # Example /// # Example

View File

@ -75,7 +75,6 @@ pub struct PanicInfo {
pub file: String, pub file: String,
pub line: usize, pub line: usize,
pub col: usize, pub col: usize,
pub resolve_panic: fn(),
} }
impl core::fmt::Display for PanicInfo { impl core::fmt::Display for PanicInfo {
@ -163,7 +162,6 @@ impl KtestItem {
Ok(()) => Err(KtestError::ShouldPanicButNoPanic), Ok(()) => Err(KtestError::ShouldPanicButNoPanic),
Err(e) => match e.downcast::<PanicInfo>() { Err(e) => match e.downcast::<PanicInfo>() {
Ok(s) => { Ok(s) => {
(s.resolve_panic)();
if let Some(expected) = self.should_panic.1 { if let Some(expected) = self.should_panic.1 {
if s.message == expected { if s.message == expected {
Ok(()) Ok(())

View File

@ -68,7 +68,7 @@ pub struct CallbackElement {
impl CallbackElement { impl CallbackElement {
pub fn call(&self, element: &TrapFrame) { 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. /// This function should be only called from the bootloader-specific module.
pub(crate) fn call_ostd_main() -> ! { pub(crate) fn call_ostd_main() -> ! {
unsafe {
// The entry point of kernel code, which should be defined by the package that // The entry point of kernel code, which should be defined by the package that
// uses OSTD. // uses OSTD.
extern "Rust" { extern "Rust" {
fn __ostd_main() -> !; 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 {
__ostd_main(); __ostd_main();
} }
} }

View File

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

View File

@ -4,60 +4,45 @@
use core::ffi::c_void; use core::ffi::c_void;
pub use unwinding::panic::{begin_panic, catch_unwind};
use crate::{ use crate::{
arch::qemu::{exit_qemu, QemuExitCode}, arch::qemu::{exit_qemu, QemuExitCode},
cpu_local_cell, early_print, early_println, early_print, early_println,
sync::SpinLock, sync::SpinLock,
}; };
extern crate cfg_if; extern crate cfg_if;
extern crate gimli; extern crate gimli;
use gimli::Register; use gimli::Register;
use unwinding::abi::{ use unwinding::abi::{
UnwindContext, UnwindReasonCode, _Unwind_Backtrace, _Unwind_FindEnclosingFunction, UnwindContext, UnwindReasonCode, _Unwind_Backtrace, _Unwind_FindEnclosingFunction,
_Unwind_GetGR, _Unwind_GetIP, _Unwind_GetGR, _Unwind_GetIP,
}; };
cpu_local_cell! { /// The default panic handler for OSTD based kernels.
static IN_PANIC: bool = false;
}
/// The asterinas panic handler.
/// ///
/// The panic handler must be defined in the binary crate or in the crate that the binary /// The user can override it by defining their own panic handler with the macro
/// crate explicitly declares by `extern crate`. We cannot let the base crate depend on OSTD /// `#[ostd::panic_handler]`.
/// due to prismatic dependencies. That's why we export this symbol and state the #[cfg(not(ktest))]
/// panic handler in the binary crate. #[no_mangle]
#[export_name = "__aster_panic_handler"] pub fn __ostd_panic_handler(info: &core::panic::PanicInfo) -> ! {
pub fn panic_handler(info: &core::panic::PanicInfo) -> ! {
let _irq_guard = crate::trap::disable_local(); let _irq_guard = crate::trap::disable_local();
crate::cpu_local_cell! {
static IN_PANIC: bool = false;
}
if IN_PANIC.load() { if IN_PANIC.load() {
early_println!("{}", info); early_println!("The panic handler panicked {:#?}", info);
early_println!("The panic handler panicked when processing the above panic. Aborting.");
abort(); abort();
} }
// If in ktest, we would like to catch the panics and resume the test. IN_PANIC.store(true);
#[cfg(ktest)]
{
use alloc::{boxed::Box, string::ToString};
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(); print_stack_trace();
abort(); abort();
} }
@ -67,7 +52,10 @@ pub fn abort() -> ! {
exit_qemu(QemuExitCode::Failed); 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 /// We acquire a global lock to prevent the frames in the stack trace from
/// interleaving. The spin lock is used merely for its simplicity. /// interleaving. The spin lock is used merely for its simplicity.
static BACKTRACE_PRINT_LOCK: SpinLock<()> = SpinLock::new(()); static BACKTRACE_PRINT_LOCK: SpinLock<()> = SpinLock::new(());
@ -98,6 +86,10 @@ fn print_stack_trace() {
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] { if #[cfg(target_arch = "x86_64")] {
let reg_name = gimli::X86_64::register_name(Register(i)).unwrap_or("unknown"); 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 { } else {
let reg_name = "unknown"; let reg_name = "unknown";
} }

View File

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

View File

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