Support verilator_coverage --write-info for lcov HTML reports.

This commit is contained in:
Wilson Snyder 2020-05-16 09:18:35 -04:00
parent 6fd7f45cef
commit d33d0301f8
10 changed files with 136 additions and 5 deletions

View File

@ -5,6 +5,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
* Verilator 4.035 devel
*** Support verilator_coverage --write-info for lcov HTML reports.
**** Support multi channel descriptor I/O (#2190) [Stephen Henry]
**** Support $countbits. (#2287) [Yossi Nivin]

View File

@ -5171,6 +5171,10 @@ For an example, after running 'make test' in the Verilator distribution,
see the examples/make_tracing_c/logs directory. Grep for lines starting
with '%' to see what lines Verilator believes need more coverage.
Info files can be written by verilator_coverage for import to C<lcov>.
This enables use of C<genhtml> for HTML reports and importing reports to
sites such as L<https://codecov.io>.
=item Where is the translate_off command? (How do I ignore a construct?)
Translate on/off pragmas are generally a bad idea, as it's easy to have

View File

@ -156,6 +156,8 @@ verilator_coverage - Verilator coverage analyzer
verilator_coverage -write merged.dat -read <datafiles>...
verilator_coverage -write-info merged.info -read <datafiles>...
Verilator_coverage processes Verilator coverage reports.
With --anotate, it reads the specified data file and generates annotated
@ -224,8 +226,20 @@ Displays program version and exits.
=item --write I<filename>
Specifies the aggregate coverage results, summed across all the files,
should be written to the given filename. This is useful in scripts to
combine many sequential runs into one master coverage file.
should be written to the given filename in verilator_coverage data format.
This is useful in scripts to combine many sequential runs into one master
coverage file.
=item --write-info I<filename.info>
Specifies the aggregate coverage results, summed across all the files,
should be written to the given filename in C<lcov> .info format.
This may be used to use C<lcov> to aggregate or generate reports.
The info format loses data compared to the Verilator coverage data format;
the info will all forms of coverage converted to line style coverage, and
if there are multiple coverage points on a single line, the minimum
coverage across those points will be used to report coverage of the line.
=back
@ -277,7 +291,7 @@ Wilson Snyder <wsnyder@wsnyder.org>
=head1 SEE ALSO
C<verilator>
C<verilator>, C<lcov>
L<verilator_coverage --help> which is the source for this document.

View File

@ -108,6 +108,9 @@ void VlcOptions::parseOptsList(int argc, char** argv) {
} else if (!strcmp(sw, "-write") && (i + 1) < argc) {
shift;
m_writeFile = argv[i];
} else if (!strcmp(sw, "-write-info") && (i + 1) < argc) {
shift;
m_writeInfoFile = argv[i];
} else {
v3fatal("Invalid option: " << argv[i]);
}
@ -170,8 +173,9 @@ int main(int argc, char** argv, char** /*env*/) {
top.tests().dump(false);
}
if (!top.opt.writeFile().empty()) {
top.writeCoverage(top.opt.writeFile());
if (!top.opt.writeFile().empty() || !top.opt.writeInfoFile().empty()) {
if (!top.opt.writeFile().empty()) top.writeCoverage(top.opt.writeFile());
if (!top.opt.writeInfoFile().empty()) top.writeInfo(top.opt.writeInfoFile());
V3Error::abortIfWarnings();
if (top.opt.unlink()) {
const VlStringSet& readFiles = top.opt.readFiles();

View File

@ -42,6 +42,7 @@ class VlcOptions {
bool m_rank; // main switch: --rank
bool m_unlink; // main switch: --unlink
string m_writeFile; // main switch: --write
string m_writeInfoFile; // main switch: --write-info
// clang-format on
private:
@ -72,6 +73,7 @@ public:
bool rank() const { return m_rank; }
bool unlink() const { return m_unlink; }
string writeFile() const { return m_writeFile; }
string writeInfoFile() const { return m_writeInfoFile; }
// METHODS (from main)
static string version();

View File

@ -77,6 +77,61 @@ void VlcTop::writeCoverage(const string& filename) {
}
}
void VlcTop::writeInfo(const string& filename) {
UINFO(2, "writeInfo " << filename << endl);
std::ofstream os(filename.c_str());
if (!os) {
v3fatal("Can't write " << filename);
return;
}
annotateCalc();
// See 'man lcov' for format details
// TN:<trace_file_name>
// Source file:
// SF:<absolute_path_to_source_file>
// FN:<line_number_of_function_start>,<function_name>
// FNDA:<execution_count>,<function_name>
// FNF:<number_functions_found>
// FNH:<number_functions_hit>
// Branches:
// BRDA:<line_number>,<block_number>,<branch_number>,<taken_count_or_-_for_zero>
// BRF:<number_of_branches_found>
// BRH:<number_of_branches_hit>
// Line counts:
// DA:<line_number>,<execution_count>
// LF:<number_of_lines_found>
// LH:<number_of_lines_hit>
// Section ending:
// end_of_record
os << "TN:verilator_coverage\n";
for (VlcSources::NameMap::iterator sit = m_sources.begin(); sit != m_sources.end(); ++sit) {
VlcSource& source = sit->second;
os << "SF:" << source.name() << endl;
VlcSource::LinenoMap& lines = source.lines();
for (VlcSource::LinenoMap::iterator lit = lines.begin(); lit != lines.end(); ++lit) {
int lineno = lit->first;
VlcSource::ColumnMap& cmap = lit->second;
bool first = true;
vluint64_t min_count = 0; // Minimum across all columns on line
for (VlcSource::ColumnMap::iterator cit = cmap.begin(); cit != cmap.end(); ++cit) {
VlcSourceCount& col = cit->second;
if (first) {
min_count = col.count();
first = false;
} else {
min_count = std::min(min_count, col.count());
}
}
os << "DA:" << lineno << "," << min_count << "\n";
}
os << "end_of_record\n";
}
}
//********************************************************************
struct CmpComputrons {

View File

@ -58,6 +58,7 @@ public:
void annotate(const string& dirname);
void readCoverage(const string& filename, bool nonfatal = false);
void writeCoverage(const string& filename);
void writeInfo(const string& filename);
void rank();
};

View File

@ -31,5 +31,24 @@ run(cmd => ["../bin/verilator_coverage",
files_identical("$Self->{obj_dir}/annotated/t_cover_line.v", "t/t_cover_line.out");
# Also try lcov
run(cmd => ["../bin/verilator_coverage",
"--write-info", "$Self->{obj_dir}/coverage.info",
"$Self->{obj_dir}/coverage.dat"],
verilator_run => 1,
);
# If installed
if (`lcov --help` !~ /Usage:/
|| `genhtml --help` !~ /Usage:/) {
skip("lcov or genhtml not installed");
} else {
run(cmd => ["genhtml",
"$Self->{obj_dir}/coverage.info",
"--output-directory $Self->{obj_dir}/html",
]);
}
ok(1);
1;

View File

@ -0,0 +1,4 @@
TN:verilator_coverage
SF:file1.sp
DA:159,53
end_of_record

26
test_regress/t/t_vlcov_info.pl Executable file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003-2009 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(dist => 1);
run(cmd => ["../bin/verilator_coverage",
"--write-info", "$Self->{obj_dir}/coverage.info",
"t/t_vlcov_data_a.dat",
"t/t_vlcov_data_b.dat",
"t/t_vlcov_data_c.dat",
"t/t_vlcov_data_d.dat",
],
verilator_run => 1,
);
files_identical("$Self->{obj_dir}/coverage.info", $Self->{golden_filename});
ok(1);
1;