[LTP] [PATCH v6 4/5] ftrace: Convert ftrace_stress_test.sh to C
Praveen K Pandey
praveen@linux.ibm.com
Tue Jun 9 06:16:31 CEST 2026
Convert ftrace_stress_test.sh to C using the new LTP test API.
This test performs stress testing of ftrace by running multiple
threads that concurrently modify various ftrace settings.
Key changes:
- Uses new LTP C API (tst_test.h)
- Leverages ftrace_lib for common operations
- Multi-threaded stress testing with proper synchronization
- Proper cleanup: stop_stress_tests() resets thread_count to 0
after joining threads to avoid double-join (undefined behavior)
- Variables stop_testing and thread_count are reset at the start
of run_test() to support multiple test iterations (-i N)
- Requires root privileges (.needs_root = 1)
- Test timeout set to 300 seconds
The test creates multiple threads that stress test different
ftrace interfaces concurrently for a specified duration.
Signed-off-by: Praveen K Pandey <praveen@linux.ibm.com>
---
.../tracing/ftrace_test/ftrace_stress_test.c | 328 ++++++++++++++++++
.../tracing/ftrace_test/ftrace_stress_test.sh | 121 -------
2 files changed, 328 insertions(+), 121 deletions(-)
create mode 100644 testcases/kernel/tracing/ftrace_test/ftrace_stress_test.c
delete mode 100755 testcases/kernel/tracing/ftrace_test/ftrace_stress_test.sh
diff --git a/testcases/kernel/tracing/ftrace_test/ftrace_stress_test.c b/testcases/kernel/tracing/ftrace_test/ftrace_stress_test.c
new file mode 100644
index 000000000..f4d267254
--- /dev/null
+++ b/testcases/kernel/tracing/ftrace_test/ftrace_stress_test.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2010 FUJITSU LIMITED
+ * Copyright (c) 2024 Linux Test Project
+ * Copyright (c) IBM, 2026
+ *
+ * Author: Li Zefan <lizf@cn.fujitsu.com>
+ * Converted to C by: Praveen K Pandey <praveen@linux.ibm.com>
+ */
+
+/*\
+ * Stress test for ftrace subsystem.
+ *
+ * This test exercises various ftrace features concurrently to detect
+ * race conditions, memory leaks, and other issues in the ftrace subsystem.
+ *
+ * Requires root to access and configure the ftrace subsystem via debugfs.
+ *
+ * [Algorithm]
+ *
+ * The test spawns multiple threads, each exercising a different ftrace
+ * feature:
+ *
+ * - Switching between different tracers
+ * - Reading from trace_pipe
+ * - Enabling/disabling events
+ * - Modifying buffer size
+ * - Setting trace filters
+ * - Modifying trace options
+ * - And more...
+ *
+ * All threads run concurrently for a specified duration, then the test
+ * verifies the system is still stable.
+ */
+
+#include <pthread.h>
+#include <signal.h>
+#include "tst_test.h"
+#include "ftrace_lib.h"
+
+#define DEFAULT_TEST_DURATION 60 /* seconds */
+#define MAX_THREADS 20
+
+static volatile int stop_testing = 0;
+static int test_duration = DEFAULT_TEST_DURATION;
+static pthread_t threads[MAX_THREADS];
+static int thread_count = 0;
+
+struct stress_test {
+ const char *name;
+ const char *file;
+ void *(*func)(void *);
+ int enabled;
+};
+
+/* Thread function for current_tracer stress test */
+static void *stress_current_tracer(void *arg)
+{
+ int count;
+ char **tracers;
+ int i, loop;
+
+ (void)arg;
+
+ tracers = ftrace_get_available_tracers(&count);
+ if (!tracers || count == 0) {
+ tst_res(TINFO, "No tracers available");
+ return NULL;
+ }
+
+ loop = 0;
+ while (!stop_testing) {
+ for (i = 0; i < count && !stop_testing; i++) {
+ /* Skip mmiotrace as it can cause issues */
+ if (strcmp(tracers[i], "mmiotrace") == 0)
+ continue;
+
+ ftrace_set_tracer(tracers[i]);
+ }
+ loop++;
+ if (loop >= 200)
+ usleep(1000000); /* Sleep 1 second after 200 loops */
+ }
+
+ for (i = 0; i < count; i++)
+ free(tracers[i]);
+ free(tracers);
+
+ return NULL;
+}
+
+/* Thread function for trace_pipe stress test */
+static void *stress_trace_pipe(void *arg)
+{
+ char *path;
+ FILE *fp;
+ int loop = 0;
+
+ (void)arg;
+
+ path = ftrace_get_path("trace_pipe");
+ if (!path)
+ return NULL;
+
+ while (!stop_testing) {
+ fp = fopen(path, "r");
+ if (fp) {
+ /* Read for a short time */
+ usleep(200000); /* 200ms */
+ fclose(fp);
+ }
+
+ usleep(200000); /* 200ms */
+ loop++;
+ if (loop >= 20) {
+ sleep(2);
+ loop = 0;
+ }
+ }
+
+ free(path);
+ return NULL;
+}
+
+/* Thread function for set_event stress test */
+static void *stress_set_event(void *arg)
+{
+ int i;
+
+ (void)arg;
+
+ while (!stop_testing) {
+ /* Enable/disable all events */
+ for (i = 0; i < 100 && !stop_testing; i++) {
+ ftrace_write_file("events/enable", "1\n");
+ ftrace_write_file("events/enable", "0\n");
+ }
+
+ sleep(1);
+ }
+
+ return NULL;
+}
+
+/* Thread function for buffer_size_kb stress test */
+static void *stress_buffer_size(void *arg)
+{
+ const char *sizes[] = {"1024", "2048", "4096", "8192"};
+ int i;
+
+ (void)arg;
+
+ while (!stop_testing) {
+ for (i = 0; i < 4 && !stop_testing; i++) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%s\n", sizes[i]);
+ ftrace_write_file("buffer_size_kb", buf);
+ usleep(100000);
+ }
+ sleep(1);
+ }
+
+ return NULL;
+}
+
+/* Thread function for tracing_on stress test */
+static void *stress_tracing_on(void *arg)
+{
+ int i;
+
+ (void)arg;
+
+ while (!stop_testing) {
+ for (i = 0; i < 100 && !stop_testing; i++) {
+ ftrace_enable_tracing();
+ ftrace_disable_tracing();
+ }
+ sleep(1);
+ }
+
+ return NULL;
+}
+
+/* Thread function for trace_options stress test */
+static void *stress_trace_options(void *arg)
+{
+ const char *options[] = {
+ "print-parent", "sym-offset", "sym-addr",
+ "verbose", "raw", "hex", "bin", "block"
+ };
+ int i;
+
+ (void)arg;
+
+ while (!stop_testing) {
+ for (i = 0; i < 8 && !stop_testing; i++) {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%s\n", options[i]);
+ ftrace_write_file("trace_options", buf);
+ snprintf(buf, sizeof(buf), "no%s\n", options[i]);
+ ftrace_write_file("trace_options", buf);
+ }
+ sleep(1);
+ }
+
+ return NULL;
+}
+
+/* Thread function for set_ftrace_filter stress test */
+static void *stress_set_ftrace_filter(void *arg)
+{
+ (void)arg;
+
+ if (!ftrace_file_exists("set_ftrace_filter"))
+ return NULL;
+
+ while (!stop_testing) {
+ ftrace_write_file("set_ftrace_filter", "schedule\n");
+ ftrace_write_file("set_ftrace_filter", "\n");
+ usleep(500000);
+ }
+
+ return NULL;
+}
+
+static struct stress_test stress_tests[] = {
+ {"current_tracer", "current_tracer", stress_current_tracer, 0},
+ {"trace_pipe", "trace_pipe", stress_trace_pipe, 0},
+ {"set_event", "events/enable", stress_set_event, 0},
+ {"buffer_size_kb", "buffer_size_kb", stress_buffer_size, 0},
+ {"tracing_on", "tracing_on", stress_tracing_on, 0},
+ {"trace_options", "trace_options", stress_trace_options, 0},
+ {"set_ftrace_filter", "set_ftrace_filter", stress_set_ftrace_filter, 0},
+ {NULL, NULL, NULL, 0}
+};
+
+static void check_available_tests(void)
+{
+ int i;
+
+ for (i = 0; stress_tests[i].name; i++) {
+ if (ftrace_file_exists(stress_tests[i].file) ||
+ access(stress_tests[i].file, F_OK) == 0) {
+ stress_tests[i].enabled = 1;
+ tst_res(TINFO, "Test target available: %s", stress_tests[i].name);
+ } else {
+ tst_res(TINFO, "Test target not available: %s", stress_tests[i].name);
+ }
+ }
+}
+
+static void start_stress_tests(void)
+{
+ int i;
+
+ thread_count = 0;
+ for (i = 0; stress_tests[i].name; i++) {
+ if (!stress_tests[i].enabled)
+ continue;
+
+ if (pthread_create(&threads[thread_count], NULL,
+ stress_tests[i].func, NULL) != 0) {
+ tst_res(TWARN, "Failed to create thread for %s",
+ stress_tests[i].name);
+ } else {
+ tst_res(TINFO, "Started stress test: %s", stress_tests[i].name);
+ thread_count++;
+ }
+ }
+
+ tst_res(TINFO, "Started %d stress test threads", thread_count);
+}
+
+static void stop_stress_tests(void)
+{
+ int i;
+
+ stop_testing = 1;
+
+ for (i = 0; i < thread_count; i++) {
+ pthread_join(threads[i], NULL);
+ }
+
+ thread_count = 0;
+ tst_res(TINFO, "All stress test threads stopped");
+}
+
+static void run_test(void)
+{
+ stop_testing = 0;
+ thread_count = 0;
+
+ tst_res(TINFO, "Starting ftrace stress test for %d seconds", test_duration);
+
+ check_available_tests();
+ start_stress_tests();
+
+ /* Run for specified duration */
+ sleep(test_duration);
+
+ stop_stress_tests();
+
+ tst_res(TPASS, "Ftrace stress test completed successfully");
+ tst_res(TINFO, "Check dmesg for any kernel warnings or errors");
+}
+
+static void setup(void)
+{
+ ftrace_initialize();
+}
+
+static void cleanup(void)
+{
+ if (thread_count > 0)
+ stop_stress_tests();
+
+ ftrace_cleanup();
+}
+
+static struct tst_test test = {
+ .test_all = run_test,
+ .setup = setup,
+ .cleanup = cleanup,
+ .needs_root = 1,
+ .needs_tmpdir = 1,
+ .timeout = DEFAULT_TEST_DURATION + 30,
+};
+
diff --git a/testcases/kernel/tracing/ftrace_test/ftrace_stress_test.sh b/testcases/kernel/tracing/ftrace_test/ftrace_stress_test.sh
deleted file mode 100755
index 159ba1a66..000000000
--- a/testcases/kernel/tracing/ftrace_test/ftrace_stress_test.sh
+++ /dev/null
@@ -1,121 +0,0 @@
-#! /bin/sh
-
-###########################################################################
-## ##
-## Copyright (c) 2010 FUJITSU LIMITED ##
-## ##
-## This program is free software: you can redistribute it and/or modify ##
-## it under the terms of the GNU General Public License as published by ##
-## the Free Software Foundation, either version 3 of the License, or ##
-## (at your option) any later version. ##
-## ##
-## This program is distributed in the hope that it will be useful, ##
-## but WITHOUT ANY WARRANTY; without even the implied warranty of ##
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ##
-## GNU General Public License for more details. ##
-## ##
-## You should have received a copy of the GNU General Public License ##
-## along with this program. If not, see <http://www.gnu.org/licenses/>. ##
-## ##
-## Author: Li Zefan <lizf@cn.fujitsu.com> ##
-## ##
-###########################################################################
-
-export TCID="ftrace-stress-test"
-export TST_TOTAL=1
-export TST_COUNT=1
-
-. ftrace_lib.sh
-
-test_targets=" \
-trace_pipe current_tracer ftrace_enabled function_profile_enabled \
-set_event set_ftrace_pid stack_max_size stack_trace trace trace_clock \
-trace_options trace_stat tracing_enabled tracing_max_latency \
-tracing_on function_profile_enabled buffer_size_kb tracing_cpumask \
-set_ftrace_filter"
-
-get_skip_targets()
-{
- NR_PIDS=0
- for target in ${test_targets}; do
- if [ ! -e $TRACING_PATH/$target ] &&
- [ ! -e /proc/sys/kernel/$target ]; then
- eval skip_$target=1
- tst_resm TINFO "$target is not supported. Skip it."
- else
- eval skip_$target=0
- NR_PIDS=$((NR_PIDS + 1))
- fi
- done
- # Export it before sub case is lanuched.
- export NR_PIDS
-}
-
-should_skip_target()
-{
- local skip_var=skip_$1
- eval local skip_val=\$${skip_var}
- [ "$skip_val" = 1 ]
-}
-
-test_kill()
-{
- tst_resm TINFO "killing ${pid0}"
- kill -USR1 ${pid0}
- wait ${pid0}
-
- local p=1;
- while [ $p -lt $NR_PIDS ]; do
- local pid_var=pid${p}
- eval local kill_pid=\$${pid_var}
- tst_resm TINFO "killing ${kill_pid}"
- kill -KILL $kill_pid
- wait ${kill_pid}
- p=$((p + 1))
- done
-}
-
-test_stress()
-{
- local index=0;
-
- tst_resm TINFO "Test targets: ${test_targets}"
-
- get_skip_targets
- for target in ${test_targets}; do
- if should_skip_target $target; then
- continue
- fi
- sh ftrace_${target}.sh &
- eval pid${index}=$!
- tst_resm TINFO "Start pid${index}=$! $SPATH/ftrace_${target}.sh"
- index=$((index + 1))
- done
- export_pids
-}
-
-export_pids()
-{
- local p=0
- while [ $p -lt $NR_PIDS ]; do
- export pid${p}
- p=$((p + 1))
- done
-}
-
-cd ftrace_stress/
-# ----------------------------
-tst_resm TINFO "Ftrace Stress Test Begin"
-
-test_begin
-
-test_stress
-
-test_wait
-
-test_kill
-
-tst_resm TINFO "Finished running the test. Run dmesg to double-check for bugs"
-
-tst_exit
-
--
2.50.1
More information about the ltp
mailing list