summaryrefslogtreecommitdiff
path: root/tools/tracing/rtla/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tools/tracing/rtla/tests')
-rw-r--r--tools/tracing/rtla/tests/engine.sh68
-rw-r--r--tools/tracing/rtla/tests/hwnoise.t5
-rw-r--r--tools/tracing/rtla/tests/osnoise.t116
-rwxr-xr-xtools/tracing/rtla/tests/scripts/check-cgroup-match.sh17
-rwxr-xr-xtools/tracing/rtla/tests/scripts/check-cpus.sh9
-rwxr-xr-xtools/tracing/rtla/tests/scripts/check-housekeeping-cpus.sh4
-rwxr-xr-xtools/tracing/rtla/tests/scripts/check-osnoise-option.sh16
-rwxr-xr-xtools/tracing/rtla/tests/scripts/check-priority.sh8
-rwxr-xr-xtools/tracing/rtla/tests/scripts/check-tracefs-value.sh11
-rwxr-xr-xtools/tracing/rtla/tests/scripts/check-user-kernel-threads.sh16
-rw-r--r--tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh11
-rw-r--r--tools/tracing/rtla/tests/timerlat.t155
-rw-r--r--tools/tracing/rtla/tests/unit/Build8
-rw-r--r--tools/tracing/rtla/tests/unit/Makefile.unit17
-rw-r--r--tools/tracing/rtla/tests/unit/actions.c393
-rw-r--r--tools/tracing/rtla/tests/unit/cli_opt_callback.c912
-rw-r--r--tools/tracing/rtla/tests/unit/cli_params_assert.h68
-rw-r--r--tools/tracing/rtla/tests/unit/osnoise_hist_cli.c575
-rw-r--r--tools/tracing/rtla/tests/unit/osnoise_top_cli.c521
-rw-r--r--tools/tracing/rtla/tests/unit/timerlat_hist_cli.c740
-rw-r--r--tools/tracing/rtla/tests/unit/timerlat_top_cli.c672
-rw-r--r--tools/tracing/rtla/tests/unit/unit_tests.c39
-rw-r--r--tools/tracing/rtla/tests/unit/utils.c106
23 files changed, 4390 insertions, 97 deletions
diff --git a/tools/tracing/rtla/tests/engine.sh b/tools/tracing/rtla/tests/engine.sh
index ed261e07c6d9..4287cd64ac31 100644
--- a/tools/tracing/rtla/tests/engine.sh
+++ b/tools/tracing/rtla/tests/engine.sh
@@ -4,6 +4,9 @@ test_begin() {
# Count tests to allow the test harness to double-check if all were
# included correctly.
ctr=0
+ # Set test directory to the directory of the script
+ scriptfile=$(realpath "$0")
+ testdir=$(dirname "$scriptfile")
[ -z "$RTLA" ] && RTLA="./rtla"
[ -n "$TEST_COUNT" ] && echo "1..$TEST_COUNT"
}
@@ -51,6 +54,11 @@ check() {
then
# Reset osnoise options before running test.
[ "$NO_RESET_OSNOISE" == 1 ] || reset_osnoise
+
+ # Create a temporary directory to contain rtla output
+ tmpdir=$(mktemp -d)
+ pushd $tmpdir >/dev/null
+
# Run rtla; in case of failure, include its output as comment
# in the test results.
result=$(eval stdbuf -oL $TIMEOUT "$RTLA" $2 2>&1); exitcode=$?
@@ -82,34 +90,60 @@ check() {
echo "$result" | col -b | while read line; do echo "# $line"; done
printf "#\n# exit code %s\n" $exitcode
fi
+
+ # Remove temporary directory
+ popd >/dev/null
+ rm -r $tmpdir
fi
}
check_with_osnoise_options() {
- # Do the same as "check", but with pre-set osnoise options.
- # Note: rtla should reset the osnoise options, this is used to test
- # if it indeed does so.
- # Save original arguments
- arg1=$1
- arg2=$2
- arg3=$3
-
- # Apply osnoise options (if not dry run)
+ # Do the same as "check", but with pre-set tracefs options.
+ # Resets osnoise first, then writes the given tracefs option=value
+ # pairs before running the check with NO_RESET_OSNOISE=1.
+ # Arguments: test_name command exit_code expected_output [path=value ...]
+ # Each path is relative to /sys/kernel/tracing/
+ local arg1=$1
+ local arg2=$2
+ local arg3=$3
+ local arg4=$4
+ local opt option value
+
+ # Apply tracefs options (if not dry run)
if [ -n "$TEST_COUNT" ]
then
[ "$NO_RESET_OSNOISE" == 1 ] || reset_osnoise
- shift
- shift
- while shift
+ shift 4
+ for opt in "$@"
do
- [ "$1" == "" ] && continue
- option=$(echo $1 | cut -d '=' -f 1)
- value=$(echo $1 | cut -d '=' -f 2)
- echo "$value" > "/sys/kernel/tracing/osnoise/$option" || return 1
+ [ -z "$opt" ] && continue
+ option="${opt%%=*}"
+ value="${opt#*=}"
+ # Try to apply the option, ignore errors: when pre-setting fails
+ # (e.g. kernel does not know the option), the test itself will likely
+ # also fail.
+ # Throwing an error here would cause the test to be incorrectly
+ # skipped.
+ echo "$value" > "/sys/kernel/tracing/$option"
done
fi
- NO_RESET_OSNOISE=1 check "$arg1" "$arg2" "$arg3"
+ NO_RESET_OSNOISE=1 check "$arg1" "$arg2" "$arg3" "$arg4"
+}
+
+check_top_hist() {
+ # Test one command with both "top" and "hist" tools, replacing "TOOL" in
+ # command with either "top" or "hist" respectively, and prefixing the test
+ # names with "top " and "hist ".
+ check "top $1" "$(echo "$2" | sed 's/TOOL/top/g')" "${@:3}"
+ check "hist $1" "$(echo "$2" | sed 's/TOOL/hist/g')" "${@:3}"
+}
+
+check_top_q_hist() {
+ # Same as above, but pass "-q" to top so that strings printed in main
+ # loop are on their own line for top too, not only for hist.
+ check "top $1" "$(echo "$2" | sed 's/TOOL/top -q/g')" "${@:3}"
+ check "hist $1" "$(echo "$2" | sed 's/TOOL/hist/g')" "${@:3}"
}
set_timeout() {
diff --git a/tools/tracing/rtla/tests/hwnoise.t b/tools/tracing/rtla/tests/hwnoise.t
index 23ce250a6852..b53d8d440fc5 100644
--- a/tools/tracing/rtla/tests/hwnoise.t
+++ b/tools/tracing/rtla/tests/hwnoise.t
@@ -6,7 +6,7 @@ test_begin
set_timeout 2m
check "verify help page" \
- "hwnoise --help" 0 "summary of hardware-related noise"
+ "hwnoise --help" 129 "Usage: rtla hwnoise"
check "detect noise higher than one microsecond" \
"hwnoise -c 0 -T 1 -d 5s -q" 0
check "set the automatic trace mode" \
@@ -18,5 +18,8 @@ check "stop the trace if a single sample is higher than 1 us" \
check "enable a trace event trigger" \
"hwnoise -t -e osnoise:irq_noise --trigger=\"hist:key=desc,duration:sort=desc,duration:vals=hitcount\" -d 10s" \
0 "Saving event osnoise:irq_noise hist to osnoise_irq_noise_hist.txt"
+check "verify OSNOISE_IRQ_DISABLE" \
+ "hwnoise -S 1 --on-threshold shell,command=\"$testdir/scripts/check-osnoise-option.sh OSNOISE_IRQ_DISABLE\"" \
+ 2 "^OSNOISE_IRQ_DISABLE=enabled$"
test_end
diff --git a/tools/tracing/rtla/tests/osnoise.t b/tools/tracing/rtla/tests/osnoise.t
index 396334608920..214fd40fc610 100644
--- a/tools/tracing/rtla/tests/osnoise.t
+++ b/tools/tracing/rtla/tests/osnoise.t
@@ -6,45 +6,97 @@ test_begin
set_timeout 2m
check "verify help page" \
- "osnoise --help" 0 "osnoise version"
-check "verify the --priority/-P param" \
- "osnoise top -P F:1 -c 0 -r 900000 -d 10s -q -S 1 --on-threshold shell,command=\"tests/scripts/check-priority.sh osnoise/ SCHED_FIFO 1\"" \
+ "osnoise --help" 129 "osnoise version"
+check_top_hist "verify help page" \
+ "osnoise TOOL --help" 129 "rtla osnoise"
+check_top_q_hist "verify the --stop/-s param" \
+ "osnoise TOOL -s 30 -T 1" 2 "osnoise hit stop tracing"
+check_top_q_hist "verify the --trace param" \
+ "osnoise TOOL -s 30 -T 1 -t" 2 "Saving trace to osnoise_trace.txt"
+
+# Thread tests
+check_top_q_hist "verify the --priority/-P param" \
+ "osnoise TOOL -P F:1 -c 0 -r 900000 -d 10s -S 1 --on-threshold shell,command=\"$testdir/scripts/check-priority.sh SCHED_FIFO 1\"" \
2 "Priorities are set correctly"
-check "verify the --stop/-s param" \
- "osnoise top -s 30 -T 1" 2 "osnoise hit stop tracing"
-check "verify the --trace param" \
- "osnoise hist -s 30 -T 1 -t" 2 "Saving trace to osnoise_trace.txt"
-check "verify the --entries/-E param" \
- "osnoise hist -P F:1 -c 0 -r 900000 -d 10s -b 10 -E 25"
-
-# Test setting default period by putting an absurdly high period
-# and stopping on threshold.
-# If default period is not set, this will time out.
+check_top_q_hist "verify the -C/--cgroup param" \
+ "osnoise TOOL -C -c 0 -r 900000 -d 10s -S 1 --on-threshold shell,command=\"$testdir/scripts/check-cgroup-match.sh\"" \
+ 2 "cgroup matches for all workload PIDs"
+check_top_q_hist "verify the -c/--cpus param" \
+ "osnoise TOOL -P F:1 -c 0 -r 900000 -d 10s -S 1 --on-threshold shell,command=$testdir/scripts/check-cpus.sh" 2 "^Affinity of threads: 0$"
+check_top_q_hist "verify the -H/--house-keeping param" \
+ "osnoise TOOL -P F:1 -H 0 -r 900000 -d 10s -S 1 --on-threshold shell,command=$testdir/scripts/check-housekeeping-cpus.sh" 2 "^Affinity of threads: 0$"
+
+# Histogram tests
+check "hist with -b/--bucket-size" \
+ "osnoise hist -b 1 -d 1s"
+check "hist with -E/--entries" \
+ "osnoise hist -E 10 -d 1s"
+check "hist with -E/--entries out of range" \
+ "osnoise hist -E 1 -d 1s" 129 "out of range \[10, 9999999\]"
+check "hist with --no-header" \
+ "osnoise hist --no-header -d 1s" 0 "" "RTLA osnoise histogram"
+check "hist with --with-zeros" \
+ "osnoise hist --with-zeros -b 100000 -E 21 -d 1s" 0 '^2000000\s+0\s+'
+check "hist with --no-index" \
+ "osnoise hist --no-index --with-zeros -d 1s" 0 "" "^count:"
+check "hist with --no-summary" \
+ "osnoise hist --no-summary -d 1s" 0 "" "^count:"
+
+# Tracer option tests - verify that rtla correctly sets tracefs options
+# Default tests: poison tracefs with wrong values, verify rtla resets to defaults
check_with_osnoise_options "apply default period" \
- "osnoise hist -s 1" 2 period_us=600000000
+ "osnoise top -q -S 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/period_us\"" \
+ 2 "^osnoise/period_us=1000000$" osnoise/period_us=600000000
+check_with_osnoise_options "apply default runtime" \
+ "osnoise top -q -S 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/runtime_us\"" \
+ 2 "^osnoise/runtime_us=1000000$" osnoise/runtime_us=100
+check_with_osnoise_options "apply default tracing_thresh" \
+ "osnoise top -q -S 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh tracing_thresh\"" \
+ 2 "^tracing_thresh=0$" tracing_thresh=999999
+check_with_osnoise_options "apply default stop_tracing_us" \
+ "osnoise top -q -S 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/stop_tracing_us\"" \
+ 2 "^osnoise/stop_tracing_us=0$" osnoise/stop_tracing_us=999999
+check_with_osnoise_options "apply default stop_tracing_total_us" \
+ "osnoise top -q -s 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/stop_tracing_total_us\"" \
+ 2 "^osnoise/stop_tracing_total_us=0$" osnoise/stop_tracing_total_us=999999
+
+# Non-default tracer option tests: verify CLI options correctly set tracefs values
+check_top_q_hist "verify -p sets period_us" \
+ "osnoise TOOL -p 2000000 -S 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/period_us\"" \
+ 2 "^osnoise/period_us=2000000$"
+check_top_q_hist "verify -r sets runtime_us" \
+ "osnoise TOOL -r 500000 -S 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/runtime_us\"" \
+ 2 "^osnoise/runtime_us=500000$"
+check_top_q_hist "verify -T sets tracing_thresh" \
+ "osnoise TOOL -T 5 -S 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh tracing_thresh\"" \
+ 2 "^tracing_thresh=5$"
+check_top_q_hist "verify -s sets stop_tracing_us" \
+ "osnoise TOOL -s 30 -S 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/stop_tracing_us\"" \
+ 2 "^osnoise/stop_tracing_us=30$"
+check_top_q_hist "verify -S sets stop_tracing_total_us" \
+ "osnoise TOOL -S 100 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/stop_tracing_total_us\"" \
+ 2 "^osnoise/stop_tracing_total_us=100$"
# Actions tests
-check "trace output through -t with custom filename" \
- "osnoise hist -S 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
-check "trace output through --on-threshold trace" \
- "osnoise hist -S 2 --on-threshold trace" 2 "^ Saving trace to osnoise_trace.txt$"
-check "trace output through --on-threshold trace with custom filename" \
- "osnoise hist -S 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
-check "exec command" \
- "osnoise hist -S 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$"
-check "multiple actions" \
- "osnoise hist -S 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$"
+check_top_q_hist "trace output through -t with custom filename" \
+ "osnoise TOOL -S 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
+check_top_q_hist "trace output through --on-threshold trace" \
+ "osnoise TOOL -S 2 --on-threshold trace" 2 "^ Saving trace to osnoise_trace.txt$"
+check_top_q_hist "trace output through --on-threshold trace with custom filename" \
+ "osnoise TOOL -S 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
+check_top_q_hist "exec command" \
+ "osnoise TOOL -S 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$"
+check_top_q_hist "multiple actions" \
+ "osnoise TOOL -S 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$"
check "hist stop at failed action" \
"osnoise hist -S 2 --on-threshold shell,command='echo -n 1; false' --on-threshold shell,command='echo -n 2'" 2 "^1# RTLA osnoise histogram$"
check "top stop at failed action" \
"osnoise top -S 2 --on-threshold shell,command='echo -n abc; false' --on-threshold shell,command='echo -n defgh'" 2 "^abc" "defgh"
-check "hist with continue" \
- "osnoise hist -S 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
-check "top with continue" \
- "osnoise top -q -S 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
-check "hist with trace output at end" \
- "osnoise hist -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$"
-check "top with trace output at end" \
- "osnoise top -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$"
+check_top_q_hist "with continue" \
+ "osnoise TOOL -S 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
+check_top_q_hist "with conditional continue" \
+ "osnoise TOOL -S 2 --on-threshold shell,command='if [ -f a ]; then echo 2; exit 1; else echo -n 1; touch a; fi' --on-threshold continue" 2 "^12$" "^2$"
+check_top_hist "with trace output at end" \
+ "osnoise TOOL -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$"
test_end
diff --git a/tools/tracing/rtla/tests/scripts/check-cgroup-match.sh b/tools/tracing/rtla/tests/scripts/check-cgroup-match.sh
new file mode 100755
index 000000000000..fdc2c68c5957
--- /dev/null
+++ b/tools/tracing/rtla/tests/scripts/check-cgroup-match.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+. "$(dirname $0)/lib/get_workload_pids.sh"
+rtla_pid=$(echo $(ps -o ppid= $$))
+rtla_cgroup=$(</proc/$rtla_pid/cgroup)
+echo "RTLA cgroup: $rtla_cgroup"
+for pid in $(get_workload_pids)
+do
+ pid_cgroup=$(</proc/$pid/cgroup)
+ echo "PID $pid cgroup: $pid_cgroup"
+ if ! [ "$pid_cgroup" = "$rtla_cgroup" ]
+ then
+ echo "Mismatch!"
+ exit 0
+ fi
+done
+echo "cgroup matches for all workload PIDs"
diff --git a/tools/tracing/rtla/tests/scripts/check-cpus.sh b/tools/tracing/rtla/tests/scripts/check-cpus.sh
new file mode 100755
index 000000000000..0b016d4a7945
--- /dev/null
+++ b/tools/tracing/rtla/tests/scripts/check-cpus.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+. "$(dirname $0)/lib/get_workload_pids.sh"
+echo -n "Affinity of threads: "
+for pid in $(get_workload_pids)
+do
+ echo -n $(taskset -c -p $pid | cut -d ':' -f 2)
+done
+echo
diff --git a/tools/tracing/rtla/tests/scripts/check-housekeeping-cpus.sh b/tools/tracing/rtla/tests/scripts/check-housekeeping-cpus.sh
new file mode 100755
index 000000000000..4742f34efb49
--- /dev/null
+++ b/tools/tracing/rtla/tests/scripts/check-housekeeping-cpus.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+pid=$(ps -o ppid= $$)
+echo "Affinity of threads:$(taskset -c -p $pid | cut -d ':' -f 2)"
diff --git a/tools/tracing/rtla/tests/scripts/check-osnoise-option.sh b/tools/tracing/rtla/tests/scripts/check-osnoise-option.sh
new file mode 100755
index 000000000000..37c62268cc15
--- /dev/null
+++ b/tools/tracing/rtla/tests/scripts/check-osnoise-option.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Check if osnoise options are enabled or disabled.
+# Usage: check-osnoise-option.sh <OPTION1> [<OPTION2> ...]
+# Output: one line per option in the format "OPTION=enabled" or "OPTION=disabled"
+
+options=$(tr ' ' '\n' < /sys/kernel/tracing/osnoise/options)
+for name in "$@"; do
+ if echo "$options" | grep -q "^NO_${name}$"; then
+ echo "$name=disabled"
+ elif echo "$options" | grep -q "^${name}$"; then
+ echo "$name=enabled"
+ else
+ echo "$name=unsupported"
+ fi
+done
diff --git a/tools/tracing/rtla/tests/scripts/check-priority.sh b/tools/tracing/rtla/tests/scripts/check-priority.sh
index 79b702a34a96..b51d5232a868 100755
--- a/tools/tracing/rtla/tests/scripts/check-priority.sh
+++ b/tools/tracing/rtla/tests/scripts/check-priority.sh
@@ -1,8 +1,8 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
-pids="$(pgrep ^$1)" || exit 1
-for pid in $pids
+. "$(dirname $0)/lib/get_workload_pids.sh"
+for pid in $(get_workload_pids)
do
- chrt -p $pid | cut -d ':' -f 2 | head -n1 | grep "^ $2\$" >/dev/null
- chrt -p $pid | cut -d ':' -f 2 | tail -n1 | grep "^ $3\$" >/dev/null
+ chrt -p $pid | cut -d ':' -f 2 | head -n1 | grep "^ $1\$" >/dev/null
+ chrt -p $pid | cut -d ':' -f 2 | tail -n1 | grep "^ $2\$" >/dev/null
done && echo "Priorities are set correctly"
diff --git a/tools/tracing/rtla/tests/scripts/check-tracefs-value.sh b/tools/tracing/rtla/tests/scripts/check-tracefs-value.sh
new file mode 100755
index 000000000000..12d0eacd6a85
--- /dev/null
+++ b/tools/tracing/rtla/tests/scripts/check-tracefs-value.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Read tracefs values and print them.
+# Usage: check-tracefs-value.sh <relative_path1> [<relative_path2> ...]
+# Each path is relative to /sys/kernel/tracing/
+# Output: one line per file in the format "path=value"
+
+for file in "$@"; do
+ read value < "/sys/kernel/tracing/$file"
+ echo "$file=$value"
+done
diff --git a/tools/tracing/rtla/tests/scripts/check-user-kernel-threads.sh b/tools/tracing/rtla/tests/scripts/check-user-kernel-threads.sh
new file mode 100755
index 000000000000..bb7ac510a735
--- /dev/null
+++ b/tools/tracing/rtla/tests/scripts/check-user-kernel-threads.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+. "$(dirname $0)/lib/get_workload_pids.sh"
+kthreadd_pid=$(pgrep ^kthreadd$)
+cnt_kernel=0
+cnt_user=0
+for pid in $(get_workload_pids)
+do
+ if [ "$(echo $(ps -o ppid= $pid))" = "$kthreadd_pid" ]
+ then
+ ((++cnt_kernel))
+ else
+ ((++cnt_user))
+ fi
+done
+echo "$cnt_kernel kernel threads, $cnt_user user threads"
diff --git a/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh b/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh
new file mode 100644
index 000000000000..d10a4e3b321d
--- /dev/null
+++ b/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+get_workload_pids() {
+ local shell_pid=$$
+ local rtla_pid=$(ps -o ppid= $shell_pid)
+
+ # kernel threads
+ pgrep -P $(pgrep ^kthreadd$) -f '^\[?(osnoise|timerlat)/[0-9]+\]?$'
+ # user threads
+ pgrep -P $rtla_pid | grep -v "^$shell_pid$"
+}
diff --git a/tools/tracing/rtla/tests/timerlat.t b/tools/tracing/rtla/tests/timerlat.t
index fd4935fd7b49..5116857d7da4 100644
--- a/tools/tracing/rtla/tests/timerlat.t
+++ b/tools/tracing/rtla/tests/timerlat.t
@@ -21,65 +21,134 @@ export RTLA_NO_BPF=$option
# Basic tests
check "verify help page" \
- "timerlat --help" 0 "timerlat version"
-check "verify -s/--stack" \
- "timerlat top -s 3 -T 10 -t" 2 "Blocking thread stack trace"
-check "verify -P/--priority" \
- "timerlat top -P F:1 -c 0 -d 10s -q -T 1 --on-threshold shell,command=\"tests/scripts/check-priority.sh timerlatu/ SCHED_FIFO 1\"" \
+ "timerlat --help" 129 "timerlat version"
+check_top_hist "verify help page" \
+ "timerlat TOOL --help" 129 "rtla timerlat"
+check_top_hist "verify -s/--stack" \
+ "timerlat TOOL -s 3 -T 10 -t" 2 "Blocking thread stack trace"
+check_top_hist "test in nanoseconds" \
+ "timerlat TOOL -i 2 -c 0 -n -d 10s" 2 "ns"
+check_top_hist "set the automatic trace mode" \
+ "timerlat TOOL -a 5" 2 "analyzing it"
+check_top_hist "dump tasks" \
+ "timerlat TOOL -a 5 --dump-tasks" 2 "Printing CPU tasks"
+check "verify --aa-only stop on threshold" \
+ "timerlat top --aa-only 5" 2 "analyzing it" "Timer Latency"
+check "verify --aa-only max latency" \
+ "timerlat top --aa-only 2000000 -d 1s" 0 "^ Max latency was" "Timer Latency"
+check_top_hist "disable auto-analysis" \
+ "timerlat TOOL -s 3 -T 10 -t --no-aa" 2 "" "analyzing it"
+
+# Thread tests
+check_top_hist "verify -P/--priority" \
+ "timerlat TOOL -P F:1 -c 0 -d 10s -T 1 --on-threshold shell,command=\"$testdir/scripts/check-priority.sh SCHED_FIFO 1\"" \
2 "Priorities are set correctly"
-check "test in nanoseconds" \
- "timerlat top -i 2 -c 0 -n -d 10s" 2 "ns"
-check "set the automatic trace mode" \
- "timerlat top -a 5" 2 "analyzing it"
-check "dump tasks" \
- "timerlat top -a 5 --dump-tasks" 2 "Printing CPU tasks"
-check "print the auto-analysis if hits the stop tracing condition" \
- "timerlat top --aa-only 5" 2
-check "disable auto-analysis" \
- "timerlat top -s 3 -T 10 -t --no-aa" 2
-check "verify -c/--cpus" \
- "timerlat hist -c 0 -d 10s"
-check "hist test in nanoseconds" \
- "timerlat hist -i 2 -c 0 -n -d 10s" 2 "ns"
+check_top_hist "verify -C/--cgroup" \
+ "timerlat TOOL -k -C -c 0 -d 10s -T 1 --on-threshold shell,command=\"$testdir/scripts/check-cgroup-match.sh\"" \
+ 2 "cgroup matches for all workload PIDs"
+check_top_q_hist "verify -c/--cpus" \
+ "timerlat TOOL -c 0 -d 10s -T 1 --on-threshold shell,command=$testdir/scripts/check-cpus.sh" 2 "^Affinity of threads: 0$"
+check_top_q_hist "verify -H/--house-keeping" \
+ "timerlat TOOL -H 0 -d 10s -T 1 --on-threshold shell,command=$testdir/scripts/check-housekeeping-cpus.sh" 2 "^Affinity of threads: 0$"
+check_top_q_hist "verify -k/--kernel-threads" \
+ "timerlat TOOL -k -c 0 -d 10s -T 1 --on-threshold shell,command=$testdir/scripts/check-user-kernel-threads.sh" 2 "1 kernel threads, 0 user threads"
+check_top_q_hist "verify -u/--user-threads" \
+ "timerlat TOOL -u -c 0 -d 10s -T 1 --on-threshold shell,command=$testdir/scripts/check-user-kernel-threads.sh" 2 "0 kernel threads, 1 user threads"
+
+# Tracer option tests - verify that rtla correctly sets tracefs options
+# Default tests: poison tracefs with wrong values, verify rtla resets to defaults
+check_with_osnoise_options "apply default timerlat_period_us" \
+ "timerlat top -q -T 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/timerlat_period_us\"" \
+ 2 "^osnoise/timerlat_period_us=1000$" osnoise/timerlat_period_us=999999
+check_with_osnoise_options "apply default print_stack" \
+ "timerlat top -q -T 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/print_stack\"" \
+ 2 "^osnoise/print_stack=0$" osnoise/print_stack=999999
+check_with_osnoise_options "apply default stop_tracing_us" \
+ "timerlat top -q -T 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/stop_tracing_us\"" \
+ 2 "^osnoise/stop_tracing_us=0$" osnoise/stop_tracing_us=999999
+check_with_osnoise_options "apply default stop_tracing_total_us" \
+ "timerlat top -q -i 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/stop_tracing_total_us\"" \
+ 2 "^osnoise/stop_tracing_total_us=0$" osnoise/stop_tracing_total_us=999999
+
+# Non-default tracer option tests: verify CLI options correctly set tracefs values
+check_top_q_hist "verify -p sets timerlat_period_us" \
+ "timerlat TOOL -p 2000 -T 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/timerlat_period_us\"" \
+ 2 "^osnoise/timerlat_period_us=2000$"
+check_top_q_hist "verify -s sets print_stack" \
+ "timerlat TOOL -s 5 -T 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/print_stack\"" \
+ 2 "^osnoise/print_stack=5$"
+check_top_q_hist "verify -i sets stop_tracing_us" \
+ "timerlat TOOL -i 2 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/stop_tracing_us\"" \
+ 2 "^osnoise/stop_tracing_us=2$"
+check_top_q_hist "verify -T sets stop_tracing_total_us" \
+ "timerlat TOOL -T 2 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/stop_tracing_total_us\"" \
+ 2 "^osnoise/stop_tracing_total_us=2$"
+check_with_osnoise_options "apply default TIMERLAT_ALIGN" \
+ "timerlat top -q -T 1 --on-threshold shell,command=\"$testdir/scripts/check-osnoise-option.sh TIMERLAT_ALIGN\"" \
+ 2 "^TIMERLAT_ALIGN=disabled$" osnoise/options=TIMERLAT_ALIGN
+check_top_q_hist "verify -A sets TIMERLAT_ALIGN" \
+ "timerlat TOOL -A 100 -T 1 --on-threshold shell,command=\"$testdir/scripts/check-osnoise-option.sh TIMERLAT_ALIGN\"" \
+ 2 "^TIMERLAT_ALIGN=enabled$"
+check_top_q_hist "verify -A sets timerlat_align_us" \
+ "timerlat TOOL -A 100 -T 1 --on-threshold shell,command=\"$testdir/scripts/check-tracefs-value.sh osnoise/timerlat_align_us\"" \
+ 2 "^osnoise/timerlat_align_us=100$"
+
+# Histogram tests
+check "hist with -b/--bucket-size" \
+ "timerlat hist -b 1 -d 1s"
+check "hist with -E/--entries" \
+ "timerlat hist -E 10 -d 1s"
+check "hist with -E/--entries out of range" \
+ "timerlat hist -E 1 -d 1s" 129 "out of range \[10, 9999999\]"
+check "hist with --no-header" \
+ "timerlat hist --no-header -d 1s" 0 "" "RTLA timerlat histogram"
+check "hist with --with-zeros" \
+ "timerlat hist --with-zeros -b 100000 -E 21 -d 1s" 0 '^2000000\s+0\s+'
+check "hist with --no-index" \
+ "timerlat hist --no-index --with-zeros -d 1s" 0 "" "^count:"
+check "hist with --no-summary" \
+ "timerlat hist --no-summary -d 1s" 0 "" "^ALL:"
+check "hist with --no-irq" \
+ "timerlat hist --no-irq -d 1s" 0 "" "IRQ-"
+check "hist with --no-thread" \
+ "timerlat hist --no-thread -d 1s" 0 "" "Thr-"
# Actions tests
-check "trace output through -t" \
- "timerlat hist -T 2 -t" 2 "^ Saving trace to timerlat_trace.txt$"
-check "trace output through -t with custom filename" \
- "timerlat hist -T 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
-check "trace output through --on-threshold trace" \
- "timerlat hist -T 2 --on-threshold trace" 2 "^ Saving trace to timerlat_trace.txt$"
-check "trace output through --on-threshold trace with custom filename" \
- "timerlat hist -T 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
-check "exec command" \
- "timerlat hist -T 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$"
-check "multiple actions" \
- "timerlat hist -T 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$"
+check_top_q_hist "trace output through -t" \
+ "timerlat TOOL -T 2 -t" 2 "^ Saving trace to timerlat_trace.txt$"
+check_top_q_hist "trace output through -t with custom filename" \
+ "timerlat TOOL -T 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
+check_top_q_hist "trace output through --on-threshold trace" \
+ "timerlat TOOL -T 2 --on-threshold trace" 2 "^ Saving trace to timerlat_trace.txt$"
+check_top_q_hist "trace output through --on-threshold trace with custom filename" \
+ "timerlat TOOL -T 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$"
+check_top_q_hist "exec command" \
+ "timerlat TOOL -T 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$"
+check_top_q_hist "multiple actions" \
+ "timerlat TOOL -T 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$"
check "hist stop at failed action" \
"timerlat hist -T 2 --on-threshold shell,command='echo -n 1; false' --on-threshold shell,command='echo -n 2'" 2 "^1# RTLA timerlat histogram$"
check "top stop at failed action" \
"timerlat top -T 2 --on-threshold shell,command='echo -n abc; false' --on-threshold shell,command='echo -n defgh'" 2 "^abc" "defgh"
-check "hist with continue" \
- "timerlat hist -T 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
-check "top with continue" \
- "timerlat top -q -T 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
-check "hist with trace output at end" \
- "timerlat hist -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$"
-check "top with trace output at end" \
- "timerlat top -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$"
+check_top_q_hist "with continue" \
+ "timerlat TOOL -T 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$"
+check_top_q_hist "with conditional continue" \
+ "timerlat TOOL -T 2 --on-threshold shell,command='if [ -f a ]; then echo 2; exit 1; else echo -n 1; touch a; fi' --on-threshold continue" 2 "^12$" "^2$"
+check_top_hist "with trace output at end" \
+ "timerlat TOOL -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$"
# BPF action program tests
if [ "$option" -eq 0 ]
then
# Test BPF action program properly in BPF mode
[ -z "$BPFTOOL" ] && BPFTOOL=bpftool
- check "hist with BPF action program (BPF mode)" \
- "timerlat hist -T 2 --bpf-action tests/bpf/bpf_action_map.o --on-threshold shell,command='$BPFTOOL map dump name rtla_test_map'" \
+ check_top_q_hist "with BPF action program (BPF mode)" \
+ "timerlat TOOL -T 2 --bpf-action $testdir/bpf/bpf_action_map.o --on-threshold shell,command='$BPFTOOL map dump name rtla_test_map'" \
2 '"value": 42'
else
# Test BPF action program failure in non-BPF mode
- check "hist with BPF action program (non-BPF mode)" \
- "timerlat hist -T 2 --bpf-action tests/bpf/bpf_action_map.o" \
+ check_top_q_hist "with BPF action program (non-BPF mode)" \
+ "timerlat TOOL -T 2 --bpf-action $testdir/bpf/bpf_action_map.o" \
1 "BPF actions are not supported in tracefs-only mode"
fi
done
diff --git a/tools/tracing/rtla/tests/unit/Build b/tools/tracing/rtla/tests/unit/Build
new file mode 100644
index 000000000000..d5a0f13922be
--- /dev/null
+++ b/tools/tracing/rtla/tests/unit/Build
@@ -0,0 +1,8 @@
+unit_tests-y += utils.o
+unit_tests-y += actions.o
+unit_tests-y += unit_tests.o
+unit_tests-y += osnoise_top_cli.o
+unit_tests-y += osnoise_hist_cli.o
+unit_tests-y += timerlat_top_cli.o
+unit_tests-y += timerlat_hist_cli.o
+unit_tests-y += cli_opt_callback.o
diff --git a/tools/tracing/rtla/tests/unit/Makefile.unit b/tools/tracing/rtla/tests/unit/Makefile.unit
new file mode 100644
index 000000000000..839abda64b76
--- /dev/null
+++ b/tools/tracing/rtla/tests/unit/Makefile.unit
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+UNIT_TESTS := $(OUTPUT)unit_tests
+UNIT_TESTS_IN := $(UNIT_TESTS)-in.o
+
+$(UNIT_TESTS): $(UNIT_TESTS_IN) $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R)
+ $(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $^ $(EXTLIBS) -lcheck
+
+$(UNIT_TESTS_IN): fixdep $(LIBSUBCMD_INCLUDES)
+ make $(build)=unit_tests
+
+unit-tests: FORCE
+ $(Q)if [ "$(feature-libcheck)" = "1" ]; then \
+ $(MAKE) $(UNIT_TESTS) && $(UNIT_TESTS); \
+ else \
+ echo "libcheck is missing, skipping unit tests. Please install check-devel/check"; \
+ fi
diff --git a/tools/tracing/rtla/tests/unit/actions.c b/tools/tracing/rtla/tests/unit/actions.c
new file mode 100644
index 000000000000..94ad5ad42774
--- /dev/null
+++ b/tools/tracing/rtla/tests/unit/actions.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <check.h>
+#include <signal.h>
+
+#include "../../src/actions.h"
+
+static struct actions actions_fixture;
+
+static void actions_fixture_setup(void)
+{
+ actions_init(&actions_fixture);
+}
+
+static void actions_fixture_teardown(void)
+{
+ actions_destroy(&actions_fixture);
+}
+
+START_TEST(test_actions_init)
+{
+ struct actions actions;
+
+ actions_init(&actions);
+
+ ck_assert_int_eq(actions.len, 0);
+ ck_assert_int_eq(actions.size, action_default_size);
+ ck_assert(!actions.continue_flag);
+ ck_assert_ptr_eq(actions.trace_output_inst, NULL);
+}
+END_TEST
+
+START_TEST(test_actions_destroy)
+{
+ struct actions actions;
+
+ actions_init(&actions);
+ actions_destroy(&actions);
+}
+END_TEST
+
+START_TEST(test_actions_reallocate)
+{
+ struct actions actions;
+ int i;
+
+ actions_init(&actions);
+
+ ck_assert_int_eq(actions.len, 0);
+ ck_assert_int_eq(actions.size, action_default_size);
+
+ /* Fill size of actions array */
+ for (i = 0; i < action_default_size; i++)
+ actions_add_continue(&actions);
+
+ ck_assert_int_eq(actions.len, action_default_size);
+ ck_assert_int_eq(actions.size, action_default_size);
+
+ /* Add one more action to trigger reallocation */
+ actions_add_continue(&actions);
+
+ ck_assert_int_eq(actions.len, action_default_size + 1);
+ ck_assert_int_eq(actions.size, action_default_size * 2);
+
+ actions_destroy(&actions);
+}
+END_TEST
+
+START_TEST(test_actions_add_trace_output)
+{
+ actions_add_trace_output(&actions_fixture, "trace_output.txt");
+
+ ck_assert_int_eq(actions_fixture.len, 1);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT);
+ ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace_output.txt");
+ ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]);
+}
+END_TEST
+
+START_TEST(test_actions_add_signal)
+{
+ actions_add_signal(&actions_fixture, SIGINT, 1234);
+
+ ck_assert_int_eq(actions_fixture.len, 1);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SIGNAL);
+ ck_assert_int_eq(actions_fixture.list[0].signal, SIGINT);
+ ck_assert_int_eq(actions_fixture.list[0].pid, 1234);
+ ck_assert(actions_fixture.present[ACTION_SIGNAL]);
+}
+END_TEST
+
+START_TEST(test_actions_add_shell)
+{
+ actions_add_shell(&actions_fixture, "echo Hello");
+
+ ck_assert_int_eq(actions_fixture.len, 1);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SHELL);
+ ck_assert_str_eq(actions_fixture.list[0].command, "echo Hello");
+ ck_assert(actions_fixture.present[ACTION_SHELL]);
+}
+END_TEST
+
+START_TEST(test_actions_add_continue)
+{
+ actions_add_continue(&actions_fixture);
+
+ ck_assert_int_eq(actions_fixture.len, 1);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_CONTINUE);
+ ck_assert(actions_fixture.present[ACTION_CONTINUE]);
+}
+END_TEST
+
+START_TEST(test_actions_add_multiple_same_action)
+{
+ actions_add_trace_output(&actions_fixture, "trace1.txt");
+ actions_add_trace_output(&actions_fixture, "trace2.txt");
+
+ ck_assert_int_eq(actions_fixture.len, 2);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT);
+ ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace1.txt");
+ ck_assert_int_eq(actions_fixture.list[1].type, ACTION_TRACE_OUTPUT);
+ ck_assert_str_eq(actions_fixture.list[1].trace_output, "trace2.txt");
+ ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]);
+}
+END_TEST
+
+START_TEST(test_actions_add_multiple_different_action)
+{
+ actions_add_trace_output(&actions_fixture, "trace_output.txt");
+ actions_add_signal(&actions_fixture, SIGINT, 1234);
+
+ ck_assert_int_eq(actions_fixture.len, 2);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT);
+ ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace_output.txt");
+ ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]);
+ ck_assert_int_eq(actions_fixture.list[1].type, ACTION_SIGNAL);
+ ck_assert_int_eq(actions_fixture.list[1].signal, SIGINT);
+ ck_assert_int_eq(actions_fixture.list[1].pid, 1234);
+ ck_assert(actions_fixture.present[ACTION_SIGNAL]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_trace_output)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "trace", "trace.txt"), 0);
+
+ ck_assert_int_eq(actions_fixture.len, 1);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT);
+ ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace.txt");
+ ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_trace_output_arg)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "trace,file=trace2.txt", "trace1.txt"), 0);
+
+ ck_assert_int_eq(actions_fixture.len, 1);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT);
+ ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace2.txt");
+ ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_trace_output_arg_bad)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "trace,foo=bar", "trace_output.txt"), -1);
+
+ ck_assert_int_eq(actions_fixture.len, 0);
+ ck_assert(!actions_fixture.present[ACTION_TRACE_OUTPUT]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_signal)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "signal,num=1,pid=1234", NULL), 0);
+
+ ck_assert_int_eq(actions_fixture.len, 1);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SIGNAL);
+ ck_assert_int_eq(actions_fixture.list[0].signal, 1);
+ ck_assert_int_eq(actions_fixture.list[0].pid, 1234);
+ ck_assert(actions_fixture.present[ACTION_SIGNAL]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_signal_swapped)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "signal,pid=1234,num=1", NULL), 0);
+
+ ck_assert_int_eq(actions_fixture.len, 1);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SIGNAL);
+ ck_assert_int_eq(actions_fixture.list[0].signal, 1);
+ ck_assert_int_eq(actions_fixture.list[0].pid, 1234);
+ ck_assert(actions_fixture.present[ACTION_SIGNAL]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_signal_parent)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "signal,pid=parent,num=1", NULL), 0);
+
+ ck_assert_int_eq(actions_fixture.len, 1);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SIGNAL);
+ ck_assert_int_eq(actions_fixture.list[0].signal, 1);
+ ck_assert_int_eq(actions_fixture.list[0].pid, -1);
+ ck_assert(actions_fixture.present[ACTION_SIGNAL]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_signal_no_arg)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "signal", NULL), -1);
+
+ ck_assert_int_eq(actions_fixture.len, 0);
+ ck_assert(!actions_fixture.present[ACTION_SIGNAL]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_signal_no_pid)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "signal,num=1", NULL), -1);
+
+ ck_assert_int_eq(actions_fixture.len, 0);
+ ck_assert(!actions_fixture.present[ACTION_SIGNAL]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_signal_no_num)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "signal,pid=1234", NULL), -1);
+
+ ck_assert_int_eq(actions_fixture.len, 0);
+ ck_assert(!actions_fixture.present[ACTION_SIGNAL]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_signal_arg_bad)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "signal,foo=bar", NULL), -1);
+
+ ck_assert_int_eq(actions_fixture.len, 0);
+ ck_assert(!actions_fixture.present[ACTION_SIGNAL]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_shell)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "shell,command=echo Hello", NULL), 0);
+
+ ck_assert_int_eq(actions_fixture.len, 1);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SHELL);
+ ck_assert_str_eq(actions_fixture.list[0].command, "echo Hello");
+ ck_assert(actions_fixture.present[ACTION_SHELL]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_shell_no_arg)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "shell", NULL), -1);
+
+ ck_assert_int_eq(actions_fixture.len, 0);
+ ck_assert(!actions_fixture.present[ACTION_SHELL]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_shell_arg_bad)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "shell,foo=bar", NULL), -1);
+ ck_assert_int_eq(actions_fixture.len, 0);
+ ck_assert(!actions_fixture.present[ACTION_SHELL]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_continue)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "continue", NULL), 0);
+
+ ck_assert_int_eq(actions_fixture.len, 1);
+ ck_assert_int_eq(actions_fixture.list[0].type, ACTION_CONTINUE);
+ ck_assert(actions_fixture.present[ACTION_CONTINUE]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_continue_arg_bad)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "continue,foo=bar", NULL), -1);
+
+ ck_assert_int_eq(actions_fixture.len, 0);
+ ck_assert(!actions_fixture.present[ACTION_CONTINUE]);
+}
+END_TEST
+
+START_TEST(test_actions_parse_invalid)
+{
+ ck_assert_int_eq(actions_parse(&actions_fixture, "foobar", NULL), -1);
+
+ ck_assert_int_eq(actions_fixture.len, 0);
+}
+END_TEST
+
+START_TEST(test_actions_perform_continue)
+{
+ actions_add_continue(&actions_fixture);
+ ck_assert_int_eq(actions_perform(&actions_fixture), 0);
+
+ ck_assert(actions_fixture.continue_flag);
+}
+END_TEST
+
+START_TEST(test_actions_perform_continue_after_successful_shell_command)
+{
+ actions_add_shell(&actions_fixture, "exit 0");
+ actions_add_continue(&actions_fixture);
+ ck_assert_int_eq(actions_perform(&actions_fixture), 0 << 8);
+
+ ck_assert(actions_fixture.continue_flag);
+}
+END_TEST
+
+START_TEST(test_actions_perform_continue_after_failed_shell_command)
+{
+ actions_add_shell(&actions_fixture, "exit 1");
+ actions_add_continue(&actions_fixture);
+ ck_assert_int_eq(actions_perform(&actions_fixture), 1 << 8);
+
+ ck_assert(!actions_fixture.continue_flag);
+}
+END_TEST
+
+START_TEST(test_actions_perform_continue_unset_flag)
+{
+ actions_fixture.continue_flag = true;
+
+ actions_add_shell(&actions_fixture, "exit 1");
+ actions_add_continue(&actions_fixture);
+ ck_assert_int_eq(actions_perform(&actions_fixture), 1 << 8);
+
+ ck_assert(!actions_fixture.continue_flag);
+}
+END_TEST
+
+Suite *actions_suite(void)
+{
+ Suite *s = suite_create("actions");
+ TCase *tc;
+
+ tc = tcase_create("alloc");
+ tcase_add_test(tc, test_actions_init);
+ tcase_add_test(tc, test_actions_destroy);
+ tcase_add_test(tc, test_actions_reallocate);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("add");
+ tcase_add_checked_fixture(tc, actions_fixture_setup, actions_fixture_teardown);
+ tcase_add_test(tc, test_actions_add_trace_output);
+ tcase_add_test(tc, test_actions_add_signal);
+ tcase_add_test(tc, test_actions_add_shell);
+ tcase_add_test(tc, test_actions_add_continue);
+ tcase_add_test(tc, test_actions_add_multiple_same_action);
+ tcase_add_test(tc, test_actions_add_multiple_different_action);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("parse");
+ tcase_add_checked_fixture(tc, actions_fixture_setup, actions_fixture_teardown);
+ tcase_add_test(tc, test_actions_parse_trace_output);
+ tcase_add_test(tc, test_actions_parse_trace_output_arg);
+ tcase_add_test(tc, test_actions_parse_trace_output_arg_bad);
+ tcase_add_test(tc, test_actions_parse_signal);
+ tcase_add_test(tc, test_actions_parse_signal_swapped);
+ tcase_add_test(tc, test_actions_parse_signal_parent);
+ tcase_add_test(tc, test_actions_parse_signal_no_arg);
+ tcase_add_test(tc, test_actions_parse_signal_no_pid);
+ tcase_add_test(tc, test_actions_parse_signal_no_num);
+ tcase_add_test(tc, test_actions_parse_signal_arg_bad);
+ tcase_add_test(tc, test_actions_parse_shell);
+ tcase_add_test(tc, test_actions_parse_shell_no_arg);
+ tcase_add_test(tc, test_actions_parse_shell_arg_bad);
+ tcase_add_test(tc, test_actions_parse_continue);
+ tcase_add_test(tc, test_actions_parse_continue_arg_bad);
+ tcase_add_test(tc, test_actions_parse_invalid);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("perform");
+ tcase_add_checked_fixture(tc, actions_fixture_setup, actions_fixture_teardown);
+ tcase_add_test(tc, test_actions_perform_continue);
+ tcase_add_test(tc, test_actions_perform_continue_after_successful_shell_command);
+ tcase_add_test(tc, test_actions_perform_continue_after_failed_shell_command);
+ tcase_add_test(tc, test_actions_perform_continue_unset_flag);
+ suite_add_tcase(s, tc);
+
+ return s;
+}
diff --git a/tools/tracing/rtla/tests/unit/cli_opt_callback.c b/tools/tracing/rtla/tests/unit/cli_opt_callback.c
new file mode 100644
index 000000000000..70e2576b1336
--- /dev/null
+++ b/tools/tracing/rtla/tests/unit/cli_opt_callback.c
@@ -0,0 +1,912 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <check.h>
+
+#define RTLA_ALLOW_CLI_P_H
+#include "../../src/cli_p.h"
+#include "cli_params_assert.h"
+
+#define TEST_CALLBACK(value, cb) OPT_CALLBACK('t', "test", value, "test value", "test help", cb)
+#define TEST_LLONG_RANGE(value, lo, hi) \
+ RTLA_OPT_CALLBACK_DATA('t', "test", value, "test value", "test help", \
+ opt_llong_callback, LLONG_RANGE(lo, hi))
+#define TEST_INT_RANGE(value, lo, hi) \
+ RTLA_OPT_CALLBACK_DATA('t', "test", value, "test value", "test help", \
+ opt_int_callback, INT_RANGE(lo, hi))
+
+START_TEST(test_opt_llong_callback_simple)
+{
+ long long test_value = 0;
+ const struct option opt = TEST_CALLBACK(&test_value, opt_llong_callback);
+
+ ck_assert_int_eq(opt_llong_callback(&opt, "1234567890", 0), 0);
+ ck_assert_int_eq(test_value, 1234567890);
+}
+END_TEST
+
+START_TEST(test_opt_llong_callback_max)
+{
+ long long test_value = 0;
+ const struct option opt = TEST_CALLBACK(&test_value, opt_llong_callback);
+
+ ck_assert_int_eq(opt_llong_callback(&opt, "9223372036854775807", 0), 0);
+ ck_assert_int_eq(test_value, 9223372036854775807LL);
+}
+END_TEST
+
+START_TEST(test_opt_llong_callback_min)
+{
+ long long test_value = 0;
+ const struct option opt = TEST_CALLBACK(&test_value, opt_llong_callback);
+
+ ck_assert_int_eq(opt_llong_callback(&opt, "-9223372036854775808", 0), 0);
+ ck_assert_int_eq(test_value, ~9223372036854775807LL);
+}
+END_TEST
+
+START_TEST(test_opt_llong_callback_non_numeric)
+{
+ long long test_value = 0;
+ const struct option opt = TEST_CALLBACK(&test_value, opt_llong_callback);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_llong_callback(&opt, "abc", 0), -1);
+ ck_assert_int_eq(test_value, 0);
+}
+END_TEST
+
+START_TEST(test_opt_llong_callback_non_numeric_suffix)
+{
+ long long test_value = 0;
+ const struct option opt = TEST_CALLBACK(&test_value, opt_llong_callback);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_llong_callback(&opt, "1234567890abc", 0), -1);
+ ck_assert_int_eq(test_value, 0);
+}
+END_TEST
+
+START_TEST(test_opt_llong_callback_unset)
+{
+ long long test_value = 0;
+ const struct option opt = TEST_CALLBACK(&test_value, opt_llong_callback);
+
+ ck_assert_int_eq(opt_llong_callback(&opt, "1234567890", 0), 0);
+ ck_assert_int_eq(opt_llong_callback(&opt, NULL, 1), 0);
+ ck_assert_int_eq(test_value, 0);
+}
+END_TEST
+
+START_TEST(test_opt_llong_callback_unset_defval)
+{
+ long long test_value = 0;
+ const long long default_value = 42;
+ const struct option opt = RTLA_OPT_LLONG_DEFVAL('t', "test", &test_value, "test value",
+ "test help", &default_value);
+
+ ck_assert_int_eq(opt_llong_callback(&opt, "1234567890", 0), 0);
+ ck_assert_int_eq(opt_llong_callback(&opt, NULL, 1), 0);
+ ck_assert_int_eq(test_value, default_value);
+}
+END_TEST
+
+START_TEST(test_opt_int_callback_simple)
+{
+ int test_value = 0;
+ const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback);
+
+ ck_assert_int_eq(opt_int_callback(&opt, "1234567890", 0), 0);
+ ck_assert_int_eq(test_value, 1234567890);
+}
+END_TEST
+
+START_TEST(test_opt_int_callback_max)
+{
+ int test_value = 0;
+ const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback);
+
+ ck_assert_int_eq(opt_int_callback(&opt, "2147483647", 0), 0);
+ ck_assert_int_eq(test_value, 2147483647);
+}
+END_TEST
+
+START_TEST(test_opt_int_callback_min)
+{
+ int test_value = 0;
+ const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback);
+
+ ck_assert_int_eq(opt_int_callback(&opt, "-2147483648", 0), 0);
+ ck_assert_int_eq(test_value, -2147483648);
+}
+END_TEST
+
+START_TEST(test_opt_int_callback_non_numeric)
+{
+ int test_value = 0;
+ const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_int_callback(&opt, "abc", 0), -1);
+ ck_assert_int_eq(test_value, 0);
+}
+END_TEST
+
+START_TEST(test_opt_int_callback_non_numeric_suffix)
+{
+ int test_value = 0;
+ const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_int_callback(&opt, "1234567890abc", 0), -1);
+ ck_assert_int_eq(test_value, 0);
+}
+END_TEST
+
+START_TEST(test_opt_int_callback_unset)
+{
+ int test_value = 0;
+ const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback);
+
+ ck_assert_int_eq(opt_int_callback(&opt, "1234567890", 0), 0);
+ ck_assert_int_eq(opt_int_callback(&opt, NULL, 1), 0);
+ ck_assert_int_eq(test_value, 0);
+}
+END_TEST
+
+START_TEST(test_opt_int_callback_unset_defval)
+{
+ int test_value = 0;
+ const struct option opt = RTLA_OPT_INT_DEFVAL('t', "test", &test_value, "test value",
+ "test help", 42);
+
+ ck_assert_int_eq(opt_int_callback(&opt, "1234567890", 0), 0);
+ ck_assert_int_eq(opt_int_callback(&opt, NULL, 1), 0);
+ ck_assert_int_eq(test_value, 42);
+}
+END_TEST
+
+START_TEST(test_opt_llong_callback_range_in)
+{
+ long long test_value = 0;
+ const struct option opt = TEST_LLONG_RANGE(&test_value, 10, 100);
+
+ ck_assert_int_eq(opt_llong_callback(&opt, "50", 0), 0);
+ ck_assert_int_eq(test_value, 50);
+}
+END_TEST
+
+START_TEST(test_opt_llong_callback_range_below)
+{
+ long long test_value = 0;
+ const struct option opt = TEST_LLONG_RANGE(&test_value, 10, 100);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_llong_callback(&opt, "9", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_llong_callback_range_above)
+{
+ long long test_value = 0;
+ const struct option opt = TEST_LLONG_RANGE(&test_value, 10, 100);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_llong_callback(&opt, "101", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_llong_callback_range_boundary)
+{
+ long long test_value = 0;
+ const struct option opt = TEST_LLONG_RANGE(&test_value, 10, 100);
+
+ ck_assert_int_eq(opt_llong_callback(&opt, "10", 0), 0);
+ ck_assert_int_eq(test_value, 10);
+ ck_assert_int_eq(opt_llong_callback(&opt, "100", 0), 0);
+ ck_assert_int_eq(test_value, 100);
+}
+END_TEST
+
+START_TEST(test_opt_int_callback_range_in)
+{
+ int test_value = 0;
+ const struct option opt = TEST_INT_RANGE(&test_value, 0, 10000);
+
+ ck_assert_int_eq(opt_int_callback(&opt, "5000", 0), 0);
+ ck_assert_int_eq(test_value, 5000);
+}
+END_TEST
+
+START_TEST(test_opt_int_callback_range_below)
+{
+ int test_value = 0;
+ const struct option opt = TEST_INT_RANGE(&test_value, 0, 10000);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_int_callback(&opt, "-1", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_int_callback_range_above)
+{
+ int test_value = 0;
+ const struct option opt = TEST_INT_RANGE(&test_value, 0, 10000);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_int_callback(&opt, "10001", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_int_callback_range_boundary)
+{
+ int test_value = 0;
+ const struct option opt = TEST_INT_RANGE(&test_value, 0, 10000);
+
+ ck_assert_int_eq(opt_int_callback(&opt, "0", 0), 0);
+ ck_assert_int_eq(test_value, 0);
+ ck_assert_int_eq(opt_int_callback(&opt, "10000", 0), 0);
+ ck_assert_int_eq(test_value, 10000);
+}
+END_TEST
+
+START_TEST(test_opt_cpus_cb)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_cpus_cb);
+
+ nr_cpus = 4;
+ ck_assert_int_eq(opt_cpus_cb(&opt, "0-3", 0), 0);
+ ck_assert_str_eq(params.cpus, "0-3");
+}
+END_TEST
+
+START_TEST(test_opt_cpus_cb_invalid)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_cpus_cb);
+
+ nr_cpus = 4;
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_cpus_cb(&opt, "0-3,5", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_cgroup_cb)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_cgroup_cb);
+
+ ck_assert_int_eq(opt_cgroup_cb(&opt, "cgroup", 0), 0);
+ ck_assert_int_eq(params.cgroup, 1);
+ ck_assert_str_eq(params.cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_opt_cgroup_cb_equals)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_cgroup_cb);
+
+ ck_assert_int_eq(opt_cgroup_cb(&opt, "=cgroup", 0), 0);
+ ck_assert_int_eq(params.cgroup, 1);
+ ck_assert_str_eq(params.cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_opt_cgroup_cb_unset)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_cgroup_cb);
+
+ ck_assert_int_eq(opt_cgroup_cb(&opt, "cgroup", 0), 0);
+ ck_assert_int_eq(opt_cgroup_cb(&opt, NULL, 1), 0);
+ ck_assert_int_eq(params.cgroup, 0);
+ ck_assert_ptr_null(params.cgroup_name);
+}
+END_TEST
+
+START_TEST(test_opt_duration_cb)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_duration_cb);
+
+ ck_assert_int_eq(opt_duration_cb(&opt, "1m", 0), 0);
+ ck_assert_int_eq(params.duration, 60);
+}
+END_TEST
+
+START_TEST(test_opt_duration_cb_invalid)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_duration_cb);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_duration_cb(&opt, "abc", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_duration_cb_unset)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_duration_cb);
+
+ ck_assert_int_eq(opt_duration_cb(&opt, "1m", 0), 0);
+ ck_assert_int_eq(opt_duration_cb(&opt, NULL, 1), 0);
+ ck_assert_int_eq(params.duration, 0);
+}
+END_TEST
+
+START_TEST(test_opt_event_cb)
+{
+ struct trace_events *events = NULL;
+ const struct option opt = TEST_CALLBACK(&events, opt_event_cb);
+
+ ck_assert_int_eq(opt_event_cb(&opt, "sched:sched_switch", 0), 0);
+ ck_assert_str_eq(events->system, "sched");
+ ck_assert_str_eq(events->event, "sched_switch");
+ ck_assert_ptr_eq(events->next, NULL);
+}
+END_TEST
+
+START_TEST(test_opt_event_cb_multiple)
+{
+ struct trace_events *events = NULL;
+ const struct option opt = TEST_CALLBACK(&events, opt_event_cb);
+
+ ck_assert_int_eq(opt_event_cb(&opt, "sched:sched_switch", 0), 0);
+ ck_assert_int_eq(opt_event_cb(&opt, "sched:sched_wakeup", 0), 0);
+ ck_assert_str_eq(events->system, "sched");
+ ck_assert_str_eq(events->event, "sched_wakeup");
+ ck_assert_str_eq(events->next->system, "sched");
+ ck_assert_str_eq(events->next->event, "sched_switch");
+ ck_assert_ptr_eq(events->next->next, NULL);
+}
+END_TEST
+
+START_TEST(test_opt_housekeeping_cb)
+{
+ struct common_params __params = {0};
+ struct common_params *params = &__params;
+ const struct option opt = TEST_CALLBACK(params, opt_housekeeping_cb);
+
+ nr_cpus = 4;
+ ck_assert_int_eq(opt_housekeeping_cb(&opt, "0-3", 0), 0);
+ ck_assert_int_eq(params->hk_cpus, 1);
+ CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 2, 3);
+}
+END_TEST
+
+START_TEST(test_opt_housekeeping_cb_invalid)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_housekeeping_cb);
+
+ nr_cpus = 4;
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_housekeeping_cb(&opt, "0-3,5", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_housekeeping_cb_unset)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_housekeeping_cb);
+
+ nr_cpus = 4;
+ ck_assert_int_eq(opt_housekeeping_cb(&opt, "0-3", 0), 0);
+ ck_assert_int_eq(opt_housekeeping_cb(&opt, NULL, 1), 0);
+ ck_assert_int_eq(params.hk_cpus, 0);
+ ck_assert_int_eq(CPU_COUNT(&params.hk_cpu_set), 0);
+}
+END_TEST
+
+START_TEST(test_opt_priority_cb)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_priority_cb);
+
+ ck_assert_int_eq(opt_priority_cb(&opt, "f:95", 0), 0);
+ ck_assert_int_eq(params.sched_param.sched_policy, SCHED_FIFO);
+ ck_assert_int_eq(params.sched_param.sched_priority, 95);
+}
+END_TEST
+
+START_TEST(test_opt_priority_cb_invalid)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_priority_cb);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_priority_cb(&opt, "abc", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_priority_cb_unset)
+{
+ struct common_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_priority_cb);
+
+ ck_assert_int_eq(opt_priority_cb(&opt, "f:95", 0), 0);
+ ck_assert_int_eq(opt_priority_cb(&opt, NULL, 1), 0);
+ ck_assert_int_eq(params.sched_param.sched_policy, 0);
+ ck_assert_int_eq(params.sched_param.sched_priority, 0);
+}
+END_TEST
+
+START_TEST(test_opt_trigger_cb)
+{
+ struct trace_events *events = trace_event_alloc("sched:sched_switch");
+ const struct option opt = TEST_CALLBACK(&events, opt_trigger_cb);
+
+ ck_assert_int_eq(opt_trigger_cb(&opt, "stacktrace", 0), 0);
+ ck_assert_str_eq(events->trigger, "stacktrace");
+}
+END_TEST
+
+START_TEST(test_opt_trigger_cb_no_event)
+{
+ struct trace_events *events = NULL;
+ const struct option opt = TEST_CALLBACK(&events, opt_trigger_cb);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_trigger_cb(&opt, "stacktrace", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_filter_cb)
+{
+ struct trace_events *events = trace_event_alloc("sched:sched_switch");
+ const struct option opt = TEST_CALLBACK(&events, opt_filter_cb);
+
+ ck_assert_int_eq(opt_filter_cb(&opt, "comm ~ \"rtla\"", 0), 0);
+ ck_assert_str_eq(events->filter, "comm ~ \"rtla\"");
+}
+END_TEST
+
+START_TEST(test_opt_filter_cb_no_event)
+{
+ struct trace_events *events = NULL;
+ const struct option opt = TEST_CALLBACK(&events, opt_filter_cb);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_filter_cb(&opt, "comm ~ \"rtla\"", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_osnoise_auto_cb)
+{
+ struct osnoise_params params = {0};
+ struct osnoise_cb_data cb_data = {&params};
+ const struct option opt = TEST_CALLBACK(&cb_data, opt_osnoise_auto_cb);
+
+ ck_assert_int_eq(opt_osnoise_auto_cb(&opt, "10", 0), 0);
+ ck_assert_int_eq(params.common.stop_us, 10);
+ ck_assert_int_eq(params.threshold, 1);
+ ck_assert_str_eq(cb_data.trace_output, "osnoise_trace.txt");
+}
+END_TEST
+
+START_TEST(test_opt_osnoise_auto_cb_unset)
+{
+ struct osnoise_params params = {0};
+ struct osnoise_cb_data cb_data = {&params};
+ const struct option opt = TEST_CALLBACK(&cb_data, opt_osnoise_auto_cb);
+
+ ck_assert_int_eq(opt_osnoise_auto_cb(&opt, "10", 0), 0);
+ ck_assert_int_eq(opt_osnoise_auto_cb(&opt, NULL, 1), 0);
+ ck_assert_int_eq(params.common.stop_us, 0);
+ ck_assert_int_eq(params.threshold, 0);
+ ck_assert_ptr_null(cb_data.trace_output);
+}
+END_TEST
+
+
+START_TEST(test_opt_osnoise_trace_output_cb)
+{
+ const char *trace_output = NULL;
+ const struct option opt = TEST_CALLBACK(&trace_output, opt_osnoise_trace_output_cb);
+
+ ck_assert_int_eq(opt_osnoise_trace_output_cb(&opt, "trace.txt", 0), 0);
+ ck_assert_str_eq(trace_output, "trace.txt");
+}
+END_TEST
+
+START_TEST(test_opt_osnoise_trace_output_cb_noarg)
+{
+ const char *trace_output = NULL;
+ const struct option opt = TEST_CALLBACK(&trace_output, opt_osnoise_trace_output_cb);
+
+ ck_assert_int_eq(opt_osnoise_trace_output_cb(&opt, NULL, 0), 0);
+ ck_assert_str_eq(trace_output, "osnoise_trace.txt");
+}
+END_TEST
+
+START_TEST(test_opt_osnoise_trace_output_cb_unset)
+{
+ const char *trace_output = NULL;
+ const struct option opt = TEST_CALLBACK(&trace_output, opt_osnoise_trace_output_cb);
+
+ ck_assert_int_eq(opt_osnoise_trace_output_cb(&opt, "trace.txt", 0), 0);
+ ck_assert_int_eq(opt_osnoise_trace_output_cb(&opt, NULL, 1), 0);
+ ck_assert_ptr_null(trace_output);
+}
+END_TEST
+
+START_TEST(test_opt_osnoise_on_threshold_cb)
+{
+ struct actions actions = {0};
+ const struct option opt = TEST_CALLBACK(&actions, opt_osnoise_on_threshold_cb);
+
+ ck_assert_int_eq(opt_osnoise_on_threshold_cb(&opt, "trace", 0), 0);
+ ck_assert_int_eq(actions.len, 1);
+ ck_assert_int_eq(actions.list[0].type, ACTION_TRACE_OUTPUT);
+ ck_assert_str_eq(actions.list[0].trace_output, "osnoise_trace.txt");
+}
+END_TEST
+
+START_TEST(test_opt_osnoise_on_threshold_cb_invalid)
+{
+ struct actions actions = {0};
+ const struct option opt = TEST_CALLBACK(&actions, opt_osnoise_on_threshold_cb);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_osnoise_on_threshold_cb(&opt, "abc", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_osnoise_on_end_cb)
+{
+ struct actions actions = {0};
+ const struct option opt = TEST_CALLBACK(&actions, opt_osnoise_on_end_cb);
+
+ ck_assert_int_eq(opt_osnoise_on_end_cb(&opt, "trace", 0), 0);
+ ck_assert_int_eq(actions.len, 1);
+ ck_assert_int_eq(actions.list[0].type, ACTION_TRACE_OUTPUT);
+ ck_assert_str_eq(actions.list[0].trace_output, "osnoise_trace.txt");
+}
+END_TEST
+
+START_TEST(test_opt_osnoise_on_end_cb_invalid)
+{
+ struct actions actions = {0};
+ const struct option opt = TEST_CALLBACK(&actions, opt_osnoise_on_end_cb);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_osnoise_on_end_cb(&opt, "abc", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_timerlat_auto_cb)
+{
+ struct timerlat_params params = {0};
+ struct timerlat_cb_data cb_data = {&params};
+ const struct option opt = TEST_CALLBACK(&cb_data, opt_timerlat_auto_cb);
+
+ ck_assert_int_eq(opt_timerlat_auto_cb(&opt, "10", 0), 0);
+ ck_assert_int_eq(params.common.stop_us, 10);
+ ck_assert_int_eq(params.common.stop_total_us, 10);
+ ck_assert_int_eq(params.print_stack, 10);
+ ck_assert_str_eq(cb_data.trace_output, "timerlat_trace.txt");
+}
+END_TEST
+
+START_TEST(test_opt_timerlat_auto_cb_unset)
+{
+ struct timerlat_params params = {0};
+ struct timerlat_cb_data cb_data = {&params};
+ const struct option opt = TEST_CALLBACK(&cb_data, opt_timerlat_auto_cb);
+
+ ck_assert_int_eq(opt_timerlat_auto_cb(&opt, "10", 0), 0);
+ ck_assert_int_eq(opt_timerlat_auto_cb(&opt, NULL, 1), 0);
+ ck_assert_int_eq(params.common.stop_us, 0);
+ ck_assert_int_eq(params.common.stop_total_us, 0);
+ ck_assert_int_eq(params.print_stack, 0);
+ ck_assert_ptr_null(cb_data.trace_output);
+}
+END_TEST
+
+
+START_TEST(test_opt_aa_only_cb)
+{
+ struct timerlat_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_aa_only_cb);
+
+ ck_assert_int_eq(opt_aa_only_cb(&opt, "10", 0), 0);
+ ck_assert_int_eq(params.common.stop_us, 10);
+ ck_assert_int_eq(params.common.stop_total_us, 10);
+ ck_assert_int_eq(params.print_stack, 10);
+ ck_assert_int_eq(params.common.aa_only, 1);
+}
+END_TEST
+
+START_TEST(test_opt_aa_only_cb_unset)
+{
+ struct timerlat_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_aa_only_cb);
+
+ ck_assert_int_eq(opt_aa_only_cb(&opt, "10", 0), 0);
+ ck_assert_int_eq(opt_aa_only_cb(&opt, NULL, 1), 0);
+ ck_assert_int_eq(params.common.stop_us, 0);
+ ck_assert_int_eq(params.common.stop_total_us, 0);
+ ck_assert_int_eq(params.print_stack, 0);
+ ck_assert_int_eq(params.common.aa_only, 0);
+}
+END_TEST
+
+START_TEST(test_opt_timerlat_trace_output_cb)
+{
+ const char *trace_output = NULL;
+ const struct option opt = TEST_CALLBACK(&trace_output, opt_timerlat_trace_output_cb);
+
+ ck_assert_int_eq(opt_timerlat_trace_output_cb(&opt, "trace.txt", 0), 0);
+ ck_assert_str_eq(trace_output, "trace.txt");
+}
+END_TEST
+
+START_TEST(test_opt_timerlat_trace_output_cb_noarg)
+{
+ const char *trace_output = NULL;
+ const struct option opt = TEST_CALLBACK(&trace_output, opt_timerlat_trace_output_cb);
+
+ ck_assert_int_eq(opt_timerlat_trace_output_cb(&opt, NULL, 0), 0);
+ ck_assert_str_eq(trace_output, "timerlat_trace.txt");
+}
+END_TEST
+
+START_TEST(test_opt_timerlat_trace_output_cb_unset)
+{
+ const char *trace_output = NULL;
+ const struct option opt = TEST_CALLBACK(&trace_output, opt_timerlat_trace_output_cb);
+
+ ck_assert_int_eq(opt_timerlat_trace_output_cb(&opt, "trace.txt", 0), 0);
+ ck_assert_int_eq(opt_timerlat_trace_output_cb(&opt, NULL, 1), 0);
+ ck_assert_ptr_null(trace_output);
+}
+END_TEST
+
+START_TEST(test_opt_timerlat_on_threshold_cb)
+{
+ struct actions actions = {0};
+ const struct option opt = TEST_CALLBACK(&actions, opt_timerlat_on_threshold_cb);
+
+ ck_assert_int_eq(opt_timerlat_on_threshold_cb(&opt, "trace", 0), 0);
+ ck_assert_int_eq(actions.len, 1);
+ ck_assert_int_eq(actions.list[0].type, ACTION_TRACE_OUTPUT);
+ ck_assert_str_eq(actions.list[0].trace_output, "timerlat_trace.txt");
+}
+END_TEST
+
+START_TEST(test_opt_timerlat_on_threshold_cb_invalid)
+{
+ struct actions actions = {0};
+ const struct option opt = TEST_CALLBACK(&actions, opt_timerlat_on_threshold_cb);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_timerlat_on_threshold_cb(&opt, "abc", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_timerlat_on_end_cb)
+{
+ struct actions actions = {0};
+ const struct option opt = TEST_CALLBACK(&actions, opt_timerlat_on_end_cb);
+
+ ck_assert_int_eq(opt_timerlat_on_end_cb(&opt, "trace", 0), 0);
+ ck_assert_int_eq(actions.len, 1);
+ ck_assert_int_eq(actions.list[0].type, ACTION_TRACE_OUTPUT);
+ ck_assert_str_eq(actions.list[0].trace_output, "timerlat_trace.txt");
+}
+END_TEST
+
+START_TEST(test_opt_timerlat_on_end_cb_invalid)
+{
+ struct actions actions = {0};
+ const struct option opt = TEST_CALLBACK(&actions, opt_timerlat_on_end_cb);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_timerlat_on_end_cb(&opt, "abc", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_user_threads_cb)
+{
+ struct timerlat_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_user_threads_cb);
+
+ ck_assert_int_eq(opt_user_threads_cb(&opt, NULL, 0), 0);
+ ck_assert_int_eq(params.common.user_workload, 1);
+ ck_assert_int_eq(params.common.user_data, 1);
+}
+END_TEST
+
+START_TEST(test_opt_user_threads_cb_unset)
+{
+ struct timerlat_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_user_threads_cb);
+
+ ck_assert_int_eq(opt_user_threads_cb(&opt, NULL, 0), 0);
+ ck_assert_int_eq(opt_user_threads_cb(&opt, NULL, 1), 0);
+ ck_assert_int_eq(params.common.user_workload, 0);
+ ck_assert_int_eq(params.common.user_data, 0);
+}
+END_TEST
+
+START_TEST(test_opt_nano_cb)
+{
+ struct timerlat_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_nano_cb);
+
+ ck_assert_int_eq(opt_nano_cb(&opt, NULL, 0), 0);
+ ck_assert_int_eq(params.common.output_divisor, 1);
+}
+END_TEST
+
+START_TEST(test_opt_nano_cb_unset)
+{
+ struct timerlat_params params = {0};
+ const struct option opt = TEST_CALLBACK(&params, opt_nano_cb);
+
+ ck_assert_int_eq(opt_nano_cb(&opt, NULL, 0), 0);
+ ck_assert_int_eq(opt_nano_cb(&opt, NULL, 1), 0);
+ ck_assert_int_eq(params.common.output_divisor, default_output_divisor);
+}
+END_TEST
+
+START_TEST(test_opt_timerlat_align_cb)
+{
+ struct timerlat_params params = {0};
+ const struct option opt = RTLA_OPT_CALLBACK_DATA('A', "aligned", &params, "us",
+ "test", opt_timerlat_align_cb, LLONG_RANGE(0, LLONG_MAX));
+
+ ck_assert_int_eq(opt_timerlat_align_cb(&opt, "500", 0), 0);
+ ck_assert(params.timerlat_align);
+ ck_assert_int_eq(params.timerlat_align_us, 500);
+}
+END_TEST
+
+START_TEST(test_opt_timerlat_align_cb_invalid)
+{
+ struct timerlat_params params = {0};
+ const struct option opt = RTLA_OPT_CALLBACK_DATA('A', "aligned", &params, "us",
+ "test", opt_timerlat_align_cb, LLONG_RANGE(0, LLONG_MAX));
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_timerlat_align_cb(&opt, "-1", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_timerlat_align_cb_unset)
+{
+ struct timerlat_params params = {0};
+ const struct option opt = RTLA_OPT_CALLBACK_DATA('A', "aligned", &params, "us",
+ "test", opt_timerlat_align_cb, LLONG_RANGE(0, LLONG_MAX));
+
+ ck_assert_int_eq(opt_timerlat_align_cb(&opt, "500", 0), 0);
+ ck_assert_int_eq(opt_timerlat_align_cb(&opt, NULL, 1), 0);
+ ck_assert_int_eq(params.timerlat_align, 0);
+ ck_assert_int_eq(params.timerlat_align_us, 0);
+}
+END_TEST
+
+START_TEST(test_opt_stack_format_cb)
+{
+ int stack_format = 0;
+ const struct option opt = TEST_CALLBACK(&stack_format, opt_stack_format_cb);
+
+ ck_assert_int_eq(opt_stack_format_cb(&opt, "full", 0), 0);
+ ck_assert_int_eq(stack_format, STACK_FORMAT_FULL);
+}
+END_TEST
+
+START_TEST(test_opt_stack_format_cb_invalid)
+{
+ int stack_format = 0;
+ const struct option opt = TEST_CALLBACK(&stack_format, opt_stack_format_cb);
+
+ assert(freopen("/dev/null", "w", stderr));
+ ck_assert_int_eq(opt_stack_format_cb(&opt, "abc", 0), -1);
+}
+END_TEST
+
+START_TEST(test_opt_stack_format_cb_unset)
+{
+ int stack_format = 0;
+ const struct option opt = TEST_CALLBACK(&stack_format, opt_stack_format_cb);
+
+ ck_assert_int_eq(opt_stack_format_cb(&opt, "full", 0), 0);
+ ck_assert_int_eq(opt_stack_format_cb(&opt, NULL, 1), 0);
+ ck_assert_int_eq(stack_format, default_stack_format);
+}
+END_TEST
+
+
+Suite *cli_opt_callback_suite(void)
+{
+ Suite *s = suite_create("cli_opt_callback");
+ TCase *tc;
+
+ tc = tcase_create("common");
+ tcase_add_test(tc, test_opt_llong_callback_simple);
+ tcase_add_test(tc, test_opt_llong_callback_max);
+ tcase_add_test(tc, test_opt_llong_callback_min);
+ tcase_add_test(tc, test_opt_llong_callback_non_numeric);
+ tcase_add_test(tc, test_opt_llong_callback_non_numeric_suffix);
+ tcase_add_test(tc, test_opt_llong_callback_unset);
+ tcase_add_test(tc, test_opt_llong_callback_unset_defval);
+ tcase_add_test(tc, test_opt_llong_callback_range_in);
+ tcase_add_test(tc, test_opt_llong_callback_range_below);
+ tcase_add_test(tc, test_opt_llong_callback_range_above);
+ tcase_add_test(tc, test_opt_llong_callback_range_boundary);
+ tcase_add_test(tc, test_opt_int_callback_simple);
+ tcase_add_test(tc, test_opt_int_callback_max);
+ tcase_add_test(tc, test_opt_int_callback_min);
+ tcase_add_test(tc, test_opt_int_callback_non_numeric);
+ tcase_add_test(tc, test_opt_int_callback_non_numeric_suffix);
+ tcase_add_test(tc, test_opt_int_callback_unset);
+ tcase_add_test(tc, test_opt_int_callback_unset_defval);
+ tcase_add_test(tc, test_opt_int_callback_range_in);
+ tcase_add_test(tc, test_opt_int_callback_range_below);
+ tcase_add_test(tc, test_opt_int_callback_range_above);
+ tcase_add_test(tc, test_opt_int_callback_range_boundary);
+ tcase_add_test(tc, test_opt_cpus_cb);
+ tcase_add_test(tc, test_opt_cpus_cb_invalid);
+ tcase_add_test(tc, test_opt_cgroup_cb);
+ tcase_add_test(tc, test_opt_cgroup_cb_equals);
+ tcase_add_test(tc, test_opt_cgroup_cb_unset);
+ tcase_add_test(tc, test_opt_duration_cb);
+ tcase_add_test(tc, test_opt_duration_cb_unset);
+ tcase_add_test(tc, test_opt_duration_cb_invalid);
+ tcase_add_test(tc, test_opt_event_cb);
+ tcase_add_test(tc, test_opt_event_cb_multiple);
+ tcase_add_test(tc, test_opt_housekeeping_cb);
+ tcase_add_test(tc, test_opt_housekeeping_cb_invalid);
+ tcase_add_test(tc, test_opt_housekeeping_cb_unset);
+ tcase_add_test(tc, test_opt_priority_cb);
+ tcase_add_test(tc, test_opt_priority_cb_invalid);
+ tcase_add_test(tc, test_opt_priority_cb_unset);
+ tcase_add_test(tc, test_opt_trigger_cb);
+ tcase_add_test(tc, test_opt_trigger_cb_no_event);
+ tcase_add_test(tc, test_opt_filter_cb);
+ tcase_add_test(tc, test_opt_filter_cb_no_event);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("osnoise");
+ tcase_add_test(tc, test_opt_osnoise_auto_cb);
+ tcase_add_test(tc, test_opt_osnoise_auto_cb_unset);
+ tcase_add_test(tc, test_opt_osnoise_trace_output_cb);
+ tcase_add_test(tc, test_opt_osnoise_trace_output_cb_noarg);
+ tcase_add_test(tc, test_opt_osnoise_trace_output_cb_unset);
+ tcase_add_test(tc, test_opt_osnoise_on_threshold_cb);
+ tcase_add_test(tc, test_opt_osnoise_on_threshold_cb_invalid);
+ tcase_add_test(tc, test_opt_osnoise_on_end_cb);
+ tcase_add_test(tc, test_opt_osnoise_on_end_cb_invalid);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("timerlat");
+ tcase_add_test(tc, test_opt_timerlat_auto_cb);
+ tcase_add_test(tc, test_opt_timerlat_auto_cb_unset);
+ tcase_add_test(tc, test_opt_aa_only_cb);
+ tcase_add_test(tc, test_opt_aa_only_cb_unset);
+ tcase_add_test(tc, test_opt_timerlat_trace_output_cb);
+ tcase_add_test(tc, test_opt_timerlat_trace_output_cb_noarg);
+ tcase_add_test(tc, test_opt_timerlat_trace_output_cb_unset);
+ tcase_add_test(tc, test_opt_timerlat_on_threshold_cb);
+ tcase_add_test(tc, test_opt_timerlat_on_threshold_cb_invalid);
+ tcase_add_test(tc, test_opt_timerlat_on_end_cb);
+ tcase_add_test(tc, test_opt_timerlat_on_end_cb_invalid);
+ tcase_add_test(tc, test_opt_user_threads_cb);
+ tcase_add_test(tc, test_opt_user_threads_cb_unset);
+ tcase_add_test(tc, test_opt_nano_cb);
+ tcase_add_test(tc, test_opt_nano_cb_unset);
+ tcase_add_test(tc, test_opt_stack_format_cb);
+ tcase_add_test(tc, test_opt_stack_format_cb_invalid);
+ tcase_add_test(tc, test_opt_stack_format_cb_unset);
+ tcase_add_test(tc, test_opt_timerlat_align_cb);
+ tcase_add_test(tc, test_opt_timerlat_align_cb_invalid);
+ tcase_add_test(tc, test_opt_timerlat_align_cb_unset);
+ suite_add_tcase(s, tc);
+
+ return s;
+}
diff --git a/tools/tracing/rtla/tests/unit/cli_params_assert.h b/tools/tracing/rtla/tests/unit/cli_params_assert.h
new file mode 100644
index 000000000000..4bc7d582fcf4
--- /dev/null
+++ b/tools/tracing/rtla/tests/unit/cli_params_assert.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#pragma once
+
+#include "../../src/timerlat.h"
+
+/* Tracing Options */
+
+#define CLI_ASSERT_SINGLE_EVENT(_system, _event) do {\
+ ck_assert_ptr_nonnull(params->events);\
+ ck_assert_str_eq(params->events->system, _system);\
+ ck_assert_str_eq(params->events->event, _event);\
+ ck_assert_ptr_null(params->events->next);\
+} while (0)
+
+#define CLI_ASSERT_SINGLE_FILTER(_filter) do {\
+ ck_assert_ptr_nonnull(params->events);\
+ ck_assert_str_eq(params->events->filter, _filter);\
+ ck_assert_ptr_null(params->events->next);\
+} while (0)
+
+#define CLI_ASSERT_SINGLE_TRIGGER(_trigger) do {\
+ ck_assert_ptr_nonnull(params->events);\
+ ck_assert_str_eq(params->events->trigger, _trigger);\
+ ck_assert_ptr_null(params->events->next);\
+} while (0)
+
+/* CPU Configuration */
+
+#define CLI_ASSERT_CPUSET(_field, ...) do {\
+ int n;\
+ int cpus[] = { __VA_ARGS__ };\
+ for (n = 0; n < sizeof(cpus) / sizeof(int); n++)\
+ ck_assert(CPU_ISSET(cpus[n], &params->_field));\
+ ck_assert_int_eq(CPU_COUNT(&params->_field), n);\
+} while (0)
+
+/* Auto Analysis and Actions */
+
+#define CLI_OSNOISE_ASSERT_AUTO(_stop) do {\
+ ck_assert_int_eq(params->stop_us, _stop);\
+ ck_assert_int_eq(osn_params->threshold, 1);\
+ ck_assert_int_eq(params->threshold_actions.len, 1);\
+ ck_assert_int_eq(params->threshold_actions.list[0].type, ACTION_TRACE_OUTPUT);\
+ ck_assert_str_eq(params->threshold_actions.list[0].trace_output, "osnoise_trace.txt");\
+} while (0)
+
+#define CLI_TIMERLAT_ASSERT_AUTO(_threshold) do {\
+ ck_assert_int_eq(params->stop_us, _threshold);\
+ ck_assert_int_eq(params->stop_total_us, _threshold);\
+ ck_assert_int_eq(tlat_params->print_stack, _threshold);\
+ ck_assert_int_eq(params->threshold_actions.len, 1);\
+ ck_assert_int_eq(params->threshold_actions.list[0].type, ACTION_TRACE_OUTPUT);\
+ ck_assert_str_eq(params->threshold_actions.list[0].trace_output, "timerlat_trace.txt");\
+} while (0)
+
+#define CLI_TIMERLAT_ASSERT_AA_ONLY(_threshold) do {\
+ ck_assert_int_eq(params->stop_us, _threshold);\
+ ck_assert_int_eq(params->stop_total_us, _threshold);\
+ ck_assert_int_eq(tlat_params->print_stack, _threshold);\
+ ck_assert_int_eq(params->threshold_actions.len, 0);\
+ ck_assert(params->aa_only);\
+} while (0)
+
+#define CLI_ASSERT_SINGLE_ACTION(_actions, _type, _arg, _valtype, _value) do {\
+ ck_assert_int_eq(params->_actions.len, 1);\
+ ck_assert_int_eq(params->_actions.list[0].type, _type);\
+ ck_assert_##_valtype##_eq(params->_actions.list[0]._arg, _value);\
+} while (0)
diff --git a/tools/tracing/rtla/tests/unit/osnoise_hist_cli.c b/tools/tracing/rtla/tests/unit/osnoise_hist_cli.c
new file mode 100644
index 000000000000..221985e6759f
--- /dev/null
+++ b/tools/tracing/rtla/tests/unit/osnoise_hist_cli.c
@@ -0,0 +1,575 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <check.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/sysinfo.h>
+
+#include "cli_params_assert.h"
+#include "../../src/cli.h"
+
+#define PARSE_ARGS(...) char *argv[] = { __VA_ARGS__, NULL };\
+ int argc = sizeof(argv) / sizeof(char *) - 1;\
+ struct common_params *params =\
+ osnoise_hist_parse_args(argc, argv);\
+ struct osnoise_params *osn_params __maybe_unused =\
+ to_osnoise_params(params)
+
+/* Tracing Options */
+
+START_TEST(test_period_short)
+{
+ PARSE_ARGS("osnoise", "hist", "-p", "100000");
+
+ ck_assert_int_eq(osn_params->period, 100000);
+}
+END_TEST
+
+START_TEST(test_period_long)
+{
+ PARSE_ARGS("osnoise", "hist", "--period", "100000");
+
+ ck_assert_int_eq(osn_params->period, 100000);
+}
+END_TEST
+
+START_TEST(test_period_unset_short)
+{
+ PARSE_ARGS("osnoise", "hist", "-p", "100000", "--no-period");
+
+ ck_assert_int_eq(osn_params->period, 0);
+}
+END_TEST
+
+START_TEST(test_period_unset_long)
+{
+ PARSE_ARGS("osnoise", "hist", "--period", "100000", "--no-period");
+
+ ck_assert_int_eq(osn_params->period, 0);
+}
+END_TEST
+
+START_TEST(test_runtime_short)
+{
+ PARSE_ARGS("osnoise", "hist", "-r", "95000");
+
+ ck_assert_int_eq(osn_params->runtime, 95000);
+}
+END_TEST
+
+START_TEST(test_runtime_long)
+{
+ PARSE_ARGS("osnoise", "hist", "--runtime", "95000");
+
+ ck_assert_int_eq(osn_params->runtime, 95000);
+}
+END_TEST
+
+START_TEST(test_stop_short)
+{
+ PARSE_ARGS("osnoise", "hist", "-s", "20");
+
+ ck_assert_int_eq(params->stop_us, 20);
+}
+END_TEST
+
+START_TEST(test_stop_long)
+{
+ PARSE_ARGS("osnoise", "hist", "--stop", "20");
+
+ ck_assert_int_eq(params->stop_us, 20);
+}
+END_TEST
+
+START_TEST(test_stop_total_short)
+{
+ PARSE_ARGS("osnoise", "hist", "-S", "20");
+
+ ck_assert_int_eq(params->stop_total_us, 20);
+}
+END_TEST
+
+START_TEST(test_stop_total_long)
+{
+ PARSE_ARGS("osnoise", "hist", "--stop-total", "20");
+
+ ck_assert_int_eq(params->stop_total_us, 20);
+}
+END_TEST
+
+START_TEST(test_threshold_short)
+{
+ PARSE_ARGS("osnoise", "hist", "-T", "5");
+
+ ck_assert_int_eq(osn_params->threshold, 5);
+}
+END_TEST
+
+START_TEST(test_threshold_long)
+{
+ PARSE_ARGS("osnoise", "hist", "--threshold", "5");
+
+ ck_assert_int_eq(osn_params->threshold, 5);
+}
+END_TEST
+
+START_TEST(test_trace_short_noarg)
+{
+ PARSE_ARGS("osnoise", "hist", "-t");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "osnoise_trace.txt");
+}
+END_TEST
+
+START_TEST(test_trace_short_followarg)
+{
+ PARSE_ARGS("osnoise", "hist", "-t", "-d", "20");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "osnoise_trace.txt");
+ ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
+}
+END_TEST
+
+START_TEST(test_trace_short_space)
+{
+ PARSE_ARGS("osnoise", "hist", "-t", "tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+START_TEST(test_trace_short_equals)
+{
+ PARSE_ARGS("osnoise", "hist", "-t=tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+START_TEST(test_trace_long_noarg)
+{
+ PARSE_ARGS("osnoise", "hist", "--trace");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "osnoise_trace.txt");
+}
+END_TEST
+
+START_TEST(test_trace_long_followarg)
+{
+ PARSE_ARGS("osnoise", "hist", "--trace", "-d", "20");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "osnoise_trace.txt");
+ ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
+}
+END_TEST
+
+START_TEST(test_trace_long_space)
+{
+ PARSE_ARGS("osnoise", "hist", "--trace", "tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+START_TEST(test_trace_long_equals)
+{
+ PARSE_ARGS("osnoise", "hist", "--trace=tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+/* Event Configuration */
+
+START_TEST(test_event_short)
+{
+ PARSE_ARGS("osnoise", "hist", "-e", "system:event");
+
+ CLI_ASSERT_SINGLE_EVENT("system", "event");
+}
+END_TEST
+
+START_TEST(test_event_long)
+{
+ PARSE_ARGS("osnoise", "hist", "--event", "system:event");
+
+ CLI_ASSERT_SINGLE_EVENT("system", "event");
+}
+END_TEST
+
+START_TEST(test_filter)
+{
+ PARSE_ARGS("osnoise", "hist", "-e", "system:event", "--filter", "filter");
+
+ CLI_ASSERT_SINGLE_FILTER("filter");
+}
+END_TEST
+
+START_TEST(test_trigger)
+{
+ PARSE_ARGS("osnoise", "hist", "-e", "system:event", "--trigger", "trigger");
+
+ CLI_ASSERT_SINGLE_TRIGGER("trigger");
+}
+END_TEST
+
+/* CPU Configuration */
+
+START_TEST(test_cpus_short)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("osnoise", "hist", "-c", "0-1,3");
+
+ ck_assert_str_eq(params->cpus, "0-1,3");
+ CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
+}
+END_TEST
+
+START_TEST(test_cpus_long)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("osnoise", "hist", "--cpus", "0-1,3");
+
+ ck_assert_str_eq(params->cpus, "0-1,3");
+ CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
+}
+END_TEST
+
+START_TEST(test_housekeeping_short)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("osnoise", "hist", "-H", "0-1,3");
+
+ CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
+}
+END_TEST
+
+START_TEST(test_housekeeping_long)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("osnoise", "hist", "--house-keeping", "0-1,3");
+
+ CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
+}
+END_TEST
+
+/* Thread Configuration */
+
+START_TEST(test_cgroup_short_noarg)
+{
+ PARSE_ARGS("osnoise", "hist", "-C");
+
+ ck_assert(params->cgroup);
+ ck_assert_ptr_null(params->cgroup_name);
+}
+END_TEST
+
+START_TEST(test_cgroup_short_space)
+{
+ PARSE_ARGS("osnoise", "hist", "-C", "cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_cgroup_short_equals)
+{
+ PARSE_ARGS("osnoise", "hist", "-C=cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_cgroup_long_noarg)
+{
+ PARSE_ARGS("osnoise", "hist", "--cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_ptr_null(params->cgroup_name);
+}
+END_TEST
+
+START_TEST(test_cgroup_long_space)
+{
+ PARSE_ARGS("osnoise", "hist", "--cgroup", "cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_cgroup_long_equals)
+{
+ PARSE_ARGS("osnoise", "hist", "--cgroup=cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_priority_short)
+{
+ PARSE_ARGS("osnoise", "hist", "-P", "f:95");
+
+ ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
+ ck_assert_int_eq(params->sched_param.sched_priority, 95);
+}
+END_TEST
+
+START_TEST(test_priority_long)
+{
+ PARSE_ARGS("osnoise", "hist", "--priority", "f:95");
+
+ ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
+ ck_assert_int_eq(params->sched_param.sched_priority, 95);
+}
+END_TEST
+
+/* Histogram Options */
+
+START_TEST(test_bucket_size_short)
+{
+ PARSE_ARGS("osnoise", "hist", "-b", "2");
+
+ ck_assert_int_eq(params->hist.bucket_size, 2);
+}
+END_TEST
+
+START_TEST(test_bucket_size_long)
+{
+ PARSE_ARGS("osnoise", "hist", "--bucket-size", "2");
+
+ ck_assert_int_eq(params->hist.bucket_size, 2);
+}
+END_TEST
+
+START_TEST(test_entries_short)
+{
+ PARSE_ARGS("osnoise", "hist", "-E", "512");
+
+ ck_assert_int_eq(params->hist.entries, 512);
+}
+END_TEST
+
+START_TEST(test_entries_long)
+{
+ PARSE_ARGS("osnoise", "hist", "--entries", "512");
+
+ ck_assert_int_eq(params->hist.entries, 512);
+}
+END_TEST
+
+START_TEST(test_no_header)
+{
+ PARSE_ARGS("osnoise", "hist", "--no-header");
+
+ ck_assert(params->hist.no_header);
+}
+END_TEST
+
+START_TEST(test_no_index)
+{
+ PARSE_ARGS("osnoise", "hist", "--with-zeros", "--no-index");
+
+ ck_assert(params->hist.no_index);
+}
+END_TEST
+
+START_TEST(test_no_summary)
+{
+ PARSE_ARGS("osnoise", "hist", "--no-summary");
+
+ ck_assert(params->hist.no_summary);
+}
+END_TEST
+
+START_TEST(test_with_zeros)
+{
+ PARSE_ARGS("osnoise", "hist", "--with-zeros");
+
+ ck_assert(params->hist.with_zeros);
+}
+END_TEST
+
+/* System Tuning */
+
+START_TEST(test_trace_buffer_size)
+{
+ PARSE_ARGS("osnoise", "hist", "--trace-buffer-size", "200");
+
+ ck_assert_int_eq(params->buffer_size, 200);
+}
+END_TEST
+
+START_TEST(test_warm_up)
+{
+ PARSE_ARGS("osnoise", "hist", "--warm-up", "5");
+
+ ck_assert_int_eq(params->warmup, 5);
+}
+END_TEST
+
+/* Auto Analysis and Actions */
+
+START_TEST(test_auto)
+{
+ PARSE_ARGS("osnoise", "hist", "-a", "20");
+
+ CLI_OSNOISE_ASSERT_AUTO(20);
+}
+END_TEST
+
+START_TEST(test_on_end)
+{
+ PARSE_ARGS("osnoise", "hist", "--on-end", "trace");
+
+ CLI_ASSERT_SINGLE_ACTION(end_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "osnoise_trace.txt");
+}
+END_TEST
+
+START_TEST(test_on_threshold)
+{
+ PARSE_ARGS("osnoise", "hist", "--on-threshold", "trace");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "osnoise_trace.txt");
+}
+END_TEST
+
+/* General */
+
+START_TEST(test_debug_short)
+{
+ PARSE_ARGS("osnoise", "hist", "-D");
+
+ ck_assert(config_debug);
+}
+END_TEST
+
+START_TEST(test_debug_long)
+{
+ PARSE_ARGS("osnoise", "hist", "--debug");
+
+ ck_assert(config_debug);
+}
+END_TEST
+
+START_TEST(test_duration_short)
+{
+ PARSE_ARGS("osnoise", "hist", "-d", "1m");
+
+ ck_assert_int_eq(params->duration, 60);
+}
+END_TEST
+
+START_TEST(test_duration_long)
+{
+ PARSE_ARGS("osnoise", "hist", "--duration", "1m");
+
+ ck_assert_int_eq(params->duration, 60);
+}
+END_TEST
+
+Suite *osnoise_hist_cli_suite(void)
+{
+ Suite *s = suite_create("osnoise_hist_cli");
+ TCase *tc;
+
+ tc = tcase_create("tracing_options");
+ tcase_add_test(tc, test_period_short);
+ tcase_add_test(tc, test_period_long);
+ tcase_add_test(tc, test_period_unset_short);
+ tcase_add_test(tc, test_period_unset_long);
+ tcase_add_test(tc, test_runtime_short);
+ tcase_add_test(tc, test_runtime_long);
+ tcase_add_test(tc, test_stop_short);
+ tcase_add_test(tc, test_stop_long);
+ tcase_add_test(tc, test_stop_total_short);
+ tcase_add_test(tc, test_stop_total_long);
+ tcase_add_test(tc, test_threshold_short);
+ tcase_add_test(tc, test_threshold_long);
+ tcase_add_test(tc, test_trace_short_noarg);
+ tcase_add_test(tc, test_trace_short_followarg);
+ tcase_add_test(tc, test_trace_short_space);
+ tcase_add_test(tc, test_trace_short_equals);
+ tcase_add_test(tc, test_trace_long_noarg);
+ tcase_add_test(tc, test_trace_long_followarg);
+ tcase_add_test(tc, test_trace_long_space);
+ tcase_add_test(tc, test_trace_long_equals);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("event_configuration");
+ tcase_add_test(tc, test_event_short);
+ tcase_add_test(tc, test_event_long);
+ tcase_add_test(tc, test_filter);
+ tcase_add_test(tc, test_trigger);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("cpu_configuration");
+ tcase_add_test(tc, test_cpus_short);
+ tcase_add_test(tc, test_cpus_long);
+ tcase_add_test(tc, test_housekeeping_short);
+ tcase_add_test(tc, test_housekeeping_long);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("thread_configuration");
+ tcase_add_test(tc, test_cgroup_short_noarg);
+ tcase_add_test(tc, test_cgroup_short_space);
+ tcase_add_test(tc, test_cgroup_short_equals);
+ tcase_add_test(tc, test_cgroup_long_noarg);
+ tcase_add_test(tc, test_cgroup_long_space);
+ tcase_add_test(tc, test_cgroup_long_equals);
+ tcase_add_test(tc, test_priority_short);
+ tcase_add_test(tc, test_priority_long);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("histogram_options");
+ tcase_add_test(tc, test_bucket_size_short);
+ tcase_add_test(tc, test_bucket_size_long);
+ tcase_add_test(tc, test_entries_short);
+ tcase_add_test(tc, test_entries_long);
+ tcase_add_test(tc, test_no_header);
+ tcase_add_test(tc, test_no_index);
+ tcase_add_test(tc, test_no_summary);
+ tcase_add_test(tc, test_with_zeros);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("system_tuning");
+ tcase_add_test(tc, test_trace_buffer_size);
+ tcase_add_test(tc, test_warm_up);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("aa_actions");
+ tcase_add_test(tc, test_auto);
+ tcase_add_test(tc, test_on_end);
+ tcase_add_test(tc, test_on_threshold);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("general");
+ tcase_add_test(tc, test_debug_short);
+ tcase_add_test(tc, test_debug_long);
+ tcase_add_test(tc, test_duration_short);
+ tcase_add_test(tc, test_duration_long);
+ suite_add_tcase(s, tc);
+
+ return s;
+}
diff --git a/tools/tracing/rtla/tests/unit/osnoise_top_cli.c b/tools/tracing/rtla/tests/unit/osnoise_top_cli.c
new file mode 100644
index 000000000000..057dbe574b07
--- /dev/null
+++ b/tools/tracing/rtla/tests/unit/osnoise_top_cli.c
@@ -0,0 +1,521 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <check.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/sysinfo.h>
+
+#include "cli_params_assert.h"
+#include "../../src/cli.h"
+
+#define PARSE_ARGS(...) char *argv[] = { __VA_ARGS__, NULL };\
+ int argc = sizeof(argv) / sizeof(char *) - 1;\
+ struct common_params *params =\
+ osnoise_top_parse_args(argc, argv);\
+ struct osnoise_params *osn_params __maybe_unused =\
+ to_osnoise_params(params)
+
+/* Tracing Options */
+
+START_TEST(test_period_short)
+{
+ PARSE_ARGS("osnoise", "top", "-p", "100000");
+
+ ck_assert_int_eq(osn_params->period, 100000);
+}
+END_TEST
+
+START_TEST(test_period_long)
+{
+ PARSE_ARGS("osnoise", "top", "--period", "100000");
+
+ ck_assert_int_eq(osn_params->period, 100000);
+}
+END_TEST
+
+START_TEST(test_period_unset_short)
+{
+ PARSE_ARGS("osnoise", "top", "-p", "100000", "--no-period");
+
+ ck_assert_int_eq(osn_params->period, 0);
+}
+END_TEST
+
+START_TEST(test_period_unset_long)
+{
+ PARSE_ARGS("osnoise", "top", "--period", "100000", "--no-period");
+
+ ck_assert_int_eq(osn_params->period, 0);
+}
+END_TEST
+
+START_TEST(test_runtime_short)
+{
+ PARSE_ARGS("osnoise", "top", "-r", "95000");
+
+ ck_assert_int_eq(osn_params->runtime, 95000);
+}
+END_TEST
+
+START_TEST(test_runtime_long)
+{
+ PARSE_ARGS("osnoise", "top", "--runtime", "95000");
+
+ ck_assert_int_eq(osn_params->runtime, 95000);
+}
+END_TEST
+
+START_TEST(test_stop_short)
+{
+ PARSE_ARGS("osnoise", "top", "-s", "20");
+
+ ck_assert_int_eq(params->stop_us, 20);
+}
+END_TEST
+
+START_TEST(test_stop_long)
+{
+ PARSE_ARGS("osnoise", "top", "--stop", "20");
+
+ ck_assert_int_eq(params->stop_us, 20);
+}
+END_TEST
+
+START_TEST(test_stop_total_short)
+{
+ PARSE_ARGS("osnoise", "top", "-S", "20");
+
+ ck_assert_int_eq(params->stop_total_us, 20);
+}
+END_TEST
+
+START_TEST(test_stop_total_long)
+{
+ PARSE_ARGS("osnoise", "top", "--stop-total", "20");
+
+ ck_assert_int_eq(params->stop_total_us, 20);
+}
+END_TEST
+
+START_TEST(test_threshold_short)
+{
+ PARSE_ARGS("osnoise", "top", "-T", "5");
+
+ ck_assert_int_eq(osn_params->threshold, 5);
+}
+END_TEST
+
+START_TEST(test_threshold_long)
+{
+ PARSE_ARGS("osnoise", "top", "--threshold", "5");
+
+ ck_assert_int_eq(osn_params->threshold, 5);
+}
+END_TEST
+
+START_TEST(test_trace_short_noarg)
+{
+ PARSE_ARGS("osnoise", "top", "-t");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "osnoise_trace.txt");
+}
+END_TEST
+
+START_TEST(test_trace_short_followarg)
+{
+ PARSE_ARGS("osnoise", "top", "-t", "-d", "20");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "osnoise_trace.txt");
+ ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
+}
+END_TEST
+
+START_TEST(test_trace_short_space)
+{
+ PARSE_ARGS("osnoise", "top", "-t", "tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+START_TEST(test_trace_short_equals)
+{
+ PARSE_ARGS("osnoise", "top", "-t=tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+START_TEST(test_trace_long_noarg)
+{
+ PARSE_ARGS("osnoise", "top", "--trace");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "osnoise_trace.txt");
+}
+END_TEST
+
+START_TEST(test_trace_long_followarg)
+{
+ PARSE_ARGS("osnoise", "top", "--trace", "-d", "20");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "osnoise_trace.txt");
+ ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
+}
+END_TEST
+
+START_TEST(test_trace_long_space)
+{
+ PARSE_ARGS("osnoise", "top", "--trace", "tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+START_TEST(test_trace_long_equals)
+{
+ PARSE_ARGS("osnoise", "top", "--trace=tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+/* Event Configuration */
+
+START_TEST(test_event_short)
+{
+ PARSE_ARGS("osnoise", "top", "-e", "system:event");
+
+ CLI_ASSERT_SINGLE_EVENT("system", "event");
+}
+END_TEST
+
+START_TEST(test_event_long)
+{
+ PARSE_ARGS("osnoise", "top", "--event", "system:event");
+
+ CLI_ASSERT_SINGLE_EVENT("system", "event");
+}
+END_TEST
+
+START_TEST(test_filter)
+{
+ PARSE_ARGS("osnoise", "top", "-e", "system:event", "--filter", "filter");
+
+ CLI_ASSERT_SINGLE_FILTER("filter");
+}
+END_TEST
+
+START_TEST(test_trigger)
+{
+ PARSE_ARGS("osnoise", "top", "-e", "system:event", "--trigger", "trigger");
+
+ CLI_ASSERT_SINGLE_TRIGGER("trigger");
+}
+END_TEST
+
+/* CPU Configuration */
+
+START_TEST(test_cpus_short)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("osnoise", "top", "-c", "0-1,3");
+
+ ck_assert_str_eq(params->cpus, "0-1,3");
+ CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
+}
+END_TEST
+
+START_TEST(test_cpus_long)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("osnoise", "top", "--cpus", "0-1,3");
+
+ ck_assert_str_eq(params->cpus, "0-1,3");
+ CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
+}
+END_TEST
+
+START_TEST(test_housekeeping_short)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("osnoise", "top", "-H", "0-1,3");
+
+ CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
+}
+END_TEST
+
+START_TEST(test_housekeeping_long)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("osnoise", "top", "--house-keeping", "0-1,3");
+
+ CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
+}
+END_TEST
+
+/* Thread Configuration */
+
+START_TEST(test_cgroup_short_noarg)
+{
+ PARSE_ARGS("osnoise", "top", "-C");
+
+ ck_assert(params->cgroup);
+ ck_assert_ptr_null(params->cgroup_name);
+}
+END_TEST
+
+START_TEST(test_cgroup_short_space)
+{
+ PARSE_ARGS("osnoise", "top", "-C", "cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_cgroup_short_equals)
+{
+ PARSE_ARGS("osnoise", "top", "-C=cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_cgroup_long_noarg)
+{
+ PARSE_ARGS("osnoise", "top", "--cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_ptr_null(params->cgroup_name);
+}
+END_TEST
+
+START_TEST(test_cgroup_long_space)
+{
+ PARSE_ARGS("osnoise", "top", "--cgroup", "cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_cgroup_long_equals)
+{
+ PARSE_ARGS("osnoise", "top", "--cgroup=cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_priority_short)
+{
+ PARSE_ARGS("osnoise", "top", "-P", "f:95");
+
+ ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
+ ck_assert_int_eq(params->sched_param.sched_priority, 95);
+}
+END_TEST
+
+START_TEST(test_priority_long)
+{
+ PARSE_ARGS("osnoise", "top", "--priority", "f:95");
+
+ ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
+ ck_assert_int_eq(params->sched_param.sched_priority, 95);
+}
+END_TEST
+
+/* Output */
+
+START_TEST(test_quiet_short)
+{
+ PARSE_ARGS("osnoise", "top", "-q");
+
+ ck_assert(params->quiet);
+}
+END_TEST
+
+START_TEST(test_quiet_long)
+{
+ PARSE_ARGS("osnoise", "top", "--quiet");
+
+ ck_assert(params->quiet);
+}
+END_TEST
+
+/* Auto Analysis and Actions */
+
+START_TEST(test_auto)
+{
+ PARSE_ARGS("osnoise", "top", "-a", "20");
+
+ CLI_OSNOISE_ASSERT_AUTO(20);
+}
+END_TEST
+
+START_TEST(test_on_end)
+{
+ PARSE_ARGS("osnoise", "top", "--on-end", "trace");
+
+ CLI_ASSERT_SINGLE_ACTION(end_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "osnoise_trace.txt");
+}
+END_TEST
+
+START_TEST(test_on_threshold)
+{
+ PARSE_ARGS("osnoise", "top", "--on-threshold", "trace");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "osnoise_trace.txt");
+}
+END_TEST
+
+/* System Tuning */
+
+START_TEST(test_trace_buffer_size)
+{
+ PARSE_ARGS("osnoise", "top", "--trace-buffer-size", "200");
+
+ ck_assert_int_eq(params->buffer_size, 200);
+}
+END_TEST
+
+START_TEST(test_warm_up)
+{
+ PARSE_ARGS("osnoise", "top", "--warm-up", "5");
+
+ ck_assert_int_eq(params->warmup, 5);
+}
+END_TEST
+
+/* General */
+
+START_TEST(test_debug_short)
+{
+ PARSE_ARGS("osnoise", "top", "-D");
+
+ ck_assert(config_debug);
+}
+END_TEST
+
+START_TEST(test_debug_long)
+{
+ PARSE_ARGS("osnoise", "top", "--debug");
+
+ ck_assert(config_debug);
+}
+END_TEST
+
+START_TEST(test_duration_short)
+{
+ PARSE_ARGS("osnoise", "top", "-d", "1m");
+
+ ck_assert_int_eq(params->duration, 60);
+}
+END_TEST
+
+START_TEST(test_duration_long)
+{
+ PARSE_ARGS("osnoise", "top", "--duration", "1m");
+
+ ck_assert_int_eq(params->duration, 60);
+}
+END_TEST
+
+Suite *osnoise_top_cli_suite(void)
+{
+ Suite *s = suite_create("osnoise_top_cli");
+ TCase *tc;
+
+ tc = tcase_create("tracing_options");
+ tcase_add_test(tc, test_period_short);
+ tcase_add_test(tc, test_period_long);
+ tcase_add_test(tc, test_period_unset_short);
+ tcase_add_test(tc, test_period_unset_long);
+ tcase_add_test(tc, test_runtime_short);
+ tcase_add_test(tc, test_runtime_long);
+ tcase_add_test(tc, test_stop_short);
+ tcase_add_test(tc, test_stop_long);
+ tcase_add_test(tc, test_stop_total_short);
+ tcase_add_test(tc, test_stop_total_long);
+ tcase_add_test(tc, test_threshold_short);
+ tcase_add_test(tc, test_threshold_long);
+ tcase_add_test(tc, test_trace_short_noarg);
+ tcase_add_test(tc, test_trace_short_followarg);
+ tcase_add_test(tc, test_trace_short_space);
+ tcase_add_test(tc, test_trace_short_equals);
+ tcase_add_test(tc, test_trace_long_noarg);
+ tcase_add_test(tc, test_trace_long_followarg);
+ tcase_add_test(tc, test_trace_long_space);
+ tcase_add_test(tc, test_trace_long_equals);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("event_configuration");
+ tcase_add_test(tc, test_event_short);
+ tcase_add_test(tc, test_event_long);
+ tcase_add_test(tc, test_filter);
+ tcase_add_test(tc, test_trigger);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("cpu_configuration");
+ tcase_add_test(tc, test_cpus_short);
+ tcase_add_test(tc, test_cpus_long);
+ tcase_add_test(tc, test_housekeeping_short);
+ tcase_add_test(tc, test_housekeeping_long);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("thread_configuration");
+ tcase_add_test(tc, test_cgroup_short_noarg);
+ tcase_add_test(tc, test_cgroup_short_space);
+ tcase_add_test(tc, test_cgroup_short_equals);
+ tcase_add_test(tc, test_cgroup_long_noarg);
+ tcase_add_test(tc, test_cgroup_long_space);
+ tcase_add_test(tc, test_cgroup_long_equals);
+ tcase_add_test(tc, test_priority_short);
+ tcase_add_test(tc, test_priority_long);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("output");
+ tcase_add_test(tc, test_quiet_short);
+ tcase_add_test(tc, test_quiet_long);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("system_tuning");
+ tcase_add_test(tc, test_trace_buffer_size);
+ tcase_add_test(tc, test_warm_up);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("aa_actions");
+ tcase_add_test(tc, test_auto);
+ tcase_add_test(tc, test_on_end);
+ tcase_add_test(tc, test_on_threshold);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("general");
+ tcase_add_test(tc, test_debug_short);
+ tcase_add_test(tc, test_debug_long);
+ tcase_add_test(tc, test_duration_short);
+ tcase_add_test(tc, test_duration_long);
+ suite_add_tcase(s, tc);
+
+ return s;
+}
diff --git a/tools/tracing/rtla/tests/unit/timerlat_hist_cli.c b/tools/tracing/rtla/tests/unit/timerlat_hist_cli.c
new file mode 100644
index 000000000000..d8dd9d752636
--- /dev/null
+++ b/tools/tracing/rtla/tests/unit/timerlat_hist_cli.c
@@ -0,0 +1,740 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <check.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/sysinfo.h>
+
+#include <linux/container_of.h>
+
+#include "cli_params_assert.h"
+#include "../../src/cli.h"
+
+#define PARSE_ARGS(...) char *argv[] = { __VA_ARGS__, NULL };\
+ int argc = sizeof(argv) / sizeof(char *) - 1;\
+ struct common_params *params =\
+ timerlat_hist_parse_args(argc, argv);\
+ struct timerlat_params *tlat_params __maybe_unused =\
+ to_timerlat_params(params)
+
+/* Tracing Options */
+
+START_TEST(test_irq_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-i", "20");
+
+ ck_assert_int_eq(params->stop_us, 20);
+}
+END_TEST
+
+START_TEST(test_irq_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--irq", "20");
+
+ ck_assert_int_eq(params->stop_us, 20);
+}
+END_TEST
+
+START_TEST(test_period_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-p", "200");
+
+ ck_assert_int_eq(tlat_params->timerlat_period_us, 200);
+}
+END_TEST
+
+START_TEST(test_period_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--period", "200");
+
+ ck_assert_int_eq(tlat_params->timerlat_period_us, 200);
+}
+END_TEST
+
+START_TEST(test_period_unset_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-p", "200", "--no-period");
+
+ ck_assert_int_eq(tlat_params->timerlat_period_us, 0);
+}
+END_TEST
+
+START_TEST(test_period_unset_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--period", "200", "--no-period");
+
+ ck_assert_int_eq(tlat_params->timerlat_period_us, 0);
+}
+END_TEST
+
+START_TEST(test_stack_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-s", "20");
+
+ ck_assert_int_eq(tlat_params->print_stack, 20);
+}
+END_TEST
+
+START_TEST(test_stack_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--stack", "20");
+
+ ck_assert_int_eq(tlat_params->print_stack, 20);
+}
+END_TEST
+
+START_TEST(test_thread_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-T", "20");
+
+ ck_assert_int_eq(params->stop_total_us, 20);
+}
+END_TEST
+
+START_TEST(test_thread_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--thread", "20");
+
+ ck_assert_int_eq(params->stop_total_us, 20);
+}
+END_TEST
+
+START_TEST(test_trace_short_noarg)
+{
+ PARSE_ARGS("timerlat", "hist", "-t");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "timerlat_trace.txt");
+}
+END_TEST
+
+START_TEST(test_trace_short_followarg)
+{
+ PARSE_ARGS("timerlat", "hist", "-t", "-d", "20");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "timerlat_trace.txt");
+ ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
+}
+END_TEST
+
+START_TEST(test_trace_short_space)
+{
+ PARSE_ARGS("timerlat", "hist", "-t", "tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+START_TEST(test_trace_short_equals)
+{
+ PARSE_ARGS("timerlat", "hist", "-t=tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+START_TEST(test_trace_long_noarg)
+{
+ PARSE_ARGS("timerlat", "hist", "--trace");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "timerlat_trace.txt");
+}
+END_TEST
+
+START_TEST(test_trace_long_followarg)
+{
+ PARSE_ARGS("timerlat", "hist", "--trace", "-d", "20");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "timerlat_trace.txt");
+ ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
+}
+END_TEST
+
+START_TEST(test_trace_long_space)
+{
+ PARSE_ARGS("timerlat", "hist", "--trace", "tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+START_TEST(test_trace_long_equals)
+{
+ PARSE_ARGS("timerlat", "hist", "--trace=tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+/* Event Configuration */
+
+START_TEST(test_event_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-e", "system:event");
+
+ CLI_ASSERT_SINGLE_EVENT("system", "event");
+}
+END_TEST
+
+START_TEST(test_event_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--event", "system:event");
+
+ CLI_ASSERT_SINGLE_EVENT("system", "event");
+}
+END_TEST
+
+START_TEST(test_filter)
+{
+ PARSE_ARGS("timerlat", "hist", "-e", "system:event", "--filter", "filter");
+
+ CLI_ASSERT_SINGLE_FILTER("filter");
+}
+END_TEST
+
+START_TEST(test_trigger)
+{
+ PARSE_ARGS("timerlat", "hist", "-e", "system:event", "--trigger", "trigger");
+
+ CLI_ASSERT_SINGLE_TRIGGER("trigger");
+}
+END_TEST
+
+/* CPU Configuration */
+
+START_TEST(test_cpus_short)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("timerlat", "hist", "-c", "0-1,3");
+
+ ck_assert_str_eq(params->cpus, "0-1,3");
+ CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
+}
+END_TEST
+
+START_TEST(test_cpus_long)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("timerlat", "hist", "--cpus", "0-1,3");
+
+ ck_assert_str_eq(params->cpus, "0-1,3");
+ CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
+}
+END_TEST
+
+START_TEST(test_housekeeping_short)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("timerlat", "hist", "-H", "0-1,3");
+
+ CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
+}
+END_TEST
+
+START_TEST(test_housekeeping_long)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("timerlat", "hist", "--house-keeping", "0-1,3");
+
+ CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
+}
+END_TEST
+
+/* Thread Configuration */
+
+START_TEST(test_cgroup_short_noarg)
+{
+ PARSE_ARGS("timerlat", "hist", "-C");
+
+ ck_assert(params->cgroup);
+ ck_assert_ptr_null(params->cgroup_name);
+}
+END_TEST
+
+START_TEST(test_cgroup_short_space)
+{
+ PARSE_ARGS("timerlat", "hist", "-C", "cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_cgroup_short_equals)
+{
+ PARSE_ARGS("timerlat", "hist", "-C=cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_cgroup_long_noarg)
+{
+ PARSE_ARGS("timerlat", "hist", "--cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_ptr_null(params->cgroup_name);
+}
+END_TEST
+
+START_TEST(test_cgroup_long_space)
+{
+ PARSE_ARGS("timerlat", "hist", "--cgroup", "cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_cgroup_long_equals)
+{
+ PARSE_ARGS("timerlat", "hist", "--cgroup=cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_kernel_threads_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-k");
+
+ ck_assert(params->kernel_workload);
+ ck_assert(!params->user_workload);
+ ck_assert(!params->user_data);
+}
+END_TEST
+
+START_TEST(test_kernel_threads_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--kernel-threads");
+
+ ck_assert(params->kernel_workload);
+ ck_assert(!params->user_workload);
+ ck_assert(!params->user_data);
+}
+END_TEST
+
+START_TEST(test_priority_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-P", "f:95");
+
+ ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
+ ck_assert_int_eq(params->sched_param.sched_priority, 95);
+}
+END_TEST
+
+START_TEST(test_priority_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--priority", "f:95");
+
+ ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
+ ck_assert_int_eq(params->sched_param.sched_priority, 95);
+}
+END_TEST
+
+START_TEST(test_user_load_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-U");
+
+ ck_assert(!params->kernel_workload);
+ ck_assert(!params->user_workload);
+ ck_assert(params->user_data);
+}
+END_TEST
+
+START_TEST(test_user_load_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--user-load");
+
+ ck_assert(!params->kernel_workload);
+ ck_assert(!params->user_workload);
+ ck_assert(params->user_data);
+}
+END_TEST
+
+START_TEST(test_user_threads_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-u");
+
+ ck_assert(!params->kernel_workload);
+ ck_assert(params->user_workload);
+ ck_assert(params->user_data);
+}
+END_TEST
+
+START_TEST(test_user_threads_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--user-threads");
+
+ ck_assert(!params->kernel_workload);
+ ck_assert(params->user_workload);
+ ck_assert(params->user_data);
+}
+END_TEST
+
+START_TEST(test_aligned_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-A", "500");
+
+ ck_assert(tlat_params->timerlat_align);
+ ck_assert_int_eq(tlat_params->timerlat_align_us, 500);
+}
+END_TEST
+
+START_TEST(test_aligned_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--aligned", "500");
+
+ ck_assert(tlat_params->timerlat_align);
+ ck_assert_int_eq(tlat_params->timerlat_align_us, 500);
+}
+END_TEST
+
+/* Histogram Options */
+
+START_TEST(test_bucket_size_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-b", "2");
+
+ ck_assert_int_eq(params->hist.bucket_size, 2);
+}
+END_TEST
+
+START_TEST(test_bucket_size_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--bucket-size", "2");
+
+ ck_assert_int_eq(params->hist.bucket_size, 2);
+}
+END_TEST
+
+START_TEST(test_entries_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-E", "512");
+
+ ck_assert_int_eq(params->hist.entries, 512);
+}
+END_TEST
+
+START_TEST(test_entries_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--entries", "512");
+
+ ck_assert_int_eq(params->hist.entries, 512);
+}
+END_TEST
+
+START_TEST(test_no_header)
+{
+ PARSE_ARGS("timerlat", "hist", "--no-header");
+
+ ck_assert(params->hist.no_header);
+}
+END_TEST
+
+START_TEST(test_no_index)
+{
+ PARSE_ARGS("timerlat", "hist", "--with-zeros", "--no-index");
+
+ ck_assert(params->hist.no_index);
+}
+END_TEST
+
+START_TEST(test_no_irq)
+{
+ PARSE_ARGS("timerlat", "hist", "--no-irq");
+
+ ck_assert(params->hist.no_irq);
+}
+END_TEST
+
+START_TEST(test_no_summary)
+{
+ PARSE_ARGS("timerlat", "hist", "--no-summary");
+
+ ck_assert(params->hist.no_summary);
+}
+END_TEST
+
+START_TEST(test_no_thread)
+{
+ PARSE_ARGS("timerlat", "hist", "--no-thread");
+
+ ck_assert(params->hist.no_thread);
+}
+END_TEST
+
+START_TEST(test_with_zeros)
+{
+ PARSE_ARGS("timerlat", "hist", "--with-zeros");
+
+ ck_assert(params->hist.with_zeros);
+}
+END_TEST
+
+/* Output */
+
+START_TEST(test_nano_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-n");
+
+ ck_assert_int_eq(params->output_divisor, 1);
+}
+END_TEST
+
+START_TEST(test_nano_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--nano");
+
+ ck_assert_int_eq(params->output_divisor, 1);
+}
+END_TEST
+
+/* System Tuning */
+
+START_TEST(test_deepest_idle_state)
+{
+ PARSE_ARGS("timerlat", "hist", "--deepest-idle-state", "1");
+
+ ck_assert_int_eq(tlat_params->deepest_idle_state, 1);
+}
+END_TEST
+
+START_TEST(test_dma_latency)
+{
+ PARSE_ARGS("timerlat", "hist", "--dma-latency", "10");
+
+ ck_assert_int_eq(tlat_params->dma_latency, 10);
+}
+END_TEST
+
+START_TEST(test_trace_buffer_size)
+{
+ PARSE_ARGS("timerlat", "hist", "--trace-buffer-size", "200");
+
+ ck_assert_int_eq(params->buffer_size, 200);
+}
+END_TEST
+
+START_TEST(test_warm_up)
+{
+ PARSE_ARGS("timerlat", "hist", "--warm-up", "5");
+
+ ck_assert_int_eq(params->warmup, 5);
+}
+END_TEST
+
+/* Auto Analysis and Actions */
+
+START_TEST(test_auto)
+{
+ PARSE_ARGS("timerlat", "hist", "-a", "20");
+
+ CLI_TIMERLAT_ASSERT_AUTO(20);
+}
+END_TEST
+
+START_TEST(test_bpf_action)
+{
+ PARSE_ARGS("timerlat", "hist", "--bpf-action", "program");
+
+ ck_assert_str_eq(tlat_params->bpf_action_program, "program");
+}
+END_TEST
+
+START_TEST(test_dump_tasks)
+{
+ PARSE_ARGS("timerlat", "hist", "--dump-tasks");
+
+ ck_assert(tlat_params->dump_tasks);
+}
+END_TEST
+
+START_TEST(test_no_aa)
+{
+ PARSE_ARGS("timerlat", "hist", "--no-aa");
+
+ ck_assert(tlat_params->no_aa);
+}
+END_TEST
+
+START_TEST(test_on_end)
+{
+ PARSE_ARGS("timerlat", "hist", "--on-end", "trace");
+
+ CLI_ASSERT_SINGLE_ACTION(end_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "timerlat_trace.txt");
+}
+END_TEST
+
+START_TEST(test_on_threshold)
+{
+ PARSE_ARGS("timerlat", "hist", "--on-threshold", "trace");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "timerlat_trace.txt");
+}
+END_TEST
+
+START_TEST(test_stack_format)
+{
+ PARSE_ARGS("timerlat", "hist", "--stack-format", "truncate");
+
+ ck_assert_int_eq(tlat_params->stack_format, STACK_FORMAT_TRUNCATE);
+}
+END_TEST
+
+/* General */
+
+START_TEST(test_debug_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-D");
+
+ ck_assert(config_debug);
+}
+END_TEST
+
+START_TEST(test_debug_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--debug");
+
+ ck_assert(config_debug);
+}
+END_TEST
+
+START_TEST(test_duration_short)
+{
+ PARSE_ARGS("timerlat", "hist", "-d", "1m");
+
+ ck_assert_int_eq(params->duration, 60);
+}
+END_TEST
+
+START_TEST(test_duration_long)
+{
+ PARSE_ARGS("timerlat", "hist", "--duration", "1m");
+
+ ck_assert_int_eq(params->duration, 60);
+}
+END_TEST
+
+Suite *timerlat_hist_cli_suite(void)
+{
+ Suite *s = suite_create("timerlat_hist_cli");
+ TCase *tc;
+
+ tc = tcase_create("tracing_options");
+ tcase_add_test(tc, test_irq_short);
+ tcase_add_test(tc, test_irq_long);
+ tcase_add_test(tc, test_period_short);
+ tcase_add_test(tc, test_period_long);
+ tcase_add_test(tc, test_period_unset_short);
+ tcase_add_test(tc, test_period_unset_long);
+ tcase_add_test(tc, test_stack_short);
+ tcase_add_test(tc, test_stack_long);
+ tcase_add_test(tc, test_thread_short);
+ tcase_add_test(tc, test_thread_long);
+ tcase_add_test(tc, test_trace_short_noarg);
+ tcase_add_test(tc, test_trace_short_followarg);
+ tcase_add_test(tc, test_trace_short_space);
+ tcase_add_test(tc, test_trace_short_equals);
+ tcase_add_test(tc, test_trace_long_noarg);
+ tcase_add_test(tc, test_trace_long_followarg);
+ tcase_add_test(tc, test_trace_long_space);
+ tcase_add_test(tc, test_trace_long_equals);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("event_configuration");
+ tcase_add_test(tc, test_event_short);
+ tcase_add_test(tc, test_event_long);
+ tcase_add_test(tc, test_filter);
+ tcase_add_test(tc, test_trigger);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("cpu_configuration");
+ tcase_add_test(tc, test_cpus_short);
+ tcase_add_test(tc, test_cpus_long);
+ tcase_add_test(tc, test_housekeeping_short);
+ tcase_add_test(tc, test_housekeeping_long);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("thread_configuration");
+ tcase_add_test(tc, test_cgroup_short_noarg);
+ tcase_add_test(tc, test_cgroup_short_space);
+ tcase_add_test(tc, test_cgroup_short_equals);
+ tcase_add_test(tc, test_cgroup_long_noarg);
+ tcase_add_test(tc, test_cgroup_long_space);
+ tcase_add_test(tc, test_cgroup_long_equals);
+ tcase_add_test(tc, test_kernel_threads_short);
+ tcase_add_test(tc, test_kernel_threads_long);
+ tcase_add_test(tc, test_priority_short);
+ tcase_add_test(tc, test_priority_long);
+ tcase_add_test(tc, test_user_load_short);
+ tcase_add_test(tc, test_user_load_long);
+ tcase_add_test(tc, test_user_threads_short);
+ tcase_add_test(tc, test_user_threads_long);
+ tcase_add_test(tc, test_aligned_short);
+ tcase_add_test(tc, test_aligned_long);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("histogram_options");
+ tcase_add_test(tc, test_bucket_size_short);
+ tcase_add_test(tc, test_bucket_size_long);
+ tcase_add_test(tc, test_entries_short);
+ tcase_add_test(tc, test_entries_long);
+ tcase_add_test(tc, test_no_header);
+ tcase_add_test(tc, test_no_index);
+ tcase_add_test(tc, test_no_irq);
+ tcase_add_test(tc, test_no_summary);
+ tcase_add_test(tc, test_no_thread);
+ tcase_add_test(tc, test_with_zeros);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("output");
+ tcase_add_test(tc, test_nano_short);
+ tcase_add_test(tc, test_nano_long);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("system_tuning");
+ tcase_add_test(tc, test_deepest_idle_state);
+ tcase_add_test(tc, test_dma_latency);
+ tcase_add_test(tc, test_trace_buffer_size);
+ tcase_add_test(tc, test_warm_up);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("aa_actions");
+ tcase_add_test(tc, test_auto);
+ tcase_add_test(tc, test_bpf_action);
+ tcase_add_test(tc, test_dump_tasks);
+ tcase_add_test(tc, test_no_aa);
+ tcase_add_test(tc, test_on_end);
+ tcase_add_test(tc, test_on_threshold);
+ tcase_add_test(tc, test_stack_format);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("general");
+ tcase_add_test(tc, test_debug_short);
+ tcase_add_test(tc, test_debug_long);
+ tcase_add_test(tc, test_duration_short);
+ tcase_add_test(tc, test_duration_long);
+ suite_add_tcase(s, tc);
+
+ return s;
+}
diff --git a/tools/tracing/rtla/tests/unit/timerlat_top_cli.c b/tools/tracing/rtla/tests/unit/timerlat_top_cli.c
new file mode 100644
index 000000000000..e9fb1a86ab8c
--- /dev/null
+++ b/tools/tracing/rtla/tests/unit/timerlat_top_cli.c
@@ -0,0 +1,672 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <check.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/sysinfo.h>
+
+#include <linux/container_of.h>
+
+#include "cli_params_assert.h"
+#include "../../src/cli.h"
+
+#define PARSE_ARGS(...) char *argv[] = { __VA_ARGS__, NULL };\
+ int argc = sizeof(argv) / sizeof(char *) - 1;\
+ struct common_params *params =\
+ timerlat_top_parse_args(argc, argv);\
+ struct timerlat_params *tlat_params __maybe_unused =\
+ to_timerlat_params(params)
+
+/* Tracing Options */
+
+START_TEST(test_irq_short)
+{
+ PARSE_ARGS("timerlat", "top", "-i", "20");
+
+ ck_assert_int_eq(params->stop_us, 20);
+}
+END_TEST
+
+START_TEST(test_irq_long)
+{
+ PARSE_ARGS("timerlat", "top", "--irq", "20");
+
+ ck_assert_int_eq(params->stop_us, 20);
+}
+END_TEST
+
+START_TEST(test_period_short)
+{
+ PARSE_ARGS("timerlat", "top", "-p", "200");
+
+ ck_assert_int_eq(tlat_params->timerlat_period_us, 200);
+}
+END_TEST
+
+START_TEST(test_period_long)
+{
+ PARSE_ARGS("timerlat", "top", "--period", "200");
+
+ ck_assert_int_eq(tlat_params->timerlat_period_us, 200);
+}
+END_TEST
+
+START_TEST(test_period_unset_short)
+{
+ PARSE_ARGS("timerlat", "top", "-p", "200", "--no-period");
+
+ ck_assert_int_eq(tlat_params->timerlat_period_us, 0);
+}
+END_TEST
+
+START_TEST(test_period_unset_long)
+{
+ PARSE_ARGS("timerlat", "top", "--period", "200", "--no-period");
+
+ ck_assert_int_eq(tlat_params->timerlat_period_us, 0);
+}
+END_TEST
+
+START_TEST(test_stack_short)
+{
+ PARSE_ARGS("timerlat", "top", "-s", "20");
+
+ ck_assert_int_eq(tlat_params->print_stack, 20);
+}
+END_TEST
+
+START_TEST(test_stack_long)
+{
+ PARSE_ARGS("timerlat", "top", "--stack", "20");
+
+ ck_assert_int_eq(tlat_params->print_stack, 20);
+}
+END_TEST
+
+START_TEST(test_thread_short)
+{
+ PARSE_ARGS("timerlat", "top", "-T", "20");
+
+ ck_assert_int_eq(params->stop_total_us, 20);
+}
+END_TEST
+
+START_TEST(test_thread_long)
+{
+ PARSE_ARGS("timerlat", "top", "--thread", "20");
+
+ ck_assert_int_eq(params->stop_total_us, 20);
+}
+END_TEST
+
+/* Event Configuration */
+
+START_TEST(test_event_short)
+{
+ PARSE_ARGS("timerlat", "top", "-e", "system:event");
+
+ CLI_ASSERT_SINGLE_EVENT("system", "event");
+}
+END_TEST
+
+START_TEST(test_event_long)
+{
+ PARSE_ARGS("timerlat", "top", "--event", "system:event");
+
+ CLI_ASSERT_SINGLE_EVENT("system", "event");
+}
+END_TEST
+
+START_TEST(test_filter)
+{
+ PARSE_ARGS("timerlat", "top", "-e", "system:event", "--filter", "filter");
+
+ CLI_ASSERT_SINGLE_FILTER("filter");
+}
+END_TEST
+
+START_TEST(test_trigger)
+{
+ PARSE_ARGS("timerlat", "top", "-e", "system:event", "--trigger", "trigger");
+
+ CLI_ASSERT_SINGLE_TRIGGER("trigger");
+}
+END_TEST
+
+START_TEST(test_trace_short_noarg)
+{
+ PARSE_ARGS("timerlat", "top", "-t");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "timerlat_trace.txt");
+}
+END_TEST
+
+START_TEST(test_trace_short_followarg)
+{
+ PARSE_ARGS("timerlat", "top", "-t", "-d", "20");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "timerlat_trace.txt");
+ ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
+}
+END_TEST
+
+START_TEST(test_trace_short_space)
+{
+ PARSE_ARGS("timerlat", "top", "-t", "tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+START_TEST(test_trace_short_equals)
+{
+ PARSE_ARGS("timerlat", "top", "-t=tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+START_TEST(test_trace_long_noarg)
+{
+ PARSE_ARGS("timerlat", "top", "--trace");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "timerlat_trace.txt");
+}
+END_TEST
+
+START_TEST(test_trace_long_followarg)
+{
+ PARSE_ARGS("timerlat", "top", "--trace", "-d", "20");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "timerlat_trace.txt");
+ ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */
+}
+END_TEST
+
+START_TEST(test_trace_long_space)
+{
+ PARSE_ARGS("timerlat", "top", "--trace", "tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+START_TEST(test_trace_long_equals)
+{
+ PARSE_ARGS("timerlat", "top", "--trace=tracefile");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "tracefile");
+}
+END_TEST
+
+/* CPU Configuration */
+
+START_TEST(test_cpus_short)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("timerlat", "top", "-c", "0-1,3");
+
+ ck_assert_str_eq(params->cpus, "0-1,3");
+ CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
+}
+END_TEST
+
+START_TEST(test_cpus_long)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("timerlat", "top", "--cpus", "0-1,3");
+
+ ck_assert_str_eq(params->cpus, "0-1,3");
+ CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3);
+}
+END_TEST
+
+START_TEST(test_housekeeping_short)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("timerlat", "top", "-H", "0-1,3");
+
+ CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
+}
+END_TEST
+
+START_TEST(test_housekeeping_long)
+{
+ nr_cpus = 4;
+
+ PARSE_ARGS("timerlat", "top", "--house-keeping", "0-1,3");
+
+ CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3);
+}
+END_TEST
+
+/* Thread Configuration */
+
+START_TEST(test_cgroup_short_noarg)
+{
+ PARSE_ARGS("timerlat", "top", "-C");
+
+ ck_assert(params->cgroup);
+ ck_assert_ptr_null(params->cgroup_name);
+}
+END_TEST
+
+START_TEST(test_cgroup_short_space)
+{
+ PARSE_ARGS("timerlat", "top", "-C", "cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_cgroup_short_equals)
+{
+ PARSE_ARGS("timerlat", "top", "-C=cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_cgroup_long_noarg)
+{
+ PARSE_ARGS("timerlat", "top", "--cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_ptr_null(params->cgroup_name);
+}
+END_TEST
+
+START_TEST(test_cgroup_long_space)
+{
+ PARSE_ARGS("timerlat", "top", "--cgroup", "cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_cgroup_long_equals)
+{
+ PARSE_ARGS("timerlat", "top", "--cgroup=cgroup");
+
+ ck_assert(params->cgroup);
+ ck_assert_str_eq(params->cgroup_name, "cgroup");
+}
+END_TEST
+
+START_TEST(test_kernel_threads_short)
+{
+ PARSE_ARGS("timerlat", "top", "-k");
+
+ ck_assert(params->kernel_workload);
+ ck_assert(!params->user_workload);
+ ck_assert(!params->user_data);
+}
+END_TEST
+
+START_TEST(test_kernel_threads_long)
+{
+ PARSE_ARGS("timerlat", "top", "--kernel-threads");
+
+ ck_assert(params->kernel_workload);
+ ck_assert(!params->user_workload);
+ ck_assert(!params->user_data);
+}
+END_TEST
+
+START_TEST(test_priority_short)
+{
+ PARSE_ARGS("timerlat", "top", "-P", "f:95");
+
+ ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
+ ck_assert_int_eq(params->sched_param.sched_priority, 95);
+}
+END_TEST
+
+START_TEST(test_priority_long)
+{
+ PARSE_ARGS("timerlat", "top", "--priority", "f:95");
+
+ ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO);
+ ck_assert_int_eq(params->sched_param.sched_priority, 95);
+}
+END_TEST
+
+START_TEST(test_user_load_short)
+{
+ PARSE_ARGS("timerlat", "top", "-U");
+
+ ck_assert(!params->kernel_workload);
+ ck_assert(!params->user_workload);
+ ck_assert(params->user_data);
+}
+END_TEST
+
+START_TEST(test_user_load_long)
+{
+ PARSE_ARGS("timerlat", "top", "--user-load");
+
+ ck_assert(!params->kernel_workload);
+ ck_assert(!params->user_workload);
+ ck_assert(params->user_data);
+}
+END_TEST
+
+START_TEST(test_user_threads_short)
+{
+ PARSE_ARGS("timerlat", "top", "-u");
+
+ ck_assert(!params->kernel_workload);
+ ck_assert(params->user_workload);
+ ck_assert(params->user_data);
+}
+END_TEST
+
+START_TEST(test_user_threads_long)
+{
+ PARSE_ARGS("timerlat", "top", "--user-threads");
+
+ ck_assert(!params->kernel_workload);
+ ck_assert(params->user_workload);
+ ck_assert(params->user_data);
+}
+END_TEST
+
+START_TEST(test_aligned_short)
+{
+ PARSE_ARGS("timerlat", "top", "-A", "500");
+
+ ck_assert(tlat_params->timerlat_align);
+ ck_assert_int_eq(tlat_params->timerlat_align_us, 500);
+}
+END_TEST
+
+START_TEST(test_aligned_long)
+{
+ PARSE_ARGS("timerlat", "top", "--aligned", "500");
+
+ ck_assert(tlat_params->timerlat_align);
+ ck_assert_int_eq(tlat_params->timerlat_align_us, 500);
+}
+END_TEST
+
+/* Output */
+
+START_TEST(test_nano_short)
+{
+ PARSE_ARGS("timerlat", "top", "-n");
+
+ ck_assert_int_eq(params->output_divisor, 1);
+}
+END_TEST
+
+START_TEST(test_nano_long)
+{
+ PARSE_ARGS("timerlat", "top", "--nano");
+
+ ck_assert_int_eq(params->output_divisor, 1);
+}
+END_TEST
+
+START_TEST(test_quiet_short)
+{
+ PARSE_ARGS("timerlat", "top", "-q");
+
+ ck_assert(params->quiet);
+}
+END_TEST
+
+START_TEST(test_quiet_long)
+{
+ PARSE_ARGS("timerlat", "top", "--quiet");
+
+ ck_assert(params->quiet);
+}
+END_TEST
+
+/* System Tuning */
+
+START_TEST(test_deepest_idle_state)
+{
+ PARSE_ARGS("timerlat", "top", "--deepest-idle-state", "1");
+
+ ck_assert_int_eq(tlat_params->deepest_idle_state, 1);
+}
+END_TEST
+
+START_TEST(test_dma_latency)
+{
+ PARSE_ARGS("timerlat", "top", "--dma-latency", "10");
+
+ ck_assert_int_eq(tlat_params->dma_latency, 10);
+}
+END_TEST
+
+START_TEST(test_trace_buffer_size)
+{
+ PARSE_ARGS("timerlat", "top", "--trace-buffer-size", "200");
+
+ ck_assert_int_eq(params->buffer_size, 200);
+}
+END_TEST
+
+START_TEST(test_warm_up)
+{
+ PARSE_ARGS("timerlat", "top", "--warm-up", "5");
+
+ ck_assert_int_eq(params->warmup, 5);
+}
+END_TEST
+
+/* Auto Analysis and Actions */
+
+START_TEST(test_auto)
+{
+ PARSE_ARGS("timerlat", "top", "-a", "20");
+
+ CLI_TIMERLAT_ASSERT_AUTO(20);
+}
+END_TEST
+
+START_TEST(test_aa_only)
+{
+ PARSE_ARGS("timerlat", "top", "--aa-only", "20");
+
+ CLI_TIMERLAT_ASSERT_AA_ONLY(20);
+}
+END_TEST
+
+START_TEST(test_bpf_action)
+{
+ PARSE_ARGS("timerlat", "top", "--bpf-action", "program");
+
+ ck_assert_str_eq(tlat_params->bpf_action_program, "program");
+}
+END_TEST
+
+START_TEST(test_dump_tasks)
+{
+ PARSE_ARGS("timerlat", "top", "--dump-tasks");
+
+ ck_assert(tlat_params->dump_tasks);
+}
+END_TEST
+
+START_TEST(test_no_aa)
+{
+ PARSE_ARGS("timerlat", "top", "--no-aa");
+
+ ck_assert(tlat_params->no_aa);
+}
+END_TEST
+
+START_TEST(test_on_end)
+{
+ PARSE_ARGS("timerlat", "top", "--on-end", "trace");
+
+ CLI_ASSERT_SINGLE_ACTION(end_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "timerlat_trace.txt");
+}
+END_TEST
+
+START_TEST(test_on_threshold)
+{
+ PARSE_ARGS("timerlat", "top", "--on-threshold", "trace");
+
+ CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str,
+ "timerlat_trace.txt");
+}
+END_TEST
+
+START_TEST(test_stack_format)
+{
+ PARSE_ARGS("timerlat", "top", "--stack-format", "truncate");
+
+ ck_assert_int_eq(tlat_params->stack_format, STACK_FORMAT_TRUNCATE);
+}
+END_TEST
+
+/* General */
+
+START_TEST(test_debug_short)
+{
+ PARSE_ARGS("timerlat", "top", "-D");
+
+ ck_assert(config_debug);
+}
+END_TEST
+
+START_TEST(test_debug_long)
+{
+ PARSE_ARGS("timerlat", "top", "--debug");
+
+ ck_assert(config_debug);
+}
+END_TEST
+
+START_TEST(test_duration_short)
+{
+ PARSE_ARGS("timerlat", "top", "-d", "1m");
+
+ ck_assert_int_eq(params->duration, 60);
+}
+END_TEST
+
+START_TEST(test_duration_long)
+{
+ PARSE_ARGS("timerlat", "top", "--duration", "1m");
+
+ ck_assert_int_eq(params->duration, 60);
+}
+END_TEST
+
+Suite *timerlat_top_cli_suite(void)
+{
+ Suite *s = suite_create("timerlat_top_cli");
+ TCase *tc;
+
+ tc = tcase_create("tracing_options");
+ tcase_add_test(tc, test_irq_short);
+ tcase_add_test(tc, test_irq_long);
+ tcase_add_test(tc, test_period_short);
+ tcase_add_test(tc, test_period_long);
+ tcase_add_test(tc, test_period_unset_short);
+ tcase_add_test(tc, test_period_unset_long);
+ tcase_add_test(tc, test_stack_short);
+ tcase_add_test(tc, test_stack_long);
+ tcase_add_test(tc, test_thread_short);
+ tcase_add_test(tc, test_thread_long);
+ tcase_add_test(tc, test_trace_short_noarg);
+ tcase_add_test(tc, test_trace_short_followarg);
+ tcase_add_test(tc, test_trace_short_space);
+ tcase_add_test(tc, test_trace_short_equals);
+ tcase_add_test(tc, test_trace_long_noarg);
+ tcase_add_test(tc, test_trace_long_followarg);
+ tcase_add_test(tc, test_trace_long_space);
+ tcase_add_test(tc, test_trace_long_equals);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("event_configuration");
+ tcase_add_test(tc, test_event_short);
+ tcase_add_test(tc, test_event_long);
+ tcase_add_test(tc, test_filter);
+ tcase_add_test(tc, test_trigger);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("cpu_configuration");
+ tcase_add_test(tc, test_cpus_short);
+ tcase_add_test(tc, test_cpus_long);
+ tcase_add_test(tc, test_housekeeping_short);
+ tcase_add_test(tc, test_housekeeping_long);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("thread_configuration");
+ tcase_add_test(tc, test_cgroup_short_noarg);
+ tcase_add_test(tc, test_cgroup_short_space);
+ tcase_add_test(tc, test_cgroup_short_equals);
+ tcase_add_test(tc, test_cgroup_long_noarg);
+ tcase_add_test(tc, test_cgroup_long_space);
+ tcase_add_test(tc, test_cgroup_long_equals);
+ tcase_add_test(tc, test_kernel_threads_short);
+ tcase_add_test(tc, test_kernel_threads_long);
+ tcase_add_test(tc, test_priority_short);
+ tcase_add_test(tc, test_priority_long);
+ tcase_add_test(tc, test_user_load_short);
+ tcase_add_test(tc, test_user_load_long);
+ tcase_add_test(tc, test_user_threads_short);
+ tcase_add_test(tc, test_user_threads_long);
+ tcase_add_test(tc, test_aligned_short);
+ tcase_add_test(tc, test_aligned_long);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("output");
+ tcase_add_test(tc, test_nano_short);
+ tcase_add_test(tc, test_nano_long);
+ tcase_add_test(tc, test_quiet_short);
+ tcase_add_test(tc, test_quiet_long);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("system_tuning");
+ tcase_add_test(tc, test_deepest_idle_state);
+ tcase_add_test(tc, test_dma_latency);
+ tcase_add_test(tc, test_trace_buffer_size);
+ tcase_add_test(tc, test_warm_up);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("aa_actions");
+ tcase_add_test(tc, test_auto);
+ tcase_add_test(tc, test_aa_only);
+ tcase_add_test(tc, test_bpf_action);
+ tcase_add_test(tc, test_dump_tasks);
+ tcase_add_test(tc, test_no_aa);
+ tcase_add_test(tc, test_on_end);
+ tcase_add_test(tc, test_on_threshold);
+ tcase_add_test(tc, test_stack_format);
+ suite_add_tcase(s, tc);
+
+ tc = tcase_create("general");
+ tcase_add_test(tc, test_debug_short);
+ tcase_add_test(tc, test_debug_long);
+ tcase_add_test(tc, test_duration_short);
+ tcase_add_test(tc, test_duration_long);
+ suite_add_tcase(s, tc);
+
+ return s;
+}
diff --git a/tools/tracing/rtla/tests/unit/unit_tests.c b/tools/tracing/rtla/tests/unit/unit_tests.c
new file mode 100644
index 000000000000..75ca813f81ca
--- /dev/null
+++ b/tools/tracing/rtla/tests/unit/unit_tests.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <check.h>
+#include <stdbool.h>
+
+#include "../../src/utils.h"
+#include "../../src/cli.h"
+
+Suite *utils_suite(void);
+Suite *actions_suite(void);
+Suite *osnoise_top_cli_suite(void);
+Suite *osnoise_hist_cli_suite(void);
+Suite *timerlat_top_cli_suite(void);
+Suite *timerlat_hist_cli_suite(void);
+Suite *cli_opt_callback_suite(void);
+
+int main(int argc, char *argv[])
+{
+ int num_failed;
+ SRunner *sr;
+
+ in_unit_test = true;
+
+ sr = srunner_create(utils_suite());
+ srunner_add_suite(sr, cli_opt_callback_suite());
+ srunner_add_suite(sr, actions_suite());
+ srunner_add_suite(sr, osnoise_top_cli_suite());
+ srunner_add_suite(sr, osnoise_hist_cli_suite());
+ srunner_add_suite(sr, timerlat_top_cli_suite());
+ srunner_add_suite(sr, timerlat_hist_cli_suite());
+
+ srunner_run_all(sr, CK_VERBOSE);
+ num_failed = srunner_ntests_failed(sr);
+
+ srunner_free(sr);
+
+ return (num_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tools/tracing/rtla/tests/unit/utils.c b/tools/tracing/rtla/tests/unit/utils.c
new file mode 100644
index 000000000000..ce53cab49457
--- /dev/null
+++ b/tools/tracing/rtla/tests/unit/utils.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <check.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/sysinfo.h>
+
+#include "../../src/utils.h"
+
+extern int nr_cpus;
+
+START_TEST(test_strtoi)
+{
+ int result;
+ char buf[64];
+
+ ck_assert_int_eq(strtoi("123", &result), 0);
+ ck_assert_int_eq(result, 123);
+ ck_assert_int_eq(strtoi(" -456", &result), 0);
+ ck_assert_int_eq(result, -456);
+
+ snprintf(buf, sizeof(buf), "%d", INT_MAX);
+ ck_assert_int_eq(strtoi(buf, &result), 0);
+ snprintf(buf, sizeof(buf), "%ld", (long)INT_MAX + 1);
+ ck_assert_int_eq(strtoi(buf, &result), -1);
+
+ ck_assert_int_eq(strtoi("", &result), -1);
+ ck_assert_int_eq(strtoi("123abc", &result), -1);
+ ck_assert_int_eq(strtoi("123 ", &result), -1);
+}
+END_TEST
+
+START_TEST(test_parse_cpu_set)
+{
+ cpu_set_t set;
+
+ nr_cpus = 8;
+ ck_assert_int_eq(parse_cpu_set("0", &set), 0);
+ ck_assert(CPU_ISSET(0, &set));
+ ck_assert(!CPU_ISSET(1, &set));
+
+ ck_assert_int_eq(parse_cpu_set("0,2", &set), 0);
+ ck_assert(CPU_ISSET(0, &set));
+ ck_assert(CPU_ISSET(2, &set));
+
+ ck_assert_int_eq(parse_cpu_set("0-3", &set), 0);
+ ck_assert(CPU_ISSET(0, &set));
+ ck_assert(CPU_ISSET(1, &set));
+ ck_assert(CPU_ISSET(2, &set));
+ ck_assert(CPU_ISSET(3, &set));
+
+ ck_assert_int_eq(parse_cpu_set("1-3,5", &set), 0);
+ ck_assert(!CPU_ISSET(0, &set));
+ ck_assert(CPU_ISSET(1, &set));
+ ck_assert(CPU_ISSET(2, &set));
+ ck_assert(CPU_ISSET(3, &set));
+ ck_assert(!CPU_ISSET(4, &set));
+ ck_assert(CPU_ISSET(5, &set));
+
+ ck_assert_int_eq(parse_cpu_set("-1", &set), 1);
+ ck_assert_int_eq(parse_cpu_set("abc", &set), 1);
+ ck_assert_int_eq(parse_cpu_set("9999", &set), 1);
+}
+END_TEST
+
+START_TEST(test_parse_prio)
+{
+ struct sched_attr attr;
+
+ ck_assert_int_eq(parse_prio("f:50", &attr), 0);
+ ck_assert_uint_eq(attr.sched_policy, SCHED_FIFO);
+ ck_assert_uint_eq(attr.sched_priority, 50U);
+
+ ck_assert_int_eq(parse_prio("r:30", &attr), 0);
+ ck_assert_uint_eq(attr.sched_policy, SCHED_RR);
+
+ ck_assert_int_eq(parse_prio("o:0", &attr), 0);
+ ck_assert_uint_eq(attr.sched_policy, SCHED_OTHER);
+ ck_assert_int_eq(attr.sched_nice, 0);
+
+ ck_assert_int_eq(parse_prio("d:10ms:100ms", &attr), 0);
+ ck_assert_uint_eq(attr.sched_policy, 6U);
+
+ ck_assert_int_eq(parse_prio("f:999", &attr), -1);
+ ck_assert_int_eq(parse_prio("o:-20", &attr), -1);
+ ck_assert_int_eq(parse_prio("d:100ms:10ms", &attr), -1);
+ ck_assert_int_eq(parse_prio("x:50", &attr), -1);
+}
+END_TEST
+
+Suite *utils_suite(void)
+{
+ Suite *s = suite_create("utils");
+ TCase *tc = tcase_create("core");
+
+ tcase_add_test(tc, test_strtoi);
+ tcase_add_test(tc, test_parse_cpu_set);
+ tcase_add_test(tc, test_parse_prio);
+
+ suite_add_tcase(s, tc);
+ return s;
+}