* [igt-dev] [PATCH i-g-t 01/12] code_cov_parse_info: silent some messages by default
2023-01-17 14:05 [igt-dev] [PATCH i-g-t 00/12] Improve code coverage tool Mauro Carvalho Chehab
@ 2023-01-17 14:05 ` Mauro Carvalho Chehab
2023-01-17 14:05 ` [igt-dev] [PATCH i-g-t 02/12] code_cov_parse_info: do some renames to make it more coherent Mauro Carvalho Chehab
` (10 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Mauro Carvalho Chehab @ 2023-01-17 14:05 UTC (permalink / raw)
To: igt-dev
From: Mauro Carvalho Chehab <mchehab@kernel.org>
Those aren't really needed on normal output.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
scripts/code_cov_parse_info | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index 72a1e8c2b0ad..1498bd09cdba 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -1030,7 +1030,7 @@ if ($has_filter) {
}
my $ntests=scalar(%test_names);
-printf "Number of tests: %d\n", $ntests if ($ntests > 1);
+printf "Number of tests: %d\n", $ntests if ($stat && $ntests > 1);
if ($show_files) {
for my $f(sort keys %used_source) {
--
2.39.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [igt-dev] [PATCH i-g-t 02/12] code_cov_parse_info: do some renames to make it more coherent
2023-01-17 14:05 [igt-dev] [PATCH i-g-t 00/12] Improve code coverage tool Mauro Carvalho Chehab
2023-01-17 14:05 ` [igt-dev] [PATCH i-g-t 01/12] code_cov_parse_info: silent some messages by default Mauro Carvalho Chehab
@ 2023-01-17 14:05 ` Mauro Carvalho Chehab
2023-01-17 14:05 ` [igt-dev] [PATCH i-g-t 03/12] code_cov_parse_info: use numberic sort for line numbers Mauro Carvalho Chehab
` (9 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Mauro Carvalho Chehab @ 2023-01-17 14:05 UTC (permalink / raw)
To: igt-dev
From: Mauro Carvalho Chehab <mchehab@kernel.org>
Let the regex array to be clearer about include regexes, and
coherent with exclude ones.
While here, also rename the file exclude check function, to
have a name closer to its functions counterpart.
No functional changes.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
scripts/code_cov_parse_info | 38 ++++++++++++++++++-------------------
1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index 1498bd09cdba..d3739211b68a 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -18,10 +18,10 @@ my %all_line;
my %used_source;
my %record;
my %files;
-my @func_regexes;
+my @func_include_regexes;
my @func_exclude_regexes;
my %test_names;
-my @src_regexes;
+my @src_include_regexes;
my @src_exclude_regexes;
my $verbose = 0;
@@ -30,7 +30,7 @@ my $skip_func = 0;
sub is_function_excluded($)
{
- return 0 if (!@func_regexes && !@func_exclude_regexes);
+ return 0 if (!@func_include_regexes && !@func_exclude_regexes);
my $func = shift;
@@ -38,28 +38,28 @@ sub is_function_excluded($)
return 1 if ($func =~ m/$r/);
}
- return 0 if (!@func_regexes);
+ return 0 if (!@func_include_regexes);
- foreach my $r (@func_regexes) {
+ foreach my $r (@func_include_regexes) {
return 0 if ($func =~ m/$r/);
}
return 1;
}
-sub filter_file($)
+sub is_file_excluded($)
{
my $s = shift;
- return 0 if (!@src_regexes && !@src_exclude_regexes);
+ return 0 if (!@src_include_regexes && !@src_exclude_regexes);
foreach my $r (@src_exclude_regexes) {
return 1 if ($s =~ m/$r/);
}
- return 0 if (!@src_regexes);
+ return 0 if (!@src_include_regexes);
- foreach my $r (@src_regexes) {
+ foreach my $r (@src_include_regexes) {
return 0 if ($s =~ m/$r/);
}
@@ -107,7 +107,7 @@ sub parse_info_data($)
$files{$source} = 1;
# Just ignore files explictly set as such
- $ignore = filter_file($source);
+ $ignore = is_file_excluded($source);
next;
}
@@ -189,7 +189,7 @@ sub parse_info_data($)
# Ignore DA/BRDA that aren't associated with functions
# Those are present on header files (maybe defines?)
- next if (@func_regexes && !$has_func);
+ next if (@func_include_regexes && !$has_func);
# FNF:<number of functions found>
if (m/^FNF:(-?\d+)/) {
@@ -899,10 +899,10 @@ GetOptions(
"only-i915|only_i915" => \$only_i915,
"only-drm|only_drm" => \$only_drm,
"func-filters|f=s" => \$func_filters,
- "include-func=s" => \@func_regexes,
+ "include-func=s" => \@func_include_regexes,
"exclude-func=s" => \@func_exclude_regexes,
"source-filters|S=s" => \$src_filters,
- "include-source=s" => \@src_regexes,
+ "include-source=s" => \@src_include_regexes,
"exclude-source=s" => \@src_exclude_regexes,
"show-files|show_files" => \$show_files,
"show-lines|show_lines" => \$show_lines,
@@ -935,9 +935,9 @@ my $str;
if ($only_i915) {
# Please keep in sync with the documentation
push @src_exclude_regexes, "selftest";
- push @src_regexes, "drm/i915";
- push @src_regexes, "drm/ttm";
- push @src_regexes, "drm/vgem";
+ push @src_include_regexes, "drm/i915";
+ push @src_include_regexes, "drm/ttm";
+ push @src_include_regexes, "drm/vgem";
}
if ($only_drm) {
@@ -946,21 +946,21 @@ if ($only_drm) {
push @src_exclude_regexes, "^/drm/";
}
-$str = open_filter_file($func_filters, \@func_regexes, \@func_exclude_regexes);
+$str = open_filter_file($func_filters, \@func_include_regexes, \@func_exclude_regexes);
if ($str) {
$filter_str .= "," if ($filter_str ne "");
$filter_str .= " function regex ($str)";
$has_filter = 1;
}
-$str = open_filter_file($src_filters, \@src_regexes, \@src_exclude_regexes);
+$str = open_filter_file($src_filters, \@src_include_regexes, \@src_exclude_regexes);
if ($str) {
$filter_str .= "," if ($filter_str ne "");
$filter_str .= " source regex ($str)";
$has_filter = 1;
}
-$ignore_unused = 1 if (@func_regexes || @func_exclude_regexes);
+$ignore_unused = 1 if (@func_include_regexes || @func_exclude_regexes);
if ($ignore_unused) {
$filter_str .= "," if ($filter_str ne "");
--
2.39.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [igt-dev] [PATCH i-g-t 03/12] code_cov_parse_info: use numberic sort for line numbers
2023-01-17 14:05 [igt-dev] [PATCH i-g-t 00/12] Improve code coverage tool Mauro Carvalho Chehab
2023-01-17 14:05 ` [igt-dev] [PATCH i-g-t 01/12] code_cov_parse_info: silent some messages by default Mauro Carvalho Chehab
2023-01-17 14:05 ` [igt-dev] [PATCH i-g-t 02/12] code_cov_parse_info: do some renames to make it more coherent Mauro Carvalho Chehab
@ 2023-01-17 14:05 ` Mauro Carvalho Chehab
2023-01-17 14:05 ` [igt-dev] [PATCH i-g-t 04/12] code_cov_parse_info: better handle include regexes Mauro Carvalho Chehab
` (8 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Mauro Carvalho Chehab @ 2023-01-17 14:05 UTC (permalink / raw)
To: igt-dev
From: Mauro Carvalho Chehab <mchehab@kernel.org>
The DA and BRDA information is originally numerically sorted.
Sort it the same way at the output data.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
scripts/code_cov_parse_info | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index d3739211b68a..ef44229983b6 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -284,6 +284,21 @@ sub parse_info_data($)
close IN or die;
}
+sub sort_where($$)
+{
+ my @a = split ",", shift;
+ my @b = split ",", shift;
+ my $ret;
+
+ $ret = $a[0] <=> $b[0];
+ return $ret if ($ret);
+
+ $ret = $a[1] <=> $b[1];
+ return $ret if ($ret);
+
+ return $a[2] <=> $b[2];
+}
+
sub write_filtered_file($)
{
my $filter = shift;
@@ -325,10 +340,10 @@ sub write_filtered_file($)
}
}
- foreach my $ln(sort keys %{ $record{$source}{$func}{da} }) {
+ foreach my $ln(sort { $a <=> $b } keys %{ $record{$source}{$func}{da} }) {
$filtered .= "DA:$ln," . $record{$source}{$func}{da}{$ln} . "\n";
}
- foreach my $where(sort keys %{ $record{$source}{$func}{brda} }) {
+ foreach my $where(sort sort_where keys %{ $record{$source}{$func}{brda} }) {
my $taken = $record{$source}{$func}{brda}{$where};
$taken = "-" if (!$taken);
$filtered .= "BRDA:$where,$taken\n";
--
2.39.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [igt-dev] [PATCH i-g-t 04/12] code_cov_parse_info: better handle include regexes
2023-01-17 14:05 [igt-dev] [PATCH i-g-t 00/12] Improve code coverage tool Mauro Carvalho Chehab
` (2 preceding siblings ...)
2023-01-17 14:05 ` [igt-dev] [PATCH i-g-t 03/12] code_cov_parse_info: use numberic sort for line numbers Mauro Carvalho Chehab
@ 2023-01-17 14:05 ` Mauro Carvalho Chehab
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 05/12] code_cov_parse_info: add a tool to analyze branch coverage Mauro Carvalho Chehab
` (7 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Mauro Carvalho Chehab @ 2023-01-17 14:05 UTC (permalink / raw)
To: igt-dev
From: Mauro Carvalho Chehab <mchehab@kernel.org>
When there are both exclude and include regexes, the include ones
may be overriding the more generic excluded ones. So, handle them
first.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
scripts/code_cov_parse_info | 38 ++++++++++++++++++++-----------------
1 file changed, 21 insertions(+), 17 deletions(-)
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index ef44229983b6..3a503423861d 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -34,17 +34,24 @@ sub is_function_excluded($)
my $func = shift;
+ # Handle includes first, as, when there are both include and exclude
+ # includes should take preference, as they can be overriding exclude
+ # rules
+ foreach my $r (@func_include_regexes) {
+ return 0 if ($func =~ m/$r/);
+ }
+
foreach my $r (@func_exclude_regexes) {
return 1 if ($func =~ m/$r/);
}
- return 0 if (!@func_include_regexes);
-
- foreach my $r (@func_include_regexes) {
- return 0 if ($func =~ m/$r/);
+ # If there are no exclude regexes, only include functions that are
+ # explicitly included.
+ if ($#func_exclude_regexes == 0) {
+ return 1;
}
- return 1;
+ return 0;
}
sub is_file_excluded($)
@@ -1193,9 +1200,10 @@ Each line at B<[filter's file]> may contain a new regex:
=back
-When both include and exclude regexes are found, exclude regexes are
-applied first and any functions that don't match the include regular
-expressions from the B<[filter's file]> will be ignored.
+Include regexes are handled first, as they can override an exclude regex.
+
+When just include regexes are used, any functions that don't match the
+include regular expressions from the B<[filter's file]> will be ignored.
Please notice that, when this filter is used, B<--ignore-unused> will be
automaticaly enabled, as the final goal is to report per-function usage.
@@ -1204,7 +1212,8 @@ automaticaly enabled, as the final goal is to report per-function usage.
Include B<regex> to the function filter. Can be used multiple times.
-When used together with B<--func-filters>, regexes here are handled first.
+When used together with B<--func-filters> or B<--exclude-func>, regexes
+here are handled first.
Please notice that, when this filter is used, B<--ignore-unused> will be
automaticaly enabled, as the final goal is to report per-function usage.
@@ -1213,8 +1222,6 @@ automaticaly enabled, as the final goal is to report per-function usage.
Include B<regex> to the function filter. Can be used multiple times.
-When used together with B<--func-filters>, regexes here are handled first.
-
Please notice that, when this filter is used, B<--ignore-unused> will be
automaticaly enabled, as the final goal is to report per-function usage.
@@ -1244,22 +1251,19 @@ Each line at B<[filter's file]> may contain a new regex:
=back
-When both include and exclude regexes are found, exclude regexes are
-applied first and any functions that don't match the include regular
-expressions from the B<[filter's file]> will be ignored.
+Include regexes are handled first, as they can override an exclude regex.
=item B<--include-src> B<regex>
Include B<regex> to the sources filter. Can be used multiple times.
-When used together with B<--src-filters>, regexes here are handled first.
+When used together with B<--src-filters> and B<--exclude-src>, regexes
+here are handled first.
=item B<--exclude-src> B<regex>
Include B<regex> to the sources filter. Can be used multiple times.
-When used together with B<--src-filters>, regexes here are handled first.
-
=item B<--ignore-unused> or B<--ignore_unused>
Filters out unused C files and headers from the code coverage results.
--
2.39.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [igt-dev] [PATCH i-g-t 05/12] code_cov_parse_info: add a tool to analyze branch coverage
2023-01-17 14:05 [igt-dev] [PATCH i-g-t 00/12] Improve code coverage tool Mauro Carvalho Chehab
` (3 preceding siblings ...)
2023-01-17 14:05 ` [igt-dev] [PATCH i-g-t 04/12] code_cov_parse_info: better handle include regexes Mauro Carvalho Chehab
@ 2023-01-17 14:06 ` Mauro Carvalho Chehab
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 06/12] code_cov_parse_info: add support for parsing JSON files Mauro Carvalho Chehab
` (6 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Mauro Carvalho Chehab @ 2023-01-17 14:06 UTC (permalink / raw)
To: igt-dev
From: Mauro Carvalho Chehab <mchehab@kernel.org>
Branch coverage is trickier than functions. Add a tool that displays
the starting line that it is not being covered, together with up
to 10 context lines before.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
scripts/code_cov_parse_info | 85 ++++++++++++++++++++++++++++++++++++-
1 file changed, 84 insertions(+), 1 deletion(-)
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index 3a503423861d..3982dbd513c4 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -894,6 +894,76 @@ sub generate_report($)
close OUT;
}
+sub check_source_branches()
+{
+ foreach my $source (sort keys(%all_branch)) {
+ next if (!$used_source{$source});
+ next if (is_file_excluded($source));
+
+ my @lines;
+ foreach my $where (sort keys %{$all_branch{$source}}) {
+ my $taken = $all_branch{$source}{$where};
+ next if ($taken > 0);
+
+ next if !($where =~ m/^(-?\d+),(-?\d+),(-?\d+)/);
+
+ my ($ln, $block, $branch, $ctx_lines);
+ $ln = $1;
+ $block = $2;
+ $branch = $3;
+
+ if (!@lines) {
+ open IN, "$source";
+ @lines = <IN>;
+ close IN;
+ }
+
+ if ($ln >= $#lines) {
+ print "Error: $ln is bigger than $#lines. Can't print branch!\n";
+ }
+
+ my $func = $all_branch{$source}{$where}{func};
+ my $func_ln = $all_branch{$source}{$where}{func_ln};
+
+ print "Branch $source:$ln, ";
+ if ($func) {
+ if ($func_ln) {
+ print "func: $func, ";
+ } else {
+ print "func: $func:$func_ln, ";
+ }
+ }
+ print "block $block, branch $branch not taken:\n";
+
+ if ($func_ln) {
+ $ctx_lines = $ln - $func_ln;
+ } else {
+ $ctx_lines = 10;
+ }
+
+
+ # Search for up to $ctx_lines before the occurrence
+ my $context = "";
+ my $has_if;
+ $ln-- if ($ln > 0);
+ my $delim = "->";
+ for (my $if_ln = $ln; $if_ln >= 0 && (($ln - $if_ln) < $ctx_lines); $if_ln--) {
+ $context = "$if_ln$delim\t$lines[$if_ln]" . $context;
+ $delim = "";
+ if ($lines[$if_ln] =~ m/(\bif\b|#if)/) {
+ $has_if = 1;
+ last;
+ }
+ }
+ if ($has_if) {
+ print $context;
+ } else {
+ print "$ln->\t$lines[$ln]";
+ }
+ }
+ }
+}
+
#
# Argument handling
#
@@ -910,6 +980,7 @@ my $show_files;
my $show_lines;
my $only_i915;
my $only_drm;
+my $check_branches;
GetOptions(
"print-coverage|print_coverage|print|p" => \$print_used,
@@ -929,6 +1000,7 @@ GetOptions(
"show-files|show_files" => \$show_files,
"show-lines|show_lines" => \$show_lines,
"report|r=s" => \$gen_report,
+ "check-branches" => \$check_branches,
"css-file|css|c=s" => \$css_file,
"title|t=s" => \$title,
"html-prolog|prolog=s" => \$html_prolog,
@@ -946,7 +1018,7 @@ if ($#ARGV < 0) {
}
# At least one action should be specified
-pod2usage(1) if (!$print_used && !$filter && !$stat && !$print_unused && !$gen_report);
+pod2usage(1) if (!$print_used && !$filter && !$stat && !$print_unused && !$gen_report && !$check_branches);
pod2usage(1) if ($gen_report && ($print_used || $filter || $stat || $print_unused));
@@ -1027,6 +1099,11 @@ if ($gen_report) {
gen_stats();
+
+if ($check_branches) {
+ check_source_branches();
+}
+
die "Nothing counted. Wrong input files?" if (!$stats{"all_files"});
print_code_coverage($print_used, $print_unused, $show_lines);
@@ -1284,6 +1361,12 @@ This option is automaticaly enabled when B<--func-filters> is used.
Shows the list of files that were used to produce the code coverage
results.
+=item B<--check-branches>
+
+Checks at the Linux Kernel source files what's the contents of the
+branches that weren't taken. The directory should match what's
+stored inside the info files.
+
=item B<--verbose> or B<-v>
Prints the name of each parsed file.
--
2.39.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [igt-dev] [PATCH i-g-t 06/12] code_cov_parse_info: add support for parsing JSON files
2023-01-17 14:05 [igt-dev] [PATCH i-g-t 00/12] Improve code coverage tool Mauro Carvalho Chehab
` (4 preceding siblings ...)
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 05/12] code_cov_parse_info: add a tool to analyze branch coverage Mauro Carvalho Chehab
@ 2023-01-17 14:06 ` Mauro Carvalho Chehab
2023-01-25 14:18 ` Kamil Konieczny
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 07/12] code_cov_parse_info: add support for compressed files Mauro Carvalho Chehab
` (5 subsequent siblings)
11 siblings, 1 reply; 14+ messages in thread
From: Mauro Carvalho Chehab @ 2023-01-17 14:06 UTC (permalink / raw)
To: igt-dev
From: Mauro Carvalho Chehab <mchehab@kernel.org>
Currently, the tool supports only info files, provided by
lcov tool. While this works, the info output lacks support to
properly identify what function is related to branches and
lines. Such limitation doesn't exist with json. So, add
support for parsing it.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
scripts/code_cov_parse_info | 441 ++++++++++++++++++++++++++++++------
1 file changed, 366 insertions(+), 75 deletions(-)
mode change 100755 => 100644 scripts/code_cov_parse_info
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
old mode 100755
new mode 100644
index 3982dbd513c4..80a9f1c25540
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -28,6 +28,16 @@ my $verbose = 0;
my $ignore_unused = 0;
my $skip_func = 0;
+my $has_json_support = eval {
+ require Cpanel::JSON::XS;
+ Cpanel::JSON::XS->import(qw(decode_json));
+ 1;
+};
+
+if (!$has_json_support) {
+ print "Warning: System doesn't have Cpanel::JSON::XS. Can't use gcov directly.\n";
+}
+
sub is_function_excluded($)
{
return 0 if (!@func_include_regexes && !@func_exclude_regexes);
@@ -76,7 +86,285 @@ sub is_file_excluded($)
# Use something that comes before any real function
my $before_sf = "!!!!";
-sub parse_info_data($)
+sub parse_json_gcov_v1($$)
+{
+ my $file = shift;
+ my $json = shift;
+
+ my $was_used = 0;
+ my $has_func = 0;
+ my $ignore = 0;
+
+ my $cur_test = $file;
+ $cur_test =~ s#^.*/##;
+ $cur_test =~ s#\.json$##;
+ $test_names{$cur_test} = 1;
+
+ # Store the common JSON data into the record
+ for my $key (keys %$json) {
+ next if ($key eq "files");
+ next if ($key eq "functions");
+ next if ($key eq "lines");
+ # Store any extra data
+ $record{$key} = $json->{$key};
+ }
+ # Store test name at the record
+ $record{tests}{$cur_test} = 1;
+
+ for my $file_ref (@{$json->{'files'}}) {
+ my $source = $file_ref->{'file'};
+
+ $files{$source} = 1;
+ next if is_file_excluded($source);
+
+ # Parse functions
+ for my $func_ref (@{$file_ref->{'functions'}}) {
+ my $func = $func_ref->{'name'};
+
+ next if is_function_excluded($func);
+
+ # Negative gcov results are possible, as reported at:
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67937
+ # Lcov ignores those. So, let's do the same here.
+ $func_ref->{'execution_count'} = 0 if ($func_ref->{'execution_count'} < 0);
+
+ # Store the record
+ for my $key (keys %$func_ref) {
+ if ($key eq "execution_count") {
+ $record{files}{$source}{func}{$func}{$key} += $func_ref->{$key};
+ } else {
+ $record{files}{$source}{func}{$func}{$key} = $func_ref->{$key};
+ }
+ }
+
+ $all_func{$func}{$source}->{ln} = $func_ref->{'start_line'};
+ $all_func{$func}{$source}->{end_ln} = $func_ref->{'end_line'};
+
+ if ($func_ref->{'execution_count'} > 0) {
+ $used_func{$func}{$source}->{count} += $func_ref->{'execution_count'};
+ $was_used = 1;
+ }
+ }
+ next if ($ignore_unused && !$was_used);
+ $used_source{$source} = 1;
+
+ # Parse lines and branches
+ for my $line_ref (@{$file_ref->{'lines'}}) {
+ my $ln = $line_ref->{'line_number'};
+
+ # Negative gcov results are possible, as reported at:
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67937
+ # Lcov ignores those. So, let's do the same here.
+ $line_ref->{'count'} = 0 if ($line_ref->{'count'} < 0);
+
+ my $func = $line_ref->{'function_name'};
+ if (!$func) {
+ # Ignore DA/BRDA that aren't associated with
+ # functions. Those are present on header files
+ # (maybe defines?)
+ next if (@func_include_regexes);
+
+ # Otherwise place them in separate
+ $func = $before_sf;
+ } else {
+ next if is_function_excluded($func);
+ }
+
+ # Store the record
+ for my $key (keys %$line_ref) {
+ next if ($key eq "line_number");
+
+ # Branches will be handled in separate
+ next if ($key eq "branches");
+ if ($key eq "count") {
+ $record{files}{$source}{line}{$ln}{$key} += $line_ref->{$key};
+ } else {
+ $record{files}{$source}{line}{$ln}{$key} = $line_ref->{$key};
+ }
+ }
+ $all_line{$source}{$ln} += $line_ref->{'count'};
+
+ my $i = 0;
+ for my $branch_ref (@{$line_ref->{'branches'}}) {
+ my $taken = $branch_ref->{'count'};
+ my $where = sprintf "%d,%d,%d", $ln, 0, $i;
+
+ # Negative gcov results are possible, as
+ # reported at:
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67937
+ # Lcov ignores those. So, let's do the same
+ # here.
+ $branch_ref->{'count'} = 0 if ($branch_ref->{'count'} < 0);
+
+ for my $key (keys %$branch_ref) {
+ if ($key eq "count") {
+ $record{files}{$source}{line}{$ln}{branches}[$i]{$key} += $branch_ref->{$key};
+ } else {
+ $record{files}{$source}{line}{$ln}{branches}[$i]{$key} = $branch_ref->{$key};
+ }
+ }
+
+ $all_branch{$source}{$where}{count} += $taken;
+ $i++;
+ }
+ if (!defined($record{files}{$source}{line}{$ln}{branches})) {
+ @{$record{files}{$source}{line}{$ln}{branches}} = ();
+ }
+ }
+ }
+
+ # As the record was changed, we need to use a different format name
+ $record{format_version} = "parse_info v1.0";
+}
+
+sub parse_json_internal_format_v1($$)
+{
+ my $file = shift;
+ my $json = shift;
+
+ my $was_used = 0;
+ my $has_func = 0;
+ my $ignore = 0;
+
+ # Store the common JSON data into the record
+ for my $key (keys %$json) {
+ next if ($key eq "files");
+ next if ($key eq "functions");
+ next if ($key eq "lines");
+ # Store any extra data
+ $record{$key} = $json->{$key};
+ }
+
+ for my $test (keys %{$json->{'tests'}}) {
+ $test_names{$test} = 1;
+ }
+
+ for my $source (keys %{$json->{'files'}}) {
+ $files{$source} = 1;
+ next if is_file_excluded($source);
+
+ my $file_ref = \%{$json->{'files'}{$source}};
+
+ # Parse functions
+ for my $func (keys %{$file_ref->{func}}) {
+ next if is_function_excluded($func);
+
+ my $func_ref = \%{$file_ref->{func}{$func}};
+
+ # Store the record
+ for my $key (keys %$func_ref) {
+ if ($key eq "execution_count") {
+ $record{files}{$source}{func}{$func}{$key} += $func_ref->{$key};
+ } else {
+ $record{files}{$source}{func}{$func}{$key} = $func_ref->{$key};
+ }
+ }
+
+ $all_func{$func}{$source}->{ln} = $func_ref->{'start_line'};
+ $all_func{$func}{$source}->{end_ln} = $func_ref->{'end_line'};
+
+ if ($func_ref->{'execution_count'} > 0) {
+ $used_func{$func}{$source}->{count} += $func_ref->{'execution_count'};
+ $was_used = 1;
+ }
+ }
+ next if ($ignore_unused && !$was_used);
+ $used_source{$source} = 1;
+
+ # Parse lines and branches
+ for my $ln (keys %{$file_ref->{line}}) {
+ my $line_ref = \%{$file_ref->{line}{$ln}};
+ my $func = $line_ref->{'function_name'};
+ if (!$func) {
+ # Ignore DA/BRDA that aren't associated with
+ # functions. Those are present on header files
+ # (maybe defines?)
+ next if (@func_include_regexes);
+
+ # Otherwise place them in separate
+ $func = $before_sf;
+ } else {
+ next if is_function_excluded($func);
+ }
+
+ # Store the record
+ for my $key (keys %$line_ref) {
+ next if ($key eq "line_number");
+
+ # Branches will be handled in separate
+ next if ($key eq "branches");
+ if ($key eq "count") {
+ $record{files}{$source}{line}{$ln}{$key} += $line_ref->{$key};
+ } else {
+ $record{files}{$source}{line}{$ln}{$key} = $line_ref->{$key};
+ }
+ }
+ $all_line{$source}{$ln} += $line_ref->{'count'};
+
+ my $i = 0;
+ for my $branch_ref (@{$line_ref->{'branches'}}) {
+ my $taken = $branch_ref->{'count'};
+ my $where = sprintf "%d,%d,%d", $ln, 0, $i;
+
+ # Negative gcov results are possible, as
+ # reported at:
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67937
+ # Lcov ignores those. So, let's do the same
+ # here.
+ $branch_ref->{'count'} = 0 if ($branch_ref->{'count'} < 0);
+
+ for my $key (keys %$branch_ref) {
+ if ($key eq "count") {
+ $record{files}{$source}{line}{$ln}{branches}[$i]{$key} += $branch_ref->{$key};
+ } else {
+ $record{files}{$source}{line}{$ln}{branches}[$i]{$key} = $branch_ref->{$key};
+ }
+ }
+
+ $all_branch{$source}{$where}{count} += $taken;
+ $i++;
+ }
+ if (!defined($record{files}{$source}{line}{$ln}{branches})) {
+ @{$record{files}{$source}{line}{$ln}{branches}} = ();
+ }
+ }
+ }
+}
+
+sub read_json($)
+{
+ my $file = shift;
+
+ if (!$has_json_support) {
+ die "Can't parse json as system doesn't have Cpanel::JSON::XS.\n";
+ }
+
+ # Read JSON data
+ open IN, $file or die "can't open $file";
+ while (<IN>) {
+ my $json = eval {
+ Cpanel::JSON::XS::decode_json($_)
+ };
+
+ if (!$json) {
+ printf "Failed to parse file $file, line $.\n";
+ next;
+ }
+ if ($json->{'format_version'} eq '1') {
+ parse_json_gcov_v1($file, $json);
+ } elsif ($json->{'format_version'} eq 'parse_info v1.0') {
+ parse_json_internal_format_v1($file, $json);
+ } else {
+ if ($json->{'format_version'}) {
+ die "Can't parse JSON version %d on file $file\n", $json->{'format_version'};
+ }
+ die "Unknown JSON format on file $file\n";
+ }
+ }
+ close IN;
+}
+
+sub read_info($)
{
my $file = shift;
my $was_used = 0;
@@ -98,6 +386,7 @@ sub parse_info_data($)
if ($1 ne $cur_test) {
$cur_test = $1;
$test_names{$cur_test} = 1;
+ $record{tests}{$cur_test} = 1;
}
$source = $before_sf;
$func = $before_sf;
@@ -158,7 +447,7 @@ sub parse_info_data($)
$skip_func = 0;
- $record{$source}{$func}{fn} = $ln;
+ $record{files}{$source}{func}{$func}{start_line} = $ln;
$all_func{$func}{$source}->{ln} = $ln;
next;
}
@@ -186,7 +475,7 @@ sub parse_info_data($)
$skip_func = 0;
$was_used = 1;
- $record{$source}{$func}{fnda} += $count;
+ $record{files}{$source}{func}{$func}{execution_count} += $count;
$used_func{$func}{$source}->{count} += $count;
next;
}
@@ -200,15 +489,10 @@ sub parse_info_data($)
# FNF:<number of functions found>
if (m/^FNF:(-?\d+)/) {
- $record{$source}{$func}{fnf} = $1;
next;
}
# FNH:<number of function hit>
if (m/^FNH:(-?\d+)/) {
- my $hits = $1;
- if (!defined($record{$source}{$func}{fnh}) || $record{$source}{$func}{fnh} < $hits) {
- $record{$source}{$func}{fnh} = $hits;
- }
next;
}
@@ -221,6 +505,10 @@ sub parse_info_data($)
my $branch = $3;
my $taken = $4;
+ if ($block != 0) {
+ print "Warning: unexpected block $block at line $.\n";
+ }
+
my $where = "$ln,$block,$branch";
$taken = 0 if ($taken eq '-');
@@ -232,22 +520,17 @@ sub parse_info_data($)
$was_used = 1 if ($taken > 0);
- $record{$source}{$func}{brda}{$where} += $taken;
- $all_branch{$source}{"$where"} += $taken;
+ $record{files}{$source}{line}{$ln}{branches}[$branch]{count} += $taken;
+ $all_branch{$source}{$where}{count} += $taken;
next;
}
# BRF:<number of branches found>
if (m/^BRF:(-?\d+)/) {
- $record{$source}{$func}{brf} = $1;
next;
}
# BRH:<number of branches hit>
if (m/^BRH:(-?\d+)/) {
- my $hits = $1;
- if (!defined($record{$source}{$func}{brh}) || $record{$source}{$func}{brh} < $hits) {
- $record{$source}{$func}{brh} = $hits;
- }
next;
}
@@ -265,23 +548,23 @@ sub parse_info_data($)
$was_used = 1 if ($count > 0);
- $record{$source}{$func}{da}{$ln} += $count;
- $all_line{$source}{"$ln"} += $count;
+ $record{files}{$source}{line}{$ln}{count} += $count;
+ if (!defined($record{files}{$source}{line}{$ln}{branches})) {
+ @{$record{files}{$source}{line}{$ln}{branches}} = ();
+ }
+
+ $all_line{$source}{$ln} += $count;
+
next;
}
# LF:<number of instrumented lines>
if (m/^LF:(-?\d+)/) {
- $record{$source}{$func}{lf} = $1;
next;
}
# LH:<number of lines with a non-zero execution count>
if (m/^LH:(-?\d+)/) {
- my $hits = $1;
- if (!defined($record{$source}{$func}{lh}) || $record{$source}{$func}{lh} < $hits) {
- $record{$source}{$func}{lh} = $hits;
- }
next;
}
@@ -306,74 +589,73 @@ sub sort_where($$)
return $a[2] <=> $b[2];
}
-sub write_filtered_file($)
+sub write_json_file($)
{
- my $filter = shift;
+ my $fname = shift;
- my $filtered = "";
+ if (!$has_json_support) {
+ die "Can't parse json as system doesn't have Cpanel::JSON::XS.\n";
+ }
+
+ my $data = eval {
+ Cpanel::JSON::XS::encode_json(\%record)
+ };
+
+ open OUT, ">$fname" or die "Can't open $fname";
+ print OUT $data or die "Failed to write to $fname";
+ close OUT or die "Failed to close to $fname";
+}
+
+sub write_info_file($)
+{
+ my $fname = shift;
+ my $data = "";
if ($title eq "") {
foreach my $testname(sort keys %test_names) {
- $filtered .= "TN:$testname\n";
+ $data .= "TN:$testname\n";
}
} else {
- $filtered .= "TN:$title\n";
+ $data .= "TN:$title\n";
}
- # Generates filtered data
- foreach my $source(sort keys %record) {
+ # Fills $data with the contents to be stored at the file
+ foreach my $source(sort keys %{$record{files}}) {
next if (!$used_source{$source});
if ($source ne $before_sf) {
- $filtered .= "SF:$source\n";
+ $data .= "SF:$source\n";
}
- foreach my $func(sort keys %{ $record{$source} }) {
+ foreach my $func(sort keys %{ $record{files}{$source}{func} }) {
if ($func ne $before_sf) {
my $fn;
my $fnda;
- if (defined($record{$source}{$func}{fn})) {
- $filtered .= "FN:" . $record{$source}{$func}{fn} . ",$func\n";
- }
- if (defined($record{$source}{$func}{fnda})) {
- $filtered .= "FNDA:" . $record{$source}{$func}{fnda} . ",$func\n";
+ if (defined($record{files}{$source}{func}{$func}{start_line})) {
+ $data .= "FN:" . $record{files}{$source}{func}{$func}{start_line} . ",$func\n";
}
- if ($record{$source}{fnf}) {
- $filtered .= "FNF:". $record{$source}{$func}{fnf} ."\n";
+ if (defined($record{files}{$source}{func}{$func}{execution_count})) {
+ $data .= "FNDA:" . $record{files}{$source}{func}{$func}{execution_count} . ",$func\n";
}
- if ($record{$source}{fnh}) {
- $filtered .= "FNH:". $record{$source}{$func}{fnh} ."\n";
- }
- }
- foreach my $ln(sort { $a <=> $b } keys %{ $record{$source}{$func}{da} }) {
- $filtered .= "DA:$ln," . $record{$source}{$func}{da}{$ln} . "\n";
}
- foreach my $where(sort sort_where keys %{ $record{$source}{$func}{brda} }) {
- my $taken = $record{$source}{$func}{brda}{$where};
+ }
+
+ foreach my $ln(sort { $a <=> $b } keys %{ $record{files}{$source}{line} }) {
+ $data .= "DA:$ln," . $record{files}{$source}{line}{$ln}{count} . "\n";
+ for (my $i = 0; $i < scalar @{$record{files}{$source}{line}{$ln}{branches}}; $i++) {
+ my $taken = $record{files}{$source}{line}{$ln}{branches}[$i]{count};
$taken = "-" if (!$taken);
- $filtered .= "BRDA:$where,$taken\n";
- }
- if ($record{$source}{$func}{brf}) {
- $filtered .= "BRF:". $record{$source}{$func}{brf} ."\n";
- }
- if ($record{$source}{$func}{brh}) {
- $filtered .= "BRH:". $record{$source}{$func}{brh} ."\n";
- }
- if ($record{$source}{$func}{lf}) {
- $filtered .= "LF:". $record{$source}{$func}{lf} ."\n";
- }
- if ($record{$source}{$func}{lh}) {
- $filtered .= "LH:". $record{$source}{$func}{lh} ."\n";
+ $data .= "BRDA:$ln,0,$i,$taken\n";
}
}
- $filtered .= "end_of_record\n";
+ $data .= "end_of_record\n";
}
- open OUT, ">$filter" or die "Can't open $filter";
- print OUT $filtered or die "Failed to write to $filter";
- close OUT or die "Failed to close to $filter";
+ open OUT, ">$fname" or die "Can't open $fname";
+ print OUT $data or die "Failed to write to $fname";
+ close OUT or die "Failed to close to $fname";
}
sub print_code_coverage($$$)
@@ -463,7 +745,7 @@ sub gen_stats()
foreach my $where (keys(%{$all_branch{$source}})) {
$stats{"branch_count"}++;
- $stats{"branch_reached"}++ if ($all_branch{$source}{$where} != 0);
+ $stats{"branch_reached"}++ if ($all_branch{$source}{$where}{count} != 0);
}
}
@@ -622,7 +904,7 @@ sub generate_report($)
}
foreach my $source (keys(%{$report{$f}{"all_branch"}})) {
foreach my $where (keys(%{$report{$f}{"all_branch"}{$source}})) {
- $all_branch{$source}{"$where"} += $report{$f}{"all_branch"}{$source}{$where};
+ $all_branch{$source}{"$where"}{count} += $report{$f}{"all_branch"}{$source}{$where}{count};
}
}
for my $source(keys(%{$report{$f}{"files"}})) {
@@ -902,7 +1184,7 @@ sub check_source_branches()
my @lines;
foreach my $where (sort keys %{$all_branch{$source}}) {
- my $taken = $all_branch{$source}{$where};
+ my $taken = $all_branch{$source}{$where}{count};
next if ($taken > 0);
next if !($where =~ m/^(-?\d+),(-?\d+),(-?\d+)/);
@@ -913,13 +1195,14 @@ sub check_source_branches()
$branch = $3;
if (!@lines) {
- open IN, "$source";
+ open IN, "$source" || die "File $source not found. Can't check branches\n";
@lines = <IN>;
close IN;
}
if ($ln >= $#lines) {
- print "Error: $ln is bigger than $#lines. Can't print branch!\n";
+ die "$source:$ln line is bigger than the number of lines at the file ($#lines lines)\n";
+ return;
}
my $func = $all_branch{$source}{$where}{func};
@@ -971,7 +1254,7 @@ sub check_source_branches()
my $print_used;
my $print_unused;
my $stat;
-my $filter;
+my $output_file;
my $help;
my $man;
my $func_filters;
@@ -986,7 +1269,7 @@ GetOptions(
"print-coverage|print_coverage|print|p" => \$print_used,
"print-unused|u" => \$print_unused,
"stat|statistics" => \$stat,
- "output|o=s" => \$filter,
+ "output|o=s" => \$output_file,
"verbose|v" => \$verbose,
"ignore-unused|ignore_unused" => \$ignore_unused,
"only-i915|only_i915" => \$only_i915,
@@ -1018,9 +1301,9 @@ if ($#ARGV < 0) {
}
# At least one action should be specified
-pod2usage(1) if (!$print_used && !$filter && !$stat && !$print_unused && !$gen_report && !$check_branches);
+pod2usage(1) if (!$print_used && !$output_file && !$stat && !$print_unused && !$gen_report && !$check_branches);
-pod2usage(1) if ($gen_report && ($print_used || $filter || $stat || $print_unused));
+pod2usage(1) if ($gen_report && ($print_used || $output_file || $stat || $print_unused));
my $filter_str = "";
my $has_filter;
@@ -1063,7 +1346,11 @@ if ($ignore_unused) {
}
foreach my $f (@ARGV) {
- parse_info_data($f);
+ if ($f =~ /.json$/) {
+ read_json($f);
+ } else {
+ read_info($f);
+ }
if ($gen_report) {
$f =~ s,.*/,,;
@@ -1137,8 +1424,12 @@ if ($show_files) {
}
}
-if ($filter) {
- write_filtered_file($filter);
+if ($output_file) {
+ if ($output_file =~ /.json$/) {
+ write_json_file($output_file);
+ } else {
+ write_info_file($output_file);
+ }
}
__END__
--
2.39.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [igt-dev] [PATCH i-g-t 06/12] code_cov_parse_info: add support for parsing JSON files
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 06/12] code_cov_parse_info: add support for parsing JSON files Mauro Carvalho Chehab
@ 2023-01-25 14:18 ` Kamil Konieczny
0 siblings, 0 replies; 14+ messages in thread
From: Kamil Konieczny @ 2023-01-25 14:18 UTC (permalink / raw)
To: igt-dev
On 2023-01-17 at 15:06:01 +0100, Mauro Carvalho Chehab wrote:
> From: Mauro Carvalho Chehab <mchehab@kernel.org>
>
> Currently, the tool supports only info files, provided by
> lcov tool. While this works, the info output lacks support to
> properly identify what function is related to branches and
> lines. Such limitation doesn't exist with json. So, add
----------------------------------------------- ^^^
s/. So,/ so /
> support for parsing it.
>
> Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Acked-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
> ---
> scripts/code_cov_parse_info | 441 ++++++++++++++++++++++++++++++------
> 1 file changed, 366 insertions(+), 75 deletions(-)
> mode change 100755 => 100644 scripts/code_cov_parse_info
>
> diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
> old mode 100755
> new mode 100644
> index 3982dbd513c4..80a9f1c25540
> --- a/scripts/code_cov_parse_info
> +++ b/scripts/code_cov_parse_info
> @@ -28,6 +28,16 @@ my $verbose = 0;
> my $ignore_unused = 0;
> my $skip_func = 0;
>
> +my $has_json_support = eval {
> + require Cpanel::JSON::XS;
> + Cpanel::JSON::XS->import(qw(decode_json));
> + 1;
> +};
> +
> +if (!$has_json_support) {
> + print "Warning: System doesn't have Cpanel::JSON::XS. Can't use gcov directly.\n";
> +}
> +
> sub is_function_excluded($)
> {
> return 0 if (!@func_include_regexes && !@func_exclude_regexes);
> @@ -76,7 +86,285 @@ sub is_file_excluded($)
> # Use something that comes before any real function
> my $before_sf = "!!!!";
>
> -sub parse_info_data($)
> +sub parse_json_gcov_v1($$)
> +{
> + my $file = shift;
> + my $json = shift;
> +
> + my $was_used = 0;
> + my $has_func = 0;
> + my $ignore = 0;
> +
> + my $cur_test = $file;
> + $cur_test =~ s#^.*/##;
> + $cur_test =~ s#\.json$##;
> + $test_names{$cur_test} = 1;
> +
> + # Store the common JSON data into the record
> + for my $key (keys %$json) {
> + next if ($key eq "files");
> + next if ($key eq "functions");
> + next if ($key eq "lines");
> + # Store any extra data
> + $record{$key} = $json->{$key};
> + }
> + # Store test name at the record
> + $record{tests}{$cur_test} = 1;
> +
> + for my $file_ref (@{$json->{'files'}}) {
> + my $source = $file_ref->{'file'};
> +
> + $files{$source} = 1;
> + next if is_file_excluded($source);
> +
> + # Parse functions
> + for my $func_ref (@{$file_ref->{'functions'}}) {
> + my $func = $func_ref->{'name'};
> +
> + next if is_function_excluded($func);
> +
> + # Negative gcov results are possible, as reported at:
> + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67937
> + # Lcov ignores those. So, let's do the same here.
> + $func_ref->{'execution_count'} = 0 if ($func_ref->{'execution_count'} < 0);
> +
> + # Store the record
> + for my $key (keys %$func_ref) {
> + if ($key eq "execution_count") {
> + $record{files}{$source}{func}{$func}{$key} += $func_ref->{$key};
> + } else {
> + $record{files}{$source}{func}{$func}{$key} = $func_ref->{$key};
> + }
> + }
> +
> + $all_func{$func}{$source}->{ln} = $func_ref->{'start_line'};
> + $all_func{$func}{$source}->{end_ln} = $func_ref->{'end_line'};
> +
> + if ($func_ref->{'execution_count'} > 0) {
> + $used_func{$func}{$source}->{count} += $func_ref->{'execution_count'};
> + $was_used = 1;
> + }
> + }
> + next if ($ignore_unused && !$was_used);
> + $used_source{$source} = 1;
> +
> + # Parse lines and branches
> + for my $line_ref (@{$file_ref->{'lines'}}) {
> + my $ln = $line_ref->{'line_number'};
> +
> + # Negative gcov results are possible, as reported at:
> + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67937
> + # Lcov ignores those. So, let's do the same here.
> + $line_ref->{'count'} = 0 if ($line_ref->{'count'} < 0);
> +
> + my $func = $line_ref->{'function_name'};
> + if (!$func) {
> + # Ignore DA/BRDA that aren't associated with
> + # functions. Those are present on header files
> + # (maybe defines?)
> + next if (@func_include_regexes);
> +
> + # Otherwise place them in separate
> + $func = $before_sf;
> + } else {
> + next if is_function_excluded($func);
> + }
> +
> + # Store the record
> + for my $key (keys %$line_ref) {
> + next if ($key eq "line_number");
> +
> + # Branches will be handled in separate
> + next if ($key eq "branches");
> + if ($key eq "count") {
> + $record{files}{$source}{line}{$ln}{$key} += $line_ref->{$key};
> + } else {
> + $record{files}{$source}{line}{$ln}{$key} = $line_ref->{$key};
> + }
> + }
> + $all_line{$source}{$ln} += $line_ref->{'count'};
> +
> + my $i = 0;
> + for my $branch_ref (@{$line_ref->{'branches'}}) {
> + my $taken = $branch_ref->{'count'};
> + my $where = sprintf "%d,%d,%d", $ln, 0, $i;
> +
> + # Negative gcov results are possible, as
> + # reported at:
> + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67937
> + # Lcov ignores those. So, let's do the same
> + # here.
> + $branch_ref->{'count'} = 0 if ($branch_ref->{'count'} < 0);
> +
> + for my $key (keys %$branch_ref) {
> + if ($key eq "count") {
> + $record{files}{$source}{line}{$ln}{branches}[$i]{$key} += $branch_ref->{$key};
> + } else {
> + $record{files}{$source}{line}{$ln}{branches}[$i]{$key} = $branch_ref->{$key};
> + }
> + }
> +
> + $all_branch{$source}{$where}{count} += $taken;
> + $i++;
> + }
> + if (!defined($record{files}{$source}{line}{$ln}{branches})) {
> + @{$record{files}{$source}{line}{$ln}{branches}} = ();
> + }
> + }
> + }
> +
> + # As the record was changed, we need to use a different format name
> + $record{format_version} = "parse_info v1.0";
> +}
> +
> +sub parse_json_internal_format_v1($$)
> +{
> + my $file = shift;
> + my $json = shift;
> +
> + my $was_used = 0;
> + my $has_func = 0;
> + my $ignore = 0;
> +
> + # Store the common JSON data into the record
> + for my $key (keys %$json) {
> + next if ($key eq "files");
> + next if ($key eq "functions");
> + next if ($key eq "lines");
> + # Store any extra data
> + $record{$key} = $json->{$key};
> + }
> +
> + for my $test (keys %{$json->{'tests'}}) {
> + $test_names{$test} = 1;
> + }
> +
> + for my $source (keys %{$json->{'files'}}) {
> + $files{$source} = 1;
> + next if is_file_excluded($source);
> +
> + my $file_ref = \%{$json->{'files'}{$source}};
> +
> + # Parse functions
> + for my $func (keys %{$file_ref->{func}}) {
> + next if is_function_excluded($func);
> +
> + my $func_ref = \%{$file_ref->{func}{$func}};
> +
> + # Store the record
> + for my $key (keys %$func_ref) {
> + if ($key eq "execution_count") {
> + $record{files}{$source}{func}{$func}{$key} += $func_ref->{$key};
> + } else {
> + $record{files}{$source}{func}{$func}{$key} = $func_ref->{$key};
> + }
> + }
> +
> + $all_func{$func}{$source}->{ln} = $func_ref->{'start_line'};
> + $all_func{$func}{$source}->{end_ln} = $func_ref->{'end_line'};
> +
> + if ($func_ref->{'execution_count'} > 0) {
> + $used_func{$func}{$source}->{count} += $func_ref->{'execution_count'};
> + $was_used = 1;
> + }
> + }
> + next if ($ignore_unused && !$was_used);
> + $used_source{$source} = 1;
> +
> + # Parse lines and branches
> + for my $ln (keys %{$file_ref->{line}}) {
> + my $line_ref = \%{$file_ref->{line}{$ln}};
> + my $func = $line_ref->{'function_name'};
> + if (!$func) {
> + # Ignore DA/BRDA that aren't associated with
> + # functions. Those are present on header files
> + # (maybe defines?)
> + next if (@func_include_regexes);
> +
> + # Otherwise place them in separate
> + $func = $before_sf;
> + } else {
> + next if is_function_excluded($func);
> + }
> +
> + # Store the record
> + for my $key (keys %$line_ref) {
> + next if ($key eq "line_number");
> +
> + # Branches will be handled in separate
> + next if ($key eq "branches");
> + if ($key eq "count") {
> + $record{files}{$source}{line}{$ln}{$key} += $line_ref->{$key};
> + } else {
> + $record{files}{$source}{line}{$ln}{$key} = $line_ref->{$key};
> + }
> + }
> + $all_line{$source}{$ln} += $line_ref->{'count'};
> +
> + my $i = 0;
> + for my $branch_ref (@{$line_ref->{'branches'}}) {
> + my $taken = $branch_ref->{'count'};
> + my $where = sprintf "%d,%d,%d", $ln, 0, $i;
> +
> + # Negative gcov results are possible, as
> + # reported at:
> + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67937
> + # Lcov ignores those. So, let's do the same
> + # here.
> + $branch_ref->{'count'} = 0 if ($branch_ref->{'count'} < 0);
> +
> + for my $key (keys %$branch_ref) {
> + if ($key eq "count") {
> + $record{files}{$source}{line}{$ln}{branches}[$i]{$key} += $branch_ref->{$key};
> + } else {
> + $record{files}{$source}{line}{$ln}{branches}[$i]{$key} = $branch_ref->{$key};
> + }
> + }
> +
> + $all_branch{$source}{$where}{count} += $taken;
> + $i++;
> + }
> + if (!defined($record{files}{$source}{line}{$ln}{branches})) {
> + @{$record{files}{$source}{line}{$ln}{branches}} = ();
> + }
> + }
> + }
> +}
> +
> +sub read_json($)
> +{
> + my $file = shift;
> +
> + if (!$has_json_support) {
> + die "Can't parse json as system doesn't have Cpanel::JSON::XS.\n";
> + }
> +
> + # Read JSON data
> + open IN, $file or die "can't open $file";
> + while (<IN>) {
> + my $json = eval {
> + Cpanel::JSON::XS::decode_json($_)
> + };
> +
> + if (!$json) {
> + printf "Failed to parse file $file, line $.\n";
> + next;
> + }
> + if ($json->{'format_version'} eq '1') {
> + parse_json_gcov_v1($file, $json);
> + } elsif ($json->{'format_version'} eq 'parse_info v1.0') {
> + parse_json_internal_format_v1($file, $json);
> + } else {
> + if ($json->{'format_version'}) {
> + die "Can't parse JSON version %d on file $file\n", $json->{'format_version'};
> + }
> + die "Unknown JSON format on file $file\n";
> + }
> + }
> + close IN;
> +}
> +
> +sub read_info($)
> {
> my $file = shift;
> my $was_used = 0;
> @@ -98,6 +386,7 @@ sub parse_info_data($)
> if ($1 ne $cur_test) {
> $cur_test = $1;
> $test_names{$cur_test} = 1;
> + $record{tests}{$cur_test} = 1;
> }
> $source = $before_sf;
> $func = $before_sf;
> @@ -158,7 +447,7 @@ sub parse_info_data($)
>
> $skip_func = 0;
>
> - $record{$source}{$func}{fn} = $ln;
> + $record{files}{$source}{func}{$func}{start_line} = $ln;
> $all_func{$func}{$source}->{ln} = $ln;
> next;
> }
> @@ -186,7 +475,7 @@ sub parse_info_data($)
> $skip_func = 0;
> $was_used = 1;
>
> - $record{$source}{$func}{fnda} += $count;
> + $record{files}{$source}{func}{$func}{execution_count} += $count;
> $used_func{$func}{$source}->{count} += $count;
> next;
> }
> @@ -200,15 +489,10 @@ sub parse_info_data($)
>
> # FNF:<number of functions found>
> if (m/^FNF:(-?\d+)/) {
> - $record{$source}{$func}{fnf} = $1;
> next;
> }
> # FNH:<number of function hit>
> if (m/^FNH:(-?\d+)/) {
> - my $hits = $1;
> - if (!defined($record{$source}{$func}{fnh}) || $record{$source}{$func}{fnh} < $hits) {
> - $record{$source}{$func}{fnh} = $hits;
> - }
> next;
> }
>
> @@ -221,6 +505,10 @@ sub parse_info_data($)
> my $branch = $3;
> my $taken = $4;
>
> + if ($block != 0) {
> + print "Warning: unexpected block $block at line $.\n";
> + }
> +
> my $where = "$ln,$block,$branch";
>
> $taken = 0 if ($taken eq '-');
> @@ -232,22 +520,17 @@ sub parse_info_data($)
>
> $was_used = 1 if ($taken > 0);
>
> - $record{$source}{$func}{brda}{$where} += $taken;
> - $all_branch{$source}{"$where"} += $taken;
> + $record{files}{$source}{line}{$ln}{branches}[$branch]{count} += $taken;
> + $all_branch{$source}{$where}{count} += $taken;
> next;
> }
>
> # BRF:<number of branches found>
> if (m/^BRF:(-?\d+)/) {
> - $record{$source}{$func}{brf} = $1;
> next;
> }
> # BRH:<number of branches hit>
> if (m/^BRH:(-?\d+)/) {
> - my $hits = $1;
> - if (!defined($record{$source}{$func}{brh}) || $record{$source}{$func}{brh} < $hits) {
> - $record{$source}{$func}{brh} = $hits;
> - }
> next;
> }
>
> @@ -265,23 +548,23 @@ sub parse_info_data($)
>
> $was_used = 1 if ($count > 0);
>
> - $record{$source}{$func}{da}{$ln} += $count;
> - $all_line{$source}{"$ln"} += $count;
> + $record{files}{$source}{line}{$ln}{count} += $count;
> + if (!defined($record{files}{$source}{line}{$ln}{branches})) {
> + @{$record{files}{$source}{line}{$ln}{branches}} = ();
> + }
> +
> + $all_line{$source}{$ln} += $count;
> +
> next;
> }
>
> # LF:<number of instrumented lines>
> if (m/^LF:(-?\d+)/) {
> - $record{$source}{$func}{lf} = $1;
> next;
> }
>
> # LH:<number of lines with a non-zero execution count>
> if (m/^LH:(-?\d+)/) {
> - my $hits = $1;
> - if (!defined($record{$source}{$func}{lh}) || $record{$source}{$func}{lh} < $hits) {
> - $record{$source}{$func}{lh} = $hits;
> - }
> next;
> }
>
> @@ -306,74 +589,73 @@ sub sort_where($$)
> return $a[2] <=> $b[2];
> }
>
> -sub write_filtered_file($)
> +sub write_json_file($)
> {
> - my $filter = shift;
> + my $fname = shift;
>
> - my $filtered = "";
> + if (!$has_json_support) {
> + die "Can't parse json as system doesn't have Cpanel::JSON::XS.\n";
> + }
> +
> + my $data = eval {
> + Cpanel::JSON::XS::encode_json(\%record)
> + };
> +
> + open OUT, ">$fname" or die "Can't open $fname";
> + print OUT $data or die "Failed to write to $fname";
> + close OUT or die "Failed to close to $fname";
> +}
> +
> +sub write_info_file($)
> +{
> + my $fname = shift;
> + my $data = "";
>
> if ($title eq "") {
> foreach my $testname(sort keys %test_names) {
> - $filtered .= "TN:$testname\n";
> + $data .= "TN:$testname\n";
> }
> } else {
> - $filtered .= "TN:$title\n";
> + $data .= "TN:$title\n";
> }
>
> - # Generates filtered data
> - foreach my $source(sort keys %record) {
> + # Fills $data with the contents to be stored at the file
> + foreach my $source(sort keys %{$record{files}}) {
> next if (!$used_source{$source});
>
> if ($source ne $before_sf) {
> - $filtered .= "SF:$source\n";
> + $data .= "SF:$source\n";
> }
>
> - foreach my $func(sort keys %{ $record{$source} }) {
> + foreach my $func(sort keys %{ $record{files}{$source}{func} }) {
> if ($func ne $before_sf) {
> my $fn;
> my $fnda;
>
> - if (defined($record{$source}{$func}{fn})) {
> - $filtered .= "FN:" . $record{$source}{$func}{fn} . ",$func\n";
> - }
> - if (defined($record{$source}{$func}{fnda})) {
> - $filtered .= "FNDA:" . $record{$source}{$func}{fnda} . ",$func\n";
> + if (defined($record{files}{$source}{func}{$func}{start_line})) {
> + $data .= "FN:" . $record{files}{$source}{func}{$func}{start_line} . ",$func\n";
> }
> - if ($record{$source}{fnf}) {
> - $filtered .= "FNF:". $record{$source}{$func}{fnf} ."\n";
> + if (defined($record{files}{$source}{func}{$func}{execution_count})) {
> + $data .= "FNDA:" . $record{files}{$source}{func}{$func}{execution_count} . ",$func\n";
> }
> - if ($record{$source}{fnh}) {
> - $filtered .= "FNH:". $record{$source}{$func}{fnh} ."\n";
> - }
> - }
>
> - foreach my $ln(sort { $a <=> $b } keys %{ $record{$source}{$func}{da} }) {
> - $filtered .= "DA:$ln," . $record{$source}{$func}{da}{$ln} . "\n";
> }
> - foreach my $where(sort sort_where keys %{ $record{$source}{$func}{brda} }) {
> - my $taken = $record{$source}{$func}{brda}{$where};
> + }
> +
> + foreach my $ln(sort { $a <=> $b } keys %{ $record{files}{$source}{line} }) {
> + $data .= "DA:$ln," . $record{files}{$source}{line}{$ln}{count} . "\n";
> + for (my $i = 0; $i < scalar @{$record{files}{$source}{line}{$ln}{branches}}; $i++) {
> + my $taken = $record{files}{$source}{line}{$ln}{branches}[$i]{count};
> $taken = "-" if (!$taken);
> - $filtered .= "BRDA:$where,$taken\n";
> - }
> - if ($record{$source}{$func}{brf}) {
> - $filtered .= "BRF:". $record{$source}{$func}{brf} ."\n";
> - }
> - if ($record{$source}{$func}{brh}) {
> - $filtered .= "BRH:". $record{$source}{$func}{brh} ."\n";
> - }
> - if ($record{$source}{$func}{lf}) {
> - $filtered .= "LF:". $record{$source}{$func}{lf} ."\n";
> - }
> - if ($record{$source}{$func}{lh}) {
> - $filtered .= "LH:". $record{$source}{$func}{lh} ."\n";
> + $data .= "BRDA:$ln,0,$i,$taken\n";
> }
> }
>
> - $filtered .= "end_of_record\n";
> + $data .= "end_of_record\n";
> }
> - open OUT, ">$filter" or die "Can't open $filter";
> - print OUT $filtered or die "Failed to write to $filter";
> - close OUT or die "Failed to close to $filter";
> + open OUT, ">$fname" or die "Can't open $fname";
> + print OUT $data or die "Failed to write to $fname";
> + close OUT or die "Failed to close to $fname";
> }
>
> sub print_code_coverage($$$)
> @@ -463,7 +745,7 @@ sub gen_stats()
>
> foreach my $where (keys(%{$all_branch{$source}})) {
> $stats{"branch_count"}++;
> - $stats{"branch_reached"}++ if ($all_branch{$source}{$where} != 0);
> + $stats{"branch_reached"}++ if ($all_branch{$source}{$where}{count} != 0);
> }
> }
>
> @@ -622,7 +904,7 @@ sub generate_report($)
> }
> foreach my $source (keys(%{$report{$f}{"all_branch"}})) {
> foreach my $where (keys(%{$report{$f}{"all_branch"}{$source}})) {
> - $all_branch{$source}{"$where"} += $report{$f}{"all_branch"}{$source}{$where};
> + $all_branch{$source}{"$where"}{count} += $report{$f}{"all_branch"}{$source}{$where}{count};
> }
> }
> for my $source(keys(%{$report{$f}{"files"}})) {
> @@ -902,7 +1184,7 @@ sub check_source_branches()
>
> my @lines;
> foreach my $where (sort keys %{$all_branch{$source}}) {
> - my $taken = $all_branch{$source}{$where};
> + my $taken = $all_branch{$source}{$where}{count};
> next if ($taken > 0);
>
> next if !($where =~ m/^(-?\d+),(-?\d+),(-?\d+)/);
> @@ -913,13 +1195,14 @@ sub check_source_branches()
> $branch = $3;
>
> if (!@lines) {
> - open IN, "$source";
> + open IN, "$source" || die "File $source not found. Can't check branches\n";
> @lines = <IN>;
> close IN;
> }
>
> if ($ln >= $#lines) {
> - print "Error: $ln is bigger than $#lines. Can't print branch!\n";
> + die "$source:$ln line is bigger than the number of lines at the file ($#lines lines)\n";
> + return;
> }
>
> my $func = $all_branch{$source}{$where}{func};
> @@ -971,7 +1254,7 @@ sub check_source_branches()
> my $print_used;
> my $print_unused;
> my $stat;
> -my $filter;
> +my $output_file;
> my $help;
> my $man;
> my $func_filters;
> @@ -986,7 +1269,7 @@ GetOptions(
> "print-coverage|print_coverage|print|p" => \$print_used,
> "print-unused|u" => \$print_unused,
> "stat|statistics" => \$stat,
> - "output|o=s" => \$filter,
> + "output|o=s" => \$output_file,
> "verbose|v" => \$verbose,
> "ignore-unused|ignore_unused" => \$ignore_unused,
> "only-i915|only_i915" => \$only_i915,
> @@ -1018,9 +1301,9 @@ if ($#ARGV < 0) {
> }
>
> # At least one action should be specified
> -pod2usage(1) if (!$print_used && !$filter && !$stat && !$print_unused && !$gen_report && !$check_branches);
> +pod2usage(1) if (!$print_used && !$output_file && !$stat && !$print_unused && !$gen_report && !$check_branches);
>
> -pod2usage(1) if ($gen_report && ($print_used || $filter || $stat || $print_unused));
> +pod2usage(1) if ($gen_report && ($print_used || $output_file || $stat || $print_unused));
>
> my $filter_str = "";
> my $has_filter;
> @@ -1063,7 +1346,11 @@ if ($ignore_unused) {
> }
>
> foreach my $f (@ARGV) {
> - parse_info_data($f);
> + if ($f =~ /.json$/) {
> + read_json($f);
> + } else {
> + read_info($f);
> + }
>
> if ($gen_report) {
> $f =~ s,.*/,,;
> @@ -1137,8 +1424,12 @@ if ($show_files) {
> }
> }
>
> -if ($filter) {
> - write_filtered_file($filter);
> +if ($output_file) {
> + if ($output_file =~ /.json$/) {
> + write_json_file($output_file);
> + } else {
> + write_info_file($output_file);
> + }
> }
>
> __END__
> --
> 2.39.0
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [igt-dev] [PATCH i-g-t 07/12] code_cov_parse_info: add support for compressed files
2023-01-17 14:05 [igt-dev] [PATCH i-g-t 00/12] Improve code coverage tool Mauro Carvalho Chehab
` (5 preceding siblings ...)
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 06/12] code_cov_parse_info: add support for parsing JSON files Mauro Carvalho Chehab
@ 2023-01-17 14:06 ` Mauro Carvalho Chehab
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 08/12] code_cov_parse_info: allow specifying the source directory Mauro Carvalho Chehab
` (4 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Mauro Carvalho Chehab @ 2023-01-17 14:06 UTC (permalink / raw)
To: igt-dev
From: Mauro Carvalho Chehab <mchehab@kernel.org>
Code coverage files are big. Add support for read/write gzipped
files, in order to save I/O, and, depending on the disk, speeding
up the tool.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
scripts/code_cov_parse_info | 56 ++++++++++++++++++++++++++-----------
1 file changed, 40 insertions(+), 16 deletions(-)
mode change 100644 => 100755 scripts/code_cov_parse_info
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
old mode 100644
new mode 100755
index 80a9f1c25540..d38ee9ee6a69
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -6,6 +6,8 @@ use Getopt::Long;
BEGIN { $Pod::Usage::Formatter = 'Pod::Text::Termcap'; }
use Pod::Usage;
use Pod::Man;
+use IO::File;
+use IO::Zlib;
my $prefix = qr ".*?(linux)\w*/";
@@ -340,8 +342,14 @@ sub read_json($)
}
# Read JSON data
- open IN, $file or die "can't open $file";
- while (<IN>) {
+ my $fh;
+ if ($file =~ m/\.gz$/) {
+ $fh = IO::Zlib->new($file, "rb") or die "can't open $file";
+ } else {
+ $fh = IO::File->new($file) or die "can't open $file";
+ }
+ print "reading $file...\n" if ($verbose);
+ while (<$fh>) {
my $json = eval {
Cpanel::JSON::XS::decode_json($_)
};
@@ -361,7 +369,7 @@ sub read_json($)
die "Unknown JSON format on file $file\n";
}
}
- close IN;
+ $fh->close() or die "Failed to close file $file";
}
sub read_info($)
@@ -376,11 +384,16 @@ sub read_info($)
# First step: parse data
- print "reading $file...\n" if ($verbose);
- open IN, $file or die "can't open $file";
# For details on .info file format, see "man geninfo"
# http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php
- while (<IN>) {
+ my $fh;
+ if ($file =~ m/\.gz$/) {
+ $fh = IO::Zlib->new($file, "rb") or die "can't open $file";
+ } else {
+ $fh = IO::File->new($file) or die "can't open $file";
+ }
+ print "reading $file...\n" if ($verbose);
+ while (<$fh>) {
# TN:<test name>
if (m/^TN:(.*)/) {
if ($1 ne $cur_test) {
@@ -571,7 +584,7 @@ sub read_info($)
printf("Warning: invalid line: $_");
}
- close IN or die;
+ $fh->close() or die "Failed to close file $file";
}
sub sort_where($$)
@@ -601,9 +614,15 @@ sub write_json_file($)
Cpanel::JSON::XS::encode_json(\%record)
};
- open OUT, ">$fname" or die "Can't open $fname";
- print OUT $data or die "Failed to write to $fname";
- close OUT or die "Failed to close to $fname";
+ if ($fname =~ m/\.gz$/) {
+ my $fh = IO::Zlib->new($fname, "wb") or die "can't open $fname";
+ print $fh $data or die "Failed to write to $fname";
+ $fh->close or die "Failed to write to $fname";
+ } else {
+ open OUT, ">$fname" or die "Can't open $fname";
+ print OUT $data or die "Failed to write to $fname";
+ close OUT or die "Failed to close to $fname";
+ }
}
sub write_info_file($)
@@ -650,12 +669,17 @@ sub write_info_file($)
$data .= "BRDA:$ln,0,$i,$taken\n";
}
}
-
$data .= "end_of_record\n";
}
- open OUT, ">$fname" or die "Can't open $fname";
- print OUT $data or die "Failed to write to $fname";
- close OUT or die "Failed to close to $fname";
+ if ($fname =~ m/\.gz$/) {
+ my $fh = IO::Zlib->new($fname, "wb") or die "can't open $fname";
+ print $fh $data or die "Failed to write to $fname";
+ $fh->close or die "Failed to write to $fname";
+ } else {
+ open OUT, ">$fname" or die "Can't open $fname";
+ print OUT $data or die "Failed to write to $fname";
+ close OUT or die "Failed to close to $fname";
+ }
}
sub print_code_coverage($$$)
@@ -1346,7 +1370,7 @@ if ($ignore_unused) {
}
foreach my $f (@ARGV) {
- if ($f =~ /.json$/) {
+ if ($f =~ /\.json(\.gz)?$/) {
read_json($f);
} else {
read_info($f);
@@ -1425,7 +1449,7 @@ if ($show_files) {
}
if ($output_file) {
- if ($output_file =~ /.json$/) {
+ if ($output_file =~ /.json(.gz)?$/) {
write_json_file($output_file);
} else {
write_info_file($output_file);
--
2.39.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [igt-dev] [PATCH i-g-t 08/12] code_cov_parse_info: allow specifying the source directory
2023-01-17 14:05 [igt-dev] [PATCH i-g-t 00/12] Improve code coverage tool Mauro Carvalho Chehab
` (6 preceding siblings ...)
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 07/12] code_cov_parse_info: add support for compressed files Mauro Carvalho Chehab
@ 2023-01-17 14:06 ` Mauro Carvalho Chehab
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 09/12] code_cov_parse_info: better handle branch filtering Mauro Carvalho Chehab
` (3 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Mauro Carvalho Chehab @ 2023-01-17 14:06 UTC (permalink / raw)
To: igt-dev
From: Mauro Carvalho Chehab <mchehab@kernel.org>
Some options may require parsing the source files. Currently,
only --check-branches use it, but other functions will be added
on other patches.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
scripts/code_cov_parse_info | 32 ++++++++++++++++++++------------
1 file changed, 20 insertions(+), 12 deletions(-)
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index d38ee9ee6a69..c2a8020f461b 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -1200,13 +1200,19 @@ sub generate_report($)
close OUT;
}
-sub check_source_branches()
+sub check_source_branches($)
{
+ my $source_dir = shift;
+ my $cached = "";
+
foreach my $source (sort keys(%all_branch)) {
next if (!$used_source{$source});
next if (is_file_excluded($source));
- my @lines;
+ open IN, "$source_dir/$source" || die "File $source_dir/$source not found. Can't check branches\n";
+ my @lines = <IN>;
+ close IN;
+
foreach my $where (sort keys %{$all_branch{$source}}) {
my $taken = $all_branch{$source}{$where}{count};
next if ($taken > 0);
@@ -1218,13 +1224,7 @@ sub check_source_branches()
$block = $2;
$branch = $3;
- if (!@lines) {
- open IN, "$source" || die "File $source not found. Can't check branches\n";
- @lines = <IN>;
- close IN;
- }
-
- if ($ln >= $#lines) {
+ if ($ln > $#lines) {
die "$source:$ln line is bigger than the number of lines at the file ($#lines lines)\n";
return;
}
@@ -1288,12 +1288,14 @@ my $show_lines;
my $only_i915;
my $only_drm;
my $check_branches;
+my $source_dir = ".";
GetOptions(
"print-coverage|print_coverage|print|p" => \$print_used,
"print-unused|u" => \$print_unused,
"stat|statistics" => \$stat,
"output|o=s" => \$output_file,
+ "source-dir|source_dir=s" => \$source_dir,
"verbose|v" => \$verbose,
"ignore-unused|ignore_unused" => \$ignore_unused,
"only-i915|only_i915" => \$only_i915,
@@ -1307,7 +1309,7 @@ GetOptions(
"show-files|show_files" => \$show_files,
"show-lines|show_lines" => \$show_lines,
"report|r=s" => \$gen_report,
- "check-branches" => \$check_branches,
+ "check-branches|check_branches" => \$check_branches,
"css-file|css|c=s" => \$css_file,
"title|t=s" => \$title,
"html-prolog|prolog=s" => \$html_prolog,
@@ -1412,7 +1414,7 @@ gen_stats();
if ($check_branches) {
- check_source_branches();
+ check_source_branches($source_dir);
}
die "Nothing counted. Wrong input files?" if (!$stats{"all_files"});
@@ -1541,6 +1543,12 @@ Produce an output file merging all input files.
The generated output file is affected by the applied filters.
+=item B<--source-dir> or B<--source_dir>
+
+Sets the source directory baseline. This is used together with other
+options that require to parse the source files (currently, only
+B<--check-branches).
+
=item B<--only-drm> or B<--only_drm>
Filters out includes outside the DRM subsystem, plus trace files.
@@ -1676,7 +1684,7 @@ This option is automaticaly enabled when B<--func-filters> is used.
Shows the list of files that were used to produce the code coverage
results.
-=item B<--check-branches>
+=item B<--check-branches> or B<--check_branches>
Checks at the Linux Kernel source files what's the contents of the
branches that weren't taken. The directory should match what's
--
2.39.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [igt-dev] [PATCH i-g-t 09/12] code_cov_parse_info: better handle branch filtering
2023-01-17 14:05 [igt-dev] [PATCH i-g-t 00/12] Improve code coverage tool Mauro Carvalho Chehab
` (7 preceding siblings ...)
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 08/12] code_cov_parse_info: allow specifying the source directory Mauro Carvalho Chehab
@ 2023-01-17 14:06 ` Mauro Carvalho Chehab
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 10/12] code_cov_parse_info: filter out branches from headers by default Mauro Carvalho Chehab
` (2 subsequent siblings)
11 siblings, 0 replies; 14+ messages in thread
From: Mauro Carvalho Chehab @ 2023-01-17 14:06 UTC (permalink / raw)
To: igt-dev
From: Mauro Carvalho Chehab <mchehab@kernel.org>
Add an option to filter out branches that aren't associated with
any functions inside the file (e. g. they came from #inline
directives).
While here, also address some issues at the branch report.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
scripts/code_cov_parse_info | 71 ++++++++++++++++++++++++++-----------
1 file changed, 51 insertions(+), 20 deletions(-)
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index c2a8020f461b..5a6a4a407359 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -25,6 +25,8 @@ my @func_exclude_regexes;
my %test_names;
my @src_include_regexes;
my @src_exclude_regexes;
+my $can_filter_lines = 1;
+my $ignore_lines_without_functions = 1;
my $verbose = 0;
my $ignore_unused = 0;
@@ -161,12 +163,6 @@ sub parse_json_gcov_v1($$)
my $func = $line_ref->{'function_name'};
if (!$func) {
- # Ignore DA/BRDA that aren't associated with
- # functions. Those are present on header files
- # (maybe defines?)
- next if (@func_include_regexes);
-
- # Otherwise place them in separate
$func = $before_sf;
} else {
next if is_function_excluded($func);
@@ -188,9 +184,17 @@ sub parse_json_gcov_v1($$)
my $i = 0;
for my $branch_ref (@{$line_ref->{'branches'}}) {
- my $taken = $branch_ref->{'count'};
my $where = sprintf "%d,%d,%d", $ln, 0, $i;
+ if ($func eq $before_sf) {
+ # Ignore DA/BRDA that aren't associated with
+ # functions. Those are present on header files
+ # (maybe defines?)
+ next if ($ignore_lines_without_functions);
+ } else {
+ $all_branch{$source}{$where}{func} = $func;
+ }
+
# Negative gcov results are possible, as
# reported at:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67937
@@ -206,7 +210,8 @@ sub parse_json_gcov_v1($$)
}
}
- $all_branch{$source}{$where}{count} += $taken;
+ $all_branch{$source}{$where}{count} += $branch_ref->{'count'};
+
$i++;
}
if (!defined($record{files}{$source}{line}{$ln}{branches})) {
@@ -382,6 +387,10 @@ sub read_info($)
my $func = $before_sf;
my $cur_test = "";
+ # Info files don't contain functions for lines. So, they can't
+ # be used to filter lines and branches used inside functions.
+ $can_filter_lines = 0;
+
# First step: parse data
# For details on .info file format, see "man geninfo"
@@ -1229,18 +1238,15 @@ sub check_source_branches($)
return;
}
+ my $func_ln;
my $func = $all_branch{$source}{$where}{func};
- my $func_ln = $all_branch{$source}{$where}{func_ln};
-
- print "Branch $source:$ln, ";
if ($func) {
- if ($func_ln) {
- print "func: $func, ";
- } else {
- print "func: $func:$func_ln, ";
- }
+ $func_ln = $all_func{$func}{$source}->{ln};
}
- print "block $block, branch $branch not taken:\n";
+
+ print "Branch $branch on $source:$ln";
+ print ", function: $func" if ($func);
+ print " not taken:\n";
if ($func_ln) {
$ctx_lines = $ln - $func_ln;
@@ -1248,14 +1254,14 @@ sub check_source_branches($)
$ctx_lines = 10;
}
-
# Search for up to $ctx_lines before the occurrence
my $context = "";
my $has_if;
$ln-- if ($ln > 0);
my $delim = "->";
for (my $if_ln = $ln; $if_ln >= 0 && (($ln - $if_ln) < $ctx_lines); $if_ln--) {
- $context = "$if_ln$delim\t$lines[$if_ln]" . $context;
+ my $cur_ln = $if_ln + 1;
+ $context = "$cur_ln$delim\t$lines[$if_ln]" . $context;
$delim = "";
if ($lines[$if_ln] =~ m/(\bif\b|#if)/) {
$has_if = 1;
@@ -1265,7 +1271,8 @@ sub check_source_branches($)
if ($has_if) {
print $context;
} else {
- print "$ln->\t$lines[$ln]";
+ my $cur_ln = $ln + 1;
+ print "$cur_ln->\t$lines[$ln]";
}
}
}
@@ -1306,6 +1313,7 @@ GetOptions(
"source-filters|S=s" => \$src_filters,
"include-source=s" => \@src_include_regexes,
"exclude-source=s" => \@src_exclude_regexes,
+ "ignore-lines-without-functions!" => \$ignore_lines_without_functions,
"show-files|show_files" => \$show_files,
"show-lines|show_lines" => \$show_lines,
"report|r=s" => \$gen_report,
@@ -1426,6 +1434,10 @@ print_summary() if ($stat);
if ($has_filter) {
my $percent = 100. * $stats{"used_files"} / $stats{"all_files"};
+ if (!$can_filter_lines) {
+ printf "Warning......: Function filters won't work with lines/branches\n";
+ }
+
printf "Filters......:%s.\n", $filter_str;
printf "Source files.: %.2f%% (%d of %d total)",
$percent, $stats{"used_files"}, $stats{"all_files"};
@@ -1677,6 +1689,25 @@ report and decrease the code coverage statistics.
This option is automaticaly enabled when B<--func-filters> is used.
+=item B<--ignore-lines-without-functions>
+
+This option works only when the input file is in JSON format.
+
+Basically, include files may contain several lines of codes that aren't
+assigned to any functions inside a source file. This is a common behavior
+for macros and inlined functions inside headers.
+
+When this option is selected, the branches stat won't contain any such code.
+
+Please notice that this is enabled by default.
+
+Use B<--no-ignore-lines-without-functions> to disable it.
+
+=item B<--no-ignore-lines-without-functions>
+
+Disables filtering out branches that are not associated with any functions
+inside the source file, but were imported via includes.
+
=back
=item B<--show-files> or B<--show_files>
--
2.39.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [igt-dev] [PATCH i-g-t 10/12] code_cov_parse_info: filter out branches from headers by default
2023-01-17 14:05 [igt-dev] [PATCH i-g-t 00/12] Improve code coverage tool Mauro Carvalho Chehab
` (8 preceding siblings ...)
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 09/12] code_cov_parse_info: better handle branch filtering Mauro Carvalho Chehab
@ 2023-01-17 14:06 ` Mauro Carvalho Chehab
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 11/12] code_cov_parse_info: add support for filtering branches Mauro Carvalho Chehab
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 12/12] code_cov_parse_info: add support for filtering Xe driver data Mauro Carvalho Chehab
11 siblings, 0 replies; 14+ messages in thread
From: Mauro Carvalho Chehab @ 2023-01-17 14:06 UTC (permalink / raw)
To: igt-dev
From: Mauro Carvalho Chehab <mchehab@kernel.org>
It is really tricky to handle branches on header files at the Linux
Kernel. Filter them out by default, allowing the user to override it.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
scripts/code_cov_parse_info | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index 5a6a4a407359..4aed3d67bd98 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -27,6 +27,7 @@ my @src_include_regexes;
my @src_exclude_regexes;
my $can_filter_lines = 1;
my $ignore_lines_without_functions = 1;
+my $ignore_branches_on_headers = 1;
my $verbose = 0;
my $ignore_unused = 0;
@@ -182,6 +183,10 @@ sub parse_json_gcov_v1($$)
}
$all_line{$source}{$ln} += $line_ref->{'count'};
+ if ($ignore_branches_on_headers) {
+ next if ($source =~ m/.h$/);
+ }
+
my $i = 0;
for my $branch_ref (@{$line_ref->{'branches'}}) {
my $where = sprintf "%d,%d,%d", $ln, 0, $i;
@@ -527,6 +532,10 @@ sub read_info($)
my $branch = $3;
my $taken = $4;
+ if ($ignore_branches_on_headers) {
+ next if ($source =~ m/.h$/);
+ }
+
if ($block != 0) {
print "Warning: unexpected block $block at line $.\n";
}
@@ -1314,6 +1323,7 @@ GetOptions(
"include-source=s" => \@src_include_regexes,
"exclude-source=s" => \@src_exclude_regexes,
"ignore-lines-without-functions!" => \$ignore_lines_without_functions,
+ "ignore-branches-on-headers!" => \$ignore_branches_on_headers,
"show-files|show_files" => \$show_files,
"show-lines|show_lines" => \$show_lines,
"report|r=s" => \$gen_report,
@@ -1708,6 +1718,30 @@ Use B<--no-ignore-lines-without-functions> to disable it.
Disables filtering out branches that are not associated with any functions
inside the source file, but were imported via includes.
+See B<--ignore-lines-without-functions> for more details.
+
+=item B<--ignore-branches-on-headers>
+
+Branches on header files are really tricky to parse, as they depend
+on how gcc optimizes the output code. That's specially hard to use on
+Linux Kernel, as there are lots of complex macros that can be optimized
+on different ways. There are even some cases where the same macro sometimes
+have zero branches, while on other cases it can contain dozen ones.
+
+When this option is selected, all branches inside header files will be
+ignored.
+
+Please notice that this is enabled by default.
+
+Use B<--no-ignore-branches-on-headers> to disable this filter, preserving
+data from all branches.
+
+=item B<--no-ignore-branches-on-headers>
+
+Disables filtering out branches that are inside header files.
+
+See B<--ignore-branches-on-headers> for more details.
+
=back
=item B<--show-files> or B<--show_files>
--
2.39.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [igt-dev] [PATCH i-g-t 11/12] code_cov_parse_info: add support for filtering branches
2023-01-17 14:05 [igt-dev] [PATCH i-g-t 00/12] Improve code coverage tool Mauro Carvalho Chehab
` (9 preceding siblings ...)
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 10/12] code_cov_parse_info: filter out branches from headers by default Mauro Carvalho Chehab
@ 2023-01-17 14:06 ` Mauro Carvalho Chehab
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 12/12] code_cov_parse_info: add support for filtering Xe driver data Mauro Carvalho Chehab
11 siblings, 0 replies; 14+ messages in thread
From: Mauro Carvalho Chehab @ 2023-01-17 14:06 UTC (permalink / raw)
To: igt-dev
From: Mauro Carvalho Chehab <mchehab@kernel.org>
Add support for passing regexes to be used to filter branches.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
scripts/code_cov_parse_info | 178 ++++++++++++++++++++++++++++++++----
1 file changed, 159 insertions(+), 19 deletions(-)
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index 4aed3d67bd98..2c3283cc1119 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -22,12 +22,16 @@ my %record;
my %files;
my @func_include_regexes;
my @func_exclude_regexes;
+my @branch_include_regexes;
+my @branch_exclude_regexes;
my %test_names;
my @src_include_regexes;
my @src_exclude_regexes;
+my $source_dir = ".";
my $can_filter_lines = 1;
my $ignore_lines_without_functions = 1;
my $ignore_branches_on_headers = 1;
+my $has_branch_filter;
my $verbose = 0;
my $ignore_unused = 0;
@@ -88,6 +92,32 @@ sub is_file_excluded($)
return 1;
}
+sub is_branch_excluded($)
+{
+ return 0 if (!@branch_include_regexes && !@branch_exclude_regexes);
+
+ my $branch = shift;
+
+ # Handle includes first, as, when there are both include and exclude
+ # includes should take preference, as they can be overriding exclude
+ # rules
+ foreach my $r (@branch_include_regexes) {
+ return 0 if ($branch =~ m/$r/);
+ }
+
+ foreach my $r (@branch_exclude_regexes) {
+ return 1 if ($branch =~ m/$r/);
+ }
+
+ # If there are no exclude regexes, only include branches that are
+ # explicitly included.
+ if ($#branch_exclude_regexes == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
# Use something that comes before any real function
my $before_sf = "!!!!";
@@ -96,7 +126,6 @@ sub parse_json_gcov_v1($$)
my $file = shift;
my $json = shift;
- my $was_used = 0;
my $has_func = 0;
my $ignore = 0;
@@ -116,8 +145,10 @@ sub parse_json_gcov_v1($$)
# Store test name at the record
$record{tests}{$cur_test} = 1;
+ my %cached;
for my $file_ref (@{$json->{'files'}}) {
my $source = $file_ref->{'file'};
+ my $was_used = 0;
$files{$source} = 1;
next if is_file_excluded($source);
@@ -150,8 +181,6 @@ sub parse_json_gcov_v1($$)
$was_used = 1;
}
}
- next if ($ignore_unused && !$was_used);
- $used_source{$source} = 1;
# Parse lines and branches
for my $line_ref (@{$file_ref->{'lines'}}) {
@@ -191,12 +220,32 @@ sub parse_json_gcov_v1($$)
for my $branch_ref (@{$line_ref->{'branches'}}) {
my $where = sprintf "%d,%d,%d", $ln, 0, $i;
+ # Filter out branches
+ if ($has_branch_filter) {
+ if (!$cached{$source}) {
+ open IN, "$source_dir/$source" || die "File $source_dir/$source not found. Can't filter branches\n";
+ push @{$cached{$source}}, <IN>;
+ close IN;
+ }
+ my $nlines = scalar(@{$cached{$source}});
+ if ($ln > $nlines) {
+ die "$source:$ln line is bigger than the number of lines at the file ($nlines lines)\n";
+ return;
+ }
+ next if (is_branch_excluded($cached{$source}[$ln - 1]));
+ }
+
if ($func eq $before_sf) {
# Ignore DA/BRDA that aren't associated with
# functions. Those are present on header files
# (maybe defines?)
next if ($ignore_lines_without_functions);
+
+ # Otherwise place them in separate
+ $func = $before_sf;
} else {
+ next if is_function_excluded($func);
+
$all_branch{$source}{$where}{func} = $func;
}
@@ -216,6 +265,7 @@ sub parse_json_gcov_v1($$)
}
$all_branch{$source}{$where}{count} += $branch_ref->{'count'};
+ $was_used = 1 if ($branch_ref->{'count'} > 0);
$i++;
}
@@ -223,6 +273,8 @@ sub parse_json_gcov_v1($$)
@{$record{files}{$source}{line}{$ln}{branches}} = ();
}
}
+ next if ($ignore_unused && !$was_used);
+ $used_source{$source} = 1;
}
# As the record was changed, we need to use a different format name
@@ -234,9 +286,9 @@ sub parse_json_internal_format_v1($$)
my $file = shift;
my $json = shift;
- my $was_used = 0;
my $has_func = 0;
my $ignore = 0;
+ my %cached;
# Store the common JSON data into the record
for my $key (keys %$json) {
@@ -253,6 +305,8 @@ sub parse_json_internal_format_v1($$)
for my $source (keys %{$json->{'files'}}) {
$files{$source} = 1;
+ my $was_used = 0;
+
next if is_file_excluded($source);
my $file_ref = \%{$json->{'files'}{$source}};
@@ -280,8 +334,6 @@ sub parse_json_internal_format_v1($$)
$was_used = 1;
}
}
- next if ($ignore_unused && !$was_used);
- $used_source{$source} = 1;
# Parse lines and branches
for my $ln (keys %{$file_ref->{line}}) {
@@ -313,19 +365,33 @@ sub parse_json_internal_format_v1($$)
}
$all_line{$source}{$ln} += $line_ref->{'count'};
+ if ($ignore_branches_on_headers) {
+ next if ($source =~ m/.h$/);
+ }
+
my $i = 0;
for my $branch_ref (@{$line_ref->{'branches'}}) {
my $taken = $branch_ref->{'count'};
my $where = sprintf "%d,%d,%d", $ln, 0, $i;
- # Negative gcov results are possible, as
- # reported at:
- # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67937
- # Lcov ignores those. So, let's do the same
- # here.
- $branch_ref->{'count'} = 0 if ($branch_ref->{'count'} < 0);
+
+ # Filter out branches
+ if ($has_branch_filter) {
+ if (!$cached{$source}) {
+ open IN, "$source_dir/$source" || die "File $source_dir/$source not found. Can't filter branches\n";
+ push @{$cached{$source}}, <IN>;
+ close IN;
+ }
+ my $nlines = scalar(@{$cached{$source}});
+ if ($ln > $nlines) {
+ die "$source:$ln line is bigger than the number of lines at the file ($nlines lines)\n";
+ return;
+ }
+ next if (is_branch_excluded($cached{$source}[$ln - 1]));
+ }
for my $key (keys %$branch_ref) {
+ next if (!$record{files}{$source}{line}{$ln}{branches});
if ($key eq "count") {
$record{files}{$source}{line}{$ln}{branches}[$i]{$key} += $branch_ref->{$key};
} else {
@@ -334,12 +400,15 @@ sub parse_json_internal_format_v1($$)
}
$all_branch{$source}{$where}{count} += $taken;
+ $was_used = 1 if ($taken > 0);
$i++;
}
if (!defined($record{files}{$source}{line}{$ln}{branches})) {
@{$record{files}{$source}{line}{$ln}{branches}} = ();
}
}
+ next if ($ignore_unused && !$was_used);
+ $used_source{$source} = 1;
}
}
@@ -391,6 +460,7 @@ sub read_info($)
my $source = $before_sf;
my $func = $before_sf;
my $cur_test = "";
+ my %cached;
# Info files don't contain functions for lines. So, they can't
# be used to filter lines and branches used inside functions.
@@ -536,6 +606,22 @@ sub read_info($)
next if ($source =~ m/.h$/);
}
+ # Filter out branches
+ if ($has_branch_filter) {
+ if (!$cached{$source}) {
+ open IN, "$source_dir/$source" || die "File $source_dir/$source not found. Can't filter branches\n";
+ push @{$cached{$source}}, <IN>;
+ close IN;
+ }
+ my $nlines = scalar(@{$cached{$source}});
+ die "File $source_dir/$source not found or it is empty. Can't filter branches\n" if (!$nlines);
+ if ($ln > $nlines) {
+ die "$source:$ln line is bigger than the number of lines at the file ($nlines lines)\n";
+ return;
+ }
+ next if (is_branch_excluded($cached{$source}[$ln - 1]));
+ }
+
if ($block != 0) {
print "Warning: unexpected block $block at line $.\n";
}
@@ -681,6 +767,7 @@ sub write_info_file($)
foreach my $ln(sort { $a <=> $b } keys %{ $record{files}{$source}{line} }) {
$data .= "DA:$ln," . $record{files}{$source}{line}{$ln}{count} . "\n";
+ next if (!$record{files}{$source}{line}{$ln}{branches});
for (my $i = 0; $i < scalar @{$record{files}{$source}{line}{$ln}{branches}}; $i++) {
my $taken = $record{files}{$source}{line}{$ln}{branches}[$i]{count};
$taken = "-" if (!$taken);
@@ -793,7 +880,7 @@ sub gen_stats()
# per-file coverage stats
$stats{"all_files"} = scalar keys(%files);
- $stats{"filtered_files"} = scalar keys(%record);
+ $stats{"filtered_files"} = scalar keys(%{$record{files}});
$stats{"used_files"} = scalar keys(%used_source);
}
@@ -1218,11 +1305,8 @@ sub generate_report($)
close OUT;
}
-sub check_source_branches($)
+sub check_source_branches()
{
- my $source_dir = shift;
- my $cached = "";
-
foreach my $source (sort keys(%all_branch)) {
next if (!$used_source{$source});
next if (is_file_excluded($source));
@@ -1299,12 +1383,12 @@ my $help;
my $man;
my $func_filters;
my $src_filters;
+my $branch_filters;
my $show_files;
my $show_lines;
my $only_i915;
my $only_drm;
my $check_branches;
-my $source_dir = ".";
GetOptions(
"print-coverage|print_coverage|print|p" => \$print_used,
@@ -1319,6 +1403,9 @@ GetOptions(
"func-filters|f=s" => \$func_filters,
"include-func=s" => \@func_include_regexes,
"exclude-func=s" => \@func_exclude_regexes,
+ "branch-filters|f=s" => \$branch_filters,
+ "include-branch=s" => \@branch_include_regexes,
+ "exclude-branch=s" => \@branch_exclude_regexes,
"source-filters|S=s" => \$src_filters,
"include-source=s" => \@src_include_regexes,
"exclude-source=s" => \@src_exclude_regexes,
@@ -1381,6 +1468,14 @@ if ($str) {
$has_filter = 1;
}
+$str = open_filter_file($branch_filters, \@branch_include_regexes, \@branch_exclude_regexes);
+if ($str) {
+ $filter_str .= "," if ($filter_str ne "");
+ $filter_str .= " branch regex ($str)";
+ $has_filter = 1;
+ $has_branch_filter = 1;
+}
+
$ignore_unused = 1 if (@func_include_regexes || @func_exclude_regexes);
if ($ignore_unused) {
@@ -1432,7 +1527,7 @@ gen_stats();
if ($check_branches) {
- check_source_branches($source_dir);
+ check_source_branches();
}
die "Nothing counted. Wrong input files?" if (!$stats{"all_files"});
@@ -1647,6 +1742,51 @@ Include B<regex> to the function filter. Can be used multiple times.
Please notice that, when this filter is used, B<--ignore-unused> will be
automaticaly enabled, as the final goal is to report per-function usage.
+=item B<--branch-filters> B<[filter's file]> or B<-f> B<[filter's file]>
+
+Use a file containing regular expressions (regex) to filter branches.
+
+Each line at B<[filter's file]> may contain a new regex:
+
+=over 4
+
+- Blank lines and lines starting with B<#> will be ignored;
+
+- Each line of the file will be handled as a new regex;
+
+- If B<+regex> is used, the filter will include B<regex> to the matches;
+
+- If B<-regex> is used, the filter will exclude B<regex> from the matches;
+
+- If the line doesn't start with neither B<+> nor B<->, containing just
+ B<regex>, the filter will include B<regex> to the matches.
+
+- Any whitespace/tab before or after B<regex> will be ignored.
+
+=back
+
+Include regexes are handled first, as they can override an exclude regex.
+
+When just include regexes are used, any branches that don't match the
+include regular expressions from the B<[filter's file]> will be ignored.
+
+=item B<--include-branch> B<regex>
+
+Include B<regex> to the branch filter. Can be used multiple times.
+
+When used together with B<--branch-filters> or B<--exclude-branch>, regexes
+here are handled first.
+
+Please notice that, when this filter is used, B<--ignore-unused> will be
+automaticaly enabled, as the final goal is to report per-branch usage.
+
+=item B<--exclude-branch> B<regex>
+
+Include B<regex> to the branchtion filter. Can be used multiple times.
+
+Please notice that, when this filter is used, B<--ignore-unused> will be
+automaticaly enabled, as the final goal is to report per-branch usage.
+
=item B<--source-filters> B<[filter's file]> or B<-S> B<[filter's file]>
Use a file containing regular expressions to filter source files.
--
2.39.0
^ permalink raw reply related [flat|nested] 14+ messages in thread* [igt-dev] [PATCH i-g-t 12/12] code_cov_parse_info: add support for filtering Xe driver data
2023-01-17 14:05 [igt-dev] [PATCH i-g-t 00/12] Improve code coverage tool Mauro Carvalho Chehab
` (10 preceding siblings ...)
2023-01-17 14:06 ` [igt-dev] [PATCH i-g-t 11/12] code_cov_parse_info: add support for filtering branches Mauro Carvalho Chehab
@ 2023-01-17 14:06 ` Mauro Carvalho Chehab
11 siblings, 0 replies; 14+ messages in thread
From: Mauro Carvalho Chehab @ 2023-01-17 14:06 UTC (permalink / raw)
To: igt-dev
From: Mauro Carvalho Chehab <mchehab@kernel.org>
Just like it does for i915, add an option to get code coverage
data from Xe driver.
For now, it won't be taking DRM core stuff into account; just
the Xe driver code itself.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
scripts/code_cov_parse_info | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index 2c3283cc1119..d133ef1a18d4 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -1387,6 +1387,7 @@ my $branch_filters;
my $show_files;
my $show_lines;
my $only_i915;
+my $only_xe;
my $only_drm;
my $check_branches;
@@ -1399,6 +1400,7 @@ GetOptions(
"verbose|v" => \$verbose,
"ignore-unused|ignore_unused" => \$ignore_unused,
"only-i915|only_i915" => \$only_i915,
+ "only-xe|only_xe" => \$only_xe,
"only-drm|only_drm" => \$only_drm,
"func-filters|f=s" => \$func_filters,
"include-func=s" => \@func_include_regexes,
@@ -1448,6 +1450,14 @@ if ($only_i915) {
push @src_include_regexes, "drm/vgem";
}
+if ($only_xe) {
+ # Please keep in sync with the documentation
+ push @src_exclude_regexes, "selftest";
+ push @src_include_regexes, "drm/xe";
+# push @src_include_regexes, "drm/ttm";
+# push @src_include_regexes, "drm/vgem";
+}
+
if ($only_drm) {
# Please keep in sync with the documentation
push @src_exclude_regexes, "trace.*\.h\$";
@@ -1493,7 +1503,9 @@ foreach my $f (@ARGV) {
if ($gen_report) {
$f =~ s,.*/,,;
+ $f =~ s/\.gz$//;
$f =~ s/\.info$//;
+ $f =~ s/\.json$//;
gen_stats();
@@ -1694,6 +1706,19 @@ Excluding files that match:
- selftest
+=item B<--only-xe> or B<--only_xe>
+
+Filters out C files and headers outside drm core and drm/i915.
+
+E. g. code coverage results will include only the files that that match
+the following regular expressions:
+
+ - drm/xe/
+
+Excluding files that match:
+
+ - selftest
+
=item B<--func-filters> B<[filter's file]> or B<-f> B<[filter's file]>
Use a file containing regular expressions (regex) to filter functions.
--
2.39.0
^ permalink raw reply related [flat|nested] 14+ messages in thread