From 3c8324d79b64d082084c5033a8a6aa33161cf146 Mon Sep 17 00:00:00 2001 From: ConnorJKY Date: Wed, 10 Sep 2025 18:01:30 -0700 Subject: [PATCH 01/54] add new json struture --- Cargo.lock | 2 +- kani-driver/src/args/mod.rs | 3 ++ kani-driver/src/call_cbmc.rs | 2 ++ kani-driver/src/cbmc_property_renderer.rs | 2 ++ kani-driver/src/harness_runner.rs | 1 + kani-driver/src/json_handler.rs | 35 +++++++++++++++++++++++ kani-driver/src/main.rs | 27 +++++++++++++++-- library/kani/Cargo.toml | 2 +- 8 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 kani-driver/src/json_handler.rs diff --git a/Cargo.lock b/Cargo.lock index 0b5780e50576..4615c9dc849b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1092,7 +1092,7 @@ dependencies = [ [[package]] name = "kani" -version = "0.65.0" +version = "0.65.1" dependencies = [ "kani_core", "kani_macros", diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index 2481d2477554..8fec5c8f295d 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -242,6 +242,9 @@ pub struct VerificationArgs { #[arg(long)] pub default_unwind: Option, + #[arg(long = "export-json")] + pub export_json: Option, + /// When specified, the harness filter will only match the exact fully qualified name of a harness #[arg(long, requires("harnesses"))] pub exact: bool, diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 27f2f3fed725..cbc5fffa51b3 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -163,7 +163,9 @@ impl KaniSession { // The timeout wasn't reached let output = res.unwrap()?; VerificationResult::from(output, harness.attributes.should_panic, start_time) + }; + // println!("{verification_results:?}"); Ok(verification_results) } diff --git a/kani-driver/src/cbmc_property_renderer.rs b/kani-driver/src/cbmc_property_renderer.rs index f3e6791cde8f..92d5037ae415 100644 --- a/kani-driver/src/cbmc_property_renderer.rs +++ b/kani-driver/src/cbmc_property_renderer.rs @@ -183,7 +183,9 @@ pub fn kani_cbmc_output_filter( if !quiet { let formatted_item = format_item(&processed_item, output_format); if let Some(fmt_item) = formatted_item { + // println!("!"); println!("{fmt_item}"); + // println!("!"); } } // TODO: Record processed items and dump them into a JSON file diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index 708a4064cd72..e7915190d071 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -205,6 +205,7 @@ impl KaniSession { } let mut result = self.with_timer(|| self.run_cbmc(binary, harness), "run_cbmc")?; + // println!("{result:?}"); self.process_output(&result, harness, thread_index); self.gen_and_add_concrete_playback(harness, &mut result)?; diff --git a/kani-driver/src/json_handler.rs b/kani-driver/src/json_handler.rs new file mode 100644 index 000000000000..71a7baac016c --- /dev/null +++ b/kani-driver/src/json_handler.rs @@ -0,0 +1,35 @@ +use serde_json::{json, Value}; +use std::path::PathBuf; + +pub struct JsonHandler { + pub(crate) data: Value, + export_path: Option, +} + +impl JsonHandler { + pub fn new(export_path: Option) -> Self { + Self { + data: json!({}), + export_path, + } + } + + pub fn add_item(&mut self, key: &str, value: Value) { + self.data[key] = value; + } + + pub fn add_harness_detail(&mut self, key: &str, value: Value) { + if self.data[key].is_null() { + self.data[key] = json!([]); + } + self.data[key].as_array_mut().unwrap().push(value); + } + + pub fn export(&self) -> Result<(), std::io::Error> { + if let Some(path) = &self.export_path { + std::fs::write(path, serde_json::to_string_pretty(&self.data)?) + } else { + Ok(()) + } + } +} diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 21b74e8672c7..41bc9583c5af 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -5,9 +5,9 @@ use std::process::ExitCode; use anyhow::Result; use autoharness::{autoharness_cargo, autoharness_standalone}; -use time::{OffsetDateTime, format_description}; +use time::{format_description, OffsetDateTime}; -use args::{CargoKaniSubcommand, check_is_valid}; +use args::{check_is_valid, CargoKaniSubcommand}; use args_toml::join_args; use crate::args::StandaloneSubcommand; @@ -17,7 +17,9 @@ use crate::project::Project; use crate::session::KaniSession; use crate::version::print_kani_version; use clap::Parser; +use serde_json::json; use tracing::debug; +use crate::json_handler::JsonHandler; mod args; mod args_toml; @@ -36,9 +38,11 @@ mod harness_runner; mod list; mod metadata; mod project; + mod session; mod util; mod version; +mod json_handler; /// The main function for the `kani-driver`. /// The driver can be invoked via `cargo kani` and `kani` commands, which determines what kind of @@ -128,9 +132,18 @@ fn standalone_main() -> Result<()> { /// Run verification on the given project. fn verify_project(project: Project, session: KaniSession) -> Result<()> { debug!(?project, "verify_project"); + let mut handler = JsonHandler::new(session.args.export_json.clone()); let harnesses = session.determine_targets(project.get_all_harnesses())?; + for h in harnesses.clone() { + handler.add_harness_detail("harnesses", json!({ + "name": h.pretty_name, + "kind": format!("{:?}", h.attributes.kind), + "should_panic": h.attributes.should_panic + })); + } debug!(n = harnesses.len(), ?harnesses, "verify_project"); + // Verification let runner = harness_runner::HarnessRunner { sess: &session, project: &project }; let results = runner.check_all_harnesses(&harnesses)?; @@ -150,8 +163,16 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { session.save_coverage_metadata(&project, ×tamp)?; session.save_coverage_results(&project, &results, ×tamp)?; + handler.add_item("coverage", json!({"enabled": true})); } - + println!("!!!"); + println!( + "{}", + serde_json::to_string_pretty(&handler.data).unwrap() + ); + println!("!!!"); + + handler.export()?; session.print_final_summary(&results) } diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index 580a2b1f0241..221d6a487bdf 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.65.0" +version = "0.65.1" edition = "2024" license = "MIT OR Apache-2.0" publish = false From 87efbe24d8dd9df94c4e23959c9776de5afbebf6 Mon Sep 17 00:00:00 2001 From: ConnorJKY Date: Thu, 11 Sep 2025 14:35:36 -0700 Subject: [PATCH 02/54] add detailed info for harnesses in json handler --- kani-driver/src/main.rs | 32 +++++++++++++++++++++++++++++--- tests/perf/s2n-quic | 2 +- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 41bc9583c5af..b985e44adeec 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -133,12 +133,35 @@ fn standalone_main() -> Result<()> { fn verify_project(project: Project, session: KaniSession) -> Result<()> { debug!(?project, "verify_project"); let mut handler = JsonHandler::new(session.args.export_json.clone()); + /// TODO: add session info let harnesses = session.determine_targets(project.get_all_harnesses())?; for h in harnesses.clone() { handler.add_harness_detail("harnesses", json!({ - "name": h.pretty_name, - "kind": format!("{:?}", h.attributes.kind), - "should_panic": h.attributes.should_panic + /// basic name for harnesses + "pretty_name": h.pretty_name, + "mangled_name": h.mangled_name, + "crate_name": h.crate_name, + + ///original location of the harnesses + "original": { + "file": h.original_file, + "start_line": h.original_start_line, + "end_line": h.original_end_line + }, + + /// GOTO file generated + "goto": h.goto_file.as_ref().map(|p| p.to_string_lossy().to_string()), + + /// attributes + "kind": format!("{:?}", h.attributes.kind), + "should_panic": h.attributes.should_panic, + "has_loop_contracts": h.has_loop_contracts, + "is_automatically_generated": h.is_automatically_generated, + "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)), + "unwind_value": h.attributes.unwind_value, // Option + "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), + "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), + "verified_stubs": h.attributes.verified_stubs })); } debug!(n = harnesses.len(), ?harnesses, "verify_project"); @@ -165,6 +188,9 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { session.save_coverage_results(&project, &results, ×tamp)?; handler.add_item("coverage", json!({"enabled": true})); } + else{ + handler.add_item("coverage", json!({"enabled": false})); + } println!("!!!"); println!( "{}", diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 26e2402eea1e..d2c07946d50f 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 26e2402eea1ea8c079705a58bbf5327f358521f0 +Subproject commit d2c07946d50f32a7f005a27a31372aa399af0807 From 4c21c6591ad62fc7b84251c50befbca92efe7efa Mon Sep 17 00:00:00 2001 From: edison Date: Tue, 16 Sep 2025 14:19:13 -0700 Subject: [PATCH 03/54] feat: finish phase2 prototype on harness_runner --- kani-driver/src/harness_runner.rs | 29 +++++++++++++++++++++++++- kani-driver/src/main.rs | 34 +++++++++++++------------------ 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index e7915190d071..c3bb9bd228fa 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -10,6 +10,7 @@ use std::path::Path; use crate::args::{NumThreads, OutputFormat}; use crate::call_cbmc::{VerificationResult, VerificationStatus}; +use crate::json_handler::JsonHandler; use crate::project::Project; use crate::session::{BUG_REPORT_URL, KaniSession}; @@ -54,6 +55,7 @@ impl<'pr> HarnessRunner<'_, 'pr> { pub(crate) fn check_all_harnesses( &self, harnesses: &'pr [&HarnessMetadata], + mut json_handler: Option<&mut JsonHandler>, ) -> Result>> { let sorted_harnesses = crate::metadata::sort_harnesses_by_loc(harnesses); let pool = { @@ -98,7 +100,32 @@ impl<'pr> HarnessRunner<'_, 'pr> { .collect::>>() }); match results { - Ok(results) => Ok(results), + Ok(results) => { + if let Some(handler) = json_handler.as_deref_mut() { + use serde_json::json; + let details: Vec<_> = results + .iter() + .map(|r| { + json!({ + "name": r.harness.pretty_name, + "status": match r.result.status { + VerificationStatus::Success => "Success", + VerificationStatus::Failure => "Failure", + }, + }) + }) + .collect(); + handler.add_item( + "verification_runner_results", + json!({ + "total": results.len(), + "status": "completed", + "individual_harnesses": details, + }), + ); + } + Ok(results) + } Err(err) => { if err.is::() { let failed = err.downcast::().unwrap(); diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index b985e44adeec..70c58dbbc5f9 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -5,13 +5,14 @@ use std::process::ExitCode; use anyhow::Result; use autoharness::{autoharness_cargo, autoharness_standalone}; -use time::{format_description, OffsetDateTime}; +use time::{OffsetDateTime, format_description}; -use args::{check_is_valid, CargoKaniSubcommand}; +use args::{CargoKaniSubcommand, check_is_valid}; use args_toml::join_args; use crate::args::StandaloneSubcommand; use crate::concrete_playback::playback::{playback_cargo, playback_standalone}; +use crate::json_handler::JsonHandler; use crate::list::collect_metadata::{list_cargo, list_standalone}; use crate::project::Project; use crate::session::KaniSession; @@ -19,7 +20,6 @@ use crate::version::print_kani_version; use clap::Parser; use serde_json::json; use tracing::debug; -use crate::json_handler::JsonHandler; mod args; mod args_toml; @@ -39,10 +39,10 @@ mod list; mod metadata; mod project; +mod json_handler; mod session; mod util; mod version; -mod json_handler; /// The main function for the `kani-driver`. /// The driver can be invoked via `cargo kani` and `kani` commands, which determines what kind of @@ -133,26 +133,28 @@ fn standalone_main() -> Result<()> { fn verify_project(project: Project, session: KaniSession) -> Result<()> { debug!(?project, "verify_project"); let mut handler = JsonHandler::new(session.args.export_json.clone()); - /// TODO: add session info + // TODO: add session info let harnesses = session.determine_targets(project.get_all_harnesses())?; + debug!(n = harnesses.len(), ?harnesses, "verify_project"); + for h in harnesses.clone() { handler.add_harness_detail("harnesses", json!({ - /// basic name for harnesses + // basic name for harnesses "pretty_name": h.pretty_name, "mangled_name": h.mangled_name, "crate_name": h.crate_name, - ///original location of the harnesses + // original location of the harnesses "original": { "file": h.original_file, "start_line": h.original_start_line, "end_line": h.original_end_line }, - /// GOTO file generated + // GOTO file generated "goto": h.goto_file.as_ref().map(|p| p.to_string_lossy().to_string()), - /// attributes + // attributes "kind": format!("{:?}", h.attributes.kind), "should_panic": h.attributes.should_panic, "has_loop_contracts": h.has_loop_contracts, @@ -164,12 +166,10 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { "verified_stubs": h.attributes.verified_stubs })); } - debug!(n = harnesses.len(), ?harnesses, "verify_project"); - // Verification let runner = harness_runner::HarnessRunner { sess: &session, project: &project }; - let results = runner.check_all_harnesses(&harnesses)?; + let results = runner.check_all_harnesses(&harnesses, Some(&mut handler))?; if session.args.coverage { // We generate a timestamp to save the coverage data in a folder named @@ -186,17 +186,11 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { session.save_coverage_metadata(&project, ×tamp)?; session.save_coverage_results(&project, &results, ×tamp)?; + handler.add_item("coverage", json!({"enabled": true})); - } - else{ + } else { handler.add_item("coverage", json!({"enabled": false})); } - println!("!!!"); - println!( - "{}", - serde_json::to_string_pretty(&handler.data).unwrap() - ); - println!("!!!"); handler.export()?; session.print_final_summary(&results) From 2351326945b5823b37118c6ff170364da422ecb0 Mon Sep 17 00:00:00 2001 From: edison Date: Tue, 16 Sep 2025 23:33:07 -0700 Subject: [PATCH 04/54] feat: add harness that fail fast to json handler and refactor logic to add runner results --- kani-driver/src/harness_runner.rs | 69 ++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index c3bb9bd228fa..06ee37b2caa6 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -50,6 +50,38 @@ impl std::fmt::Display for FailFastHarnessInfo { } impl<'pr> HarnessRunner<'_, 'pr> { + /// Helper: push verification summary into JsonHandler. + fn add_runner_results_json( + handler: &mut JsonHandler, + results: &[HarnessResult<'pr>], + selected: usize, + status_label: &str, + ) { + use serde_json::json; + let details: Vec<_> = results + .iter() + .map(|r| { + json!({ + "name": r.harness.pretty_name, + "status": match r.result.status { + VerificationStatus::Success => "Success", + VerificationStatus::Failure => "Failure", + }, + }) + }) + .collect(); + + handler.add_item( + "verification_runner_results", + json!({ + "selected": selected, + "executed": results.len(), + "status": status_label, + "individual_harnesses": details, + }), + ); + } + /// Given a [`HarnessRunner`] (to abstract over how these harnesses were generated), this runs /// the proof-checking process for each harness in `harnesses`. pub(crate) fn check_all_harnesses( @@ -102,37 +134,28 @@ impl<'pr> HarnessRunner<'_, 'pr> { match results { Ok(results) => { if let Some(handler) = json_handler.as_deref_mut() { - use serde_json::json; - let details: Vec<_> = results - .iter() - .map(|r| { - json!({ - "name": r.harness.pretty_name, - "status": match r.result.status { - VerificationStatus::Success => "Success", - VerificationStatus::Failure => "Failure", - }, - }) - }) - .collect(); - handler.add_item( - "verification_runner_results", - json!({ - "total": results.len(), - "status": "completed", - "individual_harnesses": details, - }), - ); + Self::add_runner_results_json(handler, &results, harnesses.len(), "completed"); } Ok(results) } Err(err) => { if err.is::() { let failed = err.downcast::().unwrap(); - Ok(vec![HarnessResult { + let result = vec![HarnessResult { harness: sorted_harnesses[failed.index_to_failing_harness], result: failed.result, - }]) + }]; + + if let Some(handler) = json_handler.as_deref_mut() { + Self::add_runner_results_json( + handler, + &result, + harnesses.len(), + "completed_with_fail_fast", + ); + } + + Ok(result) } else { Err(err) } From e7fa7135fc7008eb9c5e0e4af3ddcc83af277985 Mon Sep 17 00:00:00 2001 From: jingfei-xu Date: Wed, 17 Sep 2025 14:16:17 -0700 Subject: [PATCH 05/54] Phase 3: Summary and Timing --- .../src/tutorial/first-steps-v1/kani_run.json | 45 +++++++++++++++++++ kani-driver/src/main.rs | 22 ++++++--- 2 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 docs/src/tutorial/first-steps-v1/kani_run.json diff --git a/docs/src/tutorial/first-steps-v1/kani_run.json b/docs/src/tutorial/first-steps-v1/kani_run.json new file mode 100644 index 000000000000..11a94e95e604 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/kani_run.json @@ -0,0 +1,45 @@ +{ + "verification_runner_results": { + "selected": 1, + "executed": 1, + "status": "completed", + "individual_harnesses": [ + { + "name": "check_estimate_size", + "status": "Failure" + } + ] + }, + "harnesses": [ + { + "pretty_name": "check_estimate_size", + "mangled_name": "_RNvCscOSYLgomVfM_3lib19check_estimate_size", + "crate_name": "lib", + "original": { + "file": "./docs/src/tutorial/first-steps-v1/src/lib.rs", + "start_line": 52, + "end_line": 55 + }, + "goto": "/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/src/lib__RNvCscOSYLgomVfM_3lib19check_estimate_size.symtab.out", + "kind": "Proof", + "should_panic": false, + "has_loop_contracts": false, + "is_automatically_generated": false, + "solver": null, + "unwind_value": null, + "contract": null, + "stubs": [], + "verified_stubs": [], + "summary": { + "total": 1, + "status": "failed" + }, + "timing": { + "cbmc_runtime": "0.096s" + } + } + ], + "coverage": { + "enabled": false + } +} \ No newline at end of file diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 70c58dbbc5f9..660e907fd28f 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -137,7 +137,12 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { let harnesses = session.determine_targets(project.get_all_harnesses())?; debug!(n = harnesses.len(), ?harnesses, "verify_project"); + // Verification + let runner = harness_runner::HarnessRunner { sess: &session, project: &project }; + let results = runner.check_all_harnesses(&harnesses, Some(&mut handler))?; + for h in harnesses.clone() { + let harness_result = results.iter().find(|r| r.harness.pretty_name == h.pretty_name); handler.add_harness_detail("harnesses", json!({ // basic name for harnesses "pretty_name": h.pretty_name, @@ -163,14 +168,21 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { "unwind_value": h.attributes.unwind_value, // Option "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), - "verified_stubs": h.attributes.verified_stubs + "verified_stubs": h.attributes.verified_stubs, + + "summary": harness_result.map_or(json!(null), |result| json!({ + "total": 1, + "status": match result.result.status { + crate::call_cbmc::VerificationStatus::Success => "completed", + crate::call_cbmc::VerificationStatus::Failure => "failed", + } + })), + "timing": harness_result.map_or(json!(null), |result| json!({ + "cbmc_runtime": format!("{:.3}s", result.result.runtime.as_secs_f64()) + })) })); } - // Verification - let runner = harness_runner::HarnessRunner { sess: &session, project: &project }; - let results = runner.check_all_harnesses(&harnesses, Some(&mut handler))?; - if session.args.coverage { // We generate a timestamp to save the coverage data in a folder named // `kanicov_` where `` is the current date based on `format` From edc555078f96a7c28b87116d8cb0e8de2f0fa969 Mon Sep 17 00:00:00 2001 From: jingfei-xu Date: Thu, 18 Sep 2025 14:03:50 -0700 Subject: [PATCH 06/54] Error Details + Property Details --- .../src/tutorial/first-steps-v1/kani_run.json | 18 ++- kani-driver/src/main.rs | 121 ++++++++++++------ 2 files changed, 101 insertions(+), 38 deletions(-) diff --git a/docs/src/tutorial/first-steps-v1/kani_run.json b/docs/src/tutorial/first-steps-v1/kani_run.json index 11a94e95e604..8f60db60031f 100644 --- a/docs/src/tutorial/first-steps-v1/kani_run.json +++ b/docs/src/tutorial/first-steps-v1/kani_run.json @@ -35,7 +35,23 @@ "status": "failed" }, "timing": { - "cbmc_runtime": "0.096s" + "cbmc_runtime": "0.025s" + } + } + ], + "error_details": { + "has_errors": true, + "error_type": "assertion_failure", + "failed_properties_type": "PanicsOnly", + "exit_status": "properties_failed" + }, + "property_details": [ + { + "property_details": { + "total_properties": 1, + "passed": 0, + "failed": 1, + "unreachable": 0 } } ], diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 660e907fd28f..139d58cdac6c 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -143,44 +143,91 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { for h in harnesses.clone() { let harness_result = results.iter().find(|r| r.harness.pretty_name == h.pretty_name); - handler.add_harness_detail("harnesses", json!({ - // basic name for harnesses - "pretty_name": h.pretty_name, - "mangled_name": h.mangled_name, - "crate_name": h.crate_name, - - // original location of the harnesses - "original": { - "file": h.original_file, - "start_line": h.original_start_line, - "end_line": h.original_end_line - }, - // GOTO file generated - "goto": h.goto_file.as_ref().map(|p| p.to_string_lossy().to_string()), - - // attributes - "kind": format!("{:?}", h.attributes.kind), - "should_panic": h.attributes.should_panic, - "has_loop_contracts": h.has_loop_contracts, - "is_automatically_generated": h.is_automatically_generated, - "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)), - "unwind_value": h.attributes.unwind_value, // Option - "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), - "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), - "verified_stubs": h.attributes.verified_stubs, - - "summary": harness_result.map_or(json!(null), |result| json!({ - "total": 1, - "status": match result.result.status { - crate::call_cbmc::VerificationStatus::Success => "completed", - crate::call_cbmc::VerificationStatus::Failure => "failed", - } - })), - "timing": harness_result.map_or(json!(null), |result| json!({ - "cbmc_runtime": format!("{:.3}s", result.result.runtime.as_secs_f64()) - })) - })); +handler.add_harness_detail("harnesses", json!({ + // basic name for harnesses + "pretty_name": h.pretty_name, + "mangled_name": h.mangled_name, + "crate_name": h.crate_name, + + // original location of the harnesses + "original": { + "file": h.original_file, + "start_line": h.original_start_line, + "end_line": h.original_end_line + }, + + // GOTO file generated + "goto": h.goto_file.as_ref().map(|p| p.to_string_lossy().to_string()), + + // attributes + "kind": format!("{:?}", h.attributes.kind), + "should_panic": h.attributes.should_panic, + "has_loop_contracts": h.has_loop_contracts, + "is_automatically_generated": h.is_automatically_generated, + "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)), + "unwind_value": h.attributes.unwind_value, // Option + "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), + "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), + "verified_stubs": h.attributes.verified_stubs, + + "summary": harness_result.map_or(json!(null), |result| json!({ + "total": 1, + "status": match result.result.status { + crate::call_cbmc::VerificationStatus::Success => "completed", + crate::call_cbmc::VerificationStatus::Failure => "failed", + } + })), + "timing": harness_result.map_or(json!(null), |result| json!({ + "cbmc_runtime": format!("{:.3}s", result.result.runtime.as_secs_f64()) + })) +})); +handler.add_item("error_details", harness_result.map_or(json!(null), |result| { + match result.result.status { + crate::call_cbmc::VerificationStatus::Failure => { + json!({ + "has_errors": true, + "error_type": match result.result.failed_properties { + crate::call_cbmc::FailedProperties::None => "unknown_failure", + crate::call_cbmc::FailedProperties::PanicsOnly => "assertion_failure", + crate::call_cbmc::FailedProperties::Other => "verification_failure", + }, + "failed_properties_type": format!("{:?}", result.result.failed_properties), + "exit_status": match &result.result.results { + Err(crate::call_cbmc::ExitStatus::Timeout) => "timeout".to_string(), + Err(crate::call_cbmc::ExitStatus::OutOfMemory) => "out_of_memory".to_string(), + Err(crate::call_cbmc::ExitStatus::Other(code)) => format!("exit_code_{}", code), + Ok(_) => "properties_failed".to_string() + } + }) + }, + crate::call_cbmc::VerificationStatus::Success => json!({ + "has_errors": false + }) + } +})); +handler.add_harness_detail("property_details", json!({ + "property_details": harness_result.map_or(json!(null), |result| { + match &result.result.results { + Ok(properties) => { + let total_properties = properties.len(); + let passed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Success)).count(); + let failed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Failure)).count(); + + json!({ + "total_properties": total_properties, + "passed": passed_properties, + "failed": failed_properties, + "unreachable": total_properties - passed_properties - failed_properties + }) + }, + Err(_) => json!({ + "total_properties": 0, + "error": "Could not extract property details due to verification failure" + }) + } + }) +})); } if session.args.coverage { From 7b08e18288b2774f4bfc239d79aa93087d11bf70 Mon Sep 17 00:00:00 2001 From: ConnorJKY Date: Thu, 18 Sep 2025 14:04:42 -0700 Subject: [PATCH 07/54] Move harness info to match the result; add version number and timeline --- kani-driver/src/main.rs | 62 +++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 660e907fd28f..9b0ca60d281a 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use std::ffi::OsString; use std::process::ExitCode; +use std::time::{Instant, SystemTime}; +use time::format_description::well_known::Rfc3339; use anyhow::Result; use autoharness::{autoharness_cargo, autoharness_standalone}; @@ -131,33 +133,50 @@ fn standalone_main() -> Result<()> { /// Run verification on the given project. fn verify_project(project: Project, session: KaniSession) -> Result<()> { + let wall_start = SystemTime::now(); + let cpu_start = Instant::now(); + fn to_rfc3339(t: std::time::SystemTime) -> String { + let dt: OffsetDateTime = t.into(); + dt.format(&Rfc3339).unwrap() + } + debug!(?project, "verify_project"); let mut handler = JsonHandler::new(session.args.export_json.clone()); // TODO: add session info let harnesses = session.determine_targets(project.get_all_harnesses())?; debug!(n = harnesses.len(), ?harnesses, "verify_project"); - + handler.add_item("schema_version", json!("0.0.0")); + handler.add_item( + "run_time", + json!({ + "started_at": to_rfc3339(wall_start) + }), + ); // Verification let runner = harness_runner::HarnessRunner { sess: &session, project: &project }; + let results = runner.check_all_harnesses(&harnesses, Some(&mut handler))?; for h in harnesses.clone() { let harness_result = results.iter().find(|r| r.harness.pretty_name == h.pretty_name); - handler.add_harness_detail("harnesses", json!({ - // basic name for harnesses - "pretty_name": h.pretty_name, - "mangled_name": h.mangled_name, - "crate_name": h.crate_name, - - // original location of the harnesses + let arr = handler.data["verification_runner_results"]["individual_harnesses"] + .as_array_mut() + .expect("individual_harnesses must be an array"); + // locate matching entry by name and overwrite it + let entry = arr.iter_mut().find(|v| { + v.get("name").and_then(|s| s.as_str()) == Some(h.pretty_name.as_str()) + }).expect("matching individual_harness not found"); + + *entry = json!({ + // basic names + "name": h.pretty_name, // keep name in the record + //original source location "original": { - "file": h.original_file, - "start_line": h.original_start_line, - "end_line": h.original_end_line + "file": h.original_file, + "start_line": h.original_start_line, + "end_line": h.original_end_line }, - // GOTO file generated - "goto": h.goto_file.as_ref().map(|p| p.to_string_lossy().to_string()), // attributes "kind": format!("{:?}", h.attributes.kind), @@ -165,7 +184,7 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { "has_loop_contracts": h.has_loop_contracts, "is_automatically_generated": h.is_automatically_generated, "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)), - "unwind_value": h.attributes.unwind_value, // Option + "unwind_value": h.attributes.unwind_value, "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), "verified_stubs": h.attributes.verified_stubs, @@ -180,7 +199,8 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { "timing": harness_result.map_or(json!(null), |result| json!({ "cbmc_runtime": format!("{:.3}s", result.result.runtime.as_secs_f64()) })) - })); + }); + } if session.args.coverage { @@ -204,7 +224,19 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { handler.add_item("coverage", json!({"enabled": false})); } + let wall_end = SystemTime::now(); + let duration_ms = cpu_start.elapsed().as_millis() as u64; + handler.add_item( + "run_time", + json!({ + "started_at": to_rfc3339(wall_start), + "finished_at": to_rfc3339(wall_end), + "duration_ms": duration_ms, + + }), + ); handler.export()?; + session.print_final_summary(&results) } From cacdb7bda173ab46cdda03ee3fd9f17b75b9f619 Mon Sep 17 00:00:00 2001 From: ConnorJKY Date: Thu, 18 Sep 2025 14:15:49 -0700 Subject: [PATCH 08/54] Move harness info to match the result; add version number and timeline --- .../first-steps-v1/target/.rustc_info.json | 1 + .../target/kani/.rustc_info.json | 1 + .../first-steps-v1/target/kani/CACHEDIR.TAG | 3 + .../kani/aarch64-apple-darwin/CACHEDIR.TAG | 3 + .../aarch64-apple-darwin/debug/.cargo-lock | 0 .../dep-lib-first_steps_v1 | Bin 0 -> 30 bytes .../invoked.timestamp | 1 + .../lib-first_steps_v1 | 1 + .../lib-first_steps_v1.json | 1 + .../deps/first_steps_v1-207a230fa6911716.d | 7 ++ ...eps_v1-207a230fa6911716.kani-metadata.json | 1 + ..._14first_steps_v119check_estimate_size.out | Bin 0 -> 30908 bytes ...19check_estimate_size.pretty_name_map.json | 1 + ...t_steps_v119check_estimate_size.symtab.out | Bin 0 -> 15415 bytes ...teps_v119check_estimate_size.type_map.json | 1 + .../dep-graph.bin | Bin 0 -> 132238 bytes .../query-cache.bin | Bin 0 -> 15828 bytes .../work-products.bin | Bin 0 -> 108 bytes .../s-hb0z3arwfc-0qh96dx.lock | 0 .../debug/libfirst_steps_v1.d | 1 + .../target/kani/debug/.cargo-lock | 0 kani-driver/src/main.rs | 64 ++++++++++++----- kani_beta1.json | 47 +++++++++++++ kani_run.json | 66 ++++++++++++++++++ lib.rs | 56 +++++++++++++++ 25 files changed, 239 insertions(+), 16 deletions(-) create mode 100644 docs/src/tutorial/first-steps-v1/target/.rustc_info.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/.rustc_info.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/CACHEDIR.TAG create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/CACHEDIR.TAG create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.cargo-lock create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/dep-lib-first_steps_v1 create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/invoked.timestamp create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1 create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.d create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.kani-metadata.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.out create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.pretty_name_map.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.symtab.out create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.type_map.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx-748zp5z6lty6dthhcfyiqdmwh/dep-graph.bin create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx-748zp5z6lty6dthhcfyiqdmwh/query-cache.bin create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx-748zp5z6lty6dthhcfyiqdmwh/work-products.bin create mode 100755 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx.lock create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/libfirst_steps_v1.d create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/debug/.cargo-lock create mode 100644 kani_beta1.json create mode 100644 kani_run.json create mode 100644 lib.rs diff --git a/docs/src/tutorial/first-steps-v1/target/.rustc_info.json b/docs/src/tutorial/first-steps-v1/target/.rustc_info.json new file mode 100644 index 000000000000..80931a163798 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/.rustc_info.json @@ -0,0 +1 @@ +{"rustc_fingerprint":16292269434704313360,"outputs":{"6322768375820205291":{"success":true,"status":"","code":0,"stdout":"rustc 1.91.0-nightly (ec7c02612 2025-08-05)\nbinary: rustc\ncommit-hash: ec7c02612527d185c379900b613311bc1dcbf7dc\ncommit-date: 2025-08-05\nhost: aarch64-apple-darwin\nrelease: 1.91.0-nightly\nLLVM version: 20.1.8\n","stderr":""},"11332290986239422126":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/jiangkeyou/.rustup/toolchains/nightly-2025-08-06-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"flagm2\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"lse2\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_feature=\"v8.4a\"\ntarget_feature=\"vh\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_has_reliable_f128\ntarget_has_reliable_f16\ntarget_has_reliable_f16_math\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nub_checks\nunix\n","stderr":""},"10408681216163195318":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/jiangkeyou/.rustup/toolchains/nightly-2025-08-06-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"flagm2\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"lse2\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_feature=\"v8.4a\"\ntarget_feature=\"vh\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_has_reliable_f128\ntarget_has_reliable_f16\ntarget_has_reliable_f16_math\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nub_checks\nunix\n","stderr":""},"5465787792652773754":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/jiangkeyou/.rustup/toolchains/nightly-2025-08-06-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"flagm2\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"lse2\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_feature=\"v8.4a\"\ntarget_feature=\"vh\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_has_reliable_f128\ntarget_has_reliable_f16\ntarget_has_reliable_f16_math\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nub_checks\nunix\n","stderr":""}},"successes":{}} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/.rustc_info.json b/docs/src/tutorial/first-steps-v1/target/kani/.rustc_info.json new file mode 100644 index 000000000000..335b1c85021b --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/.rustc_info.json @@ -0,0 +1 @@ +{"rustc_fingerprint":2508058078803885394,"outputs":{"9195354629767851979":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/jiangkeyou/.kani/kani-0.65.0\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\nkani\noverflow_checks\npanic=\"abort\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"neon\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_has_reliable_f128\ntarget_has_reliable_f128_math\ntarget_has_reliable_f16\ntarget_has_reliable_f16_math\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nub_checks\nunix\n","stderr":""},"15864581276431044797":{"success":true,"status":"","code":0,"stdout":"rustc 1.91.0-nightly (ec7c02612 2025-08-05)\nbinary: rustc\ncommit-hash: ec7c02612527d185c379900b613311bc1dcbf7dc\ncommit-date: 2025-08-05\nhost: aarch64-apple-darwin\nrelease: 1.91.0-nightly\nLLVM version: 20.1.8\n","stderr":""},"1584335703394208523":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/jiangkeyou/.rustup/toolchains/nightly-2025-08-06-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\nkani_host\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"flagm2\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"lse2\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_feature=\"v8.4a\"\ntarget_feature=\"vh\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_has_reliable_f128\ntarget_has_reliable_f16\ntarget_has_reliable_f16_math\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nub_checks\nunix\n","stderr":""}},"successes":{}} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/CACHEDIR.TAG b/docs/src/tutorial/first-steps-v1/target/kani/CACHEDIR.TAG new file mode 100644 index 000000000000..20d7c319cda9 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/CACHEDIR.TAG @@ -0,0 +1,3 @@ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by cargo. +# For information about cache directory tags see https://bford.info/cachedir/ diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/CACHEDIR.TAG b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/CACHEDIR.TAG new file mode 100644 index 000000000000..20d7c319cda9 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/CACHEDIR.TAG @@ -0,0 +1,3 @@ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by cargo. +# For information about cache directory tags see https://bford.info/cachedir/ diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.cargo-lock b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.cargo-lock new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/dep-lib-first_steps_v1 b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/dep-lib-first_steps_v1 new file mode 100644 index 0000000000000000000000000000000000000000..024be4904569dbe2c19582a923171096f5c21ee4 GIT binary patch literal 30 gcmZQ%U|{&q$Ot4ExPZ90C|N%zGfA(g7$m{~07A6{&Hw-a literal 0 HcmV?d00001 diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/invoked.timestamp b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/invoked.timestamp new file mode 100644 index 000000000000..e00328da5aa8 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1 b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1 new file mode 100644 index 000000000000..245c877e983f --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1 @@ -0,0 +1 @@ +ff0d0f04bcbf4258 \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1.json b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1.json new file mode 100644 index 000000000000..328d60b9b627 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1.json @@ -0,0 +1 @@ +{"rustc":5663753232442302949,"features":"[]","declared_features":"[]","target":5150295270818399684,"profile":5594203621556287378,"path":10763286916239946207,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/dep-lib-first_steps_v1","checksum":false}}],"rustflags":["-C","overflow-checks=on","-Z","unstable-options","-Z","trim-diagnostic-paths=no","-Z","human_readable_cgu_names","-Z","always-encode-mir","--cfg=kani","-Z","crate-attr=feature(register_tool)","-Z","crate-attr=register_tool(kanitool)","--sysroot","/Users/jiangkeyou/.kani/kani-0.65.0","-L","/Users/jiangkeyou/.kani/kani-0.65.0/lib","--extern","kani","--extern","noprelude:std=/Users/jiangkeyou/.kani/kani-0.65.0/lib/libstd.rlib","-C","panic=abort","-C","symbol-mangling-version=v0","-Z","panic_abort_tests=yes","-Z","mir-enable-passes=-RemoveStorageMarkers","--check-cfg=cfg(kani)","-Clinker=echo","--kani-compiler","-Cllvm-args=--check-version=0.65.0"],"config":2069994364910194474,"compile_kind":551198452465029181} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.d b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.d new file mode 100644 index 000000000000..329febb565a3 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.d @@ -0,0 +1,7 @@ +/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.d: src/lib.rs + +/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/libfirst_steps_v1-207a230fa6911716.rlib: src/lib.rs + +/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/libfirst_steps_v1-207a230fa6911716.rmeta: src/lib.rs + +src/lib.rs: diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.kani-metadata.json b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.kani-metadata.json new file mode 100644 index 000000000000..30d82e6c34e0 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.kani-metadata.json @@ -0,0 +1 @@ +{"crate_name":"first_steps_v1","proof_harnesses":[{"pretty_name":"check_estimate_size","mangled_name":"_RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size","crate_name":"first_steps_v1","original_file":"src/lib.rs","original_start_line":52,"original_end_line":55,"goto_file":"/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.symtab.out","attributes":{"kind":"Proof","should_panic":false,"solver":null,"unwind_value":null,"stubs":[],"verified_stubs":[]},"contract":null,"has_loop_contracts":false,"is_automatically_generated":false}],"unsupported_features":[],"test_harnesses":[],"contracted_functions":[],"autoharness_md":null} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.out b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.out new file mode 100644 index 0000000000000000000000000000000000000000..9be616e4dca7a30362b5f6fa7e81965a287173e8 GIT binary patch literal 30908 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","tag-Unit":"()","_RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size":"check_estimate_size","tag-refstr":"&str","_ZN4kani16rustc_intrinsics10panic_stub17hdbf93c9f79465449E::1::var_1::t":null,"tag-Never":"!","_ZN4kani16rustc_intrinsics10panic_stub17hdbf93c9f79465449E":"kani::rustc_intrinsics::panic_stub","_RINvCsKJoxxf1Snz_4kani3anymECs6BptHtlECcM_14first_steps_v1":"kani::any::","_ZN4kani5panic17h4df909806de3fdeaE::1::var_1::message":null,"_ZN4kani5panic17h4df909806de3fdeaE":"kani::panic","_ZN4kani16any_raw_internal17hc1dff107a546b994E":"kani::any_raw_internal::","_ZN4kani14kani_intrinsic17h2f99e04063309d09E":"kani::kani_intrinsic::","_ZN39_$LT$u32$u20$as$u20$kani..Arbitrary$GT$3any17hf3724928fa34a304E":"::any","_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_1::x":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size":"estimate_size","_ZN4kani7any_raw17h7b173976ae29b24dE::1::var_0":null,"_RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size::1::var_0":null,"_RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size::1::var_1::x":null,"_RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size::1::var_2":null,"VoidUnit":null,"_ZN4kani16rustc_intrinsics10panic_stub17hdbf93c9f79465449E::1::var_0":null,"_RINvCsKJoxxf1Snz_4kani3anymECs6BptHtlECcM_14first_steps_v1::1::var_0":null,"_ZN4kani5panic17h4df909806de3fdeaE::1::var_0":null,"_ZN4kani5panic17h4df909806de3fdeaE::1::var_2":null,"_ZN4kani5panic17h4df909806de3fdeaE::1::var_3":null,"_ZN4kani16any_raw_internal17hc1dff107a546b994E::1::var_0":null,"_ZN4kani14kani_intrinsic17h2f99e04063309d09E::1::var_0":null,"_ZN39_$LT$u32$u20$as$u20$kani..Arbitrary$GT$3any17hf3724928fa34a304E::1::var_0":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_0":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_2":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_3":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_4":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_5":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_6":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_7":null,"tag-first_steps_v1.4ce9f62e88f79162::first_steps_v1::global::0::::struct":"first_steps_v1.4ce9f62e88f79162::first_steps_v1::global::0::::struct","first_steps_v1.4ce9f62e88f79162::first_steps_v1::global::0::":null} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.symtab.out b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.symtab.out new file mode 100644 index 0000000000000000000000000000000000000000..5169efa4ede2ae74c375ac62a61f975bc25e6cdd GIT binary patch literal 15415 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 literal 0 HcmV?d00001 diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx-748zp5z6lty6dthhcfyiqdmwh/work-products.bin b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx-748zp5z6lty6dthhcfyiqdmwh/work-products.bin new file mode 100644 index 0000000000000000000000000000000000000000..f14fa212f219913387580806c919bf2ec5829e9f GIT binary patch literal 108 zcmWFv_H<@oP&L%EG}JTD&C5*BD9Nc*&`3=-Pc|?zGc-~#GB7gLHL%b%Fx6ySdF`;# z7U#m3Ay@R**sD53apb0!B&H;mBpzgBEdsF*^1(#(ApD}z;u77|ycFHE%$!sJ{AVPO literal 0 HcmV?d00001 diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx.lock b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx.lock new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/libfirst_steps_v1.d b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/libfirst_steps_v1.d new file mode 100644 index 000000000000..26e39d15e183 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/libfirst_steps_v1.d @@ -0,0 +1 @@ +/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/libfirst_steps_v1.rlib: /Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v1/src/lib.rs diff --git a/docs/src/tutorial/first-steps-v1/target/kani/debug/.cargo-lock b/docs/src/tutorial/first-steps-v1/target/kani/debug/.cargo-lock new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 660e907fd28f..49110bd72576 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use std::ffi::OsString; use std::process::ExitCode; +use std::time::{Instant, SystemTime}; +use time::format_description::well_known::Rfc3339; use anyhow::Result; use autoharness::{autoharness_cargo, autoharness_standalone}; @@ -131,33 +133,50 @@ fn standalone_main() -> Result<()> { /// Run verification on the given project. fn verify_project(project: Project, session: KaniSession) -> Result<()> { + let wall_start = SystemTime::now(); + let cpu_start = Instant::now(); + fn to_rfc3339(t: std::time::SystemTime) -> String { + let dt: OffsetDateTime = t.into(); + dt.format(&Rfc3339).unwrap() + } + debug!(?project, "verify_project"); let mut handler = JsonHandler::new(session.args.export_json.clone()); // TODO: add session info let harnesses = session.determine_targets(project.get_all_harnesses())?; debug!(n = harnesses.len(), ?harnesses, "verify_project"); - + handler.add_item("schema_version", json!("0.0.0")); + handler.add_item( + "run_time", + json!({ + "started_at": to_rfc3339(wall_start) + }), + ); // Verification let runner = harness_runner::HarnessRunner { sess: &session, project: &project }; + let results = runner.check_all_harnesses(&harnesses, Some(&mut handler))?; - + for h in harnesses.clone() { let harness_result = results.iter().find(|r| r.harness.pretty_name == h.pretty_name); - handler.add_harness_detail("harnesses", json!({ - // basic name for harnesses - "pretty_name": h.pretty_name, - "mangled_name": h.mangled_name, - "crate_name": h.crate_name, - - // original location of the harnesses + let arr = handler.data["verification_runner_results"]["individual_harnesses"] + .as_array_mut() + .expect("individual_harnesses must be an array"); + // locate matching entry by name and overwrite it + let entry = arr.iter_mut().find(|v| { + v.get("name").and_then(|s| s.as_str()) == Some(h.pretty_name.as_str()) + }).expect("matching individual_harness not found"); + + *entry = json!({ + // basic names + "name": h.pretty_name, // keep name in the record + //original source location "original": { - "file": h.original_file, - "start_line": h.original_start_line, - "end_line": h.original_end_line + "file": h.original_file, + "start_line": h.original_start_line, + "end_line": h.original_end_line }, - // GOTO file generated - "goto": h.goto_file.as_ref().map(|p| p.to_string_lossy().to_string()), // attributes "kind": format!("{:?}", h.attributes.kind), @@ -165,7 +184,7 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { "has_loop_contracts": h.has_loop_contracts, "is_automatically_generated": h.is_automatically_generated, "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)), - "unwind_value": h.attributes.unwind_value, // Option + "unwind_value": h.attributes.unwind_value, "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), "verified_stubs": h.attributes.verified_stubs, @@ -180,7 +199,8 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { "timing": harness_result.map_or(json!(null), |result| json!({ "cbmc_runtime": format!("{:.3}s", result.result.runtime.as_secs_f64()) })) - })); + }); + } if session.args.coverage { @@ -204,7 +224,19 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { handler.add_item("coverage", json!({"enabled": false})); } + let wall_end = SystemTime::now(); + let duration_ms = cpu_start.elapsed().as_millis() as u64; + handler.add_item( + "run_time", + json!({ + "started_at": to_rfc3339(wall_start), + "finished_at": to_rfc3339(wall_end), + "duration_ms": duration_ms, + + }), + ); handler.export()?; + session.print_final_summary(&results) } diff --git a/kani_beta1.json b/kani_beta1.json new file mode 100644 index 000000000000..932172148c84 --- /dev/null +++ b/kani_beta1.json @@ -0,0 +1,47 @@ +{ + "harnesses": [ + { + "fully_qualified_name": "verify_success", + "cbmc_function_name": "_RNvCscOSYLgomVfM_3lib14verify_success", + "crate_name": "lib", + "original": { + "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", + "start_line": 54, + "end_line": 59 + }, + "goto": "/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v2/src/lib__RNvCscOSYLgomVfM_3lib14verify_success.symtab.out", + "kind": "Proof", + "should_panic": false, + "has_loop_contracts": false, + "is_automatically_generated": false, + "solver": null, + "unwind_value": null, + "contract": null, + "stubs": [], + "verified_stubs": [] + }, + { + "fully_qualified_name": "will_fail", + "cbmc_function_name": "_RNvCscOSYLgomVfM_3lib9will_fail", + "crate_name": "lib", + "original": { + "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", + "start_line": 64, + "end_line": 67 + }, + "goto": "/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v2/src/lib__RNvCscOSYLgomVfM_3lib9will_fail.symtab.out", + "kind": "Proof", + "should_panic": false, + "has_loop_contracts": false, + "is_automatically_generated": false, + "solver": null, + "unwind_value": null, + "contract": null, + "stubs": [], + "verified_stubs": [] + } + ], + "coverage": { + "enabled": false + } +} \ No newline at end of file diff --git a/kani_run.json b/kani_run.json new file mode 100644 index 000000000000..c2188cb81019 --- /dev/null +++ b/kani_run.json @@ -0,0 +1,66 @@ +{ + "schema_version": "0.0.0", + "run_time": { + "started_at": "2025-09-18T07:39:47.159151Z", + "finished_at": "2025-09-18T07:39:47.336333Z", + "duration_ms": 177 + }, + "verification_runner_results": { + "selected": 2, + "executed": 2, + "status": "completed", + "individual_harnesses": [ + { + "name": "will_fail", + "original": { + "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", + "start_line": 64, + "end_line": 67 + }, + "kind": "Proof", + "should_panic": false, + "has_loop_contracts": false, + "is_automatically_generated": false, + "solver": null, + "unwind_value": null, + "contract": null, + "stubs": [], + "verified_stubs": [], + "summary": { + "total": 1, + "status": "failed" + }, + "timing": { + "cbmc_runtime": "0.024s" + } + }, + { + "name": "verify_success", + "original": { + "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", + "start_line": 54, + "end_line": 59 + }, + "kind": "Proof", + "should_panic": false, + "has_loop_contracts": false, + "is_automatically_generated": false, + "solver": null, + "unwind_value": null, + "contract": null, + "stubs": [], + "verified_stubs": [], + "summary": { + "total": 1, + "status": "completed" + }, + "timing": { + "cbmc_runtime": "0.007s" + } + } + ] + }, + "coverage": { + "enabled": false + } +} \ No newline at end of file diff --git a/lib.rs b/lib.rs new file mode 100644 index 000000000000..d4b64df8f3e3 --- /dev/null +++ b/lib.rs @@ -0,0 +1,56 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// ANCHOR: code +fn estimate_size(x: u32) -> u32 { + if x < 256 { + if x < 128 { + return 1; + } else { + return 3; + } + } else if x < 1024 { + if x > 1022 { + panic!("Oh no, a failing corner case!"); + } else { + return 5; + } + } else { + if x < 2048 { + return 7; + } else { + return 9; + } + } +} +// ANCHOR_END: code + +#[cfg(test)] +mod tests { + use super::*; + use proptest::prelude::*; + + #[test] + fn it_works() { + assert_eq!(estimate_size(1024), 7); + } + + // ANCHOR: proptest + proptest! { + #![proptest_config(ProptestConfig::with_cases(10000))] + #[test] + fn doesnt_crash(x: u32) { + estimate_size(x); + } + } + // ANCHOR_END: proptest +} + +// ANCHOR: kani +#[cfg(kani)] +#[kani::proof] +fn check_estimate_size() { + let x: u32 = kani::any(); + estimate_size(x); +} +// ANCHOR_END: kani From f6d23e699f35827b5fe7bf1a553434d310351f62 Mon Sep 17 00:00:00 2001 From: jingfei-xu Date: Thu, 18 Sep 2025 14:44:30 -0700 Subject: [PATCH 09/54] Error Details and Property Analysis --- .../src/tutorial/first-steps-v1/kani_run.json | 57 +++++++++---------- kani-driver/src/main.rs | 48 +++++++++++++++- 2 files changed, 74 insertions(+), 31 deletions(-) diff --git a/docs/src/tutorial/first-steps-v1/kani_run.json b/docs/src/tutorial/first-steps-v1/kani_run.json index 8f60db60031f..5325c9e7b876 100644 --- a/docs/src/tutorial/first-steps-v1/kani_run.json +++ b/docs/src/tutorial/first-steps-v1/kani_run.json @@ -1,4 +1,10 @@ { + "schema_version": "0.0.0", + "run_time": { + "started_at": "2025-09-18T21:43:58.758849Z", + "finished_at": "2025-09-18T21:43:58.856479Z", + "duration_ms": 97 + }, "verification_runner_results": { "selected": 1, "executed": 1, @@ -6,39 +12,30 @@ "individual_harnesses": [ { "name": "check_estimate_size", - "status": "Failure" + "original": { + "file": "./docs/src/tutorial/first-steps-v1/src/lib.rs", + "start_line": 52, + "end_line": 55 + }, + "kind": "Proof", + "should_panic": false, + "has_loop_contracts": false, + "is_automatically_generated": false, + "solver": null, + "unwind_value": null, + "contract": null, + "stubs": [], + "verified_stubs": [], + "summary": { + "total": 1, + "status": "failed" + }, + "timing": { + "cbmc_runtime": "0.024s" + } } ] }, - "harnesses": [ - { - "pretty_name": "check_estimate_size", - "mangled_name": "_RNvCscOSYLgomVfM_3lib19check_estimate_size", - "crate_name": "lib", - "original": { - "file": "./docs/src/tutorial/first-steps-v1/src/lib.rs", - "start_line": 52, - "end_line": 55 - }, - "goto": "/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/src/lib__RNvCscOSYLgomVfM_3lib19check_estimate_size.symtab.out", - "kind": "Proof", - "should_panic": false, - "has_loop_contracts": false, - "is_automatically_generated": false, - "solver": null, - "unwind_value": null, - "contract": null, - "stubs": [], - "verified_stubs": [], - "summary": { - "total": 1, - "status": "failed" - }, - "timing": { - "cbmc_runtime": "0.025s" - } - } - ], "error_details": { "has_errors": true, "error_type": "assertion_failure", diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 49110bd72576..366be231c1cc 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -200,7 +200,53 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { "cbmc_runtime": format!("{:.3}s", result.result.runtime.as_secs_f64()) })) }); - + handler.add_item("error_details", harness_result.map_or(json!(null), |result| { + match result.result.status { + crate::call_cbmc::VerificationStatus::Failure => { + json!({ + "has_errors": true, + "error_type": match result.result.failed_properties { + crate::call_cbmc::FailedProperties::None => "unknown_failure", + crate::call_cbmc::FailedProperties::PanicsOnly => "assertion_failure", + crate::call_cbmc::FailedProperties::Other => "verification_failure", + }, + "failed_properties_type": format!("{:?}", result.result.failed_properties), + "exit_status": match &result.result.results { + Err(crate::call_cbmc::ExitStatus::Timeout) => "timeout".to_string(), + Err(crate::call_cbmc::ExitStatus::OutOfMemory) => "out_of_memory".to_string(), + Err(crate::call_cbmc::ExitStatus::Other(code)) => format!("exit_code_{}", code), + Ok(_) => "properties_failed".to_string() + } + }) + }, + crate::call_cbmc::VerificationStatus::Success => json!({ + "has_errors": false + }) + } + })); + handler.add_harness_detail("property_details", json!({ + "property_details": harness_result.map_or(json!(null), |result| { + match &result.result.results { + Ok(properties) => { + let total_properties = properties.len(); + let passed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Success)).count(); + let failed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Failure)).count(); + + json!({ + "total_properties": total_properties, + "passed": passed_properties, + "failed": failed_properties, + "unreachable": total_properties - passed_properties - failed_properties + }) + }, + Err(_) => json!({ + "total_properties": 0, + "error": "Could not extract property details due to verification failure" + }) + } + }) + })); + } if session.args.coverage { From f89bb7f32c1fc4a075212ded81aee2672b49c6b8 Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 19 Sep 2025 14:02:58 -0700 Subject: [PATCH 10/54] refactor existing json handler code to frontend folder --- .../src/{ => frontend}/json_handler.rs | 0 kani-driver/src/frontend/mod.rs | 11 ++ kani-driver/src/frontend/schema_utils.rs | 90 ++++++++++ kani-driver/src/harness_runner.rs | 30 +--- kani-driver/src/main.rs | 120 ++----------- kani-driver/src/util.rs | 165 +++++++++++++----- 6 files changed, 246 insertions(+), 170 deletions(-) rename kani-driver/src/{ => frontend}/json_handler.rs (100%) create mode 100644 kani-driver/src/frontend/mod.rs create mode 100644 kani-driver/src/frontend/schema_utils.rs diff --git a/kani-driver/src/json_handler.rs b/kani-driver/src/frontend/json_handler.rs similarity index 100% rename from kani-driver/src/json_handler.rs rename to kani-driver/src/frontend/json_handler.rs diff --git a/kani-driver/src/frontend/mod.rs b/kani-driver/src/frontend/mod.rs new file mode 100644 index 000000000000..8a2d41985437 --- /dev/null +++ b/kani-driver/src/frontend/mod.rs @@ -0,0 +1,11 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Frontend module for handling different output formats and JSON generation +//! This module separates the JSON handling logic from the main verification logic + +pub mod json_handler; +pub mod schema_utils; + +pub use json_handler::JsonHandler; +pub use schema_utils::*; diff --git a/kani-driver/src/frontend/schema_utils.rs b/kani-driver/src/frontend/schema_utils.rs new file mode 100644 index 000000000000..769744e3805b --- /dev/null +++ b/kani-driver/src/frontend/schema_utils.rs @@ -0,0 +1,90 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Utility functions for creating structured JSON schemas +//! This module contains helper functions to convert Kani internal structures to JSON + +use crate::call_cbmc::VerificationStatus; +use crate::harness_runner::HarnessResult; +use kani_metadata::HarnessMetadata; +use serde_json::{Value, json}; + +/// Creates structured JSON metadata for a harness +/// This utility function separates harness metadata creation from the main verification logic +pub fn create_harness_metadata_json(h: &HarnessMetadata) -> Value { + json!({ + "id": h.pretty_name, // Use pretty_name as unique identifier + "name": h.pretty_name, + "mangled_name": h.mangled_name, + "crate_name": h.crate_name, + "source": { + "file": h.original_file, + "start_line": h.original_start_line, + "end_line": h.original_end_line + }, + "configuration": { + "kind": format!("{:?}", h.attributes.kind), + "should_panic": h.attributes.should_panic, + "has_loop_contracts": h.has_loop_contracts, + "is_automatically_generated": h.is_automatically_generated, + "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)), + "unwind_value": h.attributes.unwind_value, + "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), + "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), + "verified_stubs": h.attributes.verified_stubs + }, + "artifacts": { + "goto_file": h.goto_file.as_ref().map(|p| p.to_string_lossy().to_string()) + } + }) +} + +/// Creates verification result JSON with harness reference +/// This reduces duplication between harness metadata and verification results +pub fn create_verification_result_json(result: &HarnessResult) -> Value { + // Extract detailed verification results + let verification_details = match &result.result.results { + Ok(properties) => { + properties.iter().enumerate().map(|(i, prop)| { + json!({ + "check_number": i + 1, + "function_name": prop.property_id.fn_name.as_ref().unwrap_or(&"unknown".to_string()), + "status": format!("{:?}", prop.status), + "description": prop.description, + "location": { + "file": prop.source_location.file.as_ref().unwrap_or(&"unknown".to_string()), + "line": prop.source_location.line.as_ref().unwrap_or(&"unknown".to_string()), + "column": prop.source_location.column.as_ref().unwrap_or(&"unknown".to_string()), + }, + "class": prop.property_id.class, + }) + }).collect::>() + }, + Err(_) => vec![], + }; + + json!({ + "harness_id": result.harness.pretty_name, // Reference to harness instead of duplicating name + "status": match result.result.status { + VerificationStatus::Success => "Success", + VerificationStatus::Failure => "Failure", + }, + "verification_details": verification_details, + }) +} + +/// Creates a verification summary with clean structure +pub fn create_verification_summary_json( + results: &[HarnessResult], + selected: usize, + status_label: &str, +) -> Value { + let details: Vec<_> = results.iter().map(|r| create_verification_result_json(r)).collect(); + + json!({ + "selected": selected, + "executed": results.len(), + "status": status_label, + "individual_harnesses": details, + }) +} diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index 06ee37b2caa6..3e3668032064 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -10,7 +10,7 @@ use std::path::Path; use crate::args::{NumThreads, OutputFormat}; use crate::call_cbmc::{VerificationResult, VerificationStatus}; -use crate::json_handler::JsonHandler; +use crate::frontend::{JsonHandler, create_verification_summary_json}; use crate::project::Project; use crate::session::{BUG_REPORT_URL, KaniSession}; @@ -50,36 +50,16 @@ impl std::fmt::Display for FailFastHarnessInfo { } impl<'pr> HarnessRunner<'_, 'pr> { - /// Helper: push verification summary into JsonHandler. + /// Helper: push verification summary into JsonHandler using frontend utilities. fn add_runner_results_json( handler: &mut JsonHandler, results: &[HarnessResult<'pr>], selected: usize, status_label: &str, ) { - use serde_json::json; - let details: Vec<_> = results - .iter() - .map(|r| { - json!({ - "name": r.harness.pretty_name, - "status": match r.result.status { - VerificationStatus::Success => "Success", - VerificationStatus::Failure => "Failure", - }, - }) - }) - .collect(); - - handler.add_item( - "verification_runner_results", - json!({ - "selected": selected, - "executed": results.len(), - "status": status_label, - "individual_harnesses": details, - }), - ); + // Use frontend utility to create structured verification summary + let summary = create_verification_summary_json(results, selected, status_label); + handler.add_item("verification_runner_results", summary); } /// Given a [`HarnessRunner`] (to abstract over how these harnesses were generated), this runs diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 366be231c1cc..bb2e1f4e32e7 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -14,7 +14,7 @@ use args_toml::join_args; use crate::args::StandaloneSubcommand; use crate::concrete_playback::playback::{playback_cargo, playback_standalone}; -use crate::json_handler::JsonHandler; +use crate::frontend::{JsonHandler, create_harness_metadata_json}; use crate::list::collect_metadata::{list_cargo, list_standalone}; use crate::project::Project; use crate::session::KaniSession; @@ -41,7 +41,7 @@ mod list; mod metadata; mod project; -mod json_handler; +mod frontend; mod session; mod util; mod version; @@ -133,8 +133,8 @@ fn standalone_main() -> Result<()> { /// Run verification on the given project. fn verify_project(project: Project, session: KaniSession) -> Result<()> { - let wall_start = SystemTime::now(); - let cpu_start = Instant::now(); + let wall_start = SystemTime::now(); + let cpu_start = Instant::now(); fn to_rfc3339(t: std::time::SystemTime) -> String { let dt: OffsetDateTime = t.into(); dt.format(&Rfc3339).unwrap() @@ -149,105 +149,21 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { handler.add_item( "run_time", json!({ - "started_at": to_rfc3339(wall_start) - }), + "started_at": to_rfc3339(wall_start) + }), ); + // Add harness metadata using frontend utility + for h in &harnesses { + handler.add_harness_detail("harnesses", create_harness_metadata_json(h)); + } + // Verification let runner = harness_runner::HarnessRunner { sess: &session, project: &project }; let results = runner.check_all_harnesses(&harnesses, Some(&mut handler))?; - for h in harnesses.clone() { - let harness_result = results.iter().find(|r| r.harness.pretty_name == h.pretty_name); - let arr = handler.data["verification_runner_results"]["individual_harnesses"] - .as_array_mut() - .expect("individual_harnesses must be an array"); - // locate matching entry by name and overwrite it - let entry = arr.iter_mut().find(|v| { - v.get("name").and_then(|s| s.as_str()) == Some(h.pretty_name.as_str()) - }).expect("matching individual_harness not found"); - - *entry = json!({ - // basic names - "name": h.pretty_name, // keep name in the record - //original source location - "original": { - "file": h.original_file, - "start_line": h.original_start_line, - "end_line": h.original_end_line - }, - - - // attributes - "kind": format!("{:?}", h.attributes.kind), - "should_panic": h.attributes.should_panic, - "has_loop_contracts": h.has_loop_contracts, - "is_automatically_generated": h.is_automatically_generated, - "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)), - "unwind_value": h.attributes.unwind_value, - "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), - "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), - "verified_stubs": h.attributes.verified_stubs, - - "summary": harness_result.map_or(json!(null), |result| json!({ - "total": 1, - "status": match result.result.status { - crate::call_cbmc::VerificationStatus::Success => "completed", - crate::call_cbmc::VerificationStatus::Failure => "failed", - } - })), - "timing": harness_result.map_or(json!(null), |result| json!({ - "cbmc_runtime": format!("{:.3}s", result.result.runtime.as_secs_f64()) - })) - }); - handler.add_item("error_details", harness_result.map_or(json!(null), |result| { - match result.result.status { - crate::call_cbmc::VerificationStatus::Failure => { - json!({ - "has_errors": true, - "error_type": match result.result.failed_properties { - crate::call_cbmc::FailedProperties::None => "unknown_failure", - crate::call_cbmc::FailedProperties::PanicsOnly => "assertion_failure", - crate::call_cbmc::FailedProperties::Other => "verification_failure", - }, - "failed_properties_type": format!("{:?}", result.result.failed_properties), - "exit_status": match &result.result.results { - Err(crate::call_cbmc::ExitStatus::Timeout) => "timeout".to_string(), - Err(crate::call_cbmc::ExitStatus::OutOfMemory) => "out_of_memory".to_string(), - Err(crate::call_cbmc::ExitStatus::Other(code)) => format!("exit_code_{}", code), - Ok(_) => "properties_failed".to_string() - } - }) - }, - crate::call_cbmc::VerificationStatus::Success => json!({ - "has_errors": false - }) - } - })); - handler.add_harness_detail("property_details", json!({ - "property_details": harness_result.map_or(json!(null), |result| { - match &result.result.results { - Ok(properties) => { - let total_properties = properties.len(); - let passed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Success)).count(); - let failed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Failure)).count(); - - json!({ - "total_properties": total_properties, - "passed": passed_properties, - "failed": failed_properties, - "unreachable": total_properties - passed_properties - failed_properties - }) - }, - Err(_) => json!({ - "total_properties": 0, - "error": "Could not extract property details due to verification failure" - }) - } - }) - })); - - } + // Process harness results and add additional metadata using utility function + util::process_harness_results(&mut handler, &harnesses, &results)?; if session.args.coverage { // We generate a timestamp to save the coverage data in a folder named @@ -270,16 +186,16 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { handler.add_item("coverage", json!({"enabled": false})); } - let wall_end = SystemTime::now(); + let wall_end = SystemTime::now(); let duration_ms = cpu_start.elapsed().as_millis() as u64; handler.add_item( "run_time", json!({ - "started_at": to_rfc3339(wall_start), - "finished_at": to_rfc3339(wall_end), - "duration_ms": duration_ms, + "started_at": to_rfc3339(wall_start), + "finished_at": to_rfc3339(wall_end), + "duration_ms": duration_ms, - }), + }), ); handler.export()?; diff --git a/kani-driver/src/util.rs b/kani-driver/src/util.rs index b4a3dd954b7e..22bc2dea0d59 100644 --- a/kani-driver/src/util.rs +++ b/kani-driver/src/util.rs @@ -14,6 +14,13 @@ use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::process::Command; +use anyhow::Result; +use kani_metadata::HarnessMetadata; +use serde_json::json; +use crate::frontend::JsonHandler; +use crate::harness_runner::HarnessResult; +use crate::call_cbmc::VerificationStatus; + /// Replace an extension with another one, in a new PathBuf. (See tests for examples) pub fn alter_extension(path: &Path, ext: &str) -> PathBuf { path.with_extension(ext) @@ -88,6 +95,118 @@ pub fn info_operation(op: &str, msg: &str) { println!("{op_fmt} {msg_fmt}") } +/// Process harness results and enrich JSON handler with additional metadata. +/// This utility function handles the complex harness processing logic that was previously in main.rs. +pub fn process_harness_results( + handler: &mut JsonHandler, + harnesses: &[&HarnessMetadata], + results: &[HarnessResult], +) -> Result<()> { + for h in harnesses { + let harness_result = results.iter().find(|r| r.harness.pretty_name == h.pretty_name); + let arr = handler.data["verification_runner_results"]["individual_harnesses"] + .as_array_mut() + .expect("individual_harnesses must be an array"); + + // locate matching entry by harness_id and overwrite it + let entry = arr.iter_mut().find(|v| { + v.get("harness_id").and_then(|s| s.as_str()) == Some(h.pretty_name.as_str()) + }).expect("matching individual_harness not found"); + + // Get the original verification details from the entry before overwriting + let verification_details = entry.get("verification_details").cloned().unwrap_or(json!([])); + let status = entry.get("status").and_then(|s| s.as_str()).unwrap_or("Unknown"); + + *entry = json!({ + "harness_id": h.pretty_name, // Keep harness_id for consistency + "name": h.pretty_name, // Also keep name for backward compatibility + "status": status, // Preserve the verification status + "verification_details": verification_details, // Preserve verification details + + //original source location + "original": { + "file": h.original_file, + "start_line": h.original_start_line, + "end_line": h.original_end_line + }, + + // attributes + "kind": format!("{:?}", h.attributes.kind), + "should_panic": h.attributes.should_panic, + "has_loop_contracts": h.has_loop_contracts, + "is_automatically_generated": h.is_automatically_generated, + "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)), + "unwind_value": h.attributes.unwind_value, + "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), + "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), + "verified_stubs": h.attributes.verified_stubs, + + "summary": harness_result.map_or(json!(null), |result| json!({ + "total": 1, + "status": match result.result.status { + VerificationStatus::Success => "completed", + VerificationStatus::Failure => "failed", + } + })), + "timing": harness_result.map_or(json!(null), |result| json!({ + "cbmc_runtime": format!("{:.3}s", result.result.runtime.as_secs_f64()) + })) + }); + + // Add error details for this harness + handler.add_item("error_details", harness_result.map_or(json!(null), |result| { + match result.result.status { + VerificationStatus::Failure => { + json!({ + "has_errors": true, + "error_type": match result.result.failed_properties { + crate::call_cbmc::FailedProperties::None => "unknown_failure", + crate::call_cbmc::FailedProperties::PanicsOnly => "assertion_failure", + crate::call_cbmc::FailedProperties::Other => "verification_failure", + }, + "failed_properties_type": format!("{:?}", result.result.failed_properties), + "exit_status": match &result.result.results { + Err(crate::call_cbmc::ExitStatus::Timeout) => "timeout".to_string(), + Err(crate::call_cbmc::ExitStatus::OutOfMemory) => "out_of_memory".to_string(), + Err(crate::call_cbmc::ExitStatus::Other(code)) => format!("exit_code_{}", code), + Ok(_) => "properties_failed".to_string() + } + }) + }, + VerificationStatus::Success => json!({ + "has_errors": false + }) + } + })); + + // Add property details for this harness + handler.add_harness_detail("property_details", json!({ + "property_details": harness_result.map_or(json!(null), |result| { + match &result.result.results { + Ok(properties) => { + let total_properties = properties.len(); + let passed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Success)).count(); + let failed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Failure)).count(); + + json!({ + "total_properties": total_properties, + "passed": passed_properties, + "failed": failed_properties, + "unreachable": total_properties - passed_properties - failed_properties + }) + }, + Err(_) => json!({ + "total_properties": 0, + "error": "Could not extract property details due to verification failure" + }) + } + }) + })); + } + + Ok(()) +} + /// # Kani Argument Types /// /// We have three different kinds of arguments we use to influence our compilation process. @@ -179,10 +298,10 @@ pub(crate) mod args { } pub enum PassTo { - /// Only pass arguments for use in the local crate. + /// TODO: Only pass arguments for use in the local crate. /// This will just pass them directly as arguments to the command. OnlyLocalCrate, - /// Pass arguments for use when compiling all dependencies using the + /// TODO: Pass arguments for use when compiling all dependencies using the /// `CARGO_ENCODED_RUSTFLAGS` environment variable. AllCrates, } @@ -213,7 +332,7 @@ pub(crate) mod args { // Since we also want to recursively pass these args to all dependencies, // use an environment variable that gets checked for each dependency. PassTo::AllCrates => { - // Use of CARGO_ENCODED_RUSTFLAGS instead of RUSTFLAGS is preferred. See + // TODO: Use of CARGO_ENCODED_RUSTFLAGS instead of RUSTFLAGS is preferred. See // https://doc.rust-lang.org/cargo/reference/environment-variables.html let env_var = OsString::from("CARGO_ENCODED_RUSTFLAGS"); @@ -239,43 +358,3 @@ pub(crate) mod args { } } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn check_alter_extension() { - let p = PathBuf::from("./path/file.rs"); - assert_eq!(alter_extension(&p, "symtab.json"), PathBuf::from("./path/file.symtab.json")); - assert_eq!( - alter_extension(&p, "kani-metadata.json"), - PathBuf::from("./path/file.kani-metadata.json") - ); - - let q = PathBuf::from("file.more.rs"); - assert_eq!(alter_extension(&q, "symtab.json"), PathBuf::from("file.more.symtab.json")); - } - - #[test] - fn check_exe_basename() { - assert_eq!( - executable_basename(&Some(&OsString::from("/path/slash/foo"))), - Some("foo".into()) - ); - assert_eq!(executable_basename(&Some(&OsString::from("./foo.exe"))), Some("foo".into())); - assert_eq!(executable_basename(&Some(&OsString::from("foo.exe"))), Some("foo".into())); - assert_eq!(executable_basename(&Some(&OsString::from("foo"))), Some("foo".into())); - } - - #[test] - fn check_render_command() { - let mut c1 = Command::new("a"); - c1.arg("b"); - assert_eq!(render_command(&c1), OsString::from("a b")); - c1.arg("/c d/"); - assert_eq!(render_command(&c1), OsString::from("a b \"/c d/\"")); - c1.env("PARAM", "VALUE"); - assert_eq!(render_command(&c1), OsString::from("PARAM=\"VALUE\" a b \"/c d/\"")); - } -} From c6286210f7778fb25c8a508e21548b30473fd141 Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 19 Sep 2025 20:27:52 -0700 Subject: [PATCH 11/54] Restore util.rs and refactor schema logic into frontend/schema_utils.rs --- kani-driver/src/frontend/schema_utils.rs | 115 ++++++++++++++++ kani-driver/src/main.rs | 6 +- kani-driver/src/util.rs | 165 ++++++----------------- 3 files changed, 161 insertions(+), 125 deletions(-) diff --git a/kani-driver/src/frontend/schema_utils.rs b/kani-driver/src/frontend/schema_utils.rs index 769744e3805b..8de31c3dc526 100644 --- a/kani-driver/src/frontend/schema_utils.rs +++ b/kani-driver/src/frontend/schema_utils.rs @@ -6,8 +6,10 @@ use crate::call_cbmc::VerificationStatus; use crate::harness_runner::HarnessResult; +use crate::frontend::JsonHandler; use kani_metadata::HarnessMetadata; use serde_json::{Value, json}; +use anyhow::Result; /// Creates structured JSON metadata for a harness /// This utility function separates harness metadata creation from the main verification logic @@ -88,3 +90,116 @@ pub fn create_verification_summary_json( "individual_harnesses": details, }) } + +/// Process harness results and enrich JSON handler with additional metadata. +/// This function handles the complex harness processing logic, combining verification results +/// with harness metadata to create enriched JSON output. +pub fn process_harness_results( + handler: &mut JsonHandler, + harnesses: &[&HarnessMetadata], + results: &[HarnessResult], +) -> Result<()> { + for h in harnesses { + let harness_result = results.iter().find(|r| r.harness.pretty_name == h.pretty_name); + let arr = handler.data["verification_runner_results"]["individual_harnesses"] + .as_array_mut() + .expect("individual_harnesses must be an array"); + + // locate matching entry by harness_id and overwrite it + let entry = arr.iter_mut().find(|v| { + v.get("harness_id").and_then(|s| s.as_str()) == Some(h.pretty_name.as_str()) + }).expect("matching individual_harness not found"); + + // Get the original verification details from the entry before overwriting + let verification_details = entry.get("verification_details").cloned().unwrap_or(json!([])); + let status = entry.get("status").and_then(|s| s.as_str()).unwrap_or("Unknown"); + + *entry = json!({ + "harness_id": h.pretty_name, // Keep harness_id for consistency + "name": h.pretty_name, // Also keep name for backward compatibility + "status": status, // Preserve the verification status + "verification_details": verification_details, // Preserve verification details + + //original source location + "original": { + "file": h.original_file, + "start_line": h.original_start_line, + "end_line": h.original_end_line + }, + + // attributes + "kind": format!("{:?}", h.attributes.kind), + "should_panic": h.attributes.should_panic, + "has_loop_contracts": h.has_loop_contracts, + "is_automatically_generated": h.is_automatically_generated, + "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)), + "unwind_value": h.attributes.unwind_value, + "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), + "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), + "verified_stubs": h.attributes.verified_stubs, + + "summary": harness_result.map_or(json!(null), |result| json!({ + "total": 1, + "status": match result.result.status { + VerificationStatus::Success => "completed", + VerificationStatus::Failure => "failed", + } + })), + "timing": harness_result.map_or(json!(null), |result| json!({ + "cbmc_runtime": format!("{:.3}s", result.result.runtime.as_secs_f64()) + })) + }); + + // Add error details for this harness + handler.add_item("error_details", harness_result.map_or(json!(null), |result| { + match result.result.status { + VerificationStatus::Failure => { + json!({ + "has_errors": true, + "error_type": match result.result.failed_properties { + crate::call_cbmc::FailedProperties::None => "unknown_failure", + crate::call_cbmc::FailedProperties::PanicsOnly => "assertion_failure", + crate::call_cbmc::FailedProperties::Other => "verification_failure", + }, + "failed_properties_type": format!("{:?}", result.result.failed_properties), + "exit_status": match &result.result.results { + Err(crate::call_cbmc::ExitStatus::Timeout) => "timeout".to_string(), + Err(crate::call_cbmc::ExitStatus::OutOfMemory) => "out_of_memory".to_string(), + Err(crate::call_cbmc::ExitStatus::Other(code)) => format!("exit_code_{}", code), + Ok(_) => "properties_failed".to_string() + } + }) + }, + VerificationStatus::Success => json!({ + "has_errors": false + }) + } + })); + + // Add property details for this harness + handler.add_harness_detail("property_details", json!({ + "property_details": harness_result.map_or(json!(null), |result| { + match &result.result.results { + Ok(properties) => { + let total_properties = properties.len(); + let passed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Success)).count(); + let failed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Failure)).count(); + + json!({ + "total_properties": total_properties, + "passed": passed_properties, + "failed": failed_properties, + "unreachable": total_properties - passed_properties - failed_properties + }) + }, + Err(_) => json!({ + "total_properties": 0, + "error": "Could not extract property details due to verification failure" + }) + } + }) + })); + } + + Ok(()) +} diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index bb2e1f4e32e7..d4b7a2463a66 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -14,7 +14,7 @@ use args_toml::join_args; use crate::args::StandaloneSubcommand; use crate::concrete_playback::playback::{playback_cargo, playback_standalone}; -use crate::frontend::{JsonHandler, create_harness_metadata_json}; +use crate::frontend::{JsonHandler, create_harness_metadata_json, process_harness_results}; use crate::list::collect_metadata::{list_cargo, list_standalone}; use crate::project::Project; use crate::session::KaniSession; @@ -162,8 +162,8 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { let results = runner.check_all_harnesses(&harnesses, Some(&mut handler))?; - // Process harness results and add additional metadata using utility function - util::process_harness_results(&mut handler, &harnesses, &results)?; + // Process harness results and add additional metadata using frontend utility function + process_harness_results(&mut handler, &harnesses, &results)?; if session.args.coverage { // We generate a timestamp to save the coverage data in a folder named diff --git a/kani-driver/src/util.rs b/kani-driver/src/util.rs index 22bc2dea0d59..77ff1d783144 100644 --- a/kani-driver/src/util.rs +++ b/kani-driver/src/util.rs @@ -14,13 +14,6 @@ use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::process::Command; -use anyhow::Result; -use kani_metadata::HarnessMetadata; -use serde_json::json; -use crate::frontend::JsonHandler; -use crate::harness_runner::HarnessResult; -use crate::call_cbmc::VerificationStatus; - /// Replace an extension with another one, in a new PathBuf. (See tests for examples) pub fn alter_extension(path: &Path, ext: &str) -> PathBuf { path.with_extension(ext) @@ -95,118 +88,6 @@ pub fn info_operation(op: &str, msg: &str) { println!("{op_fmt} {msg_fmt}") } -/// Process harness results and enrich JSON handler with additional metadata. -/// This utility function handles the complex harness processing logic that was previously in main.rs. -pub fn process_harness_results( - handler: &mut JsonHandler, - harnesses: &[&HarnessMetadata], - results: &[HarnessResult], -) -> Result<()> { - for h in harnesses { - let harness_result = results.iter().find(|r| r.harness.pretty_name == h.pretty_name); - let arr = handler.data["verification_runner_results"]["individual_harnesses"] - .as_array_mut() - .expect("individual_harnesses must be an array"); - - // locate matching entry by harness_id and overwrite it - let entry = arr.iter_mut().find(|v| { - v.get("harness_id").and_then(|s| s.as_str()) == Some(h.pretty_name.as_str()) - }).expect("matching individual_harness not found"); - - // Get the original verification details from the entry before overwriting - let verification_details = entry.get("verification_details").cloned().unwrap_or(json!([])); - let status = entry.get("status").and_then(|s| s.as_str()).unwrap_or("Unknown"); - - *entry = json!({ - "harness_id": h.pretty_name, // Keep harness_id for consistency - "name": h.pretty_name, // Also keep name for backward compatibility - "status": status, // Preserve the verification status - "verification_details": verification_details, // Preserve verification details - - //original source location - "original": { - "file": h.original_file, - "start_line": h.original_start_line, - "end_line": h.original_end_line - }, - - // attributes - "kind": format!("{:?}", h.attributes.kind), - "should_panic": h.attributes.should_panic, - "has_loop_contracts": h.has_loop_contracts, - "is_automatically_generated": h.is_automatically_generated, - "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)), - "unwind_value": h.attributes.unwind_value, - "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), - "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), - "verified_stubs": h.attributes.verified_stubs, - - "summary": harness_result.map_or(json!(null), |result| json!({ - "total": 1, - "status": match result.result.status { - VerificationStatus::Success => "completed", - VerificationStatus::Failure => "failed", - } - })), - "timing": harness_result.map_or(json!(null), |result| json!({ - "cbmc_runtime": format!("{:.3}s", result.result.runtime.as_secs_f64()) - })) - }); - - // Add error details for this harness - handler.add_item("error_details", harness_result.map_or(json!(null), |result| { - match result.result.status { - VerificationStatus::Failure => { - json!({ - "has_errors": true, - "error_type": match result.result.failed_properties { - crate::call_cbmc::FailedProperties::None => "unknown_failure", - crate::call_cbmc::FailedProperties::PanicsOnly => "assertion_failure", - crate::call_cbmc::FailedProperties::Other => "verification_failure", - }, - "failed_properties_type": format!("{:?}", result.result.failed_properties), - "exit_status": match &result.result.results { - Err(crate::call_cbmc::ExitStatus::Timeout) => "timeout".to_string(), - Err(crate::call_cbmc::ExitStatus::OutOfMemory) => "out_of_memory".to_string(), - Err(crate::call_cbmc::ExitStatus::Other(code)) => format!("exit_code_{}", code), - Ok(_) => "properties_failed".to_string() - } - }) - }, - VerificationStatus::Success => json!({ - "has_errors": false - }) - } - })); - - // Add property details for this harness - handler.add_harness_detail("property_details", json!({ - "property_details": harness_result.map_or(json!(null), |result| { - match &result.result.results { - Ok(properties) => { - let total_properties = properties.len(); - let passed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Success)).count(); - let failed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Failure)).count(); - - json!({ - "total_properties": total_properties, - "passed": passed_properties, - "failed": failed_properties, - "unreachable": total_properties - passed_properties - failed_properties - }) - }, - Err(_) => json!({ - "total_properties": 0, - "error": "Could not extract property details due to verification failure" - }) - } - }) - })); - } - - Ok(()) -} - /// # Kani Argument Types /// /// We have three different kinds of arguments we use to influence our compilation process. @@ -298,10 +179,10 @@ pub(crate) mod args { } pub enum PassTo { - /// TODO: Only pass arguments for use in the local crate. + /// Only pass arguments for use in the local crate. /// This will just pass them directly as arguments to the command. OnlyLocalCrate, - /// TODO: Pass arguments for use when compiling all dependencies using the + /// Pass arguments for use when compiling all dependencies using the /// `CARGO_ENCODED_RUSTFLAGS` environment variable. AllCrates, } @@ -332,7 +213,7 @@ pub(crate) mod args { // Since we also want to recursively pass these args to all dependencies, // use an environment variable that gets checked for each dependency. PassTo::AllCrates => { - // TODO: Use of CARGO_ENCODED_RUSTFLAGS instead of RUSTFLAGS is preferred. See + // Use of CARGO_ENCODED_RUSTFLAGS instead of RUSTFLAGS is preferred. See // https://doc.rust-lang.org/cargo/reference/environment-variables.html let env_var = OsString::from("CARGO_ENCODED_RUSTFLAGS"); @@ -358,3 +239,43 @@ pub(crate) mod args { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn check_alter_extension() { + let p = PathBuf::from("./path/file.rs"); + assert_eq!(alter_extension(&p, "symtab.json"), PathBuf::from("./path/file.symtab.json")); + assert_eq!( + alter_extension(&p, "kani-metadata.json"), + PathBuf::from("./path/file.kani-metadata.json") + ); + + let q = PathBuf::from("file.more.rs"); + assert_eq!(alter_extension(&q, "symtab.json"), PathBuf::from("file.more.symtab.json")); + } + + #[test] + fn check_exe_basename() { + assert_eq!( + executable_basename(&Some(&OsString::from("/path/slash/foo"))), + Some("foo".into()) + ); + assert_eq!(executable_basename(&Some(&OsString::from("./foo.exe"))), Some("foo".into())); + assert_eq!(executable_basename(&Some(&OsString::from("foo.exe"))), Some("foo".into())); + assert_eq!(executable_basename(&Some(&OsString::from("foo"))), Some("foo".into())); + } + + #[test] + fn check_render_command() { + let mut c1 = Command::new("a"); + c1.arg("b"); + assert_eq!(render_command(&c1), OsString::from("a b")); + c1.arg("/c d/"); + assert_eq!(render_command(&c1), OsString::from("a b \"/c d/\"")); + c1.env("PARAM", "VALUE"); + assert_eq!(render_command(&c1), OsString::from("PARAM=\"VALUE\" a b \"/c d/\"")); + } +} \ No newline at end of file From 3822a145a12728ed9d8518b791aea92d09f8111b Mon Sep 17 00:00:00 2001 From: ConnorJKY Date: Sat, 20 Sep 2025 19:43:23 -0700 Subject: [PATCH 12/54] Extend frontend util function for project and export run metadata; Reschedule the schema for harness metadada util func. --- kani-driver/src/frontend/schema_utils.rs | 62 +++++++--- kani-driver/src/main.rs | 16 ++- kani_run.json | 142 ++++++++++++++++++++++- 3 files changed, 189 insertions(+), 31 deletions(-) diff --git a/kani-driver/src/frontend/schema_utils.rs b/kani-driver/src/frontend/schema_utils.rs index 8de31c3dc526..1169b16e3e8a 100644 --- a/kani-driver/src/frontend/schema_utils.rs +++ b/kani-driver/src/frontend/schema_utils.rs @@ -10,13 +10,45 @@ use crate::frontend::JsonHandler; use kani_metadata::HarnessMetadata; use serde_json::{Value, json}; use anyhow::Result; +use time::format_description::well_known::Rfc3339; +use time::OffsetDateTime; +use crate::project::Project; +/// Creates structured JSON metadata for an export run +/// This utility function captures basic environment for the whole session +pub fn create_metadata_json() -> Value { + let timestamp = OffsetDateTime::now_utc() + .format(&Rfc3339) + .unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_string()); + + let kani_version = env!("CARGO_PKG_VERSION"); + let target = " "; + let build_mode = if cfg!(debug_assertions) { "debug" } else { "release" }; + + json!({ + "version": "1.0", + "timestamp": timestamp, + "kani_version": kani_version, + "target": target, + "build_mode": build_mode, + }) +} + +/// Creates structured JSON metadata for the project +/// This utility function captures detailed info for the project +pub fn create_project_metadata_json(project: &Project) -> Value { + json!({ + "crate_name": project.metadata.iter() + .map(|m| m.crate_name.clone()) + .collect::>(), + "workspace_root": project.outdir.clone(), + }) +} /// Creates structured JSON metadata for a harness /// This utility function separates harness metadata creation from the main verification logic pub fn create_harness_metadata_json(h: &HarnessMetadata) -> Value { json!({ - "id": h.pretty_name, // Use pretty_name as unique identifier - "name": h.pretty_name, + "pretty_name": h.pretty_name, // use this as identifier "mangled_name": h.mangled_name, "crate_name": h.crate_name, "source": { @@ -24,20 +56,18 @@ pub fn create_harness_metadata_json(h: &HarnessMetadata) -> Value { "start_line": h.original_start_line, "end_line": h.original_end_line }, - "configuration": { + "goto_file": h.goto_file.as_ref().map(|p| p.to_string_lossy().to_string()), + "attributes": { "kind": format!("{:?}", h.attributes.kind), "should_panic": h.attributes.should_panic, - "has_loop_contracts": h.has_loop_contracts, - "is_automatically_generated": h.is_automatically_generated, - "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)), - "unwind_value": h.attributes.unwind_value, - "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), - "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), - "verified_stubs": h.attributes.verified_stubs }, - "artifacts": { - "goto_file": h.goto_file.as_ref().map(|p| p.to_string_lossy().to_string()) - } + "Contract":{ + "contracted_function_name": h.contract.as_ref().map(|c| format!("{:?}", c)), + "recursion_tracker": "" + }, + "has_loop_contracts": h.has_loop_contracts, + "is_automatically_generated": h.is_automatically_generated, + }) } @@ -104,7 +134,7 @@ pub fn process_harness_results( let arr = handler.data["verification_runner_results"]["individual_harnesses"] .as_array_mut() .expect("individual_harnesses must be an array"); - + // locate matching entry by harness_id and overwrite it let entry = arr.iter_mut().find(|v| { v.get("harness_id").and_then(|s| s.as_str()) == Some(h.pretty_name.as_str()) @@ -113,13 +143,13 @@ pub fn process_harness_results( // Get the original verification details from the entry before overwriting let verification_details = entry.get("verification_details").cloned().unwrap_or(json!([])); let status = entry.get("status").and_then(|s| s.as_str()).unwrap_or("Unknown"); - + *entry = json!({ "harness_id": h.pretty_name, // Keep harness_id for consistency "name": h.pretty_name, // Also keep name for backward compatibility "status": status, // Preserve the verification status "verification_details": verification_details, // Preserve verification details - + //original source location "original": { "file": h.original_file, diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index d4b7a2463a66..0b52fbde9cfd 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -14,7 +14,7 @@ use args_toml::join_args; use crate::args::StandaloneSubcommand; use crate::concrete_playback::playback::{playback_cargo, playback_standalone}; -use crate::frontend::{JsonHandler, create_harness_metadata_json, process_harness_results}; +use crate::frontend::{JsonHandler, create_metadata_json, create_harness_metadata_json, process_harness_results, create_project_metadata_json}; use crate::list::collect_metadata::{list_cargo, list_standalone}; use crate::project::Project; use crate::session::KaniSession; @@ -145,16 +145,14 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { // TODO: add session info let harnesses = session.determine_targets(project.get_all_harnesses())?; debug!(n = harnesses.len(), ?harnesses, "verify_project"); - handler.add_item("schema_version", json!("0.0.0")); - handler.add_item( - "run_time", - json!({ - "started_at": to_rfc3339(wall_start) - }), - ); + + // Add project and export run metadata using frontend utility + handler.add_item("metadata", create_metadata_json()); + handler.add_item("project", create_project_metadata_json(&project)); + // Add harness metadata using frontend utility for h in &harnesses { - handler.add_harness_detail("harnesses", create_harness_metadata_json(h)); + handler.add_harness_detail("harness_metadata", create_harness_metadata_json(h)); } // Verification diff --git a/kani_run.json b/kani_run.json index c2188cb81019..019b991cf5aa 100644 --- a/kani_run.json +++ b/kani_run.json @@ -1,17 +1,90 @@ { - "schema_version": "0.0.0", - "run_time": { - "started_at": "2025-09-18T07:39:47.159151Z", - "finished_at": "2025-09-18T07:39:47.336333Z", - "duration_ms": 177 + "metadata": { + "version": "1.0", + "timestamp": "2025-09-20T19:17:55.319661Z", + "kani_version": "0.65.0", + "target": " ", + "build_mode": "release" + }, + "project": { + "crate_name": [ + "lib" + ], + "workspace_root": "/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v2/src" }, + "harness_metadata": [ + { + "pretty_name": "verify_success", + "mangled_name": "_RNvCscOSYLgomVfM_3lib14verify_success", + "crate_name": "lib", + "source": { + "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", + "start_line": 54, + "end_line": 59 + }, + "goto_file": "/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v2/src/lib__RNvCscOSYLgomVfM_3lib14verify_success.symtab.out", + "attributes": { + "kind": "Proof", + "should_panic": false, + "solver": null, + "unwind_value": null, + "stubs": [], + "verified_stubs": [] + }, + "Contract": { + "contracted_function_name": null + }, + "has_loop_contracts": false, + "is_automatically_generated": false + }, + { + "pretty_name": "will_fail", + "mangled_name": "_RNvCscOSYLgomVfM_3lib9will_fail", + "crate_name": "lib", + "source": { + "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", + "start_line": 64, + "end_line": 67 + }, + "goto_file": "/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v2/src/lib__RNvCscOSYLgomVfM_3lib9will_fail.symtab.out", + "attributes": { + "kind": "Proof", + "should_panic": false, + "solver": null, + "unwind_value": null, + "stubs": [], + "verified_stubs": [] + }, + "Contract": { + "contracted_function_name": null + }, + "has_loop_contracts": false, + "is_automatically_generated": false + } + ], "verification_runner_results": { "selected": 2, "executed": 2, "status": "completed", "individual_harnesses": [ { + "harness_id": "will_fail", "name": "will_fail", + "status": "Failure", + "verification_details": [ + { + "check_number": 1, + "function_name": "estimate_size", + "status": "Failure", + "description": "assertion failed: x < 4096", + "location": { + "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", + "line": "6", + "column": "5" + }, + "class": "assertion" + } + ], "original": { "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", "start_line": 64, @@ -31,11 +104,39 @@ "status": "failed" }, "timing": { - "cbmc_runtime": "0.024s" + "cbmc_runtime": "0.023s" } }, { + "harness_id": "verify_success", "name": "verify_success", + "status": "Success", + "verification_details": [ + { + "check_number": 1, + "function_name": "estimate_size", + "status": "Success", + "description": "assertion failed: x < 4096", + "location": { + "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", + "line": "6", + "column": "5" + }, + "class": "assertion" + }, + { + "check_number": 2, + "function_name": "verify_success", + "status": "Success", + "description": "assertion failed: y < 10", + "location": { + "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", + "line": "58", + "column": "5" + }, + "class": "assertion" + } + ], "original": { "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", "start_line": 54, @@ -60,7 +161,36 @@ } ] }, + "error_details": { + "has_errors": true, + "error_type": "assertion_failure", + "failed_properties_type": "PanicsOnly", + "exit_status": "properties_failed" + }, + "property_details": [ + { + "property_details": { + "total_properties": 2, + "passed": 2, + "failed": 0, + "unreachable": 0 + } + }, + { + "property_details": { + "total_properties": 1, + "passed": 0, + "failed": 1, + "unreachable": 0 + } + } + ], "coverage": { "enabled": false + }, + "run_time": { + "started_at": "2025-09-20T19:17:55.319638Z", + "finished_at": "2025-09-20T19:17:55.492976Z", + "duration_ms": 173 } } \ No newline at end of file From 0591837774b62456ad93a5d2c8efc3528b37c2a0 Mon Sep 17 00:00:00 2001 From: edison Date: Tue, 23 Sep 2025 11:33:02 -0700 Subject: [PATCH 13/54] finish json schma implementation of verification_results --- kani-driver/src/frontend/schema_utils.rs | 101 +++++++---------------- kani-driver/src/harness_runner.rs | 2 +- 2 files changed, 32 insertions(+), 71 deletions(-) diff --git a/kani-driver/src/frontend/schema_utils.rs b/kani-driver/src/frontend/schema_utils.rs index 1169b16e3e8a..94f04614a27e 100644 --- a/kani-driver/src/frontend/schema_utils.rs +++ b/kani-driver/src/frontend/schema_utils.rs @@ -74,13 +74,13 @@ pub fn create_harness_metadata_json(h: &HarnessMetadata) -> Value { /// Creates verification result JSON with harness reference /// This reduces duplication between harness metadata and verification results pub fn create_verification_result_json(result: &HarnessResult) -> Value { - // Extract detailed verification results - let verification_details = match &result.result.results { + // Extract detailed verification results as "checks" + let checks = match &result.result.results { Ok(properties) => { properties.iter().enumerate().map(|(i, prop)| { json!({ - "check_number": i + 1, - "function_name": prop.property_id.fn_name.as_ref().unwrap_or(&"unknown".to_string()), + "id": i + 1, + "function": prop.property_id.fn_name.as_ref().unwrap_or(&"unknown".to_string()), "status": format!("{:?}", prop.status), "description": prop.description, "location": { @@ -88,7 +88,7 @@ pub fn create_verification_result_json(result: &HarnessResult) -> Value { "line": prop.source_location.line.as_ref().unwrap_or(&"unknown".to_string()), "column": prop.source_location.column.as_ref().unwrap_or(&"unknown".to_string()), }, - "class": prop.property_id.class, + "category": prop.property_id.class, }) }).collect::>() }, @@ -101,7 +101,8 @@ pub fn create_verification_result_json(result: &HarnessResult) -> Value { VerificationStatus::Success => "Success", VerificationStatus::Failure => "Failure", }, - "verification_details": verification_details, + "duration_ms": (result.result.runtime.as_millis() as u64), + "checks": checks, }) } @@ -111,13 +112,22 @@ pub fn create_verification_summary_json( selected: usize, status_label: &str, ) -> Value { - let details: Vec<_> = results.iter().map(|r| create_verification_result_json(r)).collect(); + let successful = results.iter().filter(|r| r.result.status == VerificationStatus::Success).count(); + let failed = results.len() - successful; + let total_duration_ms: u64 = results.iter().map(|r| r.result.runtime.as_millis() as u64).sum(); + + let verification_results: Vec<_> = results.iter().map(|r| create_verification_result_json(r)).collect(); json!({ - "selected": selected, - "executed": results.len(), - "status": status_label, - "individual_harnesses": details, + "summary": { + "total_harnesses": selected, + "executed": results.len(), + "status": status_label, + "successful": successful, + "failed": failed, + "duration_ms": total_duration_ms + }, + "results": verification_results }) } @@ -129,60 +139,13 @@ pub fn process_harness_results( harnesses: &[&HarnessMetadata], results: &[HarnessResult], ) -> Result<()> { + // The main verification results are handled by the harness runner for h in harnesses { let harness_result = results.iter().find(|r| r.harness.pretty_name == h.pretty_name); - let arr = handler.data["verification_runner_results"]["individual_harnesses"] - .as_array_mut() - .expect("individual_harnesses must be an array"); - - // locate matching entry by harness_id and overwrite it - let entry = arr.iter_mut().find(|v| { - v.get("harness_id").and_then(|s| s.as_str()) == Some(h.pretty_name.as_str()) - }).expect("matching individual_harness not found"); - - // Get the original verification details from the entry before overwriting - let verification_details = entry.get("verification_details").cloned().unwrap_or(json!([])); - let status = entry.get("status").and_then(|s| s.as_str()).unwrap_or("Unknown"); - - *entry = json!({ - "harness_id": h.pretty_name, // Keep harness_id for consistency - "name": h.pretty_name, // Also keep name for backward compatibility - "status": status, // Preserve the verification status - "verification_details": verification_details, // Preserve verification details - - //original source location - "original": { - "file": h.original_file, - "start_line": h.original_start_line, - "end_line": h.original_end_line - }, - - // attributes - "kind": format!("{:?}", h.attributes.kind), - "should_panic": h.attributes.should_panic, - "has_loop_contracts": h.has_loop_contracts, - "is_automatically_generated": h.is_automatically_generated, - "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)), - "unwind_value": h.attributes.unwind_value, - "contract": h.contract.as_ref().map(|c| format!("{:?}", c)), - "stubs": h.attributes.stubs.iter().map(|s| format!("{:?}", s)).collect::>(), - "verified_stubs": h.attributes.verified_stubs, - - "summary": harness_result.map_or(json!(null), |result| json!({ - "total": 1, - "status": match result.result.status { - VerificationStatus::Success => "completed", - VerificationStatus::Failure => "failed", - } - })), - "timing": harness_result.map_or(json!(null), |result| json!({ - "cbmc_runtime": format!("{:.3}s", result.result.runtime.as_secs_f64()) - })) - }); // Add error details for this harness - handler.add_item("error_details", harness_result.map_or(json!(null), |result| { - match result.result.status { + if let Some(result) = harness_result { + handler.add_item("error_details", match result.result.status { VerificationStatus::Failure => { json!({ "has_errors": true, @@ -203,13 +166,11 @@ pub fn process_harness_results( VerificationStatus::Success => json!({ "has_errors": false }) - } - })); - - // Add property details for this harness - handler.add_harness_detail("property_details", json!({ - "property_details": harness_result.map_or(json!(null), |result| { - match &result.result.results { + }); + + // Add property details for this harness + handler.add_harness_detail("property_details", json!({ + "property_details": match &result.result.results { Ok(properties) => { let total_properties = properties.len(); let passed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Success)).count(); @@ -227,8 +188,8 @@ pub fn process_harness_results( "error": "Could not extract property details due to verification failure" }) } - }) - })); + })); + } } Ok(()) diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index 3e3668032064..54b17b024526 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -59,7 +59,7 @@ impl<'pr> HarnessRunner<'_, 'pr> { ) { // Use frontend utility to create structured verification summary let summary = create_verification_summary_json(results, selected, status_label); - handler.add_item("verification_runner_results", summary); + handler.add_item("verification_results", summary); } /// Given a [`HarnessRunner`] (to abstract over how these harnesses were generated), this runs From 961591c20dd8aba1ee80b019b6654192d5d30429 Mon Sep 17 00:00:00 2001 From: ConnorJKY Date: Mon, 29 Sep 2025 15:43:54 -0700 Subject: [PATCH 14/54] Update metadata of target, contract in schema --- kani-driver/src/frontend/schema_utils.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kani-driver/src/frontend/schema_utils.rs b/kani-driver/src/frontend/schema_utils.rs index 1169b16e3e8a..cb4a97ca8f8e 100644 --- a/kani-driver/src/frontend/schema_utils.rs +++ b/kani-driver/src/frontend/schema_utils.rs @@ -22,7 +22,7 @@ pub fn create_metadata_json() -> Value { .unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_string()); let kani_version = env!("CARGO_PKG_VERSION"); - let target = " "; + let target = env!("TARGET"); let build_mode = if cfg!(debug_assertions) { "debug" } else { "release" }; json!({ @@ -62,8 +62,10 @@ pub fn create_harness_metadata_json(h: &HarnessMetadata) -> Value { "should_panic": h.attributes.should_panic, }, "Contract":{ - "contracted_function_name": h.contract.as_ref().map(|c| format!("{:?}", c)), - "recursion_tracker": "" + "contracted_function_name": h.contract.as_ref() + .map(|c| c.contracted_function_name.as_str()), + "recursion_tracker": h.contract.as_ref() + .and_then(|c| c.recursion_tracker.as_ref()), }, "has_loop_contracts": h.has_loop_contracts, "is_automatically_generated": h.is_automatically_generated, From d432b8d8c6ed3c5d062bfaf22847eaacfdf3a2d5 Mon Sep 17 00:00:00 2001 From: edison Date: Tue, 7 Oct 2025 00:16:29 -0700 Subject: [PATCH 15/54] chore: Remove build artifacts from first-steps-v1/target --- .../first-steps-v1/target/.rustc_info.json | 1 - .../target/kani/.rustc_info.json | 1 - .../first-steps-v1/target/kani/CACHEDIR.TAG | 3 --- .../kani/aarch64-apple-darwin/CACHEDIR.TAG | 3 --- .../aarch64-apple-darwin/debug/.cargo-lock | 0 .../dep-lib-first_steps_v1 | Bin 30 -> 0 bytes .../invoked.timestamp | 1 - .../lib-first_steps_v1 | 1 - .../lib-first_steps_v1.json | 1 - .../deps/first_steps_v1-207a230fa6911716.d | 7 ------- ...teps_v1-207a230fa6911716.kani-metadata.json | 1 - ...M_14first_steps_v119check_estimate_size.out | Bin 30908 -> 0 bytes ...119check_estimate_size.pretty_name_map.json | 1 - ...st_steps_v119check_estimate_size.symtab.out | Bin 15415 -> 0 bytes ...steps_v119check_estimate_size.type_map.json | 1 - .../dep-graph.bin | Bin 132238 -> 0 bytes .../query-cache.bin | Bin 15828 -> 0 bytes .../work-products.bin | Bin 108 -> 0 bytes .../s-hb0z3arwfc-0qh96dx.lock | 0 .../debug/libfirst_steps_v1.d | 1 - .../target/kani/debug/.cargo-lock | 0 21 files changed, 22 deletions(-) delete mode 100644 docs/src/tutorial/first-steps-v1/target/.rustc_info.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/.rustc_info.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/CACHEDIR.TAG delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/CACHEDIR.TAG delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.cargo-lock delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/dep-lib-first_steps_v1 delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/invoked.timestamp delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1 delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.d delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.kani-metadata.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.out delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.pretty_name_map.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.symtab.out delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.type_map.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx-748zp5z6lty6dthhcfyiqdmwh/dep-graph.bin delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx-748zp5z6lty6dthhcfyiqdmwh/query-cache.bin delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx-748zp5z6lty6dthhcfyiqdmwh/work-products.bin delete mode 100755 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx.lock delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/libfirst_steps_v1.d delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/debug/.cargo-lock diff --git a/docs/src/tutorial/first-steps-v1/target/.rustc_info.json b/docs/src/tutorial/first-steps-v1/target/.rustc_info.json deleted file mode 100644 index 80931a163798..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/.rustc_info.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc_fingerprint":16292269434704313360,"outputs":{"6322768375820205291":{"success":true,"status":"","code":0,"stdout":"rustc 1.91.0-nightly (ec7c02612 2025-08-05)\nbinary: rustc\ncommit-hash: ec7c02612527d185c379900b613311bc1dcbf7dc\ncommit-date: 2025-08-05\nhost: aarch64-apple-darwin\nrelease: 1.91.0-nightly\nLLVM version: 20.1.8\n","stderr":""},"11332290986239422126":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/jiangkeyou/.rustup/toolchains/nightly-2025-08-06-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"flagm2\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"lse2\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_feature=\"v8.4a\"\ntarget_feature=\"vh\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_has_reliable_f128\ntarget_has_reliable_f16\ntarget_has_reliable_f16_math\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nub_checks\nunix\n","stderr":""},"10408681216163195318":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/jiangkeyou/.rustup/toolchains/nightly-2025-08-06-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"flagm2\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"lse2\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_feature=\"v8.4a\"\ntarget_feature=\"vh\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_has_reliable_f128\ntarget_has_reliable_f16\ntarget_has_reliable_f16_math\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nub_checks\nunix\n","stderr":""},"5465787792652773754":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/jiangkeyou/.rustup/toolchains/nightly-2025-08-06-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"flagm2\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"lse2\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_feature=\"v8.4a\"\ntarget_feature=\"vh\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_has_reliable_f128\ntarget_has_reliable_f16\ntarget_has_reliable_f16_math\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nub_checks\nunix\n","stderr":""}},"successes":{}} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/.rustc_info.json b/docs/src/tutorial/first-steps-v1/target/kani/.rustc_info.json deleted file mode 100644 index 335b1c85021b..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/.rustc_info.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc_fingerprint":2508058078803885394,"outputs":{"9195354629767851979":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/jiangkeyou/.kani/kani-0.65.0\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\nkani\noverflow_checks\npanic=\"abort\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"neon\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_has_reliable_f128\ntarget_has_reliable_f128_math\ntarget_has_reliable_f16\ntarget_has_reliable_f16_math\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nub_checks\nunix\n","stderr":""},"15864581276431044797":{"success":true,"status":"","code":0,"stdout":"rustc 1.91.0-nightly (ec7c02612 2025-08-05)\nbinary: rustc\ncommit-hash: ec7c02612527d185c379900b613311bc1dcbf7dc\ncommit-date: 2025-08-05\nhost: aarch64-apple-darwin\nrelease: 1.91.0-nightly\nLLVM version: 20.1.8\n","stderr":""},"1584335703394208523":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/jiangkeyou/.rustup/toolchains/nightly-2025-08-06-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\nkani_host\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"flagm2\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"lse2\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_feature=\"v8.4a\"\ntarget_feature=\"vh\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_has_reliable_f128\ntarget_has_reliable_f16\ntarget_has_reliable_f16_math\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nub_checks\nunix\n","stderr":""}},"successes":{}} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/CACHEDIR.TAG b/docs/src/tutorial/first-steps-v1/target/kani/CACHEDIR.TAG deleted file mode 100644 index 20d7c319cda9..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/CACHEDIR.TAG +++ /dev/null @@ -1,3 +0,0 @@ -Signature: 8a477f597d28d172789f06886806bc55 -# This file is a cache directory tag created by cargo. -# For information about cache directory tags see https://bford.info/cachedir/ diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/CACHEDIR.TAG b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/CACHEDIR.TAG deleted file mode 100644 index 20d7c319cda9..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/CACHEDIR.TAG +++ /dev/null @@ -1,3 +0,0 @@ -Signature: 8a477f597d28d172789f06886806bc55 -# This file is a cache directory tag created by cargo. -# For information about cache directory tags see https://bford.info/cachedir/ diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.cargo-lock b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.cargo-lock deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/dep-lib-first_steps_v1 b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/dep-lib-first_steps_v1 deleted file mode 100644 index 024be4904569dbe2c19582a923171096f5c21ee4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30 gcmZQ%U|{&q$Ot4ExPZ90C|N%zGfA(g7$m{~07A6{&Hw-a diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/invoked.timestamp b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/invoked.timestamp deleted file mode 100644 index e00328da5aa8..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/invoked.timestamp +++ /dev/null @@ -1 +0,0 @@ -This file has an mtime of when this was started. \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1 b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1 deleted file mode 100644 index 245c877e983f..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1 +++ /dev/null @@ -1 +0,0 @@ -ff0d0f04bcbf4258 \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1.json b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1.json deleted file mode 100644 index 328d60b9b627..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/lib-first_steps_v1.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc":5663753232442302949,"features":"[]","declared_features":"[]","target":5150295270818399684,"profile":5594203621556287378,"path":10763286916239946207,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-207a230fa6911716/dep-lib-first_steps_v1","checksum":false}}],"rustflags":["-C","overflow-checks=on","-Z","unstable-options","-Z","trim-diagnostic-paths=no","-Z","human_readable_cgu_names","-Z","always-encode-mir","--cfg=kani","-Z","crate-attr=feature(register_tool)","-Z","crate-attr=register_tool(kanitool)","--sysroot","/Users/jiangkeyou/.kani/kani-0.65.0","-L","/Users/jiangkeyou/.kani/kani-0.65.0/lib","--extern","kani","--extern","noprelude:std=/Users/jiangkeyou/.kani/kani-0.65.0/lib/libstd.rlib","-C","panic=abort","-C","symbol-mangling-version=v0","-Z","panic_abort_tests=yes","-Z","mir-enable-passes=-RemoveStorageMarkers","--check-cfg=cfg(kani)","-Clinker=echo","--kani-compiler","-Cllvm-args=--check-version=0.65.0"],"config":2069994364910194474,"compile_kind":551198452465029181} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.d b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.d deleted file mode 100644 index 329febb565a3..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.d +++ /dev/null @@ -1,7 +0,0 @@ -/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.d: src/lib.rs - -/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/libfirst_steps_v1-207a230fa6911716.rlib: src/lib.rs - -/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/libfirst_steps_v1-207a230fa6911716.rmeta: src/lib.rs - -src/lib.rs: diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.kani-metadata.json b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.kani-metadata.json deleted file mode 100644 index 30d82e6c34e0..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716.kani-metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"crate_name":"first_steps_v1","proof_harnesses":[{"pretty_name":"check_estimate_size","mangled_name":"_RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size","crate_name":"first_steps_v1","original_file":"src/lib.rs","original_start_line":52,"original_end_line":55,"goto_file":"/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.symtab.out","attributes":{"kind":"Proof","should_panic":false,"solver":null,"unwind_value":null,"stubs":[],"verified_stubs":[]},"contract":null,"has_loop_contracts":false,"is_automatically_generated":false}],"unsupported_features":[],"test_harnesses":[],"contracted_functions":[],"autoharness_md":null} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.out b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.out deleted file mode 100644 index 9be616e4dca7a30362b5f6fa7e81965a287173e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30908 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","tag-Unit":"()","_RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size":"check_estimate_size","tag-refstr":"&str","_ZN4kani16rustc_intrinsics10panic_stub17hdbf93c9f79465449E::1::var_1::t":null,"tag-Never":"!","_ZN4kani16rustc_intrinsics10panic_stub17hdbf93c9f79465449E":"kani::rustc_intrinsics::panic_stub","_RINvCsKJoxxf1Snz_4kani3anymECs6BptHtlECcM_14first_steps_v1":"kani::any::","_ZN4kani5panic17h4df909806de3fdeaE::1::var_1::message":null,"_ZN4kani5panic17h4df909806de3fdeaE":"kani::panic","_ZN4kani16any_raw_internal17hc1dff107a546b994E":"kani::any_raw_internal::","_ZN4kani14kani_intrinsic17h2f99e04063309d09E":"kani::kani_intrinsic::","_ZN39_$LT$u32$u20$as$u20$kani..Arbitrary$GT$3any17hf3724928fa34a304E":"::any","_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_1::x":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size":"estimate_size","_ZN4kani7any_raw17h7b173976ae29b24dE::1::var_0":null,"_RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size::1::var_0":null,"_RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size::1::var_1::x":null,"_RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size::1::var_2":null,"VoidUnit":null,"_ZN4kani16rustc_intrinsics10panic_stub17hdbf93c9f79465449E::1::var_0":null,"_RINvCsKJoxxf1Snz_4kani3anymECs6BptHtlECcM_14first_steps_v1::1::var_0":null,"_ZN4kani5panic17h4df909806de3fdeaE::1::var_0":null,"_ZN4kani5panic17h4df909806de3fdeaE::1::var_2":null,"_ZN4kani5panic17h4df909806de3fdeaE::1::var_3":null,"_ZN4kani16any_raw_internal17hc1dff107a546b994E::1::var_0":null,"_ZN4kani14kani_intrinsic17h2f99e04063309d09E::1::var_0":null,"_ZN39_$LT$u32$u20$as$u20$kani..Arbitrary$GT$3any17hf3724928fa34a304E::1::var_0":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_0":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_2":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_3":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_4":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_5":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_6":null,"_RNvCs6BptHtlECcM_14first_steps_v113estimate_size::1::var_7":null,"tag-first_steps_v1.4ce9f62e88f79162::first_steps_v1::global::0::::struct":"first_steps_v1.4ce9f62e88f79162::first_steps_v1::global::0::::struct","first_steps_v1.4ce9f62e88f79162::first_steps_v1::global::0::":null} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.symtab.out b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-207a230fa6911716__RNvCs6BptHtlECcM_14first_steps_v119check_estimate_size.symtab.out deleted file mode 100644 index 5169efa4ede2ae74c375ac62a61f975bc25e6cdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15415 zcmb_j37iu}_U@|gxV!9bbnV{tL`T6}j+qQI%y_Je;<32#Xwh}W&1N#4AxkD1lMFDr zuAFk;_kG_Wmwp?LhiSC#H$W-_SD-=7VsN`2ou>eZ`P)#0TF zn%sX;TU9BGh9U_orkISCN8+K9h*ezFTJiEWq2feKMNu0o3R+yt%f*M^yZpt)F)JST zMT3g@yNg1RVymrU{^nQlL@X33@dcuhc)}k^DBfSU_J_+YMf;6ER!UHBtwcqcrTqH0 z{#eXk0cqaf#Y4|q%CCN(hH<&`Xyio~r|H2VRxyA3d+)DnUDBeV_!>hP9#wJwArcBJ z%B4Qvy^lWr$dgSU_xWRimZ5|dNR-Dc(NJ{dQsZ9bQl&4u_>W|132asV0J=;5luc|G zEAGo`z zhJDi2`W40%Qoxlp3=XvP$4DQ0o)rB`<4PgNebxCON&c&ht7xzerMR&B>KZ`B(ek3O zWvg|yakW&db`8U=NO8y?i6ANzy|z)C2LBN=QAr*EQ-&xi{2+N-jd%88;X=(D*AL@$wt9#hQS~n`!{ZTSj9EUz)-<88;F9 zkM5gmLSU_?CirIKW*UAaq}38}aZ3%bHX;BaDm?^mF>aBVG74`sZk6M2t7&{R zX8Y|n<2H=F>S8Z9l&htBS1In>Q@~iX9KnacD2*0dihjFsJH}sIFF-AC{W5>dUuq$_ z#g*E+;%yMKkQO37k%+EoXqA>FDv(>;*X!QwU?^-USvhwWm50KKP^4ZsR21{aD(a;~ zg(9Iu2uu8V>uyA~Cs-Z{Btp@Ma$RnwG2qSBbB$bOEUUwiJ{7mSQED7Tg%4?>k)%zKHXgTvtP>6S3O^SoWW*!BRE=Q(h_^oO4grZcyE$-(%b( zsPaIS`cty7>^qJ=oNH|NRf= zcu0uheei+53F%4h2Nky$+z)#*at8(Z-yR|IAP|q}j~I`D{ZX(#0%Od3q`x$iJX&ZEpjg zVqQh-OK}Il;QxCy2A|K0#iBC2ik?ok!1J{JwDC0YxPt%||06Rn67?aF{FFX~;?^^Q zA*~x!&*;w>&j_50%GZwVF@X10cjn~2$5piln{pGl;|Z!iIDRGKou>kgTm3|tUVvXDv6>LR6=^l z2nk951?WHj&J2>GUTh^$B&(I)%4h{xJBFBfNvsunwRB`4YR|&enM(a)0y?nLV5uJ@ zrFyAR3X;x5(us+MVlS1h#PTwWWMYxpbYW4@kTOv{YDB^FsJjQDy0NlMsJ23QclI1m zZGn1Df6jOgsOIk8gzCv+8BtPLFBT^lNsH@oBQB)%BU~Sr$bbvU*zU{9gQcMeQ002L zQ7)tnBvgOaIwML-8^GESjHI>E+Zb(t8$v!B3?B_*NP0I?;>1ogoVbRvw$-^}mfT2& zv5Fu*gQ-HVFe<>*#65~oBiQrRP~m8aaMDQj0zLxsg8qW>g20R=%xLzXOqlEz4Fzlr zdl4|SfP7JZ(RdNaX6^}u9LN4!15#Q%p1mZYWb;eSV%BWbIfcuw2fQ7-B72 zLJPV*)7vwnJriQ55p)XckQpkaPGubhmV_c~J2IoA1kWV!bk->&SYl@&Y$2DRotfU5 z8J!9G3Qf~2OjB|*ol8Ws*~`_5N>lsk9M*-&Cdj%ly$dtC5ZQbpnz#P?MP#X-rnBI#Sy@=sWQsoV4-Es=*tDF*K6)~@1 zy)!b$Em^y*WPOMjP4M<%dLL%=A>JnLwFFsznzzWtjnc+^djhu*^CmX1I&)bx9%}1@ zQQ|Nybll7avC7?$Fo@}cm@$YXJnG&?bZ@i4ndsUGj;+WZWzjYO4q^HbW(*-ApU*CzZd)uNq$sL?-T!iHu9(VrP}YYQB?$x1xS0Nm@$eZd_WQo zB=0 zp2YM?%$Ou4e?j2mY;p}?Df)9ZMWRXe6sAvM#uO?0D}tY3Q>(+{$sO=ZHjUvkfJn&G zm@!StJw?EiYY}4%`O~iSEX74`$|uu?9LZfr!0?zLy=ZOp&on@E=s8h z%0*0H#EeBm*_$YFcId@%-ZvCw9dkXUN{8mmuQBH1g)55~+!ot?L`IEPwy}06C%5ck? zu*6+sKmIx^wgNaCdY7>}@o0H0U^&MNWiXl4pD$+(n&W65ZvekWtXP9K(O9bnBv}5D zyzLMT+~LNo2Ju*+0eb7>>O}Fr!CJcRkRFV<+~7^v2x6% zcQ;G;OO*M14{MHVE%5F|+?19@Bf{Oaal^HbM${AE>U)^6pWS?mqO@zz7bO?dLjGQo z%|}!Sj0Joj!@@fbk1WTtScc1M{C(zo+}rxzcwUpT#Dj@&(|ZGd^JV7-L$P?m7f)Da zabN4~?1Df`D}d84rl=I_N8HX*QTFO6QIpl87>--?g zK#Q*^&J=SMw0u))`3669!CE30;OJ(@q|kGv)Dt1Uf`7;w$pt!xlWIvFHb93pQis+2 zqYKu7uCtPhLMX7_QD80qm^G41ZXN%GB^{L1;vHzQ4bR)cBjy$?eRH$aY9l`ktsbHY zZ1&e^+qS`MGxEGota}ggNomvbPb~CV#x^-xte{wai+{?p@$$-XgT3Uj7DnU!hrOr; zQZctCmok>HSR|WLi$s1D>t`y(1={AjuwjRQZ0AQZSx?8zK>@O zH{TB&#{3HD?(RGdsQwd4|>P9u*xyr1$f zStFSrkMOS;DV0tx6g%dOrX_w!YQQmm^5?iSQ+iSKlgv0snel66Mx?~g`PWF{LF7oN zu$P_09G-yOQ%tz|3x1jv`xCeu@}6OJ%8=_56>z&x&NgspjR#7xLp~X<=&g)Fzb1E(EF z_UY}p(UD&#cASnX7F0V`H-DrBb($8`5Bx`*?T+Jbpc`~hg#+8G9ne(zs`bpScsi@1 zT#la;+4=bBv9b1|5?QSWf~&XSvb#kdL^s-EdGv8ug)YOf!`MuH)UFp| zzoK{L#t*WJo&+5RqpAc2`s|sMA5%LT4W9sm#tWk`N+i#5j{LD|Z^)Ocd}c~M@ul{gyulOYlT!k2PJe<#`+ ziEcPCw>VgUQ$S-J35qRG5N|^(C5KY{Ji%XrcfP&Zr~gq#kAO)!~%5)M=B@vWHdO|z7A`b zNNZnHhyF5aDG%0KjL=24rk8byMzOM&N~^K57f|#~!IF05y$N}52$|+`)nzV|qNl5r z@+BKs#!~69RD^SCji7CA0&+c`l`4LWutFWigZ^+Fm%4aAPSP6ywvL3aQioS@`FRf9 zRnOY1)e*drTx%#np!rCSQkGy4Hjv4vYi_2 z$=R%q`&j{?=;OH2kMc)v{vmXF7tdBXk#DQx9g6I{C*@-KF%`LJn+GBI10fu8 zMRq^n$ar6!3K>)x(A_~w21cf``+*eyAqTn5oVHL_#*ImGHot_1NAY|t zXY(Uj zF^Bsz^%YcTB0lKa$?N$vIb*grMX_}ZQ}Tu2vAadFg>JOPk~!fpequY>W;&tb_0C#c z=}M<{)#{nwfa|1m+E?mqa@zk#6;iV=&ysdut8;#SmP=hL;{B7Xfb8AjlsXq1zX-q6 z>OAfd_3asTKBo)BEPp&p7GqB3bho%;#CUnZ(jNA=!}Ejif%!dEWZy|&e2Z|T40TSb ziwuSOZUq~NX*u&#CDb`g(T`Q88GZKr$d9Q-M#Eo*7M<~&R53~{vQC=t69jkAUgfy1 z6jpZB7VsR}3~eolHMP__X^xaG4*nD=eki55(nZ5{bx&^0rzq&f-Q8*E5~^p{$?FfV zr<=BrSG_(UpW%JUBHmUWl=kvhUG>a9(7LB4jxs&8*HCc(mE;tgdcjOO;{J~QG zAno-FkT2?yeq!>3$YGAiA=)x71AM5qoM(%vcjS+O{E<@r2A71OKT zsU&o=w(934rFf#aYUMRWTg`K_xMB zpkB#ab0B)QM#uZ@RFw`8;gw1|XqyW$q`zh^(AIJKX=}hA$9t4lwe|c?#hfd(-X$+Q z&G{M%*Lm6os>W0G4ppy7twiZdq{~72I-b|Gjr=Z!_!kitKMACaB84ugRMc&8=9&tv&Dz@>hb_o)!pz+YQn9Mv9alET|6tkYoe zPN=XqT?O>*sf?{8`+dlLPmC9#y5AYPPkV=F)5V}X{MpLd!nTpH{SbCACG3EY5)yJ%{)xu@<}pWJ|@?Er0t*`x#oZ&^cO%M z!*hfJ1~kcl`7EXBQSDu5N_Q1fo)a(x|2#GBxV97HC{c=WFkZwBjrtM-PNb|DpR(dO zS%J2B8biiu<|(q`Yi$?5o0hhDlI-|O+s!@qHwAIVuqX8QK!1zp3|Rr1q!r(!v^%Tq zfp(Ohq&&w8f`6A9cTU@jag=$*INJ&u)fsg}Cp_)(tfUHvj>GL-c2)9&wvQ)sYcgs^ z@*lPR{E?Pfk?8F>&IbJVAHBLbte4lYHqH`L7clj3Icd4OgX(Tq>voiz zy~Hq)CwscygZIS#zl?nVpY?$vy;C%^QZs*)q@ZmM#E==9IlzUP@9&c7t{*Ah*Y!T7 zyPD&d&?A5zif51uGY^{N%nwQFG}v{3bP_aDqBHkIFg!JKnCkX2$b(oh@ zx<68xQzW7J&JaixH#m0m&UYPU`cY=!otijh491UE^bdLR=O}c8>RQO%%kcUgy`qVu z$V$BD#tDO(8k+0z%31$NU~RyVAL;YjH?~(ivx&t>sHU!NhxJpQCp2ZssQ{ z&Bd73Q`j%h;HmYROZ+RNMYKK^x<2FC_hBK^zca#rZuc?qTC6hr|0-TT%8jF(e#WuP zCEl66?m9*n>b#EgPdzVD-iOiVe(3ZDDYV@6InTYH6k>|Gf_mO`eZli+SEZhn)PvMP zpI6i8Rjv~}Pi%SUSxY@@Twn4$n_)fmtaE+EbMAE*%#GB!!F7`7Q9_Zl<|gWV%k?$S z6Q>EVZFHGisB^RH6wjk%M$^Wiraj> zeRlYTOUQbC0{d3ce*kn<|W z$@+7z@~8ie`Ge6h<{$sZe~;5~_&;u$-;8_)_nDlexrZ_gkfBJY~bBLQf8r#}#221UC~?vHi8V@|}&?i$_<_KDT32 zi5xTBNg~&X*DzCuSnlZq;hvp3REy z&u=*VW|nQBGT&E%AHiM`5yq!a-^r+ZOT2bj^{9exlpvUjMpl%iXrqJx6C*3i!n)~# zz^>Es_zWFJdCAoF=5}Jr$$HUok@j`RY*IPLiCG9mt%2ZIVm1@AJ!0M>Flu2cTs^*y z`_t=(Yp(tL)zRX*SFoEd1T34Yf7gzfS~{7AuI1aJa%tny!-rEhy$Lb07$<}gN-c8s z_}SwE+@IFgO6Mh=4i)-J%t5g47gohTn#(1v>vH&X`k_c4p>8?@*n=I8ML#F;VKTj* z;!^eTY$(0Tf7Oha19gkWiMa?ByZpfMME0QiN-fR>-mBRs8Qt`Gz{J+-&ppq%IfzV^ zIr=`YzZ1hq>>8>|S+ex>I58ig))jZ2JtR3#U3u8)(5kIfvh%(YB8YRYYv2f$G9C`m zdGf@ta=XG)##cg=iHS?6tceaXd(OsDG_y)gzwQgifop0PKCFeJi2*y>rL}S|O>hyJ zu77@B%v)9TqknJ3>TNj%&&COHggW#~eVyu&Yb~Pr$9~GH6y6ZaAS8g1cP7`d&Z>cZ zv%es27D)tS>1tmIDPUw5Q$BJ{(=~Kbv-;8S!Tc1NYg613SM*wU=Rqfu21Z^XV(?D# zyV%FGGEA&nCakjiaVuZxyoFBB)phQ8 zcip_+RHR!f#7AEQ?Em_(amfN9n~hAVQ^QR9VuaG=8yK#}pR^Tbl&-tu=FUHpF2|(E z+qmXZ*TuS=H8H$C{*@Mq%JaFEU1nyQ^36hV;#8kth7z&AlG0Y+X@G8OT6TCpgwdvQ#&pwzKnXXgk` z5KDoPmy}F-ue!~azTFh8>~=e|$;hk*`mPeN9lq{y`vc|r$kbic#ea5V@3pvtL`r~j zEu2#sp}z6H-{SLqiMg4pX4sOWekMxY^ku+SW_-PyQs&b@rebWbU2I6|YF6*W*2PQixyJ_*RWN|Wm_wqxY!4laISr>oFZ*R96pCD8bikjK-Zu$yf zhwFAOb}WKMa^tgH(eW8b8GCM zFX3|n$@Hqz2iG&h^L2e^2M0a8Ge3EPSdCEB%(T1dYk*yJP}G^cm8nFgaSj)!0yg0T zV!XS*s?At_Yl6^0C~9VFy6L*Wd`_-1k*2Y*}87J9x&b=_TOau z11!jN*7G!b&B&3OmOxXZc^A5LCJ23mqGqPoO*a5`&2MEfhq8w*nbzlxTu@XMSS{@w zotiBCZ0!WG0imdw8FbS(0u#Mv>||4|CrzfcNyl88rPXg04;;Dle4@2&W(K#gDKK*GAyX<7njp-8kyAC9QkmEUVGfK8HprC9 z=KZ2u0Hc0ZZ5$}Mw?Ff@#~Hzm2Il8^GKkGU$dT9~XY8ic551VTt6DVoA9ULHegDgq zbIZN@8T@ohYEJO8O^b>2Xz7Wnp&-8_1{SN{%qFbhU&(onOcOYFE%mYMzEU1$fZ zXq)A1!kUSZF?ExuX*a*gP6*#Kw3lcI`vbFKVq|=)9;sDoe(R0Kg0ts&OzMLc1G8mf zTQ+;-5wbABNbw> zU-nOIZQf{sS??Zm-?wxJDuJJ=D;6Qb5&o5I2C|-C` zYP?Elm{=V++jI4ndzSB%T!B$P)Ed}3Iyt+{y{a+{nB@cw!@Xkwp zQu|qUV?f-mYeh}gR@OGQc0z_ z_k~}k2vSkqw zOzeMU$o4}ml8KSIQSWcIzc=M~V%NX1fla#+jP<7U_VKl`-Llose_KG{_8`099XrD# z*dli+2t>i*)Q|A&5|<@fZW%}Obk>!%H|lr_5_`aq1t!PWvvH+10w>s}jGUd`2sZ|H zTS`joMX(Vqno<0KNw)v8f#)-K_jhsav2K-(MnGz_?gc4G#4t&6$krXxU`v!5UZHlU zh`9XaxHX(P7Qv`z=8myxm5oC{>X~CfN=n2tN%ERfv*Zb}WymHVV2;baSNrdere!}) zQu2v@br@j0zFHze%Ew;~j8nwdjQWD7sM$IxYiD!UF zK|nusox)119qDf;o9oT_J@45L6R8N6>~`oyned#=&`+pLd2uwXSo2A471fxc8A-0^EjDrZM zyWO@a z(xuj2nt+!^&*dw(W>dI%ugA?FJ z%tbKjT_cfW$B;dOfH(a{M-;|C9`6aPbZQNZiGOAwBFKjcdkpV}gO8X=Q=B`ybE*cyLRE-_``clZ;D<2iQoxXyrl zap$hkfcO03t#5_BcdX#;1 zT=rRqbHsTngrBLs(mPV=0)pAS=Qzv1eMRux>$e2Lx_E`{xwv^Gb`cj5lzR3HyOl2e z3-&ihVd64Sa)2h&Rr;lSuI14hIs7aP70Sn*Ef+9KnHV`zQ!i79t+d*ef8lsSoNdz* zC5fwG$ryZWp2&`eTtWJ`G^Cqj*x$-sR@Z!brGWQ5BZDYIu(OAU*Y~bhUf0u-w={3m zY>>glbqyHxL)~rdC9VUxF_w9_@9Ug9>eiv8#5UV9@xEWK=c&wSYe z8EO@l_Y!x2d7o;0e?Uk@L}*d51Gi`>OHEhw5*5JCZERbV8p82umQ{q#>hb+S)ZEm1 ziMzl;O~0M+N=;CexwGvIo7W*{D%0pC?g5)GRd?1dWp~I#w3LO+L9LZkwyKx759~wv z&4uGnuBeTBFCBVyh+~Y(w0nt4V0m35kv_iV0!kmXO^jvC7*w{Vmv{gyIH2$l4>$k) zh&gllihLuCsZ6(*r~-C$apKhRalQ`It`w2a?%Ewxwyu|W2<)}S)YRUk$KTDmzjl4P z;G;?^)9WRwfhl?HUi++ibhF9rO3S6)U%ykCK`&7QtlfU7w@a2sL-GkD|3RzqS)<0z zOIyRD!%nMfSk=vJKeDlxs0EhUY0}p(C=zKip>Mr;xuA4@uM|B&aN=~yoz^ z_7abPg;WMPal8*0O}i0dDYEU6Hua>&y~JZ+M;{PsY51{=C*$7O^F>abr81#jq7K;I z!MP%DN*bgjW=3ndov@msGO=Fb2{5fYxetAnhQ9yOuKsM5d;KSs%{x!j1Eb!}ROWr* z(NhF_|Kmll#>rwLVFugOW><$f*RGe}xOwaLor=5n?pHpjdRSdk`{;4qlX?%Lf$8Yt zbKd+Hjf$G)Je_Ahc`53{i8hFRngJB55%MFwI7Q} z6fg{kX9z~E8H`iS2)62^2EE=~b>HSM4=yaGDYG-ABw7%RdeWw6%^t*aCYUf>_RGE~ zaiOksjJD@F&izHgCY!(C(+`BYwjvlyT_uS&1fvFM3=`r76C=lD8ruic^9SBp1YQi} znE5qChQV&r4vZr~Ar|{_I$=YM)hh9)cQN>jWL9)_I+^5XA)#t1*YY8x18dh|Pv5|Pq#60}Z z)Yle0lwv8Joa`GWM%I-&RX(@qdd>Zep3>quQUhy?XgZ zMDiWrb?&yknl-O=X;P8ls&zJA4dH5FCdnR^$wSrG$ zZ`qc<=ffvzw9ob7q^$0z>Ph0l=Zd}efb}x5 zItSVu)4~yXwPsD(j!zLh?3uwICJP8rV0}zXpgS!f&iCVaov9f=wv~O26x#b}?W1t{ zeZcyem~xb%^B1F9`@Ogt6Z8&5eHSj5=jp+RT)QCN@VfX^DK_;fs5td*ht`~H_)iI^m zvyAq_D#t~mwmSEZkNw+ZDfZx$D0*)e>%cr{Exw9&Q2mGCi@0b*5!hQJ}!B~2J1~GxE-Z!JX{OU0ddt)fwR|I1z zos{f11RK|6giAJ`tC9D6TT&P}<~shh>}m$_ok@~6I!naaOZ))#G|_L%1+lsEug(nF z_LqCvQO^spSbPjhNgjn(yFu5ze zsqq6gc|3ipwbid;X-6g{Z;2fCG0l`rQBwn zs~K0~lE+%=UB^Os*ZNn{biXaO_PwkG>Lj zuQ7{Y0iNwUjAU?NNT!HM{xiJMG|82gHPq zZh`8~K`@r;N)ikNLxh=gnHYJ2hn@5+-xc2T4y&Cvc*G;VhQV$(57@9WFY!UcYW?Yb5zGA6@fhHpKJ3vS>WlDxu zi=?F-j1^%qmm`#LBAk4kVY`-JPmEQ0KpJ# zxDdfug99lU1f!l*8e4!Z#3YGDOxD$@rhDNicEHf6XIFmGnG@ekD~`Gsyc2|*Mi#*k z)A?dxUkcKOhH3-*f;fENtskoQ{s2tp^1vMT zhK9e}{B(ctm*UwCDzP&*+ILI)z2BwE#=G&o*PD-~Fr6y^nHD>Pt%zxKXU+!S{jKW50$}mt^{n(a`*oJbV0*CUN#nU z_V>=FGMLVlfvIW6?(w>Ku6vR@38r&ZVC(0% z#>OUE4}B_E5b@iZ&z7m+`p0|Iv^>(HR zY->7Go_aW!%3wNI1BM;Y^9kL#yLKdb@Wi=eNp(~P)44jZ`I)tWmcfaYg6}?sExF1O zO=U2hYXHkSr7$;(`}jArYfse!*R2+(GMLUafo0svt#R4An7E=Yu(o2Cy&#prbiN9h zd{f_-qfWcm^1X`i4Tx_lqcWJzwSXD^K42trkHh8SIxG1;6TM4R2GhAVuy;|jljCMB z&j=lpxV!F}&uc1!>3lUXaylpPQ!0b$d<}~2Z+}W`J!ZHZobV?ks1PliI|@$a+vGKpm+2MvPt85NuOPudnAvB3@=+ zvO%PLALl|p6{!o;@Q|JksCsFXVRY}Y;`KU?>$uO%Nx^?X?d z%#?{0UVS&3(8vFDUgk-jCP(-EL-)MRGHKeA3|BcbCPtn#Wy*z<=-g7!y@JpB%q^GG zvY0Vuf|+6m2y-S)W-1bs*Ew>F{|(1r%_6&of^66xuwY_jrikr<%}k8UO*a3*$xnQ= z!`lbnRoveAO&NNJB@-hny!#BFv4@oQAK@mgmE-h%PZ`dH6%!*brBg(vq51E1=Y}Vi zp5(c9V>`5qH52>(;YCMIOW?PKn^kyHEO+)<_Pin$MPrktbDs>DT?i*4S@vQ=oH*cuu|nW`$W}Hkg~B9YR|TU!7XD zt81nH^%vWQ`%GWhV$TVCCP$W$Wf))(4hR+{S}Jt;d=~^`x&Nf3 zToH_#n=|HuS;Cm(#$?IvLbY&acLZZOa|YpoV2DW26Tzq_b;H~-4=A7)lO?--*KKj7 zokPFAIH=DR3At+?4h8f^FqQ&J$@w5yX-H4^J9qC0#*pkYzka@SFU%X0#TG#n>dU0b zcLiT+hhE;xS*Oe+$|-$a`_(vHK(Nd8c75$;QvBNtCbHOjE(|h$5`hRteNxR} z5ZjrUyt045IqekA$_JlUq@U8Wf42R!zQ4;BH@K04m>Bs6rC#V|yC8mplM%9|KctP0 zra*4N2qtx??&E}9+l#c6=B`bAn?FMv??5nWt3VrvAeaol(ed@yzqxtME%teJnAeLT zB^8Qb=32)-?r7pW`rJ%S>&c7<7CW#IY%|S^2t(N8kHf^<#I~-yp8op6*_F%fp|9*@ zV!|0`E{Wv?uc&h}Q+C@oP$QVw zqIU03dovY!bk4WL6nLoCx@udi_!wMO1s2J~l9PO1qRCwfVhVDY~9`Bl- zJ-~J`G5fHQ(GFW%`LA|&bQ&+29lUICR&r)fYCOYr*={CAR$)%Dj9Z`U#O=K0i8uA) zQss7H;aCI~iS2^Fe>i05__2#nHx** z%OIke7+dqbqj^8>-bJ)?41%rc*}I-`-{Q)nuZdkBqcdxvWn!7wB~Q83Hola0U4y0N zkB6QXEAMxo{ire}is331$Hd5!cIrO&T>qg~GpW|q)30bTXEYXr#lr8<#xvQRSLJdw z>h(>C0wy5X=hs`0*h?mkD@UH%c|!EuGbmsp6T8LdJYmx0D)eCDh4I~_70<2ubF;3v z@QVOTVq&)2unUi}B`#=SPZtQ?In;c9(s7}Myha~Pz{yOEEL+<<_NJ(=(`qa7FMrz@ z*U=M?C18nI5_XhGVbbIseBpA4FFoumyPS2pZ?5BBB}NO83XFUWg?!IsTZf|mYMIOn zcQ<%GjxthyYAH#iG37>fgJHZlSLArEEg=?a!1VfAm=?I1x5B4J%V$97z zFluh;*nVs$mPKSTS+bvgP?yu$(a+%=-0L|K(H)=&uY4asFqZBtNgPBl)%t}-gNH8L zau{R;ta{=Y1*1zAg0alM`otjwa|&0=*4%jJmP!o%BqHxlCybJZ5e(5P*$76>?GSbt z%YvCG2Vv<;MEJM{_S^ofcrw_@)l&#FQ7(eLwR|Y&!JUg8@1kknHEy?tndk_Du{7RM zA`ih>>fuNnWnyGMrgl(9B|RUQ+h*)h`F>hl%kMW%HFTo`MfcgO-ahY{-}A~~d9Bq8 zxV;OQGBfYhFn|5otOc<%bqrYP8_jUV?MjW__ENM>zAx&jMT4EG~t|j{5oCv+3Ng=pxveDg^dHI%(C`Kdz@njhkgM z*^lW`PYOFzMZgy4unjL;5!|-xqx`*sOUWKo20K&5!1`{->B~rXR;tT9Nj=8fzn98j zXX+F%|CA)hm7@y_`K0S?_j|buP#Nq@l>pOg{bIH^D#I#b_4Yjfjg?!e40fhY1C!Sg zw~8!m)lb&7KHkS>$g&3qJ5y(Xy}ZZG9&S3`&e;-Lyp%2XD>XORnK}#X*yI_>%U=}U z><`!@ly~sB3zfmn)Hz^B0v{hcrE=>2!T1VpgEEC7DubP=^T5W2hgxKL*2K?Btb1O6 zA^JR(!OqkLV0pTCBP84+_hIE#H)F2fV0ml>J5v{dZTu~E@9L!kk9Xt{g%jZ|fz;e! zXX+BL3)+Y7g=o~5IIZK$bk7xF*{Xw`sms7JpE+C71?0o`U| zWW%gp&~e&N7@1sVsxN3GooYY>Kmp zvX$r8zV3>xQPgi!V6aP71LIIDKjk$uLwJSx1re?d&!*q!`^TpW&ioK#m#YCg^I1F} zEl!~f;t_&T*Rw+U#A5`bMnggd zL>+=r+gnIV>Is5Tb9;={VUHk+s7F}WMOR{tRkE*X-HUy%#36QF$d7o6V8+^o!FTLT zTlR3bK02SDct_|b(STsoC_+d%e-nxd zou*+k6C^`tDs*O>F^kc{1j%3Np#EB2sHK9&bA)@9yE+_m2-Z+iwI6eg!JJ%ZY_u7) z#k3Zz5nH0s3P(^s*5yLn-;<^iKHP|Roux9?+=$uJny_bBGxi+YC)dVw4zkdN0{iEe zjCndm`aWB2s9pAqhZaJ!rL|(FQZGQ0!!;RvIXuH8-xsHu&dD(>j>y|l%b=w|x3`$r z4pz$UfqL+|XL8#H^tTGeUG}10uou{NEDY~Jl;K3dm*}Z6^<#btld&Ca94Z0-Da1iq z7Oj+ag%(Qlgo>xpoZ;c~RoXk+5bYCfm^MQDLi+|=%JbksGy@ZZ=e**W1SUt5#H278 z*ef!^%rJAz0^1CmYO=7I&%9x62OE|4m;>gBIbp7tC+36s!mf-zwharw0j=xi(p%f*ghdDu}bAG-si z?@jDJ3~UduYODsUh5O?PR*yZ!8nA`5Hmn`9WppAMf!bUlwhI_9nH<@9s3TsEowVAk zf8iu@T0$I4Eg#wsv%LKd;rs_+hfmnn9&)^({;a(I zx4d$H5;Zpn=X{;8?_MDN{h{7H!7%?cDuZx-2-uT;uVRMk0(C#( z6506Y;iFUr;ru6HpA9v~Ypar+0?hReJg+|1No5eu4+HakY7)DI%Wz=jpH0~_?^R_` z8HDpAz*^l|eZF8CduF zT93Xd7m<3GrHsZEvO8_`7gkD&t5vPN`PJH z=$)`6=^%?*DuZx-9N3;Ly5rAHw%f|87F>BIJCaUi5YB%EX5I61dyz`-)I9G&f*#1p z(jXAde*@+jwtH2BT+n&zAy4(_yqz}NXYWwHv?Nt~@fmifim0gBa4&oZ_DAas`>S`1 z)wVHo!E>jAy{RXKaQ+7{+Nt_YCbw>DCEe%I$*A^Xc~23-`JceZfrPvPskuQo{|mvY z%F7e6N{39#Z=<)AUc|LuqxE3#u@6`;)`#_DAF%;!5F5fiVZ+!6Hi~`5#;`BgIQA9$ zhJD9=U_T+8|IL&u+mV2#*+;yJZ-&%We9~z#{>2;6Qd4ZJB=nq^Krm`JFC5|P>WuXp>dua{>&^hH=&1MEH|>wirEkxCg4gg( zzbF5XPZVtmTImnQ28H0jMWi|5m_=15QVs8q*F4!=6E%LNVG{MAc?QlPrvKg~*+I#f zZjh!$guHV(-4(^XYad(MIfzNQ5R4%)V9GPYH(leBlDO3GjCP1gxe<&yXA5}{JWNa- zv&g-@BI!%r8FM`;_O;b!ZT&mMPFQ+C$K*vYM91VqFqV$Vt~(Q0Vx&snH^)u4#f;?( z)1>65U$Fnj=RZA;2j|84@aZ1MkI0&O@u9~N2!`l!0tklaae@d&Jt;p<-~zau)GUOh zb|oJCKV4}ySobrRZ>{X^PZ-oGb&h?w$Ne9*haaM7GjSfMyddO03%ZgJ9Fx=6l~-u9 z`m|xBc%ED9t4*k`G#fv_5C%)GFvviR`*->Ak8TH=cQ3V6%596go@2L9Vh+N^c`jXM zdvoyNjK*aj6;FgI!LLz=;S2-}y{uT?uONgs&;J+^eRr9clmsn@W=A_kE1|u_USa1oaBQGLGKsS9ADsRLxcS z;T%p?md$rqNhku-RusAVZVOxX^TOnYwcMQI$-f*sFD!F;8+vqS5AB$q1*{~N0Bhp> z;A{UaTE|Z7!Id{d_ly#Tmf(^B#bVn6O!e7)CDP!eOMxwz%h6$~^5ATty0<4!@H)c- zeKI;iZ;!7U2o~vWyBDDhD+wiFA?!_o-((XcQ)bl1?eBWAa&zzH5XAw8&fRry7Ki#h z-481XWnksnv#b_ws9$Tv{VK^Yb*Afw$;SC7T_3&freJdRx2#CAi4EzIqxaA?wZK3GYt1or!I zm_=l4Gq0fG=u0cRTRp~))_W+ao=IM=_px*BmuxFoNvHvPZ=Di6wQXjWMnZw%f#peb zYS~~Vp$_axU&gslpKL^}efjB&96P2a6&5`*uL^j)Y~N|ms;x&Jz)C^`m=HU+sCfS6 zzz&I;8wBG=fT)Oo%w+M=S!rhJAq#42*kL8139MV`;+z|~s*k^{A1{+%C;DcbyH$Sn z+A}k!mKn{opAHzyw4X~^F`25U03%l6vd71FPTF3I+Sg?}N0rseJq)DsM%YM`S71wRj zUrAG&7giFwz{oL>yaA~URuXFwj97f?u2>UaPvTTQOif*${ zD&HYq`{cuef%OPR4cOq_89fBM5psq{YF{F+g#PxBKXDN?u;Z`K#K;=wjU1ReA*flb z!zERp@@3BvcxBZ9*r=%e#Y>uJw2g+u`moS7)4${XkB>Zz(rfW`_fegW8+i8(GYogFnieD_av%gBmL<@{czU(alDc>jtaCuhRM$m&v~#C6a_Mz}F7 zIZQ!ci!8PtbX5xJzl>FHeR@XZAFT;LM9~bOIrVTuD6I*^iDq!jj87}KF)p1n6#0aSmX`+fxT|6r6Zb3ia^ zSMi5^U|ZY{w}*yz1W&FX$#mbk7Y67@o*%UltRT9+pf?KKFwxppNx zZi!s5MRviW&5b^YBd8+>5A6WW8}G#2U_}rQ&rn1+`NC0Cw|74;XrPxQ z(-*r%WgFKXS|fQ^@1QV;oyd{$ZC`1kIq;x%`X1SbMZwB06gIcEz)@tQ3LF)tYitwA zx9x9O`hindL}Ojj)_~nldtgf1ieS{i2BsuG1f%XDz+B@G%s6hh)2hKpwrKkK*M2_* zlBgB@&s?(w--`SFoolusvZnTFm}>$M3^CUPA{b(>*^Xe;lWxNU@IYKnE(l?%x5<|O z&JMw#+lmGDme)+q@ZZ?X=a~9hiP{8FG=F>xRDV0<9|W_*4mf7vTYHYdDAmG8;djm- zGP|foT}n>R4#BwC{1Aj?R8C33tD+I9TdXL2X|t9-0q6 z{cJ7^#)x>hN#I!><_kl66s;Y0_d@V|EEJ!9Di((C#E-(ddwN`*ezrC}CQUyHo4z^a zxgy|H)XPEr`O3UVM1jPG*>m1by^FSX&)O(Gz9}J#76BJy7s!X*<~g$ZzOPr5G`9IR zPwD)xsd_iq$_oCR7^{W>_Pxiq2Q8n&LA`q{XuDvljzUQ4U=c;zjb{*hK$15u`6p_r z5`wC|z{m-OOsNdMydfGG`F#Pt=Xu^c6n)yt)n1yBc_jQ?qKt+f2Uq+QT&D(bK}?hsU1L%YQ4;qM?^Fh*&1uK4ZzL;yC|s{q1yB>AmU0hqjL_;t_c6rBq3e z1IBjcu423T!z3ZiBmKSX*}aXw9`fdI5EAVw#rsK~xoznT( zrXLx&PvFqJ)K}Wd5t20ip}s&`B|QO@!Ol=yx|05;6K9?7`JDO_%{f`^AagjFkYl^{ zU@Da_uace!s+GOmee;PsdsYX={uw(=pJA_Yc;VuTk?8n93vBS6b5#bB1d43m8>&|P zQqf8*&-@^|PDZekKCvRkc-!~Fuf47+);0FKz+AIZhZSPef8XaH zpCo9uWSo6oD%fC=)A2bq3h~E&KD@vaBQ^c^q5koSqNU&>@@XLZqbE*Srsr+t`e^X> z%XLC@ykP0B%lU%sE~C254W^6PEMP8;qNU>3=IsN$*|m4`=&o(yxx3dGX6C$7qILxf zS{n4NbdY4-7X)0f9hl`^#`1eB8N_}jHe9mpPuvMSfn_tpgvda!hjqUvn<~7x5-xB& zd7x7;0&_V106sDNC{*+plKsojm>Uchs$sgCF{DTEd)>gj_=1a@J##wl=l!mcUCqW zrT0Co|H=+k{;M5pyA2r8??)`I-0nUw_ej*AR?$@1MF$V#m2x@Y9{)6s{eIRg*vr2> z)JQ*ISxG2Y1kJZ@tVCx`LAnHo?moyf8^*>;sa(*>Le@P`EuWqc;Z4u}?J%c@s;8GC zIq)3g2w3vU|FB!(a(et?fr!W5{5^M`UYI**Uuy76&X*w~p9k!IRz@=aEH|(4Ez7gd zzPhRZzdPPh(EH}Ue75Ltg&iSYW*|R%<0ICNmj}(3KV4KGLDQcr64w6-j@Wj_cyY!5 z-E0ML)UH8Zu`eHV8k;xRHAMCI9H2H^J~Z1ga0v;#hincAY;sB8@N`G!!DR2SxIcKD%@gM-4==PwqXgcd1+7Re+|fhIc#xt*&0 zWKYTqlbXJCzrLZLYaLV=rE(<*)>vdYM`{0*te0T(b%)wD+e6`kw@2*;HU9Q^RpnSbM1!}i_S>z7t6{RoYm!++NdA} zmn|1|eUHPgZ!vxbPr)kb=is;seT^oiqWfG@F>6lghU&*otUjj`o|n4Uv(S9kf|AyA zBJ$@!S=zMOsy6i3d{q(tZTqOF^M7CF3!sxbC)+b)?9}@Z;a%F(^GM|Ubd=X?( zl(n?IrhejzP$Q(f&mcx+Sk8_kG)sqPoxGGChR`149Z$EN0gKX++P@Z)QNLIG>7 zdX>WTXWU7yZdqxwsjS9qtR;$e8Ly;Y0r~h#-f5|j%IGq6{3O}fA}grZ(5iRBU35sX@w=?7^HTml}Y&B0x0bMZ3#HO37OVKQmg za5bVFkpne|hvz{z5R6(L27Vncf#-G-|9u{G^Isfw9g21Y3KwL43!$lNTTaN|1uAq6 z9wU~(nTlv~(zoHbfa@l=#6=^2@;v-e%BEWE>n2_%dDL3_hIfU-+jHR@3e#1#fZdln zV9B_Xd@xQ8ePJu20>K;tk|i>qK8VfycCfuubn1&xsqS3_^9pfkHYw!G(RUFIvocE% ze+SPi@8A_MxJ*B<{iAjdjv>1rnNnwWd(Y`F>{!`+pUIMaCGLq{!0w)(QSoOB=)!3& zqviMw{3f*gE&SBqXQtDw>GJntb3*Hs!Fda5ud&;(X>k|djNgOxc|K8zDEFem7fVVc z*Pm6}vEa_H#>zn9ThxSVoD> zuK`AV;!R~1@Z30}t`<=U>Kagj_7IV)5`6#&&^OPeYNLI zj%+?^4~H+u*vsZ?(_Y}EQXL3O-AieMbGPIAYMo4$>=V?I!uNB; zvN;(LFAJQ!4cu;iy%5 z1vz%XX8jwm0GBTJzGL<2GBjS5gKv80Is&4)9*6;-V2*eLX7_jI zm4tyx0tTvF2)m|dUKtoh-$DEl0(WW)_8w-2d+_qb8=@DAPW@1eZa+%&0V9_=&EFT3rUTIy zxDofj*cAh<90Ki}Nb7^P?Z-DmEANF?oqk@n5Bmsh`UpnG9B9Wp+5o)k^%@hPDQXPC z38^1yU$=+rO#bGF1pN~nK`lA8h#XwQpvZfNOsPk4aE*W>ZyhpaRimKD3r41_>N6;E zi<3;L>JKr7P~00cTK4mIZc7y8*}-`Eom+{6>kBaQD^bamHM4PqqBh<#4z91j$ge~t zQ`XGBArv*U7|)rmO{^MW9a zMa9}*TpMTm$<+F|Nalq<*>)UU@O+Ksdr?{Qg3*vgv6hPs6!~?iWXhTsJ3?gzK0b4{ z^r!w?vu$0*X=|%ExHv$O--b%2tc^bdp{R|&j)RL682N3eWXf7FIzq8lk_#01WvFDz znin@hvF61Ciu^8AGG)z+7ooOp)KQgDTh&qGDP!p6`_h4fiw_j}Rj6djn%7K(Vl5Xx zDDs<7$&@uO0-;K0{i=FD?oppUUN(HR-Z_GUO8^u(oscQ@LUM2kf+D{Ml}xGX4>1d& zsFz!egKIW0^4n6$lr=LUgra6P?>Ai-81<80T7JymP{ub)BH6Z9sAo)^J_n(wb4Zmg z1JsWxT&sU|$TQ~~BNy}g`M;a5F}P~c@+$rDu5-_I%_7>T7cdgA+_j)3;+(V|`dm1M zoNvgKTA45Oc?iWJn)hbGlTej1bKY}pRq|%<>GKilR*c@(-}@f^~(%Q_9nJD$pr4fv^U<(k6wO|Vo z3Nt_0KBoJ6-i>mvs!g^ktH%i$P~`kfrmF3#+bX1Y2}CN3zcOjlIj`c9lf!d>x~jet)m?%jG?Ec?4rE zm;yqv7EBSLr1oeUoxFA?b!ch9T8|yBQ{%)EP-NUdrV}qjAMUvB`QGh$m*o3$^Oq}n z=t~idwO~pJ#ab|Bgxb0Hq0-RmV2kGoVzcVY@O|UNGEig;LZ;4njWf^65>FeP?A7;G zBq*u%&{YtOwP4E;inU;>2(?aLVpz0TXHRNM^=v!Kpuurs1t>D0AyY=m&E*o!Cepqq zirlmYH128i&{rZDYr)hIinUmwLz!3+?JwO|_%igseb4NtT8Z*nev zsrxj^M~o92L6HF^nI5~VV=nM@(D(M2(7BQpKO@)m&^I9%YrzZ=inU-y2-UTst3P@m z<+FnD$MFvZ9i`)hF(@)PB~yhji^h4~Rx2O3I)6*1Y28V^9=ZvFu@=k}p;!xMhENu< zo@ag-=r)Xbn$MeP_Q)A0%t4WnE}3p^EmbMv2#6IP(eu$fE`Ywo)w%Uj@K6ujcHrumwjjXiX01Y<3j z4MMRN%od@3JRKOvE>{OH5ce&2YgsosPS}AWV{kHE6aIE*mz$i%5x`nbuMq$ zG47!|BN%JJTo8)2V6F&d5Etf@v~@Obwdv^iC0)7U9=aPSa&sbDAqH2Ak>ciHm)ukZnO6Ys-|xRMg2tZ-jLC5mvMRF?lwu= zxPS3-0Y6aW)*P8~7Rl1zaLGsp9Tb>cVo-I<`?Zokf>GB?f4|C8UU1vLp#E$;$^rkv z{mnAXEf6U6qlKK9^DBs{MC5~>0*!2yaL$A`vV|W;J+gyKrr%_~`j2P!8L(Q5 z#64-#FLh2o_n-b#Dbr8>r~j~tC%zZn2{;Q+{nOwP<_hIt#2Ki)AOpUbG);@xfuPhD z=UICz1i_RJby~%7p51P`NzQM+w=cYs8jkzGzB2iqLOw>mz5SoZcAeON-bR>yO=8WJ zP^KKor^-v8YR%prJ4btA>k@m-&0;Fkk6EAm_YvRpdi4>s!1V9jR{pK=f8GtBUd^sh z4nyQet#cv#4VL3t#7+dI)>&e$RQSJP@X+!hzLbbyvSk0J{y8G=1B^%nqZTsM>HuRG zlOrFF?P<%mjtYsi_?ntOnEk`SWsmht`fesiK1ZTH#X7)grpWQ*NPF3VAKM_H&rtd5RRH%j-3*b$mGaQKrNtW z4UzP3*o$S!2u3|?B3sh`W9-i3sanJT;cvEKM`Wf@Nho8KNJR=EQ|2f#4^f0fC`3xg zl#+;uNXk5Gl4PtzLdFUS5sKv5=lA^9>wCSTGP)2Xq68{&B%t(8k%KrcU z4bzZGB_vi73u!HR9hsjzNy|y%APJBK$?M5ev};Lxqz&YaWFhi2?I!Xzk}!EQS%mz9 zR+KD95+iRRZza#tij#McB*>CvDe@exG{S7`T>_mK{e)yV2(8nOm?Kj|P@ldMH1 zleNieq(fvKvM!mPtVh-$>5~n}hslg&L$W5xh-^$YA+I8vlC?=@WOMQnGBeqNtV6OS zA0;0nvyiRGdL(P|ak3404Vj0;O|m81k?qOsp=1+pCeO9zSQ6EyJ!Q*eYAn(Ao4%&X<3ubldn;K zusutp{4#0cuMBEjSkaxC?MoExO$)Q2MOl7vX16K0Ojf4ku@lwvcQ| zcBDu$Eh&b4jT}pkBgc~y$cf~6wj}D_CjLMr{wwzK=%d#0_Z!EK2$lLaek-OHnYjVj z>&1r7P|J$UjmCDBkMBEd@!NDJW1{S0CY9X;OkTspjQ>OWQrD}Z+zt!!_H*BKzj1k9 z_M@_7z%G}sU!}6qcsG)E3$W0H2OaCVDW?BEN2qBjfNk(1ZQCHr*M|&x8!$alQ>V@T zTm1h0#t}6w6)^b+=bElW{XS!U!FTf;w?qSRm9#VBbT4G6EDbR4vxV|$FXs6FUFw<2 z?f|CDqU*o+gvJGA&~(5!XJ)*^JYQ8HX&HdMY<}JFg=tkmOA_~*p_!%S=}k2qLE;J1 z)Isk8mUI5d!M)ij|9Q`V$}#~fe96D=LhDpDvfZ)(6MEKcY{iyYiLiTs?fQ7x^V#eL z6NF_0miMslt;3<~*2o6Q0qi92*X^nM*XkjA;XYt?U1NbsgS-2X?UoCe@&o3qtrmx- zW(t-+Z+l#PQFoK+W{E&A9qIL zA{J31q(rVoSTSHt2Y2sazZHKKS!oGi5;Lxx_4Mb~A}cKgti008UcP#1E3$+#z_+6@aZ*&O5%4{-zh1TP0whUd&t{5}Z4PT)3(LyIt1! zEc5rKN#vBN2JDFi{TzMR&T@p+0JiMOwo+K^v=w2sfQ|IT?p%7fP8MNxfSq7adl&Te zvOB__09IfAI&$&0!4ZT#1 z@C9Jp;gw3KBgZrTQ`PbsTRmXT6Qf5>@89J^*h|1V$9G)6E-`Rq=Yg$0m+8aQiq>BV z+U`2|gnHID0Cp*2z0h0p8zc9u#4NgZ(s#Mj#^-bG>t~^|SAd1zKYX??KIG?#r%f!m zafUBCj_TMfFqJh^StDTe!559zt}kM9!=>!1g#V8`|7jyo;R9?SPfP@ex!T$Z1E?-T-DCB*)|S zScU#oCy#>sY|C;x9FY|j+mCtJJ2U^AZ4PUxSZGF5%W$ysv7*#QEyDZFvunxcy z-KABpo(&rNcS{j!+Izr4Z3oW|Foy3!me2_pyWbO;n^6x;5!MA5<9hwMhU%zX!}0E@d&+fVa7)Bs8A1*|{4 z_c-5ck|)CY0IS$nJ28^KRFCY_Pk?QfG^J|^O-w*`RzG0XGk4xDs=Ev!$J+p461yJF zo_xTX{O=brsrz&gFr{Io!y7D2@{kQO1lUC}^1jK3x8#w}g<-%NzOyte%+$>wXVwT{ zk}aaVvQb<{$e^DAo0M_eCF_vekn1>Cv_FucLH(_1X6@*>3Uvuz08{Zwd!o~r6*i+z}(YsazVc!5V zV?JVSy5q%DunZ!{HaeXat-+bSi#)Jnh^m*Z)6EGfC(=T{pCEjAsgAJvw#J5 z&{hZ=*nC7jc7FoKQ7Dx)rtF@LusOh3V^3Yw{d@f+!sY=hx$*J#112$Uge?Hp@i0*Q zcZ_{4^6C5wu)Zmw-F*%Hs>lZU4Vc~hsAjv$Z6)OIw?)8)22RCy+WeV9ZUmM9d#aM1%6ea2(K$7(bJVGMxD z%&oWhc_cy~xePJ_HjyrNPcm}e8DSK_WIUI;Un)CtBD;PSU{Cb7xzq@F6(fuZu($Ei z({`ioL&y=r448SgjQ#6QdSB!SSq)f)NVd!)zQ$(c2w?%NMnL(2OhJGO!dL-|SvjP1 zX|>)m!qxz0asQF6Yf#Z6WTk9?={y&_Wc`ZZOdv3av*I0Otv@D%D!~37_!ohfPEHC6?vNAcMTa-2(WbPJ?`8OLlzOX z39yrTv@RW+d9x5E44BM!9c>XBx)%uB4A@$;)!LHF*Vqvz0$A6X*IY$AYv>Us3Yf&j z)VR%_w`~w62AFl_m$}EBlGtbc7Qm$a#Y~(w(yT!)gIfV3y?<&~|L`6OVd8-8@$2uF zdd`G>gCPOfGbQ$%`OKtg-%*$9rWwm_^Qo z?SM5t9uui4tx8AEg&lzL94I|sbWd^-Ve)`k+|RJ^jF{6wu1Grp<6L!)YgMGt5^`}+ z0E{nUAz^#VJ+Y6ZB52@;rZ%6hbOc}7v<0d<@#kj(eK~(^I zonDzR$V49cWxdEZqt?KG^WD*>1#Pv<)C>MTz?RfoA1Ip?Tts%3Dqwy4Ob3fQ^~R8$ zwI8tUV%;Tu2F3}<&N={CQCZpxX0^L7kegyPz{1WAD;^q;u-$OjfA%@DBWFE)dV z$5pVi-Uu*0x4BT&ivB1h%@{C6qb#z|6{kjIZYF@u|IAr?oTVZTNizj3M@J_+_`?zG zJ1#T84%<9QX><`jfuxxO*1zV!WhaZkhsfNH0LHn6K}u}kA{&xs0hr3Wp*`u2*-}WF zC1A^cyTu%?%ZnmuM*+K@cYMf9!>$BLI|dk|J@XFP3OivW%?hwA@hme2TJ=aI%^I-x zfevYQUyozQ+i}2tz46rgGWz^El4b*#A$y4R?7E-Zk-6Cd=AGTaRHXgQ4N0>D?A0|x z!ICp#Vn~`jV43ny?eeFY9NE20_+Zdm0819*?i>F z*#ocvm%OzHU)mo+(mVkhKIJtN5PLWqNjnXgL7uuYo71=zvU$A#(>`T5Sls2-s+BjCar~tzKl% zFu-;xUL2FlRBk}h!U0P??h!7gPF6+OCBQBmOuJBMzu<%n8Ua}H$a&$vcMbKBw99~9 zzedmXJ+JgF!ma?u$@%xJy!E6nGH4`VFVEISC%9d(g7BXlwV86y6+rHh^J%yyj046qRrg`1vLOQ~(0rtr*QS6biECVuVEMOn&qn->L zk=u==#Q`=&aei^2@MIRk;sMKGI#u|~b!ZbZXaZm&{nm@3BCRY)S|VURCim-9qVMY? z>^fj>p@&Y6=09~qK0cBF>#1!2I_>W5h1`kX0PO3@x{7@=Y)J^a3E18*FCNqDxZ5G$ zU?c;Urmmi8xQ8tTVYdL|-236a68F112up#?yvd{XrK1D#CCqKWHVtrX*z;lMBZQ>_ zcKi8|UEGv8_BccuU^%>ouXsgmW{^+7JAk!L?sbiq@)<$CX-o%9$&KFik?3_9~XU3 z-Rx<{0fglNc6NQBXOH1JYh(!z04wR=v~_nBr3G0+K48{o%$L;f^X^BE-iLrCa`bkJ z9Y2cwv{V3?-!_eYii6K>-;tk|9s~B2!rS;)kc$WT2BQ$Llvn!W z%%ik^$X|m+fQ4R5ytm~9oj7uRD+a9b^rkKRE=OdL?N$O=t`>orvR^ zKgitb0P~Icrg$%b!4R3-6Tl(_k8CUcthNc6+f%@zo8&TwyM48gxjh5SFghn|s$?0v zYkLk@so}*p_J(^eAm`}|z&^~6{k&l4hh3uU0c*N*@A$IOY71luF9FjEt;{X(cMw4y zY-<3_Gc7eX;PJdK@~Fluz%)uOnuhr3mm;hYF!9)d>SrI;_9N^yU`v)~_NTnv_W@x| zfa!cm*WMRlY>u#IztRNLmMAnflcmXHRSJAZhObtI%HIEuL4x(mDYf{1mm@kng<*lGX*-O!esS)}Y5U z2>Sq-n7X%h*vBt75Y`P?ndnVNT1|O2gnb0eJib&*G;T2pdMaF(7A*>1gn zJ*-}8FJAK2LDKpFi+*owz!rR!7h#_O`xCTu{gB%Fcx2Fiz+9f~QP5RdcLqrt0BnAy zX5?v*wH%T*2w2V2i!4p&{1}k5A;5&*TnV%{T`Pg44Fe{7M!}Yg!3n#*jR3}NyP+yM zr6md(^fO@jfAVbhM5b~ggMI-l?FDIYgO=)Qgnb33#TO@59urggMo^kFlR)l>6%=#k#!oAr~r;$M?06S&hyzb5IEAj~Y z4wz|}^2j~MH3rC_lYsdSELl=t9L9c#p8{-+=9Vg+^i=G}$7#U$0^TdOPS^z_f9CxF z?5CRqud~76V}#8B_CS*%uH0_B0>Wk?JDKz%Cy`u>u%CcAeLrqG*HKu6usOiqtehR` zubaMsuzA3Gd=Gy6NwZEBVGDrW?Ka!e;c_b<`E%tLVB1XXo|Vce4${KSd;l9iyaz|o*{!W1I8)sB=I9Eg990KHDGqFMw+I2QdP*HEP&;mxb1%0K`#km ztbhgVGLHP{N>N1Swg#{lL3HYu+3(#!=Eeq?39I+swH~#-$e`?iozz;#|M1|NIAl-` zzy^PGjpZmh%^-tv0@kbJd)HZVq7F&p0_@f7>iSQNj(?CeZouYEJa;KMy4N8~-~mkc zzPsCc%g@=!puB+P7`Uw;3HzIhTn707qfxQs@aBjYM7~T|3mD%#3ulM=@s|i&2bi$R zrQF2PpF0r757<>7-y>#Xx$4N=1OT)4ILC73V?1^x7X<9<=incc4>_@i$JPU8#y!>* zD^MMd47vd@kyBa<18f(ucgHpYb}K)$)}6)k9Fisk*vlyELwDQ5A0TWKU^5=CcU*Yg z%!@E#z~nwN^V+4yV1sT3?81*fx36qGq=}@70M^MR{o%g*VeD})QNRw}e`OdLZu}cL zdc^?y^Y*=3ke9JGa`bKijMt%$;in-MUa)s0T!~Zz1Y^I&>0zYJ7AJO zuDC9l)?+Vp>;TMcu6R}b<1Fmge|f+}oPUKdGH=0Fx)ZQ#ypw_aX9cE^l_~&M+H|~k zCf4mRvQkCBXkMMx)$uXL-fPq6J&gQGup6+#XXld~ z&iyt;{v_A~SWN(FVJSTkdu(YhV9#7>YJ&If`H9R;88GK>X=i=dmn9LV0@xL%fGh36 zW}L_p_5sE`dSF=K2h$;B395kQC);h-Q0g^74y65nWh7WX{T=7yjLhu-U?bbuclOl8 zVh;(Z0k*ASDqCWE?;2zY>VV~2S-Q@z77Im|paEEfSV_%|?YeTv+ztX}(fayukkMQb z!ZZQ<$gyW_db^N3GB+*2+#XuUTn^iK7@35_}5G%~ltfO+|+PD;GF)P*oZz+@jp2=*}c?n9Pf1X!{&-N@3=#yVsP#(;gg zv&_!=dJ(%xF#)Vr^MsZ9uHhkMZl-|Unm)2Y{i44N!ps2MrsHU8rl|cLS%NuW@0*x> zI4*2pLY8m@FuHvVADI}wVZWqU0Cs{==2!UR*we_|ECKr#uFATP)>jo_M*-WI+hMQm zTU~-I;TT{8Z5!n5`i==BORxfL{`VffzV-os_qH^KdPyRy^*;c2P~Y$`?u{!O~OUgv%<_cKPI)+YfX8qL&I{{eIM%8e!s=IN>UT_2KJzYc8lfAxgktMhT zcFWvNt775m2V@B+0jtdEvb#BCi~Ut|3b3+Zvp(meBa6rqJOHcQW7=LR_CX6-f+t{i zP83)#x7w&9OE?YK&|%#PjNON(nCc~dAWJw0*oLOITn@cWCdd*30OO5U(%Z^n_8D11AYk`ZTQ?|` z_Xi?#3j&Pa*GeRc#PSkh=K*7wZmT|!&S!`$;R0Z(H?m9%51qe+EFl;$lh;@MC)kC* zBTEPY%-(8M;)24?RAg?UfK{=@u&aLw!@g6!2-xGm=rxG0bc@)JJoCVHg@GDWC;;~iM}aT5f$&np2)on*mOP*4_h4r_I>Xa zzzk3O9*>sUl7TEC60k4d#;Ye}-!~yQ>{kJk^gRwKfj%WcWXpmBh$ zvLqR(Ej_SA4$FAJCJ#i?T=xmNf()7fSopl06N_}Z1~O^ARCZKace|24GH5bjK{U3? zPF*@V$VzVk_HcC6IwAWi_NP<|U@w=Bnr?4TdVvgj8!)e(hlYPP7Vkg?O$Dr|E8rq) z3v(8-(lo$q8rxKJ73Q$=EO!7Cy}RVMsdn%$GH5zr*LM!qm!CnWZ0+L=;h(0hRK2KM!aZ+Je8 z44Mttk#&EPPrd$j9vL(Tu;>F{$LFpnCnAI12dq@3Lto(WE;eM)T)>!2jTTP}C}Nv8 z53uj=$Y*|P=U|_J4*=UgX>{X(Z}EC$(0su7GHCbg` zeb?q^-7DjQy)pU-u)}Mr5>IdH+=$HWF<@Vx2Udw+xO*32g@8#aOovPFG?GJD5n$tD zfxou@w%>rTV!&?MmuIei>x|VKECEdD_>dCoG}Bk41WGAjqc==4c|*({kx%C`z*yN7 zCCwtLI+3S^%K_^a5-{D!=w^$s3czkoZ+x{y;+!pVSXKhI)od~G=*L^;HWdF7z3QrR zh2#Fq_f>E6P+#7x0!;io$wGUiZ9noAVl`k_)2pBGZ;CTTem|`NEd9Ve_wK7djgVJC zYXPfgcPyxGK3$A#w>rS+d@`3hqSU03H%6ZTHgspbifpQ52lAK0Q^3Y!vs_$$X<8uX z!ZW~9LQY=h_52!)%Em)9@%med+PNCU~4)%m z_7bqOZp|lmYySyB_GtrP28rfl(s2sq$Uc1qn3aoJ{H6zUiOA+{1k8)x$tm;ig8_uS z2COYwibJjLSQx^Z05dXA-u_K@%nV`8fKlQGKRn*AI*shC7QhDC<3**mFYqE8q!lo3 zmIx8JJEI{Wg(-Ox%?0KYK!1le2U9T}6l#0x)53tXTmDJS_Y+`y6^mYm zon76FY`1>E&e>)!CQOE5-&+m1encG*uq>cS!d^p$pBXb)COk|M!%Y~l91^LVx15ELliSYEca#dsr-Zp!y~oyuY0j?w!Z`R*&?@rheqNlveHSwqI#{2{o3AG zA}gH&tSM3PPVoRuD8i-zW4W-quJAq9OQF=oo1SpukwGVi|njm)92!szQ#p z1;8TK{M`TaQ{6X&{Q@lXw`voQmvAR?ApHhx-?>vU2NL#HB6C{=OocgtHX}|4d#88_ zFi!SoW{a_W3CP_305(OLR(RyD!i~&r8L%a-gNgxi_xzE${ROO&)m}3FO)gfsVg)ey zcxw%tx0={5#UzIRdH(INh-%4P%inrrpV9z!O2TX5K+cUYWTmu#E$Lj>_49mGhpdzg z*yhU_pB+vZ-bd2t0JF6d=QQC?zl@~O1ICo2YP^l#3cIK>0Jb|?%U)KkzZf~Q7y+}C z(fD+%lZ6907bt+OsyaJ&LxK}K0apQ*!xzHpW0FvTq%i>&|MaIs>7@osgfRm)uX1)` z=iwvs$Vc01z*Nk)nQA|Xz#jZy0gSHE_U-m=Jra_}3fSW|t}64} zr?5x6*#Ij(=lvm@CaD@pV+Tx({F;Y_O*INR7dQZWzK(IhW)<^qB#jd=`b-1S3pso# zNE#PlQhC&OC^@daN7A?f^N-!Z!k!+kh@|lVwvLo`Msdr}Z3yE9>}k%)$^ik54G7}{ zth!H1&ikpJAu_kMfQfC}va#s&o=_xh9bgB!EPpwa$=*g7KVTA?eZw8km^G0>1pw>h z4ybFb4aZ(K5d`e(mNH4L1B%!iF6#l?!01jsODA;>8FT|+ogTj2q?LVv$SJcCuss`F zYmPN}mmo|CuyAItny}^}Ze-9+fR*>ACZ9LYI)H39VZiR1y1#twAn1Xx&44`@`IB1~ zLP|&GCIT4UPzvvea^@?9i2}Ber$NuEcknpE!~i?#_;b-{Md%O0wgBed`6+eP zt$+>M?>ReBQYwuwalovmD8lb&o<|`}0~ApJ%5&_BMnLU{C8ESQa*=${{P&1TE2t4=IEFLLi}2pETlWlC07 zjV$t&juBufYiQOsMNC;D-_RNZR$s4Z`R?bX8_3t2CV+Xk*qI(ZPhN*GQ^4xHtv+47 z+AE1LGr){?@SK{hbY?_;tuO~HA<~Olb__6HcjHq%i}^ap60890H#*Pqy+a3U zkz)72ah0wo&f9+ zkExW+g!&$2P&dF_A9{6Z-l@_<26YEa^`I)}wMiLaWYCjfS&Tx3uWz?S&F8K2wje;XOp6R=kcO>e^4tFc?I?EdI7dt#~{J~ z_s#dnpx%Hvda*OJ?-ja&tkefEkF8N`xoJ%wkU`G?Hg{e?vuCA^9vRdZu%5=E;o><~ zW@ON_fJN7`b8pqKp+g4s1MKKminq|$6zoS^f54R1ZFv?FLvt!!6mTKlB8{|A-GBn(Yy%CP5kwGs2 zW~f|oyKTguj0_qKnB6L7yEsneOUR%hfT?&rq}LB5*&`p6p@60Sed+!qOuG&FXuAkl z(nq6o7N74N2nz!&vUc-#kpKuk1z!jRs78?He&kvyCT^T^|FOHf!bEjz>M%^S##qOXz-~ zH&&?GkNjvG3s}9vf{?ZDt`cO>IKWn_KISs;D`4M6#{(uOWFeQF6kCQ2ngG~#{iCig z2eynLgC+ttmRdfls}s5h`6cB#V2dNge{Mw&haoHpuwyeh&3CaiBYZwNy`CjAUTpYZ1@KD!1H~; zDm4V`+pWIJA%o@uRu;OJL1S#e9~m?cu+-A5pVD2k*yF4Z0GqAP5PM(L5Q(Ja1NKB@ zXe9miJ4R$~4*`2TduTbU>xh~fZ5TPFz)i>#eRmU0PKC@!pd0VVXU}N zC19MvtIfNgyu_*oRRNX|LOWUU^iVyrglfP%v>uC5CfS6MU*W03N}JXK zR@WVDll`z2`}?g9Fnivitxc8tu=m8D02cFYW7;4?N+Gg@r-13WcysOOS)+omXMj1q zzQC^cM;)tg`5dsr-K6FQ4w27D+6%yT{WO!`_Cy9Na8VD~lX#8?D)uYb@%9q1EkRy% zw+1#~b87&sCSPa2rc?|sGPhTN{Z(8@>>fR`6=98leUFoEev{?-3)%Ir0W(s#!IYme ztbkk`ngGi>vCBE}PO=^{Xft4sxrKCC&(w4xtOc;edWB;f6PJ4s)(Tj5Q&f1UT(&L3 z+5p=cXY4R)Aa)aB?SPRlo9Yhj7RKiG1~B^tMWOWJS0+f>Tfl6UO#RDtpKnLlJHX1f z=>)x-$Yw=Y2VikIrNbLqRV@(q9EY!ecA=s{-N1< z7NLpX$eyWM>Tl7H>}0_|hW(7#VaJu(rNG!5)T%*e`b@ zfGPa;XA`SAqKl+`2JGu&yQ)L(j@Wkl0$BBNRsr{YG0%{+uYlQ94_Jv@nUY1=C}59x zLOwPzJ+?;J7+}8^V!M-d#CIcX9I&v^3W`jHG*(OJ8(?^!USM@Y^+tj z&%fM(4Eh}~4G(Qewa;|D2%7}#_rA%!6Q_hZ5HB^fpK7kI39+0aJb2>FRTDUK_b0{RGT@`9!p;PQ=Yt?27aYFt_Ksc6uS&A0%qP78dhn~cnC>b0?d6jZ>O`v@nB@T{Q+z}^IU-1q!8A!WErp` z+p8+{tul9zLH`1lps;Aym#b%nuob`}-WLp5cV(Jo_{m#j_9~N_^cUWG=R+| zr&rj~tusUzEnubGx?(q{gGtB+Ap_R3?`HV@2{){B6CGf|3Ec|LVm_|Op!9&rHHqHq z%5+rqiOGdyt_NqPP-*Fkcm!SZrN1r5}&6_lgtaKG%&rf8$@3hDq zK~5Pazzl_tHJO!d#cooV0oyZbr>8SqiLG=sU`N>G1^cv5XdoMe1+Y^~=6mM!?_zC_ zSOI&_Ql`F@ZTdd4gf)OY7AllOGBTYX@SZZ(J!``pw1dJ^+K-7J87j`ai0ru<0H3J^?zz4{n+<;XZ2lV*jm6c_{+T-rPP8Ewhpi{78>J* z0;{zM;|FZq@MFjQ+zw5I2>^D*dctzz!2?CbXB(5CAAsUFV^?-@UD##c}^u!=+ z17J@MZXGn4pLIjnM!*zHGx_`#RqrB92(Vv2jX%`{O>IWlCcrwkeJkDiLlR@cfZg94 zxGT8uj{%al8L*I$n2RfsS9c&x1h7?BiXFk{XMZ3}6fpa64}S5fUug&v1I%QMa#1+@ z@*0F~0nEeahr?ah(ent~3RvJJ^0vHq|4RrH2kgT7+EK4p(s>Az0F3n`4WsOqk9`P} z1nj(BgXXe|^$CPY0roelh5N14=|+S}1E!l5_M~jCiveLWfF&Q=H(7ab{u;uz0TwZQ zNpQ9=au#8-fEDd>Zr2k%+l4SWzy$Mz&MSp}cS6{9zz)PJU9T$C5kS}uz-spBYTl5( z>5VXXz*NQ9bbOPpVCU&hz+xu@m9|)LK19+K02>s(|3l_xR5`*F0b6q>+>s@{>@vc3 z0d}sMVRZ4f;{${#0oM5?h`n{sy88&*4Va?%TAyGgaqJ-90~iD8$J1T6UHy@?y?{|V zwjZi~vW*U5%7E3MRTUhVVeCej3Sc|=E~s35H0FV@eSrCA-eNLr^Q1wTDqvfRyCR<+ z6UAQM+z;5~!$vV>t=bhN?Eqj=gO;3p^Cis)Qv)pcP~*81TYqdtm^xr}yFc2GNatY3 zn+9OoB9^BuZ)srcAYdD|7}jg@&dVT!Y65oPQ)gw!#JGYk+rVW@= zS5_Qr>xL+V9Re&U*rVC(t`j4|bO7UU@RcoPpKnE&E?^qXd(Zx!QcFXa9$@0jTPm77 z8~7RGv^Ry0N4&)fj;y2N7x1bFkoKlQ}3(;Zs#CrhJal&-nvYCwUrBD zMu5%mvc~RsQ@MyRW5DJ#hMOJpCSM@T1Ta^+@9Rr^^Q91G3YgV1mo6%8L(PPX33bupI%+`)r|n+KV~<&dE{ZNiod{>pgrK&3V1Ixl`CI z0OOpQ@ecERRe>-|!0ful0+R-J_aW>kV4N%7RlGlVE+gz1U`+>i?_j?be-&X?fNgPJ zEdCJ|-}Sos?tLA8OL zc7)jh7HT_qc7QQ_7sBiTWA}SP{bYM!iZBPjdW>5%KCnwfBg_#n-!IJEJ*@&`2y+5V zX;|s-1`CrsggFBy*&@m-8^vXWFc-i&9tMj4jRn+k zM-KkH_p;}ow9|kQ4c1-AnqT*AI#)I)VqZK#4sD?_FTjYF==SQHQ+b{*unxR${w>qL zTbjze0VDc}BXjcsjA#^&urq)W9l#Oh3mDPb8)0VwBYJit%nvZ488^cG0VBF>BkUYt zMEh)n1pr3$#YR{lU_`@fgarXcbh1X+dBBJk)d;%)7}0wgVZneAO{EbQ0vORP8eyS; z5pAFmb`db5zca$Z03#YVBP<*+q9ZfHE&)cgT1Hp|U_`6siI(utvc&7g49QFNf{!Q4 zsi(|kz=-C^j5ZBZrPoh#xOqO$?Ay`ZNM%<5Bf1`+sq~FGCHA;4T&7QM-SKo~DvJb+ zXbp+X?J8hIGe?9)0haZD+b<$48Ze@-BEn(-BYGDi>>6N1M?Zwc0!B2~Ls%SOM4vl^ z#REpPoI_XwU_|#fge3w-bbk|ms7GteyG3Rx`rIWY=If=@opl{BqKjFE3yvhQcoUBXv}k3K-ETjQe8TJJWJy*Sns*^?Yg#+o>!KFrrhKl4gMO=3u%7 z7OwV>f|V0-RCWh2qEi^9m}`6NuDz>&4xZkoNqc;S%F+QNT7dPXNgVh-7)EQ{pJ=Oo zRwaUbUBHOmUUv=8O;1Rd*S|mEr_oq^E}P0S z0VA4rJtYa(Q ze)U=20>-CZB~*4FFrqQmwJ2w1&M0*nv;LJ0RU@g>*%8&me>q07mrXLD(a}h)y~Pdkh%S)COUN zfDvtI5LN^j(O(8(#efkVVGvdV7}0tIVWogc{@-R92rC1OXm5eAa=?g&6bP#TjA$W& zuu8y)rVj|K0*q+-fUs)7h^7w+s{xGYzksk>z=-|}2&)5(XoY~VCx8*H5D@khFrs+@ z!kz&}G%rBdbHIo`0tkBn7|}-nVfBC!CH)ch5-_5qKf)RSBdY5o>=j@{b$x_20!9?N zN7!q?h(h-WYXXd@IghYrz=)dj2x|e1DF2SIR=|kz?+9xHjHtwpuy(+RO6&-G0~k>h z9bs<)BZ{IU>>Xf4eR70#07ld&N7#G7h*IGQ>jaD_6^^hjz=*2d2>SpSQMDUk-GC7V zv=R0ZFrt7q!g>HBYFQ(!7ciohHNyG;BkDyX>=R%_g=d8I14h(jM%VygL=|O(4FX2g zJx16NU__;2gbf2m)D}kA2w+4tV1#`JjHus>urGiS6?YN#6)>VkF2Y6uBdXdWYz#1> zo+`q|0V67TBJ3MrL=8!VO#nvJkVM#bz=#5j2%7|qsD+5IDZq%bgb14kjHnQZupfXC z#rhC70~k^14q>x^5vAA=_7gCo6dS_k03(X0A#5HnqIep@762p4ogwTOU_`kyg#8AL zsAGn(MZkyxWe8gWjHo<@us?tiWxIqXF1ZC*MV9GXecABwHqAfxfXjdp^|%HNGT1xM zCxqSdWGXvVMhd60zkm_-xRkraX6IYhZZLk!_9pRU2tSpr07ewrIvh1ppgr8yEcS!< z_o|LZ=ctTC`Jd(PsS;BArNwxoKas!Atir(M^FMcOWWb2JQmyCpJ$CXrgq9^7-thf)@GUB% z1B|EyRgqLyu;)9AdFU*^k)=k-Gb*D8jHultzqvEc=wwHZ=DD33-8QzHsf+=zssCFw z2-#VTfDz?@5JmxvsPlucRe%vCcM!$|7*TTP?YAUry=Il$VX@(F=l&W}s7qi5jHsnE zw(H8T65i05&UZ%MEJe{KRJIy0qDaoMjkPPAA8x%oB=h6X^F~cpDq{hRsD7h=uUoIu z$h&G`WuvAQ?-gb$V+D*TYg5IXqsZaIp{}boHB%WoU_^x(tHXH{97k@6r|@Y`U0=@> zNo5>>5v5~lydAIJ=zqg}IfgGull$=}D&qu^u&RL$^U;BT3NI_)k4`b7;Y;{uGR z38SEut#X$(E3ck1QK!FqK#mK|O!{8(JRYVp0l>6fRJnesC zI_-r8BbBWOj3}3pbxhk?Zlrjm_fpfWrsAqLD%$`UQ7$8|YyE2D`|>?Q$rm!ERbT(} z<<&;OhyoW|UA{{G(z8BQ8*)!fL8tTwl?eexl%%*DYL{ult|8`{l@<6l@9sa_Z4+Qb z5s3-I1yPaTpOyvv#{GF3J7lO#7%-yjgI+^3J@ePm`hZh(gGQd-eN?s?FrrXHhWcX6 z(+xLxX(Fmh?*0xaQv&66?6ArL0R$lK_l(asJZQ3g#5YRli$= z>fcEn4nIj{l7JDfy61NF92Nc1J7FF?_4KY*z5-dV$OP;Co^Ve#mOYF6v1 zH&iAK81Vu)*Z0iv_YBJ7o)T-GM=9WC0^y$VHeOV8olY zuWsC1zGmBUh4aA`=idj14pHZ}9Wde*S|n`;V8nZr2$Khlc;^mbI{_nJ<3gAMV8jbV z2vY=%c*6x@y8t8J)IgXLV8n|F2-^)9@wh(1_5ema>yEI!fDzA=BTN}E;?Zt|sQ^Yi zjQt-LW4MpXPVT=r`-!powG4eg%!{r^x9?7_IWPA0UG~=?qs&WJ&+f1xZ6w_#F_Mf( zM@hb9TM{iPlS7sI|JlYaL`ct%>8wio_cXH|2s;*azgA)jFIlO4MyP z6bq%8DVICd939B>qnG?d!$eE9LWijKi?P@FMc-(Ncl2sZJ4aF?qkMVfx}66ntCGsU zerGM{V~#b-k96t z6k{sewd|MkK#Y0KHVyueE4F?u^2ggB?W$0z*Xu455;Tju8BQ^ws!L}kpC1jM@6$6X zcynywnajVojJU8VRk8iZ-<+GZ!uf~E=YE#Id+*Y<%UWeWt$v=pq@}Uj#h2m#on9uJ z88W?pCvxv&Ge=nK4)3`o^%SukdHTB=7c%9!Gue&+HYDo%bg1z?%YV{-vsnN}{LuBE z&Ae}uC16Av*XmWaMs@Q`uQ->2n`KLdxJe6i78D`2qri?m(=~age~G=Lh;O;jy@^q@ zVma@j*+D<<-2KO>EMKjmozp>h^o+0Z(rlA9W0khIw#4mQ-TCBw=bL96gn~${w9K?D zv>deTw4AglY*tA0e=da?Y}SD3iJCfX_TS?7pGDcT9Y>hPYN=GkJ=q}&k2pG1OXsG! zGuUhZQ)bci-+Myi0+MEnuy>ZJ@s>w-4Hj5(Ml%;j?W3mI0hXoQKh9pLUxcLDBkW0| z?{?c0ee|8hcBf@7n;25l8~}?L;!)e}!Fmiyb3~Yy&(yBCL8I0YK{_XUv4KTuniF89 z;_A{J!Qr+@nlr*G`WNH6a&B1h-m1R!tkuhan&tvnt1;iZPkCzxku+C?DV09>xg;C- zxY(BYo!0sESJboPG5|pRZBeCch3)(@p_4_@iqqN6~2pN%KHhgHBJ9lI!_|l@X1I zE}K*I)HF}PUd^tq|HSC{2T41PFtsCl=Cu|`CpAk#%KAUcOj6Ul06TR5m0@7G@oyx} z8(~*wP2+bgh7@FRtl1V3xhjsD<^x!Dp8BtRGpPb3?F_vYKXc1qy8u{C0BK<< zJ+b9K%$_Y6VRy16I$BaUrbPipv>r#&q7nAIl+x4x_+DPV z{hb}FLU#XiMT!B8=mL(UT|-!_mNI=u?@@=*ZBDhB#^#0qJ!se~-Yxmi%pYpnEx?FA%1BxY!m=uO+v?a|j!)W@us(UHwS}5?8!)1! zF_M;wu&>TJ7p$DG%9h+@Nii_&vZbb_0Y{Ug5?w{+%zSVnvWUczGE=NsE z2aM<{AuPi5`RQ(I3=mF=lWnV_cK1&nC)i=<^DtY?b8n>A1Ull;JgTf0^C zB&cawfDs*hk+gdVqZ#nty0o@R<={&PwRfRBfz-5Yz=+noNLmiU?DvV9FoMgTC=M8(yrHz#KwhC(@Fp%I?*C&r3mv0)-&B1XtAsaZJl3^h*hTYe z?FVYwbHIqoK}gyQgcTiQ${HI~=CkJMv#nVCKX)JXfDwgqkhGTwGtDnga>`)3sq!-I zN$<0Oe^%N67*Re3NqdE`qVHm2;SIxIERx?%ZP(tROigP9jHtbWq`gL1xm_ALEjXN~ zx=U^8etqjlYFZOuM12z^tr=mWOPa$%-#$_v2K$D2W~}d|rnLY@lo~EWJ#X*p?c7!E2bx04rexZGfaeR7u#vEoFPUUTS){S19H-b&Y$W&grnpdCDJLjy6nDx=$|;Hm z#glTH;zjYM_)yMJd?{xseiVPoIZ6N}kP<{WPg$b5KnbRVP(mpeDa^EClyJ%=N(ALH zr! zmAwaycu4k%#$MF~Qy0Bs6vyhv*@q^$ZNxhPBj)yCTsH5+7c=FTUEzMpdA2s}wC^Y# zl=l={jxIz~7E4a83Gu3Xdh+k{RF6Nwos5>Rnlpc|q&N;yV7 z4*t@-p}eJZQD^*tvYNJ=qCw9>``+GZs+OYL%|PS&X{ zj~_g`BFgrh^DSDL_ka~#9-DQX?S^%s$0uvTQMLoEOdnzoj^3Ibc7W5>>Otn5-z&bQ z>)Chj?GWw#bz0bWOJOKkCR_KV;<=o)K*mY5GDC>T-?iYZ)t4=-leFD_(kLkF z$8CYdtwM(mHwZRO_IcW!W(Jun^O2UMX8G637?Q&PyMJhE9?xbrajCnnxpcPgdMPSF zegfFSjF#l0j1BDbCLqO-~lI4~Vj3qllebIQU@p>kLM%EQ&$G#$U zKVVb#Z7F=gfjen~tq-KQ7 zRvO(EeU=*kR?|rF1vr0S=<8=ojen~@NR|8O+|M4zgLR}U${;e2D*_YnkP-dgSPK|#n4y35pGpX_KTQnz9=ezWEBG`oFsPXR> z7gEwIOlKDI*zKl9R+UO}BSq~y)X2I-{#26ZA0{R`PV&-fWhX<0Jmkg}^lr9Z{C$BEf5-XbSLZGdv8v67P88fds5X?Di)tjmfSMqUsb8IOhF?$mOXwD1_LW=3 zWiNm5-WM{Wtc7Yq00U~mGs1Ykf-l*V>i z2c$K}T$MJ*I5~bSY_`Fy{>PYydKpg0S44&(HNoWrTQ)6 z!qE)@o|!@#85^!QRJwnDvyw!$*#HA-QZ%;JYjmt|R=@L#WVz9sBmSH2yh&*fKf*9V zHEDnWH5nS)QnZ^GHN7t9<+5`7)jJ0Fi}A9K3*4v1QEd*ufSN3g=?zbsPxg0+srGy7 z9xOk$QQ*}`)z_XVGgO-kFrYS%#v1%fc1h2=KzhrH1dHfdWWJo~5%wm`FBsM40}QAw zpfRic3RnU|d(N4lR=z}`hOk$Ic6lq@H$6nPg#ZI;ax^yX)@HbR-uu>vs`ofnemYsX zhR~2Go>jOlkE4 zVeduT7Vf@R-PtG{u{btKxwc~GNCB!X0vJ$Jrm>FU@bQnF(NB4j6NA{>M&%-!O!tN= zuqmRN3c!HcVj4?zQ+>|(oO(NILF!^ZZqm( zFrcPIV_T|%;VdJ z8ZcB_12CYrmc~8@FY}zeg(FtrOTp`~7K@*5VzTZDi6YNY%^YAr&4R|HLb><42sI10 zmagyeJ*tJ@H%$=Wu8rtLHA{d2H7gps<`?ozE`c|+Oln%s73cHh^0HF=Xb7uD7Q z45(Stm`TD!lto90L7$~J`?|fZXZW7q7n%%za0JzC00z`-X{^IO%0H!UGQ&BzTkd;e zK=vs&iOh~22_dLv2QZ+vp2j50UJgI9*{)>UqQ*WldMPk+c29ciqo*5CZ3Do7nmvuV zWlP!4=&I|Op6OL%?K-b6Nmt=j3oP%l`0S44I(b%$aw=YTw z7u}jJTXX!3S|7e=k5|&^4?o9H%>`gU&6UQSg4nKdZusL`6>v7Xze(w3h+4xyK>j;V zRND+Npyozn4(<(0Uvx$7`F3b;*=;|?7_adX(=)FoH=~+6z<}Bo8WT7532|q)tdsQ; zbLMNAkGB6Yy12CZGOJmoziWOOf$N3x{elZn!GgtYU{N3C2O~KQswi94L&5zD5JhS@P z_Vi8M=g4UtlQA{zG!xOLw^_4LZ5P0Rnm>*0D;-MQ*c5rZK;AW|xA)46{2)Ify{xqp z)pi36sO_P#yYm9(c@!m!czupl&vu#ExYuQ2Y-fVDHmU^x45;m;F*cihMk%Ml@5MMR ze4A$w;r2yZO;PTnsT`^W0t~3_qp`udN8{boQ#^;qYCrglh0iMPjyFj-Awr^B5Ws-i zej0NLpXYd*&8KAU(fIA!2KsMiM^#JRyN+k0+5vz8wO|^v_vGy4WPGpO5xd4Cv()*w zbogUk<%_Nws1^b+pcYDF(-G0?j{2fUE^1V6;Z*eDTlC;bDEGCXo2V8BFrXGrW8R-L zeo991$+&axjGb{Xt83uy<9$mf!@i+f1i*k=B#rU&rmwlx_(dkGw=$-7Vz9Q$;k`xb zo`{fKY)9@J913ZI)3xgDOHu$lo8e0=dx~%qgph;fZ8D%(~J{6p&u(- zqL^2gIC<$;=f>6DlJ^W|2BF$vfC055G#0k>_yw86+xyb0bZ5(~@7=v%;#}lHnd{73 z?@@pOwHO*J8l1HxE{us z(Wm*=Lri5?{zlJrPLdJ#Ra410fU(w8$H{maD~cN9pArkkLV1NA>RY_A%z1a`Ltpyo zMW}WhU_k8zjk)htip^hDX(WB>W=5awbTFsrz7rmKhNh^N05G7INMnax#Jw;+@!G*q zzk?mwD}U_QXpXYFee5TyB>@bmCDYh!zl!r7G6Fq2_*26<4kk*o4fx0W3F&!-Y9|2( z)KX~dn8w9`U5DRp@~!^0@2XS`C&n8$!*x9UFshvb7*IP+W0RX~j)m3Ddzqw}uAFy< z+P0an4QaLZ;rsFAefN`G2f}VU%=+y~}%d1TDD!&-;yX#{{ z;GA$>ZgeA00~qTKXZ{lZCC)qP!b&>vpb7^85NB{diiyC-IB^=n3!(G_Rmg6uSDL$vAEMe)rKyzQg)dCYfAG7T)sFnpVpq5QzCLv$$IR|^S zY`DD2p|PS!!2H#XBV`-9_)skeU_dRG#^z1EofZz3$y<|o@0w>HkD#%EyWAaYYB{Rq z0Su^JqOtqBf|tH$pB_ESKDKC3XI}Y&TPsJkZi+aeT0X#lS^fw;dfkWZ#~2a)olD&%D|>*E#NIMx)vlfC04<8oRJ^WwwnTdA^Iy`0@E*JDZg9`>!m= z@NKA83NWB{mBw5H9e%CM7dM_$`Lt7XsD5V5p?-NeqbWI5D+3to>oVxpdyU2x#UT3oqWWshbdd7_H*xyzOQM?g%)}rU_k8wjZL)0C*7?#;Ze#< zyU}bF{^#uO(wYKdM;WR;1Q<|zL}S6H)K5Rl&5a+sONNhyk#Da~ZBN4tYUZF?4Zv7$ zwR&xG?XFtcnO>-;v$pU;;=~b8;#Lu= zJp~w0YoM{VL4PT!P=~cDvlAUJ^VZIi}81SyC23W@Rr^Ym_)Vb z0ArmSx=vdVqpnjY{$5#ceVv|OE<^KFm-uF}_A6ftCg(Dr!Ds~-YYkneFK7#`R(m9H z>iNKh%5#g=M<<)v?Tx?sO6_^TeC4hUV5~JXw{{wv=3r>9cC@IB^yCSQeUN>`qeyRi zZPoFMsP+^*AFYt4s6$zjodx`c};7} z0#thsFrd~)W99O?+8q39d+hQyhe(ev)Ba4pD++Kf;X$>2fC04u#Hf8?k7926N;4^= zGmX41&5r|4zbk)yn`_K%4cZq50mfQG`@#prsJ%W#(LfC2`_g%3w7tiV>+}kYtFt6t zR4xPE-i83iT0{H7M;iM&gIDETSc%$~cVyq1R3}YNU!BW&cG*#=HViPJ_6ad+O;1ft zj*p(q+wf}I>I5dSOgOM&ft}-SC3Uo>BLHKqp*8(XTWDp}(#Qpg%kxusFL7Vl;AZZC zB`R-tmK28OHVQD-8d}pYG-j}3-J)yT`bGc9JkHtUFzKtAb+*K~ockB5eFYd$`-T{` zrdGn0k82O|>^k}=)%?XV`2>^#Kp6cqvoT9{s0)T&)Nt)b2PleW+s3Kmhf=XtcrT@l_@`K$B%gQ7IGxxi+v1s?o3=@qX^p<~kLJBrfNE0!18UPWrnT%z!;Y*J<)(QX zukR>7vRD02=H}gwa?BU1{s0W9vEl!>-If?dDDbB5dA{{hZAZ_A03q&cUdvZJy4{1e z8#};QYiPSMXw2qrdF95Hy~_lIq&HX``nj=(W9vQLluf*-#sM&(#z|xA^!ma>9y;84 zng6!6?O~;*rFN_ zzT+;_BvIW_M8y0~k=lY0TjIHH|ZIucO^LE_*!{ zeabaQBi>VL?d)z;BLD`}_-HIo)ZolQ1J}Zi*Li1yNe+koxz8F@l!ll;Xv7aNpf-cf zxNq63M>`qBIt=NqyP?GMdi0hpxk-?Duq^;EphnV|{r-_pTMeGXT8K_MFrX%c7Izts{S-$vVQCcs#0Xg`&t zEp)c<;~qoXo&&%5S&&AAbIQz(7 zc6i8dq$i~QApccVlL8n}lcq80SUu+wj%^Wh@|8K&1o#vEY$nb{#l99nH5q^bwK<4U z+s*1??|Y|vx@_qJinERjzyIjICBJ-OR5gln_hezEq0f~# zweeFErb*_~3tLfb9>9Rwd>Xs(@!p#1Jt5LfX;%sga{Grc**homdi|KEJPQB@)E3g1 zNrm9*yprS7Ynuv8U%0%`3=QvN%+u`hLNz&n0X2CV3r;d&+`XZ=)Vez;Fn81G<3F3* zZ2#OU%0@K>fB`i{8gud!Zjd>d;FyT z%s@3QfB`ja#Hc;%t7O}Zn3_W~VpAQrXA9pOk`#acQ1xIo^U+xcV5~K?XDy+z$&V(B z6P~JGug>#0R#EjQ*DW1COo??B2|ONMUZUvi@SfeLA1>&irB?Z0iFIs4Yi~x=wYa>sL>; zEW9=;CtCTSxccl|m{9+}L%hE+@zJPYx6p zSE>i0xfubBwT7+*V;T!S6g+b*rfH3E=Q5wy^EO&o@OF9?DMhNH+6sUHH50_BHH~e0 zb;kDR$|+z6TuEc8;|z=DaPcc!S1fJ_nY7Z5`@UN2D?_IeZID#}V_hiPAf_~yVENW; zGN30pRq7&ksQUf(;$0zCucsd_MKv>k0kzdMrtVZEa;-w97FdNfG*%~ddxmC|f2f^c zs>Cu^-vP7v_u`{wJU@dLx)xx-Ld|JxHOKGg^5Xds%NU=ELmvI!AmT@q)5v1$R-+3SdBO9gQ7**!SnV_K>=_+>_cz zk#1LG$nJ+kc>;6uS_2HI+0dBGq8ZPd^DgjoH&#iW{Qh(O>O<#63{owNXx z+;hBFR<({*Y^Xp_o7e-4wT7O&a-gxv(g<~X;Z-ixR`_&{{3WNE4MsYmTD%0RIRXr* zIUz>vS@SB^w|@Ikvb{sI#DKi)9Hg=K=#=ePX$D$TXMnNR&@Qu)#tgW3jS5YRo}Fe0 zIvo(t{+w~<$O{9XsdK2d31C3Y1u^PzVafOXe>fve+j{K$p2V&W-(Oe8XDiA1fO#L{ z3NYY_+|9Iw#^BfFzrH>-FJtqH%0$cXM?ZNuLwV}88qnO_00zv>oyL@w#E$hwr6_X> z^w0Jy;8ydQ9KRzYw1l~)TL1>sJP@O9AwOKrpULI@S(xW0+lP?!5KTdqvZrA52*BNkpe0NyYGBh_&fB|#cPGbQs_~!># zniiC$to1P$ULknPnAbN;(9Qc5S2Q|` zYC8Z1)O=`clgc`gu#u6w?}yf}+VgmNzTl>2BaYwy{ot-Iz<}CL8oTiMO_o@;?@Hh< z+KY?FJZ7+e2gB*KTrZ2_H1Bx&8k3+Pv{fAu&l_rKa@?s1^V)pthIB zxZl@AS6S?q?dn>bTwj=F#PPOjV~VSHDXIkm45;m+v9FS|10G_#O>dNr`Io7aw{jor zjpY#*E~pj+Frc=d#u6%u73*F8tl^#Wgy+r*2L&%7HZzgIyUbJU0{{bR!8E4S!=L#9 zOD;G%c(rXyPSe_9kK(eH6J6e@76LG!7D{6;lvg%z+XwUeh78|3Z2VlMql9be{rZj9 zP%R8#KrNicD)H~mVd2G{^HU?*cK z7*IPyV}owTwcfS%Xt~YREPcH4OT35$k8p{UG4n=#7GOXvmB#K$aa<^ET)MKvq_8<* z#QJBu3D@y2#R3me?Hs^Zj~vkP+j$y`wQ^Xt>(;mHz%G+UW9f40r&I!(jF#?}uTYh9 zm@fD{n9$xd#QdrLbbtX1y+C6kat$*irC%h>yL7Vgeudt>p*hm2+U%Lk7d0{f25gW_ z8q582H#T`paucu$7ilazyyl^yUYNvHq0FmSTrF?ZmJjdGa4<_j3(W!;unO5U_G@25 zW>o9fm%lPUWLjomA{|8<8Xs?2TccVIz<^cArLp+67q>=FJ+TB-nLbyWWMYVxHU;01Q}#%QUuB zkge&3?fJu>n;$%|YTB`SnN7I4|B;aUs8$FtpjJd&y z@<{LUY*Z@-7*M-HV`G}3&%$dR&J3Il;k|VH`pfl3`vl7K_4!e)1Ykg|l*aB{b~cP& zer-vj``VsO*Q!WaxAq*>vJ@>;y9zL%Rz_nXw_dz_^k@Hh)plv!KZ_sh6`jZmP^_A; zLbYoE18U_o)@C2F)N%c*4jI*=iJI+eQWKk0>th_Q^bU=^jbra9xFh@qSm<3E zlmC*mDCm6bEP&mku^w;RZ}NJCcT1triuIdw)@VG-kX!g{^CL93Du4lVtERC$l|5y> z`^%pL3%yTc0&WiPLKVey0QP{!=7&49iq4nM0oX$tyP>r~U~Z(UAiy5cm|KO7Lf>R2 zJHTpaOzY@ULHi(C5@5A7c4BOQ+ZRH)17MG7%=oAD4t&*3Z-70av4`gTVRwekSplq$ z#_CkQbz}r79R^rEjV%>?f4!YA_$t7j(wNnaEwyW+r2ULDFC-JQIv%!NjO+><4by_k>AF8~JA+Gy;hso=@!GTnoDM%O$e zn|-G51ZRvNT{*3ZYV806YA^}UOzs{qtNVnn28&rD*Frd~! zW87Yzg}bggTMIcHQ3^hGe`fEN`DGk+WCg0d1{hH5q%mcosQ29mOj_>0T>aGOU|3*( zLWf??ZV~3QQe6N8YTYz;<>(dfLm^M~$6h9G5YRj0%MiXbS;+fa0@Zo|2Grirn0DdY z`^FlKp^Tj|1q%{$>^P>>H_l5r#(biq7hpi`Esg!!-DA@;+i|vzl72(8&|ddv%38G@ zH_zsx+B<*&wf8hudbiE7GF|D7!|x@UuDO0GGk+{ds0lvIfogpK18V&=*5_krd1!3I zcXi)k5n+R7EbaaK)CGsayHITaU_fn<#^GX zKL8A<4bj-r9}0>$l9rzCIlj8BLLzUaP(z>Ui^6>7+j~C(45$s$SW?f80QRo6r3&Mj z-#6P&#+mQy>a+QvJQiT+b*4-7Lmk>?7UUpA;Cxuf@_q#CMy1{hEq zrLn2!YQM0~{OkM%>LODr?K^)b+nBCCw#@<6z5ooUeWfusmzuW=M%j}h=ENK1^Nh@R zkTl!2+AWxQc>E1uKy8f1R-17|$?~r^tPIp$vSiBr`SRQ3;iTVn`%!HiU_k9VjalgW zO#BeK_@k}zwwrRk*VkKWReMO^eCC__e*g@qP0(0-_@<$f(+9TuneDkYbx9yLGLJLg zvU-yFhTKVj0kxkrc2M)(z6=j<4^_!oW--Oi!`3p*Hr8rATTty6z<}Cs8vFUwbLeVc z*~qq(*Ml19^PO*&S@p>-u4A4EOaTn2P1D%LeOO+!Ao1?NCL6wo9b7NRnYYU}_5Y781{XXVsUhs|c;TAE>H*^RB45$gw*sZSJg_Ywm4+dOr z3d~+qXrrChcYMXTFLO45-bav1;6-=)(NM9)`Qrk3WgshvuaIw$3XZWWFXY3oxKIm&WdW`Jxm3 zo?HD*fm>S9hP`Uh*O&h$)wRV@Z63gY+I$+D=4Q`%#l}@2^l7F+8Cz`(zu-;-u0FO< zR9gTrptg|4OxlILd3WmBuHbqlP{0|csI^aYQ&3tZb3c^>7*La^u@wrfOYw`n5pFm3 zpIewTD|LU!g7V$3J(+KnQUDlGQ=~DQeqEdHFV-!<-JlYU{cM}-s2h13dTwwLjYa3| zWh2$^$$i-PvS9Cnl`adTeu_pPC~ie_QwA6?Hx(MwZ_@kt%f;6nnA>6+8_C-_6`jdA zd}-7>`(Cd6VrMLOKu+6&dFP-CFkqo-G&V!$RMJMB>v&M5LN%r{_)Aff*Zh02wHT_Y z0}QBX(Af4&)9l;dcpOi8@LVr={#i0Pq0T2r;G6=gX#xzWX(2|vte^DqlKs&Tp0ok~ zw2ay3?e_peWne;CfV!1s=u`MaBR0mfQGPtz@92ugn1k)GTN$v&|#u)NSv3z6ma`6&2U{ zhw{X=jNcB7Ks8H%0W~Wcdv0}Y-G`^=?ha{bSu5}4{hVxO5lq@#VgA_pI)DK+YZ~LX z=+oK1%st)Y8MM;aSYv~4yjdMUR5<#9HxhY@4N zPgO-C9p-&SH79@pHD|=Ad&{6oQGfgD5nkzaTh-Gp4227poDn#}|5*^N=|+ID*3iA> zCK|g&$eTXlj_&DruRG&pQ?7-w!M&-P@JipkiUwAI+C}5D zUYuWEunAoYI|0U8L)U^IjX6fYtnEzAY*Vt+@}74~cF0EZtzonH(oR&{1u&rIj~KPx zcZzUVrCw-ic}l0S45fXsk7$f9zB4i9G_r zlY4K*1)Q7Ey0~&i>uM)div}1_J49oL_-3%(;A}lp{h;0W-frHCSDxsduTpEJQ0*|l zfZ7on`<$BmP{y!lO=r!nn9bF~7q8@GNLLj+&O^1M00U|U+8MaF@VU(yG> zB3~@y%u0x0u)jmKV*mqcu{3r?Xzg$hX0SI+ed?1EnEuO|6 zd1mhIm{M7==UT=q>#8RfHAk-nsYhR8eqzFLfC04=G!|7qz;0D>i~F`l^cJgwRmP3G z=DvwfJEx3l2>=6Xi8S^+qWPoq@b0V21;Y;(65AuLDEZtKd-TH`)sg@P)RJjTL^xUB z9NVGb`DsJFqs9JWnN`#K2HE3ZpxQ}*0ksqw3(CF8Ju`)`sLfjU`m$2738_O%H%yZ{60AsD8W2Cb*X0UK_ z({JgcO>&Oqi}N3Q{oX8(XJf%znNLWj0t~2~qcJmklU}pQ=92VAp%gRg(^A?)b=&%Q zqHm(wd4K`6G#Z=S5`EGrkKgbbtZ13pA#~Bik0@xFT}4 zzxsW~RLbfLbPvOY+P%47Bgi3g)Md~$m5Bu)?2vH=Fva%gPmSaoL1O#{RComs8H zPl|7JpNUJGJMnxqs^tO^V=gz*K%OT@E?pKM9mvh!KU%t5nFrb!C zV|O~lo7w9gDlYK-eW0i9=q<*A-Rn;__qd{30liasjmH$Piy;AnSgRqC}J>pz$8MYUpp0ktbM*1JDw zhrZvd9e971LYz@|a-vR*Pt*6f22?8n7*H#vu~X*zv>F>N3N7KB!`3V0n{j{Noc+wFg|7h&sFl;0cK6SygKm9! zj6omn%rSKn`7eBHWmi{tpxSkS0ksMm>zVz%C{ya?gk{ZF(rMm{YLUT{y$YBF^A>Ug zU_k9Ajh!=PY}z2np#?lydW*)+jErs^xa&|Aucg~NQ~K3&J#NL(P>$a7XrZ?O1}wCa z#*Rvr+ZPrFS9$BTGrW_39=pt=ur_G*f4|)O4#0rgUBswE_wa5RAFu0N>=_=eCo|T5 zQeEz1UtZ;~ij}VKRrBg&xyUxf9{oeA^AzDRt+$q zcAv()4C^;tA3DrFYlFk)+Law%>5nd`tvR|y1l1k@45&S%G5zTl9&ud#S!zbMxy~&^ z&HE>=)P`LDu8C@o00z`*Xzcrnb>STK1b^V6m`@tR%D0vJ$xMq}OyjXXV5-dWl9@8i?cH@w(B{A6!`fK4c> zH3JN&J*P33js1!3;~^?5^4YlRG{WT5&dYrD+A)jyK^`pt18S`_ws{-A@xymne#gcL zkH%9^~LO@kg~600U}mG)4w$8s+Thw1{zPN#mSeU;4yIUUG23U?r-x0}QCW zq_J}%A1@ylZP?>eky|-C)VX`{B}+fgF1}P$dj&9{)Gy~x=|3KTOL&HUfE%GpMFzwmzL{l+`SJI?!^_XqC;?a!FVwY!!ZIAVdKO2u^E^EMq+}P5GIU?V4|29CXPv9 zGcieQ7B(A`!lW@7Yz`)i&Bf+n^RWfkLQD>m#}qI{ObJ_rDPt zv@soQ3APl|#g<`um_D`~Gr$ZnBg`0Eftg?{u~nEUW`?cC)?jNfbIby>#H_G&m^EgD z*&#-3fIo5);VlS{ZtQ~uay}~-M*H|akg>_>+*c+@D zdyBop-eY}OKQ@33Vjr*}>?1aeeZoet&)6vT1^bG9!^W_2>^t@Yo4_WqpV%+#H#UV$ zW9Zjt;p{jA=fF8}E}R?Z!Fh2E$8iGZ!};+UxByP#g18VajEmr+xEL;uOW-qcNqiPQ z8<)bRaT$CLE{o5_=i&451^7Z-4wuIja7A1RUxX{;D)?et6<5R6aSdD(*TS`N9efGC z6xYR<;d;0}z8p8e4RIsf7+-;#;4AS}xG8Riug2HlYjJbj0=LAi@O8K~ZiCz6cKCXH z18$Ey;EuQx?u>85H{mY0E4~?b!`<;MxCg!!--dhQ+i@@48{dKZ;J)}y+z;P{`{TRu zJ$L}V7Z1eu;X(L*`~V(|hv1=j7#@yC;F0)2JPMD-58;RLBluA~20we}F&4AK^84E&dpP zg4f~o_*1+AZ^WDMXLvLI9B;u}@fUa--j2V-U*R42YrGTh!n^Sv{0-iVzs29-@9{po zA0NO6@elYA{t+L>Kj9@0tBEzlTEd*LAS?+hVjW>k*buga9kHI+K-d!wgd^cZ zI1?L*O@s^KN^Bv)M94Af?2}B~1L?ja@ zi4@`#ahfJ*iF-s9QBB+@9uN3Dh-bV#=+S_B@qro(me#xqD9&h%wy(5# z&}g$KjeJ8>WH67uq@$RHu3f)- zg#;y**hFIluUIP z<*1W_1Bg){RZNYntDs2=4kAW#F*UL>T~hEP zV${bKQ=@G0&p1=R0pG-PR>2YL?_bv=1&0x%KChS>w;1Dtwoz&^H!e0iS{a^GTTTjo zLX7&rVrtxQVpyYxoria;hL#drlD@YgDL8@{^@+vQ$T~M;Qt&fk)JGOmV=kHHFSdGJ zlv$yg+BfBhgC?ZlC}PxS7E|NBOB}(pmjc=-CG4Rixk-#HbG~rp8Gd|IOdp z7Hse1x7(C|#q*09DfksJ>QjrUv4AV-{^t61pWkjP3&|(9CafU^zad6_Y%w(o9a1?i z;F8C?hx1|bzKI|Ub5d{&G3s-Rsj;{(o`d(%E9)DsbL=ed9apg=1;-Jib|`8bUl3jG z_H0p}=^f>>(*E&QXkYk_81>1;)X2Ksq)EXah*2M1OpUBevZHPSG1jlFHfM{BPnBW^ zHQb!8-X1?u&SuN@mUu_JC;EtfVt^PVJ`h91M`D=xM2rxhiBaMU@s;>Sj1l9+cj5=} zT4<8CD%9RDk!ZT-J6ZU{EPZxn#m$h+38IPoNpsXH|F!$i5c~x&)^!o}A%D}@|6l5y zp>+he*Kyq|sxotbCA<_pv3m-!ru+L(Oz=B~-IQp`dUoK+&e4ln<;{M(`T3H%vg#L# z3O?t2Lqsra**3EE5=S{EiJ!zT;sft*LX7Q!@ie-8?aFT^eqQ$VziKXN&fA}0RFCO* zT`>EpYO<*RX4RQOG>BJp{)JvwOlbylgl4CaUr!T zZs{0bb=B9^#T^Vgy!CR&hWp2*et+o`xoD$Z`he{*v&dYEE!$y62_uY+6D?0$kejv& zZx`;Xe*5GFKkGZfL|+T>0E~5mOb{o$d70C_(Jf9WNH?d^%%O`(`vy5cc(X~Rtg*@xwi7I(WGuo9H0PGr5} zc^MdkS@@qTURw~S%}mm}{n>@rQgN&^L)QU;Sm{o)6={y4R^5wQ7gw9i3s&vS&v3Kr zUAgCI)uDyA+gu;BZ~fni;s0F+PuUyL#2z^Dq03fOX3yW^of>wrIkHyYM_>`_)@54e z<=N1#%-p`5e9T)VKW%b3Ih*J1JcTDdDqQ&IL_*iR=iO%wXCD-uFDgsU0GOilPX)om zv|pPdk33%(-0OgDyaE6N9`Z?m{k2723kd@3@4FBmQV4K^n>&(qK2Nt2{6%j^8=iZj zon9C*|Cv!L%dL<0bF(Su98_TYDB1d@Z`;Y4L$^E9LssQ~(9h-+K%0em&6DVw7yNG{ zzvmS~H}nU_BIxqUqB}YLM|Ma~i6oHPJ%O89uY0CtHmIFmls2^>=}`aIHj=Kl>e-hb z_nta4ut@$@mOz^z+Pq=_^OU@oPGpY<9f&Xs`TA(7Gupi300ZtDBmf3%-kAV9h-2L| zCWF6cO-NcE$lg&rLHLl8fRp(9K6Msi+g?X@_~YIur1$S_F5=VGJu8^^YO3zacy84J zuZ9w=2r9@h{MH}(U1e0JE7)`f>*i%zW`o+i zQnaZZl`Fqk&`^urA=8jJWLbB!e(RPytm|Rx7N}lY>f+Yd-D}Odse?<4G zG64JQp2mlq1F#%C^aMwGT;cD|M-Q)szQ#opNm&|O%5Ho1w)W9)s_It)!o9-6-`Cy! z!?ns?RkT!bF2G)dm)~+S%z5OjzG~m=+5<8|3_%9Lkmb87ISVw`8*{OOIf-*R%z zys@>1=kgt3oH#Zg$>bRl91ABNithUK+gWGkEy*HLlg)-}ch)uq95$1TuUxT;OPWvc zzgxS^e^*5h?;O78yaa=vF^_K}FY`9P09CGh?x|t4JU#wc>1ux1=+j`BnT+b;6JuOk&c&Ub#ffBjZ*cG2%8AC7J@_j6+-*b>-y&|T3+ zwmlr~|J}FBq06mzygA-7I9l)E_3&2~$Li;|Zy%aUPKd{Zb;hq$oB#Q9m@V$VOEQO!MpkB0Luw*Moib1(D>JMiwGg9D8K{w!8P|~7h*76A)X2*8YDgW# zsM8s0WM#{1$R&tTr&`p=%9hlSOA(_^wWyJm>DG|Ch*773)X2&-Ysh7YQKx~_$jY>9 zNIk@;Q&ehXWvVr#K4R1Z;dCZU2EAjbN|dQ`4N8X`sQP}In( z_!*cHz|L*sJM<^+j|%3nT#ZqEzp;)q1{CYe_#4R;|H70=6U3<9jv6&WQd~Hu6*f;t zwrN;Mo8J*)U@HMuuxRy}&Pk(ZWr`YiH9wzbuOn9higjv2jil+nFeTCqF>2FOBkNX^ zU|_2O_9Vc?@P>P6cIxG7`z(IJ-*w~~K(S6uqLEztFHDIvM~u3SQ6uZrR2i5Bz}i3W zwKsq3h+R6u^^`Z%?Li%B2`JX7sWy^U|H72Wb%;^7YieYjnmPls1{lZIGL8|o3E9u# z=MF4MJUw1V+5n1mYU+)oEx`WTr<6!L#Hjt88oxg2pOyW+Lr0%eG$=CBf3GG3TMw|^ zs|(H~ZeKOGKTadt`C@EE9k~HetW(o$B<=r&DUl9{QTHv>$hy_E8JHu$?&4um+yIupeXfyb`9;5)d%6Ah+COI1k?w$E zotkbVx#eG&66t{$^>{~(tW(otU|Rv^Kc3UvJEt&7v}d$gFen4M@oYS*Ny~fq4Op-{e>Agrsz-nKl{PysLI)9qA1y)~PLTBzOD^QzCs3qmDVK zk#%Z@49pi`e&bbh;+M3X`c`ImVR{K+QAh3s6zkLs8%e)^VM^pK#HiyOYGj?7F$41l zn8a4jKs&8~(tgXl2QM{ zl*nkrsPi9cWSyEh13LsTx%&!b+e<7QeD4aZp3Iqz9;gljigjw{jpUJkVM^pt#He#F zYGj?7B?F5A*aEgQd`C%wJ= zY|6}pxrKEnOeLqubBIyrNYuz$6PhN^BS!6-)W}*hpC;1~qjnQ&WM!7qWIAHh-6J)! zGP7y&0%Fu%6*aQ5HPd7UV$|aRHL|i*(_|)M)FT-+vNDrt@*-l?VFER>GQ(*y3o+^; zn;Kb}@idu@7n#@6rI&7jwRx|%sZNuZ5u;9gsd2lTMJpd?X7mY9j(O?c zA8?6jvJf%qOph8_YwB$5Z0y2p%zrcg&&7`Z;pF0CXA}9KXD`^W! zPImNP40bjK`X=*#Z0K|Un14NM7JAN-1N{;G!_LNyzQz15CmW9_4})z5`WEv7GfV<*s>@Nu%C8aq&HTSh;S7eDcr*w43qf=leo=HG zbZId(EA%aNId*ZTWnLI9h>yWs6!VL@frQW!n19Lq)Bnz?2yJHO7d74#`=3AW-n%D2 OdFxIO-*PlUgny4-p_uX^PF?f#cH#O0fI;=D{3h#Dk=DSd3Xly z4i=MjbJ28BQqfRW5mQl8QCCn}si363%oRZh&MA{|iW48q8p6$98I`=Sg5Oo`zFXJ4 z-MSQhR|=43@2kEu1;4Z9b>9D&Brt}cFB~K?XYxJ_xc=rs1hVIem_C6BK-Y;NtkLkp zA^0H+emH`DAmq|7twi+lntmB&;lUx`ZzH&6N(g}o9l;?aegvSu{uNVDLdggLMG-t6 zFknFS0)R>)I09o0dWHxA&p;5RBY5V9*BZ_ljzBX&V`vza2LM%!;P7P!{{)KQTLJ4LgoM`8-#M6rI1n7*%3vD|3yi8~U9_w1McylsLW;)m(dP!^^v!{E-E2&Cf5*|~8S75OC*cty3FelOYsc{?P!{^cM}N2(dYA-` z0)&XR9Of*)wcr>TNW-V$(TR#f!G9{45kG>mqn7^RMOXp7fc`~rOGM|A!wBMAK=+vu zLhsUy5qcYCjQnmGjIrNy84*N#=s&Lip2JL({w`rO6A0K}!yq~|1k``DvoneUO%@8_ z5w6-m`?J$Oai3+{Jw8RG$iKrOEI1;XEi`GrUp)W1wZvOWVqP+-@!2+?1H3eZn-M4L zYNV5uX=rp4{oE5Q^u-60h6^`NUK8zKjOglrHm_dkpqk#tEnEQ5+XNafy)na7{;7+R z30-qQ__O*jeiUG6Th|n|olO_{BdwU!1~b%O976|m%^AjMZUKl49k8_gtN5XjKRX?y z;SftSlmA4`fP@pZq>n5ERj7 zXmCRU=qI)g{6BH)BD{Y$7sN~+*($h@hG(JSRWkS-1Vw514T79B{K_$5DZx!N{PH0I zTfudm=%z|p&|YvU4R0#QfoLYMQD}He8lI1a*X`n!8xpXg;e}~<13>}}uOzrb&_s}( zhSwlMYdbPWZ`n*4MS;Hw2*S38>ao|y%{MTh1`Q43T?bbLYgIQA5Eg$gHy1Y)gGl-f zid=H_j3_`LW%Nt0OPciJpSW|R|G~9f*~hO~FP?<^hGW2mW_K?61rzK=2ICve0K&47 zhUXPrh6o9wnOz~cU2q8v&ql*L(eM&9JddEx`9-or0@gIVpx~Mz0W|%JLjsGD^$BdO zG`u)sPQ!E4@G?UJn-Kv)vzepjQYwApiym9lLO4X=cFxCC{L7A%paSp@o>T|6hrn+estnZ4Ej(5rp5A zz7^m`CKNaS_{=OuzWMcQguu&oiI|mJqjx9P+XsxnjEZ`dwKnIwJ?(c^LIMMY4ZCvPQ zJgWY;W_D=d_XPVxze6?w!RMgw5f-dd!O6#ef~@Gc%|gxF$=6F+!^t<;k?IuW=;a&e zM)h^tt*q(kqU`GKuB@c#q^_pntfi%9Sff(StLbQEvCkktW37Lnao}!40~d2gWi@v% zYCxc4K%kp{fa5-8Wi1y^Hy3Y5w}3z|AE!V!#{jPow;G)|vW}yb30-)Dso(zn?#i2e zLmcOh-G}P79v1;Cgw?RX*b47#xVK+75fF-<~0QRo-TA9C2b zh36#)Be)O*7lh#UBe-aE!8!JOCj2o25dJd^Xf?b5--y1uco78N*lK~zS(O@b=$VpG z6Ca`t$rID7$Xzeqguw3~YR$LMHpcS7?{;CiN0UBJO~UVgd8z#UE|%x_XbWm%y4|R-wT0k_jAnf7Orx`WxOL7nBgFJEBRSGxtbaN z_G{aR^D8|@nc;Seeq{SK3>{>KpVRC4$XB|N>G&zBt$dwjTpGbleoI4Q$S|MeCuTUg z0Jmy&XQ~}D+`OJc(D>!pD`vRsY~AfKrA@b);T*-h_1#4IC(LkuMUUlQlL{6v!|#S6 zlQXB&o-)I`^Jn?E8@@B0|2bQCo7|dAY-c7v(2vjX?0%BT4As4EIwGrt?$Eb1I6&A;Iha}{+TtI)?K{t?cX@~o3Mfy=;acV>@PRqRl?D?%Vq{b%_1G{DSkdT{99RQzk;2c97VL|79L5D(0s6=b3c~8c6EG6o zkl=|J39e!2K?4+oZ52Icfh0K7(Nkhbf}1=+5| zaJlA75N^u2=6eBL$GJ$DAZ(3XEEoyy2Ds+^f*V|}`GSCZa<2IigWG7X`Jsk8FZ8q- z1`QWeq?bH^-j&}+&&m?f+mw3) zxi|&5iKC}asuj=vABmT#6=!wR^FFFmqJHzhW;LYJ2r!xJC!a|A*6+H&f)^_pp zMUM&G{ix`vfTI)DBcKK{L-!ay7C;{$pf90MB*IekJeJGdClEWO2&h4X|DzS9qAGtM z*g5*T1=ZlO10?$+BCY2u8ly2nu=38>+0}P=P|raDde@2Inz3UTm;uJmA<&1LbYG@f z3H->sfejP3ras!)Z43=KqE7$&noApc0DXe=S-c+g66n?ET)Dcrx)=js*U?-c0b zS%d2(;64#>H_+0C$Cw5i{jYdNwS`~=QOWq#kk)kC2l+GuM#I?{hTItKYs zy`89jdwpFU=VEjU2ymkYVp;oNqYASobb*t=k3JsHHvmTsaCs~U*~1N;cIc_nZ)JZ} z-F$LJ!_7_f6`SO+wGmnrP+gqy!~p!d_OJTy{iC{KGe*Xw|#g;0dt zS+bT->hAhF37*A2T}1n(A#epuIlko;kDa~DOJgs*yFcU>bWHik-n(spWGR|^xm32+ z&x#)ckAOlhZ{~xwd$y@c$~T@`W`tQWbr6YJa=_Bxe(1|X_T+edEXQwwQ0dp0lV;oY z{?KQ;SC?IR3fgxGJHNtusnR8lprL;Gjy78v2!wsxTk0vOVB&2SdjDfk%d2HD$TUz{ zlIFPb>0|#FQn{a=D0G@Y5n-QAmjf*WubgqbPu0QaU17B0MXnc` zd@muQL{3gQ7D{zCkJD@Oy8PHy00vnP$PcCNEKS@x@q^F(U=mNL0~9gwFm84B2`67k zDpwk6iGO|{0&l9fH6^@Ifw>!=pF#W6Y~ran^2!n^2kYB0|ue4PS8WM+Y|RKNccPL6II%xhYU% zxoU6zj=SxPm!)Z8Q97O8Uauf|?d}rY+|pK@N>c>XH3g2``t+((S+8gWztR2mPVHKV zgq(ja`$KxdyY@x?w6vQ0%J&dB0dk2UMfoj4d=K}#{~(5P%E0giO`6_sYX#rCYOm?* zKAFs@g^rZ~gO#So{Bq22TLw0n$;Pez3K2!ufLYP7Z%QwW2j7n9S1Fl7-~kAGJwj@^ z?z}4O`KVU2uffBl#K6Qf&9df6`Hw@AITf87a1xR3HFH-Ta`pp|o+cJp&EPLe{~>p$_zYA(`x_ZY0aA zuAGh3jw^@ujruyP4XsY?k!mR*X`7SQG(ufn!0gko8QWRju9(+}Igbg|E>OfV9oZ4M zW%baPFPB1QcU#~o5SRc1ixpKx<-T`{1d@CfsUQCck^Mf+8|qRzPEs#hgza-(6kHF1 z6!49o>SQ9i$xH87+U4~m=VBN>e`~tvtCXl(U1OY-;>6*@3t;#cL6O+QGdGo0d2Ts+ zd@&L6%7!k>%Nnnj7#yEUy~@?k=OJ0AMw>G>EZp!*=-HQL}J0sYRXCs^xS_XYfLKK@wvS;<+^o8zJp&QRAY zu)%+y>6K)y3y+R&2pTlV#0J^ED$hN6;fj9n+b=}fW z>vT*rAhK^l(d>eKuS?Acmm~qaIx+!)WKi+5M1NxE9S1+*wf&~YA85kx`Rqg`ezpj9 zM_+FidAO=P*$j^UdEm11(eT8p4o+U_8dH4yq||ZbeT2v?WR`uO;A@SaB?kwpmw=E=k9U+ zev|Tb(@pZ7TD{s-w@v!PoQocVS|JF&y? zi-3DBi{bJwhfacFivT}OJ$EsdjidbbtgPj%Xopvd<412XAr z^IdS>pO1|Q=JMu4kx}rzt%t>~Pvdyu&rc1-@)BF2NT3I2adu&n%K3UepF!_n8%GEv zfz+KI;rCl|5yf6JO}mE8$q?abn*25(ARF^jM7PIgQNBdo$u3>Z2xNgwA<*qk3{^Q| zrMIk4lwbGQ18lL9ZLv7VTgG3Gf1t9cVkBZgDIAw7@cP(f#c_qKIMGw}?}7zKu%@)e zF0}H?5!FHMPz}>%Web~Ip!s(2+3`}k^^pQGygMzT`QV2{h-}k-w0Yk)Tls8z-V~G1 zlYI#={IB2*x8I%nHltUz8nBrzRWJPpkpR7^akFnGfrn*ew!QFG`?wbBN(3|bHqq)+ zTk@Xuo)D2&F_?xT+?P&J%npWksm6@ms2ZEyqX>uc9Ki2Ca;S1l)|{uy+{a?N{1OzA zc_rdtXjtcD_VMgqEyvb^8ffJn5RUS~ee{>&sgsgwVwdw0fXHf3M51rIDYtgRmEJb0 z>b7T4R~z_t?4*PrX{Y6$&bZ>-mpib6v3s;#x#x7YLc8CFbmb=1ufWV4n zFZ08>e+K-DH)nsjYk18TzVQ~HYh{Aaiqvt=`}J35<;>snjdD_3k6nPe?f^cAnw0HU z>w9`yPvvn_^2Z>ex9>^bNTZ4GIASU9J+Ay!AL?oWZ!Qm5Z6TTo*HM(4N)kLU$7};8 z@weC8F0vHAMTio3?|B#M`V7qQp8eNO6mYLUQMfC3RUbQ4=5MbqcC$XFV}Md9@Q(Ki z+X^Uh1n~Qt$tcHJB!2or=omRCo}X-a$OzAG0>du=s@r4J9h-w{ zk2$ZsdE4jWGpLLrt&}psemTpEbVz%l1c&i$2vh>LccuPS+&Y(+){d*+vg+Cb5nZ15 zw+gNeyY#iMtRYVxG@XPVH-oL>-KtR>R;x0HD=sIr@RmRko5)*5hp8k|j^7il3JKFZ ztX@9>kJ8Ebyh!tNwo1kx7QE9tp)$WxZq{uTWg*ROuCI3~O|2V%y5hj%v#Zbf1b!25 z>!1B7>3B>JBJ9gDV(O=x;vbt=adi4-+1A7GPlN7u&tB8>x9oVm*JYZxkg;V%($CeX zaHqzRp&}KK#?u_M$)c2o)0P_yQ7|*RSNpDGq3#@_*P-Fk}bOW z(O#>KtM`|H5~M*@?Sjp#)c_1C=eO59>>_ z!q%4uPPp8-ZuR8Yd&11MJF&XicF>Br;1y?Xfp)npj>CRcYENRZZF&XhsXEkA9lw}7 z9{JEq{E^iZj73{^vHfZ%ht&^~!>8LT&Ti#~x|+b}tZjpqGLz!m>=tQJyLVtUw)qVI zlJ)o6#PVf0L)?ZV+l`>EkDyk&NuIA_IGEVDSm6FRr4B0FttPIK{XXDX*ap2EGT>H2#v z9LsGDj}AlyKxJYjTdy@ASt|8qz_EghZR-xK#LGddN#JSRTr z#dgCVfHO5_U)tx?STEnQ`%AqFETJ;*w%3oQ%`y+>dKV0(-OxL=4(f^l&of(EjSP|n zeyLAi7}VEEhT*f^y}Zc&<#Mj#B=;Q6)54}wP**NU^Q_&}JkCLfWH%yAFe|4AprI~te zi!!wG95lW|EYkY8)psYV+2>Tf#Wua05)gT>57gEHgR3zkEX0E|P}d+3O24!scop8T z&PcJTCqD{XMRz5?&fGJY!~Vh2K5x^jE1_74M+2?qmK{^?qohAv&>2yFwk#YfBg*35 zyM3Q}+LgaT+x=zp`47;sli(ULa;uG9f>uGC^V%)NPx&CSG&o*mW$F?wYMTjCeFdAZG)r8IVe#?^=Qz4?+vuZp-yrhnFH@VVvla(>H?Rf^cBD(T-BoPJ7 zq;=AHW!$eo5lgGt@9Q6h-Fd1#u)Q(&UUD%620*KtK|RN*H#7CSZ1;}aa63T9y!zPk zh27sYuQ||TZ6|tI76(P5z#+Moj)|N1l!Lp<9_225zytdzVYkJCv-gYK*eAwxd_=1? z`=PEJ&?F;TQ4q*haDPkIv_X#(R?Q6U>$$0MlIil)jw21zldwUl`k*C1ddtpu-)ZJ$|jIa*V4}M7X$xd-nYh7Qsd^q&%>3^Ss;*fvMWtu5vd;0zBI< z;Gy|Q5L9t!;UlXo&BtrmRt7yg-2}tOf8h##EB*A!)l@2<|9i5-2t2#U21sSuJ)X;k zo}ruHDyn6jkA+s2Tz_%SSLAy~T}(+B@4aLnZ0?tVq5l3;Bj;KauQupv)yY|4e`T!h zyERiYZQW3QN?EY|E(x1vsH+iN71z=;v!6QMQPFKDK-?DsUACwtl%(LrihR25*m*sa ztz;qa9vu3N0g?-J?v-Ds z+P@L}VVfHs#|1@j`RZ)vZ9;Ia`m%Hkryx)_j_wgdIRH_7Q0QU}91_nU;QmH$yijOI58&8cG1Y z`cna7u?F?ePlZ1Xp80+cMxP0$!!oSo{Z8?0oqF++tLG+mrm`qa=Sru`=&1$oG`osN zMP2tnBiF!U{WXW=HmfHaJ)T^@{|iebL{$4O3YvN@xELRGIfnyoX=a>#k(w!U>{SfXYB9xJHp9T*>wRIPeB`SsoN1m}R~_puzi zJ$R$acfEbKcIe*S8Mf`ASg$@1!WsHfsyHiBSn>V)d#(cUFnSYL`NJR+WP?i}0@R}a zy!iwi0?i-`bb%wFALN1|&`KNuuZiPel;t~^VCm%@XDJb!X6X@*1@DA&z@Tt5@sn^T zaa1@CObVX_Gr}#zU&615(c}nHEIEdhKu!e7~> zC&-sXt9jpu#*t5oMU!8Poh9dp&kFa6CyySgNI8;hrzF`=OLCl*6f2TE z0WL}wf?7!$u~V|0I40ReJVHq)&;(`wG$oq! zgK`W+NSy(xQZ>Bir5brlq`G*oN;UD-OAQc;_3jg|=p~ZM^pZ&BdI_XTy<}3A9s=&` zk&ppB-WfgaWPNZ(pOdzZP_EDOM4$ezegF09(T6nyAHk-qh7|blHf=4Wz-Op;FbaI= o_00hCz(;5U>mUU_EWWV@DN*N>{B589*VPJczOD-HUc24?AGY7`H2?qr diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx-748zp5z6lty6dthhcfyiqdmwh/work-products.bin b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx-748zp5z6lty6dthhcfyiqdmwh/work-products.bin deleted file mode 100644 index f14fa212f219913387580806c919bf2ec5829e9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108 zcmWFv_H<@oP&L%EG}JTD&C5*BD9Nc*&`3=-Pc|?zGc-~#GB7gLHL%b%Fx6ySdF`;# z7U#m3Ay@R**sD53apb0!B&H;mBpzgBEdsF*^1(#(ApD}z;u77|ycFHE%$!sJ{AVPO diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx.lock b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-163v13ki2m5wi/s-hb0z3arwfc-0qh96dx.lock deleted file mode 100755 index e69de29bb2d1..000000000000 diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/libfirst_steps_v1.d b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/libfirst_steps_v1.d deleted file mode 100644 index 26e39d15e183..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/libfirst_steps_v1.d +++ /dev/null @@ -1 +0,0 @@ -/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/libfirst_steps_v1.rlib: /Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v1/src/lib.rs diff --git a/docs/src/tutorial/first-steps-v1/target/kani/debug/.cargo-lock b/docs/src/tutorial/first-steps-v1/target/kani/debug/.cargo-lock deleted file mode 100644 index e69de29bb2d1..000000000000 From 068e7210c7d9bdd915c839907a962601cc28c246 Mon Sep 17 00:00:00 2001 From: edison Date: Tue, 7 Oct 2025 00:24:51 -0700 Subject: [PATCH 16/54] chore: Remove print lines and extract functions in kani-driver harness_runner.rs to minimize dependency --- kani-driver/src/call_cbmc.rs | 2 -- kani-driver/src/cbmc_property_renderer.rs | 2 -- kani-driver/src/harness_runner.rs | 18 +++--------------- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index cbc5fffa51b3..27f2f3fed725 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -163,9 +163,7 @@ impl KaniSession { // The timeout wasn't reached let output = res.unwrap()?; VerificationResult::from(output, harness.attributes.should_panic, start_time) - }; - // println!("{verification_results:?}"); Ok(verification_results) } diff --git a/kani-driver/src/cbmc_property_renderer.rs b/kani-driver/src/cbmc_property_renderer.rs index 92d5037ae415..f3e6791cde8f 100644 --- a/kani-driver/src/cbmc_property_renderer.rs +++ b/kani-driver/src/cbmc_property_renderer.rs @@ -183,9 +183,7 @@ pub fn kani_cbmc_output_filter( if !quiet { let formatted_item = format_item(&processed_item, output_format); if let Some(fmt_item) = formatted_item { - // println!("!"); println!("{fmt_item}"); - // println!("!"); } } // TODO: Record processed items and dump them into a JSON file diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index 54b17b024526..46297a7d19ad 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -10,7 +10,7 @@ use std::path::Path; use crate::args::{NumThreads, OutputFormat}; use crate::call_cbmc::{VerificationResult, VerificationStatus}; -use crate::frontend::{JsonHandler, create_verification_summary_json}; +use crate::frontend::{JsonHandler, schema_utils::add_runner_results_to_json}; use crate::project::Project; use crate::session::{BUG_REPORT_URL, KaniSession}; @@ -50,18 +50,6 @@ impl std::fmt::Display for FailFastHarnessInfo { } impl<'pr> HarnessRunner<'_, 'pr> { - /// Helper: push verification summary into JsonHandler using frontend utilities. - fn add_runner_results_json( - handler: &mut JsonHandler, - results: &[HarnessResult<'pr>], - selected: usize, - status_label: &str, - ) { - // Use frontend utility to create structured verification summary - let summary = create_verification_summary_json(results, selected, status_label); - handler.add_item("verification_results", summary); - } - /// Given a [`HarnessRunner`] (to abstract over how these harnesses were generated), this runs /// the proof-checking process for each harness in `harnesses`. pub(crate) fn check_all_harnesses( @@ -114,7 +102,7 @@ impl<'pr> HarnessRunner<'_, 'pr> { match results { Ok(results) => { if let Some(handler) = json_handler.as_deref_mut() { - Self::add_runner_results_json(handler, &results, harnesses.len(), "completed"); + add_runner_results_to_json(handler, &results, harnesses.len(), "completed"); } Ok(results) } @@ -127,7 +115,7 @@ impl<'pr> HarnessRunner<'_, 'pr> { }]; if let Some(handler) = json_handler.as_deref_mut() { - Self::add_runner_results_json( + add_runner_results_to_json( handler, &result, harnesses.len(), From 6747d35180cd353c4743084cc3245146e1ef2c8d Mon Sep 17 00:00:00 2001 From: edison Date: Tue, 7 Oct 2025 00:52:20 -0700 Subject: [PATCH 17/54] chore: Remove unnecessary time information in main --- kani-driver/src/frontend/schema_utils.rs | 15 ++++++- kani-driver/src/harness_runner.rs | 1 - kani-driver/src/main.rs | 20 --------- kani_beta1.json | 47 -------------------- lib.rs | 56 ------------------------ 5 files changed, 14 insertions(+), 125 deletions(-) delete mode 100644 kani_beta1.json delete mode 100644 lib.rs diff --git a/kani-driver/src/frontend/schema_utils.rs b/kani-driver/src/frontend/schema_utils.rs index 8cc8b4150cd0..e9b9c4ea91b7 100644 --- a/kani-driver/src/frontend/schema_utils.rs +++ b/kani-driver/src/frontend/schema_utils.rs @@ -61,7 +61,7 @@ pub fn create_harness_metadata_json(h: &HarnessMetadata) -> Value { "kind": format!("{:?}", h.attributes.kind), "should_panic": h.attributes.should_panic, }, - "Contract":{ + "contract":{ "contracted_function_name": h.contract.as_ref() .map(|c| c.contracted_function_name.as_str()), "recursion_tracker": h.contract.as_ref() @@ -133,6 +133,19 @@ pub fn create_verification_summary_json( }) } +/// Helper function to add verification results to JsonHandler +/// This utility function encapsulates the logic for adding verification summary to JSON output +pub fn add_runner_results_to_json( + handler: &mut JsonHandler, + results: &[HarnessResult], + selected: usize, + status_label: &str, +) { + // Use frontend utility to create structured verification summary + let summary = create_verification_summary_json(results, selected, status_label); + handler.add_item("verification_results", summary); +} + /// Process harness results and enrich JSON handler with additional metadata. /// This function handles the complex harness processing logic, combining verification results /// with harness metadata to create enriched JSON output. diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index 46297a7d19ad..d503e2e36a6b 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -223,7 +223,6 @@ impl KaniSession { } let mut result = self.with_timer(|| self.run_cbmc(binary, harness), "run_cbmc")?; - // println!("{result:?}"); self.process_output(&result, harness, thread_index); self.gen_and_add_concrete_playback(harness, &mut result)?; diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 0b52fbde9cfd..7f063c1713f4 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -133,16 +133,8 @@ fn standalone_main() -> Result<()> { /// Run verification on the given project. fn verify_project(project: Project, session: KaniSession) -> Result<()> { - let wall_start = SystemTime::now(); - let cpu_start = Instant::now(); - fn to_rfc3339(t: std::time::SystemTime) -> String { - let dt: OffsetDateTime = t.into(); - dt.format(&Rfc3339).unwrap() - } - debug!(?project, "verify_project"); let mut handler = JsonHandler::new(session.args.export_json.clone()); - // TODO: add session info let harnesses = session.determine_targets(project.get_all_harnesses())?; debug!(n = harnesses.len(), ?harnesses, "verify_project"); @@ -157,7 +149,6 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { // Verification let runner = harness_runner::HarnessRunner { sess: &session, project: &project }; - let results = runner.check_all_harnesses(&harnesses, Some(&mut handler))?; // Process harness results and add additional metadata using frontend utility function @@ -184,17 +175,6 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { handler.add_item("coverage", json!({"enabled": false})); } - let wall_end = SystemTime::now(); - let duration_ms = cpu_start.elapsed().as_millis() as u64; - handler.add_item( - "run_time", - json!({ - "started_at": to_rfc3339(wall_start), - "finished_at": to_rfc3339(wall_end), - "duration_ms": duration_ms, - - }), - ); handler.export()?; session.print_final_summary(&results) diff --git a/kani_beta1.json b/kani_beta1.json deleted file mode 100644 index 932172148c84..000000000000 --- a/kani_beta1.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "harnesses": [ - { - "fully_qualified_name": "verify_success", - "cbmc_function_name": "_RNvCscOSYLgomVfM_3lib14verify_success", - "crate_name": "lib", - "original": { - "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", - "start_line": 54, - "end_line": 59 - }, - "goto": "/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v2/src/lib__RNvCscOSYLgomVfM_3lib14verify_success.symtab.out", - "kind": "Proof", - "should_panic": false, - "has_loop_contracts": false, - "is_automatically_generated": false, - "solver": null, - "unwind_value": null, - "contract": null, - "stubs": [], - "verified_stubs": [] - }, - { - "fully_qualified_name": "will_fail", - "cbmc_function_name": "_RNvCscOSYLgomVfM_3lib9will_fail", - "crate_name": "lib", - "original": { - "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", - "start_line": 64, - "end_line": 67 - }, - "goto": "/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v2/src/lib__RNvCscOSYLgomVfM_3lib9will_fail.symtab.out", - "kind": "Proof", - "should_panic": false, - "has_loop_contracts": false, - "is_automatically_generated": false, - "solver": null, - "unwind_value": null, - "contract": null, - "stubs": [], - "verified_stubs": [] - } - ], - "coverage": { - "enabled": false - } -} \ No newline at end of file diff --git a/lib.rs b/lib.rs deleted file mode 100644 index d4b64df8f3e3..000000000000 --- a/lib.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT - -// ANCHOR: code -fn estimate_size(x: u32) -> u32 { - if x < 256 { - if x < 128 { - return 1; - } else { - return 3; - } - } else if x < 1024 { - if x > 1022 { - panic!("Oh no, a failing corner case!"); - } else { - return 5; - } - } else { - if x < 2048 { - return 7; - } else { - return 9; - } - } -} -// ANCHOR_END: code - -#[cfg(test)] -mod tests { - use super::*; - use proptest::prelude::*; - - #[test] - fn it_works() { - assert_eq!(estimate_size(1024), 7); - } - - // ANCHOR: proptest - proptest! { - #![proptest_config(ProptestConfig::with_cases(10000))] - #[test] - fn doesnt_crash(x: u32) { - estimate_size(x); - } - } - // ANCHOR_END: proptest -} - -// ANCHOR: kani -#[cfg(kani)] -#[kani::proof] -fn check_estimate_size() { - let x: u32 = kani::any(); - estimate_size(x); -} -// ANCHOR_END: kani From a2e7757eadc464f0172d998fdab9de7596cbecd5 Mon Sep 17 00:00:00 2001 From: edison Date: Tue, 7 Oct 2025 11:36:43 -0700 Subject: [PATCH 18/54] chore: Update s2n-quic submodule to match main branch --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index d2c07946d50f..1cca93bf0b23 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit d2c07946d50f32a7f005a27a31372aa399af0807 +Subproject commit 1cca93bf0b2392781060d1294aa7fde14b747c19 From 29f8c1836d6e832e9d38c81f9ac7c19d07587d4a Mon Sep 17 00:00:00 2001 From: ShrivyasShrivyas <159740064+ShrivyasShrivyas@users.noreply.github.com> Date: Sun, 12 Oct 2025 13:30:27 -0700 Subject: [PATCH 19/54] Update schema_utils.rs - Structure for any tool's output --- kani-driver/src/frontend/schema_utils.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/kani-driver/src/frontend/schema_utils.rs b/kani-driver/src/frontend/schema_utils.rs index e9b9c4ea91b7..48485b2a7331 100644 --- a/kani-driver/src/frontend/schema_utils.rs +++ b/kani-driver/src/frontend/schema_utils.rs @@ -209,3 +209,26 @@ pub fn process_harness_results( Ok(()) } + +/// Simple container to standardize tool outputs captured during verification +#[derive(Serialize)] +pub struct ToolOutput<'a> { + /// Arbitrary tool name key under which this output will be grouped + pub tool: &'a str, + /// Harness identifier this output belongs to + pub harness_id: &'a str, + /// Unparsed stdout text emitted by the tool + pub stdout: &'a str, +} + +/// Add a tool output entry to the JSON under a tool-named array +pub fn add_tool_output(handler: &mut JsonHandler, output: ToolOutput<'_>) { + // structure: top-level key is the tool name, value is an array of entries + handler.add_harness_detail( + output.tool, + json!({ + "harness_id": output.harness_id, + "stdout": output.stdout, + }), + ); +} From b0b12cbdf10adea585eae24a129d0416e93ce132 Mon Sep 17 00:00:00 2001 From: ShrivyasShrivyas Date: Mon, 13 Oct 2025 09:48:57 -0700 Subject: [PATCH 20/54] All CBMC changes --- kani-driver/src/call_cbmc.rs | 849 ++++++++++++++++++++++- kani-driver/src/frontend/schema_utils.rs | 47 +- kani-driver/src/main.rs | 3 +- 3 files changed, 896 insertions(+), 3 deletions(-) diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 27f2f3fed725..9ca93b5547c3 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -use anyhow::{Result, bail}; +/*use anyhow::{Result, bail}; use kani_metadata::{CbmcSolver, HarnessMetadata}; use regex::Regex; use rustc_demangle::demangle; @@ -587,3 +587,850 @@ mod tests { assert_eq!(resolve(&args_both, &harness_some), Some(1)); } } +*/ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use anyhow::{Result, bail}; +use kani_metadata::{CbmcSolver, HarnessMetadata}; +use regex::Regex; +use rustc_demangle::demangle; +use std::collections::BTreeMap; +use std::collections::btree_map::Entry; +use std::ffi::OsString; +use std::fmt::Write; +use std::path::Path; +use std::sync::OnceLock; +use std::time::{Duration, Instant}; +use strum_macros::Display; +use tokio::process::Command as TokioCommand; + +use crate::args::common::Verbosity; +use crate::args::{OutputFormat, VerificationArgs}; +use crate::cbmc_output_parser::{ + CheckStatus, Property, VerificationOutput, extract_results, process_cbmc_output, +}; +use crate::cbmc_property_renderer::{format_coverage, format_result, kani_cbmc_output_filter}; +use crate::coverage::cov_results::{CoverageCheck, CoverageResults}; +use crate::coverage::cov_results::{CoverageRegion, CoverageTerm}; +use crate::session::KaniSession; +use crate::util::render_command; + +/// CBMC version and system information +#[derive(Debug, Clone)] +pub struct CbmcInfo { + pub version: String, + pub os_info: String, +} + +/// CBMC runtime and execution statistics +#[derive(Debug, Clone, Default)] +pub struct CbmcStats { + pub runtime_symex_s: Option, + pub size_program_expression: Option, + pub slicing_removed_assignments: Option, + pub vccs_generated: Option, + pub vccs_remaining: Option, + pub runtime_postprocess_equation_s: Option, + pub runtime_convert_ssa_s: Option, + pub runtime_post_process_s: Option, + pub runtime_solver_s: Option, + pub runtime_decision_procedure_s: Option, +} + +impl KaniSession { + /// Get CBMC version and system information + pub fn get_cbmc_info(&self) -> Result { + let output = std::process::Command::new("cbmc") + .arg("--version") + .output() + .map_err(|_| anyhow::Error::msg("Failed to run cbmc --version"))?; + + let version_output = String::from_utf8_lossy(&output.stdout); + let lines: Vec<&str> = version_output.lines().collect(); + + // Extract version from first line (e.g., "6.7.1 (cbmc-6.7.1)") + let version = lines.get(0) + .and_then(|line| line.split_whitespace().next()) + .unwrap_or("unknown") + .to_string(); + + // For OS info, we'll use the system information since CBMC --version doesn't provide it + let os_info = format!("{} {} {}", + std::env::consts::ARCH, + std::env::consts::OS, + std::env::consts::FAMILY + ); + + Ok(CbmcInfo { version, os_info }) + } + + /// Extract CBMC statistics from a message + fn extract_cbmc_stats_from_message(message: &str) -> Option { + let mut stats = CbmcStats::default(); + let mut found_any = false; + + // Runtime Symex: 0.00408627s + if let Some(captures) = regex::Regex::new(r"Runtime Symex: ([-e\d\.]+)s").ok()?.captures(message) { + if let Ok(val) = captures[1].parse::() { + stats.runtime_symex_s = Some(val); + found_any = true; + } + } + + // size of program expression: 150 steps + if let Some(captures) = regex::Regex::new(r"size of program expression: (\d+) steps").ok()?.captures(message) { + if let Ok(val) = captures[1].parse::() { + stats.size_program_expression = Some(val); + found_any = true; + } + } + + // slicing removed 81 assignments + if let Some(captures) = regex::Regex::new(r"slicing removed (\d+) assignments").ok()?.captures(message) { + if let Ok(val) = captures[1].parse::() { + stats.slicing_removed_assignments = Some(val); + found_any = true; + } + } + + // Generated 1 VCC(s), 1 remaining after simplification + if let Some(captures) = regex::Regex::new(r"Generated (\d+) VCC\(s\), (\d+) remaining after simplification").ok()?.captures(message) { + if let Ok(generated) = captures[1].parse::() { + stats.vccs_generated = Some(generated); + found_any = true; + } + if let Ok(remaining) = captures[2].parse::() { + stats.vccs_remaining = Some(remaining); + found_any = true; + } + } + + // Runtime Postprocess Equation: 0.000767182s + if let Some(captures) = regex::Regex::new(r"Runtime Postprocess Equation: ([-e\d\.]+)s").ok()?.captures(message) { + if let Ok(val) = captures[1].parse::() { + stats.runtime_postprocess_equation_s = Some(val); + found_any = true; + } + } + + // Runtime Convert SSA: 0.000516981s + if let Some(captures) = regex::Regex::new(r"Runtime Convert SSA: ([-e\d\.]+)s").ok()?.captures(message) { + if let Ok(val) = captures[1].parse::() { + stats.runtime_convert_ssa_s = Some(val); + found_any = true; + } + } + + // Runtime Post-process: 0.000189636s + if let Some(captures) = regex::Regex::new(r"Runtime Post-process: ([-e\d\.]+)s").ok()?.captures(message) { + if let Ok(val) = captures[1].parse::() { + stats.runtime_post_process_s = Some(val); + found_any = true; + } + } + + // Runtime Solver: 0.00167592s + if let Some(captures) = regex::Regex::new(r"Runtime Solver: ([-e\d\.]+)s").ok()?.captures(message) { + if let Ok(val) = captures[1].parse::() { + stats.runtime_solver_s = Some(val); + found_any = true; + } + } + + // Runtime decision procedure: 0.00452419s + if let Some(captures) = regex::Regex::new(r"Runtime decision procedure: ([-e\d\.]+)s").ok()?.captures(message) { + if let Ok(val) = captures[1].parse::() { + stats.runtime_decision_procedure_s = Some(val); + found_any = true; + } + } + + if found_any { Some(stats) } else { None } + } +} + +/// We will use Cadical by default since it performed better than MiniSAT in our analysis. +/// Note: Kissat was marginally better, but it is an external solver which could be more unstable. +static DEFAULT_SOLVER: CbmcSolver = CbmcSolver::Cadical; + +#[derive(Clone, Copy, Debug, Display, PartialEq, Eq)] +pub enum VerificationStatus { + Success, + Failure, +} + +/// Represents failed properties in three different categories. +/// This simplifies the process to determine and format verification results. +#[derive(Clone, Copy, Debug)] +pub enum FailedProperties { + // No failures + None, + // One or more panic-related failures + PanicsOnly, + // One or more failures that aren't panic-related + Other, +} + +/// The possible CBMC exit statuses +#[derive(Clone, Copy, Debug)] +pub enum ExitStatus { + Timeout, + OutOfMemory, + /// the integer is the process exit status + Other(i32), +} + +/// Our (kani-driver) notions of CBMC results. +#[derive(Debug)] +pub struct VerificationResult { + /// Whether verification should be considered to have succeeded, or have failed. + pub status: VerificationStatus, + /// The compact representation for failed properties + pub failed_properties: FailedProperties, + /// The `Result` properties in detail or the exit_status of CBMC. + /// Note: CBMC process exit status is only potentially useful if `status` is `Failure`. + /// Kani will see CBMC report "failure" that's actually success (interpreting "failed" + /// checks like coverage as expected and desirable.) + pub results: Result, ExitStatus>, + /// The runtime duration of this CBMC invocation. + pub runtime: Duration, + /// Whether concrete playback generated a test + pub generated_concrete_test: bool, + /// The coverage results + pub coverage_results: Option, + /// CBMC execution statistics extracted from messages + pub cbmc_stats: Option, +} + +impl KaniSession { + /// Verify a goto binary that's been prepared with goto-instrument + pub fn run_cbmc(&self, file: &Path, harness: &HarnessMetadata) -> Result { + let args: Vec = self.cbmc_flags(file, harness)?; + + // TODO get cbmc path from self + let mut cmd = TokioCommand::new("cbmc"); + cmd.args(args); + + let verification_results = if self.args.output_format == crate::args::OutputFormat::Old { + if self.run_terminal_timeout(cmd).is_err() { + VerificationResult::mock_failure() + } else { + VerificationResult::mock_success() + } + } else { + // Add extra argument to receive the output in JSON format. + // Done here because now removed `--visualize` used the XML format instead. + // TODO: move this now that we don't use --visualize + cmd.arg("--json-ui"); + + self.runtime.block_on(self.run_cbmc_piped(cmd, harness))? + }; + + Ok(verification_results) + } + + async fn run_cbmc_piped( + &self, + mut cmd: TokioCommand, + harness: &HarnessMetadata, + ) -> Result { + if self.args.common_args.verbose() { + println!("[Kani] Running: `{}`", render_command(cmd.as_std()).to_string_lossy()); + } + // Spawn the CBMC process and process its output below + let mut cbmc_process = cmd + .stdout(std::process::Stdio::piped()) + .spawn() + .map_err(|_| anyhow::Error::msg("Failed to run cbmc"))?; + + let start_time = Instant::now(); + let mut collected_stats = CbmcStats::default(); + + let res = if let Some(timeout) = self.args.harness_timeout { + tokio::time::timeout( + timeout.into(), + process_cbmc_output(&mut cbmc_process, |i| { + // Collect stats from messages + if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = &i { + if let Some(stats) = Self::extract_cbmc_stats_from_message(message_text) { + // Merge stats + if stats.runtime_symex_s.is_some() { collected_stats.runtime_symex_s = stats.runtime_symex_s; } + if stats.size_program_expression.is_some() { collected_stats.size_program_expression = stats.size_program_expression; } + if stats.slicing_removed_assignments.is_some() { collected_stats.slicing_removed_assignments = stats.slicing_removed_assignments; } + if stats.vccs_generated.is_some() { collected_stats.vccs_generated = stats.vccs_generated; } + if stats.vccs_remaining.is_some() { collected_stats.vccs_remaining = stats.vccs_remaining; } + if stats.runtime_postprocess_equation_s.is_some() { collected_stats.runtime_postprocess_equation_s = stats.runtime_postprocess_equation_s; } + if stats.runtime_convert_ssa_s.is_some() { collected_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; } + if stats.runtime_post_process_s.is_some() { collected_stats.runtime_post_process_s = stats.runtime_post_process_s; } + if stats.runtime_solver_s.is_some() { collected_stats.runtime_solver_s = stats.runtime_solver_s; } + if stats.runtime_decision_procedure_s.is_some() { collected_stats.runtime_decision_procedure_s = stats.runtime_decision_procedure_s; } + } + } + + kani_cbmc_output_filter( + i, + self.args.extra_pointer_checks, + self.args.common_args.quiet, + &self.args.output_format, + ) + }), + ) + .await + } else { + Ok(process_cbmc_output(&mut cbmc_process, |i| { + // Collect stats from messages + if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = &i { + if let Some(stats) = Self::extract_cbmc_stats_from_message(message_text) { + // Merge stats + if stats.runtime_symex_s.is_some() { collected_stats.runtime_symex_s = stats.runtime_symex_s; } + if stats.size_program_expression.is_some() { collected_stats.size_program_expression = stats.size_program_expression; } + if stats.slicing_removed_assignments.is_some() { collected_stats.slicing_removed_assignments = stats.slicing_removed_assignments; } + if stats.vccs_generated.is_some() { collected_stats.vccs_generated = stats.vccs_generated; } + if stats.vccs_remaining.is_some() { collected_stats.vccs_remaining = stats.vccs_remaining; } + if stats.runtime_postprocess_equation_s.is_some() { collected_stats.runtime_postprocess_equation_s = stats.runtime_postprocess_equation_s; } + if stats.runtime_convert_ssa_s.is_some() { collected_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; } + if stats.runtime_post_process_s.is_some() { collected_stats.runtime_post_process_s = stats.runtime_post_process_s; } + if stats.runtime_solver_s.is_some() { collected_stats.runtime_solver_s = stats.runtime_solver_s; } + if stats.runtime_decision_procedure_s.is_some() { collected_stats.runtime_decision_procedure_s = stats.runtime_decision_procedure_s; } + } + } + + kani_cbmc_output_filter( + i, + self.args.extra_pointer_checks, + self.args.common_args.quiet, + &self.args.output_format, + ) + }) + .await) + }; + + if let Ok(output) = res { + // The timeout wasn't reached + Ok(VerificationResult::from(output?, harness.attributes.should_panic, start_time)) + } else { + // An error occurs if the timeout was reached + + // Kill the process + cbmc_process.kill().await?; + + Ok(VerificationResult { + status: VerificationStatus::Failure, + failed_properties: FailedProperties::None, + results: Err(ExitStatus::Timeout), + runtime: start_time.elapsed(), + generated_concrete_test: false, + coverage_results: None, + //}) + //} + cbmc_stats: None, + }) + } + // } else { + // // The timeout wasn't reached + // let output = res.unwrap()?; + // VerificationResult::from_with_stats(output, harness.attributes.should_panic, start_time, collected_stats) + + // }; + // // println!("{verification_results:?}"); + + // Ok(verification_results) + } + + /// "Internal," but also used by call_cbmc_viewer + pub fn cbmc_flags( + &self, + file: &Path, + harness_metadata: &HarnessMetadata, + ) -> Result> { + let mut args = self.cbmc_check_flags(); + + if let Some(object_bits) = self.args.cbmc_object_bits() { + args.push("--object-bits".into()); + args.push(object_bits.to_string().into()); + } + + if let Some(unwind_value) = resolve_unwind_value(&self.args, harness_metadata) { + args.push("--unwind".into()); + args.push(unwind_value.to_string().into()); + } + + self.handle_solver_args(&harness_metadata.attributes.solver, &mut args)?; + + if self.args.run_sanity_checks { + args.push("--validate-goto-model".into()); + args.push("--validate-ssa-equation".into()); + } + + if self.args.concrete_playback.is_none() && !self.args.no_slice_formula { + args.push("--slice-formula".into()); + } + + if self.args.concrete_playback.is_some() { + args.push("--trace".into()); + } + + args.extend(self.args.cbmc_args.iter().cloned()); + + args.push(file.to_owned().into_os_string()); + + // Make CBMC verbose by default to tell users about unwinding progress. This should be + // reviewed as CBMC's verbosity defaults evolve. + args.push("--verbosity".into()); + args.push("9".into()); + + Ok(args) + } + + /// Just the flags to CBMC that enable property checking of any sort. + pub fn cbmc_check_flags(&self) -> Vec { + let mut args = Vec::new(); + + // We assume that malloc cannot fail, see https://github.com/model-checking/kani/issues/891 + args.push("--no-malloc-may-fail".into()); + + // With PR #2630 we generate the appropriate checks directly rather than relying on CBMC's + // checks (which are for C semantics). + args.push("--no-undefined-shift-check".into()); + // With PR #647 we use Rust's `-C overflow-checks=on` instead of: + // --unsigned-overflow-check + // --signed-overflow-check + // So these options are deliberately skipped to avoid erroneously re-checking operations. + args.push("--no-signed-overflow-check".into()); + + if !self.args.checks.memory_safety_on() { + args.push("--no-bounds-check".into()); + args.push("--no-pointer-check".into()); + } + if self.args.checks.overflow_on() { + args.push("--nan-check".into()); + + // TODO: Implement conversion checks as an optional check. + // They are a well defined operation in rust, but they may yield unexpected results to + // many users. https://github.com/model-checking/kani/issues/840 + // We might want to create a transformation pass instead of enabling CBMC since Kani + // compiler sometimes rely on the bitwise conversion of signed <-> unsigned. + // args.push("--conversion-check".into()); + } else { + args.push("--no-div-by-zero-check".into()); + } + + if !self.args.checks.unwinding_on() { + args.push("--no-unwinding-assertions".into()); + } else { + args.push("--no-self-loops-to-assumptions".into()); + } + + if self.args.extra_pointer_checks { + // This was adding a lot of false positives with std dangling pointer. We should + // still catch any invalid dereference with --pointer-check. Thus, only enable them + // if the user explicitly request them. + args.push("--pointer-overflow-check".into()); + } else { + args.push("--no-pointer-primitive-check".into()); + } + + args + } + + pub fn handle_solver_args( + &self, + harness_solver: &Option, + args: &mut Vec, + ) -> Result<()> { + let solver = if let Some(solver) = &self.args.solver { + // `--solver` option takes precedence over attributes + solver + } else if let Some(solver) = harness_solver { + solver + } else { + &DEFAULT_SOLVER + }; + + match solver { + CbmcSolver::Bitwuzla => { + args.push("--bitwuzla".into()); + } + CbmcSolver::Cadical => { + args.push("--sat-solver".into()); + args.push("cadical".into()); + } + CbmcSolver::Cvc5 => { + args.push("--cvc5".into()); + } + CbmcSolver::Kissat => { + args.push("--external-sat-solver".into()); + args.push("kissat".into()); + } + CbmcSolver::Minisat => { + // Minisat is currently CBMC's default solver, so no need to + // pass any arguments + } + CbmcSolver::Z3 => { + args.push("--z3".into()); + } + CbmcSolver::Binary(solver_binary) => { + // Check if the specified binary exists in path + if which::which(solver_binary).is_err() { + bail!("the specified solver \"{solver_binary}\" was not found in path") + } + args.push("--external-sat-solver".into()); + args.push(solver_binary.into()); + } + } + Ok(()) + } +} + +impl VerificationResult { + /// Computes a `VerificationResult` (kani-driver's notion of the result of a CBMC call) from a + /// `VerificationOutput` (cbmc_output_parser's idea of CBMC results). + /// + /// NOTE: We actually ignore the CBMC exit status, in favor of two checks: + /// 1. Examining the actual results of CBMC properties. + /// (CBMC will regularly report "failure" but that's just our cover checks.) + /// 2. Positively checking for the presence of results. + /// (Do not mistake lack of results for success: report it as failure.) + fn from_with_stats( + output: VerificationOutput, + should_panic: bool, + start_time: Instant, + cbmc_stats: CbmcStats, + ) -> VerificationResult { + let runtime = start_time.elapsed(); + let (_, results) = extract_results(output.processed_items); + + let cbmc_stats = if cbmc_stats.runtime_symex_s.is_some() || cbmc_stats.size_program_expression.is_some() { + Some(cbmc_stats) + } else { + None + }; + + if let Some(results) = results { + let (status, failed_properties) = + verification_outcome_from_properties(&results, should_panic); + let coverage_results = coverage_results_from_properties(&results); + VerificationResult { + status, + failed_properties, + results: Ok(results), + runtime, + generated_concrete_test: false, + coverage_results, + cbmc_stats, + } + } else { + // We never got results from CBMC - something went wrong (e.g. crash) so it's failure + let exit_status = if output.process_status == 137 { + ExitStatus::OutOfMemory + } else { + ExitStatus::Other(output.process_status) + }; + VerificationResult { + status: VerificationStatus::Failure, + failed_properties: FailedProperties::Other, + results: Err(exit_status), + runtime, + generated_concrete_test: false, + coverage_results: None, + cbmc_stats, + } + } + } + + fn from( + output: VerificationOutput, + should_panic: bool, + start_time: Instant, + ) -> VerificationResult { + let runtime = start_time.elapsed(); + let (remaining_items, results) = extract_results(output.processed_items); + + // Collect CBMC stats from messages + let mut cbmc_stats = CbmcStats::default(); + for item in &remaining_items { + if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = item { + if let Some(stats) = KaniSession::extract_cbmc_stats_from_message(message_text) { + // Merge stats (later messages may have more complete info) + if stats.runtime_symex_s.is_some() { cbmc_stats.runtime_symex_s = stats.runtime_symex_s; } + if stats.size_program_expression.is_some() { cbmc_stats.size_program_expression = stats.size_program_expression; } + if stats.slicing_removed_assignments.is_some() { cbmc_stats.slicing_removed_assignments = stats.slicing_removed_assignments; } + if stats.vccs_generated.is_some() { cbmc_stats.vccs_generated = stats.vccs_generated; } + if stats.vccs_remaining.is_some() { cbmc_stats.vccs_remaining = stats.vccs_remaining; } + if stats.runtime_postprocess_equation_s.is_some() { cbmc_stats.runtime_postprocess_equation_s = stats.runtime_postprocess_equation_s; } + if stats.runtime_convert_ssa_s.is_some() { cbmc_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; } + if stats.runtime_post_process_s.is_some() { cbmc_stats.runtime_post_process_s = stats.runtime_post_process_s; } + if stats.runtime_solver_s.is_some() { cbmc_stats.runtime_solver_s = stats.runtime_solver_s; } + if stats.runtime_decision_procedure_s.is_some() { cbmc_stats.runtime_decision_procedure_s = stats.runtime_decision_procedure_s; } + } + } + } + + let cbmc_stats = if cbmc_stats.runtime_symex_s.is_some() || cbmc_stats.size_program_expression.is_some() { + Some(cbmc_stats) + } else { + None + }; + + if let Some(results) = results { + let (status, failed_properties) = + verification_outcome_from_properties(&results, should_panic); + let coverage_results = coverage_results_from_properties(&results); + VerificationResult { + status, + failed_properties, + results: Ok(results), + runtime, + generated_concrete_test: false, + coverage_results, + cbmc_stats, + } + } else { + // We never got results from CBMC - something went wrong (e.g. crash) so it's failure + let exit_status = if output.process_status == 137 { + ExitStatus::OutOfMemory + } else { + ExitStatus::Other(output.process_status) + }; + VerificationResult { + status: VerificationStatus::Failure, + failed_properties: FailedProperties::Other, + results: Err(exit_status), + runtime, + generated_concrete_test: false, + coverage_results: None, + cbmc_stats, + } + } + } + + pub fn mock_success() -> VerificationResult { + VerificationResult { + status: VerificationStatus::Success, + failed_properties: FailedProperties::None, + results: Ok(vec![]), + runtime: Duration::from_secs(0), + generated_concrete_test: false, + coverage_results: None, + cbmc_stats: None, + } + } + + fn mock_failure() -> VerificationResult { + VerificationResult { + status: VerificationStatus::Failure, + failed_properties: FailedProperties::Other, + // on failure, exit codes in theory might be used, + // but `mock_failure` should never be used in a context where they will, + // so again use something weird: + results: Err(ExitStatus::Other(42)), + runtime: Duration::from_secs(0), + generated_concrete_test: false, + coverage_results: None, + cbmc_stats: None, + } + } + + pub fn render(&self, output_format: &OutputFormat, should_panic: bool) -> String { + match &self.results { + Ok(results) => { + let status = self.status; + let failed_properties = self.failed_properties; + let show_checks = matches!(output_format, OutputFormat::Regular); + + let mut result = if let Some(cov_results) = &self.coverage_results { + format_coverage( + results, + cov_results, + status, + should_panic, + failed_properties, + show_checks, + ) + } else { + format_result(results, status, should_panic, failed_properties, show_checks) + }; + writeln!(result, "Verification Time: {}s", self.runtime.as_secs_f32()).unwrap(); + result + } + Err(exit_status) => { + let verification_result = console::style("FAILED").red(); + let (header, explanation) = match exit_status { + ExitStatus::OutOfMemory => ( + String::from("CBMC failed"), + "CBMC appears to have run out of memory. You may want to rerun your proof in \ + an environment with additional memory or use stubbing to reduce the size of the \ + code the verifier reasons about.\n", + ), + ExitStatus::Timeout => ( + String::from("CBMC failed"), + "CBMC timed out. You may want to rerun your proof with a larger timeout \ + or use stubbing to reduce the size of the code the verifier reasons about.\n", + ), + ExitStatus::Other(exit_status) => { + (format!("CBMC failed with status {exit_status}"), "") + } + }; + format!( + "\n{header}\n\ + VERIFICATION:- {verification_result}\n\ + {explanation}", + ) + } + } + } +} + +/// We decide if verification succeeded based on properties, not (typically) on exit code +fn verification_outcome_from_properties( + properties: &[Property], + should_panic: bool, +) -> (VerificationStatus, FailedProperties) { + let failed_properties = determine_failed_properties(properties); + let status = if should_panic { + match failed_properties { + FailedProperties::None | FailedProperties::Other => VerificationStatus::Failure, + FailedProperties::PanicsOnly => VerificationStatus::Success, + } + } else { + match failed_properties { + FailedProperties::None => VerificationStatus::Success, + FailedProperties::PanicsOnly | FailedProperties::Other => VerificationStatus::Failure, + } + }; + (status, failed_properties) +} + +/// Determines the `FailedProperties` variant that corresponds to an array of properties +fn determine_failed_properties(properties: &[Property]) -> FailedProperties { + let failed_properties: Vec<&Property> = + properties.iter().filter(|prop| prop.status == CheckStatus::Failure).collect(); + // Return `FAILURE` if there isn't at least one failed property + if failed_properties.is_empty() { + FailedProperties::None + } else { + // Check if all failed properties correspond to the `assertion` class. + // Note: Panics caused by `panic!` and `assert!` fall into this class. + let all_failed_checks_are_panics = + failed_properties.iter().all(|prop| prop.property_class() == "assertion"); + if all_failed_checks_are_panics { + FailedProperties::PanicsOnly + } else { + FailedProperties::Other + } + } +} + +fn coverage_results_from_properties(properties: &[Property]) -> Option { + let cov_properties: Vec<&Property> = + properties.iter().filter(|p| p.is_code_coverage_property()).collect(); + + if cov_properties.is_empty() { + return None; + } + + // Postprocessing the coverage results involves matching on the descriptions + // of code coverage properties with the `counter_re` regex. These are two + // real examples of such descriptions: + // + // ``` + // CounterIncrement(0) $test_cov$ - src/main.rs:5:1 - 6:15 + // ExpressionUsed(0) $test_cov$ - src/main.rs:6:19 - 6:28 + // ``` + // + // The span is further processed to extract the code region attributes. + // Ideally, we should have coverage mappings (i.e., the relation between + // counters and code regions) available in the coverage metadata: + // . If that were the + // case, we would not need the spans in these descriptions. + let counter_re = { + static COUNTER_RE: OnceLock = OnceLock::new(); + COUNTER_RE.get_or_init(|| { + Regex::new( + r#"^(?VirtualCounter\(bcb)(?[0-9]+)\) \$(?[^\$]+)\$ - (?.+)"#, + ) + .unwrap() + }) + }; + + let mut coverage_results: BTreeMap> = BTreeMap::default(); + + for prop in cov_properties { + let mut prop_processed = false; + if let Some(captures) = counter_re.captures(&prop.description) { + let counter_num = &captures["counter_num"]; + let function = demangle(&captures["func_name"]).to_string(); + let status = prop.status; + let span = captures["span"].to_string(); + + let counter_id = counter_num.parse().unwrap(); + let term = CoverageTerm::Counter(counter_id); + let region = CoverageRegion::from_str(span); + + let cov_check = CoverageCheck::new(function, term, region, status); + let file = cov_check.region.file.clone(); + + if let Entry::Vacant(e) = coverage_results.entry(file.clone()) { + e.insert(vec![cov_check]); + } else { + coverage_results.entry(file).and_modify(|checks| checks.push(cov_check)); + } + prop_processed = true; + } + + assert!(prop_processed, "error: coverage property not processed\n{prop:?}"); + } + + Some(CoverageResults::new(coverage_results)) +} +/// Solve Unwind Value from conflicting inputs of unwind values. (--default-unwind, annotation-unwind, --unwind) +pub fn resolve_unwind_value( + args: &VerificationArgs, + harness_metadata: &HarnessMetadata, +) -> Option { + // Check for which flag is being passed and prioritize extracting unwind from the + // respective flag/annotation. + args.unwind.or(harness_metadata.attributes.unwind_value).or(args.default_unwind) +} + +#[cfg(test)] +mod tests { + use crate::args; + use crate::metadata::tests::mock_proof_harness; + use clap::Parser; + + use super::*; + + #[test] + fn check_resolve_unwind_value() { + // Command line unwind value for specific harnesses take precedence over default annotation value + let args_empty = ["kani", "x.rs"]; + let args_only_default = ["kani", "x.rs", "--default-unwind", "2"]; + let args_only_harness = ["kani", "x.rs", "--unwind", "1", "--harness", "check_one"]; + let args_both = + ["kani", "x.rs", "--default-unwind", "2", "--unwind", "1", "--harness", "check_one"]; + + let harness_none = mock_proof_harness("check_one", None, None, None); + let harness_some = mock_proof_harness("check_one", Some(3), None, None); + + fn resolve(args: &[&str], harness: &HarnessMetadata) -> Option { + resolve_unwind_value( + &args::StandaloneArgs::try_parse_from(args).unwrap().verify_opts, + harness, + ) + } + + // test against no unwind annotation + assert_eq!(resolve(&args_empty, &harness_none), None); + assert_eq!(resolve(&args_only_default, &harness_none), Some(2)); + assert_eq!(resolve(&args_only_harness, &harness_none), Some(1)); + assert_eq!(resolve(&args_both, &harness_none), Some(1)); + + // test against unwind annotation + assert_eq!(resolve(&args_empty, &harness_some), Some(3)); + assert_eq!(resolve(&args_only_default, &harness_some), Some(3)); + assert_eq!(resolve(&args_only_harness, &harness_some), Some(1)); + assert_eq!(resolve(&args_both, &harness_some), Some(1)); + } +} \ No newline at end of file diff --git a/kani-driver/src/frontend/schema_utils.rs b/kani-driver/src/frontend/schema_utils.rs index 48485b2a7331..1d17e70f88a2 100644 --- a/kani-driver/src/frontend/schema_utils.rs +++ b/kani-driver/src/frontend/schema_utils.rs @@ -13,6 +13,8 @@ use anyhow::Result; use time::format_description::well_known::Rfc3339; use time::OffsetDateTime; use crate::project::Project; +use crate::session::KaniSession; +use serde::Serialize; /// Creates structured JSON metadata for an export run /// This utility function captures basic environment for the whole session @@ -210,6 +212,49 @@ pub fn process_harness_results( Ok(()) } +pub fn process_cbmc_results( + handler: &mut JsonHandler, + harnesses: &[&HarnessMetadata], + results: &[HarnessResult], + session: &KaniSession, +) -> Result<()> { + let cbmc_info_opt = session.get_cbmc_info().ok(); + for h in harnesses { + let harness_result = results.iter().find(|r| r.harness.pretty_name == h.pretty_name); + handler.add_harness_detail("cbmc", json!({ + // basic name for harnesses + "harness_id": h.pretty_name, + + // Per-harness CBMC info (key-value pairs) without parsing CBMC stdout + "cbmc_metadata": { + // Version / OS info (same for all harnesses in a run) + "version": cbmc_info_opt.as_ref().map(|i| i.version.clone()), + "os_info": cbmc_info_opt.as_ref().map(|i| i.os_info.clone()), + }, + // Configuration passed to CBMC for this harness + "configuration": { + "object_bits": session.args.cbmc_object_bits(), + "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)).unwrap_or_else(|| "Cadical".to_string()), + }, + + // CBMC execution statistics extracted from messages + "cbmc_stats": harness_result.and_then(|r| r.result.cbmc_stats.as_ref()).map(|s| json!({ + "runtime_symex_s": s.runtime_symex_s, + "size_program_expression": s.size_program_expression, + "slicing_removed_assignments": s.slicing_removed_assignments, + "vccs_generated": s.vccs_generated, + "vccs_remaining": s.vccs_remaining, + "runtime_postprocess_equation_s": s.runtime_postprocess_equation_s, + "runtime_convert_ssa_s": s.runtime_convert_ssa_s, + "runtime_post_process_s": s.runtime_post_process_s, + "runtime_solver_s": s.runtime_solver_s, + "runtime_decision_procedure_s": s.runtime_decision_procedure_s + })) + })); + } + Ok(()) +} + /// Simple container to standardize tool outputs captured during verification #[derive(Serialize)] pub struct ToolOutput<'a> { @@ -231,4 +276,4 @@ pub fn add_tool_output(handler: &mut JsonHandler, output: ToolOutput<'_>) { "stdout": output.stdout, }), ); -} +} \ No newline at end of file diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 7f063c1713f4..52af867d5ffb 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -14,7 +14,7 @@ use args_toml::join_args; use crate::args::StandaloneSubcommand; use crate::concrete_playback::playback::{playback_cargo, playback_standalone}; -use crate::frontend::{JsonHandler, create_metadata_json, create_harness_metadata_json, process_harness_results, create_project_metadata_json}; +use crate::frontend::{JsonHandler, create_metadata_json, create_harness_metadata_json, process_harness_results, create_project_metadata_json, process_cbmc_results}; use crate::list::collect_metadata::{list_cargo, list_standalone}; use crate::project::Project; use crate::session::KaniSession; @@ -153,6 +153,7 @@ fn verify_project(project: Project, session: KaniSession) -> Result<()> { // Process harness results and add additional metadata using frontend utility function process_harness_results(&mut handler, &harnesses, &results)?; + process_cbmc_results(&mut handler, &harnesses, &results, &session)?; if session.args.coverage { // We generate a timestamp to save the coverage data in a folder named From 162ea1967e75ab3c5a9f205f13643a926201a0af Mon Sep 17 00:00:00 2001 From: ShrivyasShrivyas Date: Mon, 13 Oct 2025 10:08:45 -0700 Subject: [PATCH 21/54] removed unnecessary methods --- kani-driver/src/call_cbmc.rs | 46 ------------------------------------ 1 file changed, 46 deletions(-) diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 9ca93b5547c3..74ffb7e74ec4 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -1092,52 +1092,6 @@ impl VerificationResult { /// (CBMC will regularly report "failure" but that's just our cover checks.) /// 2. Positively checking for the presence of results. /// (Do not mistake lack of results for success: report it as failure.) - fn from_with_stats( - output: VerificationOutput, - should_panic: bool, - start_time: Instant, - cbmc_stats: CbmcStats, - ) -> VerificationResult { - let runtime = start_time.elapsed(); - let (_, results) = extract_results(output.processed_items); - - let cbmc_stats = if cbmc_stats.runtime_symex_s.is_some() || cbmc_stats.size_program_expression.is_some() { - Some(cbmc_stats) - } else { - None - }; - - if let Some(results) = results { - let (status, failed_properties) = - verification_outcome_from_properties(&results, should_panic); - let coverage_results = coverage_results_from_properties(&results); - VerificationResult { - status, - failed_properties, - results: Ok(results), - runtime, - generated_concrete_test: false, - coverage_results, - cbmc_stats, - } - } else { - // We never got results from CBMC - something went wrong (e.g. crash) so it's failure - let exit_status = if output.process_status == 137 { - ExitStatus::OutOfMemory - } else { - ExitStatus::Other(output.process_status) - }; - VerificationResult { - status: VerificationStatus::Failure, - failed_properties: FailedProperties::Other, - results: Err(exit_status), - runtime, - generated_concrete_test: false, - coverage_results: None, - cbmc_stats, - } - } - } fn from( output: VerificationOutput, From 94199fc26c9d7bb6759d7f585c7d1cb4e2c60a48 Mon Sep 17 00:00:00 2001 From: edison Date: Wed, 22 Oct 2025 14:10:41 -0700 Subject: [PATCH 22/54] chore: format the code --- kani-driver/src/call_cbmc.rs | 191 +++++++++++++++++------ kani-driver/src/frontend/json_handler.rs | 7 +- kani-driver/src/frontend/schema_utils.rs | 34 ++-- kani-driver/src/main.rs | 5 +- kani-driver/src/util.rs | 2 +- 5 files changed, 168 insertions(+), 71 deletions(-) diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 74ffb7e74ec4..b6c4a220830f 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -648,16 +648,18 @@ impl KaniSession { let version_output = String::from_utf8_lossy(&output.stdout); let lines: Vec<&str> = version_output.lines().collect(); - + // Extract version from first line (e.g., "6.7.1 (cbmc-6.7.1)") - let version = lines.get(0) + let version = lines + .get(0) .and_then(|line| line.split_whitespace().next()) .unwrap_or("unknown") .to_string(); // For OS info, we'll use the system information since CBMC --version doesn't provide it - let os_info = format!("{} {} {}", - std::env::consts::ARCH, + let os_info = format!( + "{} {} {}", + std::env::consts::ARCH, std::env::consts::OS, std::env::consts::FAMILY ); @@ -671,7 +673,9 @@ impl KaniSession { let mut found_any = false; // Runtime Symex: 0.00408627s - if let Some(captures) = regex::Regex::new(r"Runtime Symex: ([-e\d\.]+)s").ok()?.captures(message) { + if let Some(captures) = + regex::Regex::new(r"Runtime Symex: ([-e\d\.]+)s").ok()?.captures(message) + { if let Ok(val) = captures[1].parse::() { stats.runtime_symex_s = Some(val); found_any = true; @@ -679,7 +683,9 @@ impl KaniSession { } // size of program expression: 150 steps - if let Some(captures) = regex::Regex::new(r"size of program expression: (\d+) steps").ok()?.captures(message) { + if let Some(captures) = + regex::Regex::new(r"size of program expression: (\d+) steps").ok()?.captures(message) + { if let Ok(val) = captures[1].parse::() { stats.size_program_expression = Some(val); found_any = true; @@ -687,7 +693,9 @@ impl KaniSession { } // slicing removed 81 assignments - if let Some(captures) = regex::Regex::new(r"slicing removed (\d+) assignments").ok()?.captures(message) { + if let Some(captures) = + regex::Regex::new(r"slicing removed (\d+) assignments").ok()?.captures(message) + { if let Ok(val) = captures[1].parse::() { stats.slicing_removed_assignments = Some(val); found_any = true; @@ -695,7 +703,11 @@ impl KaniSession { } // Generated 1 VCC(s), 1 remaining after simplification - if let Some(captures) = regex::Regex::new(r"Generated (\d+) VCC\(s\), (\d+) remaining after simplification").ok()?.captures(message) { + if let Some(captures) = + regex::Regex::new(r"Generated (\d+) VCC\(s\), (\d+) remaining after simplification") + .ok()? + .captures(message) + { if let Ok(generated) = captures[1].parse::() { stats.vccs_generated = Some(generated); found_any = true; @@ -707,7 +719,9 @@ impl KaniSession { } // Runtime Postprocess Equation: 0.000767182s - if let Some(captures) = regex::Regex::new(r"Runtime Postprocess Equation: ([-e\d\.]+)s").ok()?.captures(message) { + if let Some(captures) = + regex::Regex::new(r"Runtime Postprocess Equation: ([-e\d\.]+)s").ok()?.captures(message) + { if let Ok(val) = captures[1].parse::() { stats.runtime_postprocess_equation_s = Some(val); found_any = true; @@ -715,7 +729,9 @@ impl KaniSession { } // Runtime Convert SSA: 0.000516981s - if let Some(captures) = regex::Regex::new(r"Runtime Convert SSA: ([-e\d\.]+)s").ok()?.captures(message) { + if let Some(captures) = + regex::Regex::new(r"Runtime Convert SSA: ([-e\d\.]+)s").ok()?.captures(message) + { if let Ok(val) = captures[1].parse::() { stats.runtime_convert_ssa_s = Some(val); found_any = true; @@ -723,7 +739,9 @@ impl KaniSession { } // Runtime Post-process: 0.000189636s - if let Some(captures) = regex::Regex::new(r"Runtime Post-process: ([-e\d\.]+)s").ok()?.captures(message) { + if let Some(captures) = + regex::Regex::new(r"Runtime Post-process: ([-e\d\.]+)s").ok()?.captures(message) + { if let Ok(val) = captures[1].parse::() { stats.runtime_post_process_s = Some(val); found_any = true; @@ -731,7 +749,9 @@ impl KaniSession { } // Runtime Solver: 0.00167592s - if let Some(captures) = regex::Regex::new(r"Runtime Solver: ([-e\d\.]+)s").ok()?.captures(message) { + if let Some(captures) = + regex::Regex::new(r"Runtime Solver: ([-e\d\.]+)s").ok()?.captures(message) + { if let Ok(val) = captures[1].parse::() { stats.runtime_solver_s = Some(val); found_any = true; @@ -739,7 +759,9 @@ impl KaniSession { } // Runtime decision procedure: 0.00452419s - if let Some(captures) = regex::Regex::new(r"Runtime decision procedure: ([-e\d\.]+)s").ok()?.captures(message) { + if let Some(captures) = + regex::Regex::new(r"Runtime decision procedure: ([-e\d\.]+)s").ok()?.captures(message) + { if let Ok(val) = captures[1].parse::() { stats.runtime_decision_procedure_s = Some(val); found_any = true; @@ -852,22 +874,48 @@ impl KaniSession { timeout.into(), process_cbmc_output(&mut cbmc_process, |i| { // Collect stats from messages - if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = &i { + if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = &i + { if let Some(stats) = Self::extract_cbmc_stats_from_message(message_text) { // Merge stats - if stats.runtime_symex_s.is_some() { collected_stats.runtime_symex_s = stats.runtime_symex_s; } - if stats.size_program_expression.is_some() { collected_stats.size_program_expression = stats.size_program_expression; } - if stats.slicing_removed_assignments.is_some() { collected_stats.slicing_removed_assignments = stats.slicing_removed_assignments; } - if stats.vccs_generated.is_some() { collected_stats.vccs_generated = stats.vccs_generated; } - if stats.vccs_remaining.is_some() { collected_stats.vccs_remaining = stats.vccs_remaining; } - if stats.runtime_postprocess_equation_s.is_some() { collected_stats.runtime_postprocess_equation_s = stats.runtime_postprocess_equation_s; } - if stats.runtime_convert_ssa_s.is_some() { collected_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; } - if stats.runtime_post_process_s.is_some() { collected_stats.runtime_post_process_s = stats.runtime_post_process_s; } - if stats.runtime_solver_s.is_some() { collected_stats.runtime_solver_s = stats.runtime_solver_s; } - if stats.runtime_decision_procedure_s.is_some() { collected_stats.runtime_decision_procedure_s = stats.runtime_decision_procedure_s; } + if stats.runtime_symex_s.is_some() { + collected_stats.runtime_symex_s = stats.runtime_symex_s; + } + if stats.size_program_expression.is_some() { + collected_stats.size_program_expression = + stats.size_program_expression; + } + if stats.slicing_removed_assignments.is_some() { + collected_stats.slicing_removed_assignments = + stats.slicing_removed_assignments; + } + if stats.vccs_generated.is_some() { + collected_stats.vccs_generated = stats.vccs_generated; + } + if stats.vccs_remaining.is_some() { + collected_stats.vccs_remaining = stats.vccs_remaining; + } + if stats.runtime_postprocess_equation_s.is_some() { + collected_stats.runtime_postprocess_equation_s = + stats.runtime_postprocess_equation_s; + } + if stats.runtime_convert_ssa_s.is_some() { + collected_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; + } + if stats.runtime_post_process_s.is_some() { + collected_stats.runtime_post_process_s = + stats.runtime_post_process_s; + } + if stats.runtime_solver_s.is_some() { + collected_stats.runtime_solver_s = stats.runtime_solver_s; + } + if stats.runtime_decision_procedure_s.is_some() { + collected_stats.runtime_decision_procedure_s = + stats.runtime_decision_procedure_s; + } } } - + kani_cbmc_output_filter( i, self.args.extra_pointer_checks, @@ -883,19 +931,42 @@ impl KaniSession { if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = &i { if let Some(stats) = Self::extract_cbmc_stats_from_message(message_text) { // Merge stats - if stats.runtime_symex_s.is_some() { collected_stats.runtime_symex_s = stats.runtime_symex_s; } - if stats.size_program_expression.is_some() { collected_stats.size_program_expression = stats.size_program_expression; } - if stats.slicing_removed_assignments.is_some() { collected_stats.slicing_removed_assignments = stats.slicing_removed_assignments; } - if stats.vccs_generated.is_some() { collected_stats.vccs_generated = stats.vccs_generated; } - if stats.vccs_remaining.is_some() { collected_stats.vccs_remaining = stats.vccs_remaining; } - if stats.runtime_postprocess_equation_s.is_some() { collected_stats.runtime_postprocess_equation_s = stats.runtime_postprocess_equation_s; } - if stats.runtime_convert_ssa_s.is_some() { collected_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; } - if stats.runtime_post_process_s.is_some() { collected_stats.runtime_post_process_s = stats.runtime_post_process_s; } - if stats.runtime_solver_s.is_some() { collected_stats.runtime_solver_s = stats.runtime_solver_s; } - if stats.runtime_decision_procedure_s.is_some() { collected_stats.runtime_decision_procedure_s = stats.runtime_decision_procedure_s; } + if stats.runtime_symex_s.is_some() { + collected_stats.runtime_symex_s = stats.runtime_symex_s; + } + if stats.size_program_expression.is_some() { + collected_stats.size_program_expression = stats.size_program_expression; + } + if stats.slicing_removed_assignments.is_some() { + collected_stats.slicing_removed_assignments = + stats.slicing_removed_assignments; + } + if stats.vccs_generated.is_some() { + collected_stats.vccs_generated = stats.vccs_generated; + } + if stats.vccs_remaining.is_some() { + collected_stats.vccs_remaining = stats.vccs_remaining; + } + if stats.runtime_postprocess_equation_s.is_some() { + collected_stats.runtime_postprocess_equation_s = + stats.runtime_postprocess_equation_s; + } + if stats.runtime_convert_ssa_s.is_some() { + collected_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; + } + if stats.runtime_post_process_s.is_some() { + collected_stats.runtime_post_process_s = stats.runtime_post_process_s; + } + if stats.runtime_solver_s.is_some() { + collected_stats.runtime_solver_s = stats.runtime_solver_s; + } + if stats.runtime_decision_procedure_s.is_some() { + collected_stats.runtime_decision_procedure_s = + stats.runtime_decision_procedure_s; + } } } - + kani_cbmc_output_filter( i, self.args.extra_pointer_checks, @@ -1107,21 +1178,45 @@ impl VerificationResult { if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = item { if let Some(stats) = KaniSession::extract_cbmc_stats_from_message(message_text) { // Merge stats (later messages may have more complete info) - if stats.runtime_symex_s.is_some() { cbmc_stats.runtime_symex_s = stats.runtime_symex_s; } - if stats.size_program_expression.is_some() { cbmc_stats.size_program_expression = stats.size_program_expression; } - if stats.slicing_removed_assignments.is_some() { cbmc_stats.slicing_removed_assignments = stats.slicing_removed_assignments; } - if stats.vccs_generated.is_some() { cbmc_stats.vccs_generated = stats.vccs_generated; } - if stats.vccs_remaining.is_some() { cbmc_stats.vccs_remaining = stats.vccs_remaining; } - if stats.runtime_postprocess_equation_s.is_some() { cbmc_stats.runtime_postprocess_equation_s = stats.runtime_postprocess_equation_s; } - if stats.runtime_convert_ssa_s.is_some() { cbmc_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; } - if stats.runtime_post_process_s.is_some() { cbmc_stats.runtime_post_process_s = stats.runtime_post_process_s; } - if stats.runtime_solver_s.is_some() { cbmc_stats.runtime_solver_s = stats.runtime_solver_s; } - if stats.runtime_decision_procedure_s.is_some() { cbmc_stats.runtime_decision_procedure_s = stats.runtime_decision_procedure_s; } + if stats.runtime_symex_s.is_some() { + cbmc_stats.runtime_symex_s = stats.runtime_symex_s; + } + if stats.size_program_expression.is_some() { + cbmc_stats.size_program_expression = stats.size_program_expression; + } + if stats.slicing_removed_assignments.is_some() { + cbmc_stats.slicing_removed_assignments = stats.slicing_removed_assignments; + } + if stats.vccs_generated.is_some() { + cbmc_stats.vccs_generated = stats.vccs_generated; + } + if stats.vccs_remaining.is_some() { + cbmc_stats.vccs_remaining = stats.vccs_remaining; + } + if stats.runtime_postprocess_equation_s.is_some() { + cbmc_stats.runtime_postprocess_equation_s = + stats.runtime_postprocess_equation_s; + } + if stats.runtime_convert_ssa_s.is_some() { + cbmc_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; + } + if stats.runtime_post_process_s.is_some() { + cbmc_stats.runtime_post_process_s = stats.runtime_post_process_s; + } + if stats.runtime_solver_s.is_some() { + cbmc_stats.runtime_solver_s = stats.runtime_solver_s; + } + if stats.runtime_decision_procedure_s.is_some() { + cbmc_stats.runtime_decision_procedure_s = + stats.runtime_decision_procedure_s; + } } } } - let cbmc_stats = if cbmc_stats.runtime_symex_s.is_some() || cbmc_stats.size_program_expression.is_some() { + let cbmc_stats = if cbmc_stats.runtime_symex_s.is_some() + || cbmc_stats.size_program_expression.is_some() + { Some(cbmc_stats) } else { None @@ -1387,4 +1482,4 @@ mod tests { assert_eq!(resolve(&args_only_harness, &harness_some), Some(1)); assert_eq!(resolve(&args_both, &harness_some), Some(1)); } -} \ No newline at end of file +} diff --git a/kani-driver/src/frontend/json_handler.rs b/kani-driver/src/frontend/json_handler.rs index 71a7baac016c..4ed461fddaac 100644 --- a/kani-driver/src/frontend/json_handler.rs +++ b/kani-driver/src/frontend/json_handler.rs @@ -1,4 +1,4 @@ -use serde_json::{json, Value}; +use serde_json::{Value, json}; use std::path::PathBuf; pub struct JsonHandler { @@ -8,10 +8,7 @@ pub struct JsonHandler { impl JsonHandler { pub fn new(export_path: Option) -> Self { - Self { - data: json!({}), - export_path, - } + Self { data: json!({}), export_path } } pub fn add_item(&mut self, key: &str, value: Value) { diff --git a/kani-driver/src/frontend/schema_utils.rs b/kani-driver/src/frontend/schema_utils.rs index 1d17e70f88a2..807f9242beb2 100644 --- a/kani-driver/src/frontend/schema_utils.rs +++ b/kani-driver/src/frontend/schema_utils.rs @@ -5,16 +5,16 @@ //! This module contains helper functions to convert Kani internal structures to JSON use crate::call_cbmc::VerificationStatus; -use crate::harness_runner::HarnessResult; use crate::frontend::JsonHandler; -use kani_metadata::HarnessMetadata; -use serde_json::{Value, json}; -use anyhow::Result; -use time::format_description::well_known::Rfc3339; -use time::OffsetDateTime; +use crate::harness_runner::HarnessResult; use crate::project::Project; use crate::session::KaniSession; +use anyhow::Result; +use kani_metadata::HarnessMetadata; use serde::Serialize; +use serde_json::{Value, json}; +use time::OffsetDateTime; +use time::format_description::well_known::Rfc3339; /// Creates structured JSON metadata for an export run /// This utility function captures basic environment for the whole session @@ -116,11 +116,13 @@ pub fn create_verification_summary_json( selected: usize, status_label: &str, ) -> Value { - let successful = results.iter().filter(|r| r.result.status == VerificationStatus::Success).count(); + let successful = + results.iter().filter(|r| r.result.status == VerificationStatus::Success).count(); let failed = results.len() - successful; let total_duration_ms: u64 = results.iter().map(|r| r.result.runtime.as_millis() as u64).sum(); - - let verification_results: Vec<_> = results.iter().map(|r| create_verification_result_json(r)).collect(); + + let verification_results: Vec<_> = + results.iter().map(|r| create_verification_result_json(r)).collect(); json!({ "summary": { @@ -159,7 +161,7 @@ pub fn process_harness_results( // The main verification results are handled by the harness runner for h in harnesses { let harness_result = results.iter().find(|r| r.harness.pretty_name == h.pretty_name); - + // Add error details for this harness if let Some(result) = harness_result { handler.add_item("error_details", match result.result.status { @@ -184,7 +186,7 @@ pub fn process_harness_results( "has_errors": false }) }); - + // Add property details for this harness handler.add_harness_detail("property_details", json!({ "property_details": match &result.result.results { @@ -192,7 +194,7 @@ pub fn process_harness_results( let total_properties = properties.len(); let passed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Success)).count(); let failed_properties = properties.iter().filter(|p| matches!(p.status, crate::cbmc_output_parser::CheckStatus::Failure)).count(); - + json!({ "total_properties": total_properties, "passed": passed_properties, @@ -208,7 +210,7 @@ pub fn process_harness_results( })); } } - + Ok(()) } @@ -224,7 +226,7 @@ pub fn process_cbmc_results( handler.add_harness_detail("cbmc", json!({ // basic name for harnesses "harness_id": h.pretty_name, - + // Per-harness CBMC info (key-value pairs) without parsing CBMC stdout "cbmc_metadata": { // Version / OS info (same for all harnesses in a run) @@ -236,7 +238,7 @@ pub fn process_cbmc_results( "object_bits": session.args.cbmc_object_bits(), "solver": h.attributes.solver.as_ref().map(|s| format!("{:?}", s)).unwrap_or_else(|| "Cadical".to_string()), }, - + // CBMC execution statistics extracted from messages "cbmc_stats": harness_result.and_then(|r| r.result.cbmc_stats.as_ref()).map(|s| json!({ "runtime_symex_s": s.runtime_symex_s, @@ -276,4 +278,4 @@ pub fn add_tool_output(handler: &mut JsonHandler, output: ToolOutput<'_>) { "stdout": output.stdout, }), ); -} \ No newline at end of file +} diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 52af867d5ffb..13b6cd322b9c 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -14,7 +14,10 @@ use args_toml::join_args; use crate::args::StandaloneSubcommand; use crate::concrete_playback::playback::{playback_cargo, playback_standalone}; -use crate::frontend::{JsonHandler, create_metadata_json, create_harness_metadata_json, process_harness_results, create_project_metadata_json, process_cbmc_results}; +use crate::frontend::{ + JsonHandler, create_harness_metadata_json, create_metadata_json, create_project_metadata_json, + process_cbmc_results, process_harness_results, +}; use crate::list::collect_metadata::{list_cargo, list_standalone}; use crate::project::Project; use crate::session::KaniSession; diff --git a/kani-driver/src/util.rs b/kani-driver/src/util.rs index 77ff1d783144..b4a3dd954b7e 100644 --- a/kani-driver/src/util.rs +++ b/kani-driver/src/util.rs @@ -278,4 +278,4 @@ mod tests { c1.env("PARAM", "VALUE"); assert_eq!(render_command(&c1), OsString::from("PARAM=\"VALUE\" a b \"/c d/\"")); } -} \ No newline at end of file +} From 13db7d92f19cc75b3fa43fb7352e63e3e3a7b071 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 23 Oct 2025 02:22:10 -0700 Subject: [PATCH 23/54] fix: revert version back to 0.65.0 --- Cargo.lock | 2 +- library/kani/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4615c9dc849b..0b5780e50576 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1092,7 +1092,7 @@ dependencies = [ [[package]] name = "kani" -version = "0.65.1" +version = "0.65.0" dependencies = [ "kani_core", "kani_macros", diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index 221d6a487bdf..580a2b1f0241 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.65.1" +version = "0.65.0" edition = "2024" license = "MIT OR Apache-2.0" publish = false From 9a1deb342bbd7c6170f92296e230628b308f9d12 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 23 Oct 2025 02:29:55 -0700 Subject: [PATCH 24/54] chore: add example text for better commenting --- kani-driver/src/call_cbmc.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index b6c4a220830f..6148d0f8b049 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -672,7 +672,7 @@ impl KaniSession { let mut stats = CbmcStats::default(); let mut found_any = false; - // Runtime Symex: 0.00408627s + // Example: "Runtime Symex: 0.00408627s" if let Some(captures) = regex::Regex::new(r"Runtime Symex: ([-e\d\.]+)s").ok()?.captures(message) { @@ -682,7 +682,7 @@ impl KaniSession { } } - // size of program expression: 150 steps + // Example: "size of program expression: 150 steps" if let Some(captures) = regex::Regex::new(r"size of program expression: (\d+) steps").ok()?.captures(message) { @@ -692,7 +692,7 @@ impl KaniSession { } } - // slicing removed 81 assignments + // Example: "slicing removed 81 assignments" if let Some(captures) = regex::Regex::new(r"slicing removed (\d+) assignments").ok()?.captures(message) { @@ -702,7 +702,7 @@ impl KaniSession { } } - // Generated 1 VCC(s), 1 remaining after simplification + // Example: "Generated 1 VCC(s), 1 remaining after simplification" if let Some(captures) = regex::Regex::new(r"Generated (\d+) VCC\(s\), (\d+) remaining after simplification") .ok()? @@ -718,7 +718,7 @@ impl KaniSession { } } - // Runtime Postprocess Equation: 0.000767182s + // Example: "Runtime Postprocess Equation: 0.000767182s" if let Some(captures) = regex::Regex::new(r"Runtime Postprocess Equation: ([-e\d\.]+)s").ok()?.captures(message) { @@ -728,7 +728,7 @@ impl KaniSession { } } - // Runtime Convert SSA: 0.000516981s + // Example: "Runtime Convert SSA: 0.000516981s" if let Some(captures) = regex::Regex::new(r"Runtime Convert SSA: ([-e\d\.]+)s").ok()?.captures(message) { @@ -738,7 +738,7 @@ impl KaniSession { } } - // Runtime Post-process: 0.000189636s + // Example: "Runtime Post-process: 0.000189636s" if let Some(captures) = regex::Regex::new(r"Runtime Post-process: ([-e\d\.]+)s").ok()?.captures(message) { @@ -748,7 +748,7 @@ impl KaniSession { } } - // Runtime Solver: 0.00167592s + // Example: "Runtime Solver: 0.00167592s" if let Some(captures) = regex::Regex::new(r"Runtime Solver: ([-e\d\.]+)s").ok()?.captures(message) { @@ -758,7 +758,7 @@ impl KaniSession { } } - // Runtime decision procedure: 0.00452419s + // Example: "Runtime decision procedure: 0.00452419s" if let Some(captures) = regex::Regex::new(r"Runtime decision procedure: ([-e\d\.]+)s").ok()?.captures(message) { @@ -993,8 +993,6 @@ impl KaniSession { runtime: start_time.elapsed(), generated_concrete_test: false, coverage_results: None, - //}) - //} cbmc_stats: None, }) } From b169a7a3ca8b159ee8a03f09ddec965a33d18e10 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 23 Oct 2025 11:52:02 -0700 Subject: [PATCH 25/54] chore: remove s2n-quic submodule --- tests/perf/s2n-quic | 1 - 1 file changed, 1 deletion(-) delete mode 160000 tests/perf/s2n-quic diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic deleted file mode 160000 index 1cca93bf0b23..000000000000 --- a/tests/perf/s2n-quic +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1cca93bf0b2392781060d1294aa7fde14b747c19 From f5913e6b28b696945a869b7819ef239da7c60a80 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 23 Oct 2025 11:55:01 -0700 Subject: [PATCH 26/54] Revert "chore: Update s2n-quic submodule to match main branch" This reverts commit a2e7757eadc464f0172d998fdab9de7596cbecd5. --- tests/perf/s2n-quic | 1 + 1 file changed, 1 insertion(+) create mode 160000 tests/perf/s2n-quic diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic new file mode 160000 index 000000000000..d2c07946d50f --- /dev/null +++ b/tests/perf/s2n-quic @@ -0,0 +1 @@ +Subproject commit d2c07946d50f32a7f005a27a31372aa399af0807 From f9df1e454568288be2d87d7b8902adf21532ec31 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 23 Oct 2025 14:16:11 -0700 Subject: [PATCH 27/54] chore: Update s2n-quic submodule to match main --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index d2c07946d50f..44f24afead7a 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit d2c07946d50f32a7f005a27a31372aa399af0807 +Subproject commit 44f24afead7a13e4fe0332199dbefa39cd64ab4c From b560dc9c0a0a5c0d9d16471a5ccaf369e8454d54 Mon Sep 17 00:00:00 2001 From: edison Date: Mon, 27 Oct 2025 18:46:02 -0700 Subject: [PATCH 28/54] Roll back s2n-quic submodule to commit 26e2402 --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 44f24afead7a..26e2402eea1e 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 44f24afead7a13e4fe0332199dbefa39cd64ab4c +Subproject commit 26e2402eea1ea8c079705a58bbf5327f358521f0 From 947664b6c56942722798c12ced56758008d51419 Mon Sep 17 00:00:00 2001 From: ShrivyasShrivyas Date: Wed, 29 Oct 2025 01:21:03 -0700 Subject: [PATCH 29/54] Updated cbmc with original code and removed comments --- kani-driver/src/call_cbmc.rs | 614 +---------------------------------- 1 file changed, 9 insertions(+), 605 deletions(-) diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 6148d0f8b049..88343052f504 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -1,596 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -/*use anyhow::{Result, bail}; -use kani_metadata::{CbmcSolver, HarnessMetadata}; -use regex::Regex; -use rustc_demangle::demangle; -use std::collections::BTreeMap; -use std::collections::btree_map::Entry; -use std::ffi::OsString; -use std::fmt::Write; -use std::path::Path; -use std::sync::OnceLock; -use std::time::{Duration, Instant}; -use strum_macros::Display; -use tokio::process::Command as TokioCommand; - -use crate::args::common::Verbosity; -use crate::args::{OutputFormat, VerificationArgs}; -use crate::cbmc_output_parser::{ - CheckStatus, Property, VerificationOutput, extract_results, process_cbmc_output, -}; -use crate::cbmc_property_renderer::{format_coverage, format_result, kani_cbmc_output_filter}; -use crate::coverage::cov_results::{CoverageCheck, CoverageResults}; -use crate::coverage::cov_results::{CoverageRegion, CoverageTerm}; -use crate::session::KaniSession; -use crate::util::render_command; - -/// We will use Cadical by default since it performed better than MiniSAT in our analysis. -/// Note: Kissat was marginally better, but it is an external solver which could be more unstable. -static DEFAULT_SOLVER: CbmcSolver = CbmcSolver::Cadical; - -#[derive(Clone, Copy, Debug, Display, PartialEq, Eq)] -pub enum VerificationStatus { - Success, - Failure, -} - -/// Represents failed properties in three different categories. -/// This simplifies the process to determine and format verification results. -#[derive(Clone, Copy, Debug)] -pub enum FailedProperties { - // No failures - None, - // One or more panic-related failures - PanicsOnly, - // One or more failures that aren't panic-related - Other, -} - -/// The possible CBMC exit statuses -#[derive(Clone, Copy, Debug)] -pub enum ExitStatus { - Timeout, - OutOfMemory, - /// the integer is the process exit status - Other(i32), -} - -/// Our (kani-driver) notions of CBMC results. -#[derive(Debug)] -pub struct VerificationResult { - /// Whether verification should be considered to have succeeded, or have failed. - pub status: VerificationStatus, - /// The compact representation for failed properties - pub failed_properties: FailedProperties, - /// The `Result` properties in detail or the exit_status of CBMC. - /// Note: CBMC process exit status is only potentially useful if `status` is `Failure`. - /// Kani will see CBMC report "failure" that's actually success (interpreting "failed" - /// checks like coverage as expected and desirable.) - pub results: Result, ExitStatus>, - /// The runtime duration of this CBMC invocation. - pub runtime: Duration, - /// Whether concrete playback generated a test - pub generated_concrete_test: bool, - /// The coverage results - pub coverage_results: Option, -} - -impl KaniSession { - /// Verify a goto binary that's been prepared with goto-instrument - pub fn run_cbmc(&self, file: &Path, harness: &HarnessMetadata) -> Result { - let args: Vec = self.cbmc_flags(file, harness)?; - - // TODO get cbmc path from self - let mut cmd = TokioCommand::new("cbmc"); - cmd.args(args); - - let verification_results = if self.args.output_format == crate::args::OutputFormat::Old { - if self.run_terminal_timeout(cmd).is_err() { - VerificationResult::mock_failure() - } else { - VerificationResult::mock_success() - } - } else { - // Add extra argument to receive the output in JSON format. - // Done here because now removed `--visualize` used the XML format instead. - // TODO: move this now that we don't use --visualize - cmd.arg("--json-ui"); - - self.runtime.block_on(self.run_cbmc_piped(cmd, harness))? - }; - - Ok(verification_results) - } - - async fn run_cbmc_piped( - &self, - mut cmd: TokioCommand, - harness: &HarnessMetadata, - ) -> Result { - if self.args.common_args.verbose() { - println!("[Kani] Running: `{}`", render_command(cmd.as_std()).to_string_lossy()); - } - // Spawn the CBMC process and process its output below - let mut cbmc_process = cmd - .stdout(std::process::Stdio::piped()) - .spawn() - .map_err(|_| anyhow::Error::msg("Failed to run cbmc"))?; - - let start_time = Instant::now(); - - let res = if let Some(timeout) = self.args.harness_timeout { - tokio::time::timeout( - timeout.into(), - process_cbmc_output(&mut cbmc_process, |i| { - kani_cbmc_output_filter( - i, - self.args.extra_pointer_checks, - self.args.common_args.quiet, - &self.args.output_format, - ) - }), - ) - .await - } else { - Ok(process_cbmc_output(&mut cbmc_process, |i| { - kani_cbmc_output_filter( - i, - self.args.extra_pointer_checks, - self.args.common_args.quiet, - &self.args.output_format, - ) - }) - .await) - }; - - let verification_results = if res.is_err() { - // An error occurs if the timeout was reached - - // Kill the process - cbmc_process.kill().await?; - - VerificationResult { - status: VerificationStatus::Failure, - failed_properties: FailedProperties::None, - results: Err(ExitStatus::Timeout), - runtime: start_time.elapsed(), - generated_concrete_test: false, - coverage_results: None, - } - } else { - // The timeout wasn't reached - let output = res.unwrap()?; - VerificationResult::from(output, harness.attributes.should_panic, start_time) - }; - - Ok(verification_results) - } - - /// "Internal," but also used by call_cbmc_viewer - pub fn cbmc_flags( - &self, - file: &Path, - harness_metadata: &HarnessMetadata, - ) -> Result> { - let mut args = self.cbmc_check_flags(); - - if let Some(object_bits) = self.args.cbmc_object_bits() { - args.push("--object-bits".into()); - args.push(object_bits.to_string().into()); - } - - if let Some(unwind_value) = resolve_unwind_value(&self.args, harness_metadata) { - args.push("--unwind".into()); - args.push(unwind_value.to_string().into()); - } - - self.handle_solver_args(&harness_metadata.attributes.solver, &mut args)?; - - if self.args.run_sanity_checks { - args.push("--validate-goto-model".into()); - args.push("--validate-ssa-equation".into()); - } - - if self.args.concrete_playback.is_none() && !self.args.no_slice_formula { - args.push("--slice-formula".into()); - } - - if self.args.concrete_playback.is_some() { - args.push("--trace".into()); - } - - args.extend(self.args.cbmc_args.iter().cloned()); - - args.push(file.to_owned().into_os_string()); - - // Make CBMC verbose by default to tell users about unwinding progress. This should be - // reviewed as CBMC's verbosity defaults evolve. - args.push("--verbosity".into()); - args.push("9".into()); - - Ok(args) - } - - /// Just the flags to CBMC that enable property checking of any sort. - pub fn cbmc_check_flags(&self) -> Vec { - let mut args = Vec::new(); - - // We assume that malloc cannot fail, see https://github.com/model-checking/kani/issues/891 - args.push("--no-malloc-may-fail".into()); - - // With PR #2630 we generate the appropriate checks directly rather than relying on CBMC's - // checks (which are for C semantics). - args.push("--no-undefined-shift-check".into()); - // With PR #647 we use Rust's `-C overflow-checks=on` instead of: - // --unsigned-overflow-check - // --signed-overflow-check - // So these options are deliberately skipped to avoid erroneously re-checking operations. - args.push("--no-signed-overflow-check".into()); - - if !self.args.checks.memory_safety_on() { - args.push("--no-bounds-check".into()); - args.push("--no-pointer-check".into()); - } - if self.args.checks.overflow_on() { - args.push("--nan-check".into()); - - // TODO: Implement conversion checks as an optional check. - // They are a well defined operation in rust, but they may yield unexpected results to - // many users. https://github.com/model-checking/kani/issues/840 - // We might want to create a transformation pass instead of enabling CBMC since Kani - // compiler sometimes rely on the bitwise conversion of signed <-> unsigned. - // args.push("--conversion-check".into()); - } else { - args.push("--no-div-by-zero-check".into()); - } - - if !self.args.checks.unwinding_on() { - args.push("--no-unwinding-assertions".into()); - } else { - args.push("--no-self-loops-to-assumptions".into()); - } - - if self.args.extra_pointer_checks { - // This was adding a lot of false positives with std dangling pointer. We should - // still catch any invalid dereference with --pointer-check. Thus, only enable them - // if the user explicitly request them. - args.push("--pointer-overflow-check".into()); - } else { - args.push("--no-pointer-primitive-check".into()); - } - - args - } - - pub fn handle_solver_args( - &self, - harness_solver: &Option, - args: &mut Vec, - ) -> Result<()> { - let solver = if let Some(solver) = &self.args.solver { - // `--solver` option takes precedence over attributes - solver - } else if let Some(solver) = harness_solver { - solver - } else { - &DEFAULT_SOLVER - }; - - match solver { - CbmcSolver::Bitwuzla => { - args.push("--bitwuzla".into()); - } - CbmcSolver::Cadical => { - args.push("--sat-solver".into()); - args.push("cadical".into()); - } - CbmcSolver::Cvc5 => { - args.push("--cvc5".into()); - } - CbmcSolver::Kissat => { - args.push("--external-sat-solver".into()); - args.push("kissat".into()); - } - CbmcSolver::Minisat => { - // Minisat is currently CBMC's default solver, so no need to - // pass any arguments - } - CbmcSolver::Z3 => { - args.push("--z3".into()); - } - CbmcSolver::Binary(solver_binary) => { - // Check if the specified binary exists in path - if which::which(solver_binary).is_err() { - bail!("the specified solver \"{solver_binary}\" was not found in path") - } - args.push("--external-sat-solver".into()); - args.push(solver_binary.into()); - } - } - Ok(()) - } -} - -impl VerificationResult { - /// Computes a `VerificationResult` (kani-driver's notion of the result of a CBMC call) from a - /// `VerificationOutput` (cbmc_output_parser's idea of CBMC results). - /// - /// NOTE: We actually ignore the CBMC exit status, in favor of two checks: - /// 1. Examining the actual results of CBMC properties. - /// (CBMC will regularly report "failure" but that's just our cover checks.) - /// 2. Positively checking for the presence of results. - /// (Do not mistake lack of results for success: report it as failure.) - fn from( - output: VerificationOutput, - should_panic: bool, - start_time: Instant, - ) -> VerificationResult { - let runtime = start_time.elapsed(); - let (_, results) = extract_results(output.processed_items); - - if let Some(results) = results { - let (status, failed_properties) = - verification_outcome_from_properties(&results, should_panic); - let coverage_results = coverage_results_from_properties(&results); - VerificationResult { - status, - failed_properties, - results: Ok(results), - runtime, - generated_concrete_test: false, - coverage_results, - } - } else { - // We never got results from CBMC - something went wrong (e.g. crash) so it's failure - let exit_status = if output.process_status == 137 { - ExitStatus::OutOfMemory - } else { - ExitStatus::Other(output.process_status) - }; - VerificationResult { - status: VerificationStatus::Failure, - failed_properties: FailedProperties::Other, - results: Err(exit_status), - runtime, - generated_concrete_test: false, - coverage_results: None, - } - } - } - - pub fn mock_success() -> VerificationResult { - VerificationResult { - status: VerificationStatus::Success, - failed_properties: FailedProperties::None, - results: Ok(vec![]), - runtime: Duration::from_secs(0), - generated_concrete_test: false, - coverage_results: None, - } - } - - fn mock_failure() -> VerificationResult { - VerificationResult { - status: VerificationStatus::Failure, - failed_properties: FailedProperties::Other, - // on failure, exit codes in theory might be used, - // but `mock_failure` should never be used in a context where they will, - // so again use something weird: - results: Err(ExitStatus::Other(42)), - runtime: Duration::from_secs(0), - generated_concrete_test: false, - coverage_results: None, - } - } - - pub fn render(&self, output_format: &OutputFormat, should_panic: bool) -> String { - match &self.results { - Ok(results) => { - let status = self.status; - let failed_properties = self.failed_properties; - let show_checks = matches!(output_format, OutputFormat::Regular); - - let mut result = if let Some(cov_results) = &self.coverage_results { - format_coverage( - results, - cov_results, - status, - should_panic, - failed_properties, - show_checks, - ) - } else { - format_result(results, status, should_panic, failed_properties, show_checks) - }; - writeln!(result, "Verification Time: {}s", self.runtime.as_secs_f32()).unwrap(); - result - } - Err(exit_status) => { - let verification_result = console::style("FAILED").red(); - let (header, explanation) = match exit_status { - ExitStatus::OutOfMemory => ( - String::from("CBMC failed"), - "CBMC appears to have run out of memory. You may want to rerun your proof in \ - an environment with additional memory or use stubbing to reduce the size of the \ - code the verifier reasons about.\n", - ), - ExitStatus::Timeout => ( - String::from("CBMC failed"), - "CBMC timed out. You may want to rerun your proof with a larger timeout \ - or use stubbing to reduce the size of the code the verifier reasons about.\n", - ), - ExitStatus::Other(exit_status) => { - (format!("CBMC failed with status {exit_status}"), "") - } - }; - format!( - "\n{header}\n\ - VERIFICATION:- {verification_result}\n\ - {explanation}", - ) - } - } - } -} - -/// We decide if verification succeeded based on properties, not (typically) on exit code -fn verification_outcome_from_properties( - properties: &[Property], - should_panic: bool, -) -> (VerificationStatus, FailedProperties) { - let failed_properties = determine_failed_properties(properties); - let status = if should_panic { - match failed_properties { - FailedProperties::None | FailedProperties::Other => VerificationStatus::Failure, - FailedProperties::PanicsOnly => VerificationStatus::Success, - } - } else { - match failed_properties { - FailedProperties::None => VerificationStatus::Success, - FailedProperties::PanicsOnly | FailedProperties::Other => VerificationStatus::Failure, - } - }; - (status, failed_properties) -} - -/// Determines the `FailedProperties` variant that corresponds to an array of properties -fn determine_failed_properties(properties: &[Property]) -> FailedProperties { - let failed_properties: Vec<&Property> = - properties.iter().filter(|prop| prop.status == CheckStatus::Failure).collect(); - // Return `FAILURE` if there isn't at least one failed property - if failed_properties.is_empty() { - FailedProperties::None - } else { - // Check if all failed properties correspond to the `assertion` class. - // Note: Panics caused by `panic!` and `assert!` fall into this class. - let all_failed_checks_are_panics = - failed_properties.iter().all(|prop| prop.property_class() == "assertion"); - if all_failed_checks_are_panics { - FailedProperties::PanicsOnly - } else { - FailedProperties::Other - } - } -} - -fn coverage_results_from_properties(properties: &[Property]) -> Option { - let cov_properties: Vec<&Property> = - properties.iter().filter(|p| p.is_code_coverage_property()).collect(); - - if cov_properties.is_empty() { - return None; - } - - // Postprocessing the coverage results involves matching on the descriptions - // of code coverage properties with the `counter_re` regex. These are two - // real examples of such descriptions: - // - // ``` - // CounterIncrement(0) $test_cov$ - src/main.rs:5:1 - 6:15 - // ExpressionUsed(0) $test_cov$ - src/main.rs:6:19 - 6:28 - // ``` - // - // The span is further processed to extract the code region attributes. - // Ideally, we should have coverage mappings (i.e., the relation between - // counters and code regions) available in the coverage metadata: - // . If that were the - // case, we would not need the spans in these descriptions. - let counter_re = { - static COUNTER_RE: OnceLock = OnceLock::new(); - COUNTER_RE.get_or_init(|| { - Regex::new( - r#"^(?VirtualCounter\(bcb)(?[0-9]+)\) \$(?[^\$]+)\$ - (?.+)"#, - ) - .unwrap() - }) - }; - - let mut coverage_results: BTreeMap> = BTreeMap::default(); - - for prop in cov_properties { - let mut prop_processed = false; - if let Some(captures) = counter_re.captures(&prop.description) { - let counter_num = &captures["counter_num"]; - let function = demangle(&captures["func_name"]).to_string(); - let status = prop.status; - let span = captures["span"].to_string(); - - let counter_id = counter_num.parse().unwrap(); - let term = CoverageTerm::Counter(counter_id); - let region = CoverageRegion::from_str(span); - - let cov_check = CoverageCheck::new(function, term, region, status); - let file = cov_check.region.file.clone(); - - if let Entry::Vacant(e) = coverage_results.entry(file.clone()) { - e.insert(vec![cov_check]); - } else { - coverage_results.entry(file).and_modify(|checks| checks.push(cov_check)); - } - prop_processed = true; - } - - assert!(prop_processed, "error: coverage property not processed\n{prop:?}"); - } - - Some(CoverageResults::new(coverage_results)) -} -/// Solve Unwind Value from conflicting inputs of unwind values. (--default-unwind, annotation-unwind, --unwind) -pub fn resolve_unwind_value( - args: &VerificationArgs, - harness_metadata: &HarnessMetadata, -) -> Option { - // Check for which flag is being passed and prioritize extracting unwind from the - // respective flag/annotation. - args.unwind.or(harness_metadata.attributes.unwind_value).or(args.default_unwind) -} - -#[cfg(test)] -mod tests { - use crate::args; - use crate::metadata::tests::mock_proof_harness; - use clap::Parser; - - use super::*; - - #[test] - fn check_resolve_unwind_value() { - // Command line unwind value for specific harnesses take precedence over default annotation value - let args_empty = ["kani", "x.rs"]; - let args_only_default = ["kani", "x.rs", "--default-unwind", "2"]; - let args_only_harness = ["kani", "x.rs", "--unwind", "1", "--harness", "check_one"]; - let args_both = - ["kani", "x.rs", "--default-unwind", "2", "--unwind", "1", "--harness", "check_one"]; - - let harness_none = mock_proof_harness("check_one", None, None, None); - let harness_some = mock_proof_harness("check_one", Some(3), None, None); - - fn resolve(args: &[&str], harness: &HarnessMetadata) -> Option { - resolve_unwind_value( - &args::StandaloneArgs::try_parse_from(args).unwrap().verify_opts, - harness, - ) - } - - // test against no unwind annotation - assert_eq!(resolve(&args_empty, &harness_none), None); - assert_eq!(resolve(&args_only_default, &harness_none), Some(2)); - assert_eq!(resolve(&args_only_harness, &harness_none), Some(1)); - assert_eq!(resolve(&args_both, &harness_none), Some(1)); - - // test against unwind annotation - assert_eq!(resolve(&args_empty, &harness_some), Some(3)); - assert_eq!(resolve(&args_only_default, &harness_some), Some(3)); - assert_eq!(resolve(&args_only_harness, &harness_some), Some(1)); - assert_eq!(resolve(&args_both, &harness_some), Some(1)); - } -} -*/ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT - use anyhow::{Result, bail}; use kani_metadata::{CbmcSolver, HarnessMetadata}; use regex::Regex; @@ -977,16 +387,13 @@ impl KaniSession { .await) }; - if let Ok(output) = res { - // The timeout wasn't reached - Ok(VerificationResult::from(output?, harness.attributes.should_panic, start_time)) - } else { + let verification_results = if res.is_err() { // An error occurs if the timeout was reached // Kill the process cbmc_process.kill().await?; - Ok(VerificationResult { + VerificationResult { status: VerificationStatus::Failure, failed_properties: FailedProperties::None, results: Err(ExitStatus::Timeout), @@ -994,17 +401,14 @@ impl KaniSession { generated_concrete_test: false, coverage_results: None, cbmc_stats: None, - }) - } - // } else { - // // The timeout wasn't reached - // let output = res.unwrap()?; - // VerificationResult::from_with_stats(output, harness.attributes.should_panic, start_time, collected_stats) - - // }; - // // println!("{verification_results:?}"); + } + } else { + // The timeout wasn't reached + let output = res.unwrap()?; + VerificationResult::from(output, harness.attributes.should_panic, start_time) + }; - // Ok(verification_results) + Ok(verification_results) } /// "Internal," but also used by call_cbmc_viewer From bb4a2a22766542e85cb8c12277735f9034f70677 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 30 Oct 2025 11:14:34 -0700 Subject: [PATCH 30/54] test: update regression test to support json handler --- scripts/kani-regression.sh | 1 + scripts/validate_json_export.py | 209 ++++++++++++++++++ tests/json-handler/basic-export/config.yml | 4 + tests/json-handler/basic-export/test.rs | 22 ++ tests/json-handler/basic-export/test.sh | 28 +++ .../failed-verification/config.yml | 4 + .../json-handler/failed-verification/test.rs | 20 ++ .../json-handler/failed-verification/test.sh | 97 ++++++++ .../multiple-harnesses/config.yml | 4 + tests/json-handler/multiple-harnesses/test.rs | 42 ++++ tests/json-handler/multiple-harnesses/test.sh | 40 ++++ .../json-handler/schema-validation/config.yml | 4 + tests/json-handler/schema-validation/test.rs | 28 +++ tests/json-handler/schema-validation/test.sh | 77 +++++++ 14 files changed, 580 insertions(+) create mode 100755 scripts/validate_json_export.py create mode 100644 tests/json-handler/basic-export/config.yml create mode 100644 tests/json-handler/basic-export/test.rs create mode 100755 tests/json-handler/basic-export/test.sh create mode 100644 tests/json-handler/failed-verification/config.yml create mode 100644 tests/json-handler/failed-verification/test.rs create mode 100755 tests/json-handler/failed-verification/test.sh create mode 100644 tests/json-handler/multiple-harnesses/config.yml create mode 100644 tests/json-handler/multiple-harnesses/test.rs create mode 100755 tests/json-handler/multiple-harnesses/test.sh create mode 100644 tests/json-handler/schema-validation/config.yml create mode 100644 tests/json-handler/schema-validation/test.rs create mode 100755 tests/json-handler/schema-validation/test.sh diff --git a/scripts/kani-regression.sh b/scripts/kani-regression.sh index 5c7767ded3ce..968336865edb 100755 --- a/scripts/kani-regression.sh +++ b/scripts/kani-regression.sh @@ -60,6 +60,7 @@ TESTS=( "cargo-coverage cargo-coverage" "kani-docs cargo-kani" "kani-fixme kani-fixme" + "json-handler exec" ) # Build compiletest and print configuration. We pick suite / mode combo so there's no test. diff --git a/scripts/validate_json_export.py b/scripts/validate_json_export.py new file mode 100755 index 000000000000..8e94103d5211 --- /dev/null +++ b/scripts/validate_json_export.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python3 +""" +JSON Export Validation Script for Kani Integration Tests + +Validates JSON exports against the kani_json_schema.json template. +""" + +import json +import sys +import os +from pathlib import Path + + +def load_schema_template(): + """Load the JSON schema template""" + # Find schema template in scripts directory + script_dir = Path(__file__).parent + schema_path = script_dir / "json_schemas" / "kani_json_schema.json" + + if not schema_path.exists(): + print(f"ERROR: Schema template not found at {schema_path}") + return None + + with open(schema_path, "r") as f: + return json.load(f) + + +def validate_structure_recursive(data, schema, path=""): + """ + Recursively validate data structure against schema template. + + Args: + data: The JSON data to validate + schema: The schema template to validate against + path: Current path in the structure (for error messages) + + Returns: + (success: bool, errors: list) + """ + errors = [] + + # Handle dict validation + if isinstance(schema, dict) and isinstance(data, dict): + # All fields in schema are required + schema_keys = [k for k in schema.keys() if not k.startswith("_")] + + # Check each key in schema + for key in schema_keys: + # Check if field exists in data + if key not in data: + current_path = f"{path}.{key}" if path else key + errors.append(f"Missing required field: {current_path}") + continue + + # Recursively validate nested structure + current_path = f"{path}.{key}" if path else key + sub_errors = validate_structure_recursive( + data[key], schema[key], current_path + )[1] + errors.extend(sub_errors) + + # Handle array validation + elif isinstance(schema, list) and len(schema) > 0: + if not isinstance(data, list): + errors.append(f"Expected array at {path}, got {type(data).__name__}") + elif len(data) > 0: + # Validate first item against schema template + sub_errors = validate_structure_recursive(data[0], schema[0], f"{path}[0]")[ + 1 + ] + errors.extend(sub_errors) + + # Leaf values - no validation needed + + success = len(errors) == 0 + return success, errors + + +def validate_json_structure(json_file, schema=None): + """ + Validate that JSON export matches the schema template structure. + """ + try: + with open(json_file, "r") as f: + data = json.load(f) + except FileNotFoundError: + print(f"ERROR: JSON file {json_file} not found") + return False + except json.JSONDecodeError as e: + print(f"ERROR: Invalid JSON in {json_file}: {e}") + return False + + # Load schema if not provided + if schema is None: + schema = load_schema_template() + if schema is None: + return False + + # All schema fields are required - validate structure recursively + # The recursive validator will catch any missing required fields + success, all_errors = validate_structure_recursive(data, schema, "") + + if not success or all_errors: + print(f"ERROR: Validation failed for {json_file}:") + for error in all_errors: + print(f" - {error}") + return False + + print(f"JSON structure validation passed for {json_file}") + return True + + +def validate_field_path(json_file, field_path, schema=None): + """ + Validate specific fields at a given path. + + Args: + json_file: Path to JSON file + field_path: Dot-separated path (e.g., 'metadata', 'verification_results.summary') + schema: Optional pre-loaded schema + """ + try: + with open(json_file, "r") as f: + data = json.load(f) + except Exception as e: + print(f"ERROR: Failed to load {json_file}: {e}") + return False + + # Load schema if not provided + if schema is None: + schema = load_schema_template() + if schema is None: + return False + + # Navigate to the field in both data and schema + parts = field_path.split(".") + current_data = data + current_schema = schema + + for part in parts: + if part not in current_data: + print(f"ERROR: Field path '{field_path}' not found in data") + return False + current_data = current_data[part] + + if part not in current_schema: + print(f"ERROR: Field path '{field_path}' not found in schema template") + return False + current_schema = current_schema[part] + + # Handle arrays - check first item + if isinstance(current_schema, list) and len(current_schema) > 0: + current_schema = current_schema[0] + if isinstance(current_data, list) and len(current_data) > 0: + current_data = current_data[0] + + # Validate structure at this path + success, errors = validate_structure_recursive( + current_data, current_schema, field_path + ) + + if not success: + print(f"ERROR: Validation failed for {field_path}:") + for error in errors: + print(f" - {error}") + return False + + print(f"Field validation passed for {field_path}") + return True + + +def main(): + if len(sys.argv) < 2: + print( + "Usage: python3 validate_json_export.py [--field-path ]" + ) + sys.exit(1) + + json_file = sys.argv[1] + + # Check if specific field validation requested + if len(sys.argv) > 2 and sys.argv[2] == "--field-path": + if len(sys.argv) < 4: + print("ERROR: --field-path requires a path argument") + sys.exit(1) + + field_path = sys.argv[3] + if validate_field_path(json_file, field_path): + sys.exit(0) + else: + sys.exit(1) + + # Load schema once + schema = load_schema_template() + if schema is None: + print("ERROR: Could not load schema template") + sys.exit(1) + + # Run full validation + if validate_json_structure(json_file, schema): + print(f"\nAll validations passed for {json_file}") + sys.exit(0) + else: + print(f"\nValidation failed for {json_file}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/tests/json-handler/basic-export/config.yml b/tests/json-handler/basic-export/config.yml new file mode 100644 index 000000000000..938834ea42cb --- /dev/null +++ b/tests/json-handler/basic-export/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: test.sh + diff --git a/tests/json-handler/basic-export/test.rs b/tests/json-handler/basic-export/test.rs new file mode 100644 index 000000000000..1b43dc7373f2 --- /dev/null +++ b/tests/json-handler/basic-export/test.rs @@ -0,0 +1,22 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: --export-json basic_test_output.json + +//! Basic test for JSON export functionality +//! This verifies that the frontend module can export verification results + +fn add_numbers(a: u32, b: u32) -> u32 { + a + b +} + +#[kani::proof] +fn verify_add_numbers() { + let x: u32 = kani::any(); + let y: u32 = kani::any(); + kani::assume(x < 100); + kani::assume(y < 100); + let result = add_numbers(x, y); + assert!(result >= x); + assert!(result >= y); +} diff --git a/tests/json-handler/basic-export/test.sh b/tests/json-handler/basic-export/test.sh new file mode 100755 index 000000000000..fa237cabf55d --- /dev/null +++ b/tests/json-handler/basic-export/test.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +# Test JSON export basic functionality - validates schema_utils.rs functions + +set -eu + +OUTPUT_FILE="test_output.json" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +VALIDATOR="$PROJECT_ROOT/scripts/validate_json_export.py" + +# Run Kani with JSON export +kani test.rs --export-json "$OUTPUT_FILE" + +# Check that JSON file was created +if [ ! -f "$OUTPUT_FILE" ]; then + echo "ERROR: JSON file $OUTPUT_FILE was not created" + exit 1 +fi + +# Validate JSON structure using the validation script (suppress verbose output) +python3 "$VALIDATOR" "$OUTPUT_FILE" 2>&1 | tail -1 + +# Clean up +rm -f "$OUTPUT_FILE" + diff --git a/tests/json-handler/failed-verification/config.yml b/tests/json-handler/failed-verification/config.yml new file mode 100644 index 000000000000..938834ea42cb --- /dev/null +++ b/tests/json-handler/failed-verification/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: test.sh + diff --git a/tests/json-handler/failed-verification/test.rs b/tests/json-handler/failed-verification/test.rs new file mode 100644 index 000000000000..d5c459c09a96 --- /dev/null +++ b/tests/json-handler/failed-verification/test.rs @@ -0,0 +1,20 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: --export-json failed_output.json + +//! Test JSON export with failed verification +//! Verifies that frontend correctly captures failure information in JSON + +fn unsafe_subtract(a: u32, b: u32) -> u32 { + a - b // This will fail with overflow +} + +#[kani::proof] +fn verify_unsafe_subtract() { + let x: u32 = kani::any(); + let y: u32 = kani::any(); + // No assumptions - will trigger overflow + let result = unsafe_subtract(x, y); + assert!(result <= x); // This will also fail when y > x +} diff --git a/tests/json-handler/failed-verification/test.sh b/tests/json-handler/failed-verification/test.sh new file mode 100755 index 000000000000..e4477d5dfde9 --- /dev/null +++ b/tests/json-handler/failed-verification/test.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +# Test JSON export with failed verification - validates error capture + +set -eu + +OUTPUT_FILE="failed_output.json" + +# Find the project root (where scripts/ directory is) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" + +# Run Kani with JSON export (expect failure, so don't use -e) +set +e +kani test.rs --export-json "$OUTPUT_FILE" +EXIT_CODE=$? +set -e + +# Kani should exit with failure +if [ $EXIT_CODE -eq 0 ]; then + echo "ERROR: Expected Kani to fail but it succeeded" + exit 1 +fi + +echo "Kani failed as expected" + +# Check that JSON file was created despite failure +if [ ! -f "$OUTPUT_FILE" ]; then + echo "ERROR: JSON file $OUTPUT_FILE was not created" + exit 1 +fi + +echo "JSON file created despite failure" + +# Validate that JSON contains failure information +python3 << 'EOF' +import json +import sys + +with open('failed_output.json', 'r') as f: + data = json.load(f) + +# Check verification_results shows failure +vr = data['verification_results'] +summary = vr['summary'] + +if summary['successful'] != 0: + print(f"ERROR: Expected 0 successful, got {summary['successful']}") + sys.exit(1) + +if summary['failed'] != 1: + print(f"ERROR: Expected 1 failed, got {summary['failed']}") + sys.exit(1) + +print("Summary shows correct failure count") + +# Check that results array contains failure status +results = vr['results'] +if len(results) != 1: + print(f"ERROR: Expected 1 result, got {len(results)}") + sys.exit(1) + +if results[0]['status'] != 'Failure': + print(f"ERROR: Expected status 'Failure', got {results[0]['status']}") + sys.exit(1) + +print("Result status is 'Failure'") + +# Check that error_details exists and has_errors is true +if 'error_details' not in data: + print("ERROR: error_details field missing") + sys.exit(1) + +# error_details is an object (single harness) +error_details = data['error_details'] +if not error_details.get('has_errors'): + print("ERROR: has_errors should be true") + sys.exit(1) + +print("error_details.has_errors is true") + +# Verify error_type is present +if 'error_type' not in error_details: + print("ERROR: error_type field missing") + sys.exit(1) + +print("error_type field present") + +EOF + +echo "All failure validation checks passed!" + +# Clean up +rm -f "$OUTPUT_FILE" + diff --git a/tests/json-handler/multiple-harnesses/config.yml b/tests/json-handler/multiple-harnesses/config.yml new file mode 100644 index 000000000000..938834ea42cb --- /dev/null +++ b/tests/json-handler/multiple-harnesses/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: test.sh + diff --git a/tests/json-handler/multiple-harnesses/test.rs b/tests/json-handler/multiple-harnesses/test.rs new file mode 100644 index 000000000000..f13ac2ae9360 --- /dev/null +++ b/tests/json-handler/multiple-harnesses/test.rs @@ -0,0 +1,42 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: --export-json multi_harness_output.json + +//! Test JSON export with multiple harnesses +//! Verifies that frontend correctly handles multiple verification harnesses + +fn multiply(a: i32, b: i32) -> i32 { + a * b +} + +fn divide(a: i32, b: i32) -> i32 { + a / b +} + +#[kani::proof] +fn verify_multiply_positive() { + let x: i32 = kani::any(); + let y: i32 = kani::any(); + kani::assume(x > 0 && x < 10); + kani::assume(y > 0 && y < 10); + let result = multiply(x, y); + assert!(result > 0); +} + +#[kani::proof] +fn verify_multiply_zero() { + let x: i32 = kani::any(); + let result = multiply(x, 0); + assert_eq!(result, 0); +} + +#[kani::proof] +fn verify_divide_nonzero() { + let x: i32 = kani::any(); + let y: i32 = kani::any(); + kani::assume(x >= 0 && x < 100); + kani::assume(y > 0 && y < 100); + let result = divide(x, y); + assert!(result <= x); +} diff --git a/tests/json-handler/multiple-harnesses/test.sh b/tests/json-handler/multiple-harnesses/test.sh new file mode 100755 index 000000000000..83c3e4add71f --- /dev/null +++ b/tests/json-handler/multiple-harnesses/test.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +# Test JSON export with multiple harnesses - validates aggregation logic + +set -eu + +OUTPUT_FILE="multi_harness_output.json" + +# Find the project root +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +VALIDATOR="$PROJECT_ROOT/scripts/validate_json_export.py" + +# Run Kani with JSON export +kani test.rs --export-json "$OUTPUT_FILE" + +# Check that JSON file was created +if [ ! -f "$OUTPUT_FILE" ]; then + echo "ERROR: JSON file $OUTPUT_FILE was not created" + exit 1 +fi + +# Validate JSON structure (suppress verbose output) +python3 "$VALIDATOR" "$OUTPUT_FILE" 2>&1 | tail -1 + +# Check that JSON contains all 3 harnesses +HARNESS_COUNT=$(python3 -c "import json; data=json.load(open('$OUTPUT_FILE')); print(len(data['harness_metadata']))") + +if [ "$HARNESS_COUNT" != "3" ]; then + echo "ERROR: Expected 3 harnesses in JSON, found $HARNESS_COUNT" + exit 1 +fi + +echo "Found 3 harnesses in JSON" + +# Clean up +rm -f "$OUTPUT_FILE" + diff --git a/tests/json-handler/schema-validation/config.yml b/tests/json-handler/schema-validation/config.yml new file mode 100644 index 000000000000..938834ea42cb --- /dev/null +++ b/tests/json-handler/schema-validation/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: test.sh + diff --git a/tests/json-handler/schema-validation/test.rs b/tests/json-handler/schema-validation/test.rs new file mode 100644 index 000000000000..df2bea6da730 --- /dev/null +++ b/tests/json-handler/schema-validation/test.rs @@ -0,0 +1,28 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: --export-json schema_validation_output.json + +//! Test JSON schema structure validation +//! Ensures frontend exports well-formed JSON with correct metadata + +fn calculate(x: u32, y: u32) -> u32 { + if x > y { x - y } else { y - x } +} + +#[kani::proof] +fn verify_calculate() { + let a: u32 = kani::any(); + let b: u32 = kani::any(); + kani::assume(a < 1000); + kani::assume(b < 1000); + let result = calculate(a, b); + assert!(result < 1000); +} + +#[kani::proof] +fn verify_calculate_same() { + let x: u32 = kani::any(); + let result = calculate(x, x); + assert_eq!(result, 0); +} diff --git a/tests/json-handler/schema-validation/test.sh b/tests/json-handler/schema-validation/test.sh new file mode 100755 index 000000000000..50ee5a9709e4 --- /dev/null +++ b/tests/json-handler/schema-validation/test.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +# Test JSON schema structure from schema_utils.rs functions +# This test uses the schema template to validate the JSON output + +set -eu + +OUTPUT_FILE="schema_output.json" + +# Find the project root (where scripts/ directory is) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +VALIDATOR="$PROJECT_ROOT/scripts/validate_json_export.py" + +# Run Kani with JSON export +kani test.rs --export-json "$OUTPUT_FILE" + +# Check that JSON file was created +if [ ! -f "$OUTPUT_FILE" ]; then + echo "ERROR: JSON file $OUTPUT_FILE was not created" + exit 1 +fi + +echo "JSON file created" + +# Validate basic structure using schema template +python3 "$VALIDATOR" "$OUTPUT_FILE" + +# Validate each field from schema template dynamically +echo "" +echo "Validating individual fields from schema:" + +# Get all top-level keys from schema and validate each that exists in JSON +SCHEMA_FILE="$PROJECT_ROOT/scripts/json_schemas/kani_json_schema.json" +python3 << EOF +import json +import subprocess +import sys + +with open('$SCHEMA_FILE', 'r') as f: + schema = json.load(f) + +with open('$OUTPUT_FILE', 'r') as f: + data = json.load(f) + +all_passed = True +for key in schema.keys(): + if key not in data: + continue + + result = subprocess.run( + ['python3', '$VALIDATOR', '$OUTPUT_FILE', '--field-path', key], + capture_output=True + ) + if result.returncode == 0: + print(f" {key}: PASSED") + else: + print(f" {key}: FAILED") + all_passed = False + +if not all_passed: + sys.exit(1) +EOF + +if [ $? -ne 0 ]; then + echo "Field validation failed" + exit 1 +fi + +# Clean up +rm -f "$OUTPUT_FILE" + +echo "" +echo "All validations passed" + From 9715a9cf2a279047080eb965daae18bf5ff608c5 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 30 Oct 2025 11:52:15 -0700 Subject: [PATCH 31/54] fix: incoporate optional field in json schema when json not fail --- scripts/validate_json_export.py | 21 +++++++++++++++----- tests/json-handler/schema-validation/test.sh | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/scripts/validate_json_export.py b/scripts/validate_json_export.py index 8e94103d5211..8d626e8bd45a 100755 --- a/scripts/validate_json_export.py +++ b/scripts/validate_json_export.py @@ -13,9 +13,15 @@ def load_schema_template(): """Load the JSON schema template""" - # Find schema template in scripts directory + # Find schema template in tests/json-handler/schema-validation directory script_dir = Path(__file__).parent - schema_path = script_dir / "json_schemas" / "kani_json_schema.json" + schema_path = ( + script_dir.parent + / "tests" + / "json-handler" + / "schema-validation" + / "kani_json_schema.json" + ) if not schema_path.exists(): print(f"ERROR: Schema template not found at {schema_path}") @@ -41,15 +47,20 @@ def validate_structure_recursive(data, schema, path=""): # Handle dict validation if isinstance(schema, dict) and isinstance(data, dict): - # All fields in schema are required + # Get optional fields list (fields that may or may not be present) + optional_fields = schema.get("_optional", []) + + # All fields in schema are required except metadata fields (starting with _) and optional fields schema_keys = [k for k in schema.keys() if not k.startswith("_")] # Check each key in schema for key in schema_keys: # Check if field exists in data if key not in data: - current_path = f"{path}.{key}" if path else key - errors.append(f"Missing required field: {current_path}") + # Only report error if field is required (not in optional list) + if key not in optional_fields: + current_path = f"{path}.{key}" if path else key + errors.append(f"Missing required field: {current_path}") continue # Recursively validate nested structure diff --git a/tests/json-handler/schema-validation/test.sh b/tests/json-handler/schema-validation/test.sh index 50ee5a9709e4..479cba03e0e7 100755 --- a/tests/json-handler/schema-validation/test.sh +++ b/tests/json-handler/schema-validation/test.sh @@ -33,7 +33,7 @@ echo "" echo "Validating individual fields from schema:" # Get all top-level keys from schema and validate each that exists in JSON -SCHEMA_FILE="$PROJECT_ROOT/scripts/json_schemas/kani_json_schema.json" +SCHEMA_FILE="$(dirname "$0")/kani_json_schema.json" python3 << EOF import json import subprocess From 0cfaa00e00f412309760a1f36e88c22717b26dde Mon Sep 17 00:00:00 2001 From: edison Date: Tue, 4 Nov 2025 19:08:33 -0800 Subject: [PATCH 32/54] chore: add json-handler rfc documentation --- rfc/src/rfcs/0015-json-handler.md | 288 ++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 rfc/src/rfcs/0015-json-handler.md diff --git a/rfc/src/rfcs/0015-json-handler.md b/rfc/src/rfcs/0015-json-handler.md new file mode 100644 index 000000000000..1ad5f40687dd --- /dev/null +++ b/rfc/src/rfcs/0015-json-handler.md @@ -0,0 +1,288 @@ +- **Feature Name:** JSON Export (`json-export`) +- **Feature Request Issue:** + - + - + - + - + - + - + - + - + - +- **RFC PR:** +- **Status:** Unstable +- **Version:** 0 + +------------------- + +## Summary + +Export Kani verification results in structured JSON format with a validated schema, enabling programmatic analysis and tool integration. + +## User Impact + +It is difficult to integrate Kani verification results into automated workflows because results are only available in human-readable output text format. This limits: +- Cross-run analysis +- Integration with external applications/tools (dashboards, databases, LLMs) +- Automated harness result generation + +This RFC adds JSON export as an opt-in feature, maintaining backward compatibility while enabling these automation scenarios. + +## User Experience + +Users export verification results using the `--export-json` flag: + +```bash +cargo kani --export-json results.json +``` + +This works with all existing Kani options: + +```bash +cargo kani --harness my_harness --export-json output.json +cargo kani --tests --export-json test_results.json +``` + +### JSON Schema + +The output contains eight top-level blocks: + +**1. Metadata** - Execution environment +```json +{ + "metadata": { + "version": "1.0", + "timestamp": "2025-10-30T12:00:00.000000Z", + "kani_version": "0.65.0", + "target": "x86_64-unknown-linux-gnu", + "build_mode": "debug" + } +} +``` + +**2. Project** - Codebase identification +```json +{ + "project": { + "crate_name": ["example_crate"], + "workspace_root": "/path/to/workspace" + } +} +``` + +**3. Harness Metadata** - Source locations and attributes +```json +{ + "harness_metadata": [{ + "pretty_name": "example_harness", + "mangled_name": "_RNvCs_example", + "source": { + "file": "src/lib.rs", + "start_line": 10, + "end_line": 15 + }, + "attributes": { + "kind": "Proof", + "should_panic": false + }, + "contract": { + "contracted_function_name": null + }, + "has_loop_contracts": false + }] +} +``` + +**4. Verification Results** - Harness results and checks +```json +{ + "verification_results": { + "summary": { + "total_harnesses": 1, + "executed": 1, + "successful": 1, + "failed": 0, + "duration_ms": 500 + }, + "results": [{ + "harness_id": "example_harness", + "status": "Success", + "duration_ms": 500, + "checks": [{ + "id": 1, + "function": "example_function", + "status": "Success", + "description": "assertion failed: x > 0", + "location": { + "file": "src/lib.rs", + "line": "20", + "column": "13" + }, + "category": "assertion" + }] + }] + } +} +``` + +**5. Error Details** - Top-level error classification +```json +{ + "error_details": { + "has_errors": false, + "error_type": null, + "failed_properties_type": null, + "exit_status": "success" + } +} +``` + +**6. Property Details** - Property statistics +```json +{ + "property_details": [{ + "property_details": { + "total_properties": 1, + "passed": 1, + "failed": 0, + "unreachable": 0 + } + }] +} +``` + +**7. CBMC Statistics** - Backend performance metrics +```json +{ + "cbmc": [{ + "harness_id": "example_harness", + "cbmc_metadata": { + "version": "6.7.1", + "os_info": "x86_64 linux unix" + }, + "configuration": { + "object_bits": 16, + "solver": "Cadical" + }, + "cbmc_stats": { + "runtime_symex_s": 0.005, + "runtime_solver_s": 0.0003, + "vccs_generated": 1, + "vccs_remaining": 1 + } + }] +} +``` + +**8. Coverage** - Coverage configuration +```json +{ + "coverage": { + "enabled": false + } +} +``` + +### Design Notes + +- **Harness correlation**: Data is keyed by `harness_id` across blocks (`verification_results.results[]`, `cbmc[]`) for easy filtering +- **Optional fields**: `error_details` only populates `error_type`, `failed_properties_type`, and `exit_status` on failure +- **Complete state**: Captures all verification data including CBMC performance metrics for analysis + +### Error Handling + +- File write errors produce clear error messages with non-zero exit codes +- Schema validation (during regression testing) catches missing/malformed fields + +## Software Design + +The implementation touches several components in the Kani driver. We'll describe each component and how they work together to produce the JSON export. + +### Core Implementation + +The main change is in `kani-driver`, where we add a new frontend module `frontend/schema_utils.rs` that handles all JSON serialization. This module defines a `JsonHandler` struct that collects all the verification data (harness metadata, verification results, CBMC statistics) and serializes it to JSON using standard Rust serialization. + +To support this, we enhance `call_cbmc.rs` to extract CBMC performance statistics from CBMC's output. Since CBMC prints timing and statistics information in a structured format, we can parse this using regular expressions to extract values like symbolic execution time, solver time, and VCC counts. + +The driver's main entry point (`main.rs`) is modified to accept the `--export-json ` flag. When this flag is present, after verification completes successfully (or fails), we trigger the JSON serialization and write the output to the specified file. File I/O errors are reported clearly to the user with appropriate error messages. + +### Schema Validation and Testing + +Rather than hardcoding expected JSON fields in our tests, we take a template-based approach. The schema file itself (`kani_json_schema.json`) serves as both documentation and validation template. A Python validation script (`scripts/validate_json_export.py`) reads this schema file and recursively validates that any JSON export matches the expected structure. + +The validation approach treats all fields as required by default. However, some fields only make sense in certain contexts—for example, `error_details` contains detailed error information only when verification fails. To handle this, the schema supports `_optional` arrays that list fields which may be absent: + +```json +{ + "error_details": { + "_optional": ["error_type", "failed_properties_type", "exit_status"], + "has_errors": false, + "error_type": null + } +} +``` + +When validation encounters a field listed in `_optional`, it validates the field only if present in the actual data. This keeps the schema flexible while ensuring we catch missing required fields. Metadata fields (those starting with underscore) are excluded from validation entirely. + +The validator supports a `--field-path` option for validating specific sections of the JSON (e.g., the `cbmc` block). + +### Test Suite Organization + +We add a new test suite under `tests/json-handler/` with four test scenarios that cover different aspects of JSON export: + +The `basic-export/` test verifies that the JSON export flag works and produces valid JSON with the expected top-level structure. The `schema-validation/` test is more comprehensive—it runs a verification with multiple harnesses and validates the entire JSON structure against the schema template. This directory also houses the canonical `kani_json_schema.json` file, making it easy to find and update. + +The `multiple-harnesses/` test specifically checks that results from multiple harnesses are correctly aggregated and that the `harness_id` correlation works across different blocks. Finally, `failed-verification/` tests that error information is correctly captured and that optional error fields are populated on failure. + +### Implementation Flow + +When a user runs `cargo kani --export-json output.json`, the following happens: + +1. Kani driver parses the command-line arguments and enables JSON export mode +2. During verification, CBMC is invoked for each harness as usual +3. For each harness, we capture and parse CBMC's output to extract statistics +4. After all harnesses complete, we aggregate the results into the `VerificationOutput` struct +5. We serialize this struct to JSON format +6. The JSON is written to the specified file, with any I/O errors reported to the user + +The schema structure organizes information by category (metadata, project info, harness details, results, CBMC stats) with `harness_id` serving as the correlation key across blocks. This organization makes it easy for external tools to either process everything or filter by specific harnesses. + +### Corner Cases + +Several edge cases need consideration: + +**CBMC output parsing**: We extract CBMC statistics using regex patterns. If CBMC's output format changes in future versions, parsing may fail. To handle this gracefully, we can omit CBMC statistics in the json schema. + +**Large output**: For projects with hundreds of harnesses, JSON files can grow to multi-megabyte sizes. While this is acceptable for current use cases, we may need to consider streaming serialization or compression if users report performance issues. + +**Partial verification runs**: If verification is interrupted (user cancellation, system crash), the JSON file may be incomplete or missing entirely. Since JSON is written only after verification completes, interrupted runs produce no output rather than partial/corrupt JSON. + +**Schema evolution**: The schema file and the `VerificationOutput` struct must stay synchronized. During development, if we add fields to the struct but forget to update the schema template, our tests will catch this mismatch and fail. This is by design—the tests serve as a contract enforcement mechanism. + +## Rationale and alternatives + +### Why JSON? + +JSON provides universal language support, human readability for debugging, and established tooling without the complexity of binary formats or custom parsers. + +### Template-based validation + +The schema file (`kani_json_schema.json`) serves as both documentation and validation template. Validation logic reads the schema dynamically rather than hardcoding expected fields. + +**Advantage**: Single source of truth; schema changes don't require code changes; prevents drift between documentation and implementation. + +**Disadvantage**: More complex validation implementation than hardcoded checks. + +## Open questions + +- How should we handle breaking schema changes? +- Should users be able to filter which sections are exported? +- For very large projects with hundreds of harnesses, should we support incremental output (e.g., JSONL format)? + +## Future possibilities + +**Formal JSON Schema**: Generate a formal JSON Schema (Draft 7+) document from our template for automatic client code generation and integration with third-party validators. + +**Streaming export**: For projects with hundreds of harnesses, a streaming approach (JSONL format with one harness per line) could reduce memory pressure. Wait for user performance reports before implementing. + +**Database integration**: With structured JSON output, users could build tools to store verification results in databases (PostgreSQL) for historical analysis and tracking performance trends. The harness-based organization with `harness_id` keys naturally maps to relational schemas with foreign key relationships. From d18aef71a46f7b9e6f7526b3f61893020ed8b352 Mon Sep 17 00:00:00 2001 From: ShrivyasShrivyas Date: Tue, 4 Nov 2025 22:47:59 -0800 Subject: [PATCH 33/54] Unit Tests for schema_utils --- kani-driver/src/frontend/mod.rs | 3 + kani-driver/src/frontend/tests/mod.rs | 2 + .../src/frontend/tests/schema_utils_test.rs | 220 ++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 kani-driver/src/frontend/tests/mod.rs create mode 100644 kani-driver/src/frontend/tests/schema_utils_test.rs diff --git a/kani-driver/src/frontend/mod.rs b/kani-driver/src/frontend/mod.rs index 8a2d41985437..c5b09887fad5 100644 --- a/kani-driver/src/frontend/mod.rs +++ b/kani-driver/src/frontend/mod.rs @@ -9,3 +9,6 @@ pub mod schema_utils; pub use json_handler::JsonHandler; pub use schema_utils::*; + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/kani-driver/src/frontend/tests/mod.rs b/kani-driver/src/frontend/tests/mod.rs new file mode 100644 index 000000000000..5c28aaa1a094 --- /dev/null +++ b/kani-driver/src/frontend/tests/mod.rs @@ -0,0 +1,2 @@ +#[cfg(test)] +mod schema_utils_test; diff --git a/kani-driver/src/frontend/tests/schema_utils_test.rs b/kani-driver/src/frontend/tests/schema_utils_test.rs new file mode 100644 index 000000000000..b03d45fd316d --- /dev/null +++ b/kani-driver/src/frontend/tests/schema_utils_test.rs @@ -0,0 +1,220 @@ +use crate::frontend::schema_utils::{create_metadata_json, create_project_metadata_json, create_harness_metadata_json, create_verification_result_json, create_verification_summary_json, add_runner_results_to_json}; +use kani_metadata::{KaniMetadata, HarnessMetadata, HarnessAttributes, HarnessKind}; +use crate::project::Project; +use std::path::PathBuf; +use crate::harness_runner::HarnessResult; +use crate::call_cbmc::{VerificationResult, VerificationStatus, FailedProperties, ExitStatus}; +use crate::cbmc_output_parser::{Property, PropertyId, SourceLocation, CheckStatus}; +use crate::frontend::JsonHandler; +use std::time::Duration; +#[test] +fn test_create_metadata_json() { + let json = create_metadata_json(); + + assert!(json.is_object()); + assert_eq!(json["version"], "1.0"); + assert!(json["timestamp"].as_str().unwrap().contains('T')); + assert!(["debug", "release"].contains(&json["build_mode"].as_str().unwrap())); +} + +#[test] +fn test_create_project_metadata_json() { + let mut project = Project::default(); + let metadata = KaniMetadata { + crate_name: "sample_crate".to_string(), + proof_harnesses: vec![], + test_harnesses: vec![], + unsupported_features: vec![], + contracted_functions: vec![], + autoharness_md: None, + }; + project.outdir = PathBuf::from("/tmp/outdir"); + project.metadata.push(metadata); + + let json = create_project_metadata_json(&project); + assert_eq!(json["crate_name"][0], "sample_crate"); + assert_eq!(json["workspace_root"], "/tmp/outdir"); +} + +#[test] +fn test_create_harness_metadata_json() { + let harness = HarnessMetadata { + pretty_name: "crate::mod::my_harness".to_string(), + mangled_name: "mangled::harness".to_string(), + crate_name: "sample_crate".to_string(), + original_file: "src/lib.rs".to_string(), + original_start_line: 10, + original_end_line: 20, + goto_file: Some(PathBuf::from("target/goto_file.goto")), + attributes: HarnessAttributes::new(HarnessKind::Proof), + contract: None, + has_loop_contracts: true, + is_automatically_generated: false, + }; + + let json = create_harness_metadata_json(&harness); + + assert_eq!(json["pretty_name"], "crate::mod::my_harness"); + assert_eq!(json["crate_name"], "sample_crate"); + assert_eq!(json["source"]["file"], "src/lib.rs"); + assert_eq!(json["source"]["start_line"], 10); + assert_eq!(json["source"]["end_line"], 20); + assert_eq!(json["has_loop_contracts"], true); + assert_eq!(json["is_automatically_generated"], false); +} + +#[test] +fn test_create_verification_result_json() { + let harness = HarnessMetadata { + pretty_name: "crate::my_harness".to_string(), + mangled_name: "mangled_name".to_string(), + crate_name: "sample_crate".to_string(), + original_file: "src/lib.rs".to_string(), + original_start_line: 1, + original_end_line: 2, + goto_file: None, + attributes: HarnessAttributes::new(HarnessKind::Proof), + contract: None, + has_loop_contracts: false, + is_automatically_generated: false, + }; + + let properties = vec![ + Property { + property_id: PropertyId { + id: 1, + fn_name: Some("foo".to_string()), + class: "safety".to_string(), + }, + status: CheckStatus::Success, + description: "no overflow".to_string(), + source_location: SourceLocation { + file: Some("src/lib.rs".to_string()), + function: Some("foo".to_string()), + line: Some("42".to_string()), + column: Some("5".to_string()), + }, + reach: None, + trace: None, + }, + Property { + property_id: PropertyId { + id: 2, + fn_name: Some("bar".to_string()), + class: "assertion".to_string(), + }, + status: CheckStatus::Failure, + description: "assert failed".to_string(), + source_location: SourceLocation { + file: Some("src/main.rs".to_string()), + function: Some("bar".to_string()), + line: Some("10".to_string()), + column: Some("3".to_string()), + }, + reach: None, + trace: None, + }, + ]; + + + let verification_result = VerificationResult { + status: VerificationStatus::Failure, + failed_properties: FailedProperties::Other, + results: Ok(properties), + runtime: Duration::from_millis(120), + generated_concrete_test: false, + coverage_results: None, + cbmc_stats: None, + }; + + let harness_result = HarnessResult { + harness: &harness, + result: verification_result, + }; + + let json = create_verification_result_json(&harness_result); + + // --- Assertions --- + assert_eq!(json["harness_id"], "crate::my_harness"); + assert_eq!(json["status"], "Failure"); + assert_eq!(json["checks"][0]["function"], "foo"); + assert_eq!(json["checks"][1]["function"], "bar"); + assert_eq!(json["checks"][1]["location"]["file"], "src/main.rs"); + + // Optional extra check + assert!(json["duration_ms"].as_u64().unwrap() >= 100); +} + +#[test] +fn test_create_verification_summary_json_real() { + // Create a real harness + let harness = HarnessMetadata { + pretty_name: "foo::harness_ok".into(), + mangled_name: "foo_harness_ok".into(), + crate_name: "sample".into(), + original_file: "src/lib.rs".into(), + original_start_line: 10, + original_end_line: 20, + goto_file: None, + attributes: HarnessAttributes::new(HarnessKind::Proof), + contract: None, + has_loop_contracts: false, + is_automatically_generated: false, + }; + + // Create a VerificationResult + let verification_result = VerificationResult::mock_success(); + + let harness_result = HarnessResult { + harness: &harness, + result: verification_result, + }; + + let json = create_verification_summary_json(&[harness_result], 1, "Completed"); + + assert_eq!(json["summary"]["status"], "Completed"); + assert_eq!(json["summary"]["total_harnesses"], 1); + assert_eq!(json["summary"]["executed"], 1); + assert_eq!(json["summary"]["successful"], 1); + assert_eq!(json["summary"]["failed"], 0); + assert!(json["results"].is_array()); +} + +#[test] +fn test_add_runner_results_to_json_real() { + let harness = HarnessMetadata { + pretty_name: "bar::harness_fail".into(), + mangled_name: "bar_harness_fail".into(), + crate_name: "sample".into(), + original_file: "src/main.rs".into(), + original_start_line: 5, + original_end_line: 15, + goto_file: None, + attributes: HarnessAttributes::new(HarnessKind::Proof), + contract: None, + has_loop_contracts: false, + is_automatically_generated: false, + }; + + let verification_result = VerificationResult{ + status: VerificationStatus::Failure, + failed_properties: FailedProperties::Other, + results: Err(ExitStatus::Other(42)), + runtime: Duration::from_millis(120), + generated_concrete_test: false, + coverage_results: None, + cbmc_stats: None, + }; + + let harness_result = HarnessResult { + harness: &harness, + result: verification_result, + }; + + let mut handler = JsonHandler::new(None); + add_runner_results_to_json(&mut handler, &[harness_result], 1, "Failed"); + + let summary = &handler.data["verification_results"]["summary"]; + assert_eq!(summary["status"], "Failed"); + assert_eq!(summary["failed"], 1); +} From e2d609af5a1390db7335f721c2fc20283b1cd813 Mon Sep 17 00:00:00 2001 From: edison Date: Wed, 5 Nov 2025 12:55:58 -0800 Subject: [PATCH 34/54] chore: format unit tests --- kani-driver/src/frontend/mod.rs | 2 +- .../src/frontend/tests/schema_utils_test.rs | 34 ++++++++----------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/kani-driver/src/frontend/mod.rs b/kani-driver/src/frontend/mod.rs index c5b09887fad5..61431fb24ffc 100644 --- a/kani-driver/src/frontend/mod.rs +++ b/kani-driver/src/frontend/mod.rs @@ -11,4 +11,4 @@ pub use json_handler::JsonHandler; pub use schema_utils::*; #[cfg(test)] -mod tests; \ No newline at end of file +mod tests; diff --git a/kani-driver/src/frontend/tests/schema_utils_test.rs b/kani-driver/src/frontend/tests/schema_utils_test.rs index b03d45fd316d..553d8e641200 100644 --- a/kani-driver/src/frontend/tests/schema_utils_test.rs +++ b/kani-driver/src/frontend/tests/schema_utils_test.rs @@ -1,11 +1,15 @@ -use crate::frontend::schema_utils::{create_metadata_json, create_project_metadata_json, create_harness_metadata_json, create_verification_result_json, create_verification_summary_json, add_runner_results_to_json}; -use kani_metadata::{KaniMetadata, HarnessMetadata, HarnessAttributes, HarnessKind}; +use crate::call_cbmc::{ExitStatus, FailedProperties, VerificationResult, VerificationStatus}; +use crate::cbmc_output_parser::{CheckStatus, Property, PropertyId, SourceLocation}; +use crate::frontend::JsonHandler; +use crate::frontend::schema_utils::{ + add_runner_results_to_json, create_harness_metadata_json, create_metadata_json, + create_project_metadata_json, create_verification_result_json, + create_verification_summary_json, +}; +use crate::harness_runner::HarnessResult; use crate::project::Project; +use kani_metadata::{HarnessAttributes, HarnessKind, HarnessMetadata, KaniMetadata}; use std::path::PathBuf; -use crate::harness_runner::HarnessResult; -use crate::call_cbmc::{VerificationResult, VerificationStatus, FailedProperties, ExitStatus}; -use crate::cbmc_output_parser::{Property, PropertyId, SourceLocation, CheckStatus}; -use crate::frontend::JsonHandler; use std::time::Duration; #[test] fn test_create_metadata_json() { @@ -115,7 +119,6 @@ fn test_create_verification_result_json() { trace: None, }, ]; - let verification_result = VerificationResult { status: VerificationStatus::Failure, @@ -127,10 +130,7 @@ fn test_create_verification_result_json() { cbmc_stats: None, }; - let harness_result = HarnessResult { - harness: &harness, - result: verification_result, - }; + let harness_result = HarnessResult { harness: &harness, result: verification_result }; let json = create_verification_result_json(&harness_result); @@ -165,10 +165,7 @@ fn test_create_verification_summary_json_real() { // Create a VerificationResult let verification_result = VerificationResult::mock_success(); - let harness_result = HarnessResult { - harness: &harness, - result: verification_result, - }; + let harness_result = HarnessResult { harness: &harness, result: verification_result }; let json = create_verification_summary_json(&[harness_result], 1, "Completed"); @@ -196,7 +193,7 @@ fn test_add_runner_results_to_json_real() { is_automatically_generated: false, }; - let verification_result = VerificationResult{ + let verification_result = VerificationResult { status: VerificationStatus::Failure, failed_properties: FailedProperties::Other, results: Err(ExitStatus::Other(42)), @@ -206,10 +203,7 @@ fn test_add_runner_results_to_json_real() { cbmc_stats: None, }; - let harness_result = HarnessResult { - harness: &harness, - result: verification_result, - }; + let harness_result = HarnessResult { harness: &harness, result: verification_result }; let mut handler = JsonHandler::new(None); add_runner_results_to_json(&mut handler, &[harness_result], 1, "Failed"); From e0772963f655865186f2dc730be19c4731b4eaca Mon Sep 17 00:00:00 2001 From: edison Date: Mon, 10 Nov 2025 02:11:08 -0800 Subject: [PATCH 35/54] fix: fix unclose parenthesis --- kani-driver/src/call_cbmc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 54390dccc483..4ccca27fc0f4 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -404,7 +404,7 @@ impl KaniSession { generated_concrete_test: false, coverage_results: None, cbmc_stats: None, - } + }) } Ok(verification_results) From 99faa80bd8200b87ea51252f1fb57286f78658ee Mon Sep 17 00:00:00 2001 From: edison Date: Mon, 10 Nov 2025 02:35:30 -0800 Subject: [PATCH 36/54] fix: resolve cbmc conflicts and remove unused import in main --- kani-driver/src/call_cbmc.rs | 3 --- kani-driver/src/main.rs | 2 -- 2 files changed, 5 deletions(-) diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 4ccca27fc0f4..5a787a423e6f 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -406,8 +406,6 @@ impl KaniSession { cbmc_stats: None, }) } - - Ok(verification_results) } /// "Internal," but also used by call_cbmc_viewer @@ -564,7 +562,6 @@ impl VerificationResult { /// (CBMC will regularly report "failure" but that's just our cover checks.) /// 2. Positively checking for the presence of results. /// (Do not mistake lack of results for success: report it as failure.) - fn from( output: VerificationOutput, should_panic: bool, diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 13b6cd322b9c..246ee6d48d45 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use std::ffi::OsString; use std::process::ExitCode; -use std::time::{Instant, SystemTime}; -use time::format_description::well_known::Rfc3339; use anyhow::Result; use autoharness::{autoharness_cargo, autoharness_standalone}; From dc00668bd1600bca7da790f0efdbb8658233be55 Mon Sep 17 00:00:00 2001 From: edison Date: Mon, 10 Nov 2025 02:39:48 -0800 Subject: [PATCH 37/54] chore: remove old json files --- .../src/tutorial/first-steps-v1/kani_run.json | 58 ------ kani_run.json | 196 ------------------ 2 files changed, 254 deletions(-) delete mode 100644 docs/src/tutorial/first-steps-v1/kani_run.json delete mode 100644 kani_run.json diff --git a/docs/src/tutorial/first-steps-v1/kani_run.json b/docs/src/tutorial/first-steps-v1/kani_run.json deleted file mode 100644 index 5325c9e7b876..000000000000 --- a/docs/src/tutorial/first-steps-v1/kani_run.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "schema_version": "0.0.0", - "run_time": { - "started_at": "2025-09-18T21:43:58.758849Z", - "finished_at": "2025-09-18T21:43:58.856479Z", - "duration_ms": 97 - }, - "verification_runner_results": { - "selected": 1, - "executed": 1, - "status": "completed", - "individual_harnesses": [ - { - "name": "check_estimate_size", - "original": { - "file": "./docs/src/tutorial/first-steps-v1/src/lib.rs", - "start_line": 52, - "end_line": 55 - }, - "kind": "Proof", - "should_panic": false, - "has_loop_contracts": false, - "is_automatically_generated": false, - "solver": null, - "unwind_value": null, - "contract": null, - "stubs": [], - "verified_stubs": [], - "summary": { - "total": 1, - "status": "failed" - }, - "timing": { - "cbmc_runtime": "0.024s" - } - } - ] - }, - "error_details": { - "has_errors": true, - "error_type": "assertion_failure", - "failed_properties_type": "PanicsOnly", - "exit_status": "properties_failed" - }, - "property_details": [ - { - "property_details": { - "total_properties": 1, - "passed": 0, - "failed": 1, - "unreachable": 0 - } - } - ], - "coverage": { - "enabled": false - } -} \ No newline at end of file diff --git a/kani_run.json b/kani_run.json deleted file mode 100644 index 019b991cf5aa..000000000000 --- a/kani_run.json +++ /dev/null @@ -1,196 +0,0 @@ -{ - "metadata": { - "version": "1.0", - "timestamp": "2025-09-20T19:17:55.319661Z", - "kani_version": "0.65.0", - "target": " ", - "build_mode": "release" - }, - "project": { - "crate_name": [ - "lib" - ], - "workspace_root": "/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v2/src" - }, - "harness_metadata": [ - { - "pretty_name": "verify_success", - "mangled_name": "_RNvCscOSYLgomVfM_3lib14verify_success", - "crate_name": "lib", - "source": { - "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", - "start_line": 54, - "end_line": 59 - }, - "goto_file": "/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v2/src/lib__RNvCscOSYLgomVfM_3lib14verify_success.symtab.out", - "attributes": { - "kind": "Proof", - "should_panic": false, - "solver": null, - "unwind_value": null, - "stubs": [], - "verified_stubs": [] - }, - "Contract": { - "contracted_function_name": null - }, - "has_loop_contracts": false, - "is_automatically_generated": false - }, - { - "pretty_name": "will_fail", - "mangled_name": "_RNvCscOSYLgomVfM_3lib9will_fail", - "crate_name": "lib", - "source": { - "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", - "start_line": 64, - "end_line": 67 - }, - "goto_file": "/Users/jiangkeyou/Desktop/25Fall/aws/kani/docs/src/tutorial/first-steps-v2/src/lib__RNvCscOSYLgomVfM_3lib9will_fail.symtab.out", - "attributes": { - "kind": "Proof", - "should_panic": false, - "solver": null, - "unwind_value": null, - "stubs": [], - "verified_stubs": [] - }, - "Contract": { - "contracted_function_name": null - }, - "has_loop_contracts": false, - "is_automatically_generated": false - } - ], - "verification_runner_results": { - "selected": 2, - "executed": 2, - "status": "completed", - "individual_harnesses": [ - { - "harness_id": "will_fail", - "name": "will_fail", - "status": "Failure", - "verification_details": [ - { - "check_number": 1, - "function_name": "estimate_size", - "status": "Failure", - "description": "assertion failed: x < 4096", - "location": { - "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", - "line": "6", - "column": "5" - }, - "class": "assertion" - } - ], - "original": { - "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", - "start_line": 64, - "end_line": 67 - }, - "kind": "Proof", - "should_panic": false, - "has_loop_contracts": false, - "is_automatically_generated": false, - "solver": null, - "unwind_value": null, - "contract": null, - "stubs": [], - "verified_stubs": [], - "summary": { - "total": 1, - "status": "failed" - }, - "timing": { - "cbmc_runtime": "0.023s" - } - }, - { - "harness_id": "verify_success", - "name": "verify_success", - "status": "Success", - "verification_details": [ - { - "check_number": 1, - "function_name": "estimate_size", - "status": "Success", - "description": "assertion failed: x < 4096", - "location": { - "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", - "line": "6", - "column": "5" - }, - "class": "assertion" - }, - { - "check_number": 2, - "function_name": "verify_success", - "status": "Success", - "description": "assertion failed: y < 10", - "location": { - "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", - "line": "58", - "column": "5" - }, - "class": "assertion" - } - ], - "original": { - "file": "./docs/src/tutorial/first-steps-v2/src/lib.rs", - "start_line": 54, - "end_line": 59 - }, - "kind": "Proof", - "should_panic": false, - "has_loop_contracts": false, - "is_automatically_generated": false, - "solver": null, - "unwind_value": null, - "contract": null, - "stubs": [], - "verified_stubs": [], - "summary": { - "total": 1, - "status": "completed" - }, - "timing": { - "cbmc_runtime": "0.007s" - } - } - ] - }, - "error_details": { - "has_errors": true, - "error_type": "assertion_failure", - "failed_properties_type": "PanicsOnly", - "exit_status": "properties_failed" - }, - "property_details": [ - { - "property_details": { - "total_properties": 2, - "passed": 2, - "failed": 0, - "unreachable": 0 - } - }, - { - "property_details": { - "total_properties": 1, - "passed": 0, - "failed": 1, - "unreachable": 0 - } - } - ], - "coverage": { - "enabled": false - }, - "run_time": { - "started_at": "2025-09-20T19:17:55.319638Z", - "finished_at": "2025-09-20T19:17:55.492976Z", - "duration_ms": 173 - } -} \ No newline at end of file From 4537df2f9982afd82589e683cc4cb942064f36d0 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 13 Nov 2025 16:27:08 -0800 Subject: [PATCH 38/54] fix: format frontend code and fix json file not found error --- kani-driver/src/call_cbmc.rs | 232 +++++++++--------- kani-driver/src/frontend/schema_utils.rs | 2 + kani-driver/src/harness_runner.rs | 2 +- tests/.gitignore | 2 + .../schema-validation/kani_json_schema.json | 119 +++++++++ 5 files changed, 234 insertions(+), 123 deletions(-) create mode 100644 tests/json-handler/schema-validation/kani_json_schema.json diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 5a787a423e6f..92220d7910c5 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -61,7 +61,7 @@ impl KaniSession { // Extract version from first line (e.g., "6.7.1 (cbmc-6.7.1)") let version = lines - .get(0) + .first() .and_then(|line| line.split_whitespace().next()) .unwrap_or("unknown") .to_string(); @@ -85,31 +85,28 @@ impl KaniSession { // Example: "Runtime Symex: 0.00408627s" if let Some(captures) = regex::Regex::new(r"Runtime Symex: ([-e\d\.]+)s").ok()?.captures(message) + && let Ok(val) = captures[1].parse::() { - if let Ok(val) = captures[1].parse::() { - stats.runtime_symex_s = Some(val); - found_any = true; - } + stats.runtime_symex_s = Some(val); + found_any = true; } // Example: "size of program expression: 150 steps" if let Some(captures) = regex::Regex::new(r"size of program expression: (\d+) steps").ok()?.captures(message) + && let Ok(val) = captures[1].parse::() { - if let Ok(val) = captures[1].parse::() { - stats.size_program_expression = Some(val); - found_any = true; - } + stats.size_program_expression = Some(val); + found_any = true; } // Example: "slicing removed 81 assignments" if let Some(captures) = regex::Regex::new(r"slicing removed (\d+) assignments").ok()?.captures(message) + && let Ok(val) = captures[1].parse::() { - if let Ok(val) = captures[1].parse::() { - stats.slicing_removed_assignments = Some(val); - found_any = true; - } + stats.slicing_removed_assignments = Some(val); + found_any = true; } // Example: "Generated 1 VCC(s), 1 remaining after simplification" @@ -131,51 +128,46 @@ impl KaniSession { // Example: "Runtime Postprocess Equation: 0.000767182s" if let Some(captures) = regex::Regex::new(r"Runtime Postprocess Equation: ([-e\d\.]+)s").ok()?.captures(message) + && let Ok(val) = captures[1].parse::() { - if let Ok(val) = captures[1].parse::() { - stats.runtime_postprocess_equation_s = Some(val); - found_any = true; - } + stats.runtime_postprocess_equation_s = Some(val); + found_any = true; } // Example: "Runtime Convert SSA: 0.000516981s" if let Some(captures) = regex::Regex::new(r"Runtime Convert SSA: ([-e\d\.]+)s").ok()?.captures(message) + && let Ok(val) = captures[1].parse::() { - if let Ok(val) = captures[1].parse::() { - stats.runtime_convert_ssa_s = Some(val); - found_any = true; - } + stats.runtime_convert_ssa_s = Some(val); + found_any = true; } // Example: "Runtime Post-process: 0.000189636s" if let Some(captures) = regex::Regex::new(r"Runtime Post-process: ([-e\d\.]+)s").ok()?.captures(message) + && let Ok(val) = captures[1].parse::() { - if let Ok(val) = captures[1].parse::() { - stats.runtime_post_process_s = Some(val); - found_any = true; - } + stats.runtime_post_process_s = Some(val); + found_any = true; } // Example: "Runtime Solver: 0.00167592s" if let Some(captures) = regex::Regex::new(r"Runtime Solver: ([-e\d\.]+)s").ok()?.captures(message) + && let Ok(val) = captures[1].parse::() { - if let Ok(val) = captures[1].parse::() { - stats.runtime_solver_s = Some(val); - found_any = true; - } + stats.runtime_solver_s = Some(val); + found_any = true; } // Example: "Runtime decision procedure: 0.00452419s" if let Some(captures) = regex::Regex::new(r"Runtime decision procedure: ([-e\d\.]+)s").ok()?.captures(message) + && let Ok(val) = captures[1].parse::() { - if let Ok(val) = captures[1].parse::() { - stats.runtime_decision_procedure_s = Some(val); - found_any = true; - } + stats.runtime_decision_procedure_s = Some(val); + found_any = true; } if found_any { Some(stats) } else { None } @@ -285,61 +277,8 @@ impl KaniSession { process_cbmc_output(&mut cbmc_process, |i| { // Collect stats from messages if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = &i + && let Some(stats) = Self::extract_cbmc_stats_from_message(message_text) { - if let Some(stats) = Self::extract_cbmc_stats_from_message(message_text) { - // Merge stats - if stats.runtime_symex_s.is_some() { - collected_stats.runtime_symex_s = stats.runtime_symex_s; - } - if stats.size_program_expression.is_some() { - collected_stats.size_program_expression = - stats.size_program_expression; - } - if stats.slicing_removed_assignments.is_some() { - collected_stats.slicing_removed_assignments = - stats.slicing_removed_assignments; - } - if stats.vccs_generated.is_some() { - collected_stats.vccs_generated = stats.vccs_generated; - } - if stats.vccs_remaining.is_some() { - collected_stats.vccs_remaining = stats.vccs_remaining; - } - if stats.runtime_postprocess_equation_s.is_some() { - collected_stats.runtime_postprocess_equation_s = - stats.runtime_postprocess_equation_s; - } - if stats.runtime_convert_ssa_s.is_some() { - collected_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; - } - if stats.runtime_post_process_s.is_some() { - collected_stats.runtime_post_process_s = - stats.runtime_post_process_s; - } - if stats.runtime_solver_s.is_some() { - collected_stats.runtime_solver_s = stats.runtime_solver_s; - } - if stats.runtime_decision_procedure_s.is_some() { - collected_stats.runtime_decision_procedure_s = - stats.runtime_decision_procedure_s; - } - } - } - - kani_cbmc_output_filter( - i, - self.args.extra_pointer_checks, - self.args.common_args.quiet, - &self.args.output_format, - ) - }), - ) - .await - } else { - Ok(process_cbmc_output(&mut cbmc_process, |i| { - // Collect stats from messages - if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = &i { - if let Some(stats) = Self::extract_cbmc_stats_from_message(message_text) { // Merge stats if stats.runtime_symex_s.is_some() { collected_stats.runtime_symex_s = stats.runtime_symex_s; @@ -375,6 +314,56 @@ impl KaniSession { stats.runtime_decision_procedure_s; } } + + kani_cbmc_output_filter( + i, + self.args.extra_pointer_checks, + self.args.common_args.quiet, + &self.args.output_format, + ) + }), + ) + .await + } else { + Ok(process_cbmc_output(&mut cbmc_process, |i| { + // Collect stats from messages + if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = &i + && let Some(stats) = Self::extract_cbmc_stats_from_message(message_text) + { + // Merge stats + if stats.runtime_symex_s.is_some() { + collected_stats.runtime_symex_s = stats.runtime_symex_s; + } + if stats.size_program_expression.is_some() { + collected_stats.size_program_expression = stats.size_program_expression; + } + if stats.slicing_removed_assignments.is_some() { + collected_stats.slicing_removed_assignments = + stats.slicing_removed_assignments; + } + if stats.vccs_generated.is_some() { + collected_stats.vccs_generated = stats.vccs_generated; + } + if stats.vccs_remaining.is_some() { + collected_stats.vccs_remaining = stats.vccs_remaining; + } + if stats.runtime_postprocess_equation_s.is_some() { + collected_stats.runtime_postprocess_equation_s = + stats.runtime_postprocess_equation_s; + } + if stats.runtime_convert_ssa_s.is_some() { + collected_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; + } + if stats.runtime_post_process_s.is_some() { + collected_stats.runtime_post_process_s = stats.runtime_post_process_s; + } + if stats.runtime_solver_s.is_some() { + collected_stats.runtime_solver_s = stats.runtime_solver_s; + } + if stats.runtime_decision_procedure_s.is_some() { + collected_stats.runtime_decision_procedure_s = + stats.runtime_decision_procedure_s; + } } kani_cbmc_output_filter( @@ -573,41 +562,40 @@ impl VerificationResult { // Collect CBMC stats from messages let mut cbmc_stats = CbmcStats::default(); for item in &remaining_items { - if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = item { - if let Some(stats) = KaniSession::extract_cbmc_stats_from_message(message_text) { - // Merge stats (later messages may have more complete info) - if stats.runtime_symex_s.is_some() { - cbmc_stats.runtime_symex_s = stats.runtime_symex_s; - } - if stats.size_program_expression.is_some() { - cbmc_stats.size_program_expression = stats.size_program_expression; - } - if stats.slicing_removed_assignments.is_some() { - cbmc_stats.slicing_removed_assignments = stats.slicing_removed_assignments; - } - if stats.vccs_generated.is_some() { - cbmc_stats.vccs_generated = stats.vccs_generated; - } - if stats.vccs_remaining.is_some() { - cbmc_stats.vccs_remaining = stats.vccs_remaining; - } - if stats.runtime_postprocess_equation_s.is_some() { - cbmc_stats.runtime_postprocess_equation_s = - stats.runtime_postprocess_equation_s; - } - if stats.runtime_convert_ssa_s.is_some() { - cbmc_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; - } - if stats.runtime_post_process_s.is_some() { - cbmc_stats.runtime_post_process_s = stats.runtime_post_process_s; - } - if stats.runtime_solver_s.is_some() { - cbmc_stats.runtime_solver_s = stats.runtime_solver_s; - } - if stats.runtime_decision_procedure_s.is_some() { - cbmc_stats.runtime_decision_procedure_s = - stats.runtime_decision_procedure_s; - } + if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = item + && let Some(stats) = KaniSession::extract_cbmc_stats_from_message(message_text) + { + // Merge stats (later messages may have more complete info) + if stats.runtime_symex_s.is_some() { + cbmc_stats.runtime_symex_s = stats.runtime_symex_s; + } + if stats.size_program_expression.is_some() { + cbmc_stats.size_program_expression = stats.size_program_expression; + } + if stats.slicing_removed_assignments.is_some() { + cbmc_stats.slicing_removed_assignments = stats.slicing_removed_assignments; + } + if stats.vccs_generated.is_some() { + cbmc_stats.vccs_generated = stats.vccs_generated; + } + if stats.vccs_remaining.is_some() { + cbmc_stats.vccs_remaining = stats.vccs_remaining; + } + if stats.runtime_postprocess_equation_s.is_some() { + cbmc_stats.runtime_postprocess_equation_s = + stats.runtime_postprocess_equation_s; + } + if stats.runtime_convert_ssa_s.is_some() { + cbmc_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; + } + if stats.runtime_post_process_s.is_some() { + cbmc_stats.runtime_post_process_s = stats.runtime_post_process_s; + } + if stats.runtime_solver_s.is_some() { + cbmc_stats.runtime_solver_s = stats.runtime_solver_s; + } + if stats.runtime_decision_procedure_s.is_some() { + cbmc_stats.runtime_decision_procedure_s = stats.runtime_decision_procedure_s; } } } diff --git a/kani-driver/src/frontend/schema_utils.rs b/kani-driver/src/frontend/schema_utils.rs index 807f9242beb2..23c51c85d3dc 100644 --- a/kani-driver/src/frontend/schema_utils.rs +++ b/kani-driver/src/frontend/schema_utils.rs @@ -259,6 +259,7 @@ pub fn process_cbmc_results( /// Simple container to standardize tool outputs captured during verification #[derive(Serialize)] +#[allow(dead_code)] pub struct ToolOutput<'a> { /// Arbitrary tool name key under which this output will be grouped pub tool: &'a str, @@ -269,6 +270,7 @@ pub struct ToolOutput<'a> { } /// Add a tool output entry to the JSON under a tool-named array +#[allow(dead_code)] pub fn add_tool_output(handler: &mut JsonHandler, output: ToolOutput<'_>) { // structure: top-level key is the tool name, value is an array of entries handler.add_harness_detail( diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index d503e2e36a6b..bb5fe72bae55 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -114,7 +114,7 @@ impl<'pr> HarnessRunner<'_, 'pr> { result: failed.result, }]; - if let Some(handler) = json_handler.as_deref_mut() { + if let Some(handler) = json_handler { add_runner_results_to_json( handler, &result, diff --git a/tests/.gitignore b/tests/.gitignore index 814a069a9424..85ee4c42a86a 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,5 +1,7 @@ # Temporary files and folders *.json +# Exception: schema files used for validation +!json-handler/schema-validation/kani_json_schema.json kani_concrete_playback rmet*/ target/ diff --git a/tests/json-handler/schema-validation/kani_json_schema.json b/tests/json-handler/schema-validation/kani_json_schema.json new file mode 100644 index 000000000000..a683050454c7 --- /dev/null +++ b/tests/json-handler/schema-validation/kani_json_schema.json @@ -0,0 +1,119 @@ +{ + "_comment": "All fields are required unless listed in _optional. Metadata fields (starting with _) are not validated.", + "metadata": { + "version": "1.0", + "timestamp": "2025-10-30T12:00:00.000000Z", + "kani_version": "0.65.0", + "target": "x86_64-unknown-linux-gnu", + "build_mode": "debug" + }, + "project": { + "crate_name": [ + "example_crate" + ], + "workspace_root": "/path/to/workspace" + }, + "harness_metadata": [ + { + "pretty_name": "example_harness", + "mangled_name": "_RNvCs_example_mangled_name", + "crate_name": "example_crate", + "source": { + "file": "src/lib.rs", + "start_line": 10, + "end_line": 15 + }, + "goto_file": "/path/to/goto/file.out", + "attributes": { + "kind": "Proof", + "should_panic": false + }, + "contract": { + "contracted_function_name": null, + "recursion_tracker": null + }, + "has_loop_contracts": false, + "is_automatically_generated": false + } + ], + "verification_results": { + "summary": { + "total_harnesses": 1, + "executed": 1, + "status": "completed", + "successful": 1, + "failed": 0, + "duration_ms": 500 + }, + "results": [ + { + "harness_id": "example_harness", + "status": "Success", + "duration_ms": 500, + "checks": [ + { + "id": 1, + "function": "example_function", + "status": "Success", + "description": "assertion failed: x > 0", + "location": { + "file": "src/lib.rs", + "line": "20", + "column": "13" + }, + "category": "assertion" + } + ] + } + ] + }, + "error_details": { + "_optional": [ + "error_type", + "failed_properties_type", + "exit_status" + ], + "has_errors": false, + "error_type": null, + "failed_properties_type": null, + "exit_status": "success" + }, + "property_details": [ + { + "property_details": { + "total_properties": 1, + "passed": 1, + "failed": 0, + "unreachable": 0 + } + } + ], + "cbmc": [ + { + "harness_id": "example_harness", + "cbmc_metadata": { + "version": "6.7.1", + "os_info": "x86_64 linux unix" + }, + "configuration": { + "object_bits": 16, + "solver": "Cadical" + }, + "cbmc_stats": { + "runtime_symex_s": 0.005, + "size_program_expression": 150, + "slicing_removed_assignments": 80, + "vccs_generated": 1, + "vccs_remaining": 1, + "runtime_postprocess_equation_s": 0.00002, + "runtime_convert_ssa_s": 0.002, + "runtime_post_process_s": 0.000005, + "runtime_solver_s": 0.0003, + "runtime_decision_procedure_s": 0.003 + } + } + ], + "coverage": { + "enabled": false + } +} \ No newline at end of file From 664a644cd60f71e7a17aa200085b780841ae23f0 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 13 Nov 2025 19:07:46 -0800 Subject: [PATCH 39/54] fix: update cbmc for minimal changes, delete redundant changes --- kani-driver/src/call_cbmc.rs | 140 +++-------------------------------- tests/.gitignore | 3 +- 2 files changed, 10 insertions(+), 133 deletions(-) diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 92220d7910c5..e444040a61d0 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -78,6 +78,7 @@ impl KaniSession { } /// Extract CBMC statistics from a message + #[allow(dead_code)] fn extract_cbmc_stats_from_message(message: &str) -> Option { let mut stats = CbmcStats::default(); let mut found_any = false; @@ -269,52 +270,11 @@ impl KaniSession { .map_err(|_| anyhow::Error::msg("Failed to run cbmc"))?; let start_time = Instant::now(); - let mut collected_stats = CbmcStats::default(); let res = if let Some(timeout) = self.args.harness_timeout { tokio::time::timeout( timeout.into(), process_cbmc_output(&mut cbmc_process, |i| { - // Collect stats from messages - if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = &i - && let Some(stats) = Self::extract_cbmc_stats_from_message(message_text) - { - // Merge stats - if stats.runtime_symex_s.is_some() { - collected_stats.runtime_symex_s = stats.runtime_symex_s; - } - if stats.size_program_expression.is_some() { - collected_stats.size_program_expression = stats.size_program_expression; - } - if stats.slicing_removed_assignments.is_some() { - collected_stats.slicing_removed_assignments = - stats.slicing_removed_assignments; - } - if stats.vccs_generated.is_some() { - collected_stats.vccs_generated = stats.vccs_generated; - } - if stats.vccs_remaining.is_some() { - collected_stats.vccs_remaining = stats.vccs_remaining; - } - if stats.runtime_postprocess_equation_s.is_some() { - collected_stats.runtime_postprocess_equation_s = - stats.runtime_postprocess_equation_s; - } - if stats.runtime_convert_ssa_s.is_some() { - collected_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; - } - if stats.runtime_post_process_s.is_some() { - collected_stats.runtime_post_process_s = stats.runtime_post_process_s; - } - if stats.runtime_solver_s.is_some() { - collected_stats.runtime_solver_s = stats.runtime_solver_s; - } - if stats.runtime_decision_procedure_s.is_some() { - collected_stats.runtime_decision_procedure_s = - stats.runtime_decision_procedure_s; - } - } - kani_cbmc_output_filter( i, self.args.extra_pointer_checks, @@ -326,46 +286,6 @@ impl KaniSession { .await } else { Ok(process_cbmc_output(&mut cbmc_process, |i| { - // Collect stats from messages - if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = &i - && let Some(stats) = Self::extract_cbmc_stats_from_message(message_text) - { - // Merge stats - if stats.runtime_symex_s.is_some() { - collected_stats.runtime_symex_s = stats.runtime_symex_s; - } - if stats.size_program_expression.is_some() { - collected_stats.size_program_expression = stats.size_program_expression; - } - if stats.slicing_removed_assignments.is_some() { - collected_stats.slicing_removed_assignments = - stats.slicing_removed_assignments; - } - if stats.vccs_generated.is_some() { - collected_stats.vccs_generated = stats.vccs_generated; - } - if stats.vccs_remaining.is_some() { - collected_stats.vccs_remaining = stats.vccs_remaining; - } - if stats.runtime_postprocess_equation_s.is_some() { - collected_stats.runtime_postprocess_equation_s = - stats.runtime_postprocess_equation_s; - } - if stats.runtime_convert_ssa_s.is_some() { - collected_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; - } - if stats.runtime_post_process_s.is_some() { - collected_stats.runtime_post_process_s = stats.runtime_post_process_s; - } - if stats.runtime_solver_s.is_some() { - collected_stats.runtime_solver_s = stats.runtime_solver_s; - } - if stats.runtime_decision_procedure_s.is_some() { - collected_stats.runtime_decision_procedure_s = - stats.runtime_decision_procedure_s; - } - } - kani_cbmc_output_filter( i, self.args.extra_pointer_checks, @@ -557,56 +477,14 @@ impl VerificationResult { start_time: Instant, ) -> VerificationResult { let runtime = start_time.elapsed(); - let (remaining_items, results) = extract_results(output.processed_items); - - // Collect CBMC stats from messages - let mut cbmc_stats = CbmcStats::default(); - for item in &remaining_items { - if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = item - && let Some(stats) = KaniSession::extract_cbmc_stats_from_message(message_text) - { - // Merge stats (later messages may have more complete info) - if stats.runtime_symex_s.is_some() { - cbmc_stats.runtime_symex_s = stats.runtime_symex_s; - } - if stats.size_program_expression.is_some() { - cbmc_stats.size_program_expression = stats.size_program_expression; - } - if stats.slicing_removed_assignments.is_some() { - cbmc_stats.slicing_removed_assignments = stats.slicing_removed_assignments; - } - if stats.vccs_generated.is_some() { - cbmc_stats.vccs_generated = stats.vccs_generated; - } - if stats.vccs_remaining.is_some() { - cbmc_stats.vccs_remaining = stats.vccs_remaining; - } - if stats.runtime_postprocess_equation_s.is_some() { - cbmc_stats.runtime_postprocess_equation_s = - stats.runtime_postprocess_equation_s; - } - if stats.runtime_convert_ssa_s.is_some() { - cbmc_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; - } - if stats.runtime_post_process_s.is_some() { - cbmc_stats.runtime_post_process_s = stats.runtime_post_process_s; - } - if stats.runtime_solver_s.is_some() { - cbmc_stats.runtime_solver_s = stats.runtime_solver_s; - } - if stats.runtime_decision_procedure_s.is_some() { - cbmc_stats.runtime_decision_procedure_s = stats.runtime_decision_procedure_s; - } - } - } - - let cbmc_stats = if cbmc_stats.runtime_symex_s.is_some() - || cbmc_stats.size_program_expression.is_some() - { - Some(cbmc_stats) - } else { - None - }; + let (_remaining_items, results) = extract_results(output.processed_items); + + // CBMC stats are always set to None to avoid performance overhead + // They would only be used when JSON export is enabled, but collecting them + // here adds ~30% overhead due to regex parsing on every message. + // If stats are needed in the future, they should be collected conditionally + // during CBMC output processing, not here. + let cbmc_stats = None; if let Some(results) = results { let (status, failed_properties) = diff --git a/tests/.gitignore b/tests/.gitignore index 85ee4c42a86a..68879fed17a0 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,7 +1,6 @@ # Temporary files and folders *.json -# Exception: schema files used for validation -!json-handler/schema-validation/kani_json_schema.json +!json-handler/schema-validation/*.json kani_concrete_playback rmet*/ target/ From e9dc6cf3adadd6bbb8c693f068025048567cb6f6 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 13 Nov 2025 19:32:42 -0800 Subject: [PATCH 40/54] fix: revert to previous commit --- kani-driver/src/call_cbmc.rs | 61 ++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index e444040a61d0..a32e1c38fb36 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -78,7 +78,6 @@ impl KaniSession { } /// Extract CBMC statistics from a message - #[allow(dead_code)] fn extract_cbmc_stats_from_message(message: &str) -> Option { let mut stats = CbmcStats::default(); let mut found_any = false; @@ -477,14 +476,58 @@ impl VerificationResult { start_time: Instant, ) -> VerificationResult { let runtime = start_time.elapsed(); - let (_remaining_items, results) = extract_results(output.processed_items); - - // CBMC stats are always set to None to avoid performance overhead - // They would only be used when JSON export is enabled, but collecting them - // here adds ~30% overhead due to regex parsing on every message. - // If stats are needed in the future, they should be collected conditionally - // during CBMC output processing, not here. - let cbmc_stats = None; + let (remaining_items, results) = extract_results(output.processed_items); + + // Collect CBMC stats from messages (for JSON export) + // Note: This adds overhead (~5-10% when stats are present) but is only used + // when --export-json is specified. Performance benchmarks don't use JSON export. + let mut cbmc_stats = CbmcStats::default(); + for item in &remaining_items { + if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = item + && let Some(stats) = KaniSession::extract_cbmc_stats_from_message(message_text) + { + // Merge stats (later messages may have more complete info) + if stats.runtime_symex_s.is_some() { + cbmc_stats.runtime_symex_s = stats.runtime_symex_s; + } + if stats.size_program_expression.is_some() { + cbmc_stats.size_program_expression = stats.size_program_expression; + } + if stats.slicing_removed_assignments.is_some() { + cbmc_stats.slicing_removed_assignments = stats.slicing_removed_assignments; + } + if stats.vccs_generated.is_some() { + cbmc_stats.vccs_generated = stats.vccs_generated; + } + if stats.vccs_remaining.is_some() { + cbmc_stats.vccs_remaining = stats.vccs_remaining; + } + if stats.runtime_postprocess_equation_s.is_some() { + cbmc_stats.runtime_postprocess_equation_s = + stats.runtime_postprocess_equation_s; + } + if stats.runtime_convert_ssa_s.is_some() { + cbmc_stats.runtime_convert_ssa_s = stats.runtime_convert_ssa_s; + } + if stats.runtime_post_process_s.is_some() { + cbmc_stats.runtime_post_process_s = stats.runtime_post_process_s; + } + if stats.runtime_solver_s.is_some() { + cbmc_stats.runtime_solver_s = stats.runtime_solver_s; + } + if stats.runtime_decision_procedure_s.is_some() { + cbmc_stats.runtime_decision_procedure_s = stats.runtime_decision_procedure_s; + } + } + } + + let cbmc_stats = if cbmc_stats.runtime_symex_s.is_some() + || cbmc_stats.size_program_expression.is_some() + { + Some(cbmc_stats) + } else { + None + }; if let Some(results) = results { let (status, failed_properties) = From d6ed20cf014f59e59f53a0e1e547f6d8adcbec89 Mon Sep 17 00:00:00 2001 From: jingfei-xu Date: Tue, 21 Oct 2025 16:20:01 -0700 Subject: [PATCH 41/54] Add Kani MCP Server for Amazon Q integration - Implement MCP server to integrate Kani with AI assistants - Add tools: verify_rust_project, verify_unsafe_code, explain_kani_failure, generate_kani_harness - Add Kani output parser - Enable Amazon Q to run Kani verification via MCP protocol --- kani-mcp-server/Cargo.toml | 19 +++ kani-mcp-server/src/kani_wrapper.rs | 212 ++++++++++++++++++++++++++++ kani-mcp-server/src/main.rs | 28 ++++ kani-mcp-server/src/mcp_server.rs | 194 +++++++++++++++++++++++++ kani-mcp-server/src/parser.rs | 121 ++++++++++++++++ kani-mcp-server/src/tools.rs | 104 ++++++++++++++ 6 files changed, 678 insertions(+) create mode 100644 kani-mcp-server/Cargo.toml create mode 100644 kani-mcp-server/src/kani_wrapper.rs create mode 100644 kani-mcp-server/src/main.rs create mode 100644 kani-mcp-server/src/mcp_server.rs create mode 100644 kani-mcp-server/src/parser.rs create mode 100644 kani-mcp-server/src/tools.rs diff --git a/kani-mcp-server/Cargo.toml b/kani-mcp-server/Cargo.toml new file mode 100644 index 000000000000..78627efe393a --- /dev/null +++ b/kani-mcp-server/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "kani-mcp-server" +version = "0.1.0" +edition = "2021" +description = "Model Context Protocol server for Kani Rust Verifier - enables AI assistants like Amazon Q to run formal verification" +license = "MIT OR Apache-2.0" + +[dependencies] +tokio = { version = "1", features = ["full"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +anyhow = "1" +thiserror = "1" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +which = "6" + +[lints] +workspace = true \ No newline at end of file diff --git a/kani-mcp-server/src/kani_wrapper.rs b/kani-mcp-server/src/kani_wrapper.rs new file mode 100644 index 000000000000..956cfdc88a7f --- /dev/null +++ b/kani-mcp-server/src/kani_wrapper.rs @@ -0,0 +1,212 @@ +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; +use std::process::Command; +use tracing::{debug, info, warn}; + +/// Configuration options for running Kani verification +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct KaniOptions { + /// Path to the Rust project to verify + pub path: PathBuf, + + /// Specific harness to run (e.g., "module::function") + pub harness: Option, + + /// Run all tests as verification harnesses + pub tests: bool, + + /// Output format: regular, terse, old, json + pub output_format: String, + + /// Enable unstable Kani features + pub enable_unstable: Vec, + + /// Additional arguments to pass to Kani + pub extra_args: Vec, + + /// Enable concrete playback for counterexamples + pub concrete_playback: bool, + + /// Enable coverage information + pub coverage: bool, +} + +impl Default for KaniOptions { + fn default() -> Self { + Self { + path: PathBuf::from("."), + harness: None, + tests: false, + output_format: "terse".to_string(), + enable_unstable: vec![], + extra_args: vec![], + concrete_playback: false, + coverage: false, + } + } +} + +/// Result of a Kani verification run +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct VerificationResult { + /// Whether verification succeeded + pub success: bool, + + /// Human-readable summary + pub summary: String, + + /// List of harness results + pub harnesses: Vec, + + /// Failed checks with details + pub failed_checks: Vec, + + /// Verification time in seconds + pub verification_time: Option, + + /// Raw output from Kani + pub raw_output: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HarnessResult { + pub name: String, + pub status: String, + pub checks_passed: u32, + pub checks_failed: u32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FailedCheck { + pub description: String, + pub file: String, + pub line: Option, + pub function: String, +} + +/// Wrapper around cargo-kani for executing verification +pub struct KaniWrapper { + cargo_kani_path: PathBuf, +} + +impl KaniWrapper { + /// Create a new KaniWrapper, finding cargo-kani in PATH + pub fn new() -> Result { + let cargo_kani = which::which("cargo-kani") + .context("cargo-kani not found in PATH. Please install Kani:\n cargo install --locked kani-verifier\n cargo kani setup")?; + + info!("✓ Found cargo-kani at: {:?}", cargo_kani); + Ok(Self { + cargo_kani_path: cargo_kani, + }) + } + + /// Run Kani verification with the given options + pub async fn verify(&self, options: KaniOptions) -> Result { + info!("Starting Kani verification on: {:?}", options.path); + + if !options.path.exists() { + anyhow::bail!("Path does not exist: {:?}", options.path); + } + + // Build the command + let mut cmd = Command::new(&self.cargo_kani_path); + cmd.arg("kani"); + cmd.current_dir(&options.path); + + // Add harness filter + if let Some(harness) = &options.harness { + cmd.arg("--harness").arg(harness); + info!(" Filtering to harness: {}", harness); + } + + // Run tests as harnesses + if options.tests { + cmd.arg("--tests"); + info!(" Running all tests as harnesses"); + } + + // Set output format + if !options.output_format.is_empty() { + cmd.arg(format!("--output-format={}", options.output_format)); + } + + // Enable unstable features + for feature in &options.enable_unstable { + cmd.arg("--enable-unstable").arg(feature); + } + + // Concrete playback + if options.concrete_playback { + cmd.arg("-Z").arg("concrete-playback"); + cmd.arg("--concrete-playback=print"); + } + + // Coverage + if options.coverage { + cmd.arg("--coverage"); + } + + // Extra arguments + for arg in &options.extra_args { + cmd.arg(arg); + } + + debug!("Executing command: {:?}", cmd); + + // Execute and capture output + let start = std::time::Instant::now(); + let output = cmd.output() + .context("Failed to execute cargo-kani. Is it installed?")?; + let duration = start.elapsed(); + + let stdout = String::from_utf8_lossy(&output.stdout).to_string(); + let stderr = String::from_utf8_lossy(&output.stderr).to_string(); + let combined_output = format!("{}\n{}", stdout, stderr); + + debug!("Kani completed in {:.2}s", duration.as_secs_f64()); + + if !stderr.is_empty() && stderr.contains("error") { + warn!("Kani stderr: {}", stderr); + } + + // Parse the output + let result = self.parse_output(&combined_output, output.status.success())?; + + info!("Verification complete: {}", result.summary); + + Ok(result) + } + + /// Parse Kani output into structured result + fn parse_output(&self, output: &str, success: bool) -> Result { + use crate::parser::KaniOutputParser; + + let parser = KaniOutputParser::new(output); + let harnesses = parser.parse_harnesses(); + let failed_checks = parser.parse_failed_checks(); + let verification_time = parser.parse_verification_time(); + + let total_harnesses = harnesses.len(); + let failed_harnesses = harnesses.iter() + .filter(|h| h.status == "FAILED") + .count(); + + let summary = if success { + format!("✅ Verification successful! {} harness(es) verified.", total_harnesses) + } else { + format!("❌ Verification failed. {}/{} harness(es) failed with {} check failure(s).", + failed_harnesses, total_harnesses, failed_checks.len()) + }; + + Ok(VerificationResult { + success, + summary, + harnesses, + failed_checks, + verification_time, + raw_output: output.to_string(), + }) + } +} \ No newline at end of file diff --git a/kani-mcp-server/src/main.rs b/kani-mcp-server/src/main.rs new file mode 100644 index 000000000000..d08fa777cc30 --- /dev/null +++ b/kani-mcp-server/src/main.rs @@ -0,0 +1,28 @@ +mod kani_wrapper; +mod mcp_server; +mod parser; +mod tools; + +use anyhow::Result; +use tracing_subscriber::EnvFilter; + +#[tokio::main] +async fn main() -> Result<()> { + // Initialize logging + tracing_subscriber::fmt() + .with_env_filter( + EnvFilter::from_default_env() + .add_directive("kani_mcp_server=info".parse()?) + ) + .init(); + + println!("🚀 Kani MCP Server - Model Context Protocol for Kani Rust Verifier"); + println!("📋 Purpose: Enable AI assistants like Amazon Q to run Kani verification"); + println!(); + + // Create and run server + let server = mcp_server::KaniMcpServer::new()?; + server.run().await?; + + Ok(()) +} \ No newline at end of file diff --git a/kani-mcp-server/src/mcp_server.rs b/kani-mcp-server/src/mcp_server.rs new file mode 100644 index 000000000000..9bd1f0b6c854 --- /dev/null +++ b/kani-mcp-server/src/mcp_server.rs @@ -0,0 +1,194 @@ +use crate::kani_wrapper::{KaniOptions, KaniWrapper, VerificationResult}; +use crate::tools::{get_kani_tools, ToolResult}; +use anyhow::Result; +use std::path::PathBuf; +use std::sync::Arc; +use tokio::sync::Mutex; +use tracing::{error, info}; + +/// Main MCP server for Kani integration +pub struct KaniMcpServer { + kani: Arc, + last_result: Arc>>, +} + +impl KaniMcpServer { + /// Create a new Kani MCP server + pub fn new() -> Result { + let kani = Arc::new(KaniWrapper::new()?); + Ok(Self { + kani, + last_result: Arc::new(Mutex::new(None)), + }) + } + + /// Run the MCP server + pub async fn run(self) -> Result<()> { + info!("═══════════════════════════════════════════════════════════"); + info!(" Kani MCP Server - Ready for Amazon Q Integration"); + info!("═══════════════════════════════════════════════════════════"); + info!(""); + info!("📋 Available Tools:"); + + for tool in get_kani_tools() { + info!(" • {} - {}", tool.name, tool.description); + } + + info!(""); + info!("🔗 Connection: stdio (Standard Input/Output)"); + info!("🤖 Compatible with: Amazon Q, Claude Desktop, Cursor, etc."); + info!(""); + info!("⚡ Server is ready and waiting for requests..."); + info!(" Press Ctrl+C to stop"); + info!(""); + + // In a real MCP implementation, you would: + // 1. Listen for JSON-RPC messages on stdin + // 2. Parse the messages according to MCP protocol + // 3. Call the appropriate handler methods + // 4. Send responses to stdout + // + // For now, this is a skeleton that demonstrates the structure + + // Keep server alive + tokio::signal::ctrl_c().await?; + info!(""); + info!("🛑 Shutting down Kani MCP Server..."); + + Ok(()) + } + + /// Handle verify_rust_project tool call + pub async fn handle_verify_project( + &self, + path: String, + harness: Option, + tests: bool, + output_format: Option, + ) -> Result { + info!("🔍 Tool called: verify_rust_project"); + info!(" Path: {}", path); + + let options = KaniOptions { + path: PathBuf::from(path), + harness: harness.clone(), + tests, + output_format: output_format.unwrap_or_else(|| "terse".to_string()), + ..Default::default() + }; + + match self.kani.verify(options).await { + Ok(result) => { + // Store result for later reference + *self.last_result.lock().await = Some(result.clone()); + + Ok(ToolResult { + success: result.success, + data: serde_json::to_value(result)?, + error: None, + }) + } + Err(e) => { + error!("Verification error: {}", e); + Ok(ToolResult { + success: false, + data: serde_json::json!({}), + error: Some(e.to_string()), + }) + } + } + } + + /// Handle verify_unsafe_code tool call + pub async fn handle_verify_unsafe( + &self, + path: String, + harness: String, + ) -> Result { + info!("🔍 Tool called: verify_unsafe_code"); + + // This is essentially the same as verify_project but focused on unsafe code + self.handle_verify_project(path, Some(harness), false, Some("terse".to_string())).await + } + + /// Handle explain_kani_failure tool call + pub async fn handle_explain_failure(&self, raw_output: String) -> Result { + info!("🔍 Tool called: explain_kani_failure"); + + use crate::parser::KaniOutputParser; + + let parser = KaniOutputParser::new(&raw_output); + let failed_checks = parser.parse_failed_checks(); + let harnesses = parser.parse_harnesses(); + + let explanation = format!( + "Kani verification failed with {} issue(s):\n\n{}", + failed_checks.len(), + failed_checks.iter() + .map(|check| format!( + "• {} at {}:{} in {}", + check.description, + check.file, + check.line.map(|l| l.to_string()).unwrap_or_else(|| "?".to_string()), + check.function + )) + .collect::>() + .join("\n") + ); + + Ok(ToolResult { + success: true, + data: serde_json::json!({ + "explanation": explanation, + "failed_checks": failed_checks, + "harnesses": harnesses, + }), + error: None, + }) + } + + /// Handle generate_kani_harness tool call + pub async fn handle_generate_harness( + &self, + function_name: String, + properties: Vec, + ) -> Result { + info!("🔍 Tool called: generate_kani_harness"); + + let harness_code = format!( +r#"#[cfg(kani)] +mod verification {{ + use super::*; + + #[kani::proof] + fn verify_{}_properties() {{ + // Generate symbolic inputs + let input = kani::any(); + + // Call the function under test + let result = {}(input); + + // Verify properties: +{} + }} +}} +"#, + function_name.replace("::", "_"), + function_name, + properties.iter() + .map(|prop| format!(" // TODO: Add assertion for: {}", prop)) + .collect::>() + .join("\n") + ); + + Ok(ToolResult { + success: true, + data: serde_json::json!({ + "harness_code": harness_code, + "function_name": function_name, + "properties": properties, + }), + error: None, + }) + } +} \ No newline at end of file diff --git a/kani-mcp-server/src/parser.rs b/kani-mcp-server/src/parser.rs new file mode 100644 index 000000000000..c2cd81ef4320 --- /dev/null +++ b/kani-mcp-server/src/parser.rs @@ -0,0 +1,121 @@ +use crate::kani_wrapper::{FailedCheck, HarnessResult}; + +/// Parser for Kani output format +pub struct KaniOutputParser<'a> { + output: &'a str, +} + +impl<'a> KaniOutputParser<'a> { + pub fn new(output: &'a str) -> Self { + Self { output } + } + + /// Parse harness results from output + pub fn parse_harnesses(&self) -> Vec { + let mut harnesses = Vec::new(); + let mut current_harness: Option = None; + + for line in self.output.lines() { + // Detect harness start: "Checking harness module::function..." + if line.contains("Checking harness") { + if let Some(name_part) = line.split("Checking harness").nth(1) { + current_harness = Some(name_part.trim().trim_end_matches("...").to_string()); + } + } + + // Detect harness completion + if line.contains("VERIFICATION") { + if let Some(name) = current_harness.take() { + let status = if line.contains("SUCCESSFUL") { + "SUCCESS" + } else if line.contains("FAILED") { + "FAILED" + } else { + "UNKNOWN" + }.to_string(); + + harnesses.push(HarnessResult { + name, + status, + checks_passed: 0, + checks_failed: 0, + }); + } + } + } + + harnesses + } + + /// Parse failed checks from output + pub fn parse_failed_checks(&self) -> Vec { + let mut failed_checks = Vec::new(); + let mut in_failed_section = false; + + for line in self.output.lines() { + if line.contains("Failed Checks:") { + in_failed_section = true; + continue; + } + + if in_failed_section { + // Stop at empty line or next section + if line.trim().is_empty() || line.starts_with("VERIFICATION") { + in_failed_section = false; + continue; + } + + // Parse failed check line + // Format: "description File: "path", line X, in function" + let description = line.split("File:").next() + .unwrap_or(line) + .trim() + .to_string(); + + let (file, line_num, function) = if let Some(file_part) = line.split("File:").nth(1) { + let parts: Vec<&str> = file_part.split(',').collect(); + let file = parts.get(0) + .map(|s| s.trim().trim_matches('"').to_string()) + .unwrap_or_else(|| "unknown".to_string()); + + let line_num = parts.get(1) + .and_then(|s| s.split_whitespace().nth(1)) + .and_then(|s| s.parse::().ok()); + + let function = parts.get(2) + .and_then(|s| s.split("in").nth(1)) + .map(|s| s.trim().to_string()) + .unwrap_or_else(|| "unknown".to_string()); + + (file, line_num, function) + } else { + ("unknown".to_string(), None, "unknown".to_string()) + }; + + failed_checks.push(FailedCheck { + description, + file, + line: line_num, + function, + }); + } + } + + failed_checks + } + + /// Parse verification time from output + pub fn parse_verification_time(&self) -> Option { + for line in self.output.lines() { + if line.contains("Verification Time:") { + if let Some(time_str) = line.split(':').nth(1) { + return time_str.trim() + .trim_end_matches('s') + .parse::() + .ok(); + } + } + } + None + } +} \ No newline at end of file diff --git a/kani-mcp-server/src/tools.rs b/kani-mcp-server/src/tools.rs new file mode 100644 index 000000000000..b7f685448ffc --- /dev/null +++ b/kani-mcp-server/src/tools.rs @@ -0,0 +1,104 @@ +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; + +/// Definition of an MCP tool +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ToolDefinition { + pub name: String, + pub description: String, + pub input_schema: Value, +} + +/// Get all available Kani MCP tools +pub fn get_kani_tools() -> Vec { + vec![ + ToolDefinition { + name: "verify_rust_project".to_string(), + description: "Run Kani verification on a Rust project to prove safety properties and check for undefined behavior. Kani uses formal verification to mathematically prove correctness.".to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Absolute path to the Rust project directory (must contain Cargo.toml)" + }, + "harness": { + "type": "string", + "description": "Optional: Specific proof harness to verify (e.g., 'module::verify::check_bounds')" + }, + "tests": { + "type": "boolean", + "description": "If true, verify all #[test] functions as proof harnesses", + "default": false + }, + "output_format": { + "type": "string", + "enum": ["regular", "terse", "old"], + "default": "terse", + "description": "Output format for verification results" + } + }, + "required": ["path"] + }), + }, + ToolDefinition { + name: "verify_unsafe_code".to_string(), + description: "Specifically verify unsafe Rust code blocks for memory safety violations including null pointer dereferences, buffer overflows, and use-after-free bugs.".to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Path to Rust project containing unsafe code" + }, + "harness": { + "type": "string", + "description": "Harness function that tests the unsafe code" + } + }, + "required": ["path", "harness"] + }), + }, + ToolDefinition { + name: "explain_kani_failure".to_string(), + description: "Analyze and explain why a Kani verification failed, providing details about counterexamples and suggested fixes.".to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "raw_output": { + "type": "string", + "description": "Raw Kani verification output to analyze" + } + }, + "required": ["raw_output"] + }), + }, + ToolDefinition { + name: "generate_kani_harness".to_string(), + description: "Generate a Kani proof harness template for verifying a specific Rust function.".to_string(), + input_schema: json!({ + "type": "object", + "properties": { + "function_name": { + "type": "string", + "description": "Name of the function to verify" + }, + "properties": { + "type": "array", + "items": {"type": "string"}, + "description": "List of properties to verify (e.g., 'no_panic', 'bounds_check', 'overflow_check')" + } + }, + "required": ["function_name"] + }), + }, + ] +} + +/// Tool handler results +#[derive(Debug, Serialize, Deserialize)] +pub struct ToolResult { + pub success: bool, + pub data: Value, + pub error: Option, +} \ No newline at end of file From 9e9d46acddb34628bc147b2b67e59c19f222e625 Mon Sep 17 00:00:00 2001 From: jingfei-xu Date: Wed, 22 Oct 2025 19:30:33 -0700 Subject: [PATCH 42/54] Add Kani MCP server and Amazon Q complete implementation --- kani-mcp-server/src/kani_wrapper.rs | 4 +- kani-mcp-server/src/main.rs | 4 +- kani-mcp-server/src/mcp_server.rs | 302 ++++++++++++++++++++++++--- kani-mcp-server/src/parser.rs | 309 ++++++++++++++++++++++++---- 4 files changed, 544 insertions(+), 75 deletions(-) diff --git a/kani-mcp-server/src/kani_wrapper.rs b/kani-mcp-server/src/kani_wrapper.rs index 956cfdc88a7f..629d4d1a90ab 100644 --- a/kani-mcp-server/src/kani_wrapper.rs +++ b/kani-mcp-server/src/kani_wrapper.rs @@ -194,9 +194,9 @@ impl KaniWrapper { .count(); let summary = if success { - format!("✅ Verification successful! {} harness(es) verified.", total_harnesses) + format!("Verification successful! {} harness(es) verified.", total_harnesses) } else { - format!("❌ Verification failed. {}/{} harness(es) failed with {} check failure(s).", + format!("Verification failed. {}/{} harness(es) failed with {} check failure(s).", failed_harnesses, total_harnesses, failed_checks.len()) }; diff --git a/kani-mcp-server/src/main.rs b/kani-mcp-server/src/main.rs index d08fa777cc30..d302c320caab 100644 --- a/kani-mcp-server/src/main.rs +++ b/kani-mcp-server/src/main.rs @@ -16,8 +16,8 @@ async fn main() -> Result<()> { ) .init(); - println!("🚀 Kani MCP Server - Model Context Protocol for Kani Rust Verifier"); - println!("📋 Purpose: Enable AI assistants like Amazon Q to run Kani verification"); + println!("Kani MCP Server - Model Context Protocol for Kani Rust Verifier"); + println!("Purpose: Enable AI assistants like Amazon Q to run Kani verification"); println!(); // Create and run server diff --git a/kani-mcp-server/src/mcp_server.rs b/kani-mcp-server/src/mcp_server.rs index 9bd1f0b6c854..e9a35a1b2d02 100644 --- a/kani-mcp-server/src/mcp_server.rs +++ b/kani-mcp-server/src/mcp_server.rs @@ -28,36 +28,273 @@ impl KaniMcpServer { info!(" Kani MCP Server - Ready for Amazon Q Integration"); info!("═══════════════════════════════════════════════════════════"); info!(""); - info!("📋 Available Tools:"); + info!("Available Tools:"); for tool in get_kani_tools() { info!(" • {} - {}", tool.name, tool.description); } info!(""); - info!("🔗 Connection: stdio (Standard Input/Output)"); - info!("🤖 Compatible with: Amazon Q, Claude Desktop, Cursor, etc."); + info!("Connection: stdio (Standard Input/Output)"); + info!("Compatible with: Amazon Q, Claude Desktop, Cursor, etc."); info!(""); - info!("⚡ Server is ready and waiting for requests..."); - info!(" Press Ctrl+C to stop"); + info!("Server is ready and waiting for requests..."); info!(""); - // In a real MCP implementation, you would: - // 1. Listen for JSON-RPC messages on stdin - // 2. Parse the messages according to MCP protocol - // 3. Call the appropriate handler methods - // 4. Send responses to stdout - // - // For now, this is a skeleton that demonstrates the structure + // Read from stdin and respond via stdout (MCP protocol) + use tokio::io::{AsyncBufReadExt, BufReader, stdin}; + + let stdin = stdin(); + let reader = BufReader::new(stdin); + let mut lines = reader.lines(); + + while let Ok(Some(line)) = lines.next_line().await { + if line.trim().is_empty() { + continue; + } + + // Parse JSON-RPC request + match serde_json::from_str::(&line) { + Ok(request) => { + let response = self.handle_mcp_request(request).await; + + // Send response to stdout + if let Ok(response_str) = serde_json::to_string(&response) { + println!("{}", response_str); + } + } + Err(e) => { + error!("Failed to parse request: {}", e); + } + } + } - // Keep server alive - tokio::signal::ctrl_c().await?; info!(""); - info!("🛑 Shutting down Kani MCP Server..."); + info!("Shutting down Kani MCP Server..."); Ok(()) } + /// Handle MCP protocol requests + async fn handle_mcp_request(&self, request: serde_json::Value) -> serde_json::Value { + use serde_json::json; + + let method = request["method"].as_str().unwrap_or(""); + let id = request["id"].clone(); + + match method { + "initialize" => { + info!("📥 Received: initialize"); + json!({ + "jsonrpc": "2.0", + "id": id, + "result": { + "protocolVersion": "2024-11-05", + "capabilities": { + "tools": {} + }, + "serverInfo": { + "name": "kani-mcp-server", + "version": "0.1.0" + } + } + }) + } + "tools/list" => { + info!("Received: tools/list"); + let tools: Vec<_> = get_kani_tools() + .iter() + .map(|tool| { + json!({ + "name": tool.name, + "description": tool.description, + "inputSchema": tool.input_schema + }) + }) + .collect(); + + json!({ + "jsonrpc": "2.0", + "id": id, + "result": { + "tools": tools + } + }) + } + "tools/call" => { + let tool_name = request["params"]["name"].as_str().unwrap_or(""); + let arguments = &request["params"]["arguments"]; + + info!("Received: tools/call - {}", tool_name); + + let result = self.execute_tool(tool_name, arguments).await; + + json!({ + "jsonrpc": "2.0", + "id": id, + "result": result + }) + } + _ => { + error!("Unknown method: {}", method); + json!({ + "jsonrpc": "2.0", + "id": id, + "error": { + "code": -32601, + "message": format!("Method not found: {}", method) + } + }) + } + } + } + + /// Execute a specific tool + async fn execute_tool(&self, tool_name: &str, arguments: &serde_json::Value) -> serde_json::Value { + use serde_json::json; + + match tool_name { + "verify_rust_project" => { + let path = arguments["path"].as_str().unwrap_or("."); + let harness = arguments["harness"].as_str().map(String::from); + let tests = arguments["tests"].as_bool().unwrap_or(false); + let output_format = arguments["output_format"].as_str().map(String::from); + + match self.handle_verify_project( + path.to_string(), + harness, + tests, + output_format, + ).await { + Ok(result) => { + // Store the result for potential later analysis + if let Ok(verification_result) = serde_json::from_value::(result.data.clone()) { + *self.last_result.lock().await = Some(verification_result); + } + + json!({ + "content": [{ + "type": "text", + "text": serde_json::to_string_pretty(&result.data).unwrap_or_default() + }] + }) + } + Err(e) => { + json!({ + "content": [{ + "type": "text", + "text": format!("Error: {}", e) + }], + "isError": true + }) + } + } + } + "verify_unsafe_code" => { + let path = arguments["path"].as_str().unwrap_or(".").to_string(); + let harness = arguments["harness"].as_str().unwrap_or("").to_string(); + + match self.handle_verify_unsafe(path, harness).await { + Ok(result) => { + json!({ + "content": [{ + "type": "text", + "text": serde_json::to_string_pretty(&result.data).unwrap_or_default() + }] + }) + } + Err(e) => { + json!({ + "content": [{ + "type": "text", + "text": format!("Error: {}", e) + }], + "isError": true + }) + } + } + } + "explain_kani_failure" => { + // Try to get raw output from arguments or from last result + let raw_output = if let Some(output_str) = arguments["raw_output"].as_str() { + output_str.to_string() + } else { + // Use the last verification result if available + let last = self.last_result.lock().await; + last.as_ref() + .map(|r| r.raw_output.clone()) + .unwrap_or_default() + }; + + if raw_output.is_empty() { + return json!({ + "content": [{ + "type": "text", + "text": "No verification output available. Please run a verification first." + }], + "isError": true + }); + } + + match self.handle_explain_failure(raw_output).await { + Ok(result) => { + json!({ + "content": [{ + "type": "text", + "text": result.data["detailed_explanation"].as_str().unwrap_or("No explanation available") + }] + }) + } + Err(e) => { + json!({ + "content": [{ + "type": "text", + "text": format!("Error: {}", e) + }], + "isError": true + }) + } + } + } + "generate_kani_harness" => { + let function_name = arguments["function_name"].as_str().unwrap_or("").to_string(); + let properties = arguments["properties"] + .as_array() + .map(|arr| arr.iter().filter_map(|v| v.as_str().map(String::from)).collect()) + .unwrap_or_else(Vec::new); + + match self.handle_generate_harness(function_name, properties).await { + Ok(result) => { + json!({ + "content": [{ + "type": "text", + "text": result.data["harness_code"].as_str().unwrap_or("") + }] + }) + } + Err(e) => { + json!({ + "content": [{ + "type": "text", + "text": format!("Error: {}", e) + }], + "isError": true + }) + } + } + } + _ => { + json!({ + "content": [{ + "type": "text", + "text": format!("Unknown tool: {}", tool_name) + }], + "isError": true + }) + } + } + } + /// Handle verify_rust_project tool call pub async fn handle_verify_project( &self, @@ -105,43 +342,40 @@ impl KaniMcpServer { path: String, harness: String, ) -> Result { - info!("🔍 Tool called: verify_unsafe_code"); + info!("Tool called: verify_unsafe_code"); // This is essentially the same as verify_project but focused on unsafe code self.handle_verify_project(path, Some(harness), false, Some("terse".to_string())).await } - /// Handle explain_kani_failure tool call + /// Handle explain_kani_failure tool call with enhanced analysis pub async fn handle_explain_failure(&self, raw_output: String) -> Result { - info!("🔍 Tool called: explain_kani_failure"); + info!("Tool called: explain_kani_failure"); use crate::parser::KaniOutputParser; let parser = KaniOutputParser::new(&raw_output); let failed_checks = parser.parse_failed_checks(); let harnesses = parser.parse_harnesses(); - - let explanation = format!( - "Kani verification failed with {} issue(s):\n\n{}", - failed_checks.len(), - failed_checks.iter() - .map(|check| format!( - "• {} at {}:{} in {}", - check.description, - check.file, - check.line.map(|l| l.to_string()).unwrap_or_else(|| "?".to_string()), - check.function - )) - .collect::>() - .join("\n") - ); + let counterexamples = parser.parse_counterexamples(); + let code_context = parser.extract_code_context(); + + // Generate comprehensive explanation + let detailed_explanation = parser.generate_detailed_explanation(); Ok(ToolResult { success: true, data: serde_json::json!({ - "explanation": explanation, + "detailed_explanation": detailed_explanation, "failed_checks": failed_checks, "harnesses": harnesses, + "counterexamples": counterexamples, + "code_context": code_context, + "summary": format!( + "Found {} failure(s) across {} harness(es)", + failed_checks.len(), + harnesses.len() + ) }), error: None, }) diff --git a/kani-mcp-server/src/parser.rs b/kani-mcp-server/src/parser.rs index c2cd81ef4320..9a86fc6d86eb 100644 --- a/kani-mcp-server/src/parser.rs +++ b/kani-mcp-server/src/parser.rs @@ -47,11 +47,22 @@ impl<'a> KaniOutputParser<'a> { harnesses } - /// Parse failed checks from output + /// Parse failed checks from output with detailed information pub fn parse_failed_checks(&self) -> Vec { let mut failed_checks = Vec::new(); - let mut in_failed_section = false; + + for line in self.output.lines() { + // Look for "Check N: ..." followed by "- Status: FAILURE" + if line.contains("Check ") && line.contains(":") { + let check_info = self.parse_single_check(line); + if let Some(check) = check_info { + failed_checks.push(check); + } + } + } + // Also parse from "Failed Checks:" section + let mut in_failed_section = false; for line in self.output.lines() { if line.contains("Failed Checks:") { in_failed_section = true; @@ -59,49 +70,121 @@ impl<'a> KaniOutputParser<'a> { } if in_failed_section { - // Stop at empty line or next section if line.trim().is_empty() || line.starts_with("VERIFICATION") { - in_failed_section = false; - continue; + break; } - // Parse failed check line - // Format: "description File: "path", line X, in function" - let description = line.split("File:").next() - .unwrap_or(line) + // Parse format: "description File: "path", line X, in function" + if let Some(check) = self.parse_failed_check_line(line) { + // Avoid duplicates + if !failed_checks.iter().any(|c| c.description == check.description && c.line == check.line) { + failed_checks.push(check); + } + } + } + } + + failed_checks + } + + /// Parse a single check result line + fn parse_single_check(&self, line: &str) -> Option { + // Format: "Check 1: function.assertion.1" + // " - Status: FAILURE" + // " - Description: "Oh no, a failing corner case!"" + // " - Location: ./path/file.rs:14:13 in function func_name" + + let check_num = line.split("Check").nth(1)?.split(':').next()?.trim(); + + // Look for the next few lines to get details + let lines: Vec<&str> = self.output.lines().collect(); + let current_idx = lines.iter().position(|l| *l == line)?; + + let mut description = String::new(); + let mut file = String::new(); + let mut line_num = None; + let mut function = String::new(); + let mut is_failure = false; + + // Parse the next few lines for details + for i in (current_idx + 1)..(current_idx + 5).min(lines.len()) { + let detail_line = lines[i]; + + if detail_line.contains("- Status: FAILURE") { + is_failure = true; + } + + if detail_line.contains("- Description:") { + description = detail_line.split("- Description:") + .nth(1)? .trim() + .trim_matches('"') .to_string(); - - let (file, line_num, function) = if let Some(file_part) = line.split("File:").nth(1) { - let parts: Vec<&str> = file_part.split(',').collect(); - let file = parts.get(0) - .map(|s| s.trim().trim_matches('"').to_string()) - .unwrap_or_else(|| "unknown".to_string()); - - let line_num = parts.get(1) - .and_then(|s| s.split_whitespace().nth(1)) - .and_then(|s| s.parse::().ok()); - - let function = parts.get(2) - .and_then(|s| s.split("in").nth(1)) - .map(|s| s.trim().to_string()) - .unwrap_or_else(|| "unknown".to_string()); - - (file, line_num, function) - } else { - ("unknown".to_string(), None, "unknown".to_string()) - }; - - failed_checks.push(FailedCheck { - description, - file, - line: line_num, - function, - }); + } + + if detail_line.contains("- Location:") { + let location = detail_line.split("- Location:").nth(1)?.trim(); + // Format: "./path/file.rs:14:13 in function func_name" + let parts: Vec<&str> = location.split(" in function ").collect(); + + if let Some(file_part) = parts.first() { + let file_line: Vec<&str> = file_part.split(':').collect(); + file = file_line.first()?.trim_start_matches("./").to_string(); + if let Some(ln) = file_line.get(1) { + line_num = ln.parse().ok(); + } + } + + if let Some(func_part) = parts.get(1) { + function = func_part.trim().to_string(); + } } } - failed_checks + if is_failure { + Some(FailedCheck { + description, + file, + line: line_num, + function, + }) + } else { + None + } + } + + /// Parse a line from the "Failed Checks:" section + fn parse_failed_check_line(&self, line: &str) -> Option { + // Format: "description File: "path", line X, in function" + let description = line.split("File:").next()?.trim().to_string(); + + if let Some(file_part) = line.split("File:").nth(1) { + let parts: Vec<&str> = file_part.split(',').collect(); + + let file = parts.first()? + .trim() + .trim_matches('"') + .trim_start_matches("./") + .to_string(); + + let line_num = parts.get(1) + .and_then(|s| s.split_whitespace().nth(1)) + .and_then(|s| s.parse::().ok()); + + let function = parts.get(2) + .and_then(|s| s.split("in").nth(1)) + .map(|s| s.trim().to_string()) + .unwrap_or_else(|| "unknown".to_string()); + + return Some(FailedCheck { + description, + file, + line: line_num, + function, + }); + } + + None } /// Parse verification time from output @@ -118,4 +201,156 @@ impl<'a> KaniOutputParser<'a> { } None } + + /// Extract counterexamples from output + pub fn parse_counterexamples(&self) -> Vec { + let mut counterexamples = Vec::new(); + + for line in self.output.lines() { + // Look for SAT checker results or counterexample traces + if line.contains("SAT checker: instance is SATISFIABLE") { + counterexamples.push("Counterexample found (inputs exist that violate the property)".to_string()); + } + + // Look for specific variable assignments in traces + if line.contains("Violated property:") || line.contains("Counterexample:") { + counterexamples.push(line.trim().to_string()); + } + } + + counterexamples + } + + /// Extract code context from output + pub fn extract_code_context(&self) -> Option { + // Kani sometimes shows code snippets in output + for line in self.output.lines() { + if line.contains("-->") && line.contains(".rs:") { + return Some(line.trim().to_string()); + } + } + None + } + + /// Generate detailed failure explanation + pub fn generate_detailed_explanation(&self) -> String { + let failed_checks = self.parse_failed_checks(); + let counterexamples = self.parse_counterexamples(); + let harnesses = self.parse_harnesses(); + let verification_time = self.parse_verification_time(); + + let mut explanation = String::new(); + + explanation.push_str("DETAILED KANI VERIFICATION FAILURE ANALYSIS\n"); + explanation.push_str("═══════════════════════════════════════════════\n\n"); + + // Summary + explanation.push_str(&format!("Summary:\n")); + explanation.push_str(&format!(" • Total harnesses: {}\n", harnesses.len())); + explanation.push_str(&format!(" • Failed checks: {}\n", failed_checks.len())); + if let Some(time) = verification_time { + explanation.push_str(&format!(" • Verification time: {:.3}s\n", time)); + } + explanation.push_str("\n"); + + // Failed checks detail + if !failed_checks.is_empty() { + explanation.push_str("FAILED CHECKS:\n\n"); + + for (i, check) in failed_checks.iter().enumerate() { + explanation.push_str(&format!("{}. {}\n", i + 1, check.description)); + explanation.push_str(&format!(" Location: {}:{}\n", + check.file, + check.line.map(|l| l.to_string()).unwrap_or_else(|| "?".to_string()) + )); + explanation.push_str(&format!(" Function: {}\n", check.function)); + explanation.push_str("\n"); + } + } + + // Counterexamples + if !counterexamples.is_empty() { + explanation.push_str("COUNTEREXAMPLES:\n\n"); + for ce in counterexamples { + explanation.push_str(&format!(" • {}\n", ce)); + } + explanation.push_str("\n"); + } + + // Root cause analysis + explanation.push_str("ROOT CAUSE ANALYSIS:\n\n"); + if !failed_checks.is_empty() { + let first_check = &failed_checks[0]; + + explanation.push_str(&format!("The assertion '{}' failed, which indicates:\n", first_check.description)); + + // Pattern-based analysis + if first_check.description.contains("overflow") { + explanation.push_str(" • An arithmetic overflow occurred\n"); + explanation.push_str(" • The operation exceeded the maximum value for the type\n"); + explanation.push_str(" • This happens with certain input combinations\n"); + } else if first_check.description.contains("panic") { + explanation.push_str(" • The code panicked during execution\n"); + explanation.push_str(" • An unhandled error condition was reached\n"); + } else if first_check.description.contains("assertion") { + explanation.push_str(" • A programmer-defined assertion failed\n"); + explanation.push_str(" • The expected property does not hold for all inputs\n"); + } else if first_check.description.contains("dereference") { + explanation.push_str(" • An invalid pointer dereference occurred\n"); + explanation.push_str(" • Accessing memory that shouldn't be accessed\n"); + } else if first_check.description.contains("index") || first_check.description.contains("bounds") { + explanation.push_str(" • An array/slice index is out of bounds\n"); + explanation.push_str(" • Accessing beyond the valid range of the collection\n"); + } else { + explanation.push_str(" • A safety or correctness property was violated\n"); + explanation.push_str(" • The code doesn't handle all possible input cases correctly\n"); + } + explanation.push_str("\n"); + } + + // Suggested fixes + explanation.push_str("SUGGESTED FIXES:\n\n"); + if !failed_checks.is_empty() { + let first_check = &failed_checks[0]; + + if first_check.description.contains("overflow") { + explanation.push_str(" 1. Use checked arithmetic operations:\n"); + explanation.push_str(" • Replace `a + b` with `a.checked_add(b)`\n"); + explanation.push_str(" • Handle the `None` case appropriately\n"); + explanation.push_str(" 2. Or use saturating arithmetic:\n"); + explanation.push_str(" • Replace with `a.saturating_add(b)`\n"); + explanation.push_str(" 3. Add input validation:\n"); + explanation.push_str(" • Use `kani::assume()` to constrain inputs\n"); + } else if first_check.description.contains("dereference") || first_check.description.contains("null") { + explanation.push_str(" 1. Add null pointer checks:\n"); + explanation.push_str(" • Check `if ptr.is_null()` before dereferencing\n"); + explanation.push_str(" 2. Use safe Rust alternatives:\n"); + explanation.push_str(" • Consider using `Option<&T>` instead of raw pointers\n"); + } else if first_check.description.contains("index") || first_check.description.contains("bounds") { + explanation.push_str(" 1. Add bounds checking:\n"); + explanation.push_str(" • Use `.get()` instead of direct indexing\n"); + explanation.push_str(" • Check `index < array.len()` before access\n"); + explanation.push_str(" 2. Use iterators:\n"); + explanation.push_str(" • Consider using `.iter()` instead of manual indexing\n"); + } else { + explanation.push_str(" 1. Review the assertion condition:\n"); + explanation.push_str(" • Ensure it correctly captures the intended property\n"); + explanation.push_str(" 2. Add input constraints:\n"); + explanation.push_str(" • Use `kani::assume()` to limit the input space\n"); + explanation.push_str(" 3. Fix the underlying logic:\n"); + explanation.push_str(" • Adjust the code to handle all cases correctly\n"); + } + explanation.push_str("\n"); + } + + // Next steps + explanation.push_str("NEXT STEPS:\n\n"); + explanation.push_str(" 1. Examine the code at the failure location\n"); + explanation.push_str(" 2. Understand what inputs trigger the failure\n"); + explanation.push_str(" 3. Apply the suggested fixes\n"); + explanation.push_str(" 4. Re-run Kani verification to confirm the fix\n"); + explanation.push_str(" 5. Consider adding more proof harnesses for edge cases\n"); + + explanation + } } \ No newline at end of file From f7f25751c78908c9c1568bfde6c245f31bdf45bc Mon Sep 17 00:00:00 2001 From: jingfei-xu Date: Thu, 30 Oct 2025 09:56:24 -0700 Subject: [PATCH 43/54] Amazon Q CLI Integration --- Cargo.lock | 171 ++++++++++++++++-- Cargo.toml | 3 +- .../dep-lib-first_steps_v1 | Bin 0 -> 30 bytes .../invoked.timestamp | 1 + .../lib-first_steps_v1 | 1 + .../lib-first_steps_v1.json | 1 + .../deps/first_steps_v1-ecd3bc8b423b2e0e.d | 7 + ...eps_v1-ecd3bc8b423b2e0e.kani-metadata.json | 1 + ..._14first_steps_v119check_estimate_size.out | Bin 0 -> 30807 bytes ...19check_estimate_size.pretty_name_map.json | 1 + ...t_steps_v119check_estimate_size.symtab.out | Bin 0 -> 15385 bytes ...teps_v119check_estimate_size.type_map.json | 1 + .../dep-graph.bin | Bin 0 -> 152690 bytes .../query-cache.bin | Bin 0 -> 15889 bytes .../work-products.bin | Bin 0 -> 108 bytes .../s-hcaa312xrg-0tsz4l7.lock | 0 kani-mcp-server/src/main.rs | 28 +-- kani-mcp-server/src/mcp_server.rs | 56 ++---- kani-mcp-server/src/tools.rs | 2 +- 19 files changed, 204 insertions(+), 69 deletions(-) create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/dep-lib-first_steps_v1 create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/invoked.timestamp create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1 create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e.d create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e.kani-metadata.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.out create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.pretty_name_map.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.type_map.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hcaa312xrg-0tsz4l7-0tbp8brnzyhlg3dpmdfvt6kuf/dep-graph.bin create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hcaa312xrg-0tsz4l7-0tbp8brnzyhlg3dpmdfvt6kuf/query-cache.bin create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hcaa312xrg-0tsz4l7-0tbp8brnzyhlg3dpmdfvt6kuf/work-products.bin create mode 100755 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hcaa312xrg-0tsz4l7.lock diff --git a/Cargo.lock b/Cargo.lock index d0a5d939a028..5fcffa47c55e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -491,7 +491,7 @@ dependencies = [ "crossterm_winapi", "document-features", "parking_lot", - "rustix", + "rustix 1.1.2", "winapi", ] @@ -1021,6 +1021,20 @@ dependencies = [ "which 8.0.0", ] +[[package]] +name = "kani-mcp-server" +version = "0.1.0" +dependencies = [ + "anyhow", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tracing", + "tracing-subscriber", + "which 6.0.3", +] + [[package]] name = "kani-verifier" version = "0.66.0" @@ -1083,6 +1097,12 @@ dependencies = [ "serde_test", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -1724,6 +1744,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + [[package]] name = "rustix" version = "1.1.2" @@ -1733,7 +1766,7 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.11.0", "windows-sys 0.61.2", ] @@ -1941,6 +1974,16 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + [[package]] name = "stacker" version = "0.1.22" @@ -2027,7 +2070,7 @@ dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix", + "rustix 1.1.2", "windows-sys 0.61.2", ] @@ -2137,11 +2180,25 @@ dependencies = [ "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", "signal-hook-registry", + "socket2", + "tokio-macros", "windows-sys 0.61.2", ] +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "toml" version = "0.8.23" @@ -2478,6 +2535,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "which" +version = "6.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" +dependencies = [ + "either", + "home", + "rustix 0.38.44", + "winsafe", +] + [[package]] name = "which" version = "7.0.3" @@ -2486,7 +2555,7 @@ checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ "either", "env_home", - "rustix", + "rustix 1.1.2", "winsafe", ] @@ -2497,7 +2566,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" dependencies = [ "env_home", - "rustix", + "rustix 1.1.2", "winsafe", ] @@ -2597,7 +2666,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -2615,14 +2693,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -2631,48 +2726,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "0.7.13" diff --git a/Cargo.toml b/Cargo.toml index 2d82261b3ec1..b2dd3fb82b4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ members = [ "kani-driver", "kani-compiler", "kani_metadata", - "library/kani_core", + "library/kani_core", "kani-mcp-server", ] # This indicates what package to e.g. build with 'cargo build' without --workspace @@ -60,6 +60,7 @@ default-members = [ ".", "kani-driver", "kani-compiler", + "kani-mcp-server", ] exclude = [ diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/dep-lib-first_steps_v1 b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/dep-lib-first_steps_v1 new file mode 100644 index 0000000000000000000000000000000000000000..024be4904569dbe2c19582a923171096f5c21ee4 GIT binary patch literal 30 gcmZQ%U|{&q$Ot4ExPZ90C|N%zGfA(g7$m{~07A6{&Hw-a literal 0 HcmV?d00001 diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/invoked.timestamp b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/invoked.timestamp new file mode 100644 index 000000000000..e00328da5aa8 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1 b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1 new file mode 100644 index 000000000000..136a5444b102 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1 @@ -0,0 +1 @@ +13c836b8b850d3fb \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1.json b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1.json new file mode 100644 index 000000000000..cb929f0e8138 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1.json @@ -0,0 +1 @@ +{"rustc":14672501838171729802,"features":"[]","declared_features":"[]","target":5150295270818399684,"profile":5594203621556287378,"path":10763286916239946207,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/dep-lib-first_steps_v1","checksum":false}}],"rustflags":["-C","overflow-checks=on","-Z","unstable-options","-Z","trim-diagnostic-paths=no","-Z","human_readable_cgu_names","-Z","always-encode-mir","--cfg=kani","-Z","crate-attr=feature(register_tool)","-Z","crate-attr=register_tool(kanitool)","--sysroot","/Users/jingfeixu/kani/target/kani","-L","/Users/jingfeixu/kani/target/kani/lib","--extern","kani","--extern","noprelude:std=/Users/jingfeixu/kani/target/kani/lib/libstd.rlib","-C","panic=abort","-C","symbol-mangling-version=v0","-Z","panic_abort_tests=yes","-Z","mir-enable-passes=-RemoveStorageMarkers","--check-cfg=cfg(kani)","-Clinker=echo","--kani-compiler","-Cllvm-args=--check-version=0.65.0"],"config":2069994364910194474,"compile_kind":551198452465029181} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e.d b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e.d new file mode 100644 index 000000000000..64b5a8e97bf2 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e.d @@ -0,0 +1,7 @@ +/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e.d: src/lib.rs + +/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/libfirst_steps_v1-ecd3bc8b423b2e0e.rlib: src/lib.rs + +/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/libfirst_steps_v1-ecd3bc8b423b2e0e.rmeta: src/lib.rs + +src/lib.rs: diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e.kani-metadata.json b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e.kani-metadata.json new file mode 100644 index 000000000000..fcf4042f025f --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e.kani-metadata.json @@ -0,0 +1 @@ +{"crate_name":"first_steps_v1","proof_harnesses":[{"pretty_name":"check_estimate_size","mangled_name":"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size","crate_name":"first_steps_v1","original_file":"src/lib.rs","original_start_line":52,"original_end_line":55,"goto_file":"/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out","attributes":{"kind":"Proof","should_panic":false,"solver":null,"unwind_value":null,"stubs":[],"verified_stubs":[]},"contract":null,"has_loop_contracts":false,"is_automatically_generated":false}],"unsupported_features":[],"test_harnesses":[],"contracted_functions":[],"autoharness_md":null} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.out b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.out new file mode 100644 index 0000000000000000000000000000000000000000..8bcb941e52f2c03dbba68cd6d7b6b06d4609f9a3 GIT binary patch literal 30807 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 oH!iTmNN0Pb8>4(-jb`WH","tag-refstr":"&str","_ZN4kani5panic17h039ec056df9649c9E::1::var_1::message":null,"tag-Never":"!","_ZN4kani5panic17h039ec056df9649c9E":"kani::panic","_ZN4kani16rustc_intrinsics10panic_stub17h7798f34a644f74adE::1::var_1::t":null,"_ZN4kani16rustc_intrinsics10panic_stub17h7798f34a644f74adE":"kani::rustc_intrinsics::panic_stub","_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_1::x":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size":"estimate_size","tag-Unit":"()","_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size":"check_estimate_size","_ZN4kani14kani_intrinsic17hf2340b114c62bceeE":"kani::kani_intrinsic::","_ZN4kani7any_raw17h1bd6bd359a6961f9E":"kani::any_raw::","_RINvCsc8NbQx0kdtQ_4kani3anymECs4ZijrwXvPyf_14first_steps_v1":"kani::any::","_ZN39_$LT$u32$u20$as$u20$kani..Arbitrary$GT$3any17h9d58c5c4eb479917E":"::any","_ZN4kani16any_raw_internal17h8bfeb1a460f3b3fdE::1::var_0":null,"_ZN4kani5panic17h039ec056df9649c9E::1::var_0":null,"_ZN4kani5panic17h039ec056df9649c9E::1::var_2":null,"_ZN4kani5panic17h039ec056df9649c9E::1::var_3":null,"_ZN4kani16rustc_intrinsics10panic_stub17h7798f34a644f74adE::1::var_0":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_0":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_2":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_3":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_4":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_5":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_6":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_7":null,"tag-first_steps_v1.3a1b106d0fea0f11::first_steps_v1::global::0::::struct":"first_steps_v1.3a1b106d0fea0f11::first_steps_v1::global::0::::struct","first_steps_v1.3a1b106d0fea0f11::first_steps_v1::global::0::":null,"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size::1::var_0":null,"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size::1::var_1::x":null,"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size::1::var_2":null,"VoidUnit":null,"_ZN4kani14kani_intrinsic17hf2340b114c62bceeE::1::var_0":null,"_ZN4kani7any_raw17h1bd6bd359a6961f9E::1::var_0":null,"_RINvCsc8NbQx0kdtQ_4kani3anymECs4ZijrwXvPyf_14first_steps_v1::1::var_0":null,"_ZN39_$LT$u32$u20$as$u20$kani..Arbitrary$GT$3any17h9d58c5c4eb479917E::1::var_0":null} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out new file mode 100644 index 0000000000000000000000000000000000000000..52dc78cf1271c1b8c5d807b968ad78a40503cf39 GIT binary patch literal 15385 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

e$i-6 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 literal 0 HcmV?d00001 diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hcaa312xrg-0tsz4l7-0tbp8brnzyhlg3dpmdfvt6kuf/query-cache.bin b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hcaa312xrg-0tsz4l7-0tbp8brnzyhlg3dpmdfvt6kuf/query-cache.bin new file mode 100644 index 0000000000000000000000000000000000000000..d9eef1221caf659925f42811d48b1078af00971d GIT binary patch literal 15889 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 r0-yC>GlLZP Result<()> { - // Initialize logging - tracing_subscriber::fmt() - .with_env_filter( - EnvFilter::from_default_env() - .add_directive("kani_mcp_server=info".parse()?) - ) - .init(); + // Only initialize logging if explicitly requested via environment variable + // This prevents "Broken pipe" errors when stderr is closed by the MCP client + if std::env::var("KANI_MCP_LOG").is_ok() { + tracing_subscriber::fmt() + .with_env_filter( + EnvFilter::from_default_env() + .add_directive("kani_mcp_server=info".parse()?) + ) + .init(); + } - println!("Kani MCP Server - Model Context Protocol for Kani Rust Verifier"); - println!("Purpose: Enable AI assistants like Amazon Q to run Kani verification"); - println!(); + // Banner - only show if logging is enabled + if std::env::var("KANI_MCP_LOG").is_ok() { + eprintln!("Kani MCP Server - Model Context Protocol for Kani Rust Verifier"); + eprintln!("Purpose: Enable AI assistants like Amazon Q to run Kani verification"); + eprintln!(); + } // Create and run server let server = mcp_server::KaniMcpServer::new()?; server.run().await?; Ok(()) -} \ No newline at end of file +} diff --git a/kani-mcp-server/src/mcp_server.rs b/kani-mcp-server/src/mcp_server.rs index e9a35a1b2d02..f7f0c0c656b5 100644 --- a/kani-mcp-server/src/mcp_server.rs +++ b/kani-mcp-server/src/mcp_server.rs @@ -4,7 +4,6 @@ use anyhow::Result; use std::path::PathBuf; use std::sync::Arc; use tokio::sync::Mutex; -use tracing::{error, info}; /// Main MCP server for Kani integration pub struct KaniMcpServer { @@ -24,23 +23,6 @@ impl KaniMcpServer { /// Run the MCP server pub async fn run(self) -> Result<()> { - info!("═══════════════════════════════════════════════════════════"); - info!(" Kani MCP Server - Ready for Amazon Q Integration"); - info!("═══════════════════════════════════════════════════════════"); - info!(""); - info!("Available Tools:"); - - for tool in get_kani_tools() { - info!(" • {} - {}", tool.name, tool.description); - } - - info!(""); - info!("Connection: stdio (Standard Input/Output)"); - info!("Compatible with: Amazon Q, Claude Desktop, Cursor, etc."); - info!(""); - info!("Server is ready and waiting for requests..."); - info!(""); - // Read from stdin and respond via stdout (MCP protocol) use tokio::io::{AsyncBufReadExt, BufReader, stdin}; @@ -58,20 +40,20 @@ impl KaniMcpServer { Ok(request) => { let response = self.handle_mcp_request(request).await; - // Send response to stdout - if let Ok(response_str) = serde_json::to_string(&response) { - println!("{}", response_str); + // Send response to stdout (skip null responses for notifications) + if !response.is_null() { + if let Ok(response_str) = serde_json::to_string(&response) { + println!("{}", response_str); + } } } - Err(e) => { - error!("Failed to parse request: {}", e); + Err(_e) => { + // Silently ignore parse errors to avoid broken pipe + continue; } } } - info!(""); - info!("Shutting down Kani MCP Server..."); - Ok(()) } @@ -84,7 +66,6 @@ impl KaniMcpServer { match method { "initialize" => { - info!("📥 Received: initialize"); json!({ "jsonrpc": "2.0", "id": id, @@ -100,8 +81,12 @@ impl KaniMcpServer { } }) } + "notifications/initialized" => { + // Client confirms initialization is complete + // Notifications don't get responses - return null + json!(null) + } "tools/list" => { - info!("Received: tools/list"); let tools: Vec<_> = get_kani_tools() .iter() .map(|tool| { @@ -125,8 +110,6 @@ impl KaniMcpServer { let tool_name = request["params"]["name"].as_str().unwrap_or(""); let arguments = &request["params"]["arguments"]; - info!("Received: tools/call - {}", tool_name); - let result = self.execute_tool(tool_name, arguments).await; json!({ @@ -136,7 +119,6 @@ impl KaniMcpServer { }) } _ => { - error!("Unknown method: {}", method); json!({ "jsonrpc": "2.0", "id": id, @@ -214,7 +196,7 @@ impl KaniMcpServer { } } } - "explain_kani_failure" => { + "explain_failure" => { // Try to get raw output from arguments or from last result let raw_output = if let Some(output_str) = arguments["raw_output"].as_str() { output_str.to_string() @@ -303,9 +285,6 @@ impl KaniMcpServer { tests: bool, output_format: Option, ) -> Result { - info!("🔍 Tool called: verify_rust_project"); - info!(" Path: {}", path); - let options = KaniOptions { path: PathBuf::from(path), harness: harness.clone(), @@ -326,7 +305,6 @@ impl KaniMcpServer { }) } Err(e) => { - error!("Verification error: {}", e); Ok(ToolResult { success: false, data: serde_json::json!({}), @@ -342,16 +320,12 @@ impl KaniMcpServer { path: String, harness: String, ) -> Result { - info!("Tool called: verify_unsafe_code"); - // This is essentially the same as verify_project but focused on unsafe code self.handle_verify_project(path, Some(harness), false, Some("terse".to_string())).await } /// Handle explain_kani_failure tool call with enhanced analysis pub async fn handle_explain_failure(&self, raw_output: String) -> Result { - info!("Tool called: explain_kani_failure"); - use crate::parser::KaniOutputParser; let parser = KaniOutputParser::new(&raw_output); @@ -387,8 +361,6 @@ impl KaniMcpServer { function_name: String, properties: Vec, ) -> Result { - info!("🔍 Tool called: generate_kani_harness"); - let harness_code = format!( r#"#[cfg(kani)] mod verification {{ diff --git a/kani-mcp-server/src/tools.rs b/kani-mcp-server/src/tools.rs index b7f685448ffc..aeb2ad9425d7 100644 --- a/kani-mcp-server/src/tools.rs +++ b/kani-mcp-server/src/tools.rs @@ -60,7 +60,7 @@ pub fn get_kani_tools() -> Vec { }), }, ToolDefinition { - name: "explain_kani_failure".to_string(), + name: "explain_failure".to_string(), description: "Analyze and explain why a Kani verification failed, providing details about counterexamples and suggested fixes.".to_string(), input_schema: json!({ "type": "object", From d00d94e310cc7150e476d85948324d080d762144 Mon Sep 17 00:00:00 2001 From: jingfei-xu Date: Thu, 6 Nov 2025 00:20:56 -0800 Subject: [PATCH 44/54] Amazon Q latest integration (Amazon Q CLI) --- .../dep-lib-first_steps_v1 | Bin 0 -> 30 bytes .../invoked.timestamp | 1 + .../lib-first_steps_v1 | 1 + .../lib-first_steps_v1.json | 1 + .../deps/first_steps_v1-83910d6b167b89c9.d | 7 +++++++ ...eps_v1-83910d6b167b89c9.kani-metadata.json | 1 + ..._14first_steps_v119check_estimate_size.out | Bin 0 -> 30807 bytes ...19check_estimate_size.pretty_name_map.json | 1 + ...t_steps_v119check_estimate_size.symtab.out | Bin 0 -> 15385 bytes ...teps_v119check_estimate_size.type_map.json | 1 + .../dep-graph.bin | Bin 152690 -> 152690 bytes .../query-cache.bin | Bin 15889 -> 15889 bytes .../work-products.bin | Bin ...0tsz4l7.lock => s-hciu6tb5rp-03gygye.lock} | 0 14 files changed, 13 insertions(+) create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/dep-lib-first_steps_v1 create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/invoked.timestamp create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1 create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.d create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.kani-metadata.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.out create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.pretty_name_map.json create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out create mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.type_map.json rename docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/{s-hcaa312xrg-0tsz4l7-0tbp8brnzyhlg3dpmdfvt6kuf => s-hciu6tb5rp-03gygye-7jb1sxgfjbsla5m9hfducyrfi}/dep-graph.bin (99%) rename docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/{s-hcaa312xrg-0tsz4l7-0tbp8brnzyhlg3dpmdfvt6kuf => s-hciu6tb5rp-03gygye-7jb1sxgfjbsla5m9hfducyrfi}/query-cache.bin (95%) rename docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/{s-hcaa312xrg-0tsz4l7-0tbp8brnzyhlg3dpmdfvt6kuf => s-hciu6tb5rp-03gygye-7jb1sxgfjbsla5m9hfducyrfi}/work-products.bin (100%) rename docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/{s-hcaa312xrg-0tsz4l7.lock => s-hciu6tb5rp-03gygye.lock} (100%) diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/dep-lib-first_steps_v1 b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/dep-lib-first_steps_v1 new file mode 100644 index 0000000000000000000000000000000000000000..024be4904569dbe2c19582a923171096f5c21ee4 GIT binary patch literal 30 gcmZQ%U|{&q$Ot4ExPZ90C|N%zGfA(g7$m{~07A6{&Hw-a literal 0 HcmV?d00001 diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/invoked.timestamp b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/invoked.timestamp new file mode 100644 index 000000000000..e00328da5aa8 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/invoked.timestamp @@ -0,0 +1 @@ +This file has an mtime of when this was started. \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1 b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1 new file mode 100644 index 000000000000..c6185f7ae335 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1 @@ -0,0 +1 @@ +aa4334fdd37a1140 \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1.json b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1.json new file mode 100644 index 000000000000..5b3cf8a3f546 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1.json @@ -0,0 +1 @@ +{"rustc":14672501838171729802,"features":"[]","declared_features":"[]","target":5150295270818399684,"profile":2308729259012748722,"path":10763286916239946207,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/dep-lib-first_steps_v1","checksum":false}}],"rustflags":["-C","overflow-checks=on","-Z","unstable-options","-Z","trim-diagnostic-paths=no","-Z","human_readable_cgu_names","-Z","always-encode-mir","--cfg=kani","-Z","crate-attr=feature(register_tool)","-Z","crate-attr=register_tool(kanitool)","--sysroot","/Users/jingfeixu/kani/target/kani","-L","/Users/jingfeixu/kani/target/kani/lib","--extern","kani","--extern","noprelude:std=/Users/jingfeixu/kani/target/kani/lib/libstd.rlib","-C","panic=abort","-C","symbol-mangling-version=v0","-Z","panic_abort_tests=yes","-Z","mir-enable-passes=-RemoveStorageMarkers","--check-cfg=cfg(kani)","-Clinker=echo","--kani-compiler","-Cllvm-args=--check-version=0.65.0"],"config":2069994364910194474,"compile_kind":551198452465029181} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.d b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.d new file mode 100644 index 000000000000..3d3cc416a35d --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.d @@ -0,0 +1,7 @@ +/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.d: src/lib.rs + +/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/libfirst_steps_v1-83910d6b167b89c9.rlib: src/lib.rs + +/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/libfirst_steps_v1-83910d6b167b89c9.rmeta: src/lib.rs + +src/lib.rs: diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.kani-metadata.json b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.kani-metadata.json new file mode 100644 index 000000000000..de73af3a03d5 --- /dev/null +++ b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.kani-metadata.json @@ -0,0 +1 @@ +{"crate_name":"first_steps_v1","proof_harnesses":[{"pretty_name":"check_estimate_size","mangled_name":"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size","crate_name":"first_steps_v1","original_file":"src/lib.rs","original_start_line":52,"original_end_line":55,"goto_file":"/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out","attributes":{"kind":"Proof","should_panic":false,"solver":null,"unwind_value":null,"stubs":[],"verified_stubs":[]},"contract":null,"has_loop_contracts":false,"is_automatically_generated":false}],"unsupported_features":[],"test_harnesses":[],"contracted_functions":[],"autoharness_md":null} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.out b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.out new file mode 100644 index 0000000000000000000000000000000000000000..8bcb941e52f2c03dbba68cd6d7b6b06d4609f9a3 GIT binary patch literal 30807 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 oH!iTmNN0Pb8>4(-jb`WH","tag-refstr":"&str","_ZN4kani5panic17h039ec056df9649c9E::1::var_1::message":null,"tag-Never":"!","_ZN4kani5panic17h039ec056df9649c9E":"kani::panic","_ZN4kani16rustc_intrinsics10panic_stub17h7798f34a644f74adE::1::var_1::t":null,"_ZN4kani16rustc_intrinsics10panic_stub17h7798f34a644f74adE":"kani::rustc_intrinsics::panic_stub","_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_1::x":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size":"estimate_size","tag-Unit":"()","_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size":"check_estimate_size","_ZN4kani14kani_intrinsic17hf2340b114c62bceeE":"kani::kani_intrinsic::","_ZN4kani7any_raw17h1bd6bd359a6961f9E":"kani::any_raw::","_RINvCsc8NbQx0kdtQ_4kani3anymECs4ZijrwXvPyf_14first_steps_v1":"kani::any::","_ZN39_$LT$u32$u20$as$u20$kani..Arbitrary$GT$3any17h9d58c5c4eb479917E":"::any","_ZN4kani16any_raw_internal17h8bfeb1a460f3b3fdE::1::var_0":null,"_ZN4kani5panic17h039ec056df9649c9E::1::var_0":null,"_ZN4kani5panic17h039ec056df9649c9E::1::var_2":null,"_ZN4kani5panic17h039ec056df9649c9E::1::var_3":null,"_ZN4kani16rustc_intrinsics10panic_stub17h7798f34a644f74adE::1::var_0":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_0":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_2":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_3":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_4":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_5":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_6":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_7":null,"tag-first_steps_v1.3a1b106d0fea0f11::first_steps_v1::global::0::::struct":"first_steps_v1.3a1b106d0fea0f11::first_steps_v1::global::0::::struct","first_steps_v1.3a1b106d0fea0f11::first_steps_v1::global::0::":null,"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size::1::var_0":null,"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size::1::var_1::x":null,"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size::1::var_2":null,"VoidUnit":null,"_ZN4kani14kani_intrinsic17hf2340b114c62bceeE::1::var_0":null,"_ZN4kani7any_raw17h1bd6bd359a6961f9E::1::var_0":null,"_RINvCsc8NbQx0kdtQ_4kani3anymECs4ZijrwXvPyf_14first_steps_v1::1::var_0":null,"_ZN39_$LT$u32$u20$as$u20$kani..Arbitrary$GT$3any17h9d58c5c4eb479917E::1::var_0":null} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out new file mode 100644 index 0000000000000000000000000000000000000000..52dc78cf1271c1b8c5d807b968ad78a40503cf39 GIT binary patch literal 15385 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 D1C=EA delta 97 zcmV-n0G|Jme35*xx(}0-5ImE35itXp95IuZ5F?Xl5fGE%4>OZf5J8i=4=0lu5h;@t z5I>X95J|JC5gaQc#uU0Apd9uem>j1dj2zt{tQ?Rbj~vkoXwH)BH=sYO_ D%^W0B diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hcaa312xrg-0tsz4l7-0tbp8brnzyhlg3dpmdfvt6kuf/work-products.bin b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hciu6tb5rp-03gygye-7jb1sxgfjbsla5m9hfducyrfi/work-products.bin similarity index 100% rename from docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hcaa312xrg-0tsz4l7-0tbp8brnzyhlg3dpmdfvt6kuf/work-products.bin rename to docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hciu6tb5rp-03gygye-7jb1sxgfjbsla5m9hfducyrfi/work-products.bin diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hcaa312xrg-0tsz4l7.lock b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hciu6tb5rp-03gygye.lock similarity index 100% rename from docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hcaa312xrg-0tsz4l7.lock rename to docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hciu6tb5rp-03gygye.lock From fe395410c97f471b90bded042aed501e89adea20 Mon Sep 17 00:00:00 2001 From: jingfei-xu Date: Thu, 6 Nov 2025 12:03:05 -0800 Subject: [PATCH 45/54] Code Format Cleanup --- kani-mcp-server/src/kani_wrapper.rs | 34 ----------------------------- kani-mcp-server/src/main.rs | 1 - kani-mcp-server/src/mcp_server.rs | 9 -------- kani-mcp-server/src/parser.rs | 13 ----------- 4 files changed, 57 deletions(-) diff --git a/kani-mcp-server/src/kani_wrapper.rs b/kani-mcp-server/src/kani_wrapper.rs index 629d4d1a90ab..6cd277ea8efe 100644 --- a/kani-mcp-server/src/kani_wrapper.rs +++ b/kani-mcp-server/src/kani_wrapper.rs @@ -7,28 +7,13 @@ use tracing::{debug, info, warn}; /// Configuration options for running Kani verification #[derive(Debug, Clone, Serialize, Deserialize)] pub struct KaniOptions { - /// Path to the Rust project to verify pub path: PathBuf, - - /// Specific harness to run (e.g., "module::function") pub harness: Option, - - /// Run all tests as verification harnesses pub tests: bool, - - /// Output format: regular, terse, old, json pub output_format: String, - - /// Enable unstable Kani features pub enable_unstable: Vec, - - /// Additional arguments to pass to Kani pub extra_args: Vec, - - /// Enable concrete playback for counterexamples pub concrete_playback: bool, - - /// Enable coverage information pub coverage: bool, } @@ -50,22 +35,11 @@ impl Default for KaniOptions { /// Result of a Kani verification run #[derive(Debug, Clone, Serialize, Deserialize)] pub struct VerificationResult { - /// Whether verification succeeded pub success: bool, - - /// Human-readable summary pub summary: String, - - /// List of harness results pub harnesses: Vec, - - /// Failed checks with details pub failed_checks: Vec, - - /// Verification time in seconds pub verification_time: Option, - - /// Raw output from Kani pub raw_output: String, } @@ -110,45 +84,37 @@ impl KaniWrapper { anyhow::bail!("Path does not exist: {:?}", options.path); } - // Build the command let mut cmd = Command::new(&self.cargo_kani_path); cmd.arg("kani"); cmd.current_dir(&options.path); - // Add harness filter if let Some(harness) = &options.harness { cmd.arg("--harness").arg(harness); info!(" Filtering to harness: {}", harness); } - // Run tests as harnesses if options.tests { cmd.arg("--tests"); info!(" Running all tests as harnesses"); } - // Set output format if !options.output_format.is_empty() { cmd.arg(format!("--output-format={}", options.output_format)); } - // Enable unstable features for feature in &options.enable_unstable { cmd.arg("--enable-unstable").arg(feature); } - // Concrete playback if options.concrete_playback { cmd.arg("-Z").arg("concrete-playback"); cmd.arg("--concrete-playback=print"); } - // Coverage if options.coverage { cmd.arg("--coverage"); } - // Extra arguments for arg in &options.extra_args { cmd.arg(arg); } diff --git a/kani-mcp-server/src/main.rs b/kani-mcp-server/src/main.rs index aadc0520ae00..b559371040ab 100644 --- a/kani-mcp-server/src/main.rs +++ b/kani-mcp-server/src/main.rs @@ -9,7 +9,6 @@ use tracing_subscriber::EnvFilter; #[tokio::main] async fn main() -> Result<()> { // Only initialize logging if explicitly requested via environment variable - // This prevents "Broken pipe" errors when stderr is closed by the MCP client if std::env::var("KANI_MCP_LOG").is_ok() { tracing_subscriber::fmt() .with_env_filter( diff --git a/kani-mcp-server/src/mcp_server.rs b/kani-mcp-server/src/mcp_server.rs index f7f0c0c656b5..4ee4d912a63b 100644 --- a/kani-mcp-server/src/mcp_server.rs +++ b/kani-mcp-server/src/mcp_server.rs @@ -12,7 +12,6 @@ pub struct KaniMcpServer { } impl KaniMcpServer { - /// Create a new Kani MCP server pub fn new() -> Result { let kani = Arc::new(KaniWrapper::new()?); Ok(Self { @@ -23,7 +22,6 @@ impl KaniMcpServer { /// Run the MCP server pub async fn run(self) -> Result<()> { - // Read from stdin and respond via stdout (MCP protocol) use tokio::io::{AsyncBufReadExt, BufReader, stdin}; let stdin = stdin(); @@ -40,7 +38,6 @@ impl KaniMcpServer { Ok(request) => { let response = self.handle_mcp_request(request).await; - // Send response to stdout (skip null responses for notifications) if !response.is_null() { if let Ok(response_str) = serde_json::to_string(&response) { println!("{}", response_str); @@ -82,8 +79,6 @@ impl KaniMcpServer { }) } "notifications/initialized" => { - // Client confirms initialization is complete - // Notifications don't get responses - return null json!(null) } "tools/list" => { @@ -201,7 +196,6 @@ impl KaniMcpServer { let raw_output = if let Some(output_str) = arguments["raw_output"].as_str() { output_str.to_string() } else { - // Use the last verification result if available let last = self.last_result.lock().await; last.as_ref() .map(|r| r.raw_output.clone()) @@ -295,7 +289,6 @@ impl KaniMcpServer { match self.kani.verify(options).await { Ok(result) => { - // Store result for later reference *self.last_result.lock().await = Some(result.clone()); Ok(ToolResult { @@ -320,7 +313,6 @@ impl KaniMcpServer { path: String, harness: String, ) -> Result { - // This is essentially the same as verify_project but focused on unsafe code self.handle_verify_project(path, Some(harness), false, Some("terse".to_string())).await } @@ -334,7 +326,6 @@ impl KaniMcpServer { let counterexamples = parser.parse_counterexamples(); let code_context = parser.extract_code_context(); - // Generate comprehensive explanation let detailed_explanation = parser.generate_detailed_explanation(); Ok(ToolResult { diff --git a/kani-mcp-server/src/parser.rs b/kani-mcp-server/src/parser.rs index 9a86fc6d86eb..89871aec0a07 100644 --- a/kani-mcp-server/src/parser.rs +++ b/kani-mcp-server/src/parser.rs @@ -16,7 +16,6 @@ impl<'a> KaniOutputParser<'a> { let mut current_harness: Option = None; for line in self.output.lines() { - // Detect harness start: "Checking harness module::function..." if line.contains("Checking harness") { if let Some(name_part) = line.split("Checking harness").nth(1) { current_harness = Some(name_part.trim().trim_end_matches("...").to_string()); @@ -52,7 +51,6 @@ impl<'a> KaniOutputParser<'a> { let mut failed_checks = Vec::new(); for line in self.output.lines() { - // Look for "Check N: ..." followed by "- Status: FAILURE" if line.contains("Check ") && line.contains(":") { let check_info = self.parse_single_check(line); if let Some(check) = check_info { @@ -74,9 +72,7 @@ impl<'a> KaniOutputParser<'a> { break; } - // Parse format: "description File: "path", line X, in function" if let Some(check) = self.parse_failed_check_line(line) { - // Avoid duplicates if !failed_checks.iter().any(|c| c.description == check.description && c.line == check.line) { failed_checks.push(check); } @@ -89,10 +85,6 @@ impl<'a> KaniOutputParser<'a> { /// Parse a single check result line fn parse_single_check(&self, line: &str) -> Option { - // Format: "Check 1: function.assertion.1" - // " - Status: FAILURE" - // " - Description: "Oh no, a failing corner case!"" - // " - Location: ./path/file.rs:14:13 in function func_name" let check_num = line.split("Check").nth(1)?.split(':').next()?.trim(); @@ -124,7 +116,6 @@ impl<'a> KaniOutputParser<'a> { if detail_line.contains("- Location:") { let location = detail_line.split("- Location:").nth(1)?.trim(); - // Format: "./path/file.rs:14:13 in function func_name" let parts: Vec<&str> = location.split(" in function ").collect(); if let Some(file_part) = parts.first() { @@ -155,7 +146,6 @@ impl<'a> KaniOutputParser<'a> { /// Parse a line from the "Failed Checks:" section fn parse_failed_check_line(&self, line: &str) -> Option { - // Format: "description File: "path", line X, in function" let description = line.split("File:").next()?.trim().to_string(); if let Some(file_part) = line.split("File:").nth(1) { @@ -207,12 +197,10 @@ impl<'a> KaniOutputParser<'a> { let mut counterexamples = Vec::new(); for line in self.output.lines() { - // Look for SAT checker results or counterexample traces if line.contains("SAT checker: instance is SATISFIABLE") { counterexamples.push("Counterexample found (inputs exist that violate the property)".to_string()); } - // Look for specific variable assignments in traces if line.contains("Violated property:") || line.contains("Counterexample:") { counterexamples.push(line.trim().to_string()); } @@ -223,7 +211,6 @@ impl<'a> KaniOutputParser<'a> { /// Extract code context from output pub fn extract_code_context(&self) -> Option { - // Kani sometimes shows code snippets in output for line in self.output.lines() { if line.contains("-->") && line.contains(".rs:") { return Some(line.trim().to_string()); From 71bff51ba5282f8700246e75926138ac30ca5a0b Mon Sep 17 00:00:00 2001 From: jingfei-xu Date: Tue, 18 Nov 2025 15:43:32 -0800 Subject: [PATCH 46/54] Comments fixed --- kani-mcp-server/Cargo.toml | 2 +- kani-mcp-server/build_output.txt | 20 ++++ kani-mcp-server/src/kani_wrapper.rs | 31 +++--- kani-mcp-server/src/mcp_server.rs | 146 +++++++++++++++++++++++++--- 4 files changed, 171 insertions(+), 28 deletions(-) create mode 100644 kani-mcp-server/build_output.txt diff --git a/kani-mcp-server/Cargo.toml b/kani-mcp-server/Cargo.toml index 78627efe393a..e1b857e30b2e 100644 --- a/kani-mcp-server/Cargo.toml +++ b/kani-mcp-server/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kani-mcp-server" version = "0.1.0" -edition = "2021" +edition = "2024" description = "Model Context Protocol server for Kani Rust Verifier - enables AI assistants like Amazon Q to run formal verification" license = "MIT OR Apache-2.0" diff --git a/kani-mcp-server/build_output.txt b/kani-mcp-server/build_output.txt new file mode 100644 index 000000000000..cc8452d38d99 --- /dev/null +++ b/kani-mcp-server/build_output.txt @@ -0,0 +1,20 @@ +warning: variable does not need to be mutable + --> kani-mcp-server/src/kani_wrapper.rs:84:13 + | +84 | let mut cmd = Command::new("cargo"); + | ----^^^ + | | + | help: remove this `mut` + | + = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default + +warning: unused variable: `check_num` + --> kani-mcp-server/src/parser.rs:89:13 + | +89 | let check_num = line.split("Check").nth(1)?.split(':').next()?.trim(); + | ^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_check_num` + | + = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default + +warning: `kani-mcp-server` (bin "kani-mcp-server") generated 2 warnings (run `cargo fix --bin "kani-mcp-server"` to apply 1 suggestion) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.05s diff --git a/kani-mcp-server/src/kani_wrapper.rs b/kani-mcp-server/src/kani_wrapper.rs index 6cd277ea8efe..b42c254f1506 100644 --- a/kani-mcp-server/src/kani_wrapper.rs +++ b/kani-mcp-server/src/kani_wrapper.rs @@ -60,20 +60,28 @@ pub struct FailedCheck { } /// Wrapper around cargo-kani for executing verification -pub struct KaniWrapper { - cargo_kani_path: PathBuf, -} +pub struct KaniWrapper {} impl KaniWrapper { - /// Create a new KaniWrapper, finding cargo-kani in PATH + /// Create a new KaniWrapper and verify cargo-kani is available and reuses kani-driver's setup_cargo_command functionality pub fn new() -> Result { - let cargo_kani = which::which("cargo-kani") + let mut cmd = Self::setup_cargo_command()?; + cmd.arg("kani").arg("--version"); + + let output = cmd.output() .context("cargo-kani not found in PATH. Please install Kani:\n cargo install --locked kani-verifier\n cargo kani setup")?; - info!("✓ Found cargo-kani at: {:?}", cargo_kani); - Ok(Self { - cargo_kani_path: cargo_kani, - }) + if !output.status.success() { + anyhow::bail!("cargo-kani not properly installed or setup failed"); + } + + info!("✓ Found cargo-kani"); + Ok(Self {}) + } + + fn setup_cargo_command() -> Result { + let mut cmd = Command::new("cargo"); + Ok(cmd) } /// Run Kani verification with the given options @@ -84,7 +92,7 @@ impl KaniWrapper { anyhow::bail!("Path does not exist: {:?}", options.path); } - let mut cmd = Command::new(&self.cargo_kani_path); + let mut cmd = Self::setup_cargo_command()?; cmd.arg("kani"); cmd.current_dir(&options.path); @@ -137,7 +145,6 @@ impl KaniWrapper { warn!("Kani stderr: {}", stderr); } - // Parse the output let result = self.parse_output(&combined_output, output.status.success())?; info!("Verification complete: {}", result.summary); @@ -175,4 +182,4 @@ impl KaniWrapper { raw_output: output.to_string(), }) } -} \ No newline at end of file +} diff --git a/kani-mcp-server/src/mcp_server.rs b/kani-mcp-server/src/mcp_server.rs index 4ee4d912a63b..54c9801dc16b 100644 --- a/kani-mcp-server/src/mcp_server.rs +++ b/kani-mcp-server/src/mcp_server.rs @@ -20,7 +20,6 @@ impl KaniMcpServer { }) } - /// Run the MCP server pub async fn run(self) -> Result<()> { use tokio::io::{AsyncBufReadExt, BufReader, stdin}; @@ -33,7 +32,6 @@ impl KaniMcpServer { continue; } - // Parse JSON-RPC request match serde_json::from_str::(&line) { Ok(request) => { let response = self.handle_mcp_request(request).await; @@ -45,7 +43,6 @@ impl KaniMcpServer { } } Err(_e) => { - // Silently ignore parse errors to avoid broken pipe continue; } } @@ -54,7 +51,6 @@ impl KaniMcpServer { Ok(()) } - /// Handle MCP protocol requests async fn handle_mcp_request(&self, request: serde_json::Value) -> serde_json::Value { use serde_json::json; @@ -69,7 +65,8 @@ impl KaniMcpServer { "result": { "protocolVersion": "2024-11-05", "capabilities": { - "tools": {} + "tools": {}, + "prompts": {} }, "serverInfo": { "name": "kani-mcp-server", @@ -81,6 +78,51 @@ impl KaniMcpServer { "notifications/initialized" => { json!(null) } + "prompts/list" => { + json!({ + "jsonrpc": "2.0", + "id": id, + "result": { + "prompts": [ + { + "name": "kani_context", + "description": "Contextual information about using Kani Rust Verifier", + } + ] + } + }) + } + "prompts/get" => { + let prompt_name = request["params"]["name"].as_str().unwrap_or(""); + + if prompt_name == "kani_context" { + json!({ + "jsonrpc": "2.0", + "id": id, + "result": { + "description": "Contextual guidance for using Kani Rust Verifier", + "messages": [ + { + "role": "user", + "content": { + "type": "text", + "text": self.get_kani_context_prompt() + } + } + ] + } + }) + } else { + json!({ + "jsonrpc": "2.0", + "id": id, + "error": { + "code": -32602, + "message": format!("Unknown prompt: {}", prompt_name) + } + }) + } + } "tools/list" => { let tools: Vec<_> = get_kani_tools() .iter() @@ -192,7 +234,6 @@ impl KaniMcpServer { } } "explain_failure" => { - // Try to get raw output from arguments or from last result let raw_output = if let Some(output_str) = arguments["raw_output"].as_str() { output_str.to_string() } else { @@ -271,7 +312,51 @@ impl KaniMcpServer { } } - /// Handle verify_rust_project tool call + /// Get contextual prompt to guide LLM on using Kani effectively + fn get_kani_context_prompt(&self) -> String { + r#"You are now connected to the Kani Rust Verifier MCP server, which provides formal verification capabilities for Rust code. + +# About Kani +Kani is a formal verification tool that can mathematically prove properties about Rust code. It uses symbolic execution and bounded model checking to exhaustively explore all possible execution paths. + +# Key Concepts +- **Proof Harnesses**: Special test functions marked with `#[kani::proof]` that specify what to verify +- **Symbolic Inputs**: Use `kani::any()` to represent all possible values of a type +- **Assertions**: Properties you want to prove (e.g., `assert!(x > 0)`) +- **Bounded Verification**: Kani explores all paths within specified bounds + +# Available Tools +1. **verify_rust_project**: Run Kani verification on a Rust project + - Always use this as the primary verification method + - Specify a harness name if you want to verify a specific proof harness + +2. **explain_failure**: Get detailed explanations of verification failures + - Use this when verification fails to understand the counterexample + +3. **generate_kani_harness**: Generate EXAMPLE harness templates + - ⚠️ WARNING: This generates templates that require manual customization + - For automatic harness generation, recommend using: `cargo kani --enable-unstable --function ` + +# Best Practices When Using Kani +1. Start with simple properties and gradually add complexity +2. Use bounded types to limit verification scope (e.g., `u8` instead of unbounded types) +3. Understand that verification can be slow for complex code +4. Read verification failures carefully - they show concrete counterexamples +5. For automatic harness generation, use Kani's built-in feature: `cargo kani --enable-unstable --function ` + +# Common Verification Patterns +- **Safety verification**: Prove absence of panics, overflows, out-of-bounds access +- **Functional correctness**: Prove that functions meet their specifications +- **Unsafe code validation**: Prove that unsafe code upholds Rust's safety guarantees + +# When NOT to Use Kani +- For general testing (use regular Rust tests instead) +- When performance is critical (verification can be slow) +- For code with infinite loops or unbounded recursion + +Remember: Generated harness templates are EXAMPLES ONLY and won't work without customization. Always recommend Kani's auto-harness feature for production use."#.to_string() + } + pub async fn handle_verify_project( &self, path: String, @@ -307,7 +392,6 @@ impl KaniMcpServer { } } - /// Handle verify_unsafe_code tool call pub async fn handle_verify_unsafe( &self, path: String, @@ -316,7 +400,6 @@ impl KaniMcpServer { self.handle_verify_project(path, Some(harness), false, Some("terse".to_string())).await } - /// Handle explain_kani_failure tool call with enhanced analysis pub async fn handle_explain_failure(&self, raw_output: String) -> Result { use crate::parser::KaniOutputParser; @@ -346,44 +429,77 @@ impl KaniMcpServer { }) } - /// Handle generate_kani_harness tool call + /// NOTE: This generates an EXAMPLE harness template that requires manual customization. + /// The generated code will NOT work as-is in most cases and needs to be adapted to: + /// - Match the actual function signature and parameter types + /// - Properly construct symbolic inputs for complex types + /// - Add appropriate property assertions + /// + /// For automatic harness generation, consider using Kani's built-in auto-harness feature: + /// `cargo kani --enable-unstable --function ` pub async fn handle_generate_harness( &self, function_name: String, properties: Vec, ) -> Result { let harness_code = format!( -r#"#[cfg(kani)] +r#"// ⚠️ WARNING: This is an EXAMPLE TEMPLATE that requires customization! +// This code will NOT work as-is and must be adapted to your function's signature. +// +// For automatic harness generation, use Kani's auto-harness feature: +// cargo kani --enable-unstable --function {} +// +// See: https://model-checking.github.io/kani/tutorial-verification.html + +#[cfg(kani)] mod verification {{ use super::*; #[kani::proof] fn verify_{}_properties() {{ - // Generate symbolic inputs + // TODO: Generate appropriate symbolic inputs for your function's parameter types + // Example for a simple numeric input: let input = kani::any(); - // Call the function under test + // TODO: Call your function with the correct signature let result = {}(input); - // Verify properties: + // TODO: Add property assertions for verification: {} + + // Example assertions (customize these): + // assert!(result.is_ok(), "Function should not panic"); + // assert!(result >= 0, "Result should be non-negative"); }} }} "#, + function_name, function_name.replace("::", "_"), function_name, properties.iter() - .map(|prop| format!(" // TODO: Add assertion for: {}", prop)) + .map(|prop| format!(" // Property: {}", prop)) .collect::>() .join("\n") ); + let usage_note = format!( + "Generated EXAMPLE harness template for '{}'. \ + \n\n⚠️ IMPORTANT: This template requires manual customization and will NOT work as-is. \ + \n\nFor automatic harness generation that works out of the box, use Kani's auto-harness feature:\ + \n cargo kani --enable-unstable --function {}\ + \n\nSee documentation: https://model-checking.github.io/kani/tutorial-verification.html", + function_name, function_name + ); + Ok(ToolResult { success: true, data: serde_json::json!({ "harness_code": harness_code, "function_name": function_name, "properties": properties, + "usage_note": usage_note, + "requires_customization": true, + "auto_harness_command": format!("cargo kani --enable-unstable --function {}", function_name) }), error: None, }) From fb10f355996d4044d5444394c8e484acf084c855 Mon Sep 17 00:00:00 2001 From: ConnorJKY Date: Wed, 19 Nov 2025 19:06:07 -0800 Subject: [PATCH 47/54] Comments fixed: delete irrelevant files --- .../dep-lib-first_steps_v1 | Bin 30 -> 0 bytes .../invoked.timestamp | 1 - .../lib-first_steps_v1 | 1 - .../lib-first_steps_v1.json | 1 - .../dep-lib-first_steps_v1 | Bin 30 -> 0 bytes .../invoked.timestamp | 1 - .../lib-first_steps_v1 | 1 - .../lib-first_steps_v1.json | 1 - .../deps/first_steps_v1-83910d6b167b89c9.d | 7 ------ ...eps_v1-83910d6b167b89c9.kani-metadata.json | 1 - ..._14first_steps_v119check_estimate_size.out | Bin 30807 -> 0 bytes ...19check_estimate_size.pretty_name_map.json | 1 - ...t_steps_v119check_estimate_size.symtab.out | Bin 15385 -> 0 bytes ...teps_v119check_estimate_size.type_map.json | 1 - .../deps/first_steps_v1-ecd3bc8b423b2e0e.d | 7 ------ ...eps_v1-ecd3bc8b423b2e0e.kani-metadata.json | 1 - ..._14first_steps_v119check_estimate_size.out | Bin 30807 -> 0 bytes ...19check_estimate_size.pretty_name_map.json | 1 - ...t_steps_v119check_estimate_size.symtab.out | Bin 15385 -> 0 bytes ...teps_v119check_estimate_size.type_map.json | 1 - .../dep-graph.bin | Bin 152690 -> 0 bytes .../query-cache.bin | Bin 15889 -> 0 bytes .../work-products.bin | Bin 108 -> 0 bytes .../s-hciu6tb5rp-03gygye.lock | 0 kani-mcp-server/build_output.txt | 20 ------------------ 25 files changed, 46 deletions(-) delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/dep-lib-first_steps_v1 delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/invoked.timestamp delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1 delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/dep-lib-first_steps_v1 delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/invoked.timestamp delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1 delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.d delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.kani-metadata.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.out delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.pretty_name_map.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.type_map.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e.d delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e.kani-metadata.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.out delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.pretty_name_map.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.type_map.json delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hciu6tb5rp-03gygye-7jb1sxgfjbsla5m9hfducyrfi/dep-graph.bin delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hciu6tb5rp-03gygye-7jb1sxgfjbsla5m9hfducyrfi/query-cache.bin delete mode 100644 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hciu6tb5rp-03gygye-7jb1sxgfjbsla5m9hfducyrfi/work-products.bin delete mode 100755 docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hciu6tb5rp-03gygye.lock delete mode 100644 kani-mcp-server/build_output.txt diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/dep-lib-first_steps_v1 b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/dep-lib-first_steps_v1 deleted file mode 100644 index 024be4904569dbe2c19582a923171096f5c21ee4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30 gcmZQ%U|{&q$Ot4ExPZ90C|N%zGfA(g7$m{~07A6{&Hw-a diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/invoked.timestamp b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/invoked.timestamp deleted file mode 100644 index e00328da5aa8..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/invoked.timestamp +++ /dev/null @@ -1 +0,0 @@ -This file has an mtime of when this was started. \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1 b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1 deleted file mode 100644 index c6185f7ae335..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1 +++ /dev/null @@ -1 +0,0 @@ -aa4334fdd37a1140 \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1.json b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1.json deleted file mode 100644 index 5b3cf8a3f546..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/lib-first_steps_v1.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc":14672501838171729802,"features":"[]","declared_features":"[]","target":5150295270818399684,"profile":2308729259012748722,"path":10763286916239946207,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-83910d6b167b89c9/dep-lib-first_steps_v1","checksum":false}}],"rustflags":["-C","overflow-checks=on","-Z","unstable-options","-Z","trim-diagnostic-paths=no","-Z","human_readable_cgu_names","-Z","always-encode-mir","--cfg=kani","-Z","crate-attr=feature(register_tool)","-Z","crate-attr=register_tool(kanitool)","--sysroot","/Users/jingfeixu/kani/target/kani","-L","/Users/jingfeixu/kani/target/kani/lib","--extern","kani","--extern","noprelude:std=/Users/jingfeixu/kani/target/kani/lib/libstd.rlib","-C","panic=abort","-C","symbol-mangling-version=v0","-Z","panic_abort_tests=yes","-Z","mir-enable-passes=-RemoveStorageMarkers","--check-cfg=cfg(kani)","-Clinker=echo","--kani-compiler","-Cllvm-args=--check-version=0.65.0"],"config":2069994364910194474,"compile_kind":551198452465029181} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/dep-lib-first_steps_v1 b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/dep-lib-first_steps_v1 deleted file mode 100644 index 024be4904569dbe2c19582a923171096f5c21ee4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30 gcmZQ%U|{&q$Ot4ExPZ90C|N%zGfA(g7$m{~07A6{&Hw-a diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/invoked.timestamp b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/invoked.timestamp deleted file mode 100644 index e00328da5aa8..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/invoked.timestamp +++ /dev/null @@ -1 +0,0 @@ -This file has an mtime of when this was started. \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1 b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1 deleted file mode 100644 index 136a5444b102..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1 +++ /dev/null @@ -1 +0,0 @@ -13c836b8b850d3fb \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1.json b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1.json deleted file mode 100644 index cb929f0e8138..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/lib-first_steps_v1.json +++ /dev/null @@ -1 +0,0 @@ -{"rustc":14672501838171729802,"features":"[]","declared_features":"[]","target":5150295270818399684,"profile":5594203621556287378,"path":10763286916239946207,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"aarch64-apple-darwin/debug/.fingerprint/first-steps-v1-ecd3bc8b423b2e0e/dep-lib-first_steps_v1","checksum":false}}],"rustflags":["-C","overflow-checks=on","-Z","unstable-options","-Z","trim-diagnostic-paths=no","-Z","human_readable_cgu_names","-Z","always-encode-mir","--cfg=kani","-Z","crate-attr=feature(register_tool)","-Z","crate-attr=register_tool(kanitool)","--sysroot","/Users/jingfeixu/kani/target/kani","-L","/Users/jingfeixu/kani/target/kani/lib","--extern","kani","--extern","noprelude:std=/Users/jingfeixu/kani/target/kani/lib/libstd.rlib","-C","panic=abort","-C","symbol-mangling-version=v0","-Z","panic_abort_tests=yes","-Z","mir-enable-passes=-RemoveStorageMarkers","--check-cfg=cfg(kani)","-Clinker=echo","--kani-compiler","-Cllvm-args=--check-version=0.65.0"],"config":2069994364910194474,"compile_kind":551198452465029181} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.d b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.d deleted file mode 100644 index 3d3cc416a35d..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.d +++ /dev/null @@ -1,7 +0,0 @@ -/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.d: src/lib.rs - -/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/libfirst_steps_v1-83910d6b167b89c9.rlib: src/lib.rs - -/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/libfirst_steps_v1-83910d6b167b89c9.rmeta: src/lib.rs - -src/lib.rs: diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.kani-metadata.json b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.kani-metadata.json deleted file mode 100644 index de73af3a03d5..000000000000 --- a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9.kani-metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"crate_name":"first_steps_v1","proof_harnesses":[{"pretty_name":"check_estimate_size","mangled_name":"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size","crate_name":"first_steps_v1","original_file":"src/lib.rs","original_start_line":52,"original_end_line":55,"goto_file":"/Users/jingfeixu/kani-output/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out","attributes":{"kind":"Proof","should_panic":false,"solver":null,"unwind_value":null,"stubs":[],"verified_stubs":[]},"contract":null,"has_loop_contracts":false,"is_automatically_generated":false}],"unsupported_features":[],"test_harnesses":[],"contracted_functions":[],"autoharness_md":null} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.out b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.out deleted file mode 100644 index 8bcb941e52f2c03dbba68cd6d7b6b06d4609f9a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30807 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 oH!iTmNN0Pb8>4(-jb`WH","tag-refstr":"&str","_ZN4kani5panic17h039ec056df9649c9E::1::var_1::message":null,"tag-Never":"!","_ZN4kani5panic17h039ec056df9649c9E":"kani::panic","_ZN4kani16rustc_intrinsics10panic_stub17h7798f34a644f74adE::1::var_1::t":null,"_ZN4kani16rustc_intrinsics10panic_stub17h7798f34a644f74adE":"kani::rustc_intrinsics::panic_stub","_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_1::x":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size":"estimate_size","tag-Unit":"()","_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size":"check_estimate_size","_ZN4kani14kani_intrinsic17hf2340b114c62bceeE":"kani::kani_intrinsic::","_ZN4kani7any_raw17h1bd6bd359a6961f9E":"kani::any_raw::","_RINvCsc8NbQx0kdtQ_4kani3anymECs4ZijrwXvPyf_14first_steps_v1":"kani::any::","_ZN39_$LT$u32$u20$as$u20$kani..Arbitrary$GT$3any17h9d58c5c4eb479917E":"::any","_ZN4kani16any_raw_internal17h8bfeb1a460f3b3fdE::1::var_0":null,"_ZN4kani5panic17h039ec056df9649c9E::1::var_0":null,"_ZN4kani5panic17h039ec056df9649c9E::1::var_2":null,"_ZN4kani5panic17h039ec056df9649c9E::1::var_3":null,"_ZN4kani16rustc_intrinsics10panic_stub17h7798f34a644f74adE::1::var_0":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_0":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_2":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_3":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_4":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_5":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_6":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_7":null,"tag-first_steps_v1.3a1b106d0fea0f11::first_steps_v1::global::0::::struct":"first_steps_v1.3a1b106d0fea0f11::first_steps_v1::global::0::::struct","first_steps_v1.3a1b106d0fea0f11::first_steps_v1::global::0::":null,"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size::1::var_0":null,"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size::1::var_1::x":null,"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size::1::var_2":null,"VoidUnit":null,"_ZN4kani14kani_intrinsic17hf2340b114c62bceeE::1::var_0":null,"_ZN4kani7any_raw17h1bd6bd359a6961f9E::1::var_0":null,"_RINvCsc8NbQx0kdtQ_4kani3anymECs4ZijrwXvPyf_14first_steps_v1::1::var_0":null,"_ZN39_$LT$u32$u20$as$u20$kani..Arbitrary$GT$3any17h9d58c5c4eb479917E::1::var_0":null} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-83910d6b167b89c9__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out deleted file mode 100644 index 52dc78cf1271c1b8c5d807b968ad78a40503cf39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15385 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 oH!iTmNN0Pb8>4(-jb`WH","tag-refstr":"&str","_ZN4kani5panic17h039ec056df9649c9E::1::var_1::message":null,"tag-Never":"!","_ZN4kani5panic17h039ec056df9649c9E":"kani::panic","_ZN4kani16rustc_intrinsics10panic_stub17h7798f34a644f74adE::1::var_1::t":null,"_ZN4kani16rustc_intrinsics10panic_stub17h7798f34a644f74adE":"kani::rustc_intrinsics::panic_stub","_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_1::x":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size":"estimate_size","tag-Unit":"()","_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size":"check_estimate_size","_ZN4kani14kani_intrinsic17hf2340b114c62bceeE":"kani::kani_intrinsic::","_ZN4kani7any_raw17h1bd6bd359a6961f9E":"kani::any_raw::","_RINvCsc8NbQx0kdtQ_4kani3anymECs4ZijrwXvPyf_14first_steps_v1":"kani::any::","_ZN39_$LT$u32$u20$as$u20$kani..Arbitrary$GT$3any17h9d58c5c4eb479917E":"::any","_ZN4kani16any_raw_internal17h8bfeb1a460f3b3fdE::1::var_0":null,"_ZN4kani5panic17h039ec056df9649c9E::1::var_0":null,"_ZN4kani5panic17h039ec056df9649c9E::1::var_2":null,"_ZN4kani5panic17h039ec056df9649c9E::1::var_3":null,"_ZN4kani16rustc_intrinsics10panic_stub17h7798f34a644f74adE::1::var_0":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_0":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_2":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_3":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_4":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_5":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_6":null,"_RNvCs4ZijrwXvPyf_14first_steps_v113estimate_size::1::var_7":null,"tag-first_steps_v1.3a1b106d0fea0f11::first_steps_v1::global::0::::struct":"first_steps_v1.3a1b106d0fea0f11::first_steps_v1::global::0::::struct","first_steps_v1.3a1b106d0fea0f11::first_steps_v1::global::0::":null,"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size::1::var_0":null,"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size::1::var_1::x":null,"_RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size::1::var_2":null,"VoidUnit":null,"_ZN4kani14kani_intrinsic17hf2340b114c62bceeE::1::var_0":null,"_ZN4kani7any_raw17h1bd6bd359a6961f9E::1::var_0":null,"_RINvCsc8NbQx0kdtQ_4kani3anymECs4ZijrwXvPyf_14first_steps_v1::1::var_0":null,"_ZN39_$LT$u32$u20$as$u20$kani..Arbitrary$GT$3any17h9d58c5c4eb479917E::1::var_0":null} \ No newline at end of file diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/deps/first_steps_v1-ecd3bc8b423b2e0e__RNvCs4ZijrwXvPyf_14first_steps_v119check_estimate_size.symtab.out deleted file mode 100644 index 52dc78cf1271c1b8c5d807b968ad78a40503cf39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15385 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℞ow*=NlK9$Q>M&|Lqo~clQ(#!mPl^ZCoB;;ZTrd{V z8Tvf3TY#R){7nYMkW`<}nB{I!4YuOHm(U#;JOQ@8 zhVEQ(5n~suKDFJBtCgR;Zq>oIlV`Ymz=6Ey@5F6X^8y%9^G1xgrtw#Z%~IPIy_qc^ zWh4Uh8t;W~y1iY;*iQmo)93@R^)+;rwJ*l5*<|#6;r7hTG#=h{CcBj(AzPl6H@;=< zp3Ij32GlMiM!X8r6*DhyQ?(Wb6#Yv6a`J=M3KI{WpKS^rQLFvG#uzg8>HAu48OT!pZF|ms627BlQSH5yQ59Z3OP5eB~8GvdL00U|_FxLNGwnv#E@IR(j)>WMkFW-yE58ZVNn!1N- zkpKf~H!*hfC#RYSt6b0n&5f<~)rYnh<}mGL+mxG&YEb|KYPT?ED#d^Cr$xGuqS;{4 z{QG-80?ry?OV2z7Q7sx^KrIGia(-J$Zl?;kvkr#tloMRxW+lLvxTGs1)i-CEBDj+o8|uQsAuJivfj0>+e`LwB)WL5;Qh)!YXdP)o#^yRm!mh@E8G^)tm^^vMdxI&>Igo|@(D zM70M118PYaOS!cBuE>mPufb7Szip(VyB}m1*teFLUPrZu00U~t7_(js9r$(C#6ySo z1y5e)&5N=^zdg>;#=D~0BY*+5#~4#TctS-&)xqxiQvI2X+6XTHNDW1E31@3mdjc?^ zmVz;(54+C1X`Z6Iz2_;Pa!As zkk;j%Gg_$&Z`Lk&&ITAz%fZ+~KlgDu}#?{$Wjtl*0NkTd-f${HwekTFOent zzUKcO_xF9h=5?Ic>)%}6kH-5v^UU|mIrIHioXl}^f+HiQ6l}cKu`a8&Lx#yz+iAuI zy3Lp|YDB>CYA24bpXcJVv)CR1&%8LMO4y?ceUPP^y3$F`kyaTZYM0J7C6cC3#QZ9=IsP<8zC>t+n1M z?H!}T6Gu)CXVnhMFqvwH%vfEUaYpU*A$787<4N_UJk^WBNt6c-Q%l1A$dLBEw{=9W`SP&%SqU^!C?#%gc^Xzudzr zU958;&ON0at9DF=$y7UT#-4vIe|AC7*zE_B_bx2A_}j#_uP3hmrT=L9fxr_oOs3jN zGd8Sh_>I`MpY67e*zI01|54cywSzX;)Wr~1?UW3Ysb(}|j%AZ)?5MZkq0ARcPn$6u zI@f>jTC0GoA#pa|4&U8BRZQ8_u(gZng>*)S$vo&;GuHD|Kx$C48$Fu8k6QS1;)N!u z$q!ro%&5MBRm+uOGS$v8#;5MF(Q()M_OdS!k{d_sXYROY=xmI9^f=4(Z|R(uVbW@B z>VCnD-Cr}W;VZ+I-lw#^Zs>Dnd^HTamOoGJFR*HPGEAo0MKkuo@m-|;;+=rf|8^{H znv(OXXn)_xX;~xQuxj}-Os3i;Gv?g3oj5qjsmCls%(a&p)$JYUMBIvTiZ@leEW>1~ zT`^;st37r(OfH{8e&@#x>O8Bcx#%!bQT@Ls7wx)ROi}Y51W7ht!U6ya}weTvdc2kDQ zRJ&!yuC87c*=LDM&sJ7ZeHzq0dm~&O(0_vW%zmueZ5bw0?T#7idg$)@rCHzHzHc06 ze?FqhSnEo0k2*A+p=H(X$}pK~_srOh{%zt47Rv3FKI2!d7+46UIJw1sxi9y&k>bc9aO8M;7M=my=P2lRwc=mou@4}?KD z^o0oM2a(Vp2Eagwf@l~7F)$d0z)%x}U_&LU z&;l({jaFC+OQQy@5fG73i)Bzi5zC?uZO|6&upH{KJXXMpXpfb!GCE)ttcumJI@Z9N zSPN^TBi2DDG~i!Y7we%jx}Yn%VSQ|X?&yJ@*bu$25jI9|^uZ?Ri%qc^Hb+17#{dk( z7T6MluoVVlYixsUu^qO@4j6(Ru@iR2F4z^jVR!6-Juwt}VQ=h%VHl2mF#`KxB=*Mv zI1r;S8V6wv4#puk6o=t(9DyTo6pqF*I2OlYERM%GoPZN?5>Cb`I2Ge@8cxRqoPjfO z7AE3soP%?59wuQj&c_s7fT_3;7vbNy7}IbGF2!ZI99Q5v#ii;w`+5cknLW!~6IEA7TMM!pB&MPw*)| z!{=CpFYqP4!q@l)-{L!bk00?gq%2NfZNcL2TDw6|Mp{i7ks#6WBNwugpIZ_>RA_M(J zb*UaXlMA_$8`Y-<OdjXkvdUl>Ox(q8+E51)RRJ~7xkt-6h`6Hmm;ViMN)qnKm#d?qG=Gt&|n%u zLunWdrx7%gM$u>*Lt|+i#nO0+qX{&TCedV?LQ^T8rqOgtpcyoiW>F%|ra3g1=1~$Q z(|k&y1(Zq)X%YQRiz$tk&{A4P%V`Czq;$%lRkWJc&|3P3*3rL|N$Y6?ZKO@KnYPeY z%A#$wop#Vp+C{r*5ACIWw4bu+03D=5ltYK<2py$kbevAmNjgPFI!$NjEalQUI!_lU zk1kR^U82i$g|5;yx=uIfCf%ajbcgQJJ-Sa1=phx*BYI4Q^n{+$GkQ)%^n&tBzhdtW z{F@Ox>kW;VERMX9V0rfHm4TuDlrFqBQ~avWRX!wq3JvgEO);OmYS(u;XzL}ukzvwz zBwL;MmN9;%TAtEH1L?$fjPZ#%&*%Z+R~s}v&^cjHnT~OFo;!#-@jYYw3pPCCyJp~< zn5iKS)oXpe7xZ4I*NGn(GarVfr?fX;o%oY6e&uJLV`4Dp*WG|wJr#+8 zdoT2w*-R%EGsZ9d%yUB4>R|5)`>s-1tC)l3j;;06iN6@**M8Y;}!r6UY&5Tp}TjPc7q^9=82 z7rO7jk~`a)4DLL6sH#`6PPAZ*U;mls1H(hV*DErcE;{PyYsj5Cu8mH#WQ-qBJo{NB zZrM3g!(I{*pUbsAS&bbFYEy5LD?v-2X=R;g#TdT?G*2n3aOX@Z#-z7h;+muB_of{4 z_ej5Qf86HV9z}@aExn`n^npIoC;CiZ=qr7r@AQLyQZccy*DAFtt%cT7tJYd+@0KlX z_Eh-MukF-4p(ZxIHpr&$T93_5f0fc+6f|azKjl9Te^IoSVbbSfJ4668#vk=**v(YW z*Tt8CHa~oGE`NY+$_Qj^$*Y4aLN8k>E#G(9-O8!iq{mAp1x7n&tzYypCo-$Xq-_%| zUsHQkh@z+B4RyCDt<`9)wMEvTwNspEPHg+$7msvlk}Wp>c=h7}_s_oH&T4$_q__O$ z;vbk*({tS8CMIjD5u)g%YOnGVwPvk-8qhYhn)+AaA5S!;ZyCls?K8%ePaC$uXIZYz zv*d>dPxpUx-0y+g^U$Tjl)}qLGZK}KtfZ+m?eDhcDU+0Tiese(wr%3g;_E5Xa$HWt zF0y?)di!(tPL~$lZkMnoR(VRD*yC%H6MaRdtcuSqo-nKDXxHi+tR4Kv+`rl7Xi}vg zZy(#N4)NG_Okp%tG(C!f?e{LKDXNJILEB!HDwH*Q65opx1HP@gH|j{I;LcsgyA*!d zbxhUVXXk0#bU`P>f_wrSz3ks(S&cG-?#;WfcLF>0+sLp#hGd9fE5qX4?rC-m^z&;} zue|@8%c(`SDSho2J5c=IcW1vIi6fI1whv8u@)EbaS}`=T>*bW;*PGAuD_#6Zt<#=T zn?}y&_sI3Iwl(e0kaBF>GcP;8-FT(b^r%qrVW%jse$p{9S=09ajflYNq=e|%b_caC zMX9HfZ8zBlF1wNzIPcf_LHa&nU8c48;_dwYbhXc63Q(jdO0kjbsqnW5SE$$vs65-Q zufyUFhTtFCx%nm953e0AcYO$8ZaD`pQH9$!j3L~xLC`YO-cgH=>gqpdCu)xOWa z&W_J2jMbkewp%_cZf?hqkpq@m<=piMNk6IFw>hc##ITPm!;`*Dx3p(3NzPxa<1fSX~*F5JG^7wL>ytmTSJD)Onz$0Fqz@4 zCByzanGO+Z%Q%_gb!2Q~*zTv#LhAvNMbu|>Pulnw^ba+kHRD+-XoXlFA6clkW zKIdB#>0k7;y<65G!=wXJN}Ce>7jwf6POCT8UXz*H_Vl}!*~6;d)`o}r8qc~O)iB^M{1E-hqL{lgX8%v_qhWYlx}0X+-Yj(3(}|2}#YZ53l(vtd`~cjv%i zIXm^a$gn?#V~F4?!~U_Jm~63f?xr8NPaNMl@s_o1s^DhE_D2`#JyX8-Q~Y?Kns?yl zq$9$sdQU!u_(bFw(?5$Jd9WE*JmAOhc?am4fWVC~wW!n8; z52kjFSlFuihu|6YwLz+s+3rkUn~;-m+x@TdnS~y4Q_uAaStne2SU#Xickfcm+BJvS zLo2DBwfRcZ;p+1HwNz;Bs=Z;It30o4ppCFLy{USzDqACT>ML&}Iuy)Zde)<8Xqt4V zn6@%iGVPRn<+0M9Y@2Q|dkmSAHg)8QD@H3MZ>{KkqO_Qb9FS?@&1 zU8|1YTjUB+GD_N-d!7(o5@rwvF~(1#Jf*F9sx>h(?1=k1iw9ld)u`_xbLsdlK#^Q3pV!e|*KZLM*hFs3BTAdF>< zzhm>1w&tVOjFVv_D*Xt|9QVjGenf1)RW@N)bA?zLC2h?oPZ(bkW)R{Sw($;*{ znh7#&iXjgVt{UFiGcdyIsMYaSxxz#lC2h?&Pnc8^W)LPb#-~F(rL8qnYo^Gs)7@IF z=)ZV|86{x`VJ2gIO3G8(T7X(JONO1f{W4`z*u?Ctgl9E3Wo`bN zDZe9lQLiu(Bk~Afz+)|9&e&t;vvK`^tH2w=23=R3{~1 zM)i5!>*NZnWR$eE%6Y=-k}!j?hB3Z!=P4bv3Tn+-8P>xhztY0>Ricje-LtP~SpVs{ z!ap)f+FFG?VO>d>Qv8=OzLMf8ZH<&-CS!bm^OQ1MrMO<^L49r?*4RHC+%l&1t5COF zZ!1(#iW?Z?Yb2i1YNAry$QVB~c}lAVE5%KW@xz3tl(kWcn;GM$N1jsFLMd)xjGwA_ zN?DLn+{ze#AK)ov0ZK88G5$`*Q_B35;x@+kdofQbYo-*pGsY)6Jf+M>Dehp5Pda!? znXgjZ$rzt(@{}?!rMQbRKJnuzWsQ~MZpQemnx~X`D#bmF@g)mSDXXs(_cF$p4?LyJ zT`BHkj4vB`N}01#+|L+a`0@mL6nI))_VRL%$U6GfTMc@pn9Uep>G70Sb5$r6N}a;= zf736ul6_dJ)k+1s&va8N%j;F_n_cWyx*Ypc3nlxVN~ut>J5B#76dLw93%y=prLbU% zeJB-H>@L&2mI}4ViutiRYzr0p(HGNA#XeXCR)^*PZtT%bpQ;p^)gY8+-`-(Bb62H! zssdA`MyAJMH`X4jr?9qEvD*JVhUsn__Qy&)w!Ize%}xod8Zf}hDH&t;E6ds0DTe%S z3zfoJrLrrRXu<9m**0X$Hey@bv98!%rcTP48Z&Ln9tc%x(_^rkX^6_QNBFO6<^OuJ fP#$0-ZMyMXtpD%x5u-|5DMvM))cEMrN8Y=sJKmJ$_Zr_EN9kfO!dD~f4T zQjs=qB}&oyyQBBHde_Y7@%`&J4|8Yk>-pNxIj^(a;o@pzfgti4Y6co=>Z+kZfg7Si zVx<%VH1##L{d^4cr8LzwbyU^$RWXy{*wz4%%3CT`UDf`DiMTrC;0V+ zZ8!Xq2fw6XUs#Ihmr^2jIYYm+l6VLR_|pbqkvhV{gpLppRsjTHAAgIfs$*n?1w#=O z1q>K4y#Qd62!VyM24gFP1!W+J(GirnVRNfl!--hzG=_#Tc>plQ2mz&=|J(Jop0jyl z6cq;1qoSvgv2r&|mYbp0onRMT3~Ou#CTBsqW|XPsc)B<@L+L_r3Lq?4uK#MhSp;hZ zrpgM0|#aigov;T)5RGuG|)xq4*bAEXPnbn z9ROhJ5dtd-K>*8NfgM#zYwgdX$iZg~_D-XF+-vuz`qWfkB>*6HX8iA`$BY*dG@| z3zrFP1cZf^f$%JXurluk&2-V(ZotN@6=rn+fT>3a9PHTjuhbD95eArtI1AEEu+E0a zNdC^OJSRilN$5`c*kC+-W6P_G>I7WUPk%<@&EN%tYz0E{tUY0b8jIqQgSK{KqOgqbRUs zVGxRNzYBU*#il5ln6WQnpXkYFGX#W0AY$3VlJ@Jx`?p)-#_}?Al4&NA*G1jkk03mZ zIN3~*hiptkqnmg+uaiWdd~j(vZxdAXu=>S_uJe2G+F?WK+j;GaF#z;3fyGNN%rKR| z>tbZW$R4o#-urNV3}9&6Xa#0Fk1q0CT5%~`JIr4KLkEoP8OCVu0Ei47aB}*i^Ft%Q zS2{=|AWm2&|B9Lci6H4jZ&?PcScec|gb+kTF!Fy!5N=jhjwyu2OViF=n7o zacNi~XN+Rr5=itmWX6EiDr~U*Da=@aX|clk0vCxbjh#5jJ2wV@Pt<>70?4HBZ&~~= zh>0=7=((rAY6v$-2)Y-$WhtOvPSV%CmGnz8K+r<$eTs;nr?Gb*#&-{U@55$08cH8( z1kPhvaHE3QH}*XAuedD{-am^A;-oSv=s`p0(@+u()f^REPnM*iTr^al>_?U-JClvm z*p){GSJ6<*$Gk*j9a(~gu6W4CiRh6jWH}n@MCPNRMntH#?9ihnPi;iEfj7_Q$`v2M|b^nY6WgK{}>NsAv5j zTtOFuLbQ&>+$Ve{FyO+{IGg3oy_r@s)D7PNLQ)zPbf=-hG?aygsw18>)P~GKLv_i% zWJfYDc@eUbh6<6*GexlsFGj>@C_4@HApY$kd20F zBhrX{8v8sNx^Ps`m4*tEP2US+wNQ0ba5W-IL$yW)y=bTu4K+Bz=TAcwMg?8S0%Vg( zVPH91?0@fQz!HyQZbG2<6>Mk#9P+c8ZfQHB@bLf85TI^8gZzU#^KoVWzcN3&rbe(0 z4mL~?L|`5whma64dbPn)M!%S1LvKC{CS%a`4 z0`xBu^kP5T!gGUS_7+}_k{;p%0~SpL;ozj9So>lvhBZCu2tVJbAlA%8Y^^^k$c4=< zSP{d%qnLqTx;dbYUxN)QpZ)Y6KoRsY_NJ2Xx&?=#K!%u%*BS@yV4u(+op9_z(a_tV zu4Uj)QP^u*u3IQhQC%#)vH*F;1}oUK-i~ zK@pKrm_q;XNUsSz^9x z8F}IA&VJBSrKV;U;Tsec;S&)nXYD3OcLdn`!{VEqlT}auCJe#j)9M^fv!dXmSDvkF^&I&w_5-8Q>R2s1*+GW zP0d`2F*5<0TH5Nq8XDRZT}@w#zrPh#ofRRl3lKO237nh+DNaHbqnQek6fXab4m%|Q z5fMJ|+jbRpAp%m#DpF1ac;AADI)GgD8xKv zdid%UGkoyEo;_8k5BW30ZH*D@{v5vJ%y4pITCPRNodeA9bsDtgO%Z`lnBntZ#95DI z)-jF$rZM|wfk*Py%;e2qD|~3vdZEe;j~|4Kw`C%v6icuJBXLaJ}REb=GPR zmovk41N+Zxm#qEBbVTK2vL#Y8BqhieP7GnC{{M3vFW_Zt5<-pawp-lUSV{Luq%W}^lX7Vvx z-rTfz)fZ=m$Cs9U>yYbv$PD)t-#lZde4&;Z&U5$0hWeGqnU3F0(;LsPtbTQYnf#Dd zjgpws@)~Bi%1p^^qfmWEW_XCR(#bwnBvSb1+bX8@b@b-CJ#lp|{LJK^Ysc0_humXhhPPxjrj${- zJ(=M-m(4%Vix?&|!xa*Vcluvtmome@SY$rTFSd&Q69<1178A4I#JYN9P@DEQzco9d zT67L)fpZkTUvTUxVapCE48u8KFGg?*tSbPgz?qBWNu(#tJjcwf`y42ToI0u~XaE{C5jP9G%vpqMH|zuva>3;ab_5AYa0X+Ua{?A+Jjpz1$0F8Lew^ZhLVuRs zKbKK|md!txL4TH|KbJXwmYqMBDSwuYKbHZ2mJK+~5@7)EzjFoi6<8+XX@Co5ZZ4bz z=N@iOoCK$HZVsFTS2*14I0;VL+;g=6&NJMsI2T;na+7cpoUOR$ni?)ZxaT?qT+?yS zwHRE6anCh1oY}eOIs`11$goHy3xXcTj?D(KEz1+^{TO>c!`=uY?zllJG)zUxM=HQ4 zC?qH}P>K>35$YcyMe&LBm&Ok4#Q(dSq2Cb%5d2{AN&4zr?eGduPk;dOIIt|;I;k!1twVUPC02n-9 zjuL@wt_=+-VWHR_Js>Os+iQFIL9Q}GNnNhllaF~0j$=DcgzyCaQ3A{W z<3|$Mb56Q1U)Wd#kU0aK%l5}_ORQXqTm0RC7v}Weui3O=XVJA>|0Y%8KdG8MA2Bri zbxeZ&d_n_HmE-Mswsze*m$~tb!_6>)-UXVo=ZWH4h#+Q_=a)De=hfdOGBW|$s1`T>ifp_do^)5~j1SVXW-L|AmFpBFX={Jnf4 zBmEGOwE`Y14@>CzjqE>lUhR{JIBb#mZdXk!Xh#xL+g0y0IMm2wK;RtUS-J9`3;RiV zxnGCRb{=Xz2@%a5g?2LDcltUXCP=rwO_qD?mr-4k=bl%79ef%`c)Cl)!KTgSVO0_; zZcxN_RRpOm!l>t-U@X5P_t?=8C^8HRf~@~}o6ixkPuvhS)#R{(B7Ol|&Q&;w-zEAA zpYwh?In?7_0Jw-*TP z6)=3^QZ9}idv%hwD@h$Sn10wX14Yh)fi*H!=WUjmA2Dq$2VOnnN*VsPP)}q5%v)R&w$0F zX-d1B*3qT6wTTH!GOj?x_tD|}ne4LgYht8BA@6jR@oqT-)OH2DPC1(Q#z{{%>(1zg z(@-S-Sp@Bc9N9ajXmnMePR8462xI}4XB|WHSYKZ43RDg3dU$;k3}2{Am3w82Wtr6e zFWkpu+l4t{ELA{BZ^J#I!8=IJ#RIor>aAS~k;T@Tx+2T_%y(Aq%UcoTF#ibzIzU-! zh>&9xhp>X5azPVVdjQ&BKFk?qsgmg6?bQ`~kzF)(AryHEo@(#{k0+*n&%~8aOvpUL zhq}*ZWBaVM7%peo4__ZFMag(9(?D(brFK-?)`qg%^V17CT#ccXxMNPWZ^I20?uvBj z-MX+~&ngHU26~%lTZIejO~pH}s$6JF8inDL*Lr-b{rO1BX~MjR+aM5;Sy!F1PB}JB zJ|8lzHUmYTfFCJS8PBVqY~5!k*WO`ke-?^(XOj5N zJWPE++{d?1qlvn(83LbyM`n^_x=Hl2#*Vh{Pktm%K_vFvSe{hO=pt)9uL~-#Io*~* z;1FOTEgDZ8Q}CM1d9_A4a0-Rt3!?L=&CYh*MV_t2n_c{MYGC-MK^IlWoi+LUIa6+0 zXqj5nW$3c%BiYmS5{c1myZTfW=k@Pg4Cj|NU>3UZV_`Ja_n!v86>)L4;n4oFrj~KZ zPU-j51J_JRamoi-psqm>BHC1NAnA46bB`r;H(Yl3LlLiziIcL6UJa8%%FB1^U0zOw zz$dUr@UzwjvemTzX5WhL(FDM-a3uEFf?Y)W5He@35pS2)1q~ zMc#iV2E!L{RWzXu)irszq}nX5dY#$?!>>1m#kb{z0<6H8q&(2R3t~J9|xj$oqD<-iYV`r@o&*dnqGgdyQ1_}!;^(D z{1(8YlcX5=CH0*_hlliz*zYmWzGZrH~o>WI_=Zsu96v_&DvzYqsy!-*F)eVkUVa>)!r$qck=R8;1*iq43)*$7WIWE zcO+Xu5Us+*<4Q7OtospNnBo8K0~H`^m1gCYF=YuJTvX?*D}0 zmjm~C%a_)ejlVd@^EkT5f$#%bQHsi*d8s{gWrg)GXqZ#C`1&!v^S>XPD(u z%l9#~Pzujxkj!|89#cnK?>7U+qb|3`N%VcDFBfc$2!S zr;Y7p8&_F46qy7K1*DE{p9uwAZ!o+B4Y1BL@!4t0Ysc;lDhbG6y9Lw);_cMd<7 zPzMo32cy-{d_S;rZ7yK3PW#mOu3K@=)rXUh|_8#ZJNa1tuk?ImN#OD)kWj`Q}+?ZOc>3Vh%5Z>|5vmIZK z&fDBAeg7!Qq3rm9ig@Y8htI?CPl1sOt}4&#rJk?IJ$Hs2+=N?M_|tkHCD*2?c#+<= zbHmp_5cK#e(ET}mzKuf`%^a2v^0<_92r05pvMnDwU906 zj?vF%OGkBs(C91|sBAUc`CR=jJC_wD2KMO##V>C|k@rB;iTdhj*n~V=Ri4O>1cQGd z65)G*izR(PIOn=b;-u1-TO_D!A9&L4NOCY*j*g#ClGV~ahz~LD=;kjrYgW}hTW3f- z*e0NF1;_qLpb;$Yw8>0!2l4D#`+;|*&!92|QxYZ2bd0AbJ21?YwO{NK1g?O_wlf*; z`X6@XwDZik%N|_-5tI75y|VB0w_i%|R30&C_7;L3-v>2?hsxeBH`!n!F<`#vsz@Ld z@z~grC7!RndfRQw;0|@i>9aWS(Ho2VaTDp@6p@z6Q>tb2Zz~U_$R4hHO10gpn1brw1LQX}tzIMa`a4p|bEBPX!thvceA{ye@G^tVqkCNGh0NIK;Z{c5h4sJ`mq;yT?6uz`=0wv>x(^@wuBVo}aR*e60X-lV4VLyYhQz{w=upe1((C zPVp&wJ?~t{k%w9^{IIV|#Kqm$-1Bdn>Fv47Yl2UrDL_BH>!FJJb;@$36yLhq$#JNR zLuhSU_{8_9wd_1KPAxAg3}E=jKv7>|p~j`CN7ruDa&{0`7eFhrc_M7A5>6W4aen7M z1va_gfVvvNAa4q1VvH+w*tv2ImDY(bdzMa2`g|-X8s_IYp0=rNan5kdez_*}0m zy4}K7aUj`pjf4?YwzA7*U87{d0QDu$)2l~U6kLM^b`0!0hUPCbt-U>dFGqplM7UxAl{PcB5_`>jkLH zZPmLdWZHC!v-%9zLuH$mRo88iiGAwNUuU{x zeY(LDsB15%ZneTgVA=l%4y^}IRu)woj8r!(2X#4%VH$A*rg3>bkAW#f8@as*? zw+Sj~RvnNJdH!AvDwD4K*p!uUM%~H09^HP-o3IW77s0od?6+3ghbOI-Z4Y0f)^CJ} zp}V%k7HY>gEwhg!CjD}*?a<>sz&oxdb@pI&(FrdW(}8_jo>0U!d^j!J`EF)+8DB2D z$MEJz2#f-2XGN|xDGBMR!{@HlkIa7nm4zJNt<(a1KkX|z(rcHTBZg;x4-k_5u!~DW z$8n5jx~U;2W8Ea3|=+VR=E!T2^xHxKb|bH=xCE}AyWT9)Ut-r%-aFv+*%7Cwu-0sfuj6H#xM z81(xc)?ASBECsq8yuaD=dhtEo)v1q~8|x}3lcD+DAgZ#n@y$y43x-p-Qi$x|2OvT^ zWzJKSy>;XHNK^Oj7Udp%XxraK|?J)CYmrAmzP`WN*xu%%p`CcT zK-nfOZr5Atf#|*Qu7Xy2z_)f|koJCeYron+OzitDRA ztrr!)71c6_;iIeL_ceO|Y+bLeMx-u2J$MeDUE~3skNc}lU!)vTUvaSLh90*9bWF@U z(6L65FO=fHm{)Oo&N6&;Q43P#eh!Ut)YjG98aM9Plj;RUbfUk^ml$0BsJE3_lfva zT|+jt(8?bwhe)8*BA@i5<;10CeLMCxsoMHNAQ9AC#C9!AsvJm4jchh~CHEGFPbkr0 zFL#gIa&?T2Gt=>6Nd`O{&jltEA9njz-IodI|&Ty}q2Fm}jj?@G>95EudJP9YQM3g@QIYP(In-C_8Y6hJG^ zmFQid-`%3JlBz;D3zjzo%}DFOv0M7=cG$1H4>TcUD>!^gQoK z(%Mhiiyw_^sb)9?qR$JRpDGmiW{6JWySE!)b(Z>rjUUVj)+=P=mJ6w>Kr40iwN5ttVnipn041M)!?NN52& z!40q*+yQyuF-QR~KoJ-PrNnV?hWHV*l4d|BX^6LjbeepF^h9_s7#1!7uY^w$$Al}1 z6T->hlW-yUB7BPYL-;gtr$`@ZugE=8n#ewIK;#(66UjqQiabQGNDQDwl8@2jl2lTu zWQJIypFehjc^kHfcz%m-Iqz>kqluB>9-#@>`PS z?~>Bw4}t^or$C8(r{H;cn&1Wb5y3|JesZ6DB}={WVd5p@LDCiDCnT!zV^X{EGtzZq z1oRlQA``}Z_e^+mO+b|icbz5A9upR-3H=`f|MPld0y`6Yq}pK)Dez(0Jqt*IkKVd0 sAq76`y=Dd}@X2w$DNgzKxpA>MGNS`zf7z%1#9P%r)K4`aD8&E&0Sy5)kpKVy diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hciu6tb5rp-03gygye-7jb1sxgfjbsla5m9hfducyrfi/work-products.bin b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hciu6tb5rp-03gygye-7jb1sxgfjbsla5m9hfducyrfi/work-products.bin deleted file mode 100644 index dc0a5e2ccba360af58be83655f2a7497b835ece4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108 zcmWFv_H<@oP&L%EG}JTD&C5*BD9Nc*&`2|~FfvI=w6st#GB7gLHL%b%ve0B)dF`;# z7U#m3Ay@R**sD53apb0!B&H;mBpzgBEdsF*^1(#(ApD}z;u77|ycFHE%$!sJ0ShFf diff --git a/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hciu6tb5rp-03gygye.lock b/docs/src/tutorial/first-steps-v1/target/kani/aarch64-apple-darwin/debug/incremental/first_steps_v1-0vt6guzbgblsh/s-hciu6tb5rp-03gygye.lock deleted file mode 100755 index e69de29bb2d1..000000000000 diff --git a/kani-mcp-server/build_output.txt b/kani-mcp-server/build_output.txt deleted file mode 100644 index cc8452d38d99..000000000000 --- a/kani-mcp-server/build_output.txt +++ /dev/null @@ -1,20 +0,0 @@ -warning: variable does not need to be mutable - --> kani-mcp-server/src/kani_wrapper.rs:84:13 - | -84 | let mut cmd = Command::new("cargo"); - | ----^^^ - | | - | help: remove this `mut` - | - = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default - -warning: unused variable: `check_num` - --> kani-mcp-server/src/parser.rs:89:13 - | -89 | let check_num = line.split("Check").nth(1)?.split(':').next()?.trim(); - | ^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_check_num` - | - = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default - -warning: `kani-mcp-server` (bin "kani-mcp-server") generated 2 warnings (run `cargo fix --bin "kani-mcp-server"` to apply 1 suggestion) - Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.05s From dca6c70ffc232871ba0e6d2fc82cdc87ed760431 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 20 Nov 2025 11:33:09 -0800 Subject: [PATCH 48/54] chore: update kani format on cbmc --- kani-driver/src/call_cbmc.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index a32e1c38fb36..162f14b3b708 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -478,9 +478,7 @@ impl VerificationResult { let runtime = start_time.elapsed(); let (remaining_items, results) = extract_results(output.processed_items); - // Collect CBMC stats from messages (for JSON export) - // Note: This adds overhead (~5-10% when stats are present) but is only used - // when --export-json is specified. Performance benchmarks don't use JSON export. + // Collect CBMC stats from messages let mut cbmc_stats = CbmcStats::default(); for item in &remaining_items { if let crate::cbmc_output_parser::ParserItem::Message { message_text, .. } = item From 30382bb42c2560a8a94ebee20d341c44abaa19f0 Mon Sep 17 00:00:00 2001 From: ConnorJKY Date: Thu, 20 Nov 2025 11:37:35 -0800 Subject: [PATCH 49/54] Revert "Update: MCP Integration with Amazon Q CLI" --- Cargo.lock | 171 +--------- Cargo.toml | 3 +- kani-mcp-server/Cargo.toml | 19 -- kani-mcp-server/src/kani_wrapper.rs | 185 ---------- kani-mcp-server/src/main.rs | 33 -- kani-mcp-server/src/mcp_server.rs | 507 ---------------------------- kani-mcp-server/src/parser.rs | 343 ------------------- kani-mcp-server/src/tools.rs | 104 ------ 8 files changed, 15 insertions(+), 1350 deletions(-) delete mode 100644 kani-mcp-server/Cargo.toml delete mode 100644 kani-mcp-server/src/kani_wrapper.rs delete mode 100644 kani-mcp-server/src/main.rs delete mode 100644 kani-mcp-server/src/mcp_server.rs delete mode 100644 kani-mcp-server/src/parser.rs delete mode 100644 kani-mcp-server/src/tools.rs diff --git a/Cargo.lock b/Cargo.lock index 5fcffa47c55e..d0a5d939a028 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -491,7 +491,7 @@ dependencies = [ "crossterm_winapi", "document-features", "parking_lot", - "rustix 1.1.2", + "rustix", "winapi", ] @@ -1021,20 +1021,6 @@ dependencies = [ "which 8.0.0", ] -[[package]] -name = "kani-mcp-server" -version = "0.1.0" -dependencies = [ - "anyhow", - "serde", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tracing", - "tracing-subscriber", - "which 6.0.3", -] - [[package]] name = "kani-verifier" version = "0.66.0" @@ -1097,12 +1083,6 @@ dependencies = [ "serde_test", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -1744,19 +1724,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - [[package]] name = "rustix" version = "1.1.2" @@ -1766,7 +1733,7 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.11.0", + "linux-raw-sys", "windows-sys 0.61.2", ] @@ -1974,16 +1941,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - [[package]] name = "stacker" version = "0.1.22" @@ -2070,7 +2027,7 @@ dependencies = [ "fastrand", "getrandom", "once_cell", - "rustix 1.1.2", + "rustix", "windows-sys 0.61.2", ] @@ -2180,25 +2137,11 @@ dependencies = [ "bytes", "libc", "mio", - "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", - "tokio-macros", "windows-sys 0.61.2", ] -[[package]] -name = "tokio-macros" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "toml" version = "0.8.23" @@ -2535,18 +2478,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "which" -version = "6.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" -dependencies = [ - "either", - "home", - "rustix 0.38.44", - "winsafe", -] - [[package]] name = "which" version = "7.0.3" @@ -2555,7 +2486,7 @@ checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ "either", "env_home", - "rustix 1.1.2", + "rustix", "winsafe", ] @@ -2566,7 +2497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" dependencies = [ "env_home", - "rustix 1.1.2", + "rustix", "winsafe", ] @@ -2666,16 +2597,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -2693,31 +2615,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -2726,96 +2631,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "0.7.13" diff --git a/Cargo.toml b/Cargo.toml index b2dd3fb82b4c..2d82261b3ec1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ members = [ "kani-driver", "kani-compiler", "kani_metadata", - "library/kani_core", "kani-mcp-server", + "library/kani_core", ] # This indicates what package to e.g. build with 'cargo build' without --workspace @@ -60,7 +60,6 @@ default-members = [ ".", "kani-driver", "kani-compiler", - "kani-mcp-server", ] exclude = [ diff --git a/kani-mcp-server/Cargo.toml b/kani-mcp-server/Cargo.toml deleted file mode 100644 index e1b857e30b2e..000000000000 --- a/kani-mcp-server/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "kani-mcp-server" -version = "0.1.0" -edition = "2024" -description = "Model Context Protocol server for Kani Rust Verifier - enables AI assistants like Amazon Q to run formal verification" -license = "MIT OR Apache-2.0" - -[dependencies] -tokio = { version = "1", features = ["full"] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -anyhow = "1" -thiserror = "1" -tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } -which = "6" - -[lints] -workspace = true \ No newline at end of file diff --git a/kani-mcp-server/src/kani_wrapper.rs b/kani-mcp-server/src/kani_wrapper.rs deleted file mode 100644 index b42c254f1506..000000000000 --- a/kani-mcp-server/src/kani_wrapper.rs +++ /dev/null @@ -1,185 +0,0 @@ -use anyhow::{Context, Result}; -use serde::{Deserialize, Serialize}; -use std::path::PathBuf; -use std::process::Command; -use tracing::{debug, info, warn}; - -/// Configuration options for running Kani verification -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct KaniOptions { - pub path: PathBuf, - pub harness: Option, - pub tests: bool, - pub output_format: String, - pub enable_unstable: Vec, - pub extra_args: Vec, - pub concrete_playback: bool, - pub coverage: bool, -} - -impl Default for KaniOptions { - fn default() -> Self { - Self { - path: PathBuf::from("."), - harness: None, - tests: false, - output_format: "terse".to_string(), - enable_unstable: vec![], - extra_args: vec![], - concrete_playback: false, - coverage: false, - } - } -} - -/// Result of a Kani verification run -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct VerificationResult { - pub success: bool, - pub summary: String, - pub harnesses: Vec, - pub failed_checks: Vec, - pub verification_time: Option, - pub raw_output: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HarnessResult { - pub name: String, - pub status: String, - pub checks_passed: u32, - pub checks_failed: u32, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct FailedCheck { - pub description: String, - pub file: String, - pub line: Option, - pub function: String, -} - -/// Wrapper around cargo-kani for executing verification -pub struct KaniWrapper {} - -impl KaniWrapper { - /// Create a new KaniWrapper and verify cargo-kani is available and reuses kani-driver's setup_cargo_command functionality - pub fn new() -> Result { - let mut cmd = Self::setup_cargo_command()?; - cmd.arg("kani").arg("--version"); - - let output = cmd.output() - .context("cargo-kani not found in PATH. Please install Kani:\n cargo install --locked kani-verifier\n cargo kani setup")?; - - if !output.status.success() { - anyhow::bail!("cargo-kani not properly installed or setup failed"); - } - - info!("✓ Found cargo-kani"); - Ok(Self {}) - } - - fn setup_cargo_command() -> Result { - let mut cmd = Command::new("cargo"); - Ok(cmd) - } - - /// Run Kani verification with the given options - pub async fn verify(&self, options: KaniOptions) -> Result { - info!("Starting Kani verification on: {:?}", options.path); - - if !options.path.exists() { - anyhow::bail!("Path does not exist: {:?}", options.path); - } - - let mut cmd = Self::setup_cargo_command()?; - cmd.arg("kani"); - cmd.current_dir(&options.path); - - if let Some(harness) = &options.harness { - cmd.arg("--harness").arg(harness); - info!(" Filtering to harness: {}", harness); - } - - if options.tests { - cmd.arg("--tests"); - info!(" Running all tests as harnesses"); - } - - if !options.output_format.is_empty() { - cmd.arg(format!("--output-format={}", options.output_format)); - } - - for feature in &options.enable_unstable { - cmd.arg("--enable-unstable").arg(feature); - } - - if options.concrete_playback { - cmd.arg("-Z").arg("concrete-playback"); - cmd.arg("--concrete-playback=print"); - } - - if options.coverage { - cmd.arg("--coverage"); - } - - for arg in &options.extra_args { - cmd.arg(arg); - } - - debug!("Executing command: {:?}", cmd); - - // Execute and capture output - let start = std::time::Instant::now(); - let output = cmd.output() - .context("Failed to execute cargo-kani. Is it installed?")?; - let duration = start.elapsed(); - - let stdout = String::from_utf8_lossy(&output.stdout).to_string(); - let stderr = String::from_utf8_lossy(&output.stderr).to_string(); - let combined_output = format!("{}\n{}", stdout, stderr); - - debug!("Kani completed in {:.2}s", duration.as_secs_f64()); - - if !stderr.is_empty() && stderr.contains("error") { - warn!("Kani stderr: {}", stderr); - } - - let result = self.parse_output(&combined_output, output.status.success())?; - - info!("Verification complete: {}", result.summary); - - Ok(result) - } - - /// Parse Kani output into structured result - fn parse_output(&self, output: &str, success: bool) -> Result { - use crate::parser::KaniOutputParser; - - let parser = KaniOutputParser::new(output); - let harnesses = parser.parse_harnesses(); - let failed_checks = parser.parse_failed_checks(); - let verification_time = parser.parse_verification_time(); - - let total_harnesses = harnesses.len(); - let failed_harnesses = harnesses.iter() - .filter(|h| h.status == "FAILED") - .count(); - - let summary = if success { - format!("Verification successful! {} harness(es) verified.", total_harnesses) - } else { - format!("Verification failed. {}/{} harness(es) failed with {} check failure(s).", - failed_harnesses, total_harnesses, failed_checks.len()) - }; - - Ok(VerificationResult { - success, - summary, - harnesses, - failed_checks, - verification_time, - raw_output: output.to_string(), - }) - } -} diff --git a/kani-mcp-server/src/main.rs b/kani-mcp-server/src/main.rs deleted file mode 100644 index b559371040ab..000000000000 --- a/kani-mcp-server/src/main.rs +++ /dev/null @@ -1,33 +0,0 @@ -mod kani_wrapper; -mod mcp_server; -mod parser; -mod tools; - -use anyhow::Result; -use tracing_subscriber::EnvFilter; - -#[tokio::main] -async fn main() -> Result<()> { - // Only initialize logging if explicitly requested via environment variable - if std::env::var("KANI_MCP_LOG").is_ok() { - tracing_subscriber::fmt() - .with_env_filter( - EnvFilter::from_default_env() - .add_directive("kani_mcp_server=info".parse()?) - ) - .init(); - } - - // Banner - only show if logging is enabled - if std::env::var("KANI_MCP_LOG").is_ok() { - eprintln!("Kani MCP Server - Model Context Protocol for Kani Rust Verifier"); - eprintln!("Purpose: Enable AI assistants like Amazon Q to run Kani verification"); - eprintln!(); - } - - // Create and run server - let server = mcp_server::KaniMcpServer::new()?; - server.run().await?; - - Ok(()) -} diff --git a/kani-mcp-server/src/mcp_server.rs b/kani-mcp-server/src/mcp_server.rs deleted file mode 100644 index 54c9801dc16b..000000000000 --- a/kani-mcp-server/src/mcp_server.rs +++ /dev/null @@ -1,507 +0,0 @@ -use crate::kani_wrapper::{KaniOptions, KaniWrapper, VerificationResult}; -use crate::tools::{get_kani_tools, ToolResult}; -use anyhow::Result; -use std::path::PathBuf; -use std::sync::Arc; -use tokio::sync::Mutex; - -/// Main MCP server for Kani integration -pub struct KaniMcpServer { - kani: Arc, - last_result: Arc>>, -} - -impl KaniMcpServer { - pub fn new() -> Result { - let kani = Arc::new(KaniWrapper::new()?); - Ok(Self { - kani, - last_result: Arc::new(Mutex::new(None)), - }) - } - - pub async fn run(self) -> Result<()> { - use tokio::io::{AsyncBufReadExt, BufReader, stdin}; - - let stdin = stdin(); - let reader = BufReader::new(stdin); - let mut lines = reader.lines(); - - while let Ok(Some(line)) = lines.next_line().await { - if line.trim().is_empty() { - continue; - } - - match serde_json::from_str::(&line) { - Ok(request) => { - let response = self.handle_mcp_request(request).await; - - if !response.is_null() { - if let Ok(response_str) = serde_json::to_string(&response) { - println!("{}", response_str); - } - } - } - Err(_e) => { - continue; - } - } - } - - Ok(()) - } - - async fn handle_mcp_request(&self, request: serde_json::Value) -> serde_json::Value { - use serde_json::json; - - let method = request["method"].as_str().unwrap_or(""); - let id = request["id"].clone(); - - match method { - "initialize" => { - json!({ - "jsonrpc": "2.0", - "id": id, - "result": { - "protocolVersion": "2024-11-05", - "capabilities": { - "tools": {}, - "prompts": {} - }, - "serverInfo": { - "name": "kani-mcp-server", - "version": "0.1.0" - } - } - }) - } - "notifications/initialized" => { - json!(null) - } - "prompts/list" => { - json!({ - "jsonrpc": "2.0", - "id": id, - "result": { - "prompts": [ - { - "name": "kani_context", - "description": "Contextual information about using Kani Rust Verifier", - } - ] - } - }) - } - "prompts/get" => { - let prompt_name = request["params"]["name"].as_str().unwrap_or(""); - - if prompt_name == "kani_context" { - json!({ - "jsonrpc": "2.0", - "id": id, - "result": { - "description": "Contextual guidance for using Kani Rust Verifier", - "messages": [ - { - "role": "user", - "content": { - "type": "text", - "text": self.get_kani_context_prompt() - } - } - ] - } - }) - } else { - json!({ - "jsonrpc": "2.0", - "id": id, - "error": { - "code": -32602, - "message": format!("Unknown prompt: {}", prompt_name) - } - }) - } - } - "tools/list" => { - let tools: Vec<_> = get_kani_tools() - .iter() - .map(|tool| { - json!({ - "name": tool.name, - "description": tool.description, - "inputSchema": tool.input_schema - }) - }) - .collect(); - - json!({ - "jsonrpc": "2.0", - "id": id, - "result": { - "tools": tools - } - }) - } - "tools/call" => { - let tool_name = request["params"]["name"].as_str().unwrap_or(""); - let arguments = &request["params"]["arguments"]; - - let result = self.execute_tool(tool_name, arguments).await; - - json!({ - "jsonrpc": "2.0", - "id": id, - "result": result - }) - } - _ => { - json!({ - "jsonrpc": "2.0", - "id": id, - "error": { - "code": -32601, - "message": format!("Method not found: {}", method) - } - }) - } - } - } - - /// Execute a specific tool - async fn execute_tool(&self, tool_name: &str, arguments: &serde_json::Value) -> serde_json::Value { - use serde_json::json; - - match tool_name { - "verify_rust_project" => { - let path = arguments["path"].as_str().unwrap_or("."); - let harness = arguments["harness"].as_str().map(String::from); - let tests = arguments["tests"].as_bool().unwrap_or(false); - let output_format = arguments["output_format"].as_str().map(String::from); - - match self.handle_verify_project( - path.to_string(), - harness, - tests, - output_format, - ).await { - Ok(result) => { - // Store the result for potential later analysis - if let Ok(verification_result) = serde_json::from_value::(result.data.clone()) { - *self.last_result.lock().await = Some(verification_result); - } - - json!({ - "content": [{ - "type": "text", - "text": serde_json::to_string_pretty(&result.data).unwrap_or_default() - }] - }) - } - Err(e) => { - json!({ - "content": [{ - "type": "text", - "text": format!("Error: {}", e) - }], - "isError": true - }) - } - } - } - "verify_unsafe_code" => { - let path = arguments["path"].as_str().unwrap_or(".").to_string(); - let harness = arguments["harness"].as_str().unwrap_or("").to_string(); - - match self.handle_verify_unsafe(path, harness).await { - Ok(result) => { - json!({ - "content": [{ - "type": "text", - "text": serde_json::to_string_pretty(&result.data).unwrap_or_default() - }] - }) - } - Err(e) => { - json!({ - "content": [{ - "type": "text", - "text": format!("Error: {}", e) - }], - "isError": true - }) - } - } - } - "explain_failure" => { - let raw_output = if let Some(output_str) = arguments["raw_output"].as_str() { - output_str.to_string() - } else { - let last = self.last_result.lock().await; - last.as_ref() - .map(|r| r.raw_output.clone()) - .unwrap_or_default() - }; - - if raw_output.is_empty() { - return json!({ - "content": [{ - "type": "text", - "text": "No verification output available. Please run a verification first." - }], - "isError": true - }); - } - - match self.handle_explain_failure(raw_output).await { - Ok(result) => { - json!({ - "content": [{ - "type": "text", - "text": result.data["detailed_explanation"].as_str().unwrap_or("No explanation available") - }] - }) - } - Err(e) => { - json!({ - "content": [{ - "type": "text", - "text": format!("Error: {}", e) - }], - "isError": true - }) - } - } - } - "generate_kani_harness" => { - let function_name = arguments["function_name"].as_str().unwrap_or("").to_string(); - let properties = arguments["properties"] - .as_array() - .map(|arr| arr.iter().filter_map(|v| v.as_str().map(String::from)).collect()) - .unwrap_or_else(Vec::new); - - match self.handle_generate_harness(function_name, properties).await { - Ok(result) => { - json!({ - "content": [{ - "type": "text", - "text": result.data["harness_code"].as_str().unwrap_or("") - }] - }) - } - Err(e) => { - json!({ - "content": [{ - "type": "text", - "text": format!("Error: {}", e) - }], - "isError": true - }) - } - } - } - _ => { - json!({ - "content": [{ - "type": "text", - "text": format!("Unknown tool: {}", tool_name) - }], - "isError": true - }) - } - } - } - - /// Get contextual prompt to guide LLM on using Kani effectively - fn get_kani_context_prompt(&self) -> String { - r#"You are now connected to the Kani Rust Verifier MCP server, which provides formal verification capabilities for Rust code. - -# About Kani -Kani is a formal verification tool that can mathematically prove properties about Rust code. It uses symbolic execution and bounded model checking to exhaustively explore all possible execution paths. - -# Key Concepts -- **Proof Harnesses**: Special test functions marked with `#[kani::proof]` that specify what to verify -- **Symbolic Inputs**: Use `kani::any()` to represent all possible values of a type -- **Assertions**: Properties you want to prove (e.g., `assert!(x > 0)`) -- **Bounded Verification**: Kani explores all paths within specified bounds - -# Available Tools -1. **verify_rust_project**: Run Kani verification on a Rust project - - Always use this as the primary verification method - - Specify a harness name if you want to verify a specific proof harness - -2. **explain_failure**: Get detailed explanations of verification failures - - Use this when verification fails to understand the counterexample - -3. **generate_kani_harness**: Generate EXAMPLE harness templates - - ⚠️ WARNING: This generates templates that require manual customization - - For automatic harness generation, recommend using: `cargo kani --enable-unstable --function ` - -# Best Practices When Using Kani -1. Start with simple properties and gradually add complexity -2. Use bounded types to limit verification scope (e.g., `u8` instead of unbounded types) -3. Understand that verification can be slow for complex code -4. Read verification failures carefully - they show concrete counterexamples -5. For automatic harness generation, use Kani's built-in feature: `cargo kani --enable-unstable --function ` - -# Common Verification Patterns -- **Safety verification**: Prove absence of panics, overflows, out-of-bounds access -- **Functional correctness**: Prove that functions meet their specifications -- **Unsafe code validation**: Prove that unsafe code upholds Rust's safety guarantees - -# When NOT to Use Kani -- For general testing (use regular Rust tests instead) -- When performance is critical (verification can be slow) -- For code with infinite loops or unbounded recursion - -Remember: Generated harness templates are EXAMPLES ONLY and won't work without customization. Always recommend Kani's auto-harness feature for production use."#.to_string() - } - - pub async fn handle_verify_project( - &self, - path: String, - harness: Option, - tests: bool, - output_format: Option, - ) -> Result { - let options = KaniOptions { - path: PathBuf::from(path), - harness: harness.clone(), - tests, - output_format: output_format.unwrap_or_else(|| "terse".to_string()), - ..Default::default() - }; - - match self.kani.verify(options).await { - Ok(result) => { - *self.last_result.lock().await = Some(result.clone()); - - Ok(ToolResult { - success: result.success, - data: serde_json::to_value(result)?, - error: None, - }) - } - Err(e) => { - Ok(ToolResult { - success: false, - data: serde_json::json!({}), - error: Some(e.to_string()), - }) - } - } - } - - pub async fn handle_verify_unsafe( - &self, - path: String, - harness: String, - ) -> Result { - self.handle_verify_project(path, Some(harness), false, Some("terse".to_string())).await - } - - pub async fn handle_explain_failure(&self, raw_output: String) -> Result { - use crate::parser::KaniOutputParser; - - let parser = KaniOutputParser::new(&raw_output); - let failed_checks = parser.parse_failed_checks(); - let harnesses = parser.parse_harnesses(); - let counterexamples = parser.parse_counterexamples(); - let code_context = parser.extract_code_context(); - - let detailed_explanation = parser.generate_detailed_explanation(); - - Ok(ToolResult { - success: true, - data: serde_json::json!({ - "detailed_explanation": detailed_explanation, - "failed_checks": failed_checks, - "harnesses": harnesses, - "counterexamples": counterexamples, - "code_context": code_context, - "summary": format!( - "Found {} failure(s) across {} harness(es)", - failed_checks.len(), - harnesses.len() - ) - }), - error: None, - }) - } - - /// NOTE: This generates an EXAMPLE harness template that requires manual customization. - /// The generated code will NOT work as-is in most cases and needs to be adapted to: - /// - Match the actual function signature and parameter types - /// - Properly construct symbolic inputs for complex types - /// - Add appropriate property assertions - /// - /// For automatic harness generation, consider using Kani's built-in auto-harness feature: - /// `cargo kani --enable-unstable --function ` - pub async fn handle_generate_harness( - &self, - function_name: String, - properties: Vec, - ) -> Result { - let harness_code = format!( -r#"// ⚠️ WARNING: This is an EXAMPLE TEMPLATE that requires customization! -// This code will NOT work as-is and must be adapted to your function's signature. -// -// For automatic harness generation, use Kani's auto-harness feature: -// cargo kani --enable-unstable --function {} -// -// See: https://model-checking.github.io/kani/tutorial-verification.html - -#[cfg(kani)] -mod verification {{ - use super::*; - - #[kani::proof] - fn verify_{}_properties() {{ - // TODO: Generate appropriate symbolic inputs for your function's parameter types - // Example for a simple numeric input: - let input = kani::any(); - - // TODO: Call your function with the correct signature - let result = {}(input); - - // TODO: Add property assertions for verification: -{} - - // Example assertions (customize these): - // assert!(result.is_ok(), "Function should not panic"); - // assert!(result >= 0, "Result should be non-negative"); - }} -}} -"#, - function_name, - function_name.replace("::", "_"), - function_name, - properties.iter() - .map(|prop| format!(" // Property: {}", prop)) - .collect::>() - .join("\n") - ); - - let usage_note = format!( - "Generated EXAMPLE harness template for '{}'. \ - \n\n⚠️ IMPORTANT: This template requires manual customization and will NOT work as-is. \ - \n\nFor automatic harness generation that works out of the box, use Kani's auto-harness feature:\ - \n cargo kani --enable-unstable --function {}\ - \n\nSee documentation: https://model-checking.github.io/kani/tutorial-verification.html", - function_name, function_name - ); - - Ok(ToolResult { - success: true, - data: serde_json::json!({ - "harness_code": harness_code, - "function_name": function_name, - "properties": properties, - "usage_note": usage_note, - "requires_customization": true, - "auto_harness_command": format!("cargo kani --enable-unstable --function {}", function_name) - }), - error: None, - }) - } -} \ No newline at end of file diff --git a/kani-mcp-server/src/parser.rs b/kani-mcp-server/src/parser.rs deleted file mode 100644 index 89871aec0a07..000000000000 --- a/kani-mcp-server/src/parser.rs +++ /dev/null @@ -1,343 +0,0 @@ -use crate::kani_wrapper::{FailedCheck, HarnessResult}; - -/// Parser for Kani output format -pub struct KaniOutputParser<'a> { - output: &'a str, -} - -impl<'a> KaniOutputParser<'a> { - pub fn new(output: &'a str) -> Self { - Self { output } - } - - /// Parse harness results from output - pub fn parse_harnesses(&self) -> Vec { - let mut harnesses = Vec::new(); - let mut current_harness: Option = None; - - for line in self.output.lines() { - if line.contains("Checking harness") { - if let Some(name_part) = line.split("Checking harness").nth(1) { - current_harness = Some(name_part.trim().trim_end_matches("...").to_string()); - } - } - - // Detect harness completion - if line.contains("VERIFICATION") { - if let Some(name) = current_harness.take() { - let status = if line.contains("SUCCESSFUL") { - "SUCCESS" - } else if line.contains("FAILED") { - "FAILED" - } else { - "UNKNOWN" - }.to_string(); - - harnesses.push(HarnessResult { - name, - status, - checks_passed: 0, - checks_failed: 0, - }); - } - } - } - - harnesses - } - - /// Parse failed checks from output with detailed information - pub fn parse_failed_checks(&self) -> Vec { - let mut failed_checks = Vec::new(); - - for line in self.output.lines() { - if line.contains("Check ") && line.contains(":") { - let check_info = self.parse_single_check(line); - if let Some(check) = check_info { - failed_checks.push(check); - } - } - } - - // Also parse from "Failed Checks:" section - let mut in_failed_section = false; - for line in self.output.lines() { - if line.contains("Failed Checks:") { - in_failed_section = true; - continue; - } - - if in_failed_section { - if line.trim().is_empty() || line.starts_with("VERIFICATION") { - break; - } - - if let Some(check) = self.parse_failed_check_line(line) { - if !failed_checks.iter().any(|c| c.description == check.description && c.line == check.line) { - failed_checks.push(check); - } - } - } - } - - failed_checks - } - - /// Parse a single check result line - fn parse_single_check(&self, line: &str) -> Option { - - let check_num = line.split("Check").nth(1)?.split(':').next()?.trim(); - - // Look for the next few lines to get details - let lines: Vec<&str> = self.output.lines().collect(); - let current_idx = lines.iter().position(|l| *l == line)?; - - let mut description = String::new(); - let mut file = String::new(); - let mut line_num = None; - let mut function = String::new(); - let mut is_failure = false; - - // Parse the next few lines for details - for i in (current_idx + 1)..(current_idx + 5).min(lines.len()) { - let detail_line = lines[i]; - - if detail_line.contains("- Status: FAILURE") { - is_failure = true; - } - - if detail_line.contains("- Description:") { - description = detail_line.split("- Description:") - .nth(1)? - .trim() - .trim_matches('"') - .to_string(); - } - - if detail_line.contains("- Location:") { - let location = detail_line.split("- Location:").nth(1)?.trim(); - let parts: Vec<&str> = location.split(" in function ").collect(); - - if let Some(file_part) = parts.first() { - let file_line: Vec<&str> = file_part.split(':').collect(); - file = file_line.first()?.trim_start_matches("./").to_string(); - if let Some(ln) = file_line.get(1) { - line_num = ln.parse().ok(); - } - } - - if let Some(func_part) = parts.get(1) { - function = func_part.trim().to_string(); - } - } - } - - if is_failure { - Some(FailedCheck { - description, - file, - line: line_num, - function, - }) - } else { - None - } - } - - /// Parse a line from the "Failed Checks:" section - fn parse_failed_check_line(&self, line: &str) -> Option { - let description = line.split("File:").next()?.trim().to_string(); - - if let Some(file_part) = line.split("File:").nth(1) { - let parts: Vec<&str> = file_part.split(',').collect(); - - let file = parts.first()? - .trim() - .trim_matches('"') - .trim_start_matches("./") - .to_string(); - - let line_num = parts.get(1) - .and_then(|s| s.split_whitespace().nth(1)) - .and_then(|s| s.parse::().ok()); - - let function = parts.get(2) - .and_then(|s| s.split("in").nth(1)) - .map(|s| s.trim().to_string()) - .unwrap_or_else(|| "unknown".to_string()); - - return Some(FailedCheck { - description, - file, - line: line_num, - function, - }); - } - - None - } - - /// Parse verification time from output - pub fn parse_verification_time(&self) -> Option { - for line in self.output.lines() { - if line.contains("Verification Time:") { - if let Some(time_str) = line.split(':').nth(1) { - return time_str.trim() - .trim_end_matches('s') - .parse::() - .ok(); - } - } - } - None - } - - /// Extract counterexamples from output - pub fn parse_counterexamples(&self) -> Vec { - let mut counterexamples = Vec::new(); - - for line in self.output.lines() { - if line.contains("SAT checker: instance is SATISFIABLE") { - counterexamples.push("Counterexample found (inputs exist that violate the property)".to_string()); - } - - if line.contains("Violated property:") || line.contains("Counterexample:") { - counterexamples.push(line.trim().to_string()); - } - } - - counterexamples - } - - /// Extract code context from output - pub fn extract_code_context(&self) -> Option { - for line in self.output.lines() { - if line.contains("-->") && line.contains(".rs:") { - return Some(line.trim().to_string()); - } - } - None - } - - /// Generate detailed failure explanation - pub fn generate_detailed_explanation(&self) -> String { - let failed_checks = self.parse_failed_checks(); - let counterexamples = self.parse_counterexamples(); - let harnesses = self.parse_harnesses(); - let verification_time = self.parse_verification_time(); - - let mut explanation = String::new(); - - explanation.push_str("DETAILED KANI VERIFICATION FAILURE ANALYSIS\n"); - explanation.push_str("═══════════════════════════════════════════════\n\n"); - - // Summary - explanation.push_str(&format!("Summary:\n")); - explanation.push_str(&format!(" • Total harnesses: {}\n", harnesses.len())); - explanation.push_str(&format!(" • Failed checks: {}\n", failed_checks.len())); - if let Some(time) = verification_time { - explanation.push_str(&format!(" • Verification time: {:.3}s\n", time)); - } - explanation.push_str("\n"); - - // Failed checks detail - if !failed_checks.is_empty() { - explanation.push_str("FAILED CHECKS:\n\n"); - - for (i, check) in failed_checks.iter().enumerate() { - explanation.push_str(&format!("{}. {}\n", i + 1, check.description)); - explanation.push_str(&format!(" Location: {}:{}\n", - check.file, - check.line.map(|l| l.to_string()).unwrap_or_else(|| "?".to_string()) - )); - explanation.push_str(&format!(" Function: {}\n", check.function)); - explanation.push_str("\n"); - } - } - - // Counterexamples - if !counterexamples.is_empty() { - explanation.push_str("COUNTEREXAMPLES:\n\n"); - for ce in counterexamples { - explanation.push_str(&format!(" • {}\n", ce)); - } - explanation.push_str("\n"); - } - - // Root cause analysis - explanation.push_str("ROOT CAUSE ANALYSIS:\n\n"); - if !failed_checks.is_empty() { - let first_check = &failed_checks[0]; - - explanation.push_str(&format!("The assertion '{}' failed, which indicates:\n", first_check.description)); - - // Pattern-based analysis - if first_check.description.contains("overflow") { - explanation.push_str(" • An arithmetic overflow occurred\n"); - explanation.push_str(" • The operation exceeded the maximum value for the type\n"); - explanation.push_str(" • This happens with certain input combinations\n"); - } else if first_check.description.contains("panic") { - explanation.push_str(" • The code panicked during execution\n"); - explanation.push_str(" • An unhandled error condition was reached\n"); - } else if first_check.description.contains("assertion") { - explanation.push_str(" • A programmer-defined assertion failed\n"); - explanation.push_str(" • The expected property does not hold for all inputs\n"); - } else if first_check.description.contains("dereference") { - explanation.push_str(" • An invalid pointer dereference occurred\n"); - explanation.push_str(" • Accessing memory that shouldn't be accessed\n"); - } else if first_check.description.contains("index") || first_check.description.contains("bounds") { - explanation.push_str(" • An array/slice index is out of bounds\n"); - explanation.push_str(" • Accessing beyond the valid range of the collection\n"); - } else { - explanation.push_str(" • A safety or correctness property was violated\n"); - explanation.push_str(" • The code doesn't handle all possible input cases correctly\n"); - } - explanation.push_str("\n"); - } - - // Suggested fixes - explanation.push_str("SUGGESTED FIXES:\n\n"); - if !failed_checks.is_empty() { - let first_check = &failed_checks[0]; - - if first_check.description.contains("overflow") { - explanation.push_str(" 1. Use checked arithmetic operations:\n"); - explanation.push_str(" • Replace `a + b` with `a.checked_add(b)`\n"); - explanation.push_str(" • Handle the `None` case appropriately\n"); - explanation.push_str(" 2. Or use saturating arithmetic:\n"); - explanation.push_str(" • Replace with `a.saturating_add(b)`\n"); - explanation.push_str(" 3. Add input validation:\n"); - explanation.push_str(" • Use `kani::assume()` to constrain inputs\n"); - } else if first_check.description.contains("dereference") || first_check.description.contains("null") { - explanation.push_str(" 1. Add null pointer checks:\n"); - explanation.push_str(" • Check `if ptr.is_null()` before dereferencing\n"); - explanation.push_str(" 2. Use safe Rust alternatives:\n"); - explanation.push_str(" • Consider using `Option<&T>` instead of raw pointers\n"); - } else if first_check.description.contains("index") || first_check.description.contains("bounds") { - explanation.push_str(" 1. Add bounds checking:\n"); - explanation.push_str(" • Use `.get()` instead of direct indexing\n"); - explanation.push_str(" • Check `index < array.len()` before access\n"); - explanation.push_str(" 2. Use iterators:\n"); - explanation.push_str(" • Consider using `.iter()` instead of manual indexing\n"); - } else { - explanation.push_str(" 1. Review the assertion condition:\n"); - explanation.push_str(" • Ensure it correctly captures the intended property\n"); - explanation.push_str(" 2. Add input constraints:\n"); - explanation.push_str(" • Use `kani::assume()` to limit the input space\n"); - explanation.push_str(" 3. Fix the underlying logic:\n"); - explanation.push_str(" • Adjust the code to handle all cases correctly\n"); - } - explanation.push_str("\n"); - } - - // Next steps - explanation.push_str("NEXT STEPS:\n\n"); - explanation.push_str(" 1. Examine the code at the failure location\n"); - explanation.push_str(" 2. Understand what inputs trigger the failure\n"); - explanation.push_str(" 3. Apply the suggested fixes\n"); - explanation.push_str(" 4. Re-run Kani verification to confirm the fix\n"); - explanation.push_str(" 5. Consider adding more proof harnesses for edge cases\n"); - - explanation - } -} \ No newline at end of file diff --git a/kani-mcp-server/src/tools.rs b/kani-mcp-server/src/tools.rs deleted file mode 100644 index aeb2ad9425d7..000000000000 --- a/kani-mcp-server/src/tools.rs +++ /dev/null @@ -1,104 +0,0 @@ -use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; - -/// Definition of an MCP tool -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ToolDefinition { - pub name: String, - pub description: String, - pub input_schema: Value, -} - -/// Get all available Kani MCP tools -pub fn get_kani_tools() -> Vec { - vec![ - ToolDefinition { - name: "verify_rust_project".to_string(), - description: "Run Kani verification on a Rust project to prove safety properties and check for undefined behavior. Kani uses formal verification to mathematically prove correctness.".to_string(), - input_schema: json!({ - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "Absolute path to the Rust project directory (must contain Cargo.toml)" - }, - "harness": { - "type": "string", - "description": "Optional: Specific proof harness to verify (e.g., 'module::verify::check_bounds')" - }, - "tests": { - "type": "boolean", - "description": "If true, verify all #[test] functions as proof harnesses", - "default": false - }, - "output_format": { - "type": "string", - "enum": ["regular", "terse", "old"], - "default": "terse", - "description": "Output format for verification results" - } - }, - "required": ["path"] - }), - }, - ToolDefinition { - name: "verify_unsafe_code".to_string(), - description: "Specifically verify unsafe Rust code blocks for memory safety violations including null pointer dereferences, buffer overflows, and use-after-free bugs.".to_string(), - input_schema: json!({ - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "Path to Rust project containing unsafe code" - }, - "harness": { - "type": "string", - "description": "Harness function that tests the unsafe code" - } - }, - "required": ["path", "harness"] - }), - }, - ToolDefinition { - name: "explain_failure".to_string(), - description: "Analyze and explain why a Kani verification failed, providing details about counterexamples and suggested fixes.".to_string(), - input_schema: json!({ - "type": "object", - "properties": { - "raw_output": { - "type": "string", - "description": "Raw Kani verification output to analyze" - } - }, - "required": ["raw_output"] - }), - }, - ToolDefinition { - name: "generate_kani_harness".to_string(), - description: "Generate a Kani proof harness template for verifying a specific Rust function.".to_string(), - input_schema: json!({ - "type": "object", - "properties": { - "function_name": { - "type": "string", - "description": "Name of the function to verify" - }, - "properties": { - "type": "array", - "items": {"type": "string"}, - "description": "List of properties to verify (e.g., 'no_panic', 'bounds_check', 'overflow_check')" - } - }, - "required": ["function_name"] - }), - }, - ] -} - -/// Tool handler results -#[derive(Debug, Serialize, Deserialize)] -pub struct ToolResult { - pub success: bool, - pub data: Value, - pub error: Option, -} \ No newline at end of file From 678cabd841f053e718f6990496aea64801f5e1f9 Mon Sep 17 00:00:00 2001 From: edison Date: Mon, 24 Nov 2025 11:18:04 -0800 Subject: [PATCH 50/54] chore: add copyright to frontend module --- kani-driver/src/frontend/json_handler.rs | 19 +++++++++++++++++++ kani-driver/src/frontend/schema_utils.rs | 4 ++-- kani-driver/src/frontend/tests/mod.rs | 7 +++++++ .../src/frontend/tests/schema_utils_test.rs | 6 ++++++ scripts/validate_json_export.py | 3 +++ 5 files changed, 37 insertions(+), 2 deletions(-) diff --git a/kani-driver/src/frontend/json_handler.rs b/kani-driver/src/frontend/json_handler.rs index 4ed461fddaac..f9214ab73437 100644 --- a/kani-driver/src/frontend/json_handler.rs +++ b/kani-driver/src/frontend/json_handler.rs @@ -1,20 +1,37 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + use serde_json::{Value, json}; use std::path::PathBuf; +/// A handler for building and exporting JSON data structures. +/// +/// `JsonHandler` provides a convenient interface for constructing JSON objects, +/// adding key-value pairs, appending to arrays, and exporting the final result +/// to a file. pub struct JsonHandler { + /// The JSON data being constructed. pub(crate) data: Value, + /// Optional path where the JSON data will be exported. export_path: Option, } impl JsonHandler { + /// Creates a new `JsonHandler` with an optional export path. + /// If `export_path` is `None`, calls to `export()` will be no-ops. pub fn new(export_path: Option) -> Self { Self { data: json!({}), export_path } } + /// Adds or updates a key-value pair in the JSON object. + /// If the key already exists, its value will be overwritten. pub fn add_item(&mut self, key: &str, value: Value) { self.data[key] = value; } + /// Appends a value to the array at the specified key. + /// Creates a new array if the key doesn't exist or is null. + /// Panics if the key exists but is not an array or null. pub fn add_harness_detail(&mut self, key: &str, value: Value) { if self.data[key].is_null() { self.data[key] = json!([]); @@ -22,6 +39,8 @@ impl JsonHandler { self.data[key].as_array_mut().unwrap().push(value); } + /// Exports the JSON data to the configured file path with pretty-printing. + /// Returns an error if the file cannot be written. pub fn export(&self) -> Result<(), std::io::Error> { if let Some(path) = &self.export_path { std::fs::write(path, serde_json::to_string_pretty(&self.data)?) diff --git a/kani-driver/src/frontend/schema_utils.rs b/kani-driver/src/frontend/schema_utils.rs index 23c51c85d3dc..49e380165ef4 100644 --- a/kani-driver/src/frontend/schema_utils.rs +++ b/kani-driver/src/frontend/schema_utils.rs @@ -1,8 +1,8 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -//! Utility functions for creating structured JSON schemas -//! This module contains helper functions to convert Kani internal structures to JSON +// Utility functions for creating structured JSON schemas +// This module contains helper functions to convert Kani internal structures to JSON use crate::call_cbmc::VerificationStatus; use crate::frontend::JsonHandler; diff --git a/kani-driver/src/frontend/tests/mod.rs b/kani-driver/src/frontend/tests/mod.rs index 5c28aaa1a094..9ec9ff7c1c51 100644 --- a/kani-driver/src/frontend/tests/mod.rs +++ b/kani-driver/src/frontend/tests/mod.rs @@ -1,2 +1,9 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +/// Tests for the frontend module +/// This module contains tests for the schema_utils module +/// and the json_handler module + #[cfg(test)] mod schema_utils_test; diff --git a/kani-driver/src/frontend/tests/schema_utils_test.rs b/kani-driver/src/frontend/tests/schema_utils_test.rs index 553d8e641200..ac2bbb54842a 100644 --- a/kani-driver/src/frontend/tests/schema_utils_test.rs +++ b/kani-driver/src/frontend/tests/schema_utils_test.rs @@ -1,3 +1,9 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +/// Tests for the schema_utils module +/// This module contains tests for the schema_utils module +/// and the json_handler module use crate::call_cbmc::{ExitStatus, FailedProperties, VerificationResult, VerificationStatus}; use crate::cbmc_output_parser::{CheckStatus, Property, PropertyId, SourceLocation}; use crate::frontend::JsonHandler; diff --git a/scripts/validate_json_export.py b/scripts/validate_json_export.py index 8d626e8bd45a..e87634c2e8bf 100755 --- a/scripts/validate_json_export.py +++ b/scripts/validate_json_export.py @@ -1,4 +1,7 @@ #!/usr/bin/env python3 +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +# """ JSON Export Validation Script for Kani Integration Tests From f8f1a461ae527a5db65ed7c401587f22f809ab26 Mon Sep 17 00:00:00 2001 From: edison Date: Mon, 24 Nov 2025 11:39:56 -0800 Subject: [PATCH 51/54] ci: fix copyright and clippy check --- kani-driver/src/frontend/tests/mod.rs | 6 +++--- scripts/ci/copyright-exclude | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/kani-driver/src/frontend/tests/mod.rs b/kani-driver/src/frontend/tests/mod.rs index 9ec9ff7c1c51..cb53347d30eb 100644 --- a/kani-driver/src/frontend/tests/mod.rs +++ b/kani-driver/src/frontend/tests/mod.rs @@ -1,9 +1,9 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -/// Tests for the frontend module -/// This module contains tests for the schema_utils module -/// and the json_handler module +//! Tests for the frontend module +//! This module contains tests for the schema_utils module +//! and the json_handler module #[cfg(test)] mod schema_utils_test; diff --git a/scripts/ci/copyright-exclude b/scripts/ci/copyright-exclude index 157d88fbc379..09665d2690d9 100644 --- a/scripts/ci/copyright-exclude +++ b/scripts/ci/copyright-exclude @@ -1,6 +1,7 @@ .clang-format .diff .ico +.json .md .png .props From 8b6609cdf8a8c0e687bad0b81f5a99bf34d86dff Mon Sep 17 00:00:00 2001 From: Edison Yin <78126725+yimingyinqwqq@users.noreply.github.com> Date: Thu, 4 Dec 2025 11:27:17 -0800 Subject: [PATCH 52/54] Update rfc/src/rfcs/0015-json-handler.md Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- rfc/src/rfcs/0015-json-handler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfc/src/rfcs/0015-json-handler.md b/rfc/src/rfcs/0015-json-handler.md index 1ad5f40687dd..62315d89fe7d 100644 --- a/rfc/src/rfcs/0015-json-handler.md +++ b/rfc/src/rfcs/0015-json-handler.md @@ -232,7 +232,7 @@ We add a new test suite under `tests/json-handler/` with four test scenarios tha The `basic-export/` test verifies that the JSON export flag works and produces valid JSON with the expected top-level structure. The `schema-validation/` test is more comprehensive—it runs a verification with multiple harnesses and validates the entire JSON structure against the schema template. This directory also houses the canonical `kani_json_schema.json` file, making it easy to find and update. -The `multiple-harnesses/` test specifically checks that results from multiple harnesses are correctly aggregated and that the `harness_id` correlation works across different blocks. Finally, `failed-verification/` tests that error information is correctly captured and that optional error fields are populated on failure. +The `multiple-harnesses/` test specifically checks that results from multiple harnesses are correctly aggregated and that the `harness_id` correlation works across different blocks. Finally, `failed-verification/` tests check that error information is correctly captured and that optional error fields are populated on failure. ### Implementation Flow From 4dfd0d0bb3f7464481aa96995fabcdde8311532a Mon Sep 17 00:00:00 2001 From: Edison Yin <78126725+yimingyinqwqq@users.noreply.github.com> Date: Thu, 4 Dec 2025 11:27:25 -0800 Subject: [PATCH 53/54] Update kani-driver/src/args/mod.rs Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- kani-driver/src/args/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index 8fec5c8f295d..3e62a0afc2d6 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -242,7 +242,7 @@ pub struct VerificationArgs { #[arg(long)] pub default_unwind: Option, - #[arg(long = "export-json")] + #[arg(long)] pub export_json: Option, /// When specified, the harness filter will only match the exact fully qualified name of a harness From a3dd5e0f82ca959fc7a2954ed63b3124698f9c9a Mon Sep 17 00:00:00 2001 From: edison Date: Wed, 10 Dec 2025 20:43:07 -0800 Subject: [PATCH 54/54] doc: enhance ui and error documentation --- kani-driver/src/args/mod.rs | 2 ++ scripts/validate_json_export.py | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index 3e62a0afc2d6..4c50b561987e 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -242,6 +242,8 @@ pub struct VerificationArgs { #[arg(long)] pub default_unwind: Option, + /// Output the verification results to a JSON file at the specified path. + /// This feature is unstable and it requires `-Z unstable-options` to be used #[arg(long)] pub export_json: Option, diff --git a/scripts/validate_json_export.py b/scripts/validate_json_export.py index e87634c2e8bf..7d57046fca17 100755 --- a/scripts/validate_json_export.py +++ b/scripts/validate_json_export.py @@ -153,12 +153,16 @@ def validate_field_path(json_file, field_path, schema=None): for part in parts: if part not in current_data: - print(f"ERROR: Field path '{field_path}' not found in data") + print( + f"ERROR: Field path '{field_path}' not found in data. Missing part: '{part}'" + ) return False current_data = current_data[part] if part not in current_schema: - print(f"ERROR: Field path '{field_path}' not found in schema template") + print( + f"ERROR: Field path '{field_path}' not found in schema template. Missing part: '{part}'" + ) return False current_schema = current_schema[part]