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

View File

@ -6,7 +6,7 @@ extern crate #TARGET_NAME#;
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
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]
ostd = { version = "0.9.3", path = "../../ostd" }
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 ostd::{
early_print,
early_print, early_println,
ktest::{
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")
}
#[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.
///
/// 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.
/// If it is `Some`, only the tests whose test path being the suffix of any paths in the whitelist
/// 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);
match test.run(
&(unwinding::panic::catch_unwind::<(), fn()>
&(ostd::panic::catch_unwind::<(), fn()>
as fn(fn()) -> Result<(), Box<(dyn Any + Send + 'static)>>),
) {
Ok(()) => {

View File

@ -31,8 +31,6 @@ pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
#[no_mangle]
#[linkage = "weak"]
extern "Rust" fn __ostd_main() -> ! {
// SAFETY: The function is called only once on the BSP.
unsafe { ostd::init() };
#main_fn_name();
ostd::prelude::abort();
}
@ -58,8 +56,6 @@ pub fn test_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
quote!(
#[no_mangle]
extern "Rust" fn __ostd_main() -> ! {
// SAFETY: The function is called only once on the BSP.
unsafe { ostd::init() };
#main_fn_name();
ostd::prelude::abort();
}
@ -69,6 +65,49 @@ pub fn test_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
.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.
///
/// # Example

View File

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

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() -> ! {
unsafe {
// 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 {
__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();
}