From 4159c341d959a4656293532a6aefba4eafcbdf53 Mon Sep 17 00:00:00 2001 From: Matthew Ahrens Date: Tue, 5 Oct 2021 09:06:02 -0700 Subject: [PATCH] die at specific location (#489) Allow specifying a specific file and/or line to die at, with the maybe_die_with() infrastructure. Caveat: maybe_die_with() must be called at least once from the selected call site before the time that we choose to die, otherwise the infrastructure won't be aware of that potential call site, and we won't die at all. We don't attempt to re-check later. --- cmd/zfs_object_agent/util/src/die.rs | 50 +++++++++++++++++++++------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/cmd/zfs_object_agent/util/src/die.rs b/cmd/zfs_object_agent/util/src/die.rs index c0f65caf2fc8..1173d46f52a2 100644 --- a/cmd/zfs_object_agent/util/src/die.rs +++ b/cmd/zfs_object_agent/util/src/die.rs @@ -18,8 +18,10 @@ use lazy_static::lazy_static; use log::*; use std::{ collections::HashSet, + ffi::OsStr, fmt::Display, panic::Location, + path::Path, sync::RwLock, time::{Duration, Instant}, }; @@ -32,6 +34,9 @@ lazy_static! { .map(|secs: f64| Duration::from_secs_f64(secs * rand::random::() * 2.0)); static ref LOCATIONS: RwLock>> = Default::default(); static ref BEGIN: Instant = Instant::now(); + // tunable should be the "basename" (e.g. zettacache.rs) + static ref DIE_FILE: Option = get_tunable("die_file", None); + static ref DIE_LINE: Option = get_tunable("die_line", None); } // Instead of taking a string (or Display) to print, this takes a function which @@ -54,21 +59,42 @@ where // DIE_LOCATION here so that we're sure to not evaluate it until // RUN_TIME has elapsed, so that LOCATIONS has been filled in. lazy_static! { - static ref DIE_LOCATION: &'static Location<'static> = { - let locations = LOCATIONS.read().unwrap(); - let die_location = *locations + static ref DIE_LOCATION: Option<&'static Location<'static>> = { + let possible_locations = LOCATIONS + .read() + .unwrap() .iter() - .nth(rand::random::() % locations.len()) - .unwrap(); - warn!( - "after running {} seconds, selected site to die: {}", - RUN_TIME.unwrap().as_secs(), - die_location - ); - die_location + .filter(|location| { + DIE_LINE.map_or(true, |line| location.line() == line) + && DIE_FILE.as_ref().map_or(true, |file| { + Path::new(location.file()).file_name().unwrap() + == OsStr::new(file) + }) + }) + .copied() + .collect::>(); + + if possible_locations.is_empty() { + warn!( + "after running {} seconds, no valid site to die; file:{:?} line:{:?}", + RUN_TIME.unwrap().as_secs(), + *DIE_FILE, + *DIE_LINE, + ); + None + } else { + let die_location = + possible_locations[rand::random::() % possible_locations.len()]; + warn!( + "after running {} seconds, selected site to die: {}", + RUN_TIME.unwrap().as_secs(), + die_location + ); + Some(die_location) + } }; } - if location == *DIE_LOCATION { + if Some(location) == *DIE_LOCATION { let msg = f(); let backtrace = Backtrace::new(); warn!("exiting to test failure handling: {} {:?}", msg, backtrace);