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 84 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,
"code coverage for location",
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
14 changes: 14 additions & 0 deletions kani-driver/src/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ pub struct VerificationArgs {
)]
pub enable_stubbing: bool,

/// Enable Kani coverage output alongside verification result
#[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 +644,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
14 changes: 12 additions & 2 deletions kani-driver/src/cbmc_output_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,17 @@ pub struct PropertyId {

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

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

// Returns true if this is a code_coverage check
pub fn is_code_coverage_property(&self) -> bool {
self.property_id.class == Self::COVERAGE_PROPERTY_CLASS
}

/// Returns true if this is a cover property
pub fn is_cover_property(&self) -> bool {
self.property_id.class == Self::COVER_PROPERTY_CLASS
Expand Down Expand Up @@ -322,18 +328,22 @@ impl std::fmt::Display for TraceData {
#[serde(rename_all = "UPPERCASE")]
pub enum CheckStatus {
Failure,
Satisfied, // for cover properties only
Covered, // for `code_coverage` properties only
Satisfied, // for `cover` properties only
Success,
Undetermined,
Unreachable,
Unsatisfiable, // for cover properties only
Uncovered, // for `code_coverage` properties only
Unsatisfiable, // for `cover` properties only
}

impl std::fmt::Display for CheckStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let check_str = match self {
CheckStatus::Satisfied => style("SATISFIED").green(),
CheckStatus::Success => style("SUCCESS").green(),
CheckStatus::Covered => style("COVERED").green(),
CheckStatus::Uncovered => style("UNCOVERED").yellow(),
adpaco-aws marked this conversation as resolved.
Show resolved Hide resolved
CheckStatus::Failure => style("FAILURE").red(),
CheckStatus::Unreachable => style("UNREACHABLE").yellow(),
CheckStatus::Undetermined => style("UNDETERMINED").yellow(),
Expand Down
Loading
Loading