Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Line coverage reports #2609

Merged
merged 94 commits into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from 69 commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
22751fd
Add coverage and coverage test script
jaisnan Jul 13, 2023
b3a679e
Add coverage to basic blocks (both stmts and terms)
adpaco-aws Jul 13, 2023
487e4bb
Add descriptions to printed lines
jaisnan Jul 13, 2023
4f42a32
Add cover statements to the 1st statement of each basic block (NO TERMS)
adpaco-aws Jul 13, 2023
789cc00
Add cover stmts to the front of each basic block (including terminators)
adpaco-aws Jul 13, 2023
c9ef05c
Add cover before every statement/terminator
adpaco-aws Jul 13, 2023
77da3b4
Add new `coverage` prop. class and method to codegen it
adpaco-aws Jul 14, 2023
3466b7f
Adjustments to post-processing for coverage checks
adpaco-aws Jul 14, 2023
37d9e5f
Merge branch 'main' of https://github.com/model-checking/kani into co…
jaisnan Jul 15, 2023
9543241
Flatten coverage directory
jaisnan Jul 16, 2023
c1d5569
Add coverage test suite
jaisnan Jul 17, 2023
8206ff0
Remove python script
jaisnan Jul 17, 2023
0638f8b
Add coverage-related flags to driver and compiler
adpaco-aws Jul 17, 2023
9b0b866
Format
adpaco-aws Jul 17, 2023
ba72fc5
Codegen assert as assume if not for coverage
adpaco-aws Jul 17, 2023
d94b53e
Add python script for coverage postprocessing
adpaco-aws Jul 18, 2023
b2bc2ca
Merge branch 'proto-cover-every-stmt-term' of https://github.com/adpa…
jaisnan Jul 19, 2023
8602593
Merge branch 'postcov-script' of https://github.com/adpaco-aws/rmc in…
jaisnan Jul 19, 2023
b0a4d25
Remove explicit kani::cover!() calls
jaisnan Jul 19, 2023
cf31e98
Add fixed expected files
jaisnan Jul 19, 2023
54cf004
Add changes to compiletest
jaisnan Jul 19, 2023
265158c
Add copyright string
jaisnan Jul 19, 2023
7945cc1
Remove redundant tests and expected lines
jaisnan Jul 19, 2023
252c776
add copyright string to script
jaisnan Jul 19, 2023
d4842fb
Format python script
jaisnan Jul 19, 2023
c344f80
fix expected
jaisnan Jul 19, 2023
8dad837
change test collection
jaisnan Jul 19, 2023
4f346ed
Use existing output checking function
jaisnan Jul 19, 2023
3d009b2
add coverage output format
jaisnan Jul 20, 2023
36cdaf6
remove os specific strings from expected
jaisnan Jul 20, 2023
815eef5
Fix clippy
jaisnan Jul 20, 2023
cba1909
Remove redundant expected files
jaisnan Jul 20, 2023
6a5749c
Add --coverage flag and remove post-processing script
jaisnan Jul 20, 2023
82184b3
Refactor post-processing
jaisnan Jul 20, 2023
5a1b50a
Add comments
jaisnan Jul 20, 2023
0d4d88d
Remove comments from expected files
jaisnan Jul 20, 2023
c6b681e
Address-PR-comments
jaisnan Jul 21, 2023
c95db7f
Remove assume from codegen_Assert
jaisnan Jul 25, 2023
64b003a
Add check for kani library paths
jaisnan Jul 25, 2023
2124086
Add coverage with verification results
jaisnan Jul 25, 2023
ca000cb
Add FULL,NONE and PARTIAL outputs
jaisnan Jul 25, 2023
ac3a7f5
bless expected files
jaisnan Jul 25, 2023
43c0841
bless expected files
jaisnan Jul 25, 2023
8b29842
Merge branch 'Add-coverage-prototype' of https://github.com/jaisnan/k…
jaisnan Jul 25, 2023
3012579
Add formatting
jaisnan Jul 25, 2023
414a06b
Remove full path from expected
jaisnan Jul 25, 2023
93642e8
Add more tests
jaisnan Jul 25, 2023
52bd8cb
Add better comments
jaisnan Jul 25, 2023
955e46b
Merge branch 'main' into Add-coverage-prototype
jaisnan Jul 25, 2023
59e9181
Remove unnecessary cloning
jaisnan Jul 25, 2023
593e8ca
Merge branch 'Add-coverage-prototype' of https://github.com/jaisnan/k…
jaisnan Jul 25, 2023
b232dad
Merge branch 'main' into Add-coverage-prototype
jaisnan Jul 26, 2023
1d76b5d
Add dependency coverage reports
jaisnan Jul 27, 2023
135715b
Merge branch 'main' into Add-coverage-prototype
jaisnan Jul 27, 2023
dd386e1
Merge branch 'main' into Add-coverage-prototype
jaisnan Jul 27, 2023
823d72a
Refactor coverage cases in `codegen_block`
adpaco-aws Jul 27, 2023
043e236
Add/remove comments in `codegen_block`
adpaco-aws Jul 27, 2023
48b6612
Improve comments for `Coverage` class/methods
adpaco-aws Jul 27, 2023
74dc8e8
Add `assume` to div. overflow check
adpaco-aws Jul 27, 2023
d7e85f2
Use unstable feature flag
adpaco-aws Jul 27, 2023
8d9d5ec
Update doc comment for `COVERAGE_CHECKS`
adpaco-aws Jul 27, 2023
644be2a
Change formatting cond. on coverage
adpaco-aws Jul 27, 2023
0ceecdd
Use `format_coverage`
adpaco-aws Jul 27, 2023
c1fb90c
Changes to comment/visibility of `format_result_coverage`
adpaco-aws Jul 27, 2023
d5f36ef
Rename to `Coverage` to `CodeCoverage`
adpaco-aws Jul 27, 2023
5e206b2
Add formatting
jaisnan Jul 27, 2023
e7ec1fc
dummy commit
jaisnan Jul 27, 2023
88d19f8
Fix post-processing and regression
jaisnan Jul 27, 2023
7bea0d4
Change status parsing
jaisnan Jul 27, 2023
a3aa443
Refactor post-processing function to single loop
jaisnan Jul 27, 2023
ece3554
Add comments
jaisnan Jul 27, 2023
9072fad
Remove unnecessary clone
jaisnan Jul 28, 2023
edd3f86
Add help message and fix typo
jaisnan Jul 28, 2023
b7372e4
Remove redundant tests
jaisnan Jul 28, 2023
e65d5c1
Use strum::Display
jaisnan Jul 28, 2023
d91b354
Address test related comments
jaisnan Jul 28, 2023
235b872
Change description of code coverage property class
jaisnan Jul 28, 2023
aece576
Update status to COVERED, UNCOVERED
jaisnan Jul 28, 2023
f99284e
add comment
jaisnan Jul 28, 2023
d974cf3
Update comments for `CheckStatus`
adpaco-aws Jul 28, 2023
16a5170
Update `format_result_coverage`
adpaco-aws Jul 28, 2023
3d3d391
Update comment on `update_results...coverage_checks`
adpaco-aws Jul 28, 2023
e200b52
Add comments to some tests
adpaco-aws Jul 28, 2023
cd47c9f
Run formatting
jaisnan Jul 28, 2023
a4a7f22
More comments in two tests
adpaco-aws Jul 28, 2023
dd54c09
Comments in one test
adpaco-aws Jul 28, 2023
ee3290f
Make `UNCOVERED` red
adpaco-aws Jul 28, 2023
4235ba6
Rename `CoverStatus` to `CoverageStatus`
adpaco-aws Jul 28, 2023
eea1d25
Rewrite condition for `PARTIAL` status
adpaco-aws Jul 28, 2023
88a7e2e
Rewrite status updates for coverage into match
adpaco-aws Jul 28, 2023
c518bf2
Change test to use `kani::any_where`
adpaco-aws Jul 28, 2023
9a279b5
Change comment
adpaco-aws Jul 28, 2023
1f552de
Remove a bunch of tests
adpaco-aws Jul 28, 2023
c3afce2
Add descriptions to three tests and update results
adpaco-aws Jul 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use tracing::debug;
/// Classifies the type of CBMC `assert`, as different assertions can have different semantics (e.g. cover)
///
/// Each property class should justify its existence with a note about the special handling it recieves.
#[derive(Debug, Clone, EnumString, AsRefStr)]
#[derive(Debug, Clone, EnumString, AsRefStr, PartialEq)]
#[strum(serialize_all = "snake_case")]
pub enum PropertyClass {
/// Overflow panics that can be generated by Intrisics.
Expand All @@ -45,6 +45,19 @@ pub enum PropertyClass {
///
/// SPECIAL BEHAVIOR: "Errors" for this type of assertion just mean "reachable" not failure.
Cover,
/// The class of checks used for code coverage instrumentation. Only needed
/// when working on coverage-related features.
///
/// Do not mistake with `Cover`, they are different:
/// - `CodeCoverage` checks have a fixed condition (`false`) and description.
/// - `CodeCoverage` checks are filtered out from verification results and
/// postprocessed to build coverage reports.
/// - `Cover` checks can be added by users (using the `kani::cover` macro),
/// while `CodeCoverage` checks are not exposed to users (i.e., they are
/// automatically added if running with the coverage option).
///
/// SPECIAL BEHAVIOR: "Errors" for this type of assertion just mean "reachable" not failure.
CodeCoverage,
/// Ordinary (Rust) assertions and panics.
///
/// SPECIAL BEHAVIOR: These assertion failures should be observable during normal execution of Rust code.
Expand Down Expand Up @@ -134,6 +147,21 @@ impl<'tcx> GotocCtx<'tcx> {
self.codegen_assert(cond.not(), PropertyClass::Cover, msg, loc)
}

/// Generate a cover statement for code coverage reports.
pub fn codegen_coverage(&self, span: Span) -> Stmt {
let loc = self.codegen_caller_span(&Some(span));
// Should use Stmt::cover, but currently this doesn't work with CBMC
// unless it is run with '--cover cover' (see
// https://github.com/diffblue/cbmc/issues/6613). So for now use
// `assert(false)`.
self.codegen_assert(
Expr::bool_false(),
PropertyClass::CodeCoverage,
"cover_experiment",
jaisnan marked this conversation as resolved.
Show resolved Hide resolved
loc,
)
}

// The above represent the basic operations we can perform w.r.t. assert/assume/cover
// Below are various helper functions for constructing the above more easily.

Expand Down
38 changes: 34 additions & 4 deletions kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,55 @@ impl<'tcx> GotocCtx<'tcx> {
debug!(?bb, "Codegen basicblock");
self.current_fn_mut().set_current_bb(bb);
let label: String = self.current_fn().find_label(&bb);
let check_coverage = self.queries.check_coverage;
// the first statement should be labelled. if there is no statements, then the
// terminator should be labelled.
match bbd.statements.len() {
0 => {
let term = bbd.terminator();
let tcode = self.codegen_terminator(term);
self.current_fn_mut().push_onto_block(tcode.with_label(label));
// When checking coverage, the `coverage` check should be
// labelled instead.
if check_coverage {
adpaco-aws marked this conversation as resolved.
Show resolved Hide resolved
let span = term.source_info.span;
let cover = self.codegen_coverage(span);
self.current_fn_mut().push_onto_block(cover.with_label(label));
self.current_fn_mut().push_onto_block(tcode);
} else {
self.current_fn_mut().push_onto_block(tcode.with_label(label));
}
}
_ => {
let stmt = &bbd.statements[0];
let scode = self.codegen_statement(stmt);
self.current_fn_mut().push_onto_block(scode.with_label(label));
// When checking coverage, the `coverage` check should be
// labelled instead.
if check_coverage {
let span = stmt.source_info.span;
let cover = self.codegen_coverage(span);
self.current_fn_mut().push_onto_block(cover.with_label(label));
self.current_fn_mut().push_onto_block(scode);
} else {
self.current_fn_mut().push_onto_block(scode.with_label(label));
}

for s in &bbd.statements[1..] {
if check_coverage {
let span = s.source_info.span;
let cover = self.codegen_coverage(span);
self.current_fn_mut().push_onto_block(cover);
}
let stmt = self.codegen_statement(s);
self.current_fn_mut().push_onto_block(stmt);
}
let term = self.codegen_terminator(bbd.terminator());
self.current_fn_mut().push_onto_block(term);
let term = bbd.terminator();
if check_coverage {
let span = term.source_info.span;
let cover = self.codegen_coverage(span);
self.current_fn_mut().push_onto_block(cover);
}
let tcode = self.codegen_terminator(term);
self.current_fn_mut().push_onto_block(tcode);
}
}
self.current_fn_mut().reset_current_bb();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ impl<'tcx> GotocCtx<'tcx> {
let a = fargs.remove(0);
let b = fargs.remove(0);
let div_does_not_overflow = self.div_does_not_overflow(a.clone(), b.clone());
let div_overflow_check = self.codegen_assert(
let div_overflow_check = self.codegen_assert_assume(
div_does_not_overflow,
PropertyClass::ArithmeticOverflow,
format!("attempt to compute {} which would overflow", intrinsic).as_str(),
Expand Down
2 changes: 2 additions & 0 deletions kani-compiler/src/kani_compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ impl Callbacks for KaniCompiler {
let queries = &mut (*self.queries.lock().unwrap());
queries.emit_vtable_restrictions = matches.get_flag(parser::RESTRICT_FN_PTRS);
queries.check_assertion_reachability = matches.get_flag(parser::ASSERTION_REACH_CHECKS);
queries.check_coverage = matches.get_flag(parser::COVERAGE_CHECKS);
queries.output_pretty_json = matches.get_flag(parser::PRETTY_OUTPUT_FILES);
queries.ignore_global_asm = matches.get_flag(parser::IGNORE_GLOBAL_ASM);
queries.write_json_symtab =
Expand All @@ -364,6 +365,7 @@ impl Callbacks for KaniCompiler {
{
queries.stubbing_enabled = true;
}

debug!(?queries, "config end");
}
}
Expand Down
1 change: 1 addition & 0 deletions kani-compiler/src/kani_queries/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub enum ReachabilityType {
#[derive(Debug, Default, Clone)]
pub struct QueryDb {
pub check_assertion_reachability: bool,
pub check_coverage: bool,
pub emit_vtable_restrictions: bool,
pub output_pretty_json: bool,
pub ignore_global_asm: bool,
Expand Down
9 changes: 9 additions & 0 deletions kani-compiler/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ pub const RESTRICT_FN_PTRS: &str = "restrict-vtable-fn-ptrs";
/// Option name used to enable assertion reachability checks.
pub const ASSERTION_REACH_CHECKS: &str = "assertion-reach-checks";

/// Option name used to enable coverage checks.
pub const COVERAGE_CHECKS: &str = "coverage-checks";

/// Option name used to use json pretty-print for output files.
pub const PRETTY_OUTPUT_FILES: &str = "pretty-json-files";

Expand Down Expand Up @@ -98,6 +101,12 @@ pub fn parser() -> Command {
.help("Check the reachability of every assertion.")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(COVERAGE_CHECKS)
.long(COVERAGE_CHECKS)
.help("Check the reachability of every statement.")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(REACHABILITY)
.long(REACHABILITY)
Expand Down
2 changes: 2 additions & 0 deletions kani-driver/src/args/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub enum UnstableFeatures {
ConcretePlayback,
/// Enable Kani's unstable async library.
AsyncLib,
/// Enable line coverage instrumentation/reports
LineCoverage,
}

impl ValidateArgs for CommonArgs {
Expand Down
13 changes: 13 additions & 0 deletions kani-driver/src/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,9 @@ pub struct VerificationArgs {
)]
pub enable_stubbing: bool,

#[arg(long, hide_short_help = true)]
jaisnan marked this conversation as resolved.
Show resolved Hide resolved
pub coverage: bool,

/// Arguments to pass down to Cargo
#[command(flatten)]
pub cargo: CargoCommonArgs,
Expand Down Expand Up @@ -640,6 +643,16 @@ impl ValidateArgs for VerificationArgs {
}
}

if self.coverage
&& !self.common_args.unstable_features.contains(&UnstableFeatures::LineCoverage)
{
return Err(Error::raw(
ErrorKind::MissingRequiredArgument,
"The `--coverage` argument is unstable and requires `-Z \
line-coverage` to be used.",
));
}

Ok(())
}
}
Expand Down
17 changes: 13 additions & 4 deletions kani-driver/src/call_cbmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::args::{OutputFormat, VerificationArgs};
use crate::cbmc_output_parser::{
extract_results, process_cbmc_output, CheckStatus, ParserItem, Property, VerificationOutput,
};
use crate::cbmc_property_renderer::{format_result, kani_cbmc_output_filter};
use crate::cbmc_property_renderer::{format_coverage, format_result, kani_cbmc_output_filter};
use crate::session::KaniSession;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -303,14 +303,23 @@ impl VerificationResult {
}
}

pub fn render(&self, output_format: &OutputFormat, should_panic: bool) -> String {
pub fn render(
&self,
output_format: &OutputFormat,
should_panic: bool,
coverage_mode: 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 =
format_result(results, status, should_panic, failed_properties, show_checks);

let mut result = if coverage_mode {
format_coverage(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
}
Expand Down
4 changes: 4 additions & 0 deletions kani-driver/src/call_single_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ impl KaniSession {
flags.push("--enable-stubbing".into());
}

if self.args.coverage {
flags.push("--coverage-checks".into());
}

flags.extend(
self.args
.common_args
Expand Down
4 changes: 3 additions & 1 deletion kani-driver/src/cbmc_output_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,16 @@ pub struct PropertyId {

impl Property {
const COVER_PROPERTY_CLASS: &str = "cover";
const COVERAGE_PROPERTY_CLASS: &str = "coverage";

pub fn property_class(&self) -> String {
self.property_id.class.clone()
}

/// Returns true if this is a cover property
pub fn is_cover_property(&self) -> bool {
self.property_id.class == Self::COVER_PROPERTY_CLASS
self.property_id.class == Self::COVERAGE_PROPERTY_CLASS
jaisnan marked this conversation as resolved.
Show resolved Hide resolved
|| self.property_id.class == Self::COVER_PROPERTY_CLASS
}

pub fn property_name(&self) -> String {
Expand Down
98 changes: 97 additions & 1 deletion kani-driver/src/cbmc_property_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use console::style;
use once_cell::sync::Lazy;
use regex::Regex;
use rustc_demangle::demangle;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};

type CbmcAltDescriptions = HashMap<&'static str, Vec<(&'static str, Option<&'static str>)>>;

Expand Down Expand Up @@ -421,6 +421,102 @@ pub fn format_result(
result_str
}

/// Seperate checks into coverage and non-coverage based on property class and format them seperately for --coverage. We report both verification and processed coverage
jaisnan marked this conversation as resolved.
Show resolved Hide resolved
/// results
pub fn format_coverage(
properties: &[Property],
status: VerificationStatus,
should_panic: bool,
failed_properties: FailedProperties,
show_checks: bool,
) -> String {
let non_coverage_checks: Vec<Property> =
properties.iter().filter(|&x| x.property_class() != "code_coverage").cloned().collect();
let coverage_checks: Vec<Property> =
jaisnan marked this conversation as resolved.
Show resolved Hide resolved
properties.iter().filter(|&x| x.property_class() == "code_coverage").cloned().collect();

let verification_output =
format_result(&non_coverage_checks, status, should_panic, failed_properties, show_checks);
let coverage_output = format_result_coverage(&coverage_checks);
print!("printing coverage output = {}", coverage_output);
jaisnan marked this conversation as resolved.
Show resolved Hide resolved
jaisnan marked this conversation as resolved.
Show resolved Hide resolved
let result = format!("{}\n{}", verification_output, coverage_output);

result
}

/// Generate coverage result from all coverage properties (i.e., the checks with "coverage" property class).
/// To be used when the user requests coverage information with --coverage. The output is tested through the coverage-based testing suite, not the regular expected suite.
/// Loops through each of the check with a coverage property class and gives a status of FULL if all checks pertaining
/// to a line number are SATISFIED. Similarly, it gives a status of NONE if all checks related to a line are UNSAT. If a line has both, it reports PARTIAL coverage.
fn format_result_coverage(properties: &[Property]) -> String {
let mut formatted_output = String::new();
formatted_output.push_str("\nCoverage Results:\n");

let mut coverage_checks: Vec<&Property> = properties.iter().collect();

coverage_checks.sort_by_key(|check| (&check.source_location.file, &check.source_location.line));

let mut files: HashMap<String, Vec<(usize, CheckStatus)>> = HashMap::new();
jaisnan marked this conversation as resolved.
Show resolved Hide resolved
for check in coverage_checks {
// Get line number and filename
let line_number: usize = check.source_location.line.as_ref().unwrap().parse().unwrap();
let file_name: String = check.source_location.file.as_ref().unwrap().to_string();

// Add to the files lookup map
files.entry(file_name.clone()).or_insert_with(Vec::new).push((line_number, check.status));
}

let mut coverage_results: HashMap<String, Vec<(usize, String)>> = HashMap::new();

for (file, val) in files {
let mut lines: HashSet<usize> = HashSet::new();
let mut line_results: Vec<(usize, String)> = Vec::new();
for check in val.clone() {
lines.insert(check.0);
}

// For each of these lines, create a map from line -> status
// example - {3 -> ["SAT", "UNSAT"], 4 -> ["UNSAT"] ...}
for line in lines.iter() {
let is_line_satisfied: Vec<_> = val
.iter()
.filter(|(line_number_accumulated, _)| *line == *line_number_accumulated)
.collect();

// Report lines as FULL if all of the coverage checks (property class = code_coverage) say FAILURE, NONE if all of the coverage checks say SUCCESS,
// and PARTIAL if there is a mix of the two
let covered_status: String = if is_line_satisfied
.iter()
.all(|&is_satisfiable| is_satisfiable.1.to_string().contains("FAILURE"))
{
"FULL".to_string()
} else if is_line_satisfied
.iter()
.all(|&is_satisfiable| is_satisfiable.1.to_string().contains("SUCCESS"))
{
"NONE".to_string()
} else {
"PARTIAL".to_string()
};

line_results.push((*line, covered_status));
}

line_results.sort_by_key(|&(line, _)| line);
coverage_results.insert(file.clone(), line_results);
}

// Create formatted string that is returned to the user as output
for (file, checks) in coverage_results.iter() {
for (line_number, coverage_status) in checks {
formatted_output.push_str(&format!("{}, {}, {}\n", file, line_number, coverage_status));
}
formatted_output.push('\n');
}

formatted_output
}

/// Attempts to build a message for a failed property with as much detailed
/// information on the source location as possible.
fn build_failure_message(description: String, trace: &Option<Vec<TraceItem>>) -> String {
Expand Down
6 changes: 5 additions & 1 deletion kani-driver/src/harness_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ impl KaniSession {
if !self.args.common_args.quiet && self.args.output_format != OutputFormat::Old {
println!(
"{}",
result.render(&self.args.output_format, harness.attributes.should_panic)
result.render(
&self.args.output_format,
harness.attributes.should_panic,
self.args.coverage
)
);
}
self.gen_and_add_concrete_playback(harness, &mut result)?;
Expand Down
Loading
Loading