From a4363e390510abe42226f86ba3bc7d1c0464f231 Mon Sep 17 00:00:00 2001 From: Brian Tsoi Date: Wed, 27 Nov 2024 14:17:58 -0500 Subject: [PATCH 1/2] Add test_failures.rs as example --- examples/test_failures.rs | 123 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 examples/test_failures.rs diff --git a/examples/test_failures.rs b/examples/test_failures.rs new file mode 100644 index 0000000..f73f238 --- /dev/null +++ b/examples/test_failures.rs @@ -0,0 +1,123 @@ +use { + anyhow::Context, + rust_memtester::{Memtester, MemtesterArgs}, + std::{ + mem::size_of, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread, + time::{Duration, Instant}, + }, + tracing::info, + tracing_subscriber::fmt::format::FmtSpan, +}; + +#[derive(Copy, Clone)] +struct SendableVecPtr(*mut Vec); + +unsafe impl Send for SendableVecPtr {} + +fn main() -> anyhow::Result<()> { + tracing_subscriber::fmt() + .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE) + .with_max_level(tracing::Level::TRACE) + .with_writer(std::io::stderr) + .with_thread_ids(true) + .init(); + let start_time = Instant::now(); + let (mem_usize_count, memtester_args) = match parse_args() { + Ok(parsed_args) => parsed_args, + Err(s) => { + eprintln!(concat!( + "Usage: rust-memtester ", + " ", + " ", + " ", + " ", + " ", + " " + )); + anyhow::bail!("Invalid/missing argument '{s}'"); + } + }; + + info!("Running memtester with: {memtester_args:#?}"); + let mut memory = vec![0; mem_usize_count]; + + let test_complete = Arc::new(AtomicBool::new(false)); + let test_complete_clone = test_complete.clone(); + let memory_ptr = SendableVecPtr(&mut memory); + + // TODO: This can seg fault if memory is resized when running memtester + let corrupt_memory_handle = thread::spawn(move || unsafe { + while !test_complete.load(Ordering::Acquire) { + const SLEEP_DURATION_MILLIS: u64 = 100; + corrupt_random_memory(memory_ptr); + thread::sleep(Duration::from_millis(SLEEP_DURATION_MILLIS)); + } + }); + + let memtester_handle = thread::spawn(move || { + let test_result = Memtester::all_tests_random_order(&memtester_args).run(&mut memory); + test_complete_clone.store(true, Ordering::Release); + // Wait for the corrupt memory thread to end before dropping `memory` + corrupt_memory_handle + .join() + .expect("corrupt memory thread panicked"); + test_result + }); + + let report_list = memtester_handle + .join() + .expect("memtester thread panicked") + .context("Failed to run memtester")?; + + println!("Tester ran for {:?}", start_time.elapsed()); + println!("Test results: \n{report_list}"); + + anyhow::ensure!( + report_list.all_pass(), + "Found failures or errors among memtest reports" + ); + Ok(()) +} + +/// Parse command line arguments to return a usize for the requested memory vector length and +/// other memtester arguments +fn parse_args() -> Result<(usize, MemtesterArgs), &'static str> { + const KB: usize = 1024; + const MB: usize = 1024 * KB; + + let mut iter = std::env::args().skip(1); + + macro_rules! parse_next(($n: literal) => { + iter.next().and_then(|s| s.parse().ok()).ok_or($n)? + }); + + let memsize: usize = parse_next!("memsize"); + let mem_usize_count = memsize * MB / size_of::(); + let timeout = Duration::from_millis(parse_next!("timeout_ms")); + + Ok(( + mem_usize_count, + MemtesterArgs { + timeout, + mem_lock_mode: parse_next!("mem_lock_mode"), + allow_working_set_resize: parse_next!("allow_working_set_resize"), + allow_multithread: parse_next!("allow_multithread"), + allow_early_termination: parse_next!("allow_early_termination"), + }, + )) +} + +unsafe fn corrupt_random_memory(memory: SendableVecPtr) { + use rand::Rng; + + const CORRUPTION_VAL: usize = 0; + let memory: &mut Vec = &mut *(memory.0); + + let n = rand::thread_rng().gen_range(0..memory.len()); + std::ptr::write_volatile(memory.as_mut_ptr().add(n), CORRUPTION_VAL) +} From 950562aab7f0c0a66c4424fad4e6020446fb5e9d Mon Sep 17 00:00:00 2001 From: Brian Tsoi Date: Thu, 28 Nov 2024 09:32:04 -0500 Subject: [PATCH 2/2] Simplify test_failures.rs to spawning one thread --- examples/test_failures.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/examples/test_failures.rs b/examples/test_failures.rs index f73f238..43e15ae 100644 --- a/examples/test_failures.rs +++ b/examples/test_failures.rs @@ -52,27 +52,20 @@ fn main() -> anyhow::Result<()> { // TODO: This can seg fault if memory is resized when running memtester let corrupt_memory_handle = thread::spawn(move || unsafe { - while !test_complete.load(Ordering::Acquire) { + while !test_complete_clone.load(Ordering::Acquire) { const SLEEP_DURATION_MILLIS: u64 = 100; corrupt_random_memory(memory_ptr); thread::sleep(Duration::from_millis(SLEEP_DURATION_MILLIS)); } }); - let memtester_handle = thread::spawn(move || { - let test_result = Memtester::all_tests_random_order(&memtester_args).run(&mut memory); - test_complete_clone.store(true, Ordering::Release); - // Wait for the corrupt memory thread to end before dropping `memory` - corrupt_memory_handle - .join() - .expect("corrupt memory thread panicked"); - test_result - }); - - let report_list = memtester_handle - .join() - .expect("memtester thread panicked") + let report_list = Memtester::all_tests_random_order(&memtester_args) + .run(&mut memory) .context("Failed to run memtester")?; + test_complete.store(true, Ordering::Release); + corrupt_memory_handle + .join() + .expect("corrupt memory thread panicked"); println!("Tester ran for {:?}", start_time.elapsed()); println!("Test results: \n{report_list}");