All of lore.kernel.org
 help / color / mirror / Atom feed
* [igt-dev] [PATCH 00/12] code coverage: some improvements
@ 2022-04-04  6:26 Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 01/12] scripts/code_cov*: remove the extensions from them Mauro Carvalho Chehab
                   ` (13 more replies)
  0 siblings, 14 replies; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

This series:

1. remove the code coverage script extensions and install them
   on bindir;
2. adds a small script to test if the code coverage and its
   scripts are working properly, if excecuted with a kernel
   compiled with code coverage support;
3. adds a script that allows filtering, printing statistics,
   printing used/unused functions and generating html reports.

The parsing script enhances the code coverage data collect, as it
allows quickly checking the code coverage results per test.

For instance, using the script below:

<script>
TESTLIST="my_tests.testlist"
OUT_DIR="${HOME}/results"

mkdir -p $OUT_DIR/html

echo "igt@debugfs_test@read_all_entries" > $TESTLIST
echo "igt@core_auth@basic-auth" >> $TESTLIST
echo "igt@gem_exec_basic@basic" >> $TESTLIST

sudo IGT_KERNEL_TREE="${HOME}/linux" igt_runner -s -o --coverage-per-test \
                  --collect-script code_cov_capture --test-list $TESTLIST \
                  /usr/local/libexec/igt-gpu-tools $OUT_DIR/ | sed s,$HOME/,,

sudo chown -R $(id -u):$(id -g) $OUT_DIR/

for i in $OUT_DIR/code_cov/*.info; do
	echo -e "\n$(basename $i):"
        code_cov_parse_info --only-drm --ignore-unused --stat $i
done
echo -e "\nTOTAL:"
code_cov_parse_info --only-drm --stat --output $OUT_DIR/results.info \
        $OUT_DIR/code_cov/*.info

cd $OUT_DIR/html
genhtml -q -s --legend --branch-coverage $OUT_DIR/results.info
</script>

The parse script will filter and display the function, branch and
like coverage per test, and the total one:

core_auth_basic_auth.info:
  lines......: 11.7% (8219 of 70257 lines)
  functions..: 7.1% (776 of 10971 functions)
  branches...: 7.0% (3597 of 51041 branches)
Ignored......: non-drm headers and source files where none of its code ran.
Source files.: 23.27% (165 of 709 total), 29.57% (165 of 558 filtered)

debugfs_test_read_all_entries.info:
  lines......: 19.3% (20249 of 104802 lines)
  functions..: 17.5% (1922 of 10971 functions)
  branches...: 12.7% (9449 of 74555 branches)
Ignored......: non-drm headers and source files where none of its code ran.
Source files.: 34.70% (246 of 709 total), 44.09% (246 of 558 filtered)

gem_exec_basic_basic.info:
  lines......: 17.4% (15196 of 87570 lines)
  functions..: 13.1% (1437 of 10971 functions)
  branches...: 10.3% (6562 of 63806 branches)
Ignored......: non-drm headers and source files where none of its code ran.
Source files.: 31.17% (221 of 709 total), 39.61% (221 of 558 filtered)

TOTAL:
  lines......: 15.6% (26031 of 166849 lines)
  functions..: 22.3% (2443 of 10971 functions)
  branches...: 10.6% (11968 of 112665 branches)
Ignored......: non-drm headers.
Source files.: 78.70% (558 of 709 total)

A much more detailed report can also be produced with --print,
--print-unused (optionally with --show-lines). For instance,
reporting code coverage usage for functions with "edid_" on
its name is as simple as:

$ echo "edid_" >filter.txt; code_cov_parse_info --func-filters filter.txt results/results.info --print --print-unused
TEST: Code_coverage_tests
__drm_get_edid_firmware_path(): unused
__drm_set_edid_firmware_path(): unused
displayid_iter_edid_begin(): executed 10 times
drm_add_edid_modes(): executed 2 times
drm_add_override_edid_modes(): unused
drm_connector_attach_edid_property(): unused
drm_connector_update_edid_property(): executed 8 times
drm_dp_send_real_edid_checksum(): unused
drm_edid_are_equal(): executed 4 times
drm_edid_block_valid(): executed 8 times
drm_edid_duplicate(): unused
drm_edid_get_monitor_name(): unused
drm_edid_header_is_valid(): executed 4 times
drm_edid_is_valid(): executed 2 times
drm_edid_to_eld(): executed 2 times
drm_edid_to_sad(): unused
drm_edid_to_speaker_allocation(): unused
drm_find_edid_extension(): executed 22 times
drm_get_edid_switcheroo(): unused
drm_load_edid_firmware(): executed 2 times
edid_firmware_get(): unused
edid_firmware_set(): unused
edid_fixup_preferred(): unused
edid_get_quirks(): executed 6 times
edid_load(): unused
edid_open(): executed 4 times
edid_show() from linux/drivers/gpu/drm/drm_debugfs.c: executed 4 times
edid_show() from linux/drivers/gpu/drm/drm_sysfs.c: unused
edid_vendor(): executed 348 times
edid_write(): unused
intel_panel_edid_downclock_mode(): unused
intel_panel_edid_fixed_mode(): unused
is_edid_digital_input_dp(): unused
Ignored......: unmatched functions m/(?^:edid_)/ and source files where none of its code ran.
Source files.: 0.90% (5 of 558 total), 55.56% (5 of 9 filtered)

Finally, it can place the data above on an html report and, when
multiple .info files are used, it places the data colleced from
each .info file on a row, allowing to compare the code coverage
from different tests and from different machines.

Regards,
Mauro

---


Mauro Carvalho Chehab (12):
  scripts/code_cov*: remove the extensions from them
  scripts/code_cov_parse_info: add a tool to parse code coverage info
    files
  scripts/code_cov_gen_report: add support for filtering info files
  runner: execute code coverage script also from PATH
  scripts/meson.build: install code coverage scripts
  scripts/code_cov_selftest.sh: test if IGT code coverage is working
  docs/code_coverage.md: document the code coverage filter script
  scripts/code_cov_parse_info: better handle test name
  code_cov_parse_info: fix error handling when opening files
  code_cov_parse_info: fix --show-lines logic
  code_cov_parse_info: add support for exclude filters
  code_cov_parse_info: add support for generating html reports

 docs/code_coverage.md                         |  238 +++-
 meson.build                                   |    1 +
 runner/settings.c                             |  109 +-
 .../{code_cov_capture.sh => code_cov_capture} |    0
 ...r_on_build.sh => code_cov_gather_on_build} |    0
 ...her_on_test.py => code_cov_gather_on_test} |    0
 ..._cov_gen_report.sh => code_cov_gen_report} |   19 +
 scripts/code_cov_parse_info                   | 1169 +++++++++++++++++
 scripts/code_cov_selftest.sh                  |   47 +
 scripts/meson.build                           |   13 +
 10 files changed, 1541 insertions(+), 55 deletions(-)
 rename scripts/{code_cov_capture.sh => code_cov_capture} (100%)
 rename scripts/{code_cov_gather_on_build.sh => code_cov_gather_on_build} (100%)
 rename scripts/{code_cov_gather_on_test.py => code_cov_gather_on_test} (100%)
 rename scripts/{code_cov_gen_report.sh => code_cov_gen_report} (85%)
 create mode 100755 scripts/code_cov_parse_info
 create mode 100755 scripts/code_cov_selftest.sh
 create mode 100644 scripts/meson.build

-- 
2.35.1

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [igt-dev] [PATCH 01/12] scripts/code_cov*: remove the extensions from them
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
@ 2022-04-04  6:26 ` Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 02/12] scripts/code_cov_parse_info: add a tool to parse code coverage info files Mauro Carvalho Chehab
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

As those scripts will be installed and executed from the PATH,
remove the extensions from them, in order to make it more elegant
when installed on distros.

Reviewed-by: Petri Latvala <petri.latvala@intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 scripts/{code_cov_capture.sh => code_cov_capture}                 | 0
 scripts/{code_cov_gather_on_build.sh => code_cov_gather_on_build} | 0
 scripts/{code_cov_gather_on_test.py => code_cov_gather_on_test}   | 0
 scripts/{code_cov_gen_report.sh => code_cov_gen_report}           | 0
 4 files changed, 0 insertions(+), 0 deletions(-)
 rename scripts/{code_cov_capture.sh => code_cov_capture} (100%)
 rename scripts/{code_cov_gather_on_build.sh => code_cov_gather_on_build} (100%)
 rename scripts/{code_cov_gather_on_test.py => code_cov_gather_on_test} (100%)
 rename scripts/{code_cov_gen_report.sh => code_cov_gen_report} (100%)

diff --git a/scripts/code_cov_capture.sh b/scripts/code_cov_capture
similarity index 100%
rename from scripts/code_cov_capture.sh
rename to scripts/code_cov_capture
diff --git a/scripts/code_cov_gather_on_build.sh b/scripts/code_cov_gather_on_build
similarity index 100%
rename from scripts/code_cov_gather_on_build.sh
rename to scripts/code_cov_gather_on_build
diff --git a/scripts/code_cov_gather_on_test.py b/scripts/code_cov_gather_on_test
similarity index 100%
rename from scripts/code_cov_gather_on_test.py
rename to scripts/code_cov_gather_on_test
diff --git a/scripts/code_cov_gen_report.sh b/scripts/code_cov_gen_report
similarity index 100%
rename from scripts/code_cov_gen_report.sh
rename to scripts/code_cov_gen_report
-- 
2.35.1

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [igt-dev] [PATCH 02/12] scripts/code_cov_parse_info: add a tool to parse code coverage info files
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 01/12] scripts/code_cov*: remove the extensions from them Mauro Carvalho Chehab
@ 2022-04-04  6:26 ` Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 03/12] scripts/code_cov_gen_report: add support for filtering " Mauro Carvalho Chehab
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

The code coverage info files provide useful information about what
functions were called and how many times. It can also contain data that
it is not useful for the code coverage, like calls to non-DRM related
Kernel APIs.

Add a tool that helps filtering out non-DRM files and prints what
functions were called.

Both the stats, prints and output files are affected by the filters:

$ code_cov_parse_info --stat total.info
  lines......: 40.8% (71989 of 176442 lines)
  functions..: 50.2% (5975 of 11909 functions)
  branches...: 28.7% (35006 of 121893 branches)
Source files: 744

$ code_cov_parse_info total.info --stat --only-drm
  lines......: 40.3% (70244 of 174170 lines)
  functions..: 50.8% (5835 of 11491 functions)
  branches...: 28.4% (33096 of 116707 branches)
Ignored......: non-drm headers.
Source files.: 78.36% (583 of 744 total)

$ code_cov_parse_info total.info --stat --only-drm --ignore-unused -o o.info
  lines......: 47.9% (70244 of 146752 lines)
  functions..: 50.8% (5835 of 11491 functions)
  branches...: 32.8% (33096 of 100900 branches)
Ignored......: non-drm headers and source files where none of its code ran.
Source files.: 58.87% (438 of 744 total), 75.13% (438 of 583 filtered)

$ code_cov_parse_info --stat o.info
  lines......: 47.9% (70244 of 146752 lines)
  functions..: 59.8% (5835 of 9763 functions)
  branches...: 32.8% (33096 of 100900 branches)
Source files: 438

Reviewed-by: Andrzej Hajda <andrzej.hajda@intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 scripts/code_cov_parse_info | 775 ++++++++++++++++++++++++++++++++++++
 1 file changed, 775 insertions(+)
 create mode 100755 scripts/code_cov_parse_info

diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
new file mode 100755
index 000000000000..604812b41580
--- /dev/null
+++ b/scripts/code_cov_parse_info
@@ -0,0 +1,775 @@
+#!/usr/bin/perl
+# SPDX-License-Identifier: MIT
+use strict;
+use warnings;
+use Getopt::Long;
+BEGIN { $Pod::Usage::Formatter = 'Pod::Text::Termcap'; }
+use Pod::Usage;
+use Pod::Man;
+
+my $prefix = qr ".*?(linux)\w*/";
+
+my %used_func;
+my %all_func;
+my %all_branch;
+my %all_line;
+my %used_source;
+my %record;
+my %files;
+my @func_regexes;
+my @src_regexes;
+my $testname = "";
+
+my $verbose = 0;
+my $ignore_unused = 0;
+my $only_i915 = 0;
+my $only_drm = 0;
+my $skip_func = 0;
+
+sub is_function_excluded($)
+{
+	return 0 if (!@func_regexes);
+
+	my $func = shift;
+
+	foreach my $r (@func_regexes) {
+		return 0 if ($func =~ m/$r/);
+	}
+
+	return 1;
+}
+
+sub filter_file($)
+{
+	my $s = shift;
+
+	if ($only_drm) {
+		# Please keep --only-drm doc updated with any changes her
+		if ($s =~ m/\.h$/) {
+			if ($s =~ m/trace/ || !($s =~ m/drm/)) {
+				return 1;
+			}
+		}
+	}
+
+	if ($only_i915) {
+		# Please keep --only-i915 doc updated with any changes here
+		if ($s =~ m/selftest/) {
+			return 1;
+		}
+
+		# Please keep --only-i915 doc updated with any changes here
+		if (!($s =~ m#drm/i915/# || $s =~ m#drm/ttm# || $s =~ m#drm/vgem#)) {
+			return 1;
+		}
+	}
+
+	return 0 if (!@src_regexes);
+
+	my $func = shift;
+
+	foreach my $r (@src_regexes) {
+		return 0 if ($s =~ m/$r/);
+	}
+
+	return 1;
+}
+
+# Use something that comes before any real function
+my $before_sf = "!!!!";
+
+sub parse_info_data($)
+{
+	my $file = shift;
+	my $was_used = 0;
+	my $has_func = 0;
+	my $ignore = 0;
+	my $source = $before_sf;
+	my $func = $before_sf;
+	my $cur_test = "";
+
+	# 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>) {
+		# TN:<test name>
+		if (m/^TN:(.*)/) {
+			if ($1 ne $cur_test) {
+				$cur_test = $1;
+				if (!$testname) {
+					$testname = $cur_test;
+				} else {
+					$testname = "Code_coverage_tests";
+				}
+			}
+			$source = $before_sf;
+			$func = $before_sf;
+			next;
+		}
+
+		# SF:<absolute path to the source file>
+		if (m/^[SK]F:(.*)/) {
+			$source = $1;
+
+			$was_used = 0;
+			$has_func = 0;
+			$func = $before_sf;
+			$files{$source} = 1;
+
+			# Just ignore files explictly set as such
+			$ignore = filter_file($source);
+			next;
+		}
+
+		# End of record
+		if (m/^end_of_record/) {
+			if (!$source) {
+				print "bad end_of_record field at $file, line $. Ignoring...\n";
+				next;
+			}
+
+			my $s = $source;
+
+			$source = $before_sf;
+			$func = $before_sf;
+
+			next if ($ignore);
+			next if ($ignore_unused && !$was_used);
+
+			# Mark that the source was not ignored
+			$used_source{$s} = 1;
+			next;
+		}
+
+		next if ($ignore);
+
+		# Function coverage
+
+		# FN:<line number of function start>,<function name>
+		if (m/^FN:(-?\d+),(.*)/) {
+			my $ln = $1;
+
+			$func = $2;
+			$has_func = 1;
+
+			if (is_function_excluded($func)) {
+				$skip_func = 1;
+				next;
+			}
+
+			$skip_func = 0;
+
+			$record{$source}{$func}{fn} = $ln;
+			$all_func{$func}{$source}->{ln} = $ln;
+			next;
+		}
+
+		# Parse functions that were actually used
+		# FNDA:<execution count>,<function name>
+		if (m/^FNDA:(-?\d+),(.*)/) {
+			my $count = $1;
+
+			# 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.
+			next if ($count <= 0);
+
+			$func = $2;
+			$has_func = 1;
+
+			if (is_function_excluded($func)) {
+				$skip_func = 1;
+				next;
+			}
+
+			$skip_func = 0;
+			$was_used = 1;
+
+			$record{$source}{$func}{fnda} += $count;
+			$used_func{$func}{$source}->{count} += $count;
+			next;
+		}
+
+		# Ignore data from skipped functions
+		next if ($skip_func);
+
+		# Ignore DA/BRDA that aren't associated with functions
+		# Those are present on header files (maybe defines?)
+		next if (@func_regexes && !$has_func);
+
+		# 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 ($record{$source}{$func}{fnh} < $hits) {
+				$record{$source}{$func}{fnh} = $hits;
+			}
+			next;
+		}
+
+		# Branch coverage
+
+		# BRDA:<line number>,<block number>,<branch number>,<taken>
+		if (m/^BRDA:(-?\d+),(-?\d+),(-?\d+),(.*)/) {
+			my $ln = $1;
+			my $block = $2;
+			my $branch = $3;
+			my $taken = $4;
+
+			my $where = "$ln,$block,$branch";
+
+			$taken = 0 if ($taken eq '-');
+
+			# 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.
+			$taken = 0 if ($taken < 0);
+
+			$was_used = 1 if ($taken > 0);
+
+			$record{$source}{$func}{brda}{$where} += $taken;
+			$all_branch{$source}{"$where"} += $taken;
+			next;
+		}
+
+		# BRF:<number of branches found>
+		if (m/^BRF:(-?\d+)/) {
+			$record{$source}{brf} = $1;
+			next;
+		}
+		# BRH:<number of branches hit>
+		if (m/^BRH:(-?\d+)/) {
+			my $hits = $1;
+			if ($record{$source}{$func}{brh} < $hits) {
+				$record{$source}{$func}{brh} = $hits;
+			}
+			next;
+		}
+
+		# Line coverage
+
+		# DA:<line number>,<execution count>[,<checksum>]
+		if (m/^DA:(-?\d+),(-?\d+)(,.*)?/) {
+			my $ln = $1;
+			my $count = $2;
+
+			# 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.
+			$count = 0 if ($count < 0);
+
+			$was_used = 1 if ($count > 0);
+
+			$record{$source}{$func}{da}{$ln} += $count;
+			$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 ($record{$source}{$func}{lh} < $hits) {
+				$record{$source}{$func}{lh} = $hits;
+			}
+			next;
+		}
+
+		printf("Warning: invalid line: $_");
+	}
+
+	close IN or die;
+}
+
+sub write_filtered_file($)
+{
+	my $filter = shift;
+
+	# Generates filtered data
+	my $filtered = "TN:$testname\n";
+
+	foreach my $source(sort keys %record) {
+		next if (!$used_source{$source});
+
+		if ($source ne $before_sf) {
+			$filtered .= "SF:$source\n";
+		}
+
+		foreach my $func(sort keys %{ $record{$source} }) {
+			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 ($record{$source}{fnf}) {
+					$filtered .= "FNF:". $record{$source}{$func}{fnf} ."\n";
+				}
+				if ($record{$source}{fnh}) {
+					$filtered .= "FNH:". $record{$source}{$func}{fnh} ."\n";
+				}
+			}
+
+			foreach my $ln(sort keys %{ $record{$source}{$func}{da} }) {
+				$filtered .= "DA:$ln," . $record{$source}{$func}{da}{$ln} . "\n";
+			}
+			foreach my $where(sort keys %{ $record{$source}{$func}{brda} }) {
+				my $taken = $record{$source}{$func}{brda}{$where};
+				$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";
+			}
+		}
+
+		$filtered .= "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";
+}
+
+sub print_code_coverage($$$)
+{
+	my $print_used = shift;
+	my $print_unused = shift;
+	my $show_lines = shift;
+
+	return if (!$print_used && !$print_unused);
+
+	if ($testname ne "") {
+		$testname =~ s/(.*)_on_(\w+)$/$1 on $2/;
+		print "TEST: $testname\n";
+	}
+	my $prev_file = "";
+
+	foreach my $func (sort keys(%all_func)) {
+		my @keys = sort keys(%{$all_func{$func}});
+		foreach my $file (@keys) {
+			my $count = 0;
+			my $name;
+
+			if ($used_func{$func}) {
+				if ($used_func{$func}->{$file}) {
+					$count = $used_func{$func}->{$file}->{count};
+				}
+			}
+
+			if ($show_lines) {
+				$file =~ s,$prefix,linux/,;
+				$name = "$func() from $file:" . $all_func{$func}{$file}->{ln};
+			} elsif (scalar @keys > 1) {
+				$file =~ s,$prefix,linux/,;
+				$name = "$func() from $file:";
+			} else {
+				$name = "$func():";
+			}
+			if ($print_unused) {
+				if (!$count) {
+					print "$name unused\n";
+				} elsif ($print_used) {
+					print "$name executed $count times\n";
+				}
+			} elsif ($count) {
+				print "$name executed $count times\n";
+			}
+		}
+	}
+}
+
+sub print_summary()
+{
+	# Output per-line coverage statistics
+	my $line_count = 0;
+	my $line_reached = 0;
+
+	foreach my $source (keys(%all_line)) {
+		next if (!$used_source{$source});
+
+		foreach my $where (keys(%{$all_line{$source}})) {
+			$line_count++;
+			$line_reached++ if ($all_line{$source}{$where} != 0);
+		}
+	}
+	if ($line_count) {
+		my $percent = 100. * $line_reached / $line_count;
+		printf "  lines......: %.1f%% (%d of %d lines)\n",
+			$percent, $line_reached, $line_count;
+	} else {
+		print "No line coverage data.\n";
+	}
+
+	# Output per-function coverage statistics
+	my $func_count = 0;
+	my $func_used = 0;
+
+	foreach my $func (keys(%all_func)) {
+		foreach my $file (keys(%{$all_func{$func}})) {
+			$func_count++;
+			if ($used_func{$func}) {
+				if ($used_func{$func}->{$file}) {
+					$func_used++;
+				}
+			}
+		}
+	}
+
+	if ($func_count) {
+		my $percent = 100. * $func_used / $func_count;
+		printf "  functions..: %.1f%% (%d of %d functions)\n",
+			$percent, $func_used, $func_count;
+	} else {
+		print "No functions reported. Wrong filters?\n";
+		return;
+	}
+
+	# Output per-branch coverage statistics
+	my $branch_count = 0;
+	my $branch_reached = 0;
+
+	foreach my $source (keys(%all_branch)) {
+		next if (!$used_source{$source});
+
+		foreach my $where (keys(%{$all_branch{$source}})) {
+			$branch_count++;
+			$branch_reached++ if ($all_branch{$source}{$where} != 0);
+		}
+	}
+	if ($branch_count) {
+		my $percent = 100. * $branch_reached / $branch_count;
+		printf "  branches...: %.1f%% (%d of %d branches)\n",
+			$percent, $branch_reached, $branch_count;
+	} else {
+		print "No branch coverage data.\n";
+	}
+}
+
+#
+# Argument handling
+#
+
+my $print_used;
+my $print_unused;
+my $stat;
+my $filter;
+my $help;
+my $man;
+my $func_filters;
+my $src_filters;
+my $show_files;
+my $show_lines;
+
+GetOptions(
+	"print-coverage|print_coverage|print|p" => \$print_used,
+	"print-unused|u" => \$print_unused,
+	"stat|statistics" => \$stat,
+	"output|o=s" => \$filter,
+	"verbose|v" => \$verbose,
+	"ignore-unused|ignore_unused" => \$ignore_unused,
+	"only-i915|only_i915" => \$only_i915,
+	"only-drm|only_drm" => \$only_drm,
+	"func-filters|f=s" => \$func_filters,
+	"source-filters|S=s" => \$src_filters,
+	"show-files|show_files" => \$show_files,
+	"show-lines|show_lines" => \$show_lines,
+	"help" => \$help,
+	"man" => \$man,
+) or pod2usage(2);
+
+pod2usage(-verbose => 2) if $man;
+pod2usage(1) if $help;
+
+if ($#ARGV < 0) {
+	print "$0: no input files\n";
+	pod2usage(1);
+}
+
+# At least one action should be specified
+pod2usage(1) if (!$print_used && !$filter && !$stat && !$print_unused);
+
+my $filter_str = "";
+my $has_filter;
+
+if ($func_filters) {
+	open IN, $func_filters || die "Can't open $func_filters";
+	while (<IN>) {
+		s/^\s+//;
+		s/\s+$//;
+		next if (m/^#/ || m/^$/);
+		push @func_regexes, qr /$_/;
+	}
+	close IN;
+}
+
+if ($src_filters) {
+	open IN, $src_filters || die "Can't open $src_filters";
+	while (<IN>) {
+		s/^\s+//;
+		s/\s+$//;
+		next if (m/^#/ || m/^$/);
+		push @src_regexes, qr /$_/;
+	}
+	close IN;
+}
+
+$ignore_unused = 1 if (@func_regexes);
+
+if ($only_i915) {
+	$filter_str = " non-i915 files";
+	$has_filter = 1;
+}
+
+if ($only_drm) {
+	$filter_str .= "," if ($filter_str ne "");
+	$filter_str .= " non-drm headers";
+	$has_filter = 1;
+}
+
+if (@func_regexes) {
+	$filter_str .= "," if ($filter_str ne "");
+	$filter_str .= " unmatched functions";
+	foreach my $r (@func_regexes) {
+		$filter_str .= " m/$r/";
+	}
+
+	$has_filter = 1;
+}
+
+if (@src_regexes) {
+	$filter_str .= "," if ($filter_str ne "");
+	$filter_str .= " unmatched source files";
+	foreach my $r (@src_regexes) {
+		$filter_str .= " m/$r/";
+	}
+	$has_filter = 1;
+}
+
+if ($ignore_unused) {
+	$filter_str .= "," if ($filter_str ne "");
+	$filter_str .= " source files where none of its code ran";
+	$has_filter = 1;
+}
+
+foreach my $f (@ARGV) {
+	parse_info_data($f);
+}
+
+print_code_coverage($print_used, $print_unused, $show_lines);
+
+print_summary() if ($stat);
+
+my $all_files = scalar keys(%files);
+
+die "Nothing counted. Wrong input files?" if (!$all_files);
+
+if ($has_filter) {
+	my $all_files = scalar keys(%files);
+	my $filtered_files = scalar keys(%record);
+	my $used_files = scalar keys(%used_source);
+
+	my $percent = 100. * $used_files / $all_files;
+
+	$filter_str =~ s/(.*),/$1 and/;
+	printf "Ignored......:%s.\n", $filter_str;
+	printf "Source files.: %.2f%% (%d of %d total)",
+		$percent, $used_files, $all_files;
+
+	if ($used_files != $filtered_files) {
+		my $percent_filtered = 100. * $used_files / $filtered_files;
+
+		printf ", %.2f%% (%d of %d filtered)",
+			$percent_filtered, $used_files, $filtered_files;
+	}
+	print "\n";
+} else {
+	printf "Source files: %d\n", scalar keys(%files) if($stat);
+}
+
+if ($show_files) {
+	for my $f(sort keys %used_source) {
+		print "\t$f\n";
+	}
+}
+
+if ($filter) {
+	write_filtered_file($filter);
+}
+
+__END__
+
+=head1 NAME
+
+Parses lcov data from .info files.
+
+=head1 SYNOPSIS
+
+code_cov_parse_info <options> [input file(s)]
+
+At least one of the options B<--stat>, B<--print> and/or B<--output>
+should be used.
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<--stat> or B<--statistics>
+
+Prints code coverage statistics.
+
+It displays function, line, branch and file coverage percentage.
+
+It also reports when one or more of the filtering parameters are used.
+
+The statistics report is affected by the applied filters.
+
+=item B<--print-coverage> or B<--print_coverage> or B<--print> or B<-p>
+
+Prints the functions that were executed in runtime and how many times
+they were reached.
+
+The function coverage report is affected by the applied filters.
+
+=item B<--print-unused> or B<-u>
+
+Prints the functions that were never reached.
+
+The function coverage report is affected by the applied filters.
+
+
+=item B<--show-lines> or B<--show_lines>
+
+When printing per-function code coverage data, always output the source
+file and the line number where the function is defined.
+
+=item B<--output> B<[output file]> or B<-o> B<[output file]>
+
+Produce an output file merging all input files.
+
+The generated output file is affected by the applied filters.
+
+=item B<--only-drm> or B<--only_drm>
+
+Filters out includes outside the DRM subsystem, plus trace files.
+E. g. it will exclude *.h files that match the following regular expressions:
+
+	- .*trace.*\.h$
+
+And *.h files that don't match:
+
+	- drm
+
+=item B<--only-i915> or B<--only_i915>
+
+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/i915/
+	- drm/ttm
+	- drm/vgem
+
+Excluding files that match:
+
+	- selftest
+
+=item B<--func-filters>  B<[filter's file]> or B<-f>  B<[filter's file]>
+
+Take into account only the code coverage for the functions that match
+the regular expressions contained at the B<[filter's file]>.
+
+When this filter is used, B<--ignore-unused> will be automaticaly enabled,
+as the final goal is to report per-function usage, and not per-file.
+
+=item B<--source-filters>  B<[filter's file]> or B<-S>  B<[filter's file]>
+
+Takes into account only the code coverage for the source files that match
+the regular expressions contained at the B<[filter's file]>.
+
+=item B<--ignore-unused> or B<--ignore_unused>
+
+Filters out unused C files and headers from the code coverage results.
+
+Sometimes, it is desired to ignore files where none of the functions on it
+were tested.
+
+The rationale is that such files may contain platform-specific drivers
+and code that will never be used, so, placing them will just bloat the
+report and decrease the code coverage statistics.
+
+This option is automaticaly enabled when B<--func-filters> is used.
+
+=back
+
+=item B<--show-files> or B<--show_files>
+
+Shows the list of files that were used to produce the code coverage
+results.
+
+=item B<--verbose> or B<-v>
+
+Prints the name of each parsed file.
+
+=item B<--help>
+
+Print a brief help message and exits.
+
+=item B<--man>
+
+Prints the manual page and exits.
+
+=back
+
+=head1 BUGS
+
+Report bugs to Mauro Carvalho Chehab <mauro.chehab@intel.com>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2022 Intel Corporation
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+=cut
-- 
2.35.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] [PATCH 03/12] scripts/code_cov_gen_report: add support for filtering info files
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 01/12] scripts/code_cov*: remove the extensions from them Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 02/12] scripts/code_cov_parse_info: add a tool to parse code coverage info files Mauro Carvalho Chehab
@ 2022-04-04  6:26 ` Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 04/12] runner: execute code coverage script also from PATH Mauro Carvalho Chehab
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

Now that we have a script that allows filtering the results,
add support for it when generating code coverage reports.

Reviewed-by: Andrzej Hajda <andrzej.hajda@intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 scripts/code_cov_gen_report | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/scripts/code_cov_gen_report b/scripts/code_cov_gen_report
index 05efebe19ee4..b7c90b114cde 100755
--- a/scripts/code_cov_gen_report
+++ b/scripts/code_cov_gen_report
@@ -16,6 +16,8 @@ Usage:
     $(basename $0)
 	--read <file or dir> --kernel-source <dir> --kernel-object <dir>
 	--output-dir <dir> [--info or --tar] [--force-override]
+	[--print] [--stat] [--ignore-unused] [--only-i915] [--only-drm]
+	[--show-files] [--func-filters <file>] [--source-filters <file>]
 
 --kernel-object is only needed when Kernel was built with make O=dir
 "
@@ -28,6 +30,7 @@ KSRC=
 KOBJ=
 DEST_DIR=
 FORCE=
+PARSE_ARGS=
 
 while [ "$1" != "" ]; do
 	case $1 in
@@ -69,6 +72,17 @@ while [ "$1" != "" ]; do
 			shift
 		fi
 		;;
+	--print|--stat|--ignore-unused|--only-i915|--only-drm|--show-files)
+		PARSE_ARGS="$PARSE_ARGS $1"
+		;;
+	--func-filters|--source-filters)
+		if [ "$2" == "" ]; then
+			usage 1
+		else
+			PARSE_ARGS="$PARSE_ARGS $1 $2"
+			shift
+		fi
+		;;
 	--force-override|-f)
 		FORCE=1
 		;;
@@ -166,5 +180,10 @@ else
 	fi
 fi
 
+if [ "x$PARSE_ARGS" != "x" ]; then
+	${SCRIPT_DIR}/$PARSE_INFO $PARSE_ARGS --output filtered_${TITLE}.info ${TITLE}.info
+	TITLE=filtered_${TITLE}
+fi
+
 echo "Generating HTML files..."
 genhtml -q ${TITLE}.info
-- 
2.35.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] [PATCH 04/12] runner: execute code coverage script also from PATH
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
                   ` (2 preceding siblings ...)
  2022-04-04  6:26 ` [igt-dev] [PATCH 03/12] scripts/code_cov_gen_report: add support for filtering " Mauro Carvalho Chehab
@ 2022-04-04  6:26 ` Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 05/12] scripts/meson.build: install code coverage scripts Mauro Carvalho Chehab
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

In order to prepare to execute the code coverage scripts from the
PATH, change the logic at settings in order for it to seek for
the script in the PATH, if it doesn't contain any directories
on its filename.

Note: file search routines were moved (unchanged) on this path,
in order to avoid forward prototype declarations.

Reviewed-by: Petri Latvala <petri.latvala@intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 runner/settings.c | 109 +++++++++++++++++++++++++++++-----------------
 1 file changed, 68 insertions(+), 41 deletions(-)

diff --git a/runner/settings.c b/runner/settings.c
index a7a12f506a05..4571e3af9d54 100644
--- a/runner/settings.c
+++ b/runner/settings.c
@@ -363,6 +363,73 @@ static bool executable_file(const char *filename)
 	return !access(filename, X_OK);
 }
 
+static char *_dirname(const char *path)
+{
+	char *tmppath = strdup(path);
+	char *tmpname = dirname(tmppath);
+	tmpname = strdup(tmpname);
+	free(tmppath);
+	return tmpname;
+}
+
+static char *_basename(const char *path)
+{
+	char *tmppath = strdup(path);
+	char *tmpname = basename(tmppath);
+	tmpname = strdup(tmpname);
+	free(tmppath);
+	return tmpname;
+}
+
+char *absolute_path(char *path)
+{
+	char *result = NULL;
+	char *base, *dir;
+	char *ret;
+
+	result = realpath(path, NULL);
+	if (result != NULL)
+		return result;
+
+	dir = _dirname(path);
+	ret = absolute_path(dir);
+	free(dir);
+
+	base = _basename(path);
+	asprintf(&result, "%s/%s", ret, base);
+	free(base);
+	free(ret);
+
+	return result;
+}
+
+static char *bin_path(char *fname)
+{
+	char *path, *p;
+	char file[PATH_MAX];
+
+	if (strchr(fname, '/'))
+		return absolute_path(fname);
+
+	path = strdup(getenv("PATH"));
+	p = strtok(path, ":");
+	do {
+		if (*p) {
+			strcpy(file, p);
+			strcat(file, "/");
+			strcat(file, fname);
+			if (executable_file(file)) {
+				free(path);
+				return strdup(file);
+			}
+		}
+		p = strtok(NULL, ":");
+	} while (p);
+
+	free(path);
+	return strdup(fname);
+}
+
 static void print_version(void)
 {
 	struct utsname uts;
@@ -495,7 +562,7 @@ bool parse_options(int argc, char **argv,
 			settings->cov_results_per_test = true;
 			break;
 		case OPT_CODE_COV_SCRIPT:
-			settings->code_coverage_script = absolute_path(optarg);
+			settings->code_coverage_script = bin_path(optarg);
 			break;
 
 		case OPT_MULTIPLE:
@@ -656,46 +723,6 @@ bool validate_settings(struct settings *settings)
 	return true;
 }
 
-static char *_dirname(const char *path)
-{
-	char *tmppath = strdup(path);
-	char *tmpname = dirname(tmppath);
-	tmpname = strdup(tmpname);
-	free(tmppath);
-	return tmpname;
-}
-
-static char *_basename(const char *path)
-{
-	char *tmppath = strdup(path);
-	char *tmpname = basename(tmppath);
-	tmpname = strdup(tmpname);
-	free(tmppath);
-	return tmpname;
-}
-
-char *absolute_path(char *path)
-{
-	char *result = NULL;
-	char *base, *dir;
-	char *ret;
-
-	result = realpath(path, NULL);
-	if (result != NULL)
-		return result;
-
-	dir = _dirname(path);
-	ret = absolute_path(dir);
-	free(dir);
-
-	base = _basename(path);
-	asprintf(&result, "%s/%s", ret, base);
-	free(base);
-	free(ret);
-
-	return result;
-}
-
 static char settings_filename[] = "metadata.txt";
 bool serialize_settings(struct settings *settings)
 {
-- 
2.35.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] [PATCH 05/12] scripts/meson.build: install code coverage scripts
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
                   ` (3 preceding siblings ...)
  2022-04-04  6:26 ` [igt-dev] [PATCH 04/12] runner: execute code coverage script also from PATH Mauro Carvalho Chehab
@ 2022-04-04  6:26 ` Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 06/12] scripts/code_cov_selftest.sh: test if IGT code coverage is working Mauro Carvalho Chehab
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

Those scripts are needed at runtime. So, place them under
bindir.

Reviewed-by: Andrzej Hajda <andrzej.hajda@intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 meson.build         |  1 +
 scripts/meson.build | 13 +++++++++++++
 2 files changed, 14 insertions(+)
 create mode 100644 scripts/meson.build

diff --git a/meson.build b/meson.build
index f9a284d844a3..3e937f5a5b98 100644
--- a/meson.build
+++ b/meson.build
@@ -315,6 +315,7 @@ if libdrm_intel.found()
 endif
 subdir('overlay')
 subdir('man')
+subdir('scripts')
 
 gtk_doc = dependency('gtk-doc', required : build_docs)
 if build_tests and gtk_doc.found()
diff --git a/scripts/meson.build b/scripts/meson.build
new file mode 100644
index 000000000000..342972e66078
--- /dev/null
+++ b/scripts/meson.build
@@ -0,0 +1,13 @@
+scripts = [
+	'code_cov_capture',
+	'code_cov_gather_on_build',
+	'code_cov_gather_on_test',
+	'code_cov_gen_report',
+	'code_cov_parse_info',
+]
+
+if build_tests
+	foreach prog : scripts
+		install_data(prog, install_dir : bindir, install_mode : 'r-xr-xr-x')
+	endforeach
+endif
-- 
2.35.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] [PATCH 06/12] scripts/code_cov_selftest.sh: test if IGT code coverage is working
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
                   ` (4 preceding siblings ...)
  2022-04-04  6:26 ` [igt-dev] [PATCH 05/12] scripts/meson.build: install code coverage scripts Mauro Carvalho Chehab
@ 2022-04-04  6:26 ` Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 07/12] docs/code_coverage.md: document the code coverage filter script Mauro Carvalho Chehab
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

The runner_tests.c won't be able to test code coverage, as it
requires a kernel specially built for such purpose.

So, add a script that will validate possible steps while doing
code coverage.

Reviewed-by: Ch Sai Gowtham <sai.gowtham.ch@intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 scripts/code_cov_selftest.sh | 47 ++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100755 scripts/code_cov_selftest.sh

diff --git a/scripts/code_cov_selftest.sh b/scripts/code_cov_selftest.sh
new file mode 100755
index 000000000000..1c4bd96a0111
--- /dev/null
+++ b/scripts/code_cov_selftest.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+trap 'catch $LINENO' ERR
+catch() {
+	echo "===> ERROR: Code coverage selftest failed on $0:$1" >&2
+        exit 1
+}
+
+if [ -z "$IGT_KERNEL_TREE" ] ; then
+        echo "Error! IGT_KERNEL_TREE environment var was not defined." >&2
+        exit 1
+fi
+
+TEST="igt@debugfs_test@read_all_entries"
+
+TESTLIST="my_tests.testlist"
+GATHER="scripts/code_cov_gather_on_test.py"
+LCOV_CAP="scripts/code_cov_capture.sh"
+INFO_RESULTS="info_results"
+TAR_RESULTS="tar_results"
+
+sudo rm -rf results/ $INFO_RESULTS/ $TAR_RESULTS/ || true
+
+echo "$TEST" > $TESTLIST
+
+# run-tests.sh
+echo "==> use lcov capture via run-tests.sh"
+./scripts/run-tests.sh -T $TESTLIST -k $IGT_KERNEL_TREE -c $LCOV_CAP
+echo "==> gather sysfs using run-tests.sh"
+./scripts/run-tests.sh -T $TESTLIST -k $IGT_KERNEL_TREE -P -c $GATHER
+echo "==> gather sysfs using run-tests.sh, capturing at the end"
+./scripts/run-tests.sh -T $TESTLIST -k $IGT_KERNEL_TREE -c $GATHER
+
+# igt_runner called directly
+echo "==> use lcov capture via igt_runner"
+sudo IGT_KERNEL_TREE=$IGT_KERNEL_TREE ./build/runner/igt_runner -o --test-list $TESTLIST --coverage-per-test --collect-script $LCOV_CAP  build/tests results
+echo "==> gather sysfs running igt_runner"
+sudo ./build/runner/igt_runner -o --test-list $TESTLIST --coverage-per-test --collect-script $GATHER build/tests results
+
+# html report
+echo "==> generate report from lcov info files"
+scripts/code_cov_gen_report.sh -r results/code_cov/ -k $IGT_KERNEL_TREE -o $INFO_RESULTS -i --only-i915 --ignore-unused
+echo "==> generate report from sysfs gather files"
+scripts/code_cov_gen_report.sh -r results/code_cov/ -k $IGT_KERNEL_TREE -o $TAR_RESULTS -t --only-drm --ignore-unused
+
+echo
+echo "==> All tests passed. <=="
-- 
2.35.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] [PATCH 07/12] docs/code_coverage.md: document the code coverage filter script
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
                   ` (5 preceding siblings ...)
  2022-04-04  6:26 ` [igt-dev] [PATCH 06/12] scripts/code_cov_selftest.sh: test if IGT code coverage is working Mauro Carvalho Chehab
@ 2022-04-04  6:26 ` Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 08/12] scripts/code_cov_parse_info: better handle test name Mauro Carvalho Chehab
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

Add documentation bits for the code_coverage_parse_info.

While here, also drop the extensions from the scripts that were renamed.

Reviewed-by: Ch Sai Gowtham <sai.gowtham.ch@intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 docs/code_coverage.md | 238 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 224 insertions(+), 14 deletions(-)

diff --git a/docs/code_coverage.md b/docs/code_coverage.md
index 34d56d283a75..915e800ed68b 100644
--- a/docs/code_coverage.md
+++ b/docs/code_coverage.md
@@ -132,15 +132,15 @@ Kernel that was built at the same machine, at the directory `~/linux`,
 and wants to capture one file per test, it would use:
 
 ```
-./scripts/run-tests.sh -T my.testlist -k ~/linux -c scripts/code_cov_capture.sh -P
+./scripts/run-tests.sh -T my.testlist -k ~/linux -c code_cov_capture -P
 ```
 
 ### Code Coverage Collect script
 
 While any script could in thesis be used, currently, there are two ones
-under `scripts/`:
+under the IGT's `scripts/` source directory:
 
-- `scripts/code_cov_capture.sh`:
+- `code_cov_capture`:
 
   Assumes that the Kernel was built at the same machine, and uses
   the lcov tool to generate GCC-independent code coverage data,
@@ -150,7 +150,7 @@ under `scripts/`:
 
   Such script requires `lcov` tool to be installed at the test machine.
 
-- `scripts/code_cov_gather_on_test.py`:
+- `code_cov_gather_on_test`:
 
   Generates a gzipped tarbal with the code coverage counters in
   binary format. Such kind of output should then be parsed at
@@ -162,12 +162,12 @@ For each script, the igt_runner passes just one parameter: the results
 directory + the test name.
 
 For instance, if it is needed to run a test called
-`debugfs_test (read_all_entries)` using `scripts/code_cov_capture.sh`
+`debugfs_test (read_all_entries)` using `code_cov_capture`
 parameter, e. g.:
 
 ```
 $ echo "igt@debugfs_test@read_all_entries" > my.testlist
-$ ./scripts/run-tests.sh -T my.testlist -k ~/linux -c scripts/code_cov_capture.sh -P
+$ ./scripts/run-tests.sh -T my.testlist -k ~/linux -c code_cov_capture -P
 Found test list: "/basedir/igt/build/tests/test-list.txt"
 [31410.499969] [1/1] debugfs_test (read_all_entries)
 [31411.060446] Storing code coverage results...
@@ -178,7 +178,7 @@ Done.
 The script will be called as:
 
 ```
-scripts/code_cov_capture.sh /basedir/igt/results/code_cov/debugfs_test_read_all_entries
+code_cov_capture results/code_cov/debugfs_test_read_all_entries
 ```
 
 Please notice that any character that it is not a number nor a letter at the
@@ -190,6 +190,73 @@ as titles at the lcov files.
 If any extra global parameters are needed by the script, those can be sent
 via shell's environment var.
 
+## Parsing data from code coverage *.info files
+
+The `*.info` files generated by `lcov` are plain text files that list the
+tests that were executed in runtime.
+
+The `code_cov_parse_info` script has some logic on it that allows
+printing the called functions stored inside the `*.info` file. It can also
+optionally apply the following filters. Its main options are:
+
+- `--stat` or `--statistics`
+
+  Prints code coverage statistics.
+
+  It displays function, line, branch and file coverage percentage.
+
+  The statistics report is affected by the applied filters.
+
+- `--print-coverage`, `--print` or `-p`
+
+
+  Prints the functions that were executed in runtime and how many times
+  they were reached.
+
+  The function coverage report is affected by the applied filters.
+
+- `--print-unused` or `-u`
+
+  Prints the functions that were never reached.
+
+  The function coverage report is affected by the applied filters.
+
+- `--show-lines` or `--show_lines`
+
+  When printing per-function code coverage data, always output the source
+  file and the line number where the function is defined.
+
+- `--output` *output file* or `-o` *output file*
+
+  Produces an output file merging all input files.
+
+  The generated output file is affected by the applied filters.
+
+- `--show-files` or `--show_files`
+
+   Shows the list of files that were useed to produce the code coverage
+   results.
+
+- It also has a set of parameters that filters the code coverage results:
+  `--only-drm`, `--only-i915`, `--func-filters`, `--source-filters`,
+  `--ignore-unused`.
+  When used, all coverage displayed reports, and the stored output file
+  will be affected by such filters.
+
+More details can be seen by calling:
+
+```
+code_cov_parse_info --help
+
+```
+
+or:
+
+```
+code_cov_parse_info --man
+
+```
+
 ### The `*.info` file format
 
 The `*.info` files contain several fields on it, grouped into records.
@@ -224,7 +291,7 @@ So, the above example means that, inside
 ## Generating code coverage documentation
 
 The `lcov` package contains the needed tools to parse and generate code
-coverage documentation. It is used by `scripts/code_cov_capture.sh` script
+coverage documentation. It is used by `code_cov_capture` script
 to convery from compiler-dependent `*.gcno` counters into a
 compiler-independent format (`*.info`).
 
@@ -243,11 +310,16 @@ source files. Some optional arguments can be used at the command line, or
 can be stored at `/etc/lcovrc` or `~/.lcovrc` files.
 
 As generating the documentation depends wheather the results were generated
-as with a single or multiple `*.info` files by `scripts/code_cov_capture.sh`
+as with a single or multiple `*.info` files by `code_cov_capture`
 or stored in raw formats inside `*.tar.gz` file(s) by
-`scripts/code_cov_gather_on_test.py`, there's a script that does all the
+`code_cov_gather_on_test`, there's a script that does all the
 required steps to build the code coverage html reports:
-`scripts/code_cov_gen_report.sh`.
+`code_cov_gen_report`. Besides its own command line arguments, it
+also accepts arguments to be passed to `code_cov_parse_info`.
+
+If a `code_cov_parse_info` command line parameter is passed, it will
+also call the script, in order to use a filtered `*.info` file to be
+used when generating the HTML reports.
 
 It requires the following arguments:
 
@@ -276,18 +348,156 @@ It requires the following arguments:
 
   The files specified by `--read` are gzipped tarballs containing all
   `*.gcno` files and all `*.gcda` softlinks from the `/sys/kernel/debug/gcov/`
-  directory at the test machine, created by
-  `scripts/code_cov_gather_on_test.py` script.
+  directory at the test machine, created by `code_cov_gather_on_test` script.
 
 - `--force-override`
 
   Allow using a non-empty directory for `--output-dir`.
 
+- It also accepts `--print`, `--only-drm`, `--only-i915` and `--ignore-unused`
+  options from `code_cov_parse_info`.
+
 `--info` and `--tar` are mutually exclusive and at least one of them should
 be specified.
 
+## Code coverage capture script example
+
+### Capture, parse and generate code coverage html data
+
+The script below provides a simple yet powerful script using code
+coverage capture on a test machine that also contains the Linux
+Kernel source and objects. It assumes that LGT was installed.
+
+```
+#/bin/bash -e
+
+TESTLIST="my_tests.testlist"
+OUT_DIR="${HOME}/results"
+
+mkdir -p $OUT_DIR/html
+
+echo "igt@debugfs_test@read_all_entries" > $TESTLIST
+echo "igt@core_auth@basic-auth" >> $TESTLIST
+echo "igt@gem_exec_basic@basic" >> $TESTLIST
+
+sudo IGT_KERNEL_TREE="${HOME}/linux" igt_runner -s -o --coverage-per-test \
+                  --collect-script code_cov_capture --test-list $TESTLIST \
+                  /usr/local/libexec/igt-gpu-tools $OUT_DIR/ | sed s,$HOME/,,
+
+sudo chown -R $(id -u):$(id -g) $OUT_DIR/
+
+for i in $OUT_DIR/code_cov/*.info; do
+        echo -e "\n$(basename $i):"
+        code_cov_parse_info --only-drm --ignore-unused --stat $i
+done
+echo -e "\nTOTAL:"
+code_cov_parse_info --only-drm --stat --output $OUT_DIR/results.info \
+        $OUT_DIR/code_cov/*.info
+
+cd $OUT_DIR/html
+genhtml -q -s --legend --branch-coverage $OUT_DIR/results.info
+```
+
+Running such script produces the following output:
+
+```
+[3622.993304] [1/3] debugfs_test (read_all_entries)
+[3631.95]     Code coverage wrote to results/code_cov/debugfs_test_read_all_entries.info
+[3626.217016] Storing code coverage results...
+[3631.957998] [2/3] core_auth (basic-auth)
+[3638.03]     Code coverage wrote to results/code_cov/core_auth_basic_auth.info
+[3632.116024] Storing code coverage results...
+[3638.070869] [3/3] gem_exec_basic (basic)
+[3644.24]     Code coverage wrote to results/code_cov/gem_exec_basic_basic.info
+[3638.366790] Storing code coverage results...
+Done.
+
+core_auth_basic_auth.info:
+  lines......: 11.7% (8217 of 70257 lines)
+  functions..: 7.1% (776 of 10971 functions)
+  branches...: 7.0% (3596 of 51041 branches)
+Ignored......: non-drm headers and source files where none of its code ran.
+Source files.: 23.27% (165 of 709 total), 29.57% (165 of 558 filtered)
+
+debugfs_test_read_all_entries.info:
+  lines......: 19.3% (20266 of 104802 lines)
+  functions..: 17.5% (1922 of 10971 functions)
+  branches...: 12.7% (9462 of 74555 branches)
+Ignored......: non-drm headers and source files where none of its code ran.
+Source files.: 34.70% (246 of 709 total), 44.09% (246 of 558 filtered)
+
+gem_exec_basic_basic.info:
+  lines......: 17.1% (14964 of 87503 lines)
+  functions..: 13.0% (1422 of 10971 functions)
+  branches...: 10.1% (6446 of 63758 branches)
+Ignored......: non-drm headers and source files where none of its code ran.
+Source files.: 30.89% (219 of 709 total), 39.25% (219 of 558 filtered)
+
+TOTAL:
+  lines......: 15.5% (25821 of 166849 lines)
+  functions..: 22.1% (2429 of 10971 functions)
+  branches...: 10.5% (11869 of 112665 branches)
+Ignored......: non-drm headers.
+Source files.: 78.70% (558 of 709 total)
+```
+
+### Reporting detailed function coverage stored on *.info files
+
+The `code_cov_parse_info` script can be used alone in order to provide
+a text file output containing code coverage data obtained from a *.info
+file. For example, listing code coverage usage for all functions whose name
+contains "edid_" can be done with:
+
+```
+$ echo edid_ >filter.txt
+$ code_cov_parse_info --func-filters filter.txt results/results.info -p -u --stat
+TEST: Code_coverage_tests
+__drm_get_edid_firmware_path(): unused
+__drm_set_edid_firmware_path(): unused
+displayid_iter_edid_begin(): executed 10 times
+drm_add_edid_modes(): executed 2 times
+drm_add_override_edid_modes(): unused
+drm_connector_attach_edid_property(): unused
+drm_connector_update_edid_property(): executed 8 times
+drm_dp_send_real_edid_checksum(): unused
+drm_edid_are_equal(): executed 4 times
+drm_edid_block_valid(): executed 8 times
+drm_edid_duplicate(): unused
+drm_edid_get_monitor_name(): unused
+drm_edid_header_is_valid(): executed 4 times
+drm_edid_is_valid(): executed 2 times
+drm_edid_to_eld(): executed 2 times
+drm_edid_to_sad(): unused
+drm_edid_to_speaker_allocation(): unused
+drm_find_edid_extension(): executed 22 times
+drm_get_edid_switcheroo(): unused
+drm_load_edid_firmware(): executed 2 times
+edid_firmware_get(): unused
+edid_firmware_set(): unused
+edid_fixup_preferred(): unused
+edid_get_quirks(): executed 6 times
+edid_load(): unused
+edid_open(): executed 4 times
+edid_show() from linux/drivers/gpu/drm/drm_debugfs.c: executed 4 times
+edid_show() from linux/drivers/gpu/drm/drm_sysfs.c: unused
+edid_vendor(): executed 348 times
+edid_write(): unused
+intel_panel_edid_downclock_mode(): unused
+intel_panel_edid_fixed_mode(): unused
+is_edid_digital_input_dp(): unused
+  lines......: 5.5% (5 of 91 lines)
+  functions..: 42.4% (14 of 33 functions)
+  branches...: 1.9% (1 of 52 branches)
+Ignored......: unmatched functions m/(?^:edid_)/ and source files where none of its code ran.
+Source files.: 0.90% (5 of 558 total), 55.56% (5 of 9 filtered)
+```
+
+When the function is unique, it will just display the function name and how
+many times the IGT test(s) executed it. When the same function name exists
+on multiple files (like the `edid_show()` on the above example), it will
+display multiple lines, one for each different function/file combination.
+
 ## References
 
 More information is available at Kernel gcov documentation:
 [Using gcov with the Linux kernel](https://www.kernel.org/doc/html/latest/dev-tools/gcov.html).
-
-- 
2.35.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] [PATCH 08/12] scripts/code_cov_parse_info: better handle test name
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
                   ` (6 preceding siblings ...)
  2022-04-04  6:26 ` [igt-dev] [PATCH 07/12] docs/code_coverage.md: document the code coverage filter script Mauro Carvalho Chehab
@ 2022-04-04  6:26 ` Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 09/12] code_cov_parse_info: fix error handling when opening files Mauro Carvalho Chehab
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

The TN field generated by standard lcov is weird: it keeps
repeating the TN field from time to time. At genhtml, it seems
that only the first one is used.

As we're using TN to indicate the test name, preserve all different
test names at the output file.

Also, printing such names doesn't really makes sense when
--print-used and --print-unused command line options are used,
and printing a list of 100+ names won't make much sense.

So, just remove printing the test names.

Reviewed-by: Ch Sai Gowtham <sai.gowtham.ch@intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 scripts/code_cov_parse_info | 20 ++++++++------------
 1 file changed, 8 insertions(+), 12 deletions(-)

diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index 604812b41580..3e1525a67d99 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -17,8 +17,8 @@ my %used_source;
 my %record;
 my %files;
 my @func_regexes;
+my %test_names;
 my @src_regexes;
-my $testname = "";
 
 my $verbose = 0;
 my $ignore_unused = 0;
@@ -99,11 +99,7 @@ sub parse_info_data($)
 		if (m/^TN:(.*)/) {
 			if ($1 ne $cur_test) {
 				$cur_test = $1;
-				if (!$testname) {
-					$testname = $cur_test;
-				} else {
-					$testname = "Code_coverage_tests";
-				}
+				$test_names{$cur_test} = 1;
 			}
 			$source = $before_sf;
 			$func = $before_sf;
@@ -297,9 +293,13 @@ sub write_filtered_file($)
 {
 	my $filter = shift;
 
-	# Generates filtered data
-	my $filtered = "TN:$testname\n";
+	my $filtered = "";
+
+	foreach my $testname(sort keys %test_names) {
+		$filtered .= "TN:$testname\n";
+	}
 
+	# Generates filtered data
 	foreach my $source(sort keys %record) {
 		next if (!$used_source{$source});
 
@@ -363,10 +363,6 @@ sub print_code_coverage($$$)
 
 	return if (!$print_used && !$print_unused);
 
-	if ($testname ne "") {
-		$testname =~ s/(.*)_on_(\w+)$/$1 on $2/;
-		print "TEST: $testname\n";
-	}
 	my $prev_file = "";
 
 	foreach my $func (sort keys(%all_func)) {
-- 
2.35.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] [PATCH 09/12] code_cov_parse_info: fix error handling when opening files
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
                   ` (7 preceding siblings ...)
  2022-04-04  6:26 ` [igt-dev] [PATCH 08/12] scripts/code_cov_parse_info: better handle test name Mauro Carvalho Chehab
@ 2022-04-04  6:26 ` Mauro Carvalho Chehab
  2022-04-04  6:26 ` [igt-dev] [PATCH 10/12] code_cov_parse_info: fix --show-lines logic Mauro Carvalho Chehab
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

While on bash scripts we use "||" for error handling, perl
uses "or" instead. Yet, it doesn't warn when "||" is used.

That causes it to print a warning when the open fail while
reading filter files, instead of the original intent of
finishing and reporting an error.

Reviewed-by: Ch Sai Gowtham <sai.gowtham.ch@intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 scripts/code_cov_parse_info | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index 3e1525a67d99..c8284a297b33 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -513,7 +513,7 @@ my $filter_str = "";
 my $has_filter;
 
 if ($func_filters) {
-	open IN, $func_filters || die "Can't open $func_filters";
+	open IN, $func_filters or die "Can't open $func_filters";
 	while (<IN>) {
 		s/^\s+//;
 		s/\s+$//;
@@ -524,7 +524,7 @@ if ($func_filters) {
 }
 
 if ($src_filters) {
-	open IN, $src_filters || die "Can't open $src_filters";
+	open IN, $src_filters or die "Can't open $src_filters";
 	while (<IN>) {
 		s/^\s+//;
 		s/\s+$//;
-- 
2.35.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] [PATCH 10/12] code_cov_parse_info: fix --show-lines logic
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
                   ` (8 preceding siblings ...)
  2022-04-04  6:26 ` [igt-dev] [PATCH 09/12] code_cov_parse_info: fix error handling when opening files Mauro Carvalho Chehab
@ 2022-04-04  6:26 ` Mauro Carvalho Chehab
  2022-04-11 11:57   ` Andrzej Hajda
  2022-04-04  6:26 ` [igt-dev] [PATCH 11/12] code_cov_parse_info: add support for exclude filters Mauro Carvalho Chehab
                   ` (3 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

print_code_coverage function needs to first read the number
of lines and then simplify the $file by removing its prefix.

Without that, it ends trying to access an uninitiated value,
leading to warnings and not doing what's expected.

While here, also check if the ln data is valid, just in case
the .info file might have some FN field(s) missing.

Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 scripts/code_cov_parse_info | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index c8284a297b33..7987b0068e88 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -378,8 +378,13 @@ sub print_code_coverage($$$)
 			}
 
 			if ($show_lines) {
+				my $ln = $all_func{$func}{$file}->{ln};
 				$file =~ s,$prefix,linux/,;
-				$name = "$func() from $file:" . $all_func{$func}{$file}->{ln};
+				if ($ln) {
+					$name = "$func() from $file:" . $ln;
+				} else {
+					$name = "$func() from $file";
+				}
 			} elsif (scalar @keys > 1) {
 				$file =~ s,$prefix,linux/,;
 				$name = "$func() from $file:";
-- 
2.35.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] [PATCH 11/12] code_cov_parse_info: add support for exclude filters
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
                   ` (9 preceding siblings ...)
  2022-04-04  6:26 ` [igt-dev] [PATCH 10/12] code_cov_parse_info: fix --show-lines logic Mauro Carvalho Chehab
@ 2022-04-04  6:26 ` Mauro Carvalho Chehab
  2022-04-11 12:28   ` Andrzej Hajda
  2022-04-04  6:26 ` [igt-dev] [PATCH 12/12] code_cov_parse_info: add support for generating html reports Mauro Carvalho Chehab
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

It is interesting to have support not only for including, but
also for excluding functions and files. Also, it is trivial to
have support for it.

Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 scripts/code_cov_parse_info | 71 +++++++++++++++++++++++++++++++++----
 1 file changed, 65 insertions(+), 6 deletions(-)

diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index 7987b0068e88..9624dc33468d 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -17,8 +17,10 @@ my %used_source;
 my %record;
 my %files;
 my @func_regexes;
+my @func_exclude_regexes;
 my %test_names;
 my @src_regexes;
+my @src_exclude_regexes;
 
 my $verbose = 0;
 my $ignore_unused = 0;
@@ -28,10 +30,16 @@ my $skip_func = 0;
 
 sub is_function_excluded($)
 {
-	return 0 if (!@func_regexes);
+	return 0 if (!@func_regexes && !@func_exclude_regexes);
 
 	my $func = shift;
 
+	foreach my $r (@func_exclude_regexes) {
+		return 1 if ($func =~ m/$r/);
+	}
+
+	return 0 if (!@func_regexes);
+
 	foreach my $r (@func_regexes) {
 		return 0 if ($func =~ m/$r/);
 	}
@@ -64,9 +72,13 @@ sub filter_file($)
 		}
 	}
 
-	return 0 if (!@src_regexes);
+	return 0 if (!@src_regexes && !@src_exclude_regexes);
 
-	my $func = shift;
+	foreach my $r (@src_exclude_regexes) {
+		return 1 if ($s =~ m/$r/);
+	}
+
+	return 0 if (!@src_regexes);
 
 	foreach my $r (@src_regexes) {
 		return 0 if ($s =~ m/$r/);
@@ -482,7 +494,9 @@ my $filter;
 my $help;
 my $man;
 my $func_filters;
+my $func_exclude;
 my $src_filters;
+my $src_exclude;
 my $show_files;
 my $show_lines;
 
@@ -496,7 +510,9 @@ GetOptions(
 	"only-i915|only_i915" => \$only_i915,
 	"only-drm|only_drm" => \$only_drm,
 	"func-filters|f=s" => \$func_filters,
+	"exclude-func=s" => \$func_exclude,
 	"source-filters|S=s" => \$src_filters,
+	"exclude-source=s" => \$src_exclude,
 	"show-files|show_files" => \$show_files,
 	"show-lines|show_lines" => \$show_lines,
 	"help" => \$help,
@@ -539,7 +555,29 @@ if ($src_filters) {
 	close IN;
 }
 
-$ignore_unused = 1 if (@func_regexes);
+if ($func_exclude) {
+	open IN, $func_exclude or die "Can't open $func_exclude";
+	while (<IN>) {
+		s/^\s+//;
+		s/\s+$//;
+		next if (m/^#/ || m/^$/);
+		push @func_exclude_regexes, qr /$_/;
+	}
+	close IN;
+}
+
+if ($src_exclude) {
+	open IN, $src_exclude or die "Can't open $src_exclude";
+	while (<IN>) {
+		s/^\s+//;
+		s/\s+$//;
+		next if (m/^#/ || m/^$/);
+		push @src_exclude_regexes, qr /$_/;
+	}
+	close IN;
+}
+
+$ignore_unused = 1 if (@func_regexes || @func_exclude_regexes);
 
 if ($only_i915) {
 	$filter_str = " non-i915 files";
@@ -552,7 +590,7 @@ if ($only_drm) {
 	$has_filter = 1;
 }
 
-if (@func_regexes) {
+if (@func_regexes || @func_exclude_regexes) {
 	$filter_str .= "," if ($filter_str ne "");
 	$filter_str .= " unmatched functions";
 	foreach my $r (@func_regexes) {
@@ -562,7 +600,7 @@ if (@func_regexes) {
 	$has_filter = 1;
 }
 
-if (@src_regexes) {
+if (@src_regexes || @src_exclude_regexes) {
 	$filter_str .= "," if ($filter_str ne "");
 	$filter_str .= " unmatched source files";
 	foreach my $r (@src_regexes) {
@@ -708,11 +746,32 @@ the regular expressions contained at the B<[filter's file]>.
 When this filter is used, B<--ignore-unused> will be automaticaly enabled,
 as the final goal is to report per-function usage, and not per-file.
 
+When used with B<--exclude-func>, exclusions take precedence.
+
+=item B<--exclude-func>  B<[filter's file]>
+
+Exclude all functions that match the regular expressions contained
+at the B<[filter's file]>.
+
+When this filter is used, B<--ignore-unused> will be automaticaly enabled,
+as the final goal is to report per-function usage, and not per-file.
+
+When used with B<--func-filters>, exclusions take precedence.
+
 =item B<--source-filters>  B<[filter's file]> or B<-S>  B<[filter's file]>
 
 Takes into account only the code coverage for the source files that match
 the regular expressions contained at the B<[filter's file]>.
 
+When used with B<--exclude-source>, exclusions take precedence.
+
+=item B<--exclude-source> B<[filter's file]>
+
+Exclude all files that match the regular expressions contained
+at the B<[filter's file]>.
+
+When used with B<--source-filters>, exclusions take precedence.
+
 =item B<--ignore-unused> or B<--ignore_unused>
 
 Filters out unused C files and headers from the code coverage results.
-- 
2.35.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] [PATCH 12/12] code_cov_parse_info: add support for generating html reports
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
                   ` (10 preceding siblings ...)
  2022-04-04  6:26 ` [igt-dev] [PATCH 11/12] code_cov_parse_info: add support for exclude filters Mauro Carvalho Chehab
@ 2022-04-04  6:26 ` Mauro Carvalho Chehab
  2022-04-11 13:15   ` Andrzej Hajda
  2022-04-04  7:45 ` [igt-dev] ✓ Fi.CI.BAT: success for code coverage: some improvements Patchwork
  2022-04-04  9:11 ` [igt-dev] ✗ Fi.CI.IGT: failure " Patchwork
  13 siblings, 1 reply; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-04  6:26 UTC (permalink / raw)
  To: igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham, Andrzej Hajda

From: Mauro Carvalho Chehab <mchehab@kernel.org>

While lcov has already its own report generator, it is interesting
to be able to deal with multiple files exposing each input in
separate.

So, add new command line parameters to allow it to generate html
reports. Also add some command lines to setup html title, add a
css file and include a prolog/epilog at the html body.

The title option can also be useful to rename the titles when
merging multiple info files.

Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 scripts/code_cov_parse_info | 438 +++++++++++++++++++++++++++++++-----
 1 file changed, 386 insertions(+), 52 deletions(-)

diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index 9624dc33468d..faa585ae5b18 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -9,6 +9,8 @@ use Pod::Man;
 
 my $prefix = qr ".*?(linux)\w*/";
 
+my $title = "";
+
 my %used_func;
 my %all_func;
 my %all_branch;
@@ -307,8 +309,12 @@ sub write_filtered_file($)
 
 	my $filtered = "";
 
-	foreach my $testname(sort keys %test_names) {
-		$filtered .= "TN:$testname\n";
+	if ($title eq "") {
+		foreach my $testname(sort keys %test_names) {
+			$filtered .= "TN:$testname\n";
+		}
+	} else {
+		$filtered .= "TN:$title\n";
 	}
 
 	# Generates filtered data
@@ -416,71 +422,331 @@ sub print_code_coverage($$$)
 	}
 }
 
-sub print_summary()
+my %stats;
+
+sub gen_stats()
 {
-	# Output per-line coverage statistics
-	my $line_count = 0;
-	my $line_reached = 0;
+	# per-line coverage statistics
+	$stats{"line_count"} = 0;
+	$stats{"line_reached"} = 0;
 
 	foreach my $source (keys(%all_line)) {
 		next if (!$used_source{$source});
 
 		foreach my $where (keys(%{$all_line{$source}})) {
-			$line_count++;
-			$line_reached++ if ($all_line{$source}{$where} != 0);
+			$stats{"line_count"}++;
+			$stats{"line_reached"}++ if ($all_line{$source}{$where} != 0);
 		}
 	}
-	if ($line_count) {
-		my $percent = 100. * $line_reached / $line_count;
-		printf "  lines......: %.1f%% (%d of %d lines)\n",
-			$percent, $line_reached, $line_count;
-	} else {
-		print "No line coverage data.\n";
-	}
 
-	# Output per-function coverage statistics
-	my $func_count = 0;
-	my $func_used = 0;
+	# per-function coverage statistics
+	$stats{"func_count"} = 0;
+	$stats{"func_used"} = 0;
 
 	foreach my $func (keys(%all_func)) {
 		foreach my $file (keys(%{$all_func{$func}})) {
-			$func_count++;
+			$stats{"func_count"}++;
 			if ($used_func{$func}) {
 				if ($used_func{$func}->{$file}) {
-					$func_used++;
+					$stats{"func_used"}++;
 				}
 			}
 		}
 	}
 
-	if ($func_count) {
-		my $percent = 100. * $func_used / $func_count;
+	# per-branch coverage statistics
+	$stats{"branch_count"} = 0;
+	$stats{"branch_reached"} = 0;
+
+	foreach my $source (keys(%all_branch)) {
+		next if (!$used_source{$source});
+
+		foreach my $where (keys(%{$all_branch{$source}})) {
+			$stats{"branch_count"}++;
+			$stats{"branch_reached"}++ if ($all_branch{$source}{$where} != 0);
+		}
+	}
+
+	# per-file coverage stats
+	$stats{"all_files"} = scalar keys(%files);
+	$stats{"filtered_files"} = scalar keys(%record);
+	$stats{"used_files"} = scalar keys(%used_source);
+}
+
+sub print_summary()
+{
+	if ($stats{"line_count"}) {
+		my $percent = 100. * $stats{"line_reached"} / $stats{"line_count"};
+		printf "  lines......: %.1f%% (%d of %d lines)\n",
+			$percent, $stats{"line_reached"}, $stats{"line_count"};
+	} else {
+		print "No line coverage data.\n";
+	}
+
+	if ($stats{"func_count"}) {
+		my $percent = 100. * $stats{"func_used"} / $stats{"func_count"};
 		printf "  functions..: %.1f%% (%d of %d functions)\n",
-			$percent, $func_used, $func_count;
+			$percent, $stats{"func_used"}, $stats{"func_count"};
 	} else {
 		print "No functions reported. Wrong filters?\n";
 		return;
 	}
 
-	# Output per-branch coverage statistics
-	my $branch_count = 0;
-	my $branch_reached = 0;
+	if ($stats{"branch_count"}) {
+		my $percent = 100. * $stats{"branch_reached"} / $stats{"branch_count"};
+		printf "  branches...: %.1f%% (%d of %d branches)\n",
+			$percent, $stats{"branch_reached"}, $stats{"branch_count"};
+	} else {
+		print "No branch coverage data.\n";
+	}
+}
 
-	foreach my $source (keys(%all_branch)) {
-		next if (!$used_source{$source});
+my $gen_report;
+my $css_file;
+my $html_prolog;
+my $html_epilog;
+my %report;
 
-		foreach my $where (keys(%{$all_branch{$source}})) {
-			$branch_count++;
-			$branch_reached++ if ($all_branch{$source}{$where} != 0);
+sub generate_report()
+{
+	my $percent;
+	my $prolog = "";
+	my $epilog = "";
+	my @info_files = sort(keys %report);
+
+	$title = "Code coverage results" if ($title eq "");
+
+	if ($html_prolog) {
+		open IN, $html_prolog or die "Can't open prolog file";
+		$prolog .= $_ while (<IN>);
+		close IN;
+	}
+
+	if ($html_prolog) {
+		open IN, $html_epilog or die "Can't open epilog file";
+		$epilog .= $_ while (<IN>);
+		close IN;
+	}
+
+	# Re-generate the hashes used to report stats in order to procuce the
+	# Total results
+
+	%used_func = ();
+	%all_func = ();
+	%all_branch = ();
+	%all_line = ();
+	%used_source = ();
+	%files = ();
+	%test_names = ();
+
+	foreach my $f (@info_files) {
+		foreach my $source (keys(%{$report{$f}{"all_line"}})) {
+			$used_source{$source} = 1 if ($report{$f}{"used_source"});
+			foreach my $where (keys(%{$report{$f}{"all_line"}{$source}})) {
+				$all_line{$source}{$where} += $report{$f}{"all_line"}{$source}{$where};
+			}
+		}
+		foreach my $func (keys(%{$report{$f}{"all_func"}})) {
+			foreach my $file (keys(%{$report{$f}{"all_func"}{$func}})) {
+				$all_func{$func}{$file}->{ln} = $report{$f}{"all_func"}{$func}{$file}->{ln};
+				$used_func{$func}->{$file} = 1 if ($report{$f}{"used_func"}{$func}->{$file});
+			}
+		}
+		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};
+			}
+		}
+		for my $source(keys(%{$report{$f}{"files"}})) {
+			$files{$source} = 1;
+			$used_source{$source} = 1 if ($report{$f}{"used_source"}{$source});
+		}
+		for my $test(keys(%{$report{$f}{"test_names"}})) {
+			$test_names{$test} = 1;
 		}
 	}
-	if ($branch_count) {
-		my $percent = 100. * $branch_reached / $branch_count;
-		printf "  branches...: %.1f%% (%d of %d branches)\n",
-			$percent, $branch_reached, $branch_count;
+	gen_stats();
+
+	# Colors for the html output
+
+	my $red    = "style=\"background-color:#ffb3b3\"";
+	my $yellow = "style=\"background-color:#ffffb3\"";
+	my $green  = "style=\"background-color:#d9ffd9\"";
+
+	# Open report file
+
+	open OUT, ">$gen_report" or die "Can't open $gen_report";
+
+	print OUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
+
+	print OUT "<html lang=\"en\">\n\n";
+	print OUT "<head>\n";
+	print OUT "  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n";
+	print OUT "  <title>$title</title>\n";
+	print OUT "  <link rel=\"stylesheet\" type=\"text/css\" href=\"$css_file\">\n" if ($css_file);
+	print OUT "</head>\n\n<body>\n$prolog";
+
+	print OUT "  <h1>$title</h1>\n";
+
+	print OUT "  <h2>Summary</h2>\n";
+	# Generates a table containing the code coverage statistics per input
+
+	print OUT "<table width=\"100%\" border=1 cellspacing=0 cellpadding=0>\n  <tr>\n";
+	print OUT "    <th></th>\n";
+	foreach my $f (@info_files) {
+		print OUT "    <th>$f</th>\n";
+	}
+	print OUT "    <th>TOTAL</th>\n";
+	print OUT "    <th>Total count</th>\n";
+	print OUT "  </tr><tr>\n";
+
+	print OUT "    <td><b>Functions</b></td>\n";
+	foreach my $f (@info_files) {
+		my %st = %{$report{$f}{"stats"}};
+		if ($st{"func_count"}) {
+			$percent = 100. * $st{"func_used"} / $st{"func_count"};
+
+			printf OUT "    <td>%.1f%%</td>\n", $percent;
+		} else {
+			print OUT "    <td>N. A.</td>\n";
+		}
+	}
+	if ($stats{"func_count"}) {
+		$percent = 100. * $stats{"func_used"} / $stats{"func_count"};
+
+		printf OUT "    <td>%.1f%%</td>\n", $percent;
 	} else {
-		print "No branch coverage data.\n";
+		print OUT "    <td>N. A.</td>\n";
+	}
+	print OUT "  <td>" . $stats{"func_count"} . "</td>";
+	print OUT "  </tr><tr>\n";
+
+	print OUT "    <td><b>Branches</b></td>\n";
+	foreach my $f (@info_files) {
+		my %st = %{$report{$f}{"stats"}};
+		if ($st{"branch_count"}) {
+			$percent = 100. * $st{"branch_reached"} / $st{"branch_count"};
+
+			printf OUT "    <td>%.1f%%</td>\n", $percent;
+		} else {
+			print OUT "    <td>N. A.</td>\n";
+		}
+	}
+	if ($stats{"branch_count"}) {
+		$percent = 100. * $stats{"branch_reached"} / $stats{"branch_count"};
+
+		printf OUT "    <td>%.1f%%</td>\n", $percent;
+	} else {
+		print OUT "    <td>N. A.</td>\n";
+	}
+	print OUT "  <td>" . $stats{"branch_count"} . "</td>";
+	print OUT "  </tr><tr>\n";
+
+	print OUT "    <td><b>Lines</b></td>\n";
+	foreach my $f (@info_files) {
+		my %st = %{$report{$f}{"stats"}};
+
+		if ($st{"line_count"}) {
+			$percent = 100. * $st{"line_reached"} / $st{"line_count"};
+
+			printf OUT "    <td>%.1f%%</td>\n", $percent;
+		} else {
+			print OUT "    <td>N. A.</td>\n";
+		}
+	}
+	if ($stats{"line_count"}) {
+		$percent = 100. * $stats{"line_reached"} / $stats{"line_count"};
+
+		printf OUT "    <td>%.1f%%</td>\n", $percent;
+	} else {
+		print OUT "    <td>N. A.</td>\n";
+	}
+	print OUT "  <td>" . $stats{"line_count"} . "</td>";
+
+	# If there are more than one tests per file, report them
+	my $total = scalar(keys %test_names);
+	if ($total > 1) {
+		print OUT "  </tr><tr>\n";
+		print OUT "    <td><b>Number of tests</b></td>\n";
+		foreach my $f (@info_files) {
+			my $count = scalar(keys %{$report{$f}{"test_names"}});
+
+			if ($count == 0) {
+				print OUT "    <td $red>$count</td>\n";
+			} elsif ($count < $total) {
+				print OUT "    <td $yellow>$count</td>\n";
+			} else {
+				print OUT "    <td $green>$count</td>\n";
+			}
+		}
+		print OUT "    <td $green\>$total</td>\n";
+
+	}
+	print OUT "  </tr>\n</table><p/>\n\n";
+
+	if ($total > 1) {
+		print OUT "<h2>Tests coverage</h2>\n";
+
+		print OUT "<table width=\"100%\" border=1 cellspacing=0 cellpadding=0>\n  <tr>\n";
+		print OUT "    <th>Test name</th>\n";
+		foreach my $f (@info_files) {
+			print OUT "    <th>$f</th>\n";
+		}
+
+		foreach my $t (sort keys(%test_names)) {
+			print OUT "  </tr><tr>\n";
+			printf OUT "    <td>%s</td>\n", $t;
+			foreach my $f (@info_files) {
+				if (%{$report{$f}{"test_names"}}{$t}) {
+					print OUT "    <td $green>YES</td>\n";
+				} else {
+					print OUT "    <td $red>NO</td>\n";
+				}
+			}
+		}
+		print OUT "</tr></table>\n";
 	}
+
+
+	# Generates a table containing per-function detailed data
+
+	print OUT "<h2>Functions coverage</h2>\n";
+	print OUT "<table width=\"100%\" border=1 cellspacing=0 cellpadding=0>\n  <tr>\n";
+	print OUT "    <th>Function</th>\n";
+	print OUT "    <th>Used?</th>\n";
+	foreach my $f (@info_files) {
+		print OUT "    <th>$f</th>\n";
+	}
+	print OUT "    <th>File</th>\n";
+
+	foreach my $func (sort keys(%all_func)) {
+		my @keys = sort keys(%{$all_func{$func}});
+		foreach my $file (@keys) {
+			print OUT "  </tr><tr>\n";
+			print OUT "    <td>$func</td>\n";
+			if ($used_func{$func}->{$file}) {
+				print OUT "    <td $green>YES</td>\n";
+			} else {
+				print OUT "    <td $red>NO</td>\n";
+			}
+			foreach my $f (@info_files) {
+				if ($report{$f}{"used_func"}{$func}->{$file}) {
+					print OUT "    <td $green>YES</td>\n";
+				} else {
+					print OUT "    <td $red>NO</td>\n";
+				}
+			}
+			$file =~ s,$prefix,linux/,;
+			print OUT "    <td>$file</td>\n";
+		}
+	}
+	print OUT "</tr></table>\n";
+
+	print OUT "$epilog</body>\n";
+
+	# Close the file and exit
+
+	close OUT;
 }
 
 #
@@ -515,6 +781,11 @@ GetOptions(
 	"exclude-source=s" => \$src_exclude,
 	"show-files|show_files" => \$show_files,
 	"show-lines|show_lines" => \$show_lines,
+	"report|r=s" => \$gen_report,
+	"css-file|css|c=s" => \$css_file,
+	"title|t=s" => \$title,
+	"html-prolog|prolog=s" => \$html_prolog,
+	"html-epilog|epilog=s" => \$html_epilog,
 	"help" => \$help,
 	"man" => \$man,
 ) or pod2usage(2);
@@ -528,7 +799,9 @@ if ($#ARGV < 0) {
 }
 
 # At least one action should be specified
-pod2usage(1) if (!$print_used && !$filter && !$stat && !$print_unused);
+pod2usage(1) if (!$print_used && !$filter && !$stat && !$print_unused && !$gen_report);
+
+pod2usage(1) if ($gen_report && ($print_used || $filter || $stat || $print_unused));
 
 my $filter_str = "";
 my $has_filter;
@@ -617,39 +890,67 @@ if ($ignore_unused) {
 
 foreach my $f (@ARGV) {
 	parse_info_data($f);
+
+	if ($gen_report) {
+		$f =~ s,.*/,,;
+		$f =~ s/\.info$//;
+
+		gen_stats();
+
+		$report{$f}{"stats"} = { %stats };
+		$report{$f}{"all_func"} = { %all_func };
+		$report{$f}{"used_func"} = { %used_func };
+		$report{$f}{"all_branch"} = { %all_branch };
+		$report{$f}{"all_line"} = { %all_line };
+		$report{$f}{"used_source"} = { %used_source };
+		$report{$f}{"files"} = { %files };
+		$report{$f}{"test_names"} = { %test_names };
+
+		%used_func = ();
+		%all_func = ();
+		%all_branch = ();
+		%all_line = ();
+		%used_source = ();
+		%files = ();
+		%test_names = ();
+	}
 }
 
-print_code_coverage($print_used, $print_unused, $show_lines);
+if ($gen_report) {
+	generate_report();
+	exit 0;
+}
 
-print_summary() if ($stat);
+gen_stats();
 
-my $all_files = scalar keys(%files);
+die "Nothing counted. Wrong input files?" if (!$stats{"all_files"});
 
-die "Nothing counted. Wrong input files?" if (!$all_files);
+print_code_coverage($print_used, $print_unused, $show_lines);
 
-if ($has_filter) {
-	my $all_files = scalar keys(%files);
-	my $filtered_files = scalar keys(%record);
-	my $used_files = scalar keys(%used_source);
+print_summary() if ($stat);
 
-	my $percent = 100. * $used_files / $all_files;
+if ($has_filter) {
+	my $percent = 100. * $stats{"used_files"} / $stats{"all_files"};
 
 	$filter_str =~ s/(.*),/$1 and/;
 	printf "Ignored......:%s.\n", $filter_str;
 	printf "Source files.: %.2f%% (%d of %d total)",
-		$percent, $used_files, $all_files;
+		$percent, $stats{"used_files"}, $stats{"all_files"};
 
-	if ($used_files != $filtered_files) {
-		my $percent_filtered = 100. * $used_files / $filtered_files;
+	if ($stats{"used_files"} != $stats{"filtered_files"}) {
+		my $percent_filtered = 100. * $stats{"used_files"} / $stats{"filtered_files"};
 
 		printf ", %.2f%% (%d of %d filtered)",
-			$percent_filtered, $used_files, $filtered_files;
+			$percent_filtered, $stats{"used_files"}, $stats{"filtered_files"};
 	}
 	print "\n";
 } else {
 	printf "Source files: %d\n", scalar keys(%files) if($stat);
 }
 
+my $ntests=scalar(%test_names);
+printf "Number of tests: %d\n", $ntests if ($ntests > 1);
+
 if ($show_files) {
 	for my $f(sort keys %used_source) {
 		print "\t$f\n";
@@ -670,8 +971,10 @@ Parses lcov data from .info files.
 
 code_cov_parse_info <options> [input file(s)]
 
-At least one of the options B<--stat>, B<--print> and/or B<--output>
-should be used.
+At least one of the output options should be used, e g.
+B<--stat>, B<--print>, B<--print-unused>, B<--report> and/or B<--output>.
+
+Also, B<--report> can't be used together with other output options.
 
 =head1 OPTIONS
 
@@ -700,6 +1003,37 @@ Prints the functions that were never reached.
 
 The function coverage report is affected by the applied filters.
 
+=item B<--report>  B<[output file]> or B<-r>  B<[output file]>
+
+Generates an html report containing per-test and total statistics.
+
+The function coverage report is affected by the applied filters.
+
+=item B<--css-file> B<[css file]> or B<--css> B<[css file]> or B<-c> B<[css file]
+
+Adds an optional css file to the html report.
+Used only with B<--report>.
+
+=item B<--title> B<[title] or B<-t> B<[title]
+
+If used with B<--report>, it defines the title for the for the html report.
+
+If used with B<--output>, it replaces the test names with the title. This
+is useful when merging reports from multiple tests into a summarized file.
+If not used, the B<[output file]> will contain all test names on its
+beginning.
+
+Used with B<--report> AND B<--output>.
+
+=item B<--html-prolog> B<[html file] or B<--prolog> B<[html file]
+
+Adds a prolog at the beginning of the body of the html report.
+Used only with B<--report>.
+
+=item B<--html-epilog>  B<[html file] or B<--epilog>  B<[html file]
+
+Adds an epilog before the end of the body of the html report.
+Used only with B<--report>.
 
 =item B<--show-lines> or B<--show_lines>
 
-- 
2.35.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [igt-dev] ✓ Fi.CI.BAT: success for code coverage: some improvements
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
                   ` (11 preceding siblings ...)
  2022-04-04  6:26 ` [igt-dev] [PATCH 12/12] code_cov_parse_info: add support for generating html reports Mauro Carvalho Chehab
@ 2022-04-04  7:45 ` Patchwork
  2022-04-04  9:11 ` [igt-dev] ✗ Fi.CI.IGT: failure " Patchwork
  13 siblings, 0 replies; 20+ messages in thread
From: Patchwork @ 2022-04-04  7:45 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 6292 bytes --]

== Series Details ==

Series: code coverage: some improvements
URL   : https://patchwork.freedesktop.org/series/102136/
State : success

== Summary ==

CI Bug Log - changes from CI_DRM_11444 -> IGTPW_6865
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/index.html

Participating hosts (44 -> 40)
------------------------------

  Additional (2): fi-icl-u2 fi-pnv-d510 
  Missing    (6): shard-tglu bat-rpls-2 fi-bsw-cyan shard-rkl shard-dg1 fi-bdw-samus 

Known issues
------------

  Here are the changes found in IGTPW_6865 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@amdgpu/amd_cs_nop@fork-gfx0:
    - fi-icl-u2:          NOTRUN -> [SKIP][1] ([fdo#109315]) +17 similar issues
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-icl-u2/igt@amdgpu/amd_cs_nop@fork-gfx0.html

  * igt@gem_huc_copy@huc-copy:
    - fi-pnv-d510:        NOTRUN -> [SKIP][2] ([fdo#109271]) +57 similar issues
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-pnv-d510/igt@gem_huc_copy@huc-copy.html
    - fi-icl-u2:          NOTRUN -> [SKIP][3] ([i915#2190])
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-icl-u2/igt@gem_huc_copy@huc-copy.html

  * igt@gem_lmem_swapping@parallel-random-engines:
    - fi-icl-u2:          NOTRUN -> [SKIP][4] ([i915#4613]) +3 similar issues
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-icl-u2/igt@gem_lmem_swapping@parallel-random-engines.html

  * igt@i915_selftest@live@gt_engines:
    - fi-rkl-guc:         [PASS][5] -> [INCOMPLETE][6] ([i915#4418])
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/fi-rkl-guc/igt@i915_selftest@live@gt_engines.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-rkl-guc/igt@i915_selftest@live@gt_engines.html

  * igt@i915_selftest@live@hangcheck:
    - bat-dg1-6:          [PASS][7] -> [DMESG-FAIL][8] ([i915#4494] / [i915#4957])
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/bat-dg1-6/igt@i915_selftest@live@hangcheck.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/bat-dg1-6/igt@i915_selftest@live@hangcheck.html

  * igt@kms_chamelium@hdmi-hpd-fast:
    - fi-icl-u2:          NOTRUN -> [SKIP][9] ([fdo#111827]) +8 similar issues
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-icl-u2/igt@kms_chamelium@hdmi-hpd-fast.html

  * igt@kms_cursor_legacy@basic-busy-flip-before-cursor-atomic:
    - fi-icl-u2:          NOTRUN -> [SKIP][10] ([fdo#109278]) +2 similar issues
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-icl-u2/igt@kms_cursor_legacy@basic-busy-flip-before-cursor-atomic.html

  * igt@kms_flip@basic-flip-vs-dpms@a-edp1:
    - fi-tgl-u2:          [PASS][11] -> [DMESG-WARN][12] ([i915#402])
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/fi-tgl-u2/igt@kms_flip@basic-flip-vs-dpms@a-edp1.html
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-tgl-u2/igt@kms_flip@basic-flip-vs-dpms@a-edp1.html

  * igt@kms_force_connector_basic@force-load-detect:
    - fi-icl-u2:          NOTRUN -> [SKIP][13] ([fdo#109285])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-icl-u2/igt@kms_force_connector_basic@force-load-detect.html

  * igt@kms_pipe_crc_basic@compare-crc-sanitycheck-pipe-c:
    - fi-pnv-d510:        NOTRUN -> [SKIP][14] ([fdo#109271] / [i915#5341])
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-pnv-d510/igt@kms_pipe_crc_basic@compare-crc-sanitycheck-pipe-c.html

  * igt@kms_setmode@basic-clone-single-crtc:
    - fi-icl-u2:          NOTRUN -> [SKIP][15] ([i915#3555])
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-icl-u2/igt@kms_setmode@basic-clone-single-crtc.html

  * igt@prime_vgem@basic-userptr:
    - fi-icl-u2:          NOTRUN -> [SKIP][16] ([i915#3301])
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-icl-u2/igt@prime_vgem@basic-userptr.html

  * igt@runner@aborted:
    - fi-rkl-guc:         NOTRUN -> [FAIL][17] ([i915#4312])
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/fi-rkl-guc/igt@runner@aborted.html

  
#### Possible fixes ####

  * igt@kms_busy@basic@flip:
    - {bat-adlp-6}:       [DMESG-WARN][18] ([i915#3576]) -> [PASS][19]
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/bat-adlp-6/igt@kms_busy@basic@flip.html
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/bat-adlp-6/igt@kms_busy@basic@flip.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109278]: https://bugs.freedesktop.org/show_bug.cgi?id=109278
  [fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
  [fdo#109315]: https://bugs.freedesktop.org/show_bug.cgi?id=109315
  [fdo#111827]: https://bugs.freedesktop.org/show_bug.cgi?id=111827
  [i915#2190]: https://gitlab.freedesktop.org/drm/intel/issues/2190
  [i915#3301]: https://gitlab.freedesktop.org/drm/intel/issues/3301
  [i915#3555]: https://gitlab.freedesktop.org/drm/intel/issues/3555
  [i915#3576]: https://gitlab.freedesktop.org/drm/intel/issues/3576
  [i915#402]: https://gitlab.freedesktop.org/drm/intel/issues/402
  [i915#4312]: https://gitlab.freedesktop.org/drm/intel/issues/4312
  [i915#4418]: https://gitlab.freedesktop.org/drm/intel/issues/4418
  [i915#4494]: https://gitlab.freedesktop.org/drm/intel/issues/4494
  [i915#4613]: https://gitlab.freedesktop.org/drm/intel/issues/4613
  [i915#4957]: https://gitlab.freedesktop.org/drm/intel/issues/4957
  [i915#5341]: https://gitlab.freedesktop.org/drm/intel/issues/5341


Build changes
-------------

  * CI: CI-20190529 -> None
  * IGT: IGT_6407 -> IGTPW_6865

  CI-20190529: 20190529
  CI_DRM_11444: 0f7ea306958637fd2da777d3bf1e496a86d20f13 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_6865: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/index.html
  IGT_6407: eb4044d0bded3b598c54d0230cd3620c6734489c @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/index.html

[-- Attachment #2: Type: text/html, Size: 7326 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [igt-dev] ✗ Fi.CI.IGT: failure for code coverage: some improvements
  2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
                   ` (12 preceding siblings ...)
  2022-04-04  7:45 ` [igt-dev] ✓ Fi.CI.BAT: success for code coverage: some improvements Patchwork
@ 2022-04-04  9:11 ` Patchwork
  13 siblings, 0 replies; 20+ messages in thread
From: Patchwork @ 2022-04-04  9:11 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 30251 bytes --]

== Series Details ==

Series: code coverage: some improvements
URL   : https://patchwork.freedesktop.org/series/102136/
State : failure

== Summary ==

CI Bug Log - changes from CI_DRM_11444_full -> IGTPW_6865_full
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with IGTPW_6865_full absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_6865_full, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/index.html

Participating hosts (13 -> 9)
------------------------------

  Missing    (4): pig-skl-6260u shard-skl pig-kbl-iris pig-glk-j5005 

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in IGTPW_6865_full:

### IGT changes ###

#### Possible regressions ####

  * igt@gen9_exec_parse@bb-large:
    - shard-apl:          NOTRUN -> [TIMEOUT][1]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl8/igt@gen9_exec_parse@bb-large.html

  
Known issues
------------

  Here are the changes found in IGTPW_6865_full that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@feature_discovery@display-2x:
    - shard-tglb:         NOTRUN -> [SKIP][2] ([i915#1839])
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb3/igt@feature_discovery@display-2x.html

  * igt@gem_ccs@ctrl-surf-copy-new-ctx:
    - shard-tglb:         NOTRUN -> [SKIP][3] ([i915#5325])
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb2/igt@gem_ccs@ctrl-surf-copy-new-ctx.html

  * igt@gem_ctx_isolation@preservation-s3@vcs0:
    - shard-kbl:          NOTRUN -> [DMESG-WARN][4] ([i915#180]) +6 similar issues
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl1/igt@gem_ctx_isolation@preservation-s3@vcs0.html

  * igt@gem_ctx_persistence@legacy-engines-mixed:
    - shard-snb:          NOTRUN -> [SKIP][5] ([fdo#109271] / [i915#1099]) +1 similar issue
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-snb5/igt@gem_ctx_persistence@legacy-engines-mixed.html

  * igt@gem_ctx_sseu@invalid-sseu:
    - shard-tglb:         NOTRUN -> [SKIP][6] ([i915#280]) +2 similar issues
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb5/igt@gem_ctx_sseu@invalid-sseu.html

  * igt@gem_eio@unwedge-stress:
    - shard-iclb:         [PASS][7] -> [TIMEOUT][8] ([i915#2481] / [i915#3070])
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-iclb4/igt@gem_eio@unwedge-stress.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb5/igt@gem_eio@unwedge-stress.html

  * igt@gem_exec_balancer@parallel-contexts:
    - shard-kbl:          NOTRUN -> [DMESG-WARN][9] ([i915#5076])
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl1/igt@gem_exec_balancer@parallel-contexts.html

  * igt@gem_exec_fair@basic-flow@rcs0:
    - shard-tglb:         [PASS][10] -> [FAIL][11] ([i915#2842])
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-tglb2/igt@gem_exec_fair@basic-flow@rcs0.html
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb6/igt@gem_exec_fair@basic-flow@rcs0.html

  * igt@gem_exec_fair@basic-none@vcs0:
    - shard-tglb:         NOTRUN -> [FAIL][12] ([i915#2842]) +5 similar issues
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb1/igt@gem_exec_fair@basic-none@vcs0.html

  * igt@gem_exec_fair@basic-pace@vecs0:
    - shard-kbl:          [PASS][13] -> [FAIL][14] ([i915#2842]) +1 similar issue
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-kbl4/igt@gem_exec_fair@basic-pace@vecs0.html
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl3/igt@gem_exec_fair@basic-pace@vecs0.html

  * igt@gem_exec_fair@basic-throttle@rcs0:
    - shard-iclb:         [PASS][15] -> [FAIL][16] ([i915#2849])
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-iclb5/igt@gem_exec_fair@basic-throttle@rcs0.html
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb5/igt@gem_exec_fair@basic-throttle@rcs0.html

  * igt@gem_exec_params@secure-non-master:
    - shard-tglb:         NOTRUN -> [SKIP][17] ([fdo#112283])
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb1/igt@gem_exec_params@secure-non-master.html
    - shard-iclb:         NOTRUN -> [SKIP][18] ([fdo#112283])
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb5/igt@gem_exec_params@secure-non-master.html

  * igt@gem_exec_whisper@basic-contexts-forked:
    - shard-glk:          [PASS][19] -> [DMESG-WARN][20] ([i915#118]) +1 similar issue
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-glk5/igt@gem_exec_whisper@basic-contexts-forked.html
   [20]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-glk1/igt@gem_exec_whisper@basic-contexts-forked.html

  * igt@gem_huc_copy@huc-copy:
    - shard-kbl:          NOTRUN -> [SKIP][21] ([fdo#109271] / [i915#2190])
   [21]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl1/igt@gem_huc_copy@huc-copy.html

  * igt@gem_lmem_swapping@heavy-verify-random:
    - shard-tglb:         NOTRUN -> [SKIP][22] ([i915#4613]) +3 similar issues
   [22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb1/igt@gem_lmem_swapping@heavy-verify-random.html
    - shard-iclb:         NOTRUN -> [SKIP][23] ([i915#4613]) +1 similar issue
   [23]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb7/igt@gem_lmem_swapping@heavy-verify-random.html

  * igt@gem_lmem_swapping@smem-oom:
    - shard-kbl:          NOTRUN -> [SKIP][24] ([fdo#109271] / [i915#4613]) +3 similar issues
   [24]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl7/igt@gem_lmem_swapping@smem-oom.html
    - shard-apl:          NOTRUN -> [SKIP][25] ([fdo#109271] / [i915#4613]) +1 similar issue
   [25]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl8/igt@gem_lmem_swapping@smem-oom.html

  * igt@gem_media_vme:
    - shard-tglb:         NOTRUN -> [SKIP][26] ([i915#284])
   [26]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb1/igt@gem_media_vme.html

  * igt@gem_mmap_gtt@coherency:
    - shard-tglb:         NOTRUN -> [SKIP][27] ([fdo#111656])
   [27]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb7/igt@gem_mmap_gtt@coherency.html
    - shard-iclb:         NOTRUN -> [SKIP][28] ([fdo#109292])
   [28]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb6/igt@gem_mmap_gtt@coherency.html

  * igt@gem_pxp@create-regular-context-1:
    - shard-iclb:         NOTRUN -> [SKIP][29] ([i915#4270]) +1 similar issue
   [29]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb6/igt@gem_pxp@create-regular-context-1.html

  * igt@gem_pxp@regular-baseline-src-copy-readible:
    - shard-tglb:         NOTRUN -> [SKIP][30] ([i915#4270]) +6 similar issues
   [30]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb3/igt@gem_pxp@regular-baseline-src-copy-readible.html

  * igt@gem_render_copy@y-tiled-to-vebox-linear:
    - shard-iclb:         NOTRUN -> [SKIP][31] ([i915#768]) +2 similar issues
   [31]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb1/igt@gem_render_copy@y-tiled-to-vebox-linear.html

  * igt@gem_userptr_blits@access-control:
    - shard-tglb:         NOTRUN -> [SKIP][32] ([i915#3297]) +2 similar issues
   [32]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb3/igt@gem_userptr_blits@access-control.html

  * igt@gem_userptr_blits@dmabuf-sync:
    - shard-kbl:          NOTRUN -> [SKIP][33] ([fdo#109271] / [i915#3323])
   [33]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl1/igt@gem_userptr_blits@dmabuf-sync.html
    - shard-apl:          NOTRUN -> [SKIP][34] ([fdo#109271] / [i915#3323])
   [34]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl3/igt@gem_userptr_blits@dmabuf-sync.html

  * igt@gem_userptr_blits@unsync-unmap-after-close:
    - shard-iclb:         NOTRUN -> [SKIP][35] ([i915#3297])
   [35]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb4/igt@gem_userptr_blits@unsync-unmap-after-close.html

  * igt@gem_userptr_blits@vma-merge:
    - shard-kbl:          NOTRUN -> [FAIL][36] ([i915#3318])
   [36]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl1/igt@gem_userptr_blits@vma-merge.html
    - shard-tglb:         NOTRUN -> [FAIL][37] ([i915#3318])
   [37]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb2/igt@gem_userptr_blits@vma-merge.html

  * igt@gen3_mixed_blits:
    - shard-iclb:         NOTRUN -> [SKIP][38] ([fdo#109289])
   [38]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb2/igt@gen3_mixed_blits.html

  * igt@gen7_exec_parse@basic-allowed:
    - shard-tglb:         NOTRUN -> [SKIP][39] ([fdo#109289]) +5 similar issues
   [39]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb5/igt@gen7_exec_parse@basic-allowed.html

  * igt@gen9_exec_parse@bb-oversize:
    - shard-tglb:         NOTRUN -> [SKIP][40] ([i915#2527] / [i915#2856]) +7 similar issues
   [40]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb6/igt@gen9_exec_parse@bb-oversize.html

  * igt@gen9_exec_parse@valid-registers:
    - shard-iclb:         NOTRUN -> [SKIP][41] ([i915#2856]) +6 similar issues
   [41]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb1/igt@gen9_exec_parse@valid-registers.html

  * igt@i915_pm_dc@dc6-psr:
    - shard-iclb:         [PASS][42] -> [FAIL][43] ([i915#454])
   [42]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-iclb4/igt@i915_pm_dc@dc6-psr.html
   [43]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb6/igt@i915_pm_dc@dc6-psr.html

  * igt@i915_pm_dc@dc9-dpms:
    - shard-apl:          [PASS][44] -> [SKIP][45] ([fdo#109271])
   [44]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-apl4/igt@i915_pm_dc@dc9-dpms.html
   [45]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl3/igt@i915_pm_dc@dc9-dpms.html

  * igt@i915_pm_lpsp@kms-lpsp@kms-lpsp-hdmi-a:
    - shard-glk:          NOTRUN -> [SKIP][46] ([fdo#109271] / [i915#1937])
   [46]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-glk6/igt@i915_pm_lpsp@kms-lpsp@kms-lpsp-hdmi-a.html

  * igt@i915_pm_rpm@modeset-non-lpsp-stress:
    - shard-tglb:         NOTRUN -> [SKIP][47] ([fdo#111644] / [i915#1397] / [i915#2411])
   [47]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb1/igt@i915_pm_rpm@modeset-non-lpsp-stress.html

  * igt@i915_pm_rpm@modeset-pc8-residency-stress:
    - shard-tglb:         NOTRUN -> [SKIP][48] ([fdo#109506] / [i915#2411])
   [48]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb7/igt@i915_pm_rpm@modeset-pc8-residency-stress.html

  * igt@i915_pm_rpm@system-suspend-modeset:
    - shard-glk:          [PASS][49] -> [DMESG-WARN][50] ([i915#118] / [i915#1888])
   [49]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-glk8/igt@i915_pm_rpm@system-suspend-modeset.html
   [50]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-glk2/igt@i915_pm_rpm@system-suspend-modeset.html

  * igt@i915_query@query-topology-known-pci-ids:
    - shard-tglb:         NOTRUN -> [SKIP][51] ([fdo#109303])
   [51]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb2/igt@i915_query@query-topology-known-pci-ids.html
    - shard-iclb:         NOTRUN -> [SKIP][52] ([fdo#109303])
   [52]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb2/igt@i915_query@query-topology-known-pci-ids.html

  * igt@i915_query@query-topology-unsupported:
    - shard-iclb:         NOTRUN -> [SKIP][53] ([fdo#109302])
   [53]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb4/igt@i915_query@query-topology-unsupported.html
    - shard-tglb:         NOTRUN -> [SKIP][54] ([fdo#109302])
   [54]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb2/igt@i915_query@query-topology-unsupported.html

  * igt@i915_selftest@live@gt_lrc:
    - shard-tglb:         NOTRUN -> [DMESG-FAIL][55] ([i915#2373])
   [55]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb2/igt@i915_selftest@live@gt_lrc.html

  * igt@i915_selftest@live@gt_pm:
    - shard-tglb:         NOTRUN -> [DMESG-FAIL][56] ([i915#1759])
   [56]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb2/igt@i915_selftest@live@gt_pm.html

  * igt@i915_suspend@fence-restore-tiled2untiled:
    - shard-apl:          [PASS][57] -> [DMESG-WARN][58] ([i915#180])
   [57]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-apl7/igt@i915_suspend@fence-restore-tiled2untiled.html
   [58]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl4/igt@i915_suspend@fence-restore-tiled2untiled.html

  * igt@i915_suspend@forcewake:
    - shard-kbl:          [PASS][59] -> [DMESG-WARN][60] ([i915#180])
   [59]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-kbl6/igt@i915_suspend@forcewake.html
   [60]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl6/igt@i915_suspend@forcewake.html

  * igt@kms_addfb_basic@invalid-smem-bo-on-discrete:
    - shard-tglb:         NOTRUN -> [SKIP][61] ([i915#3826])
   [61]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb2/igt@kms_addfb_basic@invalid-smem-bo-on-discrete.html

  * igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0-async-flip:
    - shard-iclb:         NOTRUN -> [SKIP][62] ([i915#5286]) +8 similar issues
   [62]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb4/igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0-async-flip.html

  * igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0-hflip:
    - shard-tglb:         NOTRUN -> [SKIP][63] ([i915#5286]) +12 similar issues
   [63]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb6/igt@kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0-hflip.html

  * igt@kms_big_fb@linear-16bpp-rotate-270:
    - shard-tglb:         NOTRUN -> [SKIP][64] ([fdo#111614]) +4 similar issues
   [64]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb2/igt@kms_big_fb@linear-16bpp-rotate-270.html

  * igt@kms_big_fb@linear-16bpp-rotate-90:
    - shard-iclb:         NOTRUN -> [SKIP][65] ([fdo#110725] / [fdo#111614]) +2 similar issues
   [65]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb3/igt@kms_big_fb@linear-16bpp-rotate-90.html

  * igt@kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip:
    - shard-apl:          NOTRUN -> [SKIP][66] ([fdo#109271] / [i915#3777]) +4 similar issues
   [66]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl2/igt@kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip.html
    - shard-glk:          NOTRUN -> [SKIP][67] ([fdo#109271] / [i915#3777]) +2 similar issues
   [67]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-glk7/igt@kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip.html

  * igt@kms_big_fb@yf-tiled-8bpp-rotate-180:
    - shard-tglb:         NOTRUN -> [SKIP][68] ([fdo#111615]) +13 similar issues
   [68]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb5/igt@kms_big_fb@yf-tiled-8bpp-rotate-180.html

  * igt@kms_big_fb@yf-tiled-max-hw-stride-32bpp-rotate-0-hflip-async-flip:
    - shard-kbl:          NOTRUN -> [SKIP][69] ([fdo#109271] / [i915#3777]) +7 similar issues
   [69]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl3/igt@kms_big_fb@yf-tiled-max-hw-stride-32bpp-rotate-0-hflip-async-flip.html

  * igt@kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-0:
    - shard-iclb:         NOTRUN -> [SKIP][70] ([fdo#110723]) +6 similar issues
   [70]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb3/igt@kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-0.html

  * igt@kms_ccs@pipe-a-bad-pixel-format-y_tiled_ccs:
    - shard-tglb:         NOTRUN -> [SKIP][71] ([i915#3689]) +12 similar issues
   [71]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb1/igt@kms_ccs@pipe-a-bad-pixel-format-y_tiled_ccs.html

  * igt@kms_ccs@pipe-a-bad-rotation-90-y_tiled_gen12_mc_ccs:
    - shard-glk:          NOTRUN -> [SKIP][72] ([fdo#109271] / [i915#3886]) +4 similar issues
   [72]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-glk4/igt@kms_ccs@pipe-a-bad-rotation-90-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-a-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc:
    - shard-apl:          NOTRUN -> [SKIP][73] ([fdo#109271] / [i915#3886]) +13 similar issues
   [73]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl7/igt@kms_ccs@pipe-a-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc.html

  * igt@kms_ccs@pipe-b-bad-aux-stride-y_tiled_gen12_mc_ccs:
    - shard-tglb:         NOTRUN -> [SKIP][74] ([i915#3689] / [i915#3886]) +7 similar issues
   [74]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb2/igt@kms_ccs@pipe-b-bad-aux-stride-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-b-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc:
    - shard-kbl:          NOTRUN -> [SKIP][75] ([fdo#109271] / [i915#3886]) +23 similar issues
   [75]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl4/igt@kms_ccs@pipe-b-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc.html

  * igt@kms_ccs@pipe-c-crc-primary-rotation-180-y_tiled_gen12_mc_ccs:
    - shard-iclb:         NOTRUN -> [SKIP][76] ([fdo#109278] / [i915#3886]) +8 similar issues
   [76]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb1/igt@kms_ccs@pipe-c-crc-primary-rotation-180-y_tiled_gen12_mc_ccs.html

  * igt@kms_ccs@pipe-d-bad-rotation-90-yf_tiled_ccs:
    - shard-tglb:         NOTRUN -> [SKIP][77] ([fdo#111615] / [i915#3689]) +12 similar issues
   [77]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb1/igt@kms_ccs@pipe-d-bad-rotation-90-yf_tiled_ccs.html

  * igt@kms_chamelium@hdmi-mode-timings:
    - shard-snb:          NOTRUN -> [SKIP][78] ([fdo#109271] / [fdo#111827]) +6 similar issues
   [78]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-snb2/igt@kms_chamelium@hdmi-mode-timings.html
    - shard-glk:          NOTRUN -> [SKIP][79] ([fdo#109271] / [fdo#111827]) +6 similar issues
   [79]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-glk7/igt@kms_chamelium@hdmi-mode-timings.html

  * igt@kms_color@pipe-d-ctm-max:
    - shard-iclb:         NOTRUN -> [SKIP][80] ([fdo#109278] / [i915#1149])
   [80]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb7/igt@kms_color@pipe-d-ctm-max.html

  * igt@kms_color_chamelium@pipe-a-ctm-blue-to-red:
    - shard-kbl:          NOTRUN -> [SKIP][81] ([fdo#109271] / [fdo#111827]) +39 similar issues
   [81]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl1/igt@kms_color_chamelium@pipe-a-ctm-blue-to-red.html

  * igt@kms_color_chamelium@pipe-a-ctm-limited-range:
    - shard-apl:          NOTRUN -> [SKIP][82] ([fdo#109271] / [fdo#111827]) +17 similar issues
   [82]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl8/igt@kms_color_chamelium@pipe-a-ctm-limited-range.html

  * igt@kms_color_chamelium@pipe-b-ctm-0-75:
    - shard-tglb:         NOTRUN -> [SKIP][83] ([fdo#109284] / [fdo#111827]) +27 similar issues
   [83]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb3/igt@kms_color_chamelium@pipe-b-ctm-0-75.html

  * igt@kms_color_chamelium@pipe-b-ctm-red-to-blue:
    - shard-iclb:         NOTRUN -> [SKIP][84] ([fdo#109284] / [fdo#111827]) +12 similar issues
   [84]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb8/igt@kms_color_chamelium@pipe-b-ctm-red-to-blue.html

  * igt@kms_color_chamelium@pipe-d-ctm-limited-range:
    - shard-iclb:         NOTRUN -> [SKIP][85] ([fdo#109278] / [fdo#109284] / [fdo#111827])
   [85]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb3/igt@kms_color_chamelium@pipe-d-ctm-limited-range.html

  * igt@kms_content_protection@atomic-dpms:
    - shard-kbl:          NOTRUN -> [TIMEOUT][86] ([i915#1319])
   [86]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl4/igt@kms_content_protection@atomic-dpms.html

  * igt@kms_content_protection@dp-mst-lic-type-0:
    - shard-iclb:         NOTRUN -> [SKIP][87] ([i915#3116])
   [87]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb2/igt@kms_content_protection@dp-mst-lic-type-0.html
    - shard-tglb:         NOTRUN -> [SKIP][88] ([i915#3116] / [i915#3299])
   [88]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb6/igt@kms_content_protection@dp-mst-lic-type-0.html

  * igt@kms_content_protection@uevent:
    - shard-kbl:          NOTRUN -> [FAIL][89] ([i915#2105])
   [89]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl6/igt@kms_content_protection@uevent.html
    - shard-tglb:         NOTRUN -> [SKIP][90] ([i915#1063]) +1 similar issue
   [90]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb7/igt@kms_content_protection@uevent.html
    - shard-iclb:         NOTRUN -> [SKIP][91] ([fdo#109300] / [fdo#111066]) +1 similar issue
   [91]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb7/igt@kms_content_protection@uevent.html

  * igt@kms_cursor_crc@pipe-b-cursor-512x170-offscreen:
    - shard-iclb:         NOTRUN -> [SKIP][92] ([fdo#109278] / [fdo#109279])
   [92]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb7/igt@kms_cursor_crc@pipe-b-cursor-512x170-offscreen.html

  * igt@kms_cursor_crc@pipe-b-cursor-suspend:
    - shard-apl:          NOTRUN -> [DMESG-WARN][93] ([i915#180])
   [93]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl1/igt@kms_cursor_crc@pipe-b-cursor-suspend.html

  * igt@kms_cursor_crc@pipe-c-cursor-32x10-rapid-movement:
    - shard-tglb:         NOTRUN -> [SKIP][94] ([i915#3359]) +10 similar issues
   [94]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb6/igt@kms_cursor_crc@pipe-c-cursor-32x10-rapid-movement.html

  * igt@kms_cursor_crc@pipe-d-cursor-32x32-offscreen:
    - shard-tglb:         NOTRUN -> [SKIP][95] ([i915#3319]) +4 similar issues
   [95]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb5/igt@kms_cursor_crc@pipe-d-cursor-32x32-offscreen.html

  * igt@kms_cursor_crc@pipe-d-cursor-512x512-offscreen:
    - shard-tglb:         NOTRUN -> [SKIP][96] ([fdo#109279] / [i915#3359]) +5 similar issues
   [96]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb5/igt@kms_cursor_crc@pipe-d-cursor-512x512-offscreen.html

  * igt@kms_cursor_crc@pipe-d-cursor-suspend:
    - shard-kbl:          NOTRUN -> [SKIP][97] ([fdo#109271]) +407 similar issues
   [97]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl1/igt@kms_cursor_crc@pipe-d-cursor-suspend.html

  * igt@kms_cursor_legacy@basic-busy-flip-before-cursor-varying-size:
    - shard-tglb:         NOTRUN -> [SKIP][98] ([i915#4103]) +1 similar issue
   [98]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb7/igt@kms_cursor_legacy@basic-busy-flip-before-cursor-varying-size.html

  * igt@kms_cursor_legacy@cursorb-vs-flipb-atomic-transitions-varying-size:
    - shard-iclb:         NOTRUN -> [SKIP][99] ([fdo#109274] / [fdo#109278]) +4 similar issues
   [99]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb7/igt@kms_cursor_legacy@cursorb-vs-flipb-atomic-transitions-varying-size.html

  * igt@kms_dp_tiled_display@basic-test-pattern-with-chamelium:
    - shard-tglb:         NOTRUN -> [SKIP][100] ([i915#3528])
   [100]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb6/igt@kms_dp_tiled_display@basic-test-pattern-with-chamelium.html

  * igt@kms_draw_crc@draw-method-rgb565-mmap-gtt-4tiled:
    - shard-tglb:         NOTRUN -> [SKIP][101] ([i915#5287]) +5 similar issues
   [101]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb2/igt@kms_draw_crc@draw-method-rgb565-mmap-gtt-4tiled.html

  * igt@kms_draw_crc@draw-method-xrgb8888-mmap-wc-4tiled:
    - shard-iclb:         NOTRUN -> [SKIP][102] ([i915#5287]) +2 similar issues
   [102]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb7/igt@kms_draw_crc@draw-method-xrgb8888-mmap-wc-4tiled.html

  * igt@kms_fbcon_fbt@fbc-suspend:
    - shard-apl:          [PASS][103] -> [INCOMPLETE][104] ([i915#180] / [i915#1982])
   [103]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-apl7/igt@kms_fbcon_fbt@fbc-suspend.html
   [104]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl2/igt@kms_fbcon_fbt@fbc-suspend.html
    - shard-kbl:          NOTRUN -> [INCOMPLETE][105] ([i915#180] / [i915#636])
   [105]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl7/igt@kms_fbcon_fbt@fbc-suspend.html

  * igt@kms_flip@2x-blocking-absolute-wf_vblank-interruptible:
    - shard-tglb:         NOTRUN -> [SKIP][106] ([fdo#109274] / [fdo#111825]) +23 similar issues
   [106]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb5/igt@kms_flip@2x-blocking-absolute-wf_vblank-interruptible.html

  * igt@kms_flip@2x-blocking-wf_vblank:
    - shard-iclb:         NOTRUN -> [SKIP][107] ([fdo#109274]) +7 similar issues
   [107]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb8/igt@kms_flip@2x-blocking-wf_vblank.html

  * igt@kms_flip@2x-flip-vs-expired-vblank-interruptible@ab-hdmi-a1-hdmi-a2:
    - shard-glk:          [PASS][108] -> [FAIL][109] ([i915#2122])
   [108]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-glk7/igt@kms_flip@2x-flip-vs-expired-vblank-interruptible@ab-hdmi-a1-hdmi-a2.html
   [109]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-glk1/igt@kms_flip@2x-flip-vs-expired-vblank-interruptible@ab-hdmi-a1-hdmi-a2.html

  * igt@kms_flip@flip-vs-expired-vblank@a-hdmi-a1:
    - shard-glk:          [PASS][110] -> [FAIL][111] ([i915#79])
   [110]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-glk6/igt@kms_flip@flip-vs-expired-vblank@a-hdmi-a1.html
   [111]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-glk7/igt@kms_flip@flip-vs-expired-vblank@a-hdmi-a1.html

  * igt@kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling:
    - shard-tglb:         NOTRUN -> [SKIP][112] ([i915#2587]) +1 similar issue
   [112]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb1/igt@kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling.html

  * igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling:
    - shard-iclb:         [PASS][113] -> [SKIP][114] ([i915#3701])
   [113]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-iclb4/igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling.html
   [114]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb2/igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling.html

  * igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling:
    - shard-snb:          NOTRUN -> [SKIP][115] ([fdo#109271]) +172 similar issues
   [115]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-snb4/igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling.html

  * igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-upscaling:
    - shard-iclb:         NOTRUN -> [SKIP][116] ([i915#2587])
   [116]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb2/igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-upscaling.html

  * igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-blt:
    - shard-iclb:         NOTRUN -> [SKIP][117] ([fdo#109280]) +26 similar issues
   [117]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-iclb1/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-blt.html

  * igt@kms_frontbuffer_tracking@fbcpsr-2p-primscrn-cur-indfb-draw-mmap-gtt:
    - shard-tglb:         NOTRUN -> [SKIP][118] ([fdo#109280] / [fdo#111825]) +57 similar issues
   [118]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb5/igt@kms_frontbuffer_tracking@fbcpsr-2p-primscrn-cur-indfb-draw-mmap-gtt.html

  * igt@kms_hdmi_inject@inject-audio:
    - shard-tglb:         [PASS][119] -> [SKIP][120] ([i915#433])
   [119]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_11444/shard-tglb6/igt@kms_hdmi_inject@inject-audio.html
   [120]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb1/igt@kms_hdmi_inject@inject-audio.html

  * igt@kms_hdr@static-toggle-suspend:
    - shard-tglb:         NOTRUN -> [SKIP][121] ([i915#3555]) +1 similar issue
   [121]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-tglb5/igt@kms_hdr@static-toggle-suspend.html

  * igt@kms_pipe_crc_basic@disable-crc-after-crtc-pipe-d:
    - shard-apl:          NOTRUN -> [SKIP][122] ([fdo#109271] / [i915#533])
   [122]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl3/igt@kms_pipe_crc_basic@disable-crc-after-crtc-pipe-d.html

  * igt@kms_plane_alpha_blend@pipe-a-alpha-opaque-fb:
    - shard-apl:          NOTRUN -> [FAIL][123] ([fdo#108145] / [i915#265]) +1 similar issue
   [123]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl7/igt@kms_plane_alpha_blend@pipe-a-alpha-opaque-fb.html
    - shard-glk:          NOTRUN -> [FAIL][124] ([fdo#108145] / [i915#265])
   [124]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-glk4/igt@kms_plane_alpha_blend@pipe-a-alpha-opaque-fb.html

  * igt@kms_plane_alpha_blend@pipe-a-alpha-transparent-fb:
    - shard-kbl:          NOTRUN -> [FAIL][125] ([i915#265])
   [125]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl6/igt@kms_plane_alpha_blend@pipe-a-alpha-transparent-fb.html

  * igt@kms_plane_alpha_blend@pipe-c-alpha-basic:
    - shard-kbl:          NOTRUN -> [FAIL][126] ([fdo#108145] / [i915#265]) +4 similar issues
   [126]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-kbl6/igt@kms_plane_alpha_blend@pipe-c-alpha-basic.html

  * igt@kms_plane_alpha_blend@pipe-c-alpha-transparent-fb:
    - shard-apl:          NOTRUN -> [FAIL][127] ([i915#265]) +1 similar issue
   [127]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/shard-apl6/igt@kms

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_6865/index.html

[-- Attachment #2: Type: text/html, Size: 33885 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [igt-dev] [PATCH 10/12] code_cov_parse_info: fix --show-lines logic
  2022-04-04  6:26 ` [igt-dev] [PATCH 10/12] code_cov_parse_info: fix --show-lines logic Mauro Carvalho Chehab
@ 2022-04-11 11:57   ` Andrzej Hajda
  0 siblings, 0 replies; 20+ messages in thread
From: Andrzej Hajda @ 2022-04-11 11:57 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham



On 04.04.2022 08:26, Mauro Carvalho Chehab wrote:
> From: Mauro Carvalho Chehab <mchehab@kernel.org>
>
> print_code_coverage function needs to first read the number
> of lines and then simplify the $file by removing its prefix.
>
> Without that, it ends trying to access an uninitiated value,
> leading to warnings and not doing what's expected.
>
> While here, also check if the ln data is valid, just in case
> the .info file might have some FN field(s) missing.
>
> Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
> ---
>   scripts/code_cov_parse_info | 7 ++++++-
>   1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
> index c8284a297b33..7987b0068e88 100755
> --- a/scripts/code_cov_parse_info
> +++ b/scripts/code_cov_parse_info
> @@ -378,8 +378,13 @@ sub print_code_coverage($$$)
>   			}
>   
>   			if ($show_lines) {
> +				my $ln = $all_func{$func}{$file}->{ln};
>   				$file =~ s,$prefix,linux/,;
> -				$name = "$func() from $file:" . $all_func{$func}{$file}->{ln};
> +				if ($ln) {
> +					$name = "$func() from $file:" . $ln;
> +				} else {
> +					$name = "$func() from $file";
> +				}

$name = "$func() from $file";

$name .= ":" . $ln if ($ln);

Would be more compact.

Anyway:
Reviewed-by: Andrzej Hajda <andrzej.hajda@intel.com>

Regards
Andrzej


>   			} elsif (scalar @keys > 1) {
>   				$file =~ s,$prefix,linux/,;
>   				$name = "$func() from $file:";

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [igt-dev] [PATCH 11/12] code_cov_parse_info: add support for exclude filters
  2022-04-04  6:26 ` [igt-dev] [PATCH 11/12] code_cov_parse_info: add support for exclude filters Mauro Carvalho Chehab
@ 2022-04-11 12:28   ` Andrzej Hajda
  2022-04-12  9:16     ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 20+ messages in thread
From: Andrzej Hajda @ 2022-04-11 12:28 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham



On 04.04.2022 08:26, Mauro Carvalho Chehab wrote:
> From: Mauro Carvalho Chehab <mchehab@kernel.org>
>
> It is interesting to have support not only for including, but
> also for excluding functions and files. Also, it is trivial to
> have support for it.
>
> Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
> ---
>   scripts/code_cov_parse_info | 71 +++++++++++++++++++++++++++++++++----
>   1 file changed, 65 insertions(+), 6 deletions(-)
>
> diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
> index 7987b0068e88..9624dc33468d 100755
> --- a/scripts/code_cov_parse_info
> +++ b/scripts/code_cov_parse_info
> @@ -17,8 +17,10 @@ my %used_source;
>   my %record;
>   my %files;
>   my @func_regexes;
> +my @func_exclude_regexes;
>   my %test_names;
>   my @src_regexes;
> +my @src_exclude_regexes;
>   
>   my $verbose = 0;
>   my $ignore_unused = 0;
> @@ -28,10 +30,16 @@ my $skip_func = 0;
>   
>   sub is_function_excluded($)
>   {
> -	return 0 if (!@func_regexes);
> +	return 0 if (!@func_regexes && !@func_exclude_regexes);
>   
>   	my $func = shift;
>   
> +	foreach my $r (@func_exclude_regexes) {
> +		return 1 if ($func =~ m/$r/);
> +	}
> +
> +	return 0 if (!@func_regexes);
> +
>   	foreach my $r (@func_regexes) {
>   		return 0 if ($func =~ m/$r/);
>   	}
> @@ -64,9 +72,13 @@ sub filter_file($)
>   		}
>   	}
>   
> -	return 0 if (!@src_regexes);
> +	return 0 if (!@src_regexes && !@src_exclude_regexes);
>   
> -	my $func = shift;
> +	foreach my $r (@src_exclude_regexes) {
> +		return 1 if ($s =~ m/$r/);
> +	}
> +
> +	return 0 if (!@src_regexes);
>   
>   	foreach my $r (@src_regexes) {
>   		return 0 if ($s =~ m/$r/);
> @@ -482,7 +494,9 @@ my $filter;
>   my $help;
>   my $man;
>   my $func_filters;
> +my $func_exclude;
>   my $src_filters;
> +my $src_exclude;
>   my $show_files;
>   my $show_lines;
>   
> @@ -496,7 +510,9 @@ GetOptions(
>   	"only-i915|only_i915" => \$only_i915,
>   	"only-drm|only_drm" => \$only_drm,
>   	"func-filters|f=s" => \$func_filters,
> +	"exclude-func=s" => \$func_exclude,
>   	"source-filters|S=s" => \$src_filters,
> +	"exclude-source=s" => \$src_exclude,
>   	"show-files|show_files" => \$show_files,
>   	"show-lines|show_lines" => \$show_lines,
>   	"help" => \$help,
> @@ -539,7 +555,29 @@ if ($src_filters) {
>   	close IN;
>   }
>   
> -$ignore_unused = 1 if (@func_regexes);
> +if ($func_exclude) {
> +	open IN, $func_exclude or die "Can't open $func_exclude";
> +	while (<IN>) {
> +		s/^\s+//;
> +		s/\s+$//;
> +		next if (m/^#/ || m/^$/);
> +		push @func_exclude_regexes, qr /$_/;
> +	}
> +	close IN;
> +}
> +
> +if ($src_exclude) {
> +	open IN, $src_exclude or die "Can't open $src_exclude";
> +	while (<IN>) {
> +		s/^\s+//;
> +		s/\s+$//;
> +		next if (m/^#/ || m/^$/);
> +		push @src_exclude_regexes, qr /$_/;
> +	}
> +	close IN;
> +}
> +

Now there are four loops with the same purpose, I think it is time to 
put common code into one routine.

> +$ignore_unused = 1 if (@func_regexes || @func_exclude_regexes);
>   
>   if ($only_i915) {
>   	$filter_str = " non-i915 files";
> @@ -552,7 +590,7 @@ if ($only_drm) {
>   	$has_filter = 1;
>   }
>   
> -if (@func_regexes) {
> +if (@func_regexes || @func_exclude_regexes) {
>   	$filter_str .= "," if ($filter_str ne "");
>   	$filter_str .= " unmatched functions";
>   	foreach my $r (@func_regexes) {
> @@ -562,7 +600,7 @@ if (@func_regexes) {
>   	$has_filter = 1;
>   }
>   
> -if (@src_regexes) {
> +if (@src_regexes || @src_exclude_regexes) {
>   	$filter_str .= "," if ($filter_str ne "");
>   	$filter_str .= " unmatched source files";
>   	foreach my $r (@src_regexes) {
> @@ -708,11 +746,32 @@ the regular expressions contained at the B<[filter's file]>.
>   When this filter is used, B<--ignore-unused> will be automaticaly enabled,
>   as the final goal is to report per-function usage, and not per-file.
>   
> +When used with B<--exclude-func>, exclusions take precedence.
> +
> +=item B<--exclude-func>  B<[filter's file]>
> +
> +Exclude all functions that match the regular expressions contained
> +at the B<[filter's file]>.
> +
> +When this filter is used, B<--ignore-unused> will be automaticaly enabled,
> +as the final goal is to report per-function usage, and not per-file.
> +
> +When used with B<--func-filters>, exclusions take precedence.
> +
>   =item B<--source-filters>  B<[filter's file]> or B<-S>  B<[filter's file]>
>   
>   Takes into account only the code coverage for the source files that match
>   the regular expressions contained at the B<[filter's file]>.
>   
> +When used with B<--exclude-source>, exclusions take precedence.
> +
> +=item B<--exclude-source> B<[filter's file]>
> +
> +Exclude all files that match the regular expressions contained
> +at the B<[filter's file]>.
> +
> +When used with B<--source-filters>, exclusions take precedence.
> +

Now there are two filtering options per functions and two per files.
Wouldn't be better to squash include/exclude rules into one param in 
'rsync like' patterns:
+ include_pattern
- exclude_pattern
...
This way it would be easier to manage filters - less options, less 
helper files.

One could think about squashing also filters for function and files, but 
this I am not sure.


Regards
Andrzej


>   =item B<--ignore-unused> or B<--ignore_unused>
>   
>   Filters out unused C files and headers from the code coverage results.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [igt-dev] [PATCH 12/12] code_cov_parse_info: add support for generating html reports
  2022-04-04  6:26 ` [igt-dev] [PATCH 12/12] code_cov_parse_info: add support for generating html reports Mauro Carvalho Chehab
@ 2022-04-11 13:15   ` Andrzej Hajda
  2022-04-11 15:49     ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 20+ messages in thread
From: Andrzej Hajda @ 2022-04-11 13:15 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, igt-dev, Petri Latvala; +Cc: Ch Sai Gowtham



On 04.04.2022 08:26, Mauro Carvalho Chehab wrote:
> From: Mauro Carvalho Chehab <mchehab@kernel.org>
>
> While lcov has already its own report generator, it is interesting
> to be able to deal with multiple files exposing each input in
> separate.
>
> So, add new command line parameters to allow it to generate html
> reports. Also add some command lines to setup html title, add a
> css file and include a prolog/epilog at the html body.
>
> The title option can also be useful to rename the titles when
> merging multiple info files.
>
> Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
> ---
>   scripts/code_cov_parse_info | 438 +++++++++++++++++++++++++++++++-----
>   1 file changed, 386 insertions(+), 52 deletions(-)
>
> diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
> index 9624dc33468d..faa585ae5b18 100755
> --- a/scripts/code_cov_parse_info
> +++ b/scripts/code_cov_parse_info
> @@ -9,6 +9,8 @@ use Pod::Man;
>   
>   my $prefix = qr ".*?(linux)\w*/";
>   
> +my $title = "";
> +
>   my %used_func;
>   my %all_func;
>   my %all_branch;
> @@ -307,8 +309,12 @@ sub write_filtered_file($)
>   
>   	my $filtered = "";
>   
> -	foreach my $testname(sort keys %test_names) {
> -		$filtered .= "TN:$testname\n";
> +	if ($title eq "") {
> +		foreach my $testname(sort keys %test_names) {
> +			$filtered .= "TN:$testname\n";
> +		}
> +	} else {
> +		$filtered .= "TN:$title\n";
>   	}
>   
>   	# Generates filtered data
> @@ -416,71 +422,331 @@ sub print_code_coverage($$$)
>   	}
>   }
>   
> -sub print_summary()
> +my %stats;
> +
> +sub gen_stats()
>   {
> -	# Output per-line coverage statistics
> -	my $line_count = 0;
> -	my $line_reached = 0;
> +	# per-line coverage statistics
> +	$stats{"line_count"} = 0;
> +	$stats{"line_reached"} = 0;
>   
>   	foreach my $source (keys(%all_line)) {
>   		next if (!$used_source{$source});
>   
>   		foreach my $where (keys(%{$all_line{$source}})) {
> -			$line_count++;
> -			$line_reached++ if ($all_line{$source}{$where} != 0);
> +			$stats{"line_count"}++;
> +			$stats{"line_reached"}++ if ($all_line{$source}{$where} != 0);
>   		}
>   	}
> -	if ($line_count) {
> -		my $percent = 100. * $line_reached / $line_count;
> -		printf "  lines......: %.1f%% (%d of %d lines)\n",
> -			$percent, $line_reached, $line_count;
> -	} else {
> -		print "No line coverage data.\n";
> -	}
>   
> -	# Output per-function coverage statistics
> -	my $func_count = 0;
> -	my $func_used = 0;
> +	# per-function coverage statistics
> +	$stats{"func_count"} = 0;
> +	$stats{"func_used"} = 0;
>   
>   	foreach my $func (keys(%all_func)) {
>   		foreach my $file (keys(%{$all_func{$func}})) {
> -			$func_count++;
> +			$stats{"func_count"}++;
>   			if ($used_func{$func}) {
>   				if ($used_func{$func}->{$file}) {
> -					$func_used++;
> +					$stats{"func_used"}++;
>   				}
>   			}
>   		}
>   	}
>   
> -	if ($func_count) {
> -		my $percent = 100. * $func_used / $func_count;
> +	# per-branch coverage statistics
> +	$stats{"branch_count"} = 0;
> +	$stats{"branch_reached"} = 0;
> +
> +	foreach my $source (keys(%all_branch)) {
> +		next if (!$used_source{$source});
> +
> +		foreach my $where (keys(%{$all_branch{$source}})) {
> +			$stats{"branch_count"}++;
> +			$stats{"branch_reached"}++ if ($all_branch{$source}{$where} != 0);
> +		}
> +	}
> +
> +	# per-file coverage stats
> +	$stats{"all_files"} = scalar keys(%files);
> +	$stats{"filtered_files"} = scalar keys(%record);
> +	$stats{"used_files"} = scalar keys(%used_source);
> +}
> +
> +sub print_summary()
> +{
> +	if ($stats{"line_count"}) {
> +		my $percent = 100. * $stats{"line_reached"} / $stats{"line_count"};
> +		printf "  lines......: %.1f%% (%d of %d lines)\n",
> +			$percent, $stats{"line_reached"}, $stats{"line_count"};
> +	} else {
> +		print "No line coverage data.\n";
> +	}
> +
> +	if ($stats{"func_count"}) {
> +		my $percent = 100. * $stats{"func_used"} / $stats{"func_count"};
>   		printf "  functions..: %.1f%% (%d of %d functions)\n",
> -			$percent, $func_used, $func_count;
> +			$percent, $stats{"func_used"}, $stats{"func_count"};
>   	} else {
>   		print "No functions reported. Wrong filters?\n";
>   		return;
>   	}
>   
> -	# Output per-branch coverage statistics
> -	my $branch_count = 0;
> -	my $branch_reached = 0;
> +	if ($stats{"branch_count"}) {
> +		my $percent = 100. * $stats{"branch_reached"} / $stats{"branch_count"};
> +		printf "  branches...: %.1f%% (%d of %d branches)\n",
> +			$percent, $stats{"branch_reached"}, $stats{"branch_count"};
> +	} else {
> +		print "No branch coverage data.\n";
> +	}
> +}
>   
> -	foreach my $source (keys(%all_branch)) {
> -		next if (!$used_source{$source});
> +my $gen_report;
> +my $css_file;
> +my $html_prolog;
> +my $html_epilog;
> +my %report;
>   
> -		foreach my $where (keys(%{$all_branch{$source}})) {
> -			$branch_count++;
> -			$branch_reached++ if ($all_branch{$source}{$where} != 0);
> +sub generate_report()
> +{
> +	my $percent;
> +	my $prolog = "";
> +	my $epilog = "";
> +	my @info_files = sort(keys %report);
> +
> +	$title = "Code coverage results" if ($title eq "");
> +
> +	if ($html_prolog) {
> +		open IN, $html_prolog or die "Can't open prolog file";
> +		$prolog .= $_ while (<IN>);
> +		close IN;
> +	}
> +
> +	if ($html_prolog) {

$html_epilog



> +		open IN, $html_epilog or die "Can't open epilog file";
> +		$epilog .= $_ while (<IN>);
> +		close IN;
> +	}
> +
> +	# Re-generate the hashes used to report stats in order to procuce the
> +	# Total results
> +
> +	%used_func = ();
> +	%all_func = ();
> +	%all_branch = ();
> +	%all_line = ();
> +	%used_source = ();
> +	%files = ();
> +	%test_names = ();
> +
> +	foreach my $f (@info_files) {
> +		foreach my $source (keys(%{$report{$f}{"all_line"}})) {
> +			$used_source{$source} = 1 if ($report{$f}{"used_source"});
> +			foreach my $where (keys(%{$report{$f}{"all_line"}{$source}})) {
> +				$all_line{$source}{$where} += $report{$f}{"all_line"}{$source}{$where};
> +			}
> +		}
> +		foreach my $func (keys(%{$report{$f}{"all_func"}})) {
> +			foreach my $file (keys(%{$report{$f}{"all_func"}{$func}})) {
> +				$all_func{$func}{$file}->{ln} = $report{$f}{"all_func"}{$func}{$file}->{ln};
> +				$used_func{$func}->{$file} = 1 if ($report{$f}{"used_func"}{$func}->{$file});
> +			}
> +		}
> +		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};
> +			}
> +		}
> +		for my $source(keys(%{$report{$f}{"files"}})) {
> +			$files{$source} = 1;
> +			$used_source{$source} = 1 if ($report{$f}{"used_source"}{$source});
> +		}
> +		for my $test(keys(%{$report{$f}{"test_names"}})) {
> +			$test_names{$test} = 1;
>   		}
>   	}
> -	if ($branch_count) {
> -		my $percent = 100. * $branch_reached / $branch_count;
> -		printf "  branches...: %.1f%% (%d of %d branches)\n",
> -			$percent, $branch_reached, $branch_count;
> +	gen_stats();
> +
> +	# Colors for the html output
> +
> +	my $red    = "style=\"background-color:#ffb3b3\"";
> +	my $yellow = "style=\"background-color:#ffffb3\"";
> +	my $green  = "style=\"background-color:#d9ffd9\"";
> +
> +	# Open report file
> +
> +	open OUT, ">$gen_report" or die "Can't open $gen_report";
> +
> +	print OUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
> +
> +	print OUT "<html lang=\"en\">\n\n";
> +	print OUT "<head>\n";
> +	print OUT "  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n";
> +	print OUT "  <title>$title</title>\n";
> +	print OUT "  <link rel=\"stylesheet\" type=\"text/css\" href=\"$css_file\">\n" if ($css_file);
> +	print OUT "</head>\n\n<body>\n$prolog";
> +
> +	print OUT "  <h1>$title</h1>\n";
> +
> +	print OUT "  <h2>Summary</h2>\n";
> +	# Generates a table containing the code coverage statistics per input
> +
> +	print OUT "<table width=\"100%\" border=1 cellspacing=0 cellpadding=0>\n  <tr>\n";
> +	print OUT "    <th></th>\n";
> +	foreach my $f (@info_files) {
> +		print OUT "    <th>$f</th>\n";
> +	}
> +	print OUT "    <th>TOTAL</th>\n";
> +	print OUT "    <th>Total count</th>\n";
> +	print OUT "  </tr><tr>\n";
> +
> +	print OUT "    <td><b>Functions</b></td>\n";
> +	foreach my $f (@info_files) {
> +		my %st = %{$report{$f}{"stats"}};
> +		if ($st{"func_count"}) {
> +			$percent = 100. * $st{"func_used"} / $st{"func_count"};
> +
> +			printf OUT "    <td>%.1f%%</td>\n", $percent;
> +		} else {
> +			print OUT "    <td>N. A.</td>\n";
> +		}
> +	}
> +	if ($stats{"func_count"}) {
> +		$percent = 100. * $stats{"func_used"} / $stats{"func_count"};
> +
> +		printf OUT "    <td>%.1f%%</td>\n", $percent;
>   	} else {
> -		print "No branch coverage data.\n";
> +		print OUT "    <td>N. A.</td>\n";
> +	}
> +	print OUT "  <td>" . $stats{"func_count"} . "</td>";
> +	print OUT "  </tr><tr>\n";
> +
> +	print OUT "    <td><b>Branches</b></td>\n";
> +	foreach my $f (@info_files) {
> +		my %st = %{$report{$f}{"stats"}};
> +		if ($st{"branch_count"}) {
> +			$percent = 100. * $st{"branch_reached"} / $st{"branch_count"};
> +
> +			printf OUT "    <td>%.1f%%</td>\n", $percent;
> +		} else {
> +			print OUT "    <td>N. A.</td>\n";
> +		}
> +	}
> +	if ($stats{"branch_count"}) {
> +		$percent = 100. * $stats{"branch_reached"} / $stats{"branch_count"};
> +
> +		printf OUT "    <td>%.1f%%</td>\n", $percent;
> +	} else {
> +		print OUT "    <td>N. A.</td>\n";
> +	}
> +	print OUT "  <td>" . $stats{"branch_count"} . "</td>";
> +	print OUT "  </tr><tr>\n";
> +
> +	print OUT "    <td><b>Lines</b></td>\n";
> +	foreach my $f (@info_files) {
> +		my %st = %{$report{$f}{"stats"}};
> +
> +		if ($st{"line_count"}) {
> +			$percent = 100. * $st{"line_reached"} / $st{"line_count"};
> +
> +			printf OUT "    <td>%.1f%%</td>\n", $percent;
> +		} else {
> +			print OUT "    <td>N. A.</td>\n";
> +		}
> +	}
> +	if ($stats{"line_count"}) {
> +		$percent = 100. * $stats{"line_reached"} / $stats{"line_count"};
> +
> +		printf OUT "    <td>%.1f%%</td>\n", $percent;
> +	} else {
> +		print OUT "    <td>N. A.</td>\n";
> +	}
> +	print OUT "  <td>" . $stats{"line_count"} . "</td>";
> +
> +	# If there are more than one tests per file, report them
> +	my $total = scalar(keys %test_names);
> +	if ($total > 1) {
> +		print OUT "  </tr><tr>\n";
> +		print OUT "    <td><b>Number of tests</b></td>\n";
> +		foreach my $f (@info_files) {
> +			my $count = scalar(keys %{$report{$f}{"test_names"}});
> +
> +			if ($count == 0) {
> +				print OUT "    <td $red>$count</td>\n";
> +			} elsif ($count < $total) {
> +				print OUT "    <td $yellow>$count</td>\n";
> +			} else {
> +				print OUT "    <td $green>$count</td>\n";
> +			}
> +		}
> +		print OUT "    <td $green\>$total</td>\n";
> +
> +	}
> +	print OUT "  </tr>\n</table><p/>\n\n";
> +
> +	if ($total > 1) {
> +		print OUT "<h2>Tests coverage</h2>\n";
> +
> +		print OUT "<table width=\"100%\" border=1 cellspacing=0 cellpadding=0>\n  <tr>\n";
> +		print OUT "    <th>Test name</th>\n";
> +		foreach my $f (@info_files) {
> +			print OUT "    <th>$f</th>\n";
> +		}
> +
> +		foreach my $t (sort keys(%test_names)) {
> +			print OUT "  </tr><tr>\n";
> +			printf OUT "    <td>%s</td>\n", $t;
> +			foreach my $f (@info_files) {
> +				if (%{$report{$f}{"test_names"}}{$t}) {
> +					print OUT "    <td $green>YES</td>\n";
> +				} else {
> +					print OUT "    <td $red>NO</td>\n";
> +				}
> +			}
> +		}
> +		print OUT "</tr></table>\n";
>   	}
> +
> +
> +	# Generates a table containing per-function detailed data
> +
> +	print OUT "<h2>Functions coverage</h2>\n";
> +	print OUT "<table width=\"100%\" border=1 cellspacing=0 cellpadding=0>\n  <tr>\n";
> +	print OUT "    <th>Function</th>\n";
> +	print OUT "    <th>Used?</th>\n";
> +	foreach my $f (@info_files) {
> +		print OUT "    <th>$f</th>\n";
> +	}
> +	print OUT "    <th>File</th>\n";
> +
> +	foreach my $func (sort keys(%all_func)) {
> +		my @keys = sort keys(%{$all_func{$func}});
> +		foreach my $file (@keys) {
> +			print OUT "  </tr><tr>\n";
> +			print OUT "    <td>$func</td>\n";
> +			if ($used_func{$func}->{$file}) {
> +				print OUT "    <td $green>YES</td>\n";
> +			} else {
> +				print OUT "    <td $red>NO</td>\n";
> +			}
> +			foreach my $f (@info_files) {
> +				if ($report{$f}{"used_func"}{$func}->{$file}) {
> +					print OUT "    <td $green>YES</td>\n";
> +				} else {
> +					print OUT "    <td $red>NO</td>\n";
> +				}
> +			}
> +			$file =~ s,$prefix,linux/,;
> +			print OUT "    <td>$file</td>\n";
> +		}
> +	}
> +	print OUT "</tr></table>\n";
> +
> +	print OUT "$epilog</body>\n";
> +
> +	# Close the file and exit
> +
> +	close OUT;
>   }
>   
>   #
> @@ -515,6 +781,11 @@ GetOptions(
>   	"exclude-source=s" => \$src_exclude,
>   	"show-files|show_files" => \$show_files,
>   	"show-lines|show_lines" => \$show_lines,
> +	"report|r=s" => \$gen_report,
> +	"css-file|css|c=s" => \$css_file,
> +	"title|t=s" => \$title,
> +	"html-prolog|prolog=s" => \$html_prolog,
> +	"html-epilog|epilog=s" => \$html_epilog,
>   	"help" => \$help,
>   	"man" => \$man,
>   ) or pod2usage(2);
> @@ -528,7 +799,9 @@ if ($#ARGV < 0) {
>   }
>   
>   # At least one action should be specified
> -pod2usage(1) if (!$print_used && !$filter && !$stat && !$print_unused);
> +pod2usage(1) if (!$print_used && !$filter && !$stat && !$print_unused && !$gen_report);
> +
> +pod2usage(1) if ($gen_report && ($print_used || $filter || $stat || $print_unused));
>   
>   my $filter_str = "";
>   my $has_filter;
> @@ -617,39 +890,67 @@ if ($ignore_unused) {
>   
>   foreach my $f (@ARGV) {
>   	parse_info_data($f);
> +
> +	if ($gen_report) {
> +		$f =~ s,.*/,,;
> +		$f =~ s/\.info$//;
> +
> +		gen_stats();
> +
> +		$report{$f}{"stats"} = { %stats };
> +		$report{$f}{"all_func"} = { %all_func };
> +		$report{$f}{"used_func"} = { %used_func };
> +		$report{$f}{"all_branch"} = { %all_branch };
> +		$report{$f}{"all_line"} = { %all_line };
> +		$report{$f}{"used_source"} = { %used_source };
> +		$report{$f}{"files"} = { %files };
> +		$report{$f}{"test_names"} = { %test_names };
> +
> +		%used_func = ();
> +		%all_func = ();
> +		%all_branch = ();
> +		%all_line = ();
> +		%used_source = ();
> +		%files = ();
> +		%test_names = ();
> +	}
>   }
>   
> -print_code_coverage($print_used, $print_unused, $show_lines);
> +if ($gen_report) {
> +	generate_report();
> +	exit 0;
> +}
>   
> -print_summary() if ($stat);
> +gen_stats();
>   
> -my $all_files = scalar keys(%files);
> +die "Nothing counted. Wrong input files?" if (!$stats{"all_files"});
>   
> -die "Nothing counted. Wrong input files?" if (!$all_files);
> +print_code_coverage($print_used, $print_unused, $show_lines);
>   
> -if ($has_filter) {
> -	my $all_files = scalar keys(%files);
> -	my $filtered_files = scalar keys(%record);
> -	my $used_files = scalar keys(%used_source);
> +print_summary() if ($stat);
>   
> -	my $percent = 100. * $used_files / $all_files;
> +if ($has_filter) {
> +	my $percent = 100. * $stats{"used_files"} / $stats{"all_files"};
>   
>   	$filter_str =~ s/(.*),/$1 and/;
>   	printf "Ignored......:%s.\n", $filter_str;
>   	printf "Source files.: %.2f%% (%d of %d total)",
> -		$percent, $used_files, $all_files;
> +		$percent, $stats{"used_files"}, $stats{"all_files"};
>   
> -	if ($used_files != $filtered_files) {
> -		my $percent_filtered = 100. * $used_files / $filtered_files;
> +	if ($stats{"used_files"} != $stats{"filtered_files"}) {
> +		my $percent_filtered = 100. * $stats{"used_files"} / $stats{"filtered_files"};
>   
>   		printf ", %.2f%% (%d of %d filtered)",
> -			$percent_filtered, $used_files, $filtered_files;
> +			$percent_filtered, $stats{"used_files"}, $stats{"filtered_files"};
>   	}
>   	print "\n";
>   } else {
>   	printf "Source files: %d\n", scalar keys(%files) if($stat);
>   }
>   
> +my $ntests=scalar(%test_names);
> +printf "Number of tests: %d\n", $ntests if ($ntests > 1);
> +
>   if ($show_files) {
>   	for my $f(sort keys %used_source) {
>   		print "\t$f\n";
> @@ -670,8 +971,10 @@ Parses lcov data from .info files.
>   
>   code_cov_parse_info <options> [input file(s)]
>   
> -At least one of the options B<--stat>, B<--print> and/or B<--output>
> -should be used.
> +At least one of the output options should be used, e g.
> +B<--stat>, B<--print>, B<--print-unused>, B<--report> and/or B<--output>.
> +
> +Also, B<--report> can't be used together with other output options.
>   
>   =head1 OPTIONS
>   
> @@ -700,6 +1003,37 @@ Prints the functions that were never reached.
>   
>   The function coverage report is affected by the applied filters.
>   
> +=item B<--report>  B<[output file]> or B<-r>  B<[output file]>
> +
> +Generates an html report containing per-test and total statistics.
> +
> +The function coverage report is affected by the applied filters.
> +
> +=item B<--css-file> B<[css file]> or B<--css> B<[css file]> or B<-c> B<[css file]
> +
> +Adds an optional css file to the html report.
> +Used only with B<--report>.
> +
> +=item B<--title> B<[title] or B<-t> B<[title]
> +
> +If used with B<--report>, it defines the title for the for the html report.
> +
> +If used with B<--output>, it replaces the test names with the title. This
> +is useful when merging reports from multiple tests into a summarized file.
> +If not used, the B<[output file]> will contain all test names on its
> +beginning.
> +
> +Used with B<--report> AND B<--output>.
> +
> +=item B<--html-prolog> B<[html file] or B<--prolog> B<[html file]
> +
> +Adds a prolog at the beginning of the body of the html report.
> +Used only with B<--report>.
> +
> +=item B<--html-epilog>  B<[html file] or B<--epilog>  B<[html file]
> +
> +Adds an epilog before the end of the body of the html report.
> +Used only with B<--report>.

Lots of code, I have always mixed feelings about code generating html. 
It's maintainability is quite difficult.
I am not UI/presentation expert, so it is hard to decide, but if there 
are no better options (XML/XSLT, perl frameworks to generate reports, 
....), lets try it.

Regards
Andrzej


>   
>   =item B<--show-lines> or B<--show_lines>
>   

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [igt-dev] [PATCH 12/12] code_cov_parse_info: add support for generating html reports
  2022-04-11 13:15   ` Andrzej Hajda
@ 2022-04-11 15:49     ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-11 15:49 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: igt-dev, Ch Sai Gowtham, Petri Latvala

On Mon, 11 Apr 2022 15:15:19 +0200
Andrzej Hajda <andrzej.hajda@intel.com> wrote:

> > +sub generate_report()
> > +{
> > +	my $percent;
> > +	my $prolog = "";
> > +	my $epilog = "";
> > +	my @info_files = sort(keys %report);
> > +
> > +	$title = "Code coverage results" if ($title eq "");
> > +
> > +	if ($html_prolog) {
> > +		open IN, $html_prolog or die "Can't open prolog file";
> > +		$prolog .= $_ while (<IN>);
> > +		close IN;
> > +	}
> > +
> > +	if ($html_prolog) {  
> 
> $html_epilog

OK.

> Lots of code, I have always mixed feelings about code generating html. 
> It's maintainability is quite difficult.
> I am not UI/presentation expert, so it is hard to decide, but if there 
> are no better options (XML/XSLT, perl frameworks to generate reports, 
> ....), lets try it.

I would prefer to avoid using perl frameworks, as this makes harder
to package it, as it would add more dependencies to igt. Also, not
sure if this would actually affect the size, as we still need to store
the results per .info input file, and have the loops there to calculate
percentages.

As the idea here is not to have anything fancier than tables, I
guess the current code won't require too much maintenance. I could 
have opted to output ReST directly (or github-flavored markdown), 
or something as complex as docbook or LaTeX, but html seems more portable
and converting from html to either ReST (or any other markup language) is
as easy as calling pandoc.

Btw, using a text markup language would lose colors, as most markdown
languages don't allow changing font colors (I guess the only text markup
that supports color is asciidoc - which is a little bit odd). The colors
are helpful when trying to analyze the resulting data.

Also, I'm imagining a scenario in the future where CI would place the
output of those reports on some web page. so, producing html sounded
an interesting choice.

Regards,
Mauro

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [igt-dev] [PATCH 11/12] code_cov_parse_info: add support for exclude filters
  2022-04-11 12:28   ` Andrzej Hajda
@ 2022-04-12  9:16     ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 20+ messages in thread
From: Mauro Carvalho Chehab @ 2022-04-12  9:16 UTC (permalink / raw)
  To: Andrzej Hajda; +Cc: igt-dev, Ch Sai Gowtham, Petri Latvala

On Mon, 11 Apr 2022 14:28:24 +0200
Andrzej Hajda <andrzej.hajda@intel.com> wrote:


> > +When used with B<--exclude-source>, exclusions take precedence.
> > +
> > +=item B<--exclude-source> B<[filter's file]>
> > +
> > +Exclude all files that match the regular expressions contained
> > +at the B<[filter's file]>.
> > +
> > +When used with B<--source-filters>, exclusions take precedence.
> > +  
> 
> Now there are two filtering options per functions and two per files.
> Wouldn't be better to squash include/exclude rules into one param in 
> 'rsync like' patterns:
> + include_pattern
> - exclude_pattern
> ...
> This way it would be easier to manage filters - less options, less 
> helper files.

Added support for it. I also preserved the old behavior of placing
just an include pattern without a "+", in order to be backward-compatible.

> One could think about squashing also filters for function and files, but 
> this I am not sure.

I see your point. Yet, I opted to have a single file. Btw,
depending on the file contents, the same filter file can be used as
both source and function filters. For instance, a filter file like:

	+i915
	+gem
	-display
	-selftest

can either be used for functions or sources (or both).

Regards,
Mauro

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2022-04-12  9:17 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-04  6:26 [igt-dev] [PATCH 00/12] code coverage: some improvements Mauro Carvalho Chehab
2022-04-04  6:26 ` [igt-dev] [PATCH 01/12] scripts/code_cov*: remove the extensions from them Mauro Carvalho Chehab
2022-04-04  6:26 ` [igt-dev] [PATCH 02/12] scripts/code_cov_parse_info: add a tool to parse code coverage info files Mauro Carvalho Chehab
2022-04-04  6:26 ` [igt-dev] [PATCH 03/12] scripts/code_cov_gen_report: add support for filtering " Mauro Carvalho Chehab
2022-04-04  6:26 ` [igt-dev] [PATCH 04/12] runner: execute code coverage script also from PATH Mauro Carvalho Chehab
2022-04-04  6:26 ` [igt-dev] [PATCH 05/12] scripts/meson.build: install code coverage scripts Mauro Carvalho Chehab
2022-04-04  6:26 ` [igt-dev] [PATCH 06/12] scripts/code_cov_selftest.sh: test if IGT code coverage is working Mauro Carvalho Chehab
2022-04-04  6:26 ` [igt-dev] [PATCH 07/12] docs/code_coverage.md: document the code coverage filter script Mauro Carvalho Chehab
2022-04-04  6:26 ` [igt-dev] [PATCH 08/12] scripts/code_cov_parse_info: better handle test name Mauro Carvalho Chehab
2022-04-04  6:26 ` [igt-dev] [PATCH 09/12] code_cov_parse_info: fix error handling when opening files Mauro Carvalho Chehab
2022-04-04  6:26 ` [igt-dev] [PATCH 10/12] code_cov_parse_info: fix --show-lines logic Mauro Carvalho Chehab
2022-04-11 11:57   ` Andrzej Hajda
2022-04-04  6:26 ` [igt-dev] [PATCH 11/12] code_cov_parse_info: add support for exclude filters Mauro Carvalho Chehab
2022-04-11 12:28   ` Andrzej Hajda
2022-04-12  9:16     ` Mauro Carvalho Chehab
2022-04-04  6:26 ` [igt-dev] [PATCH 12/12] code_cov_parse_info: add support for generating html reports Mauro Carvalho Chehab
2022-04-11 13:15   ` Andrzej Hajda
2022-04-11 15:49     ` Mauro Carvalho Chehab
2022-04-04  7:45 ` [igt-dev] ✓ Fi.CI.BAT: success for code coverage: some improvements Patchwork
2022-04-04  9:11 ` [igt-dev] ✗ Fi.CI.IGT: failure " Patchwork

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.