Guides#
pw_kernel: A Rust-based kernel for embedded systems
This guide covers real-world usage topics such as unit testing and panic detecting.
Unit testing#
pw_kernel includes a lightweight unit testing framework designed for both
bare-metal and kernel-aware tests.
Writing a test#
Tests are written as standard Rust functions annotated with the #[test]
attribute from the unittest crate. The test function should return a
unittest::Result<()>.
Bare-metal test example:
// in your_module/lib.rs or your_module/tests.rs
#[cfg(test)]
mod tests {
use unittest::test;
fn add(a: u32, b: u32) -> u32 {
a + b
}
#[test]
fn test_addition() -> unittest::Result<()> {
unittest::assert_eq!(add(2, 2), 4);
unittest::assert_ne!(add(2, 2), 5);
unittest::assert_true!(add(1,1) == 2);
Ok(())
}
}
Assertions#
The unittest crate provides several assertion macros:
unittest::assert_eq!(a, b): Asserts thatais equal tob.unittest::assert_ne!(a, b): Asserts thatais not equal tob.unittest::assert_true!(expr): Asserts thatexprevaluates to true.unittest::assert_false!(expr): Asserts thatexprevaluates to false.
Kernel-aware tests#
For tests that require kernel services (e.g., testing scheduler behavior, mutexes,
or timers), you can mark them as needing the kernel by passing the
needs_kernel argument to the #[test] attribute:
use kernel::sync::mutex::Mutex;
use unittest::test;
static MY_MUTEX: Mutex<u32> = Mutex::new(0);
#[test(needs_kernel)]
fn test_mutex_locking() -> unittest::Result<()> {
let guard = MY_MUTEX.lock();
unittest::assert_eq!(*guard, 0);
// guard is dropped here, unlocking the mutex
Ok(())
}
Panic detector#
panic_detector scans a Rust binary and uses static analysis to display
a list of all panic call sites.
Currently only rv32 ELF files are supported.
Setup:
Add a panic handler to the Rust binary which will be used by
panic_detectorto find the panic call sites.#[panic_handler] fn panic_handler(info: &core::panic::PanicInfo) -> ! { if let Some(location) = info.location() { panic_is_possible(location.file().as_ptr(), location.file().len(), location.line(), location.column()); } else { panic_is_possible(core::ptr::null(), 0, 0, 0); } } #[unsafe(no_mangle)] #[inline(never)] extern "C" fn panic_is_possible(filename: *const u8, filename_len: usize, line: u32, col: u32) -> !{ // The arguments to this function are reverse-engineereddocs.html // from the machine code by static analysis to // display a list of all the panic call-sites to the // user in the mutask_no_panic_test() error message. // See welder/src/check_panic.rs for more details. // If this symbol exists in the binary, panics are // possible. Presubmit tests can ensure that this symbol // does not exist in the final binary. Do not rename or // remove this function. core::hint::black_box(filename); core::hint::black_box(filename_len); core::hint::black_box(line); core::hint::black_box(col); loop {} }
Add a test target to Bazel for the binary to validate.
rust_binary( name = "example", srcs = ["main.rs"], ) load("//pw_kernel/tooling/panic_detector:rust_binary_no_panics_test.bzl", "rust_binary_no_panics_test") rust_binary_no_panics_test( name = "example_no_panic_test", binary = ":example", )
Run the bazel test. If any panics are detected ,
panic_detectorprints the locations tostdout.$ bazel test //:example_no_panic_test … Found panic ufmt-0.2.0/src/helpers.rs line 58 column 13. Branch trace: 00103896 lui a0,0x104 (task0_entry) 0010389e jal ra,-1734 (task0_entry) 0010320a jalr ra,ra,-82 (panic_const_add_overflow) 001031d2 jalr ra,ra,-78 (core::panicking::panic_fmt) 00103192 jal ra,2 (rust_begin_unwind) 00103194 addi sp,sp,-16 (panic_is_possible) …