[PATCH v3 18/54] selftests-dyndbg: add tools/testing/selftests/dynamic_debug/*

jim.cromie at gmail.com jim.cromie at gmail.com
Tue Apr 15 19:46:43 UTC 2025


On Tue, Apr 15, 2025 at 4:02 AM Louis Chauvet <louis.chauvet at bootlin.com> wrote:
>
>
>
> Le 02/04/2025 à 19:41, Jim Cromie a écrit :
> > Add a selftest script for dynamic-debug.  The config requires
> > CONFIG_TEST_DYNAMIC_DEBUG=m and CONFIG_TEST_DYNAMIC_DEBUG_SUBMOD=m,
> > which tacitly requires either CONFIG_DYNAMIC_DEBUG=y or
> > CONFIG_DYNAMIC_DEBUG_CORE=y
> >
> > ATM this has just basic_tests(), which modify pr_debug() flags in the
> > builtin params module.  This means they're available to manipulate and
> > observe the effects in "cat control".
> >
> > This is backported from another feature branch; the support-fns (thx
> > Lukas) have unused features at the moment, they'll get used shortly.
> >
> > The script enables simple virtme-ng testing:
> >
> >     [jimc at gandalf b0-ftrace]$ vrun_t
> >     virtme-ng 1.32+115.g07b109d
> >     doing: vng --name v6.14-rc4-60-gd5f48427de0c \
> >         --user root -v -p 4 -a dynamic_debug.verbose=3 V=1 \
> >         -- ../tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
> >     virtme: waiting for virtiofsd to start
> >     ..
> >
> > And add dynamic_debug to TARGETS, so `make run_tests` sees it properly
> >
> > For the impatient, set TARGETS explicitly:
> >
> >    bash-5.2# make TARGETS=dynamic_debug run_tests
> >    make[1]: ...
> >    TAP version 13
> >    1..1
> >    [   35.552922] dyndbg: read 3 bytes from userspace
> >    [   35.553099] dyndbg: query 0: "=_" mod:*
> >    [   35.553544] dyndbg: processed 1 queries, with 1778 matches, 0 errs
> >
> > Signed-off-by: Jim Cromie <jim.cromie at gmail.com>
> > Co-developed-by: Łukasz Bartosik <ukaszb at chromium.org>
> > Signed-off-by: Łukasz Bartosik <ukaszb at chromium.org>
> > ---
> > -r3 turn off green at end
> >      drop config dep on TEST_DYNAMIC_DEBUG,
> >      since basic-test uses builtin params
> >
> > - check KCONFIG_CONFIG to avoid silly fails
> >
> > Several tests are dependent upon config choices. Lets avoid failing
> > where that is noise.
> >
> > The KCONFIG_CONFIG var exists to convey the config-file around.  If
> > the var names a file, read it and extract the relevant CONFIG items,
> > and use them to skip the dependent tests, thus avoiding the fails that
> > would follow, and the disruption to whatever CI is running these
> > selftests.
> >
> > If the envar doesn't name a config-file, ".config" is assumed.
> >
> > CONFIG_DYNAMIC_DEBUG=y:
> >
> > basic-tests() and comma-terminator-tests() test for the presence of
> > the builtin pr_debugs in module/main.c, which I deemed stable and
> > therefore safe to count.  That said, the test fails if only
> > CONFIG_DYNAMIC_DEBUG_CORE=y is set.  It could be rewritten to test
> > against test-dynamic-debug.ko, but that just trades one config
> > dependence for another.
> >
> > CONFIG_TEST_DYNAMIC_DEBUG=m
> >
> > As written, test_percent_splitting() modprobes test_dynamic_debug,
> > enables several classes, and count them.  It could be re-written to
> > work for the builtin module also, but builtin test modules are not a
> > common or desirable build/config.
> >
> > CONFIG_TEST_DYNAMIC_DEBUG=m && CONFIG_TEST_DYNAMIC_DEBUG_SUBMOD=m
> >
> > test_mod_submod() recaps the bug found in DRM-CI where drivers werent
> > enabled by drm.debug=<bits>.  It modprobes both test_dynamic_debug &
> > test_dynamic_debug_submod, so it depends on a loadable modules config.
> >
> > It could be rewritten to work in a builtin parent config; DRM=y is
> > common enough to be pertinent, but testing that config also wouldn't
> > really test anything more fully than all-loadable modules, since they
> > default together.
> >
> > generalize-test-env
> > ---
> >   MAINTAINERS                                   |   1 +
> >   tools/testing/selftests/Makefile              |   1 +
> >   .../testing/selftests/dynamic_debug/Makefile  |   9 +
> >   tools/testing/selftests/dynamic_debug/config  |   7 +
> >   .../dynamic_debug/dyndbg_selftest.sh          | 257 ++++++++++++++++++
> >   5 files changed, 275 insertions(+)
> >   create mode 100644 tools/testing/selftests/dynamic_debug/Makefile
> >   create mode 100644 tools/testing/selftests/dynamic_debug/config
> >   create mode 100755 tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 1c5fcbd9e408..1192ad6c65c1 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -8140,6 +8140,7 @@ S:      Maintained
> >   F:  include/linux/dynamic_debug.h
> >   F:  lib/dynamic_debug.c
> >   F:  lib/test_dynamic_debug*.c
> > +F:   tools/testing/selftests/dynamic_debug/*
> >
> >   DYNAMIC INTERRUPT MODERATION
> >   M:  Tal Gilboa <talgi at nvidia.com>
> > diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> > index 8daac70c2f9d..b6a323c7f986 100644
> > --- a/tools/testing/selftests/Makefile
> > +++ b/tools/testing/selftests/Makefile
> > @@ -26,6 +26,7 @@ TARGETS += drivers/net/team
> >   TARGETS += drivers/net/virtio_net
> >   TARGETS += drivers/platform/x86/intel/ifs
> >   TARGETS += dt
> > +TARGETS += dynamic_debug
> >   TARGETS += efivarfs
> >   TARGETS += exec
> >   TARGETS += fchmodat2
> > diff --git a/tools/testing/selftests/dynamic_debug/Makefile b/tools/testing/selftests/dynamic_debug/Makefile
> > new file mode 100644
> > index 000000000000..6d06fa7f1040
> > --- /dev/null
> > +++ b/tools/testing/selftests/dynamic_debug/Makefile
> > @@ -0,0 +1,9 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +# borrowed from Makefile for user memory selftests
> > +
> > +# No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
> > +all:
> > +
> > +TEST_PROGS := dyndbg_selftest.sh
> > +
> > +include ../lib.mk
> > diff --git a/tools/testing/selftests/dynamic_debug/config b/tools/testing/selftests/dynamic_debug/config
> > new file mode 100644
> > index 000000000000..0f906ff53908
> > --- /dev/null
> > +++ b/tools/testing/selftests/dynamic_debug/config
> > @@ -0,0 +1,7 @@
> > +
> > +# basic tests ref the builtin params module
> > +CONFIG_DYNAMIC_DEBUG=m
> > +
> > +# more testing is possible with these
> > +# CONFIG_TEST_DYNAMIC_DEBUG=m
> > +# CONFIG_TEST_DYNAMIC_DEBUG_SUBMOD=m
> > diff --git a/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
> > new file mode 100755
> > index 000000000000..465fad3f392c
> > --- /dev/null
> > +++ b/tools/testing/selftests/dynamic_debug/dyndbg_selftest.sh
> > @@ -0,0 +1,257 @@
> > +#!/bin/bash
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +
> > +V=${V:=0}  # invoke as V=1 $0  for global verbose
> > +RED="\033[0;31m"
> > +GREEN="\033[0;32m"
> > +YELLOW="\033[0;33m"
> > +BLUE="\033[0;34m"
> > +MAGENTA="\033[0;35m"
> > +CYAN="\033[0;36m"
> > +NC="\033[0;0m"
> > +error_msg=""
> > +
> > +[ -e /proc/dynamic_debug/control ] || {
> > +    echo -e "${RED}: this test requires CONFIG_DYNAMIC_DEBUG=y ${NC}"
> > +    exit 0 # nothing to test here, no good reason to fail.
> > +}
> > +
> > +# need info to avoid failures due to untestable configs
> > +
> > +[ -f "$KCONFIG_CONFIG" ] || KCONFIG_CONFIG=".config"
> > +if [ -f "$KCONFIG_CONFIG" ]; then
> > +    echo "# consulting KCONFIG_CONFIG: $KCONFIG_CONFIG"
> > +    grep -q "CONFIG_DYNAMIC_DEBUG=y" $KCONFIG_CONFIG ; LACK_DD_BUILTIN=$?
> > +    grep -q "CONFIG_TEST_DYNAMIC_DEBUG=m" $KCONFIG_CONFIG ; LACK_TMOD=$?
> > +    grep -q "CONFIG_TEST_DYNAMIC_DEBUG_SUBMOD=m" $KCONFIG_CONFIG ; LACK_TMOD_SUBMOD=$?
> > +    if [ $V -eq 1 ]; then
> > +     echo LACK_DD_BUILTIN: $LACK_DD_BUILTIN
> > +     echo LACK_TMOD: $LACK_TMOD
> > +     echo LACK_TMOD_SUBMOD: $LACK_TMOD_SUBMOD
> > +    fi
> > +else
> > +    LACK_DD_BUILTIN=0
> > +    LACK_TMOD=0
> > +    LACK_TMOD_SUBMOD=0
> > +fi
>
> Nitpick for the sh file: is it normal to have inconsistent indenting ?(4
> space, tabs, 8 spaces)
>

hmm.
I just checked scripts/bpf_doc.py
it uses 4 space indents, I didnt check for tabs.

But I will go look for inconsistencies and mixes of spaces & tabs

> > +function vx () {
> > +    echo $1 > /sys/module/dynamic_debug/parameters/verbose
> > +}
> > +
> > +function ddgrep () {
> > +    grep $1 /proc/dynamic_debug/control
> > +}
> > +
> > +function doprints () {
> > +    cat /sys/module/test_dynamic_debug/parameters/do_prints
> > +}
> > +
> > +function ddcmd () {
> > +    exp_exit_code=0
> > +    num_args=$#
> > +    if [ "${@:$#}" = "pass" ]; then
> > +     num_args=$#-1
> > +    elif [ "${@:$#}" = "fail" ]; then
> > +        num_args=$#-1
> > +     exp_exit_code=1
> > +    fi
> > +    args=${@:1:$num_args}
> > +    output=$((echo "$args" > /proc/dynamic_debug/control) 2>&1)
> > +    exit_code=$?
> > +    error_msg=$(echo $output | cut -d ":" -f 5 | sed -e 's/^[[:space:]]*//')
> > +    handle_exit_code $BASH_LINENO $FUNCNAME $exit_code $exp_exit_code
> > +}
> > +
> > +function handle_exit_code() {
> > +    local exp_exit_code=0
> > +    [ $# == 4 ] && exp_exit_code=$4
> > +    if [ $3 -ne $exp_exit_code ]; then
> > +        echo -e "${RED}: $BASH_SOURCE:$1 $2() expected to exit with code $exp_exit_code"
> > +     [ $3 == 1 ] && echo "Error: '$error_msg'"
> > +        exit
> > +    fi
> > +}
> > +
> > +# $1 - pattern to match, pattern in $1 is enclosed by spaces for a match ""\s$1\s"
> > +# $2 - number of times the pattern passed in $1 is expected to match
> > +# $3 - optional can be set either to "-r" or "-v"
> > +#       "-r" means relaxed matching in this case pattern provided in $1 is passed
> > +#       as is without enclosing it with spaces
> > +#       "-v" prints matching lines
> > +# $4 - optional when $3 is set to "-r" then $4 can be used to pass "-v"
> > +function check_match_ct {
> > +    pattern="\s$1\s"
> > +    exp_cnt=0
> > +
> > +    [ "$3" == "-r" ] && pattern="$1"
> > +    let cnt=$(ddgrep "$pattern" | wc -l)
> > +    if [ $V -eq 1 ] || [ "$3" == "-v" ] || [ "$4" == "-v" ]; then
> > +        echo -ne "${BLUE}" && ddgrep $pattern && echo -ne "${NC}"
> > +    fi
> > +    [ $# -gt 1 ] && exp_cnt=$2
> > +    if [ $cnt -ne $exp_cnt ]; then
> > +        echo -e "${RED}: $BASH_SOURCE:$BASH_LINENO check failed expected $exp_cnt on $1, got $cnt"
> > +        exit
> > +    else
> > +        echo ": $cnt matches on $1"
> > +    fi
> > +}
> > +
> > +# $1 - trace instance name
> > +# #2 - if > 0 then directory is expected to exist, if <= 0 then otherwise
> > +# $3 - "-v" for verbose
> > +function check_trace_instance_dir {
> > +    if [ -e /sys/kernel/tracing/instances/$1 ]; then
> > +        if [ "$3" == "-v" ] ; then
> > +            echo "ls -l /sys/kernel/tracing/instances/$1: "
> > +            ls -l /sys/kernel/tracing/instances/$1
> > +        fi
> > +     if [ $2 -le 0 ]; then
> > +            echo -e "${RED}: $BASH_SOURCE:$BASH_LINENO error trace instance \
> > +                 '/sys/kernel/tracing/instances/$1' does exist"
> > +         exit
> > +     fi
> > +    else
> > +     if [ $2 -gt 0 ]; then
> > +            echo -e "${RED}: $BASH_SOURCE:$BASH_LINENO error trace instance \
> > +                 '/sys/kernel/tracing/instances/$1' does not exist"
> > +         exit
> > +        fi
> > +    fi
> > +}
> > +
> > +function tmark {
> > +    echo $* > /sys/kernel/tracing/trace_marker
> > +}
> > +
> > +# $1 - trace instance name
> > +# $2 - line number
> > +# $3 - if > 0 then the instance is expected to be opened, otherwise
> > +# the instance is expected to be closed
> > +function check_trace_instance {
> > +    output=$(tail -n9 /proc/dynamic_debug/control | grep ": Opened trace instances" \
> > +         | xargs -n1 | grep $1)
> > +    if [ "$output" != $1 ] && [ $3 -gt 0 ]; then
> > +        echo -e "${RED}: $BASH_SOURCE:$2 trace instance $1 is not opened"
> > +        exit
> > +    fi
> > +    if [ "$output" == $1 ] && [ $3 -le 0 ]; then
> > +        echo -e "${RED}: $BASH_SOURCE:$2 trace instance $1 is not closed"
> > +        exit
> > +    fi
> > +}
> > +
> > +function is_trace_instance_opened {
> > +    check_trace_instance $1 $BASH_LINENO 1
> > +}
> > +
> > +function is_trace_instance_closed {
> > +    check_trace_instance $1 $BASH_LINENO 0
> > +}
> > +
> > +# $1 - trace instance directory to delete
> > +# $2 - if > 0 then directory is expected to be deleted successfully, if <= 0 then otherwise
> > +function del_trace_instance_dir() {
> > +    exp_exit_code=1
> > +    [ $2 -gt 0 ] && exp_exit_code=0
> > +    output=$((rmdir /sys/kernel/debug/tracing/instances/$1) 2>&1)
> > +    exit_code=$?
> > +    error_msg=$(echo $output | cut -d ":" -f 3 | sed -e 's/^[[:space:]]*//')
> > +    handle_exit_code $BASH_LINENO $FUNCNAME $exit_code $exp_exit_code
> > +}
> > +
> > +function error_log_ref {
> > +    # to show what I got
> > +    : echo "# error-log-ref: $1"
> > +    : echo cat \$2
> > +}
> > +
> > +function ifrmmod {
> > +    lsmod | grep $1 2>&1>/dev/null && rmmod $1
> > +}
> > +
> > +# $1 - text to search for
> > +function search_trace() {
> > +    search_trace_name 0 1 $1
> > +}
> > +
> > +# $1 - trace instance name, 0 for global event trace
> > +# $2 - line number counting from the bottom
> > +# $3 - text to search for
> > +function search_trace_name() {
> > +     if [ "$1" = "0" ]; then
> > +         buf=$(cat /sys/kernel/debug/tracing/trace)
> > +         line=$(tail -$2 /sys/kernel/debug/tracing/trace | head -1 | sed -e 's/^[[:space:]]*//')
> > +     else
> > +         buf=$(cat /sys/kernel/debug/tracing/instances/$1/trace)
> > +         line=$(tail -$2 /sys/kernel/debug/tracing/instances/$1/trace | head -1 | \
> > +                sed -e 's/^[[:space:]]*//')
> > +     fi
> > +     if [ $2 = 0 ]; then
> > +         # whole-buf check
> > +         output=$(echo $buf | grep "$3")
> > +     else
> > +         output=$(echo $line | grep "$3")
> > +     fi
> > +     if [ "$output" = "" ]; then
> > +            echo -e "${RED}: $BASH_SOURCE:$BASH_LINENO search for '$3' failed \
> > +                 in line '$line' or '$buf'"
> > +         exit
> > +     fi
> > +     if [ $V = 1 ]; then
> > +         echo -e "${MAGENTA}: search_trace_name in $1 found: \n$output \nin:${BLUE} $buf ${NC}"
> > +        fi
> > +}
> > +
> > +# $1 - error message to check
> > +function check_err_msg() {
> > +    if [ "$error_msg" != "$1" ]; then
> > +        echo -e "${RED}: $BASH_SOURCE:$BASH_LINENO error message '$error_msg' \
> > +             does not match with '$1'"
> > +        exit
> > +    fi
> > +}
> > +
> > +function basic_tests {
> > +    echo -e "${GREEN}# BASIC_TESTS ${NC}"
> > +    if [ $LACK_DD_BUILTIN -eq 1 ]; then
> > +     echo "SKIP"
> > +     return
> > +    fi
> > +    ddcmd =_ # zero everything
> > +    check_match_ct =p 0
> > +
> > +    # module params are builtin to handle boot args
> > +    check_match_ct '\[params\]' 4 -r
> > +    ddcmd module params +mpf
> > +    check_match_ct =pmf 4
> > +
> > +    # multi-cmd input, newline separated, with embedded comments
> > +    cat <<"EOF" > /proc/dynamic_debug/control
> > +      module params =_                               # clear params
> > +      module params +mf                              # set flags
> > +      module params func parse_args +sl              # other flags
> > +EOF
> > +    check_match_ct =mf 3
> > +    check_match_ct =mfsl 1
> > +    ddcmd =_
> > +}
> > +
> > +tests_list=(
> > +    basic_tests
> > +)
> > +
> > +# Run tests
> > +
> > +ifrmmod test_dynamic_debug_submod
> > +ifrmmod test_dynamic_debug
> > +
> > +for test in "${tests_list[@]}"
> > +do
> > +    $test
> > +    echo ""
> > +done
> > +echo -en "${GREEN}# Done on: "
> > +date
> > +echo -en "${NC}"
>
> --
> Louis Chauvet, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
>
>


More information about the dri-devel mailing list