diff options
author | Darrick J. Wong <djwong@kernel.org> | 2023-07-25 18:56:51 -0700 |
---|---|---|
committer | Zorro Lang <zlang@kernel.org> | 2023-08-05 02:06:34 +0800 |
commit | d39fb9a81d76305ab1b3c0c3a042e767a7c1bf89 (patch) | |
tree | 5759a315f68c824da387c276b95663d17aed61d0 | |
parent | 24cfe625794ea0c807d0739291f0a9ce7ddd5a2b (diff) | |
download | xfstests-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-- | README | 3 | ||||
-rwxr-xr-x | check | 18 | ||||
-rw-r--r-- | common/gcov | 87 |
3 files changed, 108 insertions, 0 deletions
@@ -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 @@ -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 +} |