rtla creates one of four tracefs instances per run (osnoise_top,
osnoise_hist, timerlat_top, timerlat_hist) and removes it on exit. A
cleanup failure only shows up as the next run failing with -EEXIST or
stale tracer state. tests/engine.sh already removes leftover instances
before each test, so the failure mode is known but not tested.

Add cleanup.t and a helper script covering the abnormal exits: SIGINT,
SIGTERM, the -d timer, a failing --on-threshold shell action, and three
interrupted runs followed by a normal run.

rtla did not handle SIGTERM, so killing it left the tracefs instance
behind. Install stop_trace as the SIGTERM handler, matching SIGINT and
the -d timer.

The helper needs to start rtla in the background and signal it, which
engine.sh's foreground check() cannot express. cleanup.t drives the
helper and emits the TAP result itself.

The test needs root, a writable tracefs and CONFIG_OSNOISE_TRACER. It
skips when any of those are missing. The action-failure case also skips
if the stop-total threshold is not crossed, since an idle machine may
never generate a sample above the limit.

Signed-off-by: Chaosheng Qu <[email protected]>
---
 tools/tracing/rtla/src/common.c               |   2 +
 tools/tracing/rtla/tests/cleanup.t            |  71 +++++++
 .../rtla/tests/scripts/check-cleanup.sh       | 197 ++++++++++++++++++
 3 files changed, 270 insertions(+)
 create mode 100644 tools/tracing/rtla/tests/cleanup.t
 create mode 100755 tools/tracing/rtla/tests/scripts/check-cleanup.sh

diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c
index 8c7f5e75b2ec..b86a600769ed 100644
--- a/tools/tracing/rtla/src/common.c
+++ b/tools/tracing/rtla/src/common.c
@@ -39,6 +39,7 @@ static void stop_trace(int sig)
 static void set_signals(struct common_params *params)
 {
        signal(SIGINT, stop_trace);
+       signal(SIGTERM, stop_trace);
        if (params->duration) {
                signal(SIGALRM, stop_trace);
                alarm(params->duration);
@@ -51,6 +52,7 @@ static void set_signals(struct common_params *params)
 static void unset_signals(struct common_params *params)
 {
        signal(SIGINT, SIG_DFL);
+       signal(SIGTERM, SIG_DFL);
        if (params->duration) {
                alarm(0);
                signal(SIGALRM, SIG_DFL);
diff --git a/tools/tracing/rtla/tests/cleanup.t 
b/tools/tracing/rtla/tests/cleanup.t
new file mode 100644
index 000000000000..f3b495b0aee4
--- /dev/null
+++ b/tools/tracing/rtla/tests/cleanup.t
@@ -0,0 +1,71 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Verify that rtla removes the tracefs instance it created on the abnormal
+# exit paths users actually hit:
+#
+#   - SIGINT during a run            (interactive Ctrl-C)
+#   - SIGTERM during a run
+#   - rtla's own -d/--duration timer
+#   - a failing --on-threshold shell action
+#   - several interrupted runs in a row, followed by a normal run
+#
+# A leaked instance pollutes the next invocation.
+source tests/engine.sh
+test_begin
+
+set_timeout 2m
+
+#
+# These cases need to run rtla in the background and signal it, which
+# engine.sh's check() (one foreground command) cannot express.
+#
+run_cleanup_cases() {
+       local helper="$testdir/scripts/check-cleanup.sh"
+       local out rc timeout_cmd skip
+
+       ctr=$((ctr + 1))
+       [ -z "$TEST_COUNT" ] && return
+
+       if [ ! -x "$helper" ]; then
+               echo "not ok $ctr - cleanup helper is not executable"
+               return
+       fi
+
+       # check() normally does this before every test. Do the same here, both 
to
+       # leave a clean tracer state and to make a hung helper terminate.
+       reset_osnoise || {
+               echo "ok $ctr - instances cleaned up on SIGINT, timeout and 
action failure # SKIP tracefs is not writable"
+               return
+       }
+       timeout_cmd=${TIMEOUT:-timeout -v -k 15s 2m}
+
+       out=$($timeout_cmd "$helper" "$RTLA" 2>&1)
+       rc=$?
+       if [ $rc -eq 0 ]; then
+               # The helper may skip individual cases; a group-level SKIP 
means it
+               # could not run at all.
+               skip=$(printf '%s\n' "$out" | sed -n 's/^SKIP: //p' | head -n 1)
+               if [ -n "$skip" ]; then
+                       echo "ok $ctr - instances cleaned up on SIGINT, timeout 
and action failure # SKIP $skip"
+                       return
+               fi
+               printf '%s\n' "$out" | grep '^SKIP: ' | while read -r line; do
+                       echo "# ${line}"
+               done
+               echo "ok $ctr - instances cleaned up on SIGINT, timeout and 
action failure"
+       elif [ $rc -eq 124 ] || [ $rc -eq 137 ]; then
+               echo "not ok $ctr - instances cleaned up on SIGINT, timeout and 
action failure"
+               echo "# helper timed out after 2m"
+       else
+               echo "not ok $ctr - instances cleaned up on SIGINT, timeout and 
action failure"
+               echo "# helper exited $rc"
+               printf '%s\n' "$out" | while read -r line; do
+                       echo "# $line"
+               done
+       fi
+}
+
+run_cleanup_cases
+
+test_end
diff --git a/tools/tracing/rtla/tests/scripts/check-cleanup.sh 
b/tools/tracing/rtla/tests/scripts/check-cleanup.sh
new file mode 100755
index 000000000000..1b4397123708
--- /dev/null
+++ b/tools/tracing/rtla/tests/scripts/check-cleanup.sh
@@ -0,0 +1,197 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# Verify that rtla removes the tracefs instances it created, even when it is
+# interrupted (SIGINT/SIGTERM), killed by its own timeout, or when a shell
+# action fails.
+#
+# This is a *test* for tools/tracing/rtla; it does not change rtla behaviour.
+#
+# Requires: root, tracefs mounted at /sys/kernel/tracing, 
CONFIG_OSNOISE_TRACER.
+# Usage: rtla_cleanup_test.sh /path/to/rtla
+
+RTLA=${1:-rtla}
+TRACEFS=${TRACEFS:-/sys/kernel/tracing}
+INSTANCES=$TRACEFS/instances
+FAIL=0
+PASS=0
+
+log()  { printf '%s\n' "$*"; }
+pass() { PASS=$((PASS+1)); log "PASS: $*"; }
+fail() { FAIL=$((FAIL+1)); log "FAIL: $*"; }
+cleanup_all() {
+       for i in osnoise_top osnoise_hist timerlat_top timerlat_hist 
hwnoise_top; do
+               [ -d "$INSTANCES/$i" ] && rmdir "$INSTANCES/$i" 2>/dev/null
+       done
+}
+
+# Wait up to $2 ms for $1 to disappear.
+wait_gone() {
+       _path=$1
+       _ms=${2:-5000}
+       _i=0
+       while [ -d "$_path" ] && [ $_i -lt $_ms ]; do
+               sleep 0.05
+               _i=$((_i+50))
+       done
+       [ ! -d "$_path" ]
+}
+
+# Wait until $1 exists (up to $2 ms).
+wait_present() {
+       _path=$1
+       _ms=${2:-10000}
+       _i=0
+       while [ ! -d "$_path" ] && [ $_i -lt $_ms ]; do
+               sleep 0.05
+               _i=$((_i+50))
+       done
+       [ -d "$_path" ]
+}
+
+if [ ! -d "$INSTANCES" ] || [ ! -w "$INSTANCES" ] ||
+   [ ! -d "$TRACEFS/osnoise" ]; then
+       log "SKIP: tracefs, writable instances or CONFIG_OSNOISE_TRACER 
unavailable"
+       exit 0
+fi
+
+# Leave nothing behind from the test before it starts, and make sure the
+# command under test is actually usable.
+cleanup_all
+if ! "$RTLA" --version >/dev/null 2>&1; then
+       log "SKIP: $RTLA is not usable"
+       exit 0
+fi
+
+# Kill a background rtla if a case fails before reaping it.
+stop_pid() {
+       [ -n "$1" ] || return
+       kill -KILL "$1" 2>/dev/null
+       wait "$1" 2>/dev/null
+}
+
+# Wait for an rtla instance and its signal handler.  The instance appears
+# before set_signals() runs, so wait for rtla to start its workload (or at
+# least for it to survive a short grace period) before sending SIGINT.
+wait_running() {
+       _path=$1
+       _pid=$2
+       wait_present "$_path" 10000 || return 1
+       sleep 1
+       kill -0 "$_pid" 2>/dev/null
+}
+
+# ---------------------------------------------------------------------------
+# 1. SIGINT during a long run
+# ---------------------------------------------------------------------------
+cleanup_all
+$RTLA osnoise top -q -d 30s -P F:1 >/dev/null 2>&1 &
+pid=$!
+if ! wait_running "$INSTANCES/osnoise_top" "$pid"; then
+       fail "SIGINT: instance was never created (rtla did not start?)"
+       stop_pid "$pid"
+else
+       kill -INT "$pid" 2>/dev/null
+       wait "$pid" 2>/dev/null
+       if wait_gone "$INSTANCES/osnoise_top"; then
+               pass "SIGINT: instance removed after interrupt"
+       else
+               fail "SIGINT: instance still present after interrupt"
+       fi
+fi
+
+# ---------------------------------------------------------------------------
+# 2. SIGTERM during a long run
+# ---------------------------------------------------------------------------
+cleanup_all
+$RTLA osnoise top -q -d 30s -P F:1 >/dev/null 2>&1 &
+pid=$!
+if ! wait_running "$INSTANCES/osnoise_top" "$pid"; then
+       fail "SIGTERM: instance was never created (rtla did not start)"
+       stop_pid "$pid"
+else
+       kill -TERM "$pid" 2>/dev/null
+       wait "$pid" 2>/dev/null
+       if wait_gone "$INSTANCES/osnoise_top"; then
+               pass "SIGTERM: instance removed after terminate"
+       else
+               fail "SIGTERM: instance still present after terminate"
+       fi
+fi
+
+# ---------------------------------------------------------------------------
+# 3. rtla's own -d timeout path
+# ---------------------------------------------------------------------------
+cleanup_all
+$RTLA osnoise top -q -d 2s -P F:1 >/dev/null 2>&1
+rc=$?
+if [ "$rc" -ne 0 ]; then
+       fail "self-timeout: rtla failed to start or run (exit=$rc)"
+elif wait_gone "$INSTANCES/osnoise_top"; then
+       pass "self-timeout: instance removed (exit=$rc)"
+else
+       fail "self-timeout: instance still present (exit=$rc)"
+fi
+
+# ---------------------------------------------------------------------------
+# 4. shell action failure (-a / --on-threshold shell,command=...)
+# ---------------------------------------------------------------------------
+# -S is the stop-total threshold and yields exit 2 when the action fails.
+# A shell action only runs if that threshold is crossed; an idle machine may
+# never do so.  Do not call the no-threshold case a pass.
+cleanup_all
+action_rc=0
+for n in 1 2 3; do
+       $RTLA osnoise top -q -d 5s -P F:1 -r 900000 -S 1 \
+               --on-threshold 'shell,command="/bin/false"' >/dev/null 2>&1
+       rc=$?
+       action_rc=$rc
+       [ "$rc" -eq 2 ] && break
+done
+if [ "$action_rc" -eq 0 ]; then
+       log "SKIP: action-failure: stop-total threshold was not crossed"
+elif [ "$action_rc" -ne 2 ]; then
+       fail "action-failure: unexpected exit=$action_rc (wanted 2)"
+elif wait_gone "$INSTANCES/osnoise_top"; then
+       pass "action-failure: instance removed (exit=$action_rc)"
+else
+       fail "action-failure: instance still present (exit=$action_rc)"
+fi
+
+# ---------------------------------------------------------------------------
+# 5. repeated interrupted runs must not poison the next run
+# ---------------------------------------------------------------------------
+cleanup_all
+for n in 1 2 3; do
+       $RTLA osnoise top -q -d 20s -P F:1 >/dev/null 2>&1 &
+       pid=$!
+       if ! wait_running "$INSTANCES/osnoise_top" "$pid"; then
+               fail "repeated interrupts: run $n never created an instance"
+               stop_pid "$pid"
+               continue
+       fi
+       kill -INT "$pid" 2>/dev/null
+       wait "$pid" 2>/dev/null
+       if ! wait_gone "$INSTANCES/osnoise_top"; then
+               fail "repeated interrupts: run $n left an instance behind"
+               cleanup_all
+       fi
+done
+
+$RTLA osnoise top -q -d 2s -P F:1 >/dev/null 2>&1
+rc=$?
+if [ "$rc" -eq 0 ]; then
+       pass "repeated interrupts: following run still succeeds"
+else
+       fail "repeated interrupts: following run failed (exit=$rc)"
+fi
+if wait_gone "$INSTANCES/osnoise_top"; then
+       pass "repeated interrupts: no leftover instance"
+else
+       fail "repeated interrupts: leftover instance"
+fi
+
+cleanup_all
+log ""
+log "SUMMARY: $PASS passed, $FAIL failed"
+[ "$FAIL" -eq 0 ]
-- 
2.43.0


Reply via email to