diff mbox series

[2/2] check: generate gcov code coverage reports at the end of each section

Message ID 168972906191.1698606.10738894314642211560.stgit@frogsfrogsfrogs (mailing list archive)
State New, archived
Headers show
Series fstests: testing improvements | expand

Commit Message

Darrick J. Wong July 19, 2023, 1:11 a.m. UTC
From: Darrick J. Wong <djwong@kernel.org>

Support collecting kernel code coverage information as reported in
debugfs.  At the start of each section, we reset the gcov counters;
during the section wrapup, we'll collect the kernel gcov data.

If lcov is installed and the kernel source code is available, it will
also generate a nice html report.  If a CLI web browser is available, it
will also format the html report into text for easy grepping.

This requires the test runner to set REPORT_GCOV=1 explicitly and gcov
to be enabled in the kernel.

Cc: tytso@mit.edu
Cc: kent.overstreet@linux.dev
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
 README |    3 ++
 check  |   86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 89 insertions(+)

Comments

Zorro Lang July 19, 2023, 4:19 p.m. UTC | #1
On Tue, Jul 18, 2023 at 06:11:01PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
> 
> Support collecting kernel code coverage information as reported in
> debugfs.  At the start of each section, we reset the gcov counters;
> during the section wrapup, we'll collect the kernel gcov data.
> 
> If lcov is installed and the kernel source code is available, it will
> also generate a nice html report.  If a CLI web browser is available, it
> will also format the html report into text for easy grepping.
> 
> This requires the test runner to set REPORT_GCOV=1 explicitly and gcov
> to be enabled in the kernel.
> 
> Cc: tytso@mit.edu
> Cc: kent.overstreet@linux.dev
> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> ---

Hi Darrick,

Is that possible to split this function from check script to tools/ ?

Thanks,
Zorro

>  README |    3 ++
>  check  |   86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 89 insertions(+)
> 
> 
> diff --git a/README b/README
> index 9790334db1..ccfdcbe703 100644
> --- a/README
> +++ b/README
> @@ -249,6 +249,9 @@ Kernel/Modules related configuration:
>     to "forever" and we'll wait forever until the module is gone.
>   - Set KCONFIG_PATH to specify your preferred location of kernel config
>     file. The config is used by tests to check if kernel feature is enabled.
> + - Set REPORT_GCOV to a directory path to make lcov and genhtml generate
> +   html reports from any gcov code coverage data collected by the kernel.
> +   If REPORT_GCOV is set to 1, the report will be written to $REPORT_DIR/gcov/.
>  
>  Test control:
>   - Set LOAD_FACTOR to a nonzero positive integer to increase the amount of
> diff --git a/check b/check
> index 97c7c4c7d1..3e6f27c653 100755
> --- a/check
> +++ b/check
> @@ -451,6 +451,87 @@ _global_log() {
>  	fi
>  }
>  
> +GCOV_DIR=/sys/kernel/debug/gcov
> +
> +# Find the topmost directories of the .gcno directory hierarchy
> +_gcov_find_topdirs() {
> +	find "${GCOV_DIR}/" -name '*.gcno' -printf '%d|%h\n' | \
> +		sort -g -k 1 | \
> +		uniq | \
> +		$AWK_PROG -F '|' 'BEGIN { x = -1 } { if (x < 0) x = $1; if ($1 == x) printf("%s\n", $2);}'
> +}
> +
> +# Generate lcov html report from kernel gcov data if configured
> +_gcov_generate_report() {
> +	unset REPORT_GCOV	# don't trigger multiple times if ^C
> +
> +	local output_dir="$1"
> +	test -n "${output_dir}" || return
> +	test "$output_dir" = "1" && output_dir="$REPORT_DIR/gcov"
> +
> +	# Kernel support built in?
> +	test -d "$GCOV_DIR" || return
> +
> +	readarray -t gcno_dirs < <(_gcov_find_topdirs)
> +	test "${#gcno_dirs[@]}" -gt 0 || return
> +
> +	mkdir -p "${output_dir}/"
> +
> +	# Collect raw coverage data from the kernel
> +	readarray -t source_dirs < <(find "${GCOV_DIR}/" -mindepth 1 -maxdepth 1 -type d)
> +	for dir in "${source_dirs[@]}"; do
> +		cp -p -R -d -u "${dir}" "${output_dir}/"
> +	done
> +
> +	# If lcov is installed, use it to summarize the gcda data.
> +	# If it is not installed, there's no point in going forward
> +	command -v lcov > /dev/null || return
> +	local lcov=(lcov --exclude 'include*' --capture)
> +	lcov+=(--output-file "${output_dir}/gcov.report")
> +	for d in "${gcno_dirs[@]}"; do
> +		lcov+=(--directory "${d}")
> +	done
> +
> +	# Generate a detailed HTML report from the summary
> +	local gcov_start_time="$(date --date="${fstests_start_time:-now}")"
> +	local genhtml=()
> +	if command -v genhtml > /dev/null; then
> +		genhtml+=(genhtml -o "${output_dir}/" "${output_dir}/gcov.report")
> +		genhtml+=(--title "fstests on $(hostname -s) @ ${gcov_start_time}" --legend)
> +	fi
> +
> +	# Try to convert the HTML report summary as text for easier grepping if
> +	# there's an HTML renderer present
> +	local totext=()
> +	test "${#totext[@]}" -eq 0 && \
> +		command -v lynx &>/dev/null && \
> +		totext=(lynx -dump "${output_dir}/index.html" -width 120 -nonumbers -nolist)
> +	test "${#totext[@]}" -eq 0 && \
> +		command -v links &>/dev/null && \
> +		totext=(links -dump "${output_dir}/index.html" -width 120)
> +	test "${#totext[@]}" -eq 0 && \
> +		command -v elinks &>/dev/null && \
> +		totext=(elinks -dump "${output_dir}/index.html" --dump-width 120 --no-numbering --no-references)
> +
> +	# Analyze kernel data
> +	"${lcov[@]}" > "${output_dir}/gcov.stdout" 2> "${output_dir}/gcov.stderr"
> +	test "${#genhtml[@]}" -ne 0 && \
> +		"${genhtml[@]}" >> "${output_dir}/gcov.stdout" 2>> "${output_dir}/gcov.stderr"
> +	test "${#totext[@]}" -ne 0 && \
> +		"${totext[@]}" > "${output_dir}/index.txt" 2>> "${output_dir}/gcov.stderr"
> +}
> +
> +# Reset gcov usage data
> +_gcov_reset() {
> +	test -n "${REPORT_GCOV}" || return
> +
> +	if [ -w "${GCOV_DIR}/reset" ]; then
> +		echo 1 > "${GCOV_DIR}/reset"
> +	else
> +		unset REPORT_GCOV
> +	fi
> +}
> +
>  _wrapup()
>  {
>  	seq="check"
> @@ -527,6 +608,10 @@ _wrapup()
>  					     "${#bad[*]}" "${#notrun[*]}" \
>  					     "$((sect_stop - sect_start))"
>  		fi
> +
> +		# Generate code coverage report
> +		test -n "$REPORT_GCOV" && _gcov_generate_report "$REPORT_GCOV"
> +
>  		needwrap=false
>  	fi
>  
> @@ -801,6 +886,7 @@ function run_section()
>  	  echo "MOUNT_OPTIONS -- `_scratch_mount_options`"
>  	fi
>  	echo
> +	_gcov_reset
>  	needwrap=true
>  
>  	if [ ! -z "$SCRATCH_DEV" ]; then
>
Darrick J. Wong July 20, 2023, 2:29 a.m. UTC | #2
On Thu, Jul 20, 2023 at 12:19:16AM +0800, Zorro Lang wrote:
> On Tue, Jul 18, 2023 at 06:11:01PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <djwong@kernel.org>
> > 
> > Support collecting kernel code coverage information as reported in
> > debugfs.  At the start of each section, we reset the gcov counters;
> > during the section wrapup, we'll collect the kernel gcov data.
> > 
> > If lcov is installed and the kernel source code is available, it will
> > also generate a nice html report.  If a CLI web browser is available, it
> > will also format the html report into text for easy grepping.
> > 
> > This requires the test runner to set REPORT_GCOV=1 explicitly and gcov
> > to be enabled in the kernel.
> > 
> > Cc: tytso@mit.edu
> > Cc: kent.overstreet@linux.dev
> > Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> > ---
> 
> Hi Darrick,
> 
> Is that possible to split this function from check script to tools/ ?

I don't mind separating it, though I don't see much reason to.  Are you
concerned about ./check growing larger?

--D

> Thanks,
> Zorro
> 
> >  README |    3 ++
> >  check  |   86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 89 insertions(+)
> > 
> > 
> > diff --git a/README b/README
> > index 9790334db1..ccfdcbe703 100644
> > --- a/README
> > +++ b/README
> > @@ -249,6 +249,9 @@ Kernel/Modules related configuration:
> >     to "forever" and we'll wait forever until the module is gone.
> >   - Set KCONFIG_PATH to specify your preferred location of kernel config
> >     file. The config is used by tests to check if kernel feature is enabled.
> > + - Set REPORT_GCOV to a directory path to make lcov and genhtml generate
> > +   html reports from any gcov code coverage data collected by the kernel.
> > +   If REPORT_GCOV is set to 1, the report will be written to $REPORT_DIR/gcov/.
> >  
> >  Test control:
> >   - Set LOAD_FACTOR to a nonzero positive integer to increase the amount of
> > diff --git a/check b/check
> > index 97c7c4c7d1..3e6f27c653 100755
> > --- a/check
> > +++ b/check
> > @@ -451,6 +451,87 @@ _global_log() {
> >  	fi
> >  }
> >  
> > +GCOV_DIR=/sys/kernel/debug/gcov
> > +
> > +# Find the topmost directories of the .gcno directory hierarchy
> > +_gcov_find_topdirs() {
> > +	find "${GCOV_DIR}/" -name '*.gcno' -printf '%d|%h\n' | \
> > +		sort -g -k 1 | \
> > +		uniq | \
> > +		$AWK_PROG -F '|' 'BEGIN { x = -1 } { if (x < 0) x = $1; if ($1 == x) printf("%s\n", $2);}'
> > +}
> > +
> > +# Generate lcov html report from kernel gcov data if configured
> > +_gcov_generate_report() {
> > +	unset REPORT_GCOV	# don't trigger multiple times if ^C
> > +
> > +	local output_dir="$1"
> > +	test -n "${output_dir}" || return
> > +	test "$output_dir" = "1" && output_dir="$REPORT_DIR/gcov"
> > +
> > +	# Kernel support built in?
> > +	test -d "$GCOV_DIR" || return
> > +
> > +	readarray -t gcno_dirs < <(_gcov_find_topdirs)
> > +	test "${#gcno_dirs[@]}" -gt 0 || return
> > +
> > +	mkdir -p "${output_dir}/"
> > +
> > +	# Collect raw coverage data from the kernel
> > +	readarray -t source_dirs < <(find "${GCOV_DIR}/" -mindepth 1 -maxdepth 1 -type d)
> > +	for dir in "${source_dirs[@]}"; do
> > +		cp -p -R -d -u "${dir}" "${output_dir}/"
> > +	done
> > +
> > +	# If lcov is installed, use it to summarize the gcda data.
> > +	# If it is not installed, there's no point in going forward
> > +	command -v lcov > /dev/null || return
> > +	local lcov=(lcov --exclude 'include*' --capture)
> > +	lcov+=(--output-file "${output_dir}/gcov.report")
> > +	for d in "${gcno_dirs[@]}"; do
> > +		lcov+=(--directory "${d}")
> > +	done
> > +
> > +	# Generate a detailed HTML report from the summary
> > +	local gcov_start_time="$(date --date="${fstests_start_time:-now}")"
> > +	local genhtml=()
> > +	if command -v genhtml > /dev/null; then
> > +		genhtml+=(genhtml -o "${output_dir}/" "${output_dir}/gcov.report")
> > +		genhtml+=(--title "fstests on $(hostname -s) @ ${gcov_start_time}" --legend)
> > +	fi
> > +
> > +	# Try to convert the HTML report summary as text for easier grepping if
> > +	# there's an HTML renderer present
> > +	local totext=()
> > +	test "${#totext[@]}" -eq 0 && \
> > +		command -v lynx &>/dev/null && \
> > +		totext=(lynx -dump "${output_dir}/index.html" -width 120 -nonumbers -nolist)
> > +	test "${#totext[@]}" -eq 0 && \
> > +		command -v links &>/dev/null && \
> > +		totext=(links -dump "${output_dir}/index.html" -width 120)
> > +	test "${#totext[@]}" -eq 0 && \
> > +		command -v elinks &>/dev/null && \
> > +		totext=(elinks -dump "${output_dir}/index.html" --dump-width 120 --no-numbering --no-references)
> > +
> > +	# Analyze kernel data
> > +	"${lcov[@]}" > "${output_dir}/gcov.stdout" 2> "${output_dir}/gcov.stderr"
> > +	test "${#genhtml[@]}" -ne 0 && \
> > +		"${genhtml[@]}" >> "${output_dir}/gcov.stdout" 2>> "${output_dir}/gcov.stderr"
> > +	test "${#totext[@]}" -ne 0 && \
> > +		"${totext[@]}" > "${output_dir}/index.txt" 2>> "${output_dir}/gcov.stderr"
> > +}
> > +
> > +# Reset gcov usage data
> > +_gcov_reset() {
> > +	test -n "${REPORT_GCOV}" || return
> > +
> > +	if [ -w "${GCOV_DIR}/reset" ]; then
> > +		echo 1 > "${GCOV_DIR}/reset"
> > +	else
> > +		unset REPORT_GCOV
> > +	fi
> > +}
> > +
> >  _wrapup()
> >  {
> >  	seq="check"
> > @@ -527,6 +608,10 @@ _wrapup()
> >  					     "${#bad[*]}" "${#notrun[*]}" \
> >  					     "$((sect_stop - sect_start))"
> >  		fi
> > +
> > +		# Generate code coverage report
> > +		test -n "$REPORT_GCOV" && _gcov_generate_report "$REPORT_GCOV"
> > +
> >  		needwrap=false
> >  	fi
> >  
> > @@ -801,6 +886,7 @@ function run_section()
> >  	  echo "MOUNT_OPTIONS -- `_scratch_mount_options`"
> >  	fi
> >  	echo
> > +	_gcov_reset
> >  	needwrap=true
> >  
> >  	if [ ! -z "$SCRATCH_DEV" ]; then
> > 
>
Zorro Lang July 20, 2023, 2:24 p.m. UTC | #3
On Wed, Jul 19, 2023 at 07:29:24PM -0700, Darrick J. Wong wrote:
> On Thu, Jul 20, 2023 at 12:19:16AM +0800, Zorro Lang wrote:
> > On Tue, Jul 18, 2023 at 06:11:01PM -0700, Darrick J. Wong wrote:
> > > From: Darrick J. Wong <djwong@kernel.org>
> > > 
> > > Support collecting kernel code coverage information as reported in
> > > debugfs.  At the start of each section, we reset the gcov counters;
> > > during the section wrapup, we'll collect the kernel gcov data.
> > > 
> > > If lcov is installed and the kernel source code is available, it will
> > > also generate a nice html report.  If a CLI web browser is available, it
> > > will also format the html report into text for easy grepping.
> > > 
> > > This requires the test runner to set REPORT_GCOV=1 explicitly and gcov
> > > to be enabled in the kernel.
> > > 
> > > Cc: tytso@mit.edu
> > > Cc: kent.overstreet@linux.dev
> > > Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> > > ---
> > 
> > Hi Darrick,
> > 
> > Is that possible to split this function from check script to tools/ ?
> 
> I don't mind separating it, though I don't see much reason to.  Are you
> concerned about ./check growing larger?

Yeah, a little bit, the check script become smore and more complicated.

> 
> --D
> 
> > Thanks,
> > Zorro
> > 
> > >  README |    3 ++
> > >  check  |   86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> > >  2 files changed, 89 insertions(+)
> > > 
> > > 
> > > diff --git a/README b/README
> > > index 9790334db1..ccfdcbe703 100644
> > > --- a/README
> > > +++ b/README
> > > @@ -249,6 +249,9 @@ Kernel/Modules related configuration:
> > >     to "forever" and we'll wait forever until the module is gone.
> > >   - Set KCONFIG_PATH to specify your preferred location of kernel config
> > >     file. The config is used by tests to check if kernel feature is enabled.
> > > + - Set REPORT_GCOV to a directory path to make lcov and genhtml generate
> > > +   html reports from any gcov code coverage data collected by the kernel.
> > > +   If REPORT_GCOV is set to 1, the report will be written to $REPORT_DIR/gcov/.
> > >  
> > >  Test control:
> > >   - Set LOAD_FACTOR to a nonzero positive integer to increase the amount of
> > > diff --git a/check b/check
> > > index 97c7c4c7d1..3e6f27c653 100755
> > > --- a/check
> > > +++ b/check
> > > @@ -451,6 +451,87 @@ _global_log() {
> > >  	fi
> > >  }
> > >  
> > > +GCOV_DIR=/sys/kernel/debug/gcov
> > > +
> > > +# Find the topmost directories of the .gcno directory hierarchy
> > > +_gcov_find_topdirs() {
> > > +	find "${GCOV_DIR}/" -name '*.gcno' -printf '%d|%h\n' | \
> > > +		sort -g -k 1 | \
> > > +		uniq | \
> > > +		$AWK_PROG -F '|' 'BEGIN { x = -1 } { if (x < 0) x = $1; if ($1 == x) printf("%s\n", $2);}'
> > > +}
> > > +
> > > +# Generate lcov html report from kernel gcov data if configured
> > > +_gcov_generate_report() {
> > > +	unset REPORT_GCOV	# don't trigger multiple times if ^C
> > > +
> > > +	local output_dir="$1"
> > > +	test -n "${output_dir}" || return
> > > +	test "$output_dir" = "1" && output_dir="$REPORT_DIR/gcov"
> > > +
> > > +	# Kernel support built in?
> > > +	test -d "$GCOV_DIR" || return
> > > +
> > > +	readarray -t gcno_dirs < <(_gcov_find_topdirs)
> > > +	test "${#gcno_dirs[@]}" -gt 0 || return
> > > +
> > > +	mkdir -p "${output_dir}/"
> > > +
> > > +	# Collect raw coverage data from the kernel
> > > +	readarray -t source_dirs < <(find "${GCOV_DIR}/" -mindepth 1 -maxdepth 1 -type d)
> > > +	for dir in "${source_dirs[@]}"; do
> > > +		cp -p -R -d -u "${dir}" "${output_dir}/"
> > > +	done
> > > +
> > > +	# If lcov is installed, use it to summarize the gcda data.
> > > +	# If it is not installed, there's no point in going forward
> > > +	command -v lcov > /dev/null || return
> > > +	local lcov=(lcov --exclude 'include*' --capture)
> > > +	lcov+=(--output-file "${output_dir}/gcov.report")
> > > +	for d in "${gcno_dirs[@]}"; do
> > > +		lcov+=(--directory "${d}")
> > > +	done
> > > +
> > > +	# Generate a detailed HTML report from the summary
> > > +	local gcov_start_time="$(date --date="${fstests_start_time:-now}")"
> > > +	local genhtml=()
> > > +	if command -v genhtml > /dev/null; then
> > > +		genhtml+=(genhtml -o "${output_dir}/" "${output_dir}/gcov.report")
> > > +		genhtml+=(--title "fstests on $(hostname -s) @ ${gcov_start_time}" --legend)
> > > +	fi
> > > +
> > > +	# Try to convert the HTML report summary as text for easier grepping if
> > > +	# there's an HTML renderer present
> > > +	local totext=()
> > > +	test "${#totext[@]}" -eq 0 && \
> > > +		command -v lynx &>/dev/null && \
> > > +		totext=(lynx -dump "${output_dir}/index.html" -width 120 -nonumbers -nolist)
> > > +	test "${#totext[@]}" -eq 0 && \
> > > +		command -v links &>/dev/null && \
> > > +		totext=(links -dump "${output_dir}/index.html" -width 120)
> > > +	test "${#totext[@]}" -eq 0 && \
> > > +		command -v elinks &>/dev/null && \
> > > +		totext=(elinks -dump "${output_dir}/index.html" --dump-width 120 --no-numbering --no-references)
> > > +
> > > +	# Analyze kernel data
> > > +	"${lcov[@]}" > "${output_dir}/gcov.stdout" 2> "${output_dir}/gcov.stderr"
> > > +	test "${#genhtml[@]}" -ne 0 && \
> > > +		"${genhtml[@]}" >> "${output_dir}/gcov.stdout" 2>> "${output_dir}/gcov.stderr"
> > > +	test "${#totext[@]}" -ne 0 && \
> > > +		"${totext[@]}" > "${output_dir}/index.txt" 2>> "${output_dir}/gcov.stderr"
> > > +}
> > > +
> > > +# Reset gcov usage data
> > > +_gcov_reset() {
> > > +	test -n "${REPORT_GCOV}" || return
> > > +
> > > +	if [ -w "${GCOV_DIR}/reset" ]; then
> > > +		echo 1 > "${GCOV_DIR}/reset"
> > > +	else
> > > +		unset REPORT_GCOV
> > > +	fi
> > > +}
> > > +
> > >  _wrapup()
> > >  {
> > >  	seq="check"
> > > @@ -527,6 +608,10 @@ _wrapup()
> > >  					     "${#bad[*]}" "${#notrun[*]}" \
> > >  					     "$((sect_stop - sect_start))"
> > >  		fi
> > > +
> > > +		# Generate code coverage report
> > > +		test -n "$REPORT_GCOV" && _gcov_generate_report "$REPORT_GCOV"
> > > +
> > >  		needwrap=false
> > >  	fi
> > >  
> > > @@ -801,6 +886,7 @@ function run_section()
> > >  	  echo "MOUNT_OPTIONS -- `_scratch_mount_options`"
> > >  	fi
> > >  	echo
> > > +	_gcov_reset
> > >  	needwrap=true
> > >  
> > >  	if [ ! -z "$SCRATCH_DEV" ]; then
> > > 
> > 
>
Darrick J. Wong July 26, 2023, 12:05 a.m. UTC | #4
On Thu, Jul 20, 2023 at 10:24:29PM +0800, Zorro Lang wrote:
> On Wed, Jul 19, 2023 at 07:29:24PM -0700, Darrick J. Wong wrote:
> > On Thu, Jul 20, 2023 at 12:19:16AM +0800, Zorro Lang wrote:
> > > On Tue, Jul 18, 2023 at 06:11:01PM -0700, Darrick J. Wong wrote:
> > > > From: Darrick J. Wong <djwong@kernel.org>
> > > > 
> > > > Support collecting kernel code coverage information as reported in
> > > > debugfs.  At the start of each section, we reset the gcov counters;
> > > > during the section wrapup, we'll collect the kernel gcov data.
> > > > 
> > > > If lcov is installed and the kernel source code is available, it will
> > > > also generate a nice html report.  If a CLI web browser is available, it
> > > > will also format the html report into text for easy grepping.
> > > > 
> > > > This requires the test runner to set REPORT_GCOV=1 explicitly and gcov
> > > > to be enabled in the kernel.
> > > > 
> > > > Cc: tytso@mit.edu
> > > > Cc: kent.overstreet@linux.dev
> > > > Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> > > > ---
> > > 
> > > Hi Darrick,
> > > 
> > > Is that possible to split this function from check script to tools/ ?
> > 
> > I don't mind separating it, though I don't see much reason to.  Are you
> > concerned about ./check growing larger?
> 
> Yeah, a little bit, the check script become smore and more complicated.

Ok, I'll do that, and augment the documentation for patch 1/1.

--D

> > 
> > --D
> > 
> > > Thanks,
> > > Zorro
> > > 
> > > >  README |    3 ++
> > > >  check  |   86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> > > >  2 files changed, 89 insertions(+)
> > > > 
> > > > 
> > > > diff --git a/README b/README
> > > > index 9790334db1..ccfdcbe703 100644
> > > > --- a/README
> > > > +++ b/README
> > > > @@ -249,6 +249,9 @@ Kernel/Modules related configuration:
> > > >     to "forever" and we'll wait forever until the module is gone.
> > > >   - Set KCONFIG_PATH to specify your preferred location of kernel config
> > > >     file. The config is used by tests to check if kernel feature is enabled.
> > > > + - Set REPORT_GCOV to a directory path to make lcov and genhtml generate
> > > > +   html reports from any gcov code coverage data collected by the kernel.
> > > > +   If REPORT_GCOV is set to 1, the report will be written to $REPORT_DIR/gcov/.
> > > >  
> > > >  Test control:
> > > >   - Set LOAD_FACTOR to a nonzero positive integer to increase the amount of
> > > > diff --git a/check b/check
> > > > index 97c7c4c7d1..3e6f27c653 100755
> > > > --- a/check
> > > > +++ b/check
> > > > @@ -451,6 +451,87 @@ _global_log() {
> > > >  	fi
> > > >  }
> > > >  
> > > > +GCOV_DIR=/sys/kernel/debug/gcov
> > > > +
> > > > +# Find the topmost directories of the .gcno directory hierarchy
> > > > +_gcov_find_topdirs() {
> > > > +	find "${GCOV_DIR}/" -name '*.gcno' -printf '%d|%h\n' | \
> > > > +		sort -g -k 1 | \
> > > > +		uniq | \
> > > > +		$AWK_PROG -F '|' 'BEGIN { x = -1 } { if (x < 0) x = $1; if ($1 == x) printf("%s\n", $2);}'
> > > > +}
> > > > +
> > > > +# Generate lcov html report from kernel gcov data if configured
> > > > +_gcov_generate_report() {
> > > > +	unset REPORT_GCOV	# don't trigger multiple times if ^C
> > > > +
> > > > +	local output_dir="$1"
> > > > +	test -n "${output_dir}" || return
> > > > +	test "$output_dir" = "1" && output_dir="$REPORT_DIR/gcov"
> > > > +
> > > > +	# Kernel support built in?
> > > > +	test -d "$GCOV_DIR" || return
> > > > +
> > > > +	readarray -t gcno_dirs < <(_gcov_find_topdirs)
> > > > +	test "${#gcno_dirs[@]}" -gt 0 || return
> > > > +
> > > > +	mkdir -p "${output_dir}/"
> > > > +
> > > > +	# Collect raw coverage data from the kernel
> > > > +	readarray -t source_dirs < <(find "${GCOV_DIR}/" -mindepth 1 -maxdepth 1 -type d)
> > > > +	for dir in "${source_dirs[@]}"; do
> > > > +		cp -p -R -d -u "${dir}" "${output_dir}/"
> > > > +	done
> > > > +
> > > > +	# If lcov is installed, use it to summarize the gcda data.
> > > > +	# If it is not installed, there's no point in going forward
> > > > +	command -v lcov > /dev/null || return
> > > > +	local lcov=(lcov --exclude 'include*' --capture)
> > > > +	lcov+=(--output-file "${output_dir}/gcov.report")
> > > > +	for d in "${gcno_dirs[@]}"; do
> > > > +		lcov+=(--directory "${d}")
> > > > +	done
> > > > +
> > > > +	# Generate a detailed HTML report from the summary
> > > > +	local gcov_start_time="$(date --date="${fstests_start_time:-now}")"
> > > > +	local genhtml=()
> > > > +	if command -v genhtml > /dev/null; then
> > > > +		genhtml+=(genhtml -o "${output_dir}/" "${output_dir}/gcov.report")
> > > > +		genhtml+=(--title "fstests on $(hostname -s) @ ${gcov_start_time}" --legend)
> > > > +	fi
> > > > +
> > > > +	# Try to convert the HTML report summary as text for easier grepping if
> > > > +	# there's an HTML renderer present
> > > > +	local totext=()
> > > > +	test "${#totext[@]}" -eq 0 && \
> > > > +		command -v lynx &>/dev/null && \
> > > > +		totext=(lynx -dump "${output_dir}/index.html" -width 120 -nonumbers -nolist)
> > > > +	test "${#totext[@]}" -eq 0 && \
> > > > +		command -v links &>/dev/null && \
> > > > +		totext=(links -dump "${output_dir}/index.html" -width 120)
> > > > +	test "${#totext[@]}" -eq 0 && \
> > > > +		command -v elinks &>/dev/null && \
> > > > +		totext=(elinks -dump "${output_dir}/index.html" --dump-width 120 --no-numbering --no-references)
> > > > +
> > > > +	# Analyze kernel data
> > > > +	"${lcov[@]}" > "${output_dir}/gcov.stdout" 2> "${output_dir}/gcov.stderr"
> > > > +	test "${#genhtml[@]}" -ne 0 && \
> > > > +		"${genhtml[@]}" >> "${output_dir}/gcov.stdout" 2>> "${output_dir}/gcov.stderr"
> > > > +	test "${#totext[@]}" -ne 0 && \
> > > > +		"${totext[@]}" > "${output_dir}/index.txt" 2>> "${output_dir}/gcov.stderr"
> > > > +}
> > > > +
> > > > +# Reset gcov usage data
> > > > +_gcov_reset() {
> > > > +	test -n "${REPORT_GCOV}" || return
> > > > +
> > > > +	if [ -w "${GCOV_DIR}/reset" ]; then
> > > > +		echo 1 > "${GCOV_DIR}/reset"
> > > > +	else
> > > > +		unset REPORT_GCOV
> > > > +	fi
> > > > +}
> > > > +
> > > >  _wrapup()
> > > >  {
> > > >  	seq="check"
> > > > @@ -527,6 +608,10 @@ _wrapup()
> > > >  					     "${#bad[*]}" "${#notrun[*]}" \
> > > >  					     "$((sect_stop - sect_start))"
> > > >  		fi
> > > > +
> > > > +		# Generate code coverage report
> > > > +		test -n "$REPORT_GCOV" && _gcov_generate_report "$REPORT_GCOV"
> > > > +
> > > >  		needwrap=false
> > > >  	fi
> > > >  
> > > > @@ -801,6 +886,7 @@ function run_section()
> > > >  	  echo "MOUNT_OPTIONS -- `_scratch_mount_options`"
> > > >  	fi
> > > >  	echo
> > > > +	_gcov_reset
> > > >  	needwrap=true
> > > >  
> > > >  	if [ ! -z "$SCRATCH_DEV" ]; then
> > > > 
> > > 
> > 
>
diff mbox series

Patch

diff --git a/README b/README
index 9790334db1..ccfdcbe703 100644
--- a/README
+++ b/README
@@ -249,6 +249,9 @@  Kernel/Modules related configuration:
    to "forever" and we'll wait forever until the module is gone.
  - Set KCONFIG_PATH to specify your preferred location of kernel config
    file. The config is used by tests to check if kernel feature is enabled.
+ - Set REPORT_GCOV to a directory path to make lcov and genhtml generate
+   html reports from any gcov code coverage data collected by the kernel.
+   If REPORT_GCOV is set to 1, the report will be written to $REPORT_DIR/gcov/.
 
 Test control:
  - Set LOAD_FACTOR to a nonzero positive integer to increase the amount of
diff --git a/check b/check
index 97c7c4c7d1..3e6f27c653 100755
--- a/check
+++ b/check
@@ -451,6 +451,87 @@  _global_log() {
 	fi
 }
 
+GCOV_DIR=/sys/kernel/debug/gcov
+
+# Find the topmost directories of the .gcno directory hierarchy
+_gcov_find_topdirs() {
+	find "${GCOV_DIR}/" -name '*.gcno' -printf '%d|%h\n' | \
+		sort -g -k 1 | \
+		uniq | \
+		$AWK_PROG -F '|' 'BEGIN { x = -1 } { if (x < 0) x = $1; if ($1 == x) printf("%s\n", $2);}'
+}
+
+# Generate lcov html report from kernel gcov data if configured
+_gcov_generate_report() {
+	unset REPORT_GCOV	# don't trigger multiple times if ^C
+
+	local output_dir="$1"
+	test -n "${output_dir}" || return
+	test "$output_dir" = "1" && output_dir="$REPORT_DIR/gcov"
+
+	# Kernel support built in?
+	test -d "$GCOV_DIR" || return
+
+	readarray -t gcno_dirs < <(_gcov_find_topdirs)
+	test "${#gcno_dirs[@]}" -gt 0 || return
+
+	mkdir -p "${output_dir}/"
+
+	# Collect raw coverage data from the kernel
+	readarray -t source_dirs < <(find "${GCOV_DIR}/" -mindepth 1 -maxdepth 1 -type d)
+	for dir in "${source_dirs[@]}"; do
+		cp -p -R -d -u "${dir}" "${output_dir}/"
+	done
+
+	# If lcov is installed, use it to summarize the gcda data.
+	# If it is not installed, there's no point in going forward
+	command -v lcov > /dev/null || return
+	local lcov=(lcov --exclude 'include*' --capture)
+	lcov+=(--output-file "${output_dir}/gcov.report")
+	for d in "${gcno_dirs[@]}"; do
+		lcov+=(--directory "${d}")
+	done
+
+	# Generate a detailed HTML report from the summary
+	local gcov_start_time="$(date --date="${fstests_start_time:-now}")"
+	local genhtml=()
+	if command -v genhtml > /dev/null; then
+		genhtml+=(genhtml -o "${output_dir}/" "${output_dir}/gcov.report")
+		genhtml+=(--title "fstests on $(hostname -s) @ ${gcov_start_time}" --legend)
+	fi
+
+	# Try to convert the HTML report summary as text for easier grepping if
+	# there's an HTML renderer present
+	local totext=()
+	test "${#totext[@]}" -eq 0 && \
+		command -v lynx &>/dev/null && \
+		totext=(lynx -dump "${output_dir}/index.html" -width 120 -nonumbers -nolist)
+	test "${#totext[@]}" -eq 0 && \
+		command -v links &>/dev/null && \
+		totext=(links -dump "${output_dir}/index.html" -width 120)
+	test "${#totext[@]}" -eq 0 && \
+		command -v elinks &>/dev/null && \
+		totext=(elinks -dump "${output_dir}/index.html" --dump-width 120 --no-numbering --no-references)
+
+	# Analyze kernel data
+	"${lcov[@]}" > "${output_dir}/gcov.stdout" 2> "${output_dir}/gcov.stderr"
+	test "${#genhtml[@]}" -ne 0 && \
+		"${genhtml[@]}" >> "${output_dir}/gcov.stdout" 2>> "${output_dir}/gcov.stderr"
+	test "${#totext[@]}" -ne 0 && \
+		"${totext[@]}" > "${output_dir}/index.txt" 2>> "${output_dir}/gcov.stderr"
+}
+
+# Reset gcov usage data
+_gcov_reset() {
+	test -n "${REPORT_GCOV}" || return
+
+	if [ -w "${GCOV_DIR}/reset" ]; then
+		echo 1 > "${GCOV_DIR}/reset"
+	else
+		unset REPORT_GCOV
+	fi
+}
+
 _wrapup()
 {
 	seq="check"
@@ -527,6 +608,10 @@  _wrapup()
 					     "${#bad[*]}" "${#notrun[*]}" \
 					     "$((sect_stop - sect_start))"
 		fi
+
+		# Generate code coverage report
+		test -n "$REPORT_GCOV" && _gcov_generate_report "$REPORT_GCOV"
+
 		needwrap=false
 	fi
 
@@ -801,6 +886,7 @@  function run_section()
 	  echo "MOUNT_OPTIONS -- `_scratch_mount_options`"
 	fi
 	echo
+	_gcov_reset
 	needwrap=true
 
 	if [ ! -z "$SCRATCH_DEV" ]; then