blob: ea5760f7acde8e42025ffd1c9addba524432c1a7 [file] [log] [blame]
use genmc_sys::LogLevel;
use super::GenmcParams;
use crate::{IsolatedOp, MiriConfig, RejectOpWith};
/// Configuration for GenMC mode.
/// The `params` field is shared with the C++ side.
/// The remaining options are kept on the Rust side.
#[derive(Debug, Default, Clone)]
pub struct GenmcConfig {
/// Parameters sent to the C++ side to create a new handle to the GenMC model checker.
pub(super) params: GenmcParams,
pub(super) do_estimation: bool,
/// Print the output message that GenMC generates when an error occurs.
/// This error message is currently hard to use, since there is no clear mapping between the events that GenMC sees and the Rust code location where this event was produced.
pub(super) print_genmc_output: bool,
/// The log level for GenMC.
pub(super) log_level: LogLevel,
/// Enable more verbose output, such as number of executions estimate
/// and time to completion of verification step.
pub(super) verbose_output: bool,
}
impl GenmcConfig {
/// Function for parsing command line options for GenMC mode.
///
/// All GenMC arguments start with the string "-Zmiri-genmc".
/// Passing any GenMC argument will enable GenMC mode.
///
/// `trimmed_arg` should be the argument to be parsed, with the suffix "-Zmiri-genmc" removed.
pub fn parse_arg(
genmc_config: &mut Option<GenmcConfig>,
trimmed_arg: &str,
) -> Result<(), String> {
if genmc_config.is_none() {
*genmc_config = Some(Default::default());
}
if trimmed_arg.is_empty() {
return Ok(()); // this corresponds to "-Zmiri-genmc"
}
let genmc_config = genmc_config.as_mut().unwrap();
let Some(trimmed_arg) = trimmed_arg.strip_prefix("-") else {
return Err(format!("Invalid GenMC argument \"-Zmiri-genmc{trimmed_arg}\""));
};
if let Some(log_level) = trimmed_arg.strip_prefix("log=") {
genmc_config.log_level = log_level.parse()?;
} else if let Some(trimmed_arg) = trimmed_arg.strip_prefix("print-exec-graphs") {
use genmc_sys::ExecutiongraphPrinting;
genmc_config.params.print_execution_graphs = match trimmed_arg {
"=none" => ExecutiongraphPrinting::None,
// Make GenMC print explored executions.
"" | "=explored" => ExecutiongraphPrinting::Explored,
// Make GenMC print blocked executions.
"=blocked" => ExecutiongraphPrinting::Blocked,
// Make GenMC print all executions.
"=all" => ExecutiongraphPrinting::ExploredAndBlocked,
_ =>
return Err(format!(
"Invalid suffix to GenMC argument '-Zmiri-genmc-print-exec-graphs', expected '', '=none', '=explored', '=blocked' or '=all'"
)),
}
} else if trimmed_arg == "estimate" {
// FIXME(genmc): should this be on by default (like for GenMC)?
// Enable estimating the execution space and require time before running the actual verification.
genmc_config.do_estimation = true;
} else if let Some(estimation_max_str) = trimmed_arg.strip_prefix("estimation-max=") {
// Set the maximum number of executions to explore during estimation.
genmc_config.params.estimation_max = estimation_max_str.parse().ok().filter(|estimation_max| *estimation_max > 0).ok_or_else(|| {
format!(
"'-Zmiri-genmc-estimation-max=...' expects a positive integer argument, but got '{estimation_max_str}'"
)
})?;
} else if trimmed_arg == "print-genmc-output" {
genmc_config.print_genmc_output = true;
} else if trimmed_arg == "verbose" {
genmc_config.verbose_output = true;
} else {
return Err(format!("Invalid GenMC argument: \"-Zmiri-genmc-{trimmed_arg}\""));
}
Ok(())
}
/// Validate settings for GenMC mode (NOP if GenMC mode disabled).
///
/// Unsupported configurations return an error.
/// Adjusts Miri settings where required, printing a warnings if the change might be unexpected for the user.
pub fn validate(miri_config: &mut MiriConfig) -> Result<(), &'static str> {
let Some(genmc_config) = miri_config.genmc_config.as_mut() else {
return Ok(());
};
// Check for disallowed configurations.
if !miri_config.data_race_detector {
return Err("Cannot disable data race detection in GenMC mode");
} else if !miri_config.native_lib.is_empty() {
return Err("native-lib not supported in GenMC mode.");
} else if miri_config.isolated_op != IsolatedOp::Reject(RejectOpWith::Abort) {
return Err("Cannot disable isolation in GenMC mode");
}
// Adjust settings where needed.
if !miri_config.weak_memory_emulation {
genmc_config.params.disable_weak_memory_emulation = true;
}
if miri_config.borrow_tracker.is_some() {
eprintln!(
"warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode."
);
miri_config.borrow_tracker = None;
}
// We enable fixed scheduling so Miri doesn't randomly yield before a terminator, which anyway
// would be a NOP in GenMC mode.
miri_config.fixed_scheduling = true;
Ok(())
}
}