1#![doc = include_str!("../README.md")]
2
3mod args;
4mod traits;
5mod types;
6mod verify;
7
8use log::error;
9use log::info;
10use log::log_enabled;
11use log::trace;
12use log::warn;
13use machine_check_common::check::KnownConclusion;
14use machine_check_common::iir::description::IMachine;
15use machine_check_common::iir::property::IProperty;
16use machine_check_exec::Strategy;
17
18use args::ProgramArgs;
19pub use args::{ExecArgs, ExecStrategy};
20pub use traits::Ext;
21pub use types::{Bitvector, BitvectorArray, Signed, Unsigned};
22
23pub use ::mck::concr::Machine;
29
30use ::mck::concr::FullMachine;
31
32pub use ::machine_check_macros::bitmask_switch;
70
71pub use ::machine_check_macros::machine_description;
94
95pub fn run<M: FullMachine>(system: M) -> ExecResult {
103 let parsed_args = <ExecArgs as clap::Parser>::parse_from(std::env::args());
104 execute(system, parsed_args)
105}
106
107pub fn parse_args<A: clap::Args>(args: impl Iterator<Item = String>) -> (ExecArgs, A) {
112 let parsed_args = <ProgramArgs<A> as clap::Parser>::parse_from(args);
113 (parsed_args.run_args, parsed_args.system_args)
114}
115
116pub use ::machine_check_common::ExecError;
117pub use ::machine_check_common::ExecResult;
118pub use ::machine_check_common::ExecStats;
119
120pub fn execute<M: FullMachine>(system: M, exec_args: ExecArgs) -> ExecResult {
124 let silent = exec_args.silent;
126 let batch = exec_args.batch;
127
128 let mut filter_level_num = exec_args.verbose;
129
130 let mut overriden_log_level = false;
131 if let Ok(Ok(log_level_override)) =
132 std::env::var("MACHINE_CHECK_LOG_LEVEL_OVERRIDE").map(|var| var.parse::<u8>())
133 {
134 filter_level_num = log_level_override;
135 overriden_log_level = true;
136 }
137
138 let mut filter_level = match filter_level_num {
139 0 => log::LevelFilter::Info,
140 1 => log::LevelFilter::Debug,
141 _ => log::LevelFilter::Trace,
142 };
143
144 if silent && !overriden_log_level {
145 filter_level = log::LevelFilter::Off;
146 }
147
148 let _ = env_logger::builder().filter_level(filter_level).try_init();
150
151 if overriden_log_level {
152 warn!(
153 "Overriden log level to {:?} ({})",
154 filter_level, filter_level_num
155 );
156 }
157
158 let strategy = Strategy {
159 naive_inputs: matches!(exec_args.strategy, ExecStrategy::Naive),
160 use_decay: matches!(exec_args.strategy, ExecStrategy::Decay),
161 };
162
163 let abstract_system = <M::Abstr as mck::abstr::Abstr<M>>::from_concrete(system);
164
165 let machine = M::machine();
166 let machine: IMachine = rmp_serde::from_slice(machine).expect(
167 "Machine
168 should be deserialized",
169 );
170
171 let result = 'result: {
172 let prop = if let Some(property_str) = exec_args.property {
174 match machine_check_machine::process_property::<M>(&machine, &property_str) {
175 Ok(ok) => Some(ok),
176 Err(err) => {
177 break 'result ExecResult {
178 result: Err(ExecError::PropertyNotConstructible(
179 property_str,
180 err.to_string(),
181 )),
182 stats: ExecStats::default(),
183 };
184 }
185 }
186 } else {
187 None
189 };
190 if prop.is_none() && !exec_args.gui && !exec_args.inherent {
191 panic!("Expected either a property or inherent verification");
192 }
193
194 if exec_args.gui {
195 ExecResult {
197 result: Err(start_gui::<M>(abstract_system, machine, prop, strategy)),
198 stats: ExecStats::default(),
199 }
200 } else {
201 info!("Starting verification.");
202
203 let result = verify::verify::<M>(
204 abstract_system,
205 machine,
206 prop,
207 exec_args.assume_inherent,
208 strategy,
209 );
210
211 if log_enabled!(log::Level::Trace) {
212 trace!("Verification result: {:?}", result);
213 }
214
215 if log_enabled!(log::Level::Info) {
216 match result.result {
218 Ok(_) => info!("Verification ended."),
219 Err(_) => error!("Verification returned an error."),
220 }
221 }
222 result
223 }
224 };
225
226 if !silent {
227 if batch {
228 if let Err(err) = serde_json::to_writer(std::io::stdout(), &result) {
230 panic!("Could not serialize verification result: {:?}", err);
231 }
232 } else if !matches!(result.result, Err(ExecError::NoResult))
233 && log_enabled!(log::Level::Info)
234 {
235 let result_title = match &result.result {
237 Ok(KnownConclusion::False) => "Result: DOES NOT HOLD",
238 Ok(KnownConclusion::True) => "Result: HOLDS",
239 Ok(KnownConclusion::Dependent) => "Result: DEPENDS ON PARAMETERS",
240 Err(err) => &format!("Result: ERROR ({:?})", err.to_string()),
241 };
242
243 let mut stats_cells: Vec<(&str, String)> = [
244 ("Refinements", result.stats.num_refinements),
245 ("Generated states", result.stats.num_generated_states),
246 ("Final states", result.stats.num_final_states),
247 (
248 "Generated transitions",
249 result.stats.num_generated_transitions,
250 ),
251 ("Final transitions", result.stats.num_final_transitions),
252 ]
253 .into_iter()
254 .map(|(name, value)| (name, value.to_string()))
255 .collect();
256
257 if let Some(inherent_panic_message) = &result.stats.inherent_panic_message {
258 stats_cells.push((
259 "Inherent panic message",
260 format!("{:?}", inherent_panic_message),
261 ));
262 }
263
264 let inner_table_width = stats_cells
265 .iter()
266 .map(|(name, value)| format!("{}: {}", name, value).len())
267 .max()
268 .unwrap()
269 .max(result_title.len());
270
271 let result_title = format!(
272 "| {:^width$} |",
273 result_title,
274 width = inner_table_width
275 );
276 let table_bar = format!("+{}+", "-".repeat(result_title.len().saturating_sub(2)));
277
278 eprintln!("{}\n{}\n{}", table_bar, result_title, table_bar);
280 for (name, value) in stats_cells {
281 eprintln!(
282 "| {}: {:>width$} |",
283 name,
284 value,
285 width = inner_table_width - name.len()
286 )
287 }
288 eprintln!("{}", table_bar);
289 }
290 }
291 result
292}
293
294fn start_gui<M: FullMachine>(
295 abstract_system: M::Abstr,
296 machine: IMachine,
297 property: Option<IProperty>,
298 strategy: Strategy,
299) -> ExecError {
300 #[cfg(feature = "gui")]
302 match machine_check_gui::run::<M>(abstract_system, machine, property, strategy) {
303 Ok(()) => ExecError::NoResult,
304 Err(err) => err,
305 }
306 #[cfg(not(feature = "gui"))]
307 {
308 let _ = (abstract_system, machine, property, strategy);
310 ExecError::GuiError(String::from("The GUI feature was not enabled during build"))
311 }
312}
313
314#[doc(hidden)]
315pub mod mck {
316 pub use mck::*;
317}