[igt-dev] [PATCH i-g-t 11/12] code_cov_parse_info: add support for filtering branches
Mauro Carvalho Chehab
mauro.chehab at linux.intel.com
Tue Jan 17 14:06:06 UTC 2023
From: Mauro Carvalho Chehab <mchehab at kernel.org>
Add support for passing regexes to be used to filter branches.
Signed-off-by: Mauro Carvalho Chehab <mchehab at 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
More information about the igt-dev
mailing list