[igt-dev] [PATCH i-g-t 11/12] code_cov_parse_info: add support for filtering branches
Kamil Konieczny
kamil.konieczny at linux.intel.com
Wed Jan 25 15:49:22 UTC 2023
Hi Mauro,
On 2023-01-17 at 15:06:06 +0100, Mauro Carvalho Chehab wrote:
> 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$/);
----------------------------------------------------- ^
imho here we should escape dot, so m/\.h$/
Rest looks good,
Acked-by: Kamil Konieczny <kamil.konieczny at linux.intel.com>
--
Kamil
> + }
> +
> 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