#![doc = include_str!("../README.md")]
mod args;
mod traits;
mod types;
mod verify;
use log::error;
use log::info;
use log::log_enabled;
use log::trace;
use log::warn;
use machine_check_common::check::KnownConclusion;
use machine_check_common::property::Property;
use machine_check_exec::Strategy;
use args::ProgramArgs;
pub use args::{ExecArgs, ExecStrategy};
pub use traits::Ext;
pub use types::{Bitvector, BitvectorArray, Signed, Unsigned};
pub use ::mck::concr::Machine;
use ::mck::concr::FullMachine;
pub use ::machine_check_macros::bitmask_switch;
pub use ::machine_check_macros::machine_description;
pub fn run<M: FullMachine>(system: M) -> ExecResult {
let parsed_args = <ExecArgs as clap::Parser>::parse_from(std::env::args());
execute(system, parsed_args)
}
pub fn parse_args<A: clap::Args>(args: impl Iterator<Item = String>) -> (ExecArgs, A) {
let parsed_args = <ProgramArgs<A> as clap::Parser>::parse_from(args);
(parsed_args.run_args, parsed_args.system_args)
}
pub use ::machine_check_common::ExecError;
pub use ::machine_check_common::ExecResult;
pub use ::machine_check_common::ExecStats;
pub fn execute<M: FullMachine>(system: M, exec_args: ExecArgs) -> ExecResult {
let silent = exec_args.silent;
let batch = exec_args.batch;
let mut filter_level_num = exec_args.verbose;
let mut overriden_log_level = false;
if let Ok(Ok(log_level_override)) =
std::env::var("MACHINE_CHECK_LOG_LEVEL_OVERRIDE").map(|var| var.parse::<u8>())
{
filter_level_num = log_level_override;
overriden_log_level = true;
}
let mut filter_level = match filter_level_num {
0 => log::LevelFilter::Info,
1 => log::LevelFilter::Debug,
_ => log::LevelFilter::Trace,
};
if silent && !overriden_log_level {
filter_level = log::LevelFilter::Off;
}
let _ = env_logger::builder().filter_level(filter_level).try_init();
if overriden_log_level {
warn!(
"Overriden log level to {:?} ({})",
filter_level, filter_level_num
);
}
let strategy = Strategy {
naive_inputs: matches!(exec_args.strategy, ExecStrategy::Naive),
use_decay: matches!(exec_args.strategy, ExecStrategy::Decay),
};
let prop = if let Some(property_str) = exec_args.property {
match Property::parse(&property_str) {
Ok(prop) => Some(prop),
Err(err) => {
error!("Cannot construct the property: {}", err);
return ExecResult {
result: Err(err),
stats: ExecStats::default(),
};
}
}
} else {
None
};
if prop.is_none() && !exec_args.gui && !exec_args.inherent {
panic!("Expected either a property or inherent verification");
}
let result = if exec_args.gui {
ExecResult {
result: Err(start_gui(system, prop, strategy)),
stats: ExecStats::default(),
}
} else {
info!("Starting verification.");
let result = verify::verify(system, prop, exec_args.assume_inherent, strategy);
if log_enabled!(log::Level::Trace) {
trace!("Verification result: {:?}", result);
}
if log_enabled!(log::Level::Info) {
match result.result {
Ok(_) => info!("Verification ended."),
Err(_) => error!("Verification returned an error."),
}
}
result
};
if !silent {
if batch {
if let Err(err) = serde_json::to_writer(std::io::stdout(), &result) {
panic!("Could not serialize verification result: {:?}", err);
}
} else if !matches!(result.result, Err(ExecError::NoResult))
&& log_enabled!(log::Level::Info)
{
let result_title = match &result.result {
Ok(KnownConclusion::False) => "Result: DOES NOT HOLD",
Ok(KnownConclusion::True) => "Result: HOLDS",
Ok(KnownConclusion::Dependent) => "Result: DEPENDS ON PARAMETERS",
Err(err) => &format!("Result: ERROR ({})", err),
};
let mut stats_cells: Vec<(&str, String)> = [
("Refinements", result.stats.num_refinements),
("Generated states", result.stats.num_generated_states),
("Final states", result.stats.num_final_states),
(
"Generated transitions",
result.stats.num_generated_transitions,
),
("Final transitions", result.stats.num_final_transitions),
]
.into_iter()
.map(|(name, value)| (name, value.to_string()))
.collect();
if let Some(inherent_panic_message) = &result.stats.inherent_panic_message {
stats_cells.push((
"Inherent panic message",
format!("{:?}", inherent_panic_message),
));
}
let inner_table_width = stats_cells
.iter()
.map(|(name, value)| format!("{}: {}", name, value).len())
.max()
.unwrap()
.max(result_title.len());
let result_title = format!(
"| {:^width$} |",
result_title,
width = inner_table_width
);
let table_bar = format!("+{}+", "-".repeat(result_title.len().saturating_sub(2)));
eprintln!("{}\n{}\n{}", table_bar, result_title, table_bar);
for (name, value) in stats_cells {
eprintln!(
"| {}: {:>width$} |",
name,
value,
width = inner_table_width - name.len()
)
}
eprintln!("{}", table_bar);
}
}
result
}
fn start_gui<M: FullMachine>(
system: M,
property: Option<Property>,
strategy: Strategy,
) -> ExecError {
#[cfg(feature = "gui")]
match machine_check_gui::run(system, property, strategy) {
Ok(()) => ExecError::NoResult,
Err(err) => err,
}
#[cfg(not(feature = "gui"))]
{
let _ = (system, property, strategy);
ExecError::GuiError(String::from("The GUI feature was not enabled during build"))
}
}
#[doc(hidden)]
pub mod mck {
pub use mck::*;
}