From: Chaosheng Qu <[email protected]> Subject: [PATCH] rtla: test that SIGINT unwinds cleanly To: Steven Rostedt <[email protected]> Cc: Tomas Glozar <[email protected]>, [email protected], [email protected]
rtla's tests only ever run the tool to completion. Nothing exercises the path where the tool is interrupted, which is where the trace instance teardown can go wrong: a run that leaves an instance behind makes the next run fail for reasons that have nothing to do with the tool, and a run that never returns is not detected at all. Add check_with_action(), which runs the tool like check() does but performs an action while it is running, and always reports LEFTOVER_INSTANCES=n so that a test can assert the instance was removed. The tool is started with "eval exec" so that $! is the tool itself: the action signals $RTLA_PID directly, and timeout(1) stays the direct parent so it forwards the signal. Then use it for osnoise and timerlat: SIGINT must make the tool unwind and remove its instance. check-sigint.sh looks at the process state rather than kill -0, because a tool that has exited but has not been reaped still answers kill -0. Signed-off-by: Chaosheng Qu <[email protected]> --- diff --git a/tools/tracing/rtla/tests/engine.sh b/tools/tracing/rtla/tests/engine.sh index 4287cd6..3327e16 100644 --- a/tools/tracing/rtla/tests/engine.sh +++ b/tools/tracing/rtla/tests/engine.sh @@ -41,6 +41,86 @@ reset_osnoise() { popd >/dev/null } +leftover_instances() { + # Count the trace instances rtla should have removed on exit. + local d count=0 + for d in osnoise_top osnoise_hist timerlat_top timerlat_hist osnoise_trace + do + [ -d "/sys/kernel/tracing/instances/$d" ] && count=$((count + 1)) + done + echo $count +} + +check_with_action() { + # Same as "check", but run ACTION while rtla is running, so that a test + # can interrupt the tool mid-run. + # Arguments: test_name rtla_args action exit_code [expected [unexpected]] + # The output of ACTION is appended to rtla's output, and the number of + # leftover trace instances is always reported as LEFTOVER_INSTANCES=n. + # ACTION can use $RTLA_PID to signal the tool. + local test_name=$1 tested_command=$2 action=$3 + local expected_exitcode=${4:-0} + local expected_output=$5 unexpected_output=$6 + local result exitcode fail failbuf pid + + ctr=$(($ctr + 1)) + [ -z "$TEST_COUNT" ] && return + + [ "$NO_RESET_OSNOISE" == 1 ] || reset_osnoise + + tmpdir=$(mktemp -d) + pushd $tmpdir >/dev/null + + # eval so that the test can use shell syntax in its arguments, and exec + # so that $! is the tool itself and not an intermediate shell: tests + # signal $RTLA_PID directly. timeout forwards the signal to its child. + # The arguments are re-parsed by the same eval as in check(), so quoting + # behaves identically; only exec and the quoted $RTLA are added. + ( eval "exec stdbuf -oL $TIMEOUT \"$RTLA\" $tested_command" ) > rtla.txt 2>&1 & + pid=$! + # The ACTION runs as a separate process, so RTLA_PID has to be exported + # or the helper scripts see it as unset. + export RTLA_PID=$pid + + sleep "${ACTION_DELAY:-3}" + eval "$action" >> rtla.txt 2>&1 + + wait $pid + exitcode=$? + echo "LEFTOVER_INSTANCES=$(leftover_instances)" >> rtla.txt + + result=$(cat rtla.txt) + failbuf='' + fail=0 + + if [ -n "$expected_output" ] && ! grep -qE "$expected_output" <<< "$result" + then + fail=1 + failbuf+=$(printf "# Output match failed: \"%s\"" "$expected_output") + failbuf+=$'\n' + fi + + if [ -n "$unexpected_output" ] && grep -qE "$unexpected_output" <<< "$result" + then + fail=1 + failbuf+=$(printf "# Output non-match failed: \"%s\"" "$unexpected_output") + failbuf+=$'\n' + fi + + if [ $exitcode -eq $expected_exitcode ] && [ $fail -eq 0 ] + then + echo "ok $ctr - $test_name" + else + echo "not ok $ctr - $test_name" + echo -n "$failbuf" + echo "$result" | col -b | while read line; do echo "# $line"; done + printf "#\n# exit code %s\n" $exitcode + fi + + popd >/dev/null + rm -r $tmpdir +} + check() { test_name=$0 tested_command=$1 diff --git a/tools/tracing/rtla/tests/osnoise.t b/tools/tracing/rtla/tests/osnoise.t index 214fd40..df260e9 100644 --- a/tools/tracing/rtla/tests/osnoise.t +++ b/tools/tracing/rtla/tests/osnoise.t @@ -99,4 +99,11 @@ check_top_q_hist "with conditional continue" \ check_top_hist "with trace output at end" \ "osnoise TOOL -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$" +# Cleanup test: the tool must unwind on SIGINT without leaving its trace +# instance behind. +check_with_action "top exits on SIGINT" \ + "osnoise top -q" \ + "$testdir/scripts/check-sigint.sh" \ + 0 "SIGINT handled OK" "LEFTOVER_INSTANCES=[1-9]" + test_end diff --git a/tools/tracing/rtla/tests/scripts/check-sigint.sh b/tools/tracing/rtla/tests/scripts/check-sigint.sh new file mode 100755 index 0000000..7f63424 --- /dev/null +++ b/tools/tracing/rtla/tests/scripts/check-sigint.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Interrupt the tool with SIGINT and check that it unwinds instead of +# hanging. The caller reports LEFTOVER_INSTANCES separately. +# Usage: check-sigint.sh +# Output: "SIGINT handled OK" on success, an error otherwise. +# +# The tool is a child of the test shell, so once it has exited it stays +# visible as a zombie until it is reaped -- and kill -0 still succeeds on a +# zombie. Check the process state instead, or a clean exit looks like a hang. + +pid=$RTLA_PID +[ -n "$pid" ] || { echo "RTLA_PID not set"; exit 1; } + +kill -INT "$pid" 2>/dev/null || { echo "Failed to signal rtla"; exit 1; } + +# rtla's SIGINT handler stops tracing and lets the main loop return. Give it +# a few seconds before declaring it stuck. +for _ in $(seq 30) +do + [ -r "/proc/$pid/stat" ] || break + [ "$(awk '{print $3}' "/proc/$pid/stat" 2>/dev/null)" = "Z" ] && break + sleep 0.2 +done + +if [ -r "/proc/$pid/stat" ] && [ "$(awk '{print $3}' "/proc/$pid/stat")" != "Z" ] +then + echo "rtla still running 6s after SIGINT" + exit 1 +fi + +echo "SIGINT handled OK" diff --git a/tools/tracing/rtla/tests/timerlat.t b/tools/tracing/rtla/tests/timerlat.t index 5116857..fe9c9da 100644 --- a/tools/tracing/rtla/tests/timerlat.t +++ b/tools/tracing/rtla/tests/timerlat.t @@ -153,4 +153,11 @@ else fi done +# Cleanup test: the tool must unwind on SIGINT without leaving its trace +# instance behind. +check_with_action "top exits on SIGINT" \ + "timerlat top -q" \ + "$testdir/scripts/check-sigint.sh" \ + 0 "SIGINT handled OK" "LEFTOVER_INSTANCES=[1-9]" + test_end
