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