|
4 | 4 | use std::str::FromStr; |
5 | 5 |
|
6 | 6 | use crate::args::Timeout; |
| 7 | +use crate::args::autoharness_args::{CargoAutoharnessArgs, StandaloneAutoharnessArgs}; |
7 | 8 | use crate::call_cbmc::VerificationStatus; |
8 | 9 | use crate::call_single_file::to_rustc_arg; |
9 | 10 | use crate::harness_runner::HarnessResult; |
10 | 11 | use crate::session::KaniSession; |
| 12 | +use crate::{InvocationType, print_kani_version, project, verify_project}; |
11 | 13 | use anyhow::Result; |
| 14 | +use comfy_table::Table as PrettyTable; |
| 15 | +use kani_metadata::{AutoHarnessSkipReason, KaniMetadata}; |
12 | 16 |
|
13 | 17 | const AUTOHARNESS_TIMEOUT: &str = "30s"; |
14 | 18 | const LOOP_UNWIND_DEFAULT: u32 = 20; |
15 | 19 |
|
| 20 | +pub fn autoharness_cargo(args: CargoAutoharnessArgs) -> Result<()> { |
| 21 | + let mut session = KaniSession::new(args.verify_opts)?; |
| 22 | + session.enable_autoharness(); |
| 23 | + session.add_default_bounds(); |
| 24 | + session.add_auto_harness_args( |
| 25 | + args.common_autoharness_args.include_function, |
| 26 | + args.common_autoharness_args.exclude_function, |
| 27 | + ); |
| 28 | + let project = project::cargo_project(&mut session, false)?; |
| 29 | + let metadata = project.metadata.clone(); |
| 30 | + let res = verify_project(project, session); |
| 31 | + print_skipped_fns(metadata); |
| 32 | + res |
| 33 | +} |
| 34 | + |
| 35 | +pub fn autoharness_standalone(args: StandaloneAutoharnessArgs) -> Result<()> { |
| 36 | + let mut session = KaniSession::new(args.verify_opts)?; |
| 37 | + session.enable_autoharness(); |
| 38 | + session.add_default_bounds(); |
| 39 | + session.add_auto_harness_args( |
| 40 | + args.common_autoharness_args.include_function, |
| 41 | + args.common_autoharness_args.exclude_function, |
| 42 | + ); |
| 43 | + |
| 44 | + if !session.args.common_args.quiet { |
| 45 | + print_kani_version(InvocationType::Standalone); |
| 46 | + } |
| 47 | + |
| 48 | + let project = project::standalone_project(&args.input, args.crate_name, &session)?; |
| 49 | + let metadata = project.metadata.clone(); |
| 50 | + let res = verify_project(project, session); |
| 51 | + print_skipped_fns(metadata); |
| 52 | + res |
| 53 | +} |
| 54 | + |
| 55 | +/// Print a table of the functions that we skipped and why. |
| 56 | +fn print_skipped_fns(metadata: Vec<KaniMetadata>) { |
| 57 | + let mut skipped_fns = PrettyTable::new(); |
| 58 | + skipped_fns.set_header(vec!["Skipped Function", "Reason for Skipping"]); |
| 59 | + |
| 60 | + for md in metadata { |
| 61 | + let skipped_md = md.autoharness_skipped_fns.unwrap(); |
| 62 | + skipped_fns.add_rows(skipped_md.into_iter().filter_map(|(func, reason)| match reason { |
| 63 | + AutoHarnessSkipReason::MissingArbitraryImpl(ref args) => { |
| 64 | + Some(vec![func, format!("{}: {}", reason, args.join(", "))]) |
| 65 | + } |
| 66 | + AutoHarnessSkipReason::GenericFn |
| 67 | + | AutoHarnessSkipReason::NoBody |
| 68 | + | AutoHarnessSkipReason::UserFilter => Some(vec![func, reason.to_string()]), |
| 69 | + // We don't report Kani implementations to the user to avoid exposing Kani functions we insert during instrumentation. |
| 70 | + // For those we don't insert during instrumentation that are in this category (manual harnesses or Kani trait implementations), |
| 71 | + // it should be obvious that we wouldn't generate harnesses, so reporting those functions as "skipped" is unlikely to be useful. |
| 72 | + AutoHarnessSkipReason::KaniImpl => None, |
| 73 | + })); |
| 74 | + } |
| 75 | + |
| 76 | + if skipped_fns.is_empty() { |
| 77 | + println!( |
| 78 | + "\nSkipped Functions: None. Kani generated automatic harnesses for all functions in the package." |
| 79 | + ); |
| 80 | + return; |
| 81 | + } |
| 82 | + |
| 83 | + println!("\nKani did not generate automatic harnesses for the following functions."); |
| 84 | + println!( |
| 85 | + "If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832" |
| 86 | + ); |
| 87 | + println!("{skipped_fns}"); |
| 88 | +} |
| 89 | + |
16 | 90 | impl KaniSession { |
17 | 91 | /// Enable autoharness mode. |
18 | 92 | pub fn enable_autoharness(&mut self) { |
@@ -52,48 +126,50 @@ impl KaniSession { |
52 | 126 | let failing = failures.len(); |
53 | 127 | let total = succeeding + failing; |
54 | 128 |
|
55 | | - // TODO: it would be nice if we had access to which functions the user included/excluded here |
56 | | - // so that we could print a comparison for them of any of the included functions that we skipped. |
57 | | - println!("Autoharness Summary:"); |
58 | | - println!( |
59 | | - "Note that Kani will only generate an automatic harness for a function if it determines that each of its arguments implement the Arbitrary trait." |
60 | | - ); |
61 | | - println!( |
62 | | - "Examine the summary closely to determine which functions were automatically verified." |
63 | | - ); |
64 | | - if failing > 0 { |
65 | | - println!( |
66 | | - "Also note that Kani sets default --harness-timeout of {AUTOHARNESS_TIMEOUT} and --default-unwind of {LOOP_UNWIND_DEFAULT}." |
67 | | - ); |
68 | | - println!( |
69 | | - "If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract)." |
70 | | - ); |
71 | | - } |
| 129 | + println!("\nAutoharness Summary:"); |
| 130 | + |
| 131 | + let mut verified_fns = PrettyTable::new(); |
| 132 | + verified_fns.set_header(vec![ |
| 133 | + "Selected Function", |
| 134 | + "Kind of Automatic Harness", |
| 135 | + "Verification Result", |
| 136 | + ]); |
72 | 137 |
|
73 | | - // Since autoverification skips over some functions, print the successes to make it easier to see what we verified in one place. |
74 | 138 | for success in successes { |
75 | | - println!("Verification succeeded for - {}", success.harness.pretty_name); |
| 139 | + verified_fns.add_row(vec![ |
| 140 | + success.harness.pretty_name.clone(), |
| 141 | + success.harness.attributes.kind.to_string(), |
| 142 | + success.result.status.to_string(), |
| 143 | + ]); |
76 | 144 | } |
77 | 145 |
|
78 | 146 | for failure in failures { |
79 | | - println!("Verification failed for - {}", failure.harness.pretty_name); |
| 147 | + verified_fns.add_row(vec![ |
| 148 | + failure.harness.pretty_name.clone(), |
| 149 | + failure.harness.attributes.kind.to_string(), |
| 150 | + failure.result.status.to_string(), |
| 151 | + ]); |
80 | 152 | } |
81 | 153 |
|
82 | | - if total > 0 { |
| 154 | + println!("{verified_fns}"); |
| 155 | + |
| 156 | + if failing > 0 { |
83 | 157 | println!( |
84 | | - "Complete - {succeeding} successfully verified functions, {failing} failures, {total} total." |
| 158 | + "Note that `kani autoharness` sets default --harness-timeout of {AUTOHARNESS_TIMEOUT} and --default-unwind of {LOOP_UNWIND_DEFAULT}." |
85 | 159 | ); |
86 | | - } else { |
87 | 160 | println!( |
88 | | - "No functions were eligible for automatic verification. Functions can only be automatically verified if each of their arguments implement kani::Arbitrary." |
| 161 | + "If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract)." |
89 | 162 | ); |
| 163 | + } |
| 164 | + |
| 165 | + if total > 0 { |
90 | 166 | println!( |
91 | | - "If you specified --include-function or --exclude-function, make sure that your filters were not overly restrictive." |
| 167 | + "Complete - {succeeding} successfully verified functions, {failing} failures, {total} total." |
92 | 168 | ); |
| 169 | + } else { |
| 170 | + println!("No functions were eligible for automatic verification."); |
93 | 171 | } |
94 | 172 |
|
95 | | - // Manual harness summary may come afterward, so separate them with a new line. |
96 | | - println!(); |
97 | 173 | Ok(()) |
98 | 174 | } |
99 | 175 | } |
0 commit comments