Implement should_panic for ktest and clear the codebase

This commit is contained in:
Zhang Junyang 2023-11-05 00:14:28 +08:00 committed by Tate, Hongliang Tian
parent bb0560530f
commit 45a6b2f46c
14 changed files with 242 additions and 170 deletions

16
Cargo.lock generated
View File

@ -478,6 +478,12 @@ dependencies = [
"syn 2.0.29",
]
[[package]]
name = "gimli"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
[[package]]
name = "glob"
version = "0.3.1"
@ -647,6 +653,7 @@ dependencies = [
"static_assertions",
"tdx-guest",
"trapframe",
"unwinding",
"volatile",
"x86",
"x86_64",
@ -1446,6 +1453,15 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "unwinding"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37a19a21a537f635c16c7576f22d0f2f7d63353c1337ad4ce0d8001c7952a25b"
dependencies = [
"gimli",
]
[[package]]
name = "utf8parse"
version = "0.2.1"

View File

@ -17,6 +17,18 @@ x86_64 = "0.14.2"
jinux-time = { path = "services/comps/time" }
jinux-framebuffer = { path = "services/comps/framebuffer" }
[profile.dev]
opt-level = 0
debug = true
lto = false
panic = "unwind"
[profile.release]
opt-level = 3
debug = false
lto = false
panic = "unwind"
[workspace]
members = [

View File

@ -1,16 +1,19 @@
use std::{error::Error, path::PathBuf};
fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let linker_script_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
let target = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let linker_script_path = if target == "x86_64" {
PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
.join("framework")
.join("jinux-frame")
.join("src")
.join("arch")
.join("x86")
.join("boot")
.join("linker.ld");
.join("linker.ld")
} else {
panic!("Unsupported target arch: {}", target);
};
println!("cargo:rerun-if-changed={}", linker_script_path.display());
println!("cargo:rustc-link-arg=-T{}", linker_script_path.display());
println!("cargo:rerun-if-env-changed=CARGO_PKG_NAME");
Ok(())
}

View File

@ -6,24 +6,25 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bitflags = "1.3"
cfg-if = "1.0"
spin = "0.9.4"
volatile = { version = "0.4.5", features = ["unstable"] }
buddy_system_allocator = "0.9.0"
pod = { git = "https://github.com/jinzhao-dev/pod", rev = "d7dba56" }
align_ext = { path = "../libs/align_ext" }
ktest = { path = "../libs/ktest" }
intrusive-collections = "0.9.5"
log = "0.4"
lazy_static = { version = "1.0", features = ["spin_no_std"] }
trapframe = { git = "https://github.com/jinzhao-dev/trapframe-rs", rev = "9758a83" }
inherit-methods-macro = { git = "https://github.com/jinzhao-dev/inherit-methods-macro", rev = "98f7e3e" }
tdx-guest = { path = "../libs/tdx-guest", optional = true }
bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
static_assertions = "1.1.0"
int-to-c-enum = { path = "../../services/libs/int-to-c-enum" }
bit_field = "0.10.1"
bitflags = "1.3"
bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
buddy_system_allocator = "0.9.0"
cfg-if = "1.0"
inherit-methods-macro = { git = "https://github.com/jinzhao-dev/inherit-methods-macro", rev = "98f7e3e" }
int-to-c-enum = { path = "../../services/libs/int-to-c-enum" }
intrusive-collections = "0.9.5"
ktest = { path = "../libs/ktest" }
lazy_static = { version = "1.0", features = ["spin_no_std"] }
log = "0.4"
pod = { git = "https://github.com/jinzhao-dev/pod", rev = "d7dba56" }
spin = "0.9.4"
static_assertions = "1.1.0"
tdx-guest = { path = "../libs/tdx-guest", optional = true }
trapframe = { git = "https://github.com/jinzhao-dev/trapframe-rs", rev = "9758a83" }
unwinding = { version = "0.2.1", default-features = false, features = ["fde-static", "hide-trace", "panic", "personality", "unwinder"] }
volatile = { version = "0.4.5", features = ["unstable"] }
[target.x86_64-custom.dependencies]
x86_64 = "0.14.2"

View File

@ -21,20 +21,23 @@ SECTIONS
. += KERNEL_VMA;
.text : AT(ADDR(.text) - KERNEL_VMA) { *(.text .text.*) }
.text : AT(ADDR(.text) - KERNEL_VMA) {
*(.text .text.*)
PROVIDE(__etext = .);
}
.rodata : AT(ADDR(.rodata) - KERNEL_VMA) { *(.rodata .rodata.*) }
.eh_frame_hdr : AT(ADDR(.eh_frame_hdr) - KERNEL_VMA) {
__eh_frame_hdr = .;
KEEP(*(.eh_frame_hdr))
__eh_frame_hdr_end = .;
KEEP(*(.eh_frame_hdr .eh_frame_hdr.*))
}
. = ALIGN(8);
.eh_frame : AT(ADDR(.eh_frame) - KERNEL_VMA) {
__eh_frame = .;
KEEP(*(.eh_frame))
__eh_frame_end = .;
PROVIDE(__eh_frame = .);
KEEP(*(.eh_frame .eh_frame.*))
}
.gcc_except_table : AT(ADDR(.gcc_except_table) - KERNEL_VMA) { *(.gcc_except_table .gcc_except_table.*) }
.data.rel.ro : AT(ADDR(.data.rel.ro) - KERNEL_VMA) { *(.data.rel.ro .data.rel.ro.*) }
.dynamic : AT(ADDR(.dynamic) - KERNEL_VMA) { *(.dynamic) }

View File

@ -7,6 +7,7 @@ pub(crate) mod irq;
pub(crate) mod kernel;
pub(crate) mod mm;
pub(crate) mod pci;
pub mod qemu;
#[cfg(feature = "intel_tdx")]
pub(crate) mod tdx_guest;
pub(crate) mod timer;

View File

@ -0,0 +1,23 @@
//! QEMU isa debug device.
/// The exit code of x86 QEMU isa debug device. In `qemu-system-x86_64` the
/// exit code will be `(code << 1) | 1`. So you could never let QEMU invoke
/// `exit(0)`. We also need to check if the exit code is returned by the
/// kernel, so we couldn't use 0 as exit_success because this may conflict
/// with QEMU return value 1, which indicates that QEMU itself fails.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {
Success = 0x10,
Failed = 0x20,
}
pub fn exit_qemu(exit_code: QemuExitCode) -> ! {
use x86_64::instructions::port::Port;
unsafe {
let mut port = Port::new(0xf4);
port.write(exit_code as u32);
}
unreachable!()
}

View File

@ -1,21 +1,22 @@
//! The framework part of Jinux.
#![no_std]
#![allow(dead_code)]
#![allow(unused_variables)]
#![feature(negative_impls)]
#![feature(fn_traits)]
#![feature(const_maybe_uninit_zeroed)]
#![feature(alloc_error_handler)]
#![feature(core_intrinsics)]
#![feature(new_uninit)]
#![feature(strict_provenance)]
#![feature(const_maybe_uninit_zeroed)]
#![feature(const_mut_refs)]
#![feature(const_ptr_sub_ptr)]
#![feature(const_trait_impl)]
#![feature(core_intrinsics)]
#![feature(fn_traits)]
#![feature(generators)]
#![feature(iter_from_generator)]
#![feature(const_mut_refs)]
#![feature(let_chains)]
#![feature(negative_impls)]
#![feature(new_uninit)]
#![feature(panic_info_message)]
#![feature(ptr_sub_ptr)]
#![feature(const_ptr_sub_ptr)]
#![feature(strict_provenance)]
#![allow(dead_code)]
#![allow(unused_variables)]
#![no_std]
extern crate alloc;
#[macro_use]
@ -32,6 +33,7 @@ pub mod cpu;
mod error;
pub mod io_mem;
pub mod logger;
pub mod panicking;
pub mod prelude;
pub mod sync;
pub mod task;
@ -44,14 +46,8 @@ pub mod vm;
pub use self::cpu::CpuLocal;
pub use self::error::Error;
pub use self::prelude::Result;
use alloc::vec::Vec;
use arch::irq::{IrqCallbackHandle, IrqLine};
use core::{mem, panic::PanicInfo};
#[cfg(feature = "intel_tdx")]
use tdx_guest::init_tdx;
use trapframe::TrapFrame;
static mut IRQ_CALLBACK_LIST: Vec<IrqCallbackHandle> = Vec::new();
pub fn init() {
arch::before_all_init();
@ -69,19 +65,10 @@ pub fn init() {
trap::init();
arch::after_all_init();
bus::init();
register_irq_common_callback();
invoke_c_init_funcs();
invoke_ffi_init_funcs();
}
fn register_irq_common_callback() {
unsafe {
for i in 0..256 {
IRQ_CALLBACK_LIST.push(IrqLine::acquire(i as u8).on_active(general_handler))
}
}
}
fn invoke_c_init_funcs() {
fn invoke_ffi_init_funcs() {
extern "C" {
fn __sinit_array();
fn __einit_array();
@ -95,86 +82,17 @@ fn invoke_c_init_funcs() {
}
}
fn general_handler(trap_frame: &TrapFrame) {
// info!("general handler");
// println!("{:#x?}", trap_frame);
// println!("rip = 0x{:x}", trap_frame.rip);
// println!("rsp = 0x{:x}", trap_frame.rsp);
// println!("cr2 = 0x{:x}", trap_frame.cr2);
// // println!("rbx = 0x{:x}", trap_frame.)
// panic!("couldn't handler trap right now");
}
#[inline(always)]
pub(crate) const fn zero<T>() -> T {
unsafe { mem::MaybeUninit::zeroed().assume_init() }
}
/// The panic handler provided by Jinux Frame.
///
/// The definition of the real panic handler is located at the kernel binary
/// crate with the `#[panic_handler]` attribute. This function provides a
/// default implementation of the panic handler, which can forwarded to by the
/// kernel binary crate.
///
/// ```rust
/// extern crate jinux_frame;
/// #[panic_handler]
/// fn panic(info: &PanicInfo) -> ! {
/// jinux_frame::panic_handler(info);
/// }
/// ```
pub fn panic_handler(info: &PanicInfo) -> ! {
println!("[panic]:{:#?}", info);
// let mut fp: usize;
// let stop = unsafe{
// Task::current().kstack.get_top()
// };
// info!("stop:{:x}",stop);
// unsafe{
// asm!("mov rbp, {}", out(reg) fp);
// info!("fp:{:x}",fp);
// println!("---START BACKTRACE---");
// for i in 0..10 {
// if fp == stop {
// break;
// }
// println!("#{}:ra={:#x}", i, *((fp - 8) as *const usize));
// info!("fp target:{:x}",*((fp ) as *const usize));
// fp = *((fp - 16) as *const usize);
// }
// println!("---END BACKTRACE---");
// }
exit_qemu(QemuExitCode::Failed);
}
/// The exit code of x86 QEMU isa debug device. In `qemu-system-x86_64` the
/// exit code will be `(code << 1) | 1`. So you could never let QEMU invoke
/// `exit(0)`. We also need to check if the exit code is returned by the
/// kernel, so we couldn't use 0 as exit_success because this may conflict
/// with QEMU return value 1, which indicates that QEMU itself fails.
#[cfg(target_arch = "x86_64")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {
Success = 0x10,
Failed = 0x20,
}
pub fn exit_qemu(exit_code: QemuExitCode) -> ! {
use x86_64::instructions::port::Port;
unsafe {
let mut port = Port::new(0xf4);
port.write(exit_code as u32);
}
unreachable!()
}
/// Unit test for the ktest framework and functions of the frame.
#[if_cfg_ktest]
mod test {
#[ktest]
fn trivial_assertion() {
assert_eq!(0, 0);
}
#[ktest]
#[should_panic]
fn failing_assertion() {
assert_eq!(0, 1);
}
}

View File

@ -0,0 +1,38 @@
//! Panic support in Jinux Frame.
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use crate::arch::qemu::{exit_qemu, QemuExitCode};
use crate::println;
#[derive(Clone, Debug)]
pub struct PanicInfo {
pub message: String,
pub file: String,
pub line: usize,
pub col: usize,
}
impl core::fmt::Display for PanicInfo {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
writeln!(f, "Panicked at {}:{}:{}", self.file, self.line, self.col)?;
writeln!(f, "{}", self.message)
}
}
#[panic_handler]
pub fn panic_handler(info: &core::panic::PanicInfo) -> ! {
let throw_info = PanicInfo {
message: info.message().unwrap().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.
unwinding::panic::begin_panic(Box::new(throw_info.clone()));
// If the exception is not caught (e.g. by ktest), then print the information
// and exit failed using the debug device.
println!("[uncaught panic] {}", info);
exit_qemu(QemuExitCode::Failed);
}

View File

@ -21,6 +21,11 @@
//! fn trivial_assertion() {
//! assert_eq!(0, 0);
//! }
//! #[ktest]
//! #[should_panic]
//! fn failing_assertion() {
//! assert_eq!(0, 1);
//! }
//! }
//! ```
//!
@ -54,8 +59,8 @@
//! a default conditional compilation setting:
//! `#[cfg(all(ktest, any(ktest = "all", ktest = #crate_name)))]`
//!
//! Currently we do not support `#[should_panic]` attribute, and this feature will
//! be added in the future.
//! We do not support `#[should_panic]` attribute, but the implementation is quite
//! slow currently. Use it with cautious.
//!
//! Doctest is not taken into consideration yet, and the interface is subject to
//! change.
@ -128,27 +133,43 @@ pub fn ktest(_attr: TokenStream, item: TokenStream) -> TokenStream {
proc_macro2::Span::call_site(),
);
let should_panic = input.attrs.iter().any(|attr| {
attr.path()
.segments
.iter()
.any(|segment| segment.ident == "should_panic")
});
let package_name = std::env::var("CARGO_PKG_NAME").unwrap();
let span = proc_macro::Span::call_site();
let source = span.source_file();
let crate_name = std::env::var("CARGO_PKG_NAME").unwrap();
let hint_str = format!(
"[{}] {}: {}()",
crate_name,
source.path().to_str().unwrap(),
fn_name
);
let source = span.source_file().path();
let source = source.to_str().unwrap();
let line = span.line();
let col = span.column();
let register = quote! {
struct #ktest_item_struct {
fn_: fn() -> (),
hint: &'static str,
should_panic: bool,
module_path: &'static str,
fn_name: &'static str,
package: &'static str,
source: &'static str,
line: usize,
col: usize,
}
#[cfg(ktest)]
#[used]
#[link_section = ".ktest_array"]
static #fn_ktest_item_name: #ktest_item_struct = #ktest_item_struct {
fn_: #fn_name,
hint: #hint_str,
should_panic: #should_panic,
module_path: module_path!(),
fn_name: stringify!(#fn_name),
package: #package_name,
source: #source,
line: #line,
col: #col,
};
};
@ -165,27 +186,70 @@ pub fn ktest(_attr: TokenStream, item: TokenStream) -> TokenStream {
#[proc_macro]
pub fn do_ktests(_item: TokenStream) -> TokenStream {
let body = quote! {
use crate::arch::qemu::{exit_qemu, QemuExitCode};
struct KtestItem {
fn_: fn() -> (),
hint: &'static str,
should_panic: bool,
module_path: &'static str,
fn_name: &'static str,
package: &'static str,
source: &'static str,
line: usize,
col: usize,
};
extern "C" {
fn __ktest_array();
fn __ktest_array_end();
}
};
let item_size = core::mem::size_of::<KtestItem>() as u64;
let l = (__ktest_array_end as u64 - __ktest_array as u64) / item_size;
crate::println!("Running {} tests", l);
for i in 0..l {
unsafe {
let address = (__ktest_array as u64 + item_size * i) as *const u64;
let item = address as *const KtestItem;
crate::print!("{} ...", (*item).hint);
((*item).fn_)();
let item_ptr = (__ktest_array as u64 + item_size * i) as *const u64;
let item = item_ptr as *const KtestItem;
crate::print!("[{}] test {}::{} ...", (*item).package, (*item).module_path, (*item).fn_name);
let test_result = unwinding::panic::catch_unwind((*item).fn_);
let print_failure_heading = || {
crate::println!("\nfailures:\n");
crate::println!("---- {}:{}:{} - {} ----", (*item).source, (*item).line, (*item).col, (*item).fn_name);
};
if !(*item).should_panic {
match test_result {
Ok(()) => {
crate::println!(" ok");
},
Err(e) => {
crate::println!(" FAILED");
print_failure_heading();
match e.downcast::<crate::panicking::PanicInfo>() {
Ok(s) => {
crate::println!("[caught panic] {}", s);
},
Err(payload) => {
crate::println!("[caught panic] unknown panic payload: {:#?}", payload);
},
}
crate::println!(" Ok!");
exit_qemu(QemuExitCode::Failed);
},
}
crate::exit_qemu(crate::QemuExitCode::Success);
} else {
match test_result {
Ok(()) => {
crate::println!(" FAILED");
print_failure_heading();
crate::println!("test did not panic as expected");
exit_qemu(QemuExitCode::Failed);
},
Err(_) => {
crate::println!(" ok");
},
}
}
}
}
exit_qemu(QemuExitCode::Success);
};
TokenStream::from(body)

View File

@ -1,23 +1,17 @@
#![no_std]
#![no_main]
// The `no_mangle`` attribute for the `jinux_main` entrypoint requires the removal of safety check.
// The `export_name` attribute for the `jinux_main` entrypoint requires the removal of safety check.
// Please be aware that the kernel is not allowed to introduce any other unsafe operations.
// #![forbid(unsafe_code)]
extern crate jinux_frame;
use core::panic::PanicInfo;
use jinux_frame::println;
#[no_mangle]
pub fn jinux_main() -> ! {
#[export_name = "jinux_main"]
pub fn main() -> ! {
jinux_frame::init();
println!("[kernel] finish init jinux_frame");
component::init_all(component::parse_metadata!()).unwrap();
jinux_std::init();
jinux_std::run_first_process();
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
jinux_frame::panic_handler(info);
}

View File

@ -27,7 +27,10 @@ use crate::{
Thread,
},
};
use jinux_frame::{boot, exit_qemu, QemuExitCode};
use jinux_frame::{
arch::qemu::{exit_qemu, QemuExitCode},
boot,
};
use process::Process;
extern crate alloc;

View File

@ -531,9 +531,6 @@ mod test {
}
#[ktest]
// FIXME: should_panic doesn't work with ktest, two negative makes a positive...
#[should_panic]
/// FIXME: alloc continuous frames is not supported now
fn alloc_continuous_vmo() {
let vmo = VmoOptions::<Full>::new(10 * PAGE_SIZE)
.flags(VmoFlags::CONTIGUOUS)

View File

@ -11,7 +11,6 @@
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"panic-strategy": "abort",
"disable-redzone": true,
"features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float"
}