aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2023-07-25 18:56:51 -0700
committerZorro Lang <zlang@kernel.org>2023-08-05 02:06:34 +0800
commitd39fb9a81d76305ab1b3c0c3a042e767a7c1bf89 (patch)
tree5759a315f68c824da387c276b95663d17aed61d0
parent24cfe625794ea0c807d0739291f0a9ce7ddd5a2b (diff)
downloadxfstests-dev-d39fb9a81d76305ab1b3c0c3a042e767a7c1bf89.tar.gz
check: generate gcov code coverage reports at the end of each section
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> Reviewed-by: Zorro Lang <zlang@redhat.com> Signed-off-by: Zorro Lang <zlang@kernel.org>
-rw-r--r--README3
-rwxr-xr-xcheck18
-rw-r--r--common/gcov87
3 files changed, 108 insertions, 0 deletions
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 89e7e7bf20..287cd8955e 100755
--- a/check
+++ b/check
@@ -447,6 +447,11 @@ _global_log() {
fi
}
+if [ -n "$REPORT_GCOV" ]; then
+ . ./common/gcov
+ _gcov_check_report_gcov
+fi
+
_wrapup()
{
seq="check"
@@ -523,6 +528,18 @@ _wrapup()
"${#bad[*]}" "${#notrun[*]}" \
"$((sect_stop - sect_start))"
fi
+
+ # Generate code coverage report
+ if [ -n "$REPORT_GCOV" ]; then
+ # don't trigger multiple times if caller hits ^C
+ local gcov_report_dir="$REPORT_GCOV"
+ test "$gcov_report_dir" = "1" && \
+ gcov_report_dir="$REPORT_DIR/gcov"
+ unset REPORT_GCOV
+
+ _gcov_generate_report "$gcov_report_dir"
+ fi
+
needwrap=false
fi
@@ -797,6 +814,7 @@ function run_section()
echo "MOUNT_OPTIONS -- `_scratch_mount_options`"
fi
echo
+ test -n "$REPORT_GCOV" && _gcov_reset
needwrap=true
if [ ! -z "$SCRATCH_DEV" ]; then
diff --git a/common/gcov b/common/gcov
new file mode 100644
index 0000000000..b7e3ed5a93
--- /dev/null
+++ b/common/gcov
@@ -0,0 +1,87 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle. All Rights Reserved.
+#
+# Routines for capturing kernel code coverage reports
+
+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() {
+ local output_dir="$1"
+ test -n "${output_dir}" || return
+
+ # 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}/raw/"
+
+ # 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}/raw/"
+ 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() {
+ echo 1 > "${GCOV_DIR}/reset"
+}
+
+# If the caller wanted us to capture gcov reports but the kernel doesn't
+# support it, turn it off.
+_gcov_check_report_gcov() {
+ test -z "$REPORT_GCOV" && return 0
+ test -w "${GCOV_DIR}/reset" && return 0
+
+ unset REPORT_GCOV
+ return 1
+}