From 4a1cec7450b7159a0ee57403f44460ac4d618b4f Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Thu, 8 Jan 2026 11:49:55 +0200 Subject: [PATCH 01/28] tools/rtla: Generate optstring from long options getopt_long() processes short and long options independently. RTLA, like the majority of applications, uses both short and long variants for each logical option. Since the val member of struct option holds the letter of the short variant, the string of short options can be reconstructed from the array of long options. Add getopt_auto() to generate optstring from an array of long options, eliminating the need to maintain separate short option strings. Signed-off-by: Costa Shulyupin Reviewed-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260108095011.2115719-1-costa.shul@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/common.c | 32 +++++++++++++++++++++++++- tools/tracing/rtla/src/common.h | 2 ++ tools/tracing/rtla/src/osnoise_hist.c | 3 +-- tools/tracing/rtla/src/osnoise_top.c | 3 +-- tools/tracing/rtla/src/timerlat_hist.c | 3 +-- tools/tracing/rtla/src/timerlat_top.c | 3 +-- 6 files changed, 37 insertions(+), 9 deletions(-) diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c index ceff76a62a30..f310b0d59ad3 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -39,6 +39,36 @@ static void set_signals(struct common_params *params) } } +/* + * getopt_auto - auto-generates optstring from long_options + */ +int getopt_auto(int argc, char **argv, const struct option *long_opts) +{ + char opts[256]; + int n = 0; + + for (int i = 0; long_opts[i].name; i++) { + if (long_opts[i].val < 32 || long_opts[i].val > 127) + continue; + + if (n + 4 >= sizeof(opts)) + fatal("optstring buffer overflow"); + + opts[n++] = long_opts[i].val; + + if (long_opts[i].has_arg == required_argument) + opts[n++] = ':'; + else if (long_opts[i].has_arg == optional_argument) { + opts[n++] = ':'; + opts[n++] = ':'; + } + } + + opts[n] = '\0'; + + return getopt_long(argc, argv, opts, long_opts, NULL); +} + /* * common_parse_options - parse common command line options * @@ -69,7 +99,7 @@ int common_parse_options(int argc, char **argv, struct common_params *common) }; opterr = 0; - c = getopt_long(argc, argv, "c:C::Dd:e:H:P:", long_options, NULL); + c = getopt_auto(argc, argv, long_options); opterr = 1; switch (c) { diff --git a/tools/tracing/rtla/src/common.h b/tools/tracing/rtla/src/common.h index 7602c5593ef5..d4b3715700be 100644 --- a/tools/tracing/rtla/src/common.h +++ b/tools/tracing/rtla/src/common.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #pragma once +#include #include "actions.h" #include "timerlat_u.h" #include "trace.h" @@ -156,6 +157,7 @@ int osnoise_set_stop_us(struct osnoise_context *context, long long stop_us); int osnoise_set_stop_total_us(struct osnoise_context *context, long long stop_total_us); +int getopt_auto(int argc, char **argv, const struct option *long_opts); int common_parse_options(int argc, char **argv, struct common_params *common); int common_apply_config(struct osnoise_tool *tool, struct common_params *params); int top_main_loop(struct osnoise_tool *tool); diff --git a/tools/tracing/rtla/src/osnoise_hist.c b/tools/tracing/rtla/src/osnoise_hist.c index 9d70ea34807f..5c863e7aad28 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c @@ -506,8 +506,7 @@ static struct common_params if (common_parse_options(argc, argv, ¶ms->common)) continue; - c = getopt_long(argc, argv, "a:b:E:hp:r:s:S:t::T:01234:5:6:7:", - long_options, NULL); + c = getopt_auto(argc, argv, long_options); /* detect the end of the options. */ if (c == -1) diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/osnoise_top.c index d54d47947fb4..b7aed40fd216 100644 --- a/tools/tracing/rtla/src/osnoise_top.c +++ b/tools/tracing/rtla/src/osnoise_top.c @@ -358,8 +358,7 @@ struct common_params *osnoise_top_parse_args(int argc, char **argv) if (common_parse_options(argc, argv, ¶ms->common)) continue; - c = getopt_long(argc, argv, "a:hp:qr:s:S:t::T:0:1:2:3:", - long_options, NULL); + c = getopt_auto(argc, argv, long_options); /* Detect the end of the options. */ if (c == -1) diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 4e8c38a61197..096de8ba3efb 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -825,8 +825,7 @@ static struct common_params if (common_parse_options(argc, argv, ¶ms->common)) continue; - c = getopt_long(argc, argv, "a:b:E:hi:knp:s:t::T:uU0123456:7:8:9\1\2:\3:", - long_options, NULL); + c = getopt_auto(argc, argv, long_options); /* detect the end of the options. */ if (c == -1) diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 284b74773c2b..27c14aa71a8b 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -588,8 +588,7 @@ static struct common_params if (common_parse_options(argc, argv, ¶ms->common)) continue; - c = getopt_long(argc, argv, "a:hi:knp:qs:t::T:uU0:1:2:345:6:7:", - long_options, NULL); + c = getopt_auto(argc, argv, long_options); /* detect the end of the options. */ if (c == -1) From 153e211fc74a096cbd3570ca7388ab4cbeadc6c5 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Mon, 19 Jan 2026 12:58:56 +0200 Subject: [PATCH 02/28] tools/build: Add feature test for libcheck Enable support for unit tests in rtla. Note that the pkg-config file for libcheck is named check.pc. Signed-off-by: Costa Shulyupin Link: https://lore.kernel.org/r/20260119105857.797498-2-costa.shul@redhat.com Signed-off-by: Tomas Glozar --- tools/build/Makefile.feature | 3 +++ tools/build/feature/Makefile | 4 ++++ tools/build/feature/test-libcheck.c | 8 ++++++++ 3 files changed, 15 insertions(+) create mode 100644 tools/build/feature/test-libcheck.c diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 0b7a7c38cb88..2f192d3bf61b 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -115,6 +115,7 @@ FEATURE_TESTS_EXTRA := \ hello \ libbabeltrace \ libcapstone \ + libcheck \ libbfd-liberty \ libbfd-liberty-z \ libopencsd \ @@ -176,6 +177,8 @@ ifneq ($(PKG_CONFIG),) $(foreach package,$(FEATURE_PKG_CONFIG),$(call feature_pkg_config,$(package))) endif +FEATURE_CHECK_LDFLAGS-libcheck = -lcheck + # Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features. # If in the future we need per-feature checks/flags for features not # mentioned in this list we need to refactor this ;-). diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 1fbcb3ce74d2..99bdc8a6d26e 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -50,6 +50,7 @@ FILES= \ test-timerfd.bin \ test-libbabeltrace.bin \ test-libcapstone.bin \ + test-libcheck.bin \ test-compile-32.bin \ test-compile-x32.bin \ test-zlib.bin \ @@ -307,6 +308,9 @@ $(OUTPUT)test-libbabeltrace.bin: $(OUTPUT)test-libcapstone.bin: $(BUILD) # -lcapstone provided by $(FEATURE_CHECK_LDFLAGS-libcapstone) +$(OUTPUT)test-libcheck.bin: + $(BUILD) # -lcheck is provided by $(FEATURE_CHECK_LDFLAGS-libcheck) + $(OUTPUT)test-compile-32.bin: $(CC) -m32 -Wall -Werror -o $@ test-compile.c diff --git a/tools/build/feature/test-libcheck.c b/tools/build/feature/test-libcheck.c new file mode 100644 index 000000000000..cfb8d452e9ef --- /dev/null +++ b/tools/build/feature/test-libcheck.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +int main(void) +{ + Suite *s = suite_create("test"); + return s == 0; +} From 3a4080e5e426352a78f944db0ff29e029d89193b Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Mon, 19 Jan 2026 12:58:57 +0200 Subject: [PATCH 03/28] tools/rtla: Add unit tests for utils.c Add unit tests for utility functions in src/utils.c using the Check testing framework. The tests verify parse_cpu_set(), strtoi(), and parse_prio() functions. Unit tests are built conditionally when libcheck is available. Run tests with 'make unit-test'. The test framework uses the Check library which provides process isolation for each test, preventing failures in one test from affecting others. Signed-off-by: Costa Shulyupin Link: https://lore.kernel.org/r/20260119105857.797498-3-costa.shul@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/Build | 1 + tools/tracing/rtla/Makefile | 5 + tools/tracing/rtla/Makefile.config | 8 ++ tools/tracing/rtla/README.txt | 1 + tools/tracing/rtla/tests/unit/Build | 2 + tools/tracing/rtla/tests/unit/Makefile.unit | 17 +++ tools/tracing/rtla/tests/unit/unit_tests.c | 123 ++++++++++++++++++++ 7 files changed, 157 insertions(+) create mode 100644 tools/tracing/rtla/tests/unit/Build create mode 100644 tools/tracing/rtla/tests/unit/Makefile.unit create mode 100644 tools/tracing/rtla/tests/unit/unit_tests.c diff --git a/tools/tracing/rtla/Build b/tools/tracing/rtla/Build index 6c9d5b36a315..3ce2e0f567fd 100644 --- a/tools/tracing/rtla/Build +++ b/tools/tracing/rtla/Build @@ -1 +1,2 @@ rtla-y += src/ +unit_tests-y += tests/unit/ diff --git a/tools/tracing/rtla/Makefile b/tools/tracing/rtla/Makefile index 2701256abaf3..45690ee14544 100644 --- a/tools/tracing/rtla/Makefile +++ b/tools/tracing/rtla/Makefile @@ -33,12 +33,14 @@ DOCSRC := ../../../Documentation/tools/rtla/ FEATURE_TESTS := libtraceevent FEATURE_TESTS += libtracefs FEATURE_TESTS += libcpupower +FEATURE_TESTS += libcheck FEATURE_TESTS += libbpf FEATURE_TESTS += clang-bpf-co-re FEATURE_TESTS += bpftool-skeletons FEATURE_DISPLAY := libtraceevent FEATURE_DISPLAY += libtracefs FEATURE_DISPLAY += libcpupower +FEATURE_DISPLAY += libcheck FEATURE_DISPLAY += libbpf FEATURE_DISPLAY += clang-bpf-co-re FEATURE_DISPLAY += bpftool-skeletons @@ -47,6 +49,7 @@ all: $(RTLA) include $(srctree)/tools/build/Makefile.include include Makefile.rtla +include tests/unit/Makefile.unit # check for dependencies only on required targets NON_CONFIG_TARGETS := clean install tarball doc doc_clean doc_install @@ -109,6 +112,8 @@ clean: doc_clean fixdep-clean $(Q)rm -f rtla rtla-static fixdep FEATURE-DUMP rtla-* $(Q)rm -rf feature $(Q)rm -f src/timerlat.bpf.o src/timerlat.skel.h example/timerlat_bpf_action.o + $(Q)rm -f $(UNIT_TESTS) + check: $(RTLA) tests/bpf/bpf_action_map.o RTLA=$(RTLA) BPFTOOL=$(SYSTEM_BPFTOOL) prove -o -f -v tests/ examples: example/timerlat_bpf_action.o diff --git a/tools/tracing/rtla/Makefile.config b/tools/tracing/rtla/Makefile.config index 07ff5e8f3006..0bdd258b76de 100644 --- a/tools/tracing/rtla/Makefile.config +++ b/tools/tracing/rtla/Makefile.config @@ -61,6 +61,14 @@ else $(info Please install libcpupower-dev/kernel-tools-libs-devel) endif +$(call feature_check,libcheck) +ifeq ($(feature-libcheck), 1) + $(call detected,CONFIG_LIBCHECK) +else + $(info libcheck is missing, building without unit tests support.) + $(info Please install check-devel/check) +endif + ifndef BUILD_BPF_SKEL # BPF skeletons are used to implement improved sample collection, enable # them by default. diff --git a/tools/tracing/rtla/README.txt b/tools/tracing/rtla/README.txt index 43e98311d10f..a9faee4dbb3a 100644 --- a/tools/tracing/rtla/README.txt +++ b/tools/tracing/rtla/README.txt @@ -12,6 +12,7 @@ RTLA depends on the following libraries and tools: - libtracefs - libtraceevent - libcpupower (optional, for --deepest-idle-state) + - libcheck (optional, for unit tests) For BPF sample collection support, the following extra dependencies are required: diff --git a/tools/tracing/rtla/tests/unit/Build b/tools/tracing/rtla/tests/unit/Build new file mode 100644 index 000000000000..5f1e531ea8c9 --- /dev/null +++ b/tools/tracing/rtla/tests/unit/Build @@ -0,0 +1,2 @@ +unit_tests-y += unit_tests.o +unit_tests-y +=../../src/utils.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..2088c9cc3571 --- /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) + $(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $^ -lcheck + +$(UNIT_TESTS_IN): + 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/unit_tests.c b/tools/tracing/rtla/tests/unit/unit_tests.c new file mode 100644 index 000000000000..aa53f8605e36 --- /dev/null +++ b/tools/tracing/rtla/tests/unit/unit_tests.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "../../src/utils.h" + +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; + int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + + ck_assert_int_eq(parse_cpu_set("0", &set), 0); + ck_assert(CPU_ISSET(0, &set)); + ck_assert(!CPU_ISSET(1, &set)); + + if (nr_cpus > 2) { + ck_assert_int_eq(parse_cpu_set("0,2", &set), 0); + ck_assert(CPU_ISSET(0, &set)); + ck_assert(CPU_ISSET(2, &set)); + } + + if (nr_cpus > 3) { + 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)); + } + + if (nr_cpus > 5) { + 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; +} + +int main(void) +{ + int num_failed; + SRunner *sr; + + sr = srunner_create(utils_suite()); + srunner_run_all(sr, CK_NORMAL); + num_failed = srunner_ntests_failed(sr); + + srunner_free(sr); + + return (num_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} From ef59e454156eb068ba3f2d9e14b397fd7953f65a Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Mon, 19 Jan 2026 12:52:21 +0100 Subject: [PATCH 04/28] rtla/timerlat: Add --stack-format option In the current implementation, the auto-analysis code for printing the stack captured in the tracefs buffer of the aa instance stops at the first encountered address that cannot be resolved into a function symbol. This is not always the desired behavior on all platforms; sometimes, there might be resolvable entries after unresolvable ones, and sometimes, the user might want to inspect the raw pointers for the unresolvable entries. Add a new option, --stack-format, with three values: - truncate: stop at first unresolvable entry. This is the current behavior, and is kept as the default. - skip: skip unresolvable entries, but do not stop on them. - full: print all entries, including unresolvable ones. To make this work, the "size" field of the stack entry is now also read and used as the maximum number of entries to print, capped at 64, since that is the fixed length of the "caller" field. Link: https://lore.kernel.org/r/20260119115222.744150-1-tglozar@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/timerlat.c | 2 +- tools/tracing/rtla/src/timerlat.h | 1 + tools/tracing/rtla/src/timerlat_aa.c | 36 +++++++++++++++++++++----- tools/tracing/rtla/src/timerlat_aa.h | 2 +- tools/tracing/rtla/src/timerlat_hist.c | 10 +++++++ tools/tracing/rtla/src/timerlat_top.c | 10 +++++++ tools/tracing/rtla/src/utils.c | 18 +++++++++++++ tools/tracing/rtla/src/utils.h | 7 +++++ 8 files changed, 77 insertions(+), 9 deletions(-) diff --git a/tools/tracing/rtla/src/timerlat.c b/tools/tracing/rtla/src/timerlat.c index 8f8811f7a13b..9e4daed0aafc 100644 --- a/tools/tracing/rtla/src/timerlat.c +++ b/tools/tracing/rtla/src/timerlat.c @@ -134,7 +134,7 @@ int timerlat_enable(struct osnoise_tool *tool) if (!tool->aa) return -1; - retval = timerlat_aa_init(tool->aa, params->dump_tasks); + retval = timerlat_aa_init(tool->aa, params->dump_tasks, params->stack_format); if (retval) { err_msg("Failed to enable the auto analysis instance\n"); return retval; diff --git a/tools/tracing/rtla/src/timerlat.h b/tools/tracing/rtla/src/timerlat.h index 8dd5d134ce08..364203a29abd 100644 --- a/tools/tracing/rtla/src/timerlat.h +++ b/tools/tracing/rtla/src/timerlat.h @@ -28,6 +28,7 @@ struct timerlat_params { int deepest_idle_state; enum timerlat_tracing_mode mode; const char *bpf_action_program; + enum stack_format stack_format; }; #define to_timerlat_params(ptr) container_of(ptr, struct timerlat_params, common) diff --git a/tools/tracing/rtla/src/timerlat_aa.c b/tools/tracing/rtla/src/timerlat_aa.c index 31e66ea2b144..178de60dcef9 100644 --- a/tools/tracing/rtla/src/timerlat_aa.c +++ b/tools/tracing/rtla/src/timerlat_aa.c @@ -104,6 +104,7 @@ struct timerlat_aa_data { struct timerlat_aa_context { int nr_cpus; int dump_tasks; + enum stack_format stack_format; /* per CPU data */ struct timerlat_aa_data *taa_data; @@ -481,23 +482,43 @@ static int timerlat_aa_stack_handler(struct trace_seq *s, struct tep_record *rec { struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx(); struct timerlat_aa_data *taa_data = timerlat_aa_get_data(taa_ctx, record->cpu); + enum stack_format stack_format = taa_ctx->stack_format; unsigned long *caller; const char *function; - int val, i; + int val; + unsigned long long i; trace_seq_reset(taa_data->stack_seq); trace_seq_printf(taa_data->stack_seq, " Blocking thread stack trace\n"); caller = tep_get_field_raw(s, event, "caller", record, &val, 1); + if (caller) { - for (i = 0; ; i++) { + unsigned long long size; + unsigned long long max_entries; + + if (tep_get_field_val(s, event, "size", record, &size, 1) == 0) + max_entries = size < 64 ? size : 64; + else + max_entries = 64; + + for (i = 0; i < max_entries; i++) { function = tep_find_function(taa_ctx->tool->trace.tep, caller[i]); - if (!function) - break; - trace_seq_printf(taa_data->stack_seq, " %.*s -> %s\n", - 14, spaces, function); + if (!function) { + if (stack_format == STACK_FORMAT_TRUNCATE) + break; + else if (stack_format == STACK_FORMAT_SKIP) + continue; + else if (stack_format == STACK_FORMAT_FULL) + trace_seq_printf(taa_data->stack_seq, " %.*s -> 0x%lx\n", + 14, spaces, caller[i]); + } else { + trace_seq_printf(taa_data->stack_seq, " %.*s -> %s\n", + 14, spaces, function); + } } } + return 0; } @@ -1020,7 +1041,7 @@ out_ctx: * * Returns 0 on success, -1 otherwise. */ -int timerlat_aa_init(struct osnoise_tool *tool, int dump_tasks) +int timerlat_aa_init(struct osnoise_tool *tool, int dump_tasks, enum stack_format stack_format) { int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); struct timerlat_aa_context *taa_ctx; @@ -1035,6 +1056,7 @@ int timerlat_aa_init(struct osnoise_tool *tool, int dump_tasks) taa_ctx->nr_cpus = nr_cpus; taa_ctx->tool = tool; taa_ctx->dump_tasks = dump_tasks; + taa_ctx->stack_format = stack_format; taa_ctx->taa_data = calloc(nr_cpus, sizeof(*taa_ctx->taa_data)); if (!taa_ctx->taa_data) diff --git a/tools/tracing/rtla/src/timerlat_aa.h b/tools/tracing/rtla/src/timerlat_aa.h index cea4bb1531a8..a11b5f30cdce 100644 --- a/tools/tracing/rtla/src/timerlat_aa.h +++ b/tools/tracing/rtla/src/timerlat_aa.h @@ -3,7 +3,7 @@ * Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira */ -int timerlat_aa_init(struct osnoise_tool *tool, int dump_task); +int timerlat_aa_init(struct osnoise_tool *tool, int dump_task, enum stack_format stack_format); void timerlat_aa_destroy(void); void timerlat_auto_analysis(int irq_thresh, int thread_thresh); diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 096de8ba3efb..88211e54bc9d 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -747,6 +747,7 @@ static void timerlat_hist_usage(void) " --on-threshold : define action to be executed at latency threshold, multiple are allowed", " --on-end : define action to be executed at measurement end, multiple are allowed", " --bpf-action : load and execute BPF program when latency threshold is exceeded", + " --stack-format : set the stack format (truncate, skip, full)", NULL, }; @@ -787,6 +788,9 @@ static struct common_params /* default to BPF mode */ params->mode = TRACING_MODE_BPF; + /* default to truncate stack format */ + params->stack_format = STACK_FORMAT_TRUNCATE; + while (1) { static struct option long_options[] = { {"auto", required_argument, 0, 'a'}, @@ -819,6 +823,7 @@ static struct common_params {"on-threshold", required_argument, 0, '\5'}, {"on-end", required_argument, 0, '\6'}, {"bpf-action", required_argument, 0, '\7'}, + {"stack-format", required_argument, 0, '\10'}, {0, 0, 0, 0} }; @@ -965,6 +970,11 @@ static struct common_params case '\7': params->bpf_action_program = optarg; break; + case '\10': + params->stack_format = parse_stack_format(optarg); + if (params->stack_format == -1) + fatal("Invalid --stack-format option"); + break; default: fatal("Invalid option"); } diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 27c14aa71a8b..7a00f3844f56 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -518,6 +518,7 @@ static void timerlat_top_usage(void) " --on-threshold : define action to be executed at latency threshold, multiple are allowed", " --on-end: define action to be executed at measurement end, multiple are allowed", " --bpf-action : load and execute BPF program when latency threshold is exceeded", + " --stack-format : set the stack format (truncate, skip, full)", NULL, }; @@ -556,6 +557,9 @@ static struct common_params /* default to BPF mode */ params->mode = TRACING_MODE_BPF; + /* default to truncate stack format */ + params->stack_format = STACK_FORMAT_TRUNCATE; + while (1) { static struct option long_options[] = { {"auto", required_argument, 0, 'a'}, @@ -582,6 +586,7 @@ static struct common_params {"on-threshold", required_argument, 0, '9'}, {"on-end", required_argument, 0, '\1'}, {"bpf-action", required_argument, 0, '\2'}, + {"stack-format", required_argument, 0, '\3'}, {0, 0, 0, 0} }; @@ -715,6 +720,11 @@ static struct common_params case '\2': params->bpf_action_program = optarg; break; + case '\3': + params->stack_format = parse_stack_format(optarg); + if (params->stack_format == -1) + fatal("Invalid --stack-format option"); + break; default: fatal("Invalid option"); } diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index 0da3b2470c31..d979159f6b70 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -164,6 +164,24 @@ err: return 1; } +/* + * parse_stack_format - parse the stack format + * + * Return: the stack format on success, -1 otherwise. + */ +int parse_stack_format(char *arg) +{ + if (!strcmp(arg, "truncate")) + return STACK_FORMAT_TRUNCATE; + if (!strcmp(arg, "skip")) + return STACK_FORMAT_SKIP; + if (!strcmp(arg, "full")) + return STACK_FORMAT_FULL; + + debug_msg("Error parsing the stack format %s\n", arg); + return -1; +} + /* * parse_duration - parse duration with s/m/h/d suffix converting it to seconds */ diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h index f7c2a52a0ab5..80d5ec0cf934 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h @@ -62,8 +62,15 @@ struct sched_attr { }; #endif /* SCHED_ATTR_SIZE_VER0 */ +enum stack_format { + STACK_FORMAT_TRUNCATE, + STACK_FORMAT_SKIP, + STACK_FORMAT_FULL +}; + int parse_prio(char *arg, struct sched_attr *sched_param); int parse_cpu_set(char *cpu_list, cpu_set_t *set); +int parse_stack_format(char *arg); int __set_sched_attr(int pid, struct sched_attr *attr); int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr); int set_comm_cgroup(const char *comm_prefix, const char *cgroup); From e4be7e96d076b014abb77fc7fe72c92e8788f3df Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Mon, 19 Jan 2026 12:52:22 +0100 Subject: [PATCH 05/28] Documentation/rtla: Document --stack-format option Add documentation for --stack-format option of rtla-timerlat into its common options. Reviewed-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260119115222.744150-2-tglozar@redhat.com Signed-off-by: Tomas Glozar --- Documentation/tools/rtla/common_timerlat_options.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/tools/rtla/common_timerlat_options.txt b/Documentation/tools/rtla/common_timerlat_options.txt index 07a285fcf7cf..ab159b2cbfe7 100644 --- a/Documentation/tools/rtla/common_timerlat_options.txt +++ b/Documentation/tools/rtla/common_timerlat_options.txt @@ -83,3 +83,15 @@ **Note**: BPF actions require BPF support to be available. If BPF is not available or disabled, the tool falls back to tracefs mode and BPF actions are not supported. + +**--stack-format** *format* + + Adjust the format of the stack trace printed during auto-analysis. + + The supported values for *format* are: + + * **truncate** Print the stack trace up to the first unknown address (default). + * **skip** Skip unknown addresses. + * **full** Print the entire stack trace, including unknown addresses. + + For unknown addresses, the raw pointer is printed. From 115b06a008756ec777249dc9f432dc1a6c681396 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Fri, 6 Mar 2026 21:49:49 +0200 Subject: [PATCH 06/28] tools/rtla: Consolidate nr_cpus usage across all tools sysconf(_SC_NPROCESSORS_CONF) (via get_nprocs_conf) reflects cpu_possible_mask, which is fixed at boot time, so querying it repeatedly is unnecessary. Replace multiple calls to sysconf(_SC_NPROCESSORS_CONF) with a single global nr_cpus variable initialized once at startup. `#pragma once` in timerlat_u.h is needed for pre-C23 compilers to avoid redefinition errors. Signed-off-by: Costa Shulyupin Link: https://lore.kernel.org/r/20260306194953.2511960-2-costa.shul@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/common.c | 7 ++-- tools/tracing/rtla/src/common.h | 2 ++ tools/tracing/rtla/src/osnoise_hist.c | 3 -- tools/tracing/rtla/src/osnoise_top.c | 7 ---- tools/tracing/rtla/src/timerlat.c | 5 +-- tools/tracing/rtla/src/timerlat_aa.c | 1 - tools/tracing/rtla/src/timerlat_hist.c | 3 -- tools/tracing/rtla/src/timerlat_top.c | 7 ---- tools/tracing/rtla/src/timerlat_u.c | 3 +- tools/tracing/rtla/src/timerlat_u.h | 1 + tools/tracing/rtla/src/utils.c | 10 +----- tools/tracing/rtla/tests/unit/unit_tests.c | 40 ++++++++++------------ 12 files changed, 29 insertions(+), 60 deletions(-) diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c index f310b0d59ad3..a31fbaea5da6 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -5,12 +5,14 @@ #include #include #include -#include #include +#include + #include "common.h" struct trace_instance *trace_inst; volatile int stop_tracing; +int nr_cpus; static void stop_trace(int sig) { @@ -165,7 +167,7 @@ common_apply_config(struct osnoise_tool *tool, struct common_params *params) } if (!params->cpus) { - for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++) + for (i = 0; i < nr_cpus; i++) CPU_SET(i, ¶ms->monitored_cpus); } @@ -213,6 +215,7 @@ int run_tool(struct tool_ops *ops, int argc, char *argv[]) bool stopped; int retval; + nr_cpus = get_nprocs_conf(); params = ops->parse_args(argc, argv); if (!params) exit(1); diff --git a/tools/tracing/rtla/src/common.h b/tools/tracing/rtla/src/common.h index d4b3715700be..90a3c0d1dbf3 100644 --- a/tools/tracing/rtla/src/common.h +++ b/tools/tracing/rtla/src/common.h @@ -108,6 +108,8 @@ struct common_params { struct timerlat_u_params user; }; +extern int nr_cpus; + #define for_each_monitored_cpu(cpu, nr_cpus, common) \ for (cpu = 0; cpu < nr_cpus; cpu++) \ if (!(common)->cpus || CPU_ISSET(cpu, &(common)->monitored_cpus)) diff --git a/tools/tracing/rtla/src/osnoise_hist.c b/tools/tracing/rtla/src/osnoise_hist.c index 5c863e7aad28..00b8c95cbf85 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c @@ -646,9 +646,6 @@ static struct osnoise_tool *osnoise_init_hist(struct common_params *params) { struct osnoise_tool *tool; - int nr_cpus; - - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); tool = osnoise_init_tool("osnoise_hist"); if (!tool) diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/osnoise_top.c index b7aed40fd216..9a6cd9a2470a 100644 --- a/tools/tracing/rtla/src/osnoise_top.c +++ b/tools/tracing/rtla/src/osnoise_top.c @@ -232,12 +232,8 @@ osnoise_print_stats(struct osnoise_tool *top) { struct osnoise_params *params = to_osnoise_params(top->params); struct trace_instance *trace = &top->trace; - static int nr_cpus = -1; int i; - if (nr_cpus == -1) - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - if (!params->common.quiet) clear_terminal(trace->seq); @@ -494,9 +490,6 @@ out_err: struct osnoise_tool *osnoise_init_top(struct common_params *params) { struct osnoise_tool *tool; - int nr_cpus; - - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); tool = osnoise_init_tool("osnoise_top"); if (!tool) diff --git a/tools/tracing/rtla/src/timerlat.c b/tools/tracing/rtla/src/timerlat.c index 9e4daed0aafc..31c921efa7c1 100644 --- a/tools/tracing/rtla/src/timerlat.c +++ b/tools/tracing/rtla/src/timerlat.c @@ -99,7 +99,7 @@ out_err: int timerlat_enable(struct osnoise_tool *tool) { struct timerlat_params *params = to_timerlat_params(tool->params); - int retval, nr_cpus, i; + int retval, i; if (params->dma_latency >= 0) { dma_latency_fd = set_cpu_dma_latency(params->dma_latency); @@ -115,8 +115,6 @@ int timerlat_enable(struct osnoise_tool *tool) return -1; } - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - for_each_monitored_cpu(i, nr_cpus, ¶ms->common) { if (save_cpu_idle_disable_state(i) < 0) { err_msg("Could not save cpu idle state.\n"); @@ -214,7 +212,6 @@ void timerlat_analyze(struct osnoise_tool *tool, bool stopped) void timerlat_free(struct osnoise_tool *tool) { struct timerlat_params *params = to_timerlat_params(tool->params); - int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); int i; timerlat_aa_destroy(); diff --git a/tools/tracing/rtla/src/timerlat_aa.c b/tools/tracing/rtla/src/timerlat_aa.c index 178de60dcef9..095483375823 100644 --- a/tools/tracing/rtla/src/timerlat_aa.c +++ b/tools/tracing/rtla/src/timerlat_aa.c @@ -1043,7 +1043,6 @@ out_ctx: */ int timerlat_aa_init(struct osnoise_tool *tool, int dump_tasks, enum stack_format stack_format) { - int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); struct timerlat_aa_context *taa_ctx; int retval; diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 88211e54bc9d..3ebe41eed9f6 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -1040,9 +1040,6 @@ static struct osnoise_tool *timerlat_init_hist(struct common_params *params) { struct osnoise_tool *tool; - int nr_cpus; - - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); tool = osnoise_init_tool("timerlat_hist"); if (!tool) diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 7a00f3844f56..4105638f45c4 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -442,15 +442,11 @@ timerlat_print_stats(struct osnoise_tool *top) struct timerlat_params *params = to_timerlat_params(top->params); struct trace_instance *trace = &top->trace; struct timerlat_top_cpu summary; - static int nr_cpus = -1; int i; if (params->common.aa_only) return; - if (nr_cpus == -1) - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - if (!params->common.quiet) clear_terminal(trace->seq); @@ -790,9 +786,6 @@ static struct osnoise_tool *timerlat_init_top(struct common_params *params) { struct osnoise_tool *top; - int nr_cpus; - - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); top = osnoise_init_tool("timerlat_top"); if (!top) diff --git a/tools/tracing/rtla/src/timerlat_u.c b/tools/tracing/rtla/src/timerlat_u.c index ce68e39d25fd..a569fe7f93aa 100644 --- a/tools/tracing/rtla/src/timerlat_u.c +++ b/tools/tracing/rtla/src/timerlat_u.c @@ -16,7 +16,7 @@ #include #include -#include "utils.h" +#include "common.h" #include "timerlat_u.h" /* @@ -131,7 +131,6 @@ static int timerlat_u_send_kill(pid_t *procs, int nr_cpus) */ void *timerlat_u_dispatcher(void *data) { - int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); struct timerlat_u_params *params = data; char proc_name[128]; int procs_count = 0; diff --git a/tools/tracing/rtla/src/timerlat_u.h b/tools/tracing/rtla/src/timerlat_u.h index 661511908957..a692331bd1c7 100644 --- a/tools/tracing/rtla/src/timerlat_u.h +++ b/tools/tracing/rtla/src/timerlat_u.h @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 +#pragma once /* * Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira */ diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index d979159f6b70..77593718766a 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -19,7 +19,7 @@ #include #include -#include "utils.h" +#include "common.h" #define MAX_MSG_LENGTH 1024 int config_debug; @@ -119,14 +119,11 @@ int parse_cpu_set(char *cpu_list, cpu_set_t *set) { const char *p; int end_cpu; - int nr_cpus; int cpu; int i; CPU_ZERO(set); - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - for (p = cpu_list; *p; ) { cpu = atoi(p); if (cpu < 0 || (!cpu && *p != '0') || cpu >= nr_cpus) @@ -577,7 +574,6 @@ int save_cpu_idle_disable_state(unsigned int cpu) unsigned int nr_states; unsigned int state; int disabled; - int nr_cpus; nr_states = cpuidle_state_count(cpu); @@ -585,7 +581,6 @@ int save_cpu_idle_disable_state(unsigned int cpu) return 0; if (saved_cpu_idle_disable_state == NULL) { - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); saved_cpu_idle_disable_state = calloc(nr_cpus, sizeof(unsigned int *)); if (!saved_cpu_idle_disable_state) return -1; @@ -662,13 +657,10 @@ int restore_cpu_idle_disable_state(unsigned int cpu) void free_cpu_idle_disable_states(void) { int cpu; - int nr_cpus; if (!saved_cpu_idle_disable_state) return; - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - for (cpu = 0; cpu < nr_cpus; cpu++) { free(saved_cpu_idle_disable_state[cpu]); saved_cpu_idle_disable_state[cpu] = NULL; diff --git a/tools/tracing/rtla/tests/unit/unit_tests.c b/tools/tracing/rtla/tests/unit/unit_tests.c index aa53f8605e36..f3c6d89e3300 100644 --- a/tools/tracing/rtla/tests/unit/unit_tests.c +++ b/tools/tracing/rtla/tests/unit/unit_tests.c @@ -7,8 +7,10 @@ #include #include #include +#include #include "../../src/utils.h" +int nr_cpus; START_TEST(test_strtoi) { @@ -34,35 +36,29 @@ END_TEST START_TEST(test_parse_cpu_set) { cpu_set_t set; - int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + 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)); - if (nr_cpus > 2) { - 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,2", &set), 0); + ck_assert(CPU_ISSET(0, &set)); + ck_assert(CPU_ISSET(2, &set)); - if (nr_cpus > 3) { - 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("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)); - if (nr_cpus > 5) { - 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-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); From ea06305ff9920115e07b6947568cbfba4736e10c Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Fri, 6 Mar 2026 21:49:50 +0200 Subject: [PATCH 07/28] tools/rtla: Remove unneeded nr_cpus arguments nr_cpus does not change at runtime, so passing it through function arguments is unnecessary. Use the global nr_cpus instead of propagating it via parameters. Signed-off-by: Costa Shulyupin Link: https://lore.kernel.org/r/20260306194953.2511960-3-costa.shul@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/osnoise_hist.c | 4 ++-- tools/tracing/rtla/src/osnoise_top.c | 4 ++-- tools/tracing/rtla/src/timerlat_bpf.c | 19 ++++++++----------- tools/tracing/rtla/src/timerlat_bpf.h | 12 ++++-------- tools/tracing/rtla/src/timerlat_hist.c | 21 ++++++++------------- tools/tracing/rtla/src/timerlat_top.c | 19 +++++++------------ tools/tracing/rtla/src/timerlat_u.c | 6 +++--- 7 files changed, 34 insertions(+), 51 deletions(-) diff --git a/tools/tracing/rtla/src/osnoise_hist.c b/tools/tracing/rtla/src/osnoise_hist.c index 00b8c95cbf85..f39f60d3b00e 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c @@ -62,7 +62,7 @@ static void osnoise_free_hist_tool(struct osnoise_tool *tool) * osnoise_alloc_histogram - alloc runtime data */ static struct osnoise_hist_data -*osnoise_alloc_histogram(int nr_cpus, int entries, int bucket_size) +*osnoise_alloc_histogram(int entries, int bucket_size) { struct osnoise_hist_data *data; int cpu; @@ -651,7 +651,7 @@ static struct osnoise_tool if (!tool) return NULL; - tool->data = osnoise_alloc_histogram(nr_cpus, params->hist.entries, + tool->data = osnoise_alloc_histogram(params->hist.entries, params->hist.bucket_size); if (!tool->data) goto out_err; diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/osnoise_top.c index 9a6cd9a2470a..3a241b69f622 100644 --- a/tools/tracing/rtla/src/osnoise_top.c +++ b/tools/tracing/rtla/src/osnoise_top.c @@ -51,7 +51,7 @@ static void osnoise_free_top_tool(struct osnoise_tool *tool) /* * osnoise_alloc_histogram - alloc runtime data */ -static struct osnoise_top_data *osnoise_alloc_top(int nr_cpus) +static struct osnoise_top_data *osnoise_alloc_top(void) { struct osnoise_top_data *data; @@ -495,7 +495,7 @@ struct osnoise_tool *osnoise_init_top(struct common_params *params) if (!tool) return NULL; - tool->data = osnoise_alloc_top(nr_cpus); + tool->data = osnoise_alloc_top(); if (!tool->data) { osnoise_destroy_tool(tool); return NULL; diff --git a/tools/tracing/rtla/src/timerlat_bpf.c b/tools/tracing/rtla/src/timerlat_bpf.c index 05adf18303df..dd3cf71d74e5 100644 --- a/tools/tracing/rtla/src/timerlat_bpf.c +++ b/tools/tracing/rtla/src/timerlat_bpf.c @@ -147,24 +147,23 @@ static int get_value(struct bpf_map *map_irq, int key, long long *value_irq, long long *value_thread, - long long *value_user, - int cpus) + long long *value_user) { int err; err = bpf_map__lookup_elem(map_irq, &key, sizeof(unsigned int), value_irq, - sizeof(long long) * cpus, 0); + sizeof(long long) * nr_cpus, 0); if (err) return err; err = bpf_map__lookup_elem(map_thread, &key, sizeof(unsigned int), value_thread, - sizeof(long long) * cpus, 0); + sizeof(long long) * nr_cpus, 0); if (err) return err; err = bpf_map__lookup_elem(map_user, &key, sizeof(unsigned int), value_user, - sizeof(long long) * cpus, 0); + sizeof(long long) * nr_cpus, 0); if (err) return err; return 0; @@ -176,13 +175,12 @@ static int get_value(struct bpf_map *map_irq, int timerlat_bpf_get_hist_value(int key, long long *value_irq, long long *value_thread, - long long *value_user, - int cpus) + long long *value_user) { return get_value(bpf->maps.hist_irq, bpf->maps.hist_thread, bpf->maps.hist_user, - key, value_irq, value_thread, value_user, cpus); + key, value_irq, value_thread, value_user); } /* @@ -191,13 +189,12 @@ int timerlat_bpf_get_hist_value(int key, int timerlat_bpf_get_summary_value(enum summary_field key, long long *value_irq, long long *value_thread, - long long *value_user, - int cpus) + long long *value_user) { return get_value(bpf->maps.summary_irq, bpf->maps.summary_thread, bpf->maps.summary_user, - key, value_irq, value_thread, value_user, cpus); + key, value_irq, value_thread, value_user); } /* diff --git a/tools/tracing/rtla/src/timerlat_bpf.h b/tools/tracing/rtla/src/timerlat_bpf.h index 169abeaf4363..531c9ef16f51 100644 --- a/tools/tracing/rtla/src/timerlat_bpf.h +++ b/tools/tracing/rtla/src/timerlat_bpf.h @@ -23,13 +23,11 @@ int timerlat_bpf_restart_tracing(void); int timerlat_bpf_get_hist_value(int key, long long *value_irq, long long *value_thread, - long long *value_user, - int cpus); + long long *value_user); int timerlat_bpf_get_summary_value(enum summary_field key, long long *value_irq, long long *value_thread, - long long *value_user, - int cpus); + long long *value_user); int timerlat_load_bpf_action_program(const char *program_path); static inline int have_libbpf_support(void) { return 1; } #else @@ -45,16 +43,14 @@ static inline int timerlat_bpf_restart_tracing(void) { return -1; }; static inline int timerlat_bpf_get_hist_value(int key, long long *value_irq, long long *value_thread, - long long *value_user, - int cpus) + long long *value_user) { return -1; } static inline int timerlat_bpf_get_summary_value(enum summary_field key, long long *value_irq, long long *value_thread, - long long *value_user, - int cpus) + long long *value_user) { return -1; } diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 3ebe41eed9f6..7e735b62488c 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -83,7 +83,7 @@ static void timerlat_free_histogram_tool(struct osnoise_tool *tool) * timerlat_alloc_histogram - alloc runtime data */ static struct timerlat_hist_data -*timerlat_alloc_histogram(int nr_cpus, int entries, int bucket_size) +*timerlat_alloc_histogram(int entries, int bucket_size) { struct timerlat_hist_data *data; int cpu; @@ -211,7 +211,7 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) /* Pull histogram */ for (i = 0; i < data->entries; i++) { err = timerlat_bpf_get_hist_value(i, value_irq, value_thread, - value_user, data->nr_cpus); + value_user); if (err) return err; for (j = 0; j < data->nr_cpus; j++) { @@ -223,8 +223,7 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) /* Pull summary */ err = timerlat_bpf_get_summary_value(SUMMARY_COUNT, - value_irq, value_thread, value_user, - data->nr_cpus); + value_irq, value_thread, value_user); if (err) return err; for (i = 0; i < data->nr_cpus; i++) { @@ -234,8 +233,7 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) } err = timerlat_bpf_get_summary_value(SUMMARY_MIN, - value_irq, value_thread, value_user, - data->nr_cpus); + value_irq, value_thread, value_user); if (err) return err; for (i = 0; i < data->nr_cpus; i++) { @@ -245,8 +243,7 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) } err = timerlat_bpf_get_summary_value(SUMMARY_MAX, - value_irq, value_thread, value_user, - data->nr_cpus); + value_irq, value_thread, value_user); if (err) return err; for (i = 0; i < data->nr_cpus; i++) { @@ -256,8 +253,7 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) } err = timerlat_bpf_get_summary_value(SUMMARY_SUM, - value_irq, value_thread, value_user, - data->nr_cpus); + value_irq, value_thread, value_user); if (err) return err; for (i = 0; i < data->nr_cpus; i++) { @@ -267,8 +263,7 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) } err = timerlat_bpf_get_summary_value(SUMMARY_OVERFLOW, - value_irq, value_thread, value_user, - data->nr_cpus); + value_irq, value_thread, value_user); if (err) return err; for (i = 0; i < data->nr_cpus; i++) { @@ -1045,7 +1040,7 @@ static struct osnoise_tool if (!tool) return NULL; - tool->data = timerlat_alloc_histogram(nr_cpus, params->hist.entries, + tool->data = timerlat_alloc_histogram(params->hist.entries, params->hist.bucket_size); if (!tool->data) goto out_err; diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 4105638f45c4..994e89a57cd3 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -62,7 +62,7 @@ static void timerlat_free_top_tool(struct osnoise_tool *tool) /* * timerlat_alloc_histogram - alloc runtime data */ -static struct timerlat_top_data *timerlat_alloc_top(int nr_cpus) +static struct timerlat_top_data *timerlat_alloc_top(void) { struct timerlat_top_data *data; int cpu; @@ -196,8 +196,7 @@ static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool) /* Pull summary */ err = timerlat_bpf_get_summary_value(SUMMARY_CURRENT, - value_irq, value_thread, value_user, - data->nr_cpus); + value_irq, value_thread, value_user); if (err) return err; for (i = 0; i < data->nr_cpus; i++) { @@ -207,8 +206,7 @@ static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool) } err = timerlat_bpf_get_summary_value(SUMMARY_COUNT, - value_irq, value_thread, value_user, - data->nr_cpus); + value_irq, value_thread, value_user); if (err) return err; for (i = 0; i < data->nr_cpus; i++) { @@ -218,8 +216,7 @@ static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool) } err = timerlat_bpf_get_summary_value(SUMMARY_MIN, - value_irq, value_thread, value_user, - data->nr_cpus); + value_irq, value_thread, value_user); if (err) return err; for (i = 0; i < data->nr_cpus; i++) { @@ -229,8 +226,7 @@ static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool) } err = timerlat_bpf_get_summary_value(SUMMARY_MAX, - value_irq, value_thread, value_user, - data->nr_cpus); + value_irq, value_thread, value_user); if (err) return err; for (i = 0; i < data->nr_cpus; i++) { @@ -240,8 +236,7 @@ static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool) } err = timerlat_bpf_get_summary_value(SUMMARY_SUM, - value_irq, value_thread, value_user, - data->nr_cpus); + value_irq, value_thread, value_user); if (err) return err; for (i = 0; i < data->nr_cpus; i++) { @@ -791,7 +786,7 @@ static struct osnoise_tool if (!top) return NULL; - top->data = timerlat_alloc_top(nr_cpus); + top->data = timerlat_alloc_top(); if (!top->data) goto out_err; diff --git a/tools/tracing/rtla/src/timerlat_u.c b/tools/tracing/rtla/src/timerlat_u.c index a569fe7f93aa..03b4e68e8b1e 100644 --- a/tools/tracing/rtla/src/timerlat_u.c +++ b/tools/tracing/rtla/src/timerlat_u.c @@ -99,7 +99,7 @@ static int timerlat_u_main(int cpu, struct timerlat_u_params *params) * * Return the number of processes that received the kill. */ -static int timerlat_u_send_kill(pid_t *procs, int nr_cpus) +static int timerlat_u_send_kill(pid_t *procs) { int killed = 0; int i, retval; @@ -169,7 +169,7 @@ void *timerlat_u_dispatcher(void *data) /* parent */ if (pid == -1) { - timerlat_u_send_kill(procs, nr_cpus); + timerlat_u_send_kill(procs); debug_msg("Failed to create child processes"); pthread_exit(&retval); } @@ -196,7 +196,7 @@ void *timerlat_u_dispatcher(void *data) sleep(1); } - timerlat_u_send_kill(procs, nr_cpus); + timerlat_u_send_kill(procs); while (procs_count) { pid = waitpid(-1, &wstatus, 0); From caf3fc0fdd63b4b163ee4eac1ca822772108c0f8 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Fri, 6 Mar 2026 21:49:51 +0200 Subject: [PATCH 08/28] tools/rtla: Remove unneeded nr_cpus members nr_cpus does not change at runtime, so keeping it in struct members is unnecessary. Use the global nr_cpus instead of struct members. Signed-off-by: Costa Shulyupin Link: https://lore.kernel.org/r/20260306194953.2511960-4-costa.shul@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/osnoise_hist.c | 18 ++++++------ tools/tracing/rtla/src/osnoise_top.c | 3 -- tools/tracing/rtla/src/timerlat_aa.c | 10 +++---- tools/tracing/rtla/src/timerlat_hist.c | 38 ++++++++++++-------------- tools/tracing/rtla/src/timerlat_top.c | 19 ++++++------- 5 files changed, 38 insertions(+), 50 deletions(-) diff --git a/tools/tracing/rtla/src/osnoise_hist.c b/tools/tracing/rtla/src/osnoise_hist.c index f39f60d3b00e..5bbec27b46a7 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c @@ -29,7 +29,6 @@ struct osnoise_hist_data { struct osnoise_hist_cpu *hist; int entries; int bucket_size; - int nr_cpus; }; /* @@ -41,7 +40,7 @@ osnoise_free_histogram(struct osnoise_hist_data *data) int cpu; /* one histogram for IRQ and one for thread, per CPU */ - for (cpu = 0; cpu < data->nr_cpus; cpu++) { + for (cpu = 0; cpu < nr_cpus; cpu++) { if (data->hist[cpu].samples) free(data->hist[cpu].samples); } @@ -73,7 +72,6 @@ static struct osnoise_hist_data data->entries = entries; data->bucket_size = bucket_size; - data->nr_cpus = nr_cpus; data->hist = calloc(1, sizeof(*data->hist) * nr_cpus); if (!data->hist) @@ -246,7 +244,7 @@ static void osnoise_hist_header(struct osnoise_tool *tool) if (!params->common.hist.no_index) trace_seq_printf(s, "Index"); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -275,7 +273,7 @@ osnoise_print_summary(struct osnoise_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "count:"); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -287,7 +285,7 @@ osnoise_print_summary(struct osnoise_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "min: "); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -300,7 +298,7 @@ osnoise_print_summary(struct osnoise_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "avg: "); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -316,7 +314,7 @@ osnoise_print_summary(struct osnoise_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "max: "); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -351,7 +349,7 @@ osnoise_print_stats(struct osnoise_tool *tool) trace_seq_printf(trace->seq, "%-6d", bucket * data->bucket_size); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -387,7 +385,7 @@ osnoise_print_stats(struct osnoise_tool *tool) if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "over: "); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].count) continue; diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/osnoise_top.c index 3a241b69f622..672cdb254f4c 100644 --- a/tools/tracing/rtla/src/osnoise_top.c +++ b/tools/tracing/rtla/src/osnoise_top.c @@ -31,7 +31,6 @@ struct osnoise_top_cpu { struct osnoise_top_data { struct osnoise_top_cpu *cpu_data; - int nr_cpus; }; /* @@ -59,8 +58,6 @@ static struct osnoise_top_data *osnoise_alloc_top(void) if (!data) return NULL; - data->nr_cpus = nr_cpus; - /* one set of histograms per CPU */ data->cpu_data = calloc(1, sizeof(*data->cpu_data) * nr_cpus); if (!data->cpu_data) diff --git a/tools/tracing/rtla/src/timerlat_aa.c b/tools/tracing/rtla/src/timerlat_aa.c index 095483375823..41d8d48c5b41 100644 --- a/tools/tracing/rtla/src/timerlat_aa.c +++ b/tools/tracing/rtla/src/timerlat_aa.c @@ -102,7 +102,6 @@ struct timerlat_aa_data { * The analysis context and system wide view */ struct timerlat_aa_context { - int nr_cpus; int dump_tasks; enum stack_format stack_format; @@ -759,7 +758,7 @@ void timerlat_auto_analysis(int irq_thresh, int thread_thresh) irq_thresh = irq_thresh * 1000; thread_thresh = thread_thresh * 1000; - for (cpu = 0; cpu < taa_ctx->nr_cpus; cpu++) { + for (cpu = 0; cpu < nr_cpus; cpu++) { taa_data = timerlat_aa_get_data(taa_ctx, cpu); if (irq_thresh && taa_data->tlat_irq_latency >= irq_thresh) { @@ -787,7 +786,7 @@ void timerlat_auto_analysis(int irq_thresh, int thread_thresh) printf("\n"); printf("Printing CPU tasks:\n"); - for (cpu = 0; cpu < taa_ctx->nr_cpus; cpu++) { + for (cpu = 0; cpu < nr_cpus; cpu++) { taa_data = timerlat_aa_get_data(taa_ctx, cpu); tep = taa_ctx->tool->trace.tep; @@ -813,7 +812,7 @@ static void timerlat_aa_destroy_seqs(struct timerlat_aa_context *taa_ctx) if (!taa_ctx->taa_data) return; - for (i = 0; i < taa_ctx->nr_cpus; i++) { + for (i = 0; i < nr_cpus; i++) { taa_data = timerlat_aa_get_data(taa_ctx, i); if (taa_data->prev_irqs_seq) { @@ -863,7 +862,7 @@ static int timerlat_aa_init_seqs(struct timerlat_aa_context *taa_ctx) struct timerlat_aa_data *taa_data; int i; - for (i = 0; i < taa_ctx->nr_cpus; i++) { + for (i = 0; i < nr_cpus; i++) { taa_data = timerlat_aa_get_data(taa_ctx, i); @@ -1052,7 +1051,6 @@ int timerlat_aa_init(struct osnoise_tool *tool, int dump_tasks, enum stack_forma __timerlat_aa_ctx = taa_ctx; - taa_ctx->nr_cpus = nr_cpus; taa_ctx->tool = tool; taa_ctx->dump_tasks = dump_tasks; taa_ctx->stack_format = stack_format; diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 7e735b62488c..cfb745b75b8a 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -44,7 +44,6 @@ struct timerlat_hist_data { struct timerlat_hist_cpu *hist; int entries; int bucket_size; - int nr_cpus; }; /* @@ -56,7 +55,7 @@ timerlat_free_histogram(struct timerlat_hist_data *data) int cpu; /* one histogram for IRQ and one for thread, per CPU */ - for (cpu = 0; cpu < data->nr_cpus; cpu++) { + for (cpu = 0; cpu < nr_cpus; cpu++) { if (data->hist[cpu].irq) free(data->hist[cpu].irq); @@ -94,7 +93,6 @@ static struct timerlat_hist_data data->entries = entries; data->bucket_size = bucket_size; - data->nr_cpus = nr_cpus; /* one set of histograms per CPU */ data->hist = calloc(1, sizeof(*data->hist) * nr_cpus); @@ -204,9 +202,9 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) { struct timerlat_hist_data *data = tool->data; int i, j, err; - long long value_irq[data->nr_cpus], - value_thread[data->nr_cpus], - value_user[data->nr_cpus]; + long long value_irq[nr_cpus], + value_thread[nr_cpus], + value_user[nr_cpus]; /* Pull histogram */ for (i = 0; i < data->entries; i++) { @@ -214,7 +212,7 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) value_user); if (err) return err; - for (j = 0; j < data->nr_cpus; j++) { + for (j = 0; j < nr_cpus; j++) { data->hist[j].irq[i] = value_irq[j]; data->hist[j].thread[i] = value_thread[j]; data->hist[j].user[i] = value_user[j]; @@ -226,7 +224,7 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) value_irq, value_thread, value_user); if (err) return err; - for (i = 0; i < data->nr_cpus; i++) { + for (i = 0; i < nr_cpus; i++) { data->hist[i].irq_count = value_irq[i]; data->hist[i].thread_count = value_thread[i]; data->hist[i].user_count = value_user[i]; @@ -236,7 +234,7 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) value_irq, value_thread, value_user); if (err) return err; - for (i = 0; i < data->nr_cpus; i++) { + for (i = 0; i < nr_cpus; i++) { data->hist[i].min_irq = value_irq[i]; data->hist[i].min_thread = value_thread[i]; data->hist[i].min_user = value_user[i]; @@ -246,7 +244,7 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) value_irq, value_thread, value_user); if (err) return err; - for (i = 0; i < data->nr_cpus; i++) { + for (i = 0; i < nr_cpus; i++) { data->hist[i].max_irq = value_irq[i]; data->hist[i].max_thread = value_thread[i]; data->hist[i].max_user = value_user[i]; @@ -256,7 +254,7 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) value_irq, value_thread, value_user); if (err) return err; - for (i = 0; i < data->nr_cpus; i++) { + for (i = 0; i < nr_cpus; i++) { data->hist[i].sum_irq = value_irq[i]; data->hist[i].sum_thread = value_thread[i]; data->hist[i].sum_user = value_user[i]; @@ -266,7 +264,7 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) value_irq, value_thread, value_user); if (err) return err; - for (i = 0; i < data->nr_cpus; i++) { + for (i = 0; i < nr_cpus; i++) { data->hist[i].irq[data->entries] = value_irq[i]; data->hist[i].thread[data->entries] = value_thread[i]; data->hist[i].user[data->entries] = value_user[i]; @@ -300,7 +298,7 @@ static void timerlat_hist_header(struct osnoise_tool *tool) if (!params->common.hist.no_index) trace_seq_printf(s, "Index"); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -352,7 +350,7 @@ timerlat_print_summary(struct timerlat_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "count:"); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -374,7 +372,7 @@ timerlat_print_summary(struct timerlat_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "min: "); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -402,7 +400,7 @@ timerlat_print_summary(struct timerlat_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "avg: "); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -430,7 +428,7 @@ timerlat_print_summary(struct timerlat_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "max: "); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -475,7 +473,7 @@ timerlat_print_stats_all(struct timerlat_params *params, sum.min_thread = ~0; sum.min_user = ~0; - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -622,7 +620,7 @@ timerlat_print_stats(struct osnoise_tool *tool) trace_seq_printf(trace->seq, "%-6d", bucket * data->bucket_size); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -660,7 +658,7 @@ timerlat_print_stats(struct osnoise_tool *tool) if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "over: "); - for_each_monitored_cpu(cpu, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 994e89a57cd3..8fad4edb0d72 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -41,7 +41,6 @@ struct timerlat_top_cpu { struct timerlat_top_data { struct timerlat_top_cpu *cpu_data; - int nr_cpus; }; /* @@ -71,8 +70,6 @@ static struct timerlat_top_data *timerlat_alloc_top(void) if (!data) return NULL; - data->nr_cpus = nr_cpus; - /* one set of histograms per CPU */ data->cpu_data = calloc(1, sizeof(*data->cpu_data) * nr_cpus); if (!data->cpu_data) @@ -190,16 +187,16 @@ static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool) { struct timerlat_top_data *data = tool->data; int i, err; - long long value_irq[data->nr_cpus], - value_thread[data->nr_cpus], - value_user[data->nr_cpus]; + long long value_irq[nr_cpus], + value_thread[nr_cpus], + value_user[nr_cpus]; /* Pull summary */ err = timerlat_bpf_get_summary_value(SUMMARY_CURRENT, value_irq, value_thread, value_user); if (err) return err; - for (i = 0; i < data->nr_cpus; i++) { + for (i = 0; i < nr_cpus; i++) { data->cpu_data[i].cur_irq = value_irq[i]; data->cpu_data[i].cur_thread = value_thread[i]; data->cpu_data[i].cur_user = value_user[i]; @@ -209,7 +206,7 @@ static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool) value_irq, value_thread, value_user); if (err) return err; - for (i = 0; i < data->nr_cpus; i++) { + for (i = 0; i < nr_cpus; i++) { data->cpu_data[i].irq_count = value_irq[i]; data->cpu_data[i].thread_count = value_thread[i]; data->cpu_data[i].user_count = value_user[i]; @@ -219,7 +216,7 @@ static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool) value_irq, value_thread, value_user); if (err) return err; - for (i = 0; i < data->nr_cpus; i++) { + for (i = 0; i < nr_cpus; i++) { data->cpu_data[i].min_irq = value_irq[i]; data->cpu_data[i].min_thread = value_thread[i]; data->cpu_data[i].min_user = value_user[i]; @@ -229,7 +226,7 @@ static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool) value_irq, value_thread, value_user); if (err) return err; - for (i = 0; i < data->nr_cpus; i++) { + for (i = 0; i < nr_cpus; i++) { data->cpu_data[i].max_irq = value_irq[i]; data->cpu_data[i].max_thread = value_thread[i]; data->cpu_data[i].max_user = value_user[i]; @@ -239,7 +236,7 @@ static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool) value_irq, value_thread, value_user); if (err) return err; - for (i = 0; i < data->nr_cpus; i++) { + for (i = 0; i < nr_cpus; i++) { data->cpu_data[i].sum_irq = value_irq[i]; data->cpu_data[i].sum_thread = value_thread[i]; data->cpu_data[i].sum_user = value_user[i]; From 458c9519ab21ea473020c53a50d7d5b1720ded30 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Fri, 6 Mar 2026 21:49:52 +0200 Subject: [PATCH 09/28] tools/rtla: Remove unneeded nr_cpus from for_each_monitored_cpu nr_cpus does not change at runtime, so passing it through the macro argument is unnecessary. Remove the argument and use the global nr_cpus instead. Signed-off-by: Costa Shulyupin Link: https://lore.kernel.org/r/20260306194953.2511960-5-costa.shul@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/common.h | 2 +- tools/tracing/rtla/src/osnoise_hist.c | 15 +++++++-------- tools/tracing/rtla/src/osnoise_top.c | 2 +- tools/tracing/rtla/src/timerlat.c | 4 ++-- tools/tracing/rtla/src/timerlat_hist.c | 16 ++++++++-------- tools/tracing/rtla/src/timerlat_top.c | 2 +- 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/tools/tracing/rtla/src/common.h b/tools/tracing/rtla/src/common.h index 90a3c0d1dbf3..22ec436a93cc 100644 --- a/tools/tracing/rtla/src/common.h +++ b/tools/tracing/rtla/src/common.h @@ -110,7 +110,7 @@ struct common_params { extern int nr_cpus; -#define for_each_monitored_cpu(cpu, nr_cpus, common) \ +#define for_each_monitored_cpu(cpu, common) \ for (cpu = 0; cpu < nr_cpus; cpu++) \ if (!(common)->cpus || CPU_ISSET(cpu, &(common)->monitored_cpus)) diff --git a/tools/tracing/rtla/src/osnoise_hist.c b/tools/tracing/rtla/src/osnoise_hist.c index 5bbec27b46a7..4181e025511a 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c @@ -244,7 +244,7 @@ static void osnoise_hist_header(struct osnoise_tool *tool) if (!params->common.hist.no_index) trace_seq_printf(s, "Index"); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -273,8 +273,7 @@ osnoise_print_summary(struct osnoise_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "count:"); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { - + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -285,7 +284,7 @@ osnoise_print_summary(struct osnoise_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "min: "); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -298,7 +297,7 @@ osnoise_print_summary(struct osnoise_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "avg: "); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -314,7 +313,7 @@ osnoise_print_summary(struct osnoise_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "max: "); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -349,7 +348,7 @@ osnoise_print_stats(struct osnoise_tool *tool) trace_seq_printf(trace->seq, "%-6d", bucket * data->bucket_size); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -385,7 +384,7 @@ osnoise_print_stats(struct osnoise_tool *tool) if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "over: "); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/osnoise_top.c index 672cdb254f4c..f92caea9f2e9 100644 --- a/tools/tracing/rtla/src/osnoise_top.c +++ b/tools/tracing/rtla/src/osnoise_top.c @@ -236,7 +236,7 @@ osnoise_print_stats(struct osnoise_tool *top) osnoise_top_header(top); - for_each_monitored_cpu(i, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(i, ¶ms->common) { osnoise_top_print(top, i); } diff --git a/tools/tracing/rtla/src/timerlat.c b/tools/tracing/rtla/src/timerlat.c index 31c921efa7c1..8a44537e25cb 100644 --- a/tools/tracing/rtla/src/timerlat.c +++ b/tools/tracing/rtla/src/timerlat.c @@ -115,7 +115,7 @@ int timerlat_enable(struct osnoise_tool *tool) return -1; } - for_each_monitored_cpu(i, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(i, ¶ms->common) { if (save_cpu_idle_disable_state(i) < 0) { err_msg("Could not save cpu idle state.\n"); return -1; @@ -218,7 +218,7 @@ void timerlat_free(struct osnoise_tool *tool) if (dma_latency_fd >= 0) close(dma_latency_fd); if (params->deepest_idle_state >= -1) { - for_each_monitored_cpu(i, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(i, ¶ms->common) { restore_cpu_idle_disable_state(i); } } diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index cfb745b75b8a..ee1af251c94d 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -298,7 +298,7 @@ static void timerlat_hist_header(struct osnoise_tool *tool) if (!params->common.hist.no_index) trace_seq_printf(s, "Index"); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -350,7 +350,7 @@ timerlat_print_summary(struct timerlat_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "count:"); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -372,7 +372,7 @@ timerlat_print_summary(struct timerlat_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "min: "); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -400,7 +400,7 @@ timerlat_print_summary(struct timerlat_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "avg: "); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -428,7 +428,7 @@ timerlat_print_summary(struct timerlat_params *params, if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "max: "); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -473,7 +473,7 @@ timerlat_print_stats_all(struct timerlat_params *params, sum.min_thread = ~0; sum.min_user = ~0; - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -620,7 +620,7 @@ timerlat_print_stats(struct osnoise_tool *tool) trace_seq_printf(trace->seq, "%-6d", bucket * data->bucket_size); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -658,7 +658,7 @@ timerlat_print_stats(struct osnoise_tool *tool) if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "over: "); - for_each_monitored_cpu(cpu, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 8fad4edb0d72..cc296c9d7fe7 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -446,7 +446,7 @@ timerlat_print_stats(struct osnoise_tool *top) timerlat_top_header(params, top); - for_each_monitored_cpu(i, nr_cpus, ¶ms->common) { + for_each_monitored_cpu(i, ¶ms->common) { timerlat_top_print(top, i); timerlat_top_update_sum(top, i, &summary); } From 009a8e681fb003f38dd57a640e11ed826740b5c1 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:14 -0300 Subject: [PATCH 10/28] rtla: Exit on memory allocation failures during initialization Most memory allocations in rtla happen during early initialization before any resources are acquired that would require cleanup. In these cases, propagating allocation errors just adds complexity without any benefit. There's nothing to clean up, and the program must exit anyway. This patch introduces fatal allocation wrappers (calloc_fatal, reallocarray_fatal, strdup_fatal) that call fatal() on allocation failure. These wrappers simplify the code by eliminating unnecessary error propagation paths. The patch converts early allocations to use these wrappers in actions_init() and related action functions, osnoise_context_alloc() and osnoise_init_tool(), trace_instance_init() and trace event functions, and parameter structure allocations in main functions. This simplifies the code while maintaining the same behavior: immediate exit on allocation failure during initialization. Allocations that require cleanup, such as those in histogram allocation functions with goto cleanup paths, are left unchanged and continue to return errors. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-2-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/actions.c | 50 ++++++++++++-------------- tools/tracing/rtla/src/actions.h | 8 ++--- tools/tracing/rtla/src/osnoise.c | 22 ++++-------- tools/tracing/rtla/src/osnoise_hist.c | 22 ++++-------- tools/tracing/rtla/src/osnoise_top.c | 22 ++++-------- tools/tracing/rtla/src/timerlat_hist.c | 22 ++++-------- tools/tracing/rtla/src/timerlat_top.c | 22 ++++-------- tools/tracing/rtla/src/trace.c | 30 ++++------------ tools/tracing/rtla/src/trace.h | 4 +-- tools/tracing/rtla/src/utils.c | 35 ++++++++++++++++++ tools/tracing/rtla/src/utils.h | 3 ++ 11 files changed, 108 insertions(+), 132 deletions(-) diff --git a/tools/tracing/rtla/src/actions.c b/tools/tracing/rtla/src/actions.c index a42615011962..22b8283a183f 100644 --- a/tools/tracing/rtla/src/actions.c +++ b/tools/tracing/rtla/src/actions.c @@ -15,7 +15,7 @@ void actions_init(struct actions *self) { self->size = action_default_size; - self->list = calloc(self->size, sizeof(struct action)); + self->list = calloc_fatal(self->size, sizeof(struct action)); self->len = 0; self->continue_flag = false; @@ -50,8 +50,10 @@ static struct action * actions_new(struct actions *self) { if (self->len >= self->size) { - self->size *= 2; - self->list = realloc(self->list, self->size * sizeof(struct action)); + const size_t new_size = self->size * 2; + + self->list = reallocarray_fatal(self->list, new_size, sizeof(struct action)); + self->size = new_size; } return &self->list[self->len++]; @@ -60,25 +62,21 @@ actions_new(struct actions *self) /* * actions_add_trace_output - add an action to output trace */ -int +void actions_add_trace_output(struct actions *self, const char *trace_output) { struct action *action = actions_new(self); self->present[ACTION_TRACE_OUTPUT] = true; action->type = ACTION_TRACE_OUTPUT; - action->trace_output = calloc(strlen(trace_output) + 1, sizeof(char)); - if (!action->trace_output) - return -1; + action->trace_output = calloc_fatal(strlen(trace_output) + 1, sizeof(char)); strcpy(action->trace_output, trace_output); - - return 0; } /* * actions_add_trace_output - add an action to send signal to a process */ -int +void actions_add_signal(struct actions *self, int signal, int pid) { struct action *action = actions_new(self); @@ -87,40 +85,32 @@ actions_add_signal(struct actions *self, int signal, int pid) action->type = ACTION_SIGNAL; action->signal = signal; action->pid = pid; - - return 0; } /* * actions_add_shell - add an action to execute a shell command */ -int +void actions_add_shell(struct actions *self, const char *command) { struct action *action = actions_new(self); self->present[ACTION_SHELL] = true; action->type = ACTION_SHELL; - action->command = calloc(strlen(command) + 1, sizeof(char)); - if (!action->command) - return -1; + action->command = calloc_fatal(strlen(command) + 1, sizeof(char)); strcpy(action->command, command); - - return 0; } /* * actions_add_continue - add an action to resume measurement */ -int +void actions_add_continue(struct actions *self) { struct action *action = actions_new(self); self->present[ACTION_CONTINUE] = true; action->type = ACTION_CONTINUE; - - return 0; } /* @@ -176,7 +166,8 @@ actions_parse(struct actions *self, const char *trigger, const char *tracefn) /* Only one argument allowed */ return -1; } - return actions_add_trace_output(self, trace_output); + actions_add_trace_output(self, trace_output); + break; case ACTION_SIGNAL: /* Takes two arguments, num (signal) and pid */ while (token != NULL) { @@ -200,21 +191,26 @@ actions_parse(struct actions *self, const char *trigger, const char *tracefn) /* Missing argument */ return -1; - return actions_add_signal(self, signal, pid); + actions_add_signal(self, signal, pid); + break; case ACTION_SHELL: if (token == NULL) return -1; - if (strlen(token) > 8 && strncmp(token, "command=", 8) == 0) - return actions_add_shell(self, token + 8); - return -1; + if (strlen(token) > 8 && strncmp(token, "command=", 8)) + return -1; + actions_add_shell(self, token + 8); + break; case ACTION_CONTINUE: /* Takes no argument */ if (token != NULL) return -1; - return actions_add_continue(self); + actions_add_continue(self); + break; default: return -1; } + + return 0; } /* diff --git a/tools/tracing/rtla/src/actions.h b/tools/tracing/rtla/src/actions.h index fb77069c972b..034048682fef 100644 --- a/tools/tracing/rtla/src/actions.h +++ b/tools/tracing/rtla/src/actions.h @@ -49,9 +49,9 @@ struct actions { void actions_init(struct actions *self); void actions_destroy(struct actions *self); -int actions_add_trace_output(struct actions *self, const char *trace_output); -int actions_add_signal(struct actions *self, int signal, int pid); -int actions_add_shell(struct actions *self, const char *command); -int actions_add_continue(struct actions *self); +void actions_add_trace_output(struct actions *self, const char *trace_output); +void actions_add_signal(struct actions *self, int signal, int pid); +void actions_add_shell(struct actions *self, const char *command); +void actions_add_continue(struct actions *self); int actions_parse(struct actions *self, const char *trigger, const char *tracefn); int actions_perform(struct actions *self); diff --git a/tools/tracing/rtla/src/osnoise.c b/tools/tracing/rtla/src/osnoise.c index 945eb61efc46..ec074cd53dd8 100644 --- a/tools/tracing/rtla/src/osnoise.c +++ b/tools/tracing/rtla/src/osnoise.c @@ -938,9 +938,7 @@ struct osnoise_context *osnoise_context_alloc(void) { struct osnoise_context *context; - context = calloc(1, sizeof(*context)); - if (!context) - return NULL; + context = calloc_fatal(1, sizeof(*context)); context->orig_stop_us = OSNOISE_OPTION_INIT_VAL; context->stop_us = OSNOISE_OPTION_INIT_VAL; @@ -1017,24 +1015,16 @@ void osnoise_destroy_tool(struct osnoise_tool *top) struct osnoise_tool *osnoise_init_tool(char *tool_name) { struct osnoise_tool *top; - int retval; - - top = calloc(1, sizeof(*top)); - if (!top) - return NULL; + top = calloc_fatal(1, sizeof(*top)); top->context = osnoise_context_alloc(); - if (!top->context) - goto out_err; - retval = trace_instance_init(&top->trace, tool_name); - if (retval) - goto out_err; + if (trace_instance_init(&top->trace, tool_name)) { + osnoise_destroy_tool(top); + return NULL; + } return top; -out_err: - osnoise_destroy_tool(top); - return NULL; } /* diff --git a/tools/tracing/rtla/src/osnoise_hist.c b/tools/tracing/rtla/src/osnoise_hist.c index 4181e025511a..8ad816b80265 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c @@ -463,9 +463,7 @@ static struct common_params int c; char *trace_output = NULL; - params = calloc(1, sizeof(*params)); - if (!params) - exit(1); + params = calloc_fatal(1, sizeof(*params)); actions_init(¶ms->common.threshold_actions); actions_init(¶ms->common.end_actions); @@ -575,22 +573,16 @@ static struct common_params params->common.hist.with_zeros = 1; break; case '4': /* trigger */ - if (params->common.events) { - retval = trace_event_add_trigger(params->common.events, optarg); - if (retval) - fatal("Error adding trigger %s", optarg); - } else { + if (params->common.events) + trace_event_add_trigger(params->common.events, optarg); + else fatal("--trigger requires a previous -e"); - } break; case '5': /* filter */ - if (params->common.events) { - retval = trace_event_add_filter(params->common.events, optarg); - if (retval) - fatal("Error adding filter %s", optarg); - } else { + if (params->common.events) + trace_event_add_filter(params->common.events, optarg); + else fatal("--filter requires a previous -e"); - } break; case '6': params->common.warmup = get_llong_from_str(optarg); diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/osnoise_top.c index f92caea9f2e9..244bdce022ad 100644 --- a/tools/tracing/rtla/src/osnoise_top.c +++ b/tools/tracing/rtla/src/osnoise_top.c @@ -312,9 +312,7 @@ struct common_params *osnoise_top_parse_args(int argc, char **argv) int c; char *trace_output = NULL; - params = calloc(1, sizeof(*params)); - if (!params) - exit(1); + params = calloc_fatal(1, sizeof(*params)); actions_init(¶ms->common.threshold_actions); actions_init(¶ms->common.end_actions); @@ -402,22 +400,16 @@ struct common_params *osnoise_top_parse_args(int argc, char **argv) params->threshold = get_llong_from_str(optarg); break; case '0': /* trigger */ - if (params->common.events) { - retval = trace_event_add_trigger(params->common.events, optarg); - if (retval) - fatal("Error adding trigger %s", optarg); - } else { + if (params->common.events) + trace_event_add_trigger(params->common.events, optarg); + else fatal("--trigger requires a previous -e"); - } break; case '1': /* filter */ - if (params->common.events) { - retval = trace_event_add_filter(params->common.events, optarg); - if (retval) - fatal("Error adding filter %s", optarg); - } else { + if (params->common.events) + trace_event_add_filter(params->common.events, optarg); + else fatal("--filter requires a previous -e"); - } break; case '2': params->common.warmup = get_llong_from_str(optarg); diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index ee1af251c94d..6524be4e2bf7 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -760,9 +760,7 @@ static struct common_params int c; char *trace_output = NULL; - params = calloc(1, sizeof(*params)); - if (!params) - exit(1); + params = calloc_fatal(1, sizeof(*params)); actions_init(¶ms->common.threshold_actions); actions_init(¶ms->common.end_actions); @@ -911,22 +909,16 @@ static struct common_params params->common.hist.with_zeros = 1; break; case '6': /* trigger */ - if (params->common.events) { - retval = trace_event_add_trigger(params->common.events, optarg); - if (retval) - fatal("Error adding trigger %s", optarg); - } else { + if (params->common.events) + trace_event_add_trigger(params->common.events, optarg); + else fatal("--trigger requires a previous -e"); - } break; case '7': /* filter */ - if (params->common.events) { - retval = trace_event_add_filter(params->common.events, optarg); - if (retval) - fatal("Error adding filter %s", optarg); - } else { + if (params->common.events) + trace_event_add_filter(params->common.events, optarg); + else fatal("--filter requires a previous -e"); - } break; case '8': params->dma_latency = get_llong_from_str(optarg); diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index cc296c9d7fe7..8dd04c5ffb68 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -526,9 +526,7 @@ static struct common_params int c; char *trace_output = NULL; - params = calloc(1, sizeof(*params)); - if (!params) - exit(1); + params = calloc_fatal(1, sizeof(*params)); actions_init(¶ms->common.threshold_actions); actions_init(¶ms->common.end_actions); @@ -656,22 +654,16 @@ static struct common_params params->common.user_data = true; break; case '0': /* trigger */ - if (params->common.events) { - retval = trace_event_add_trigger(params->common.events, optarg); - if (retval) - fatal("Error adding trigger %s", optarg); - } else { + if (params->common.events) + trace_event_add_trigger(params->common.events, optarg); + else fatal("--trigger requires a previous -e"); - } break; case '1': /* filter */ - if (params->common.events) { - retval = trace_event_add_filter(params->common.events, optarg); - if (retval) - fatal("Error adding filter %s", optarg); - } else { + if (params->common.events) + trace_event_add_filter(params->common.events, optarg); + else fatal("--filter requires a previous -e"); - } break; case '2': /* dma-latency */ params->dma_latency = get_llong_from_str(optarg); diff --git a/tools/tracing/rtla/src/trace.c b/tools/tracing/rtla/src/trace.c index b8be3e28680e..211ca54b15b0 100644 --- a/tools/tracing/rtla/src/trace.c +++ b/tools/tracing/rtla/src/trace.c @@ -191,9 +191,7 @@ void trace_instance_destroy(struct trace_instance *trace) */ int trace_instance_init(struct trace_instance *trace, char *tool_name) { - trace->seq = calloc(1, sizeof(*trace->seq)); - if (!trace->seq) - goto out_err; + trace->seq = calloc_fatal(1, sizeof(*trace->seq)); trace_seq_init(trace->seq); @@ -274,15 +272,9 @@ struct trace_events *trace_event_alloc(const char *event_string) { struct trace_events *tevent; - tevent = calloc(1, sizeof(*tevent)); - if (!tevent) - return NULL; + tevent = calloc_fatal(1, sizeof(*tevent)); - tevent->system = strdup(event_string); - if (!tevent->system) { - free(tevent); - return NULL; - } + tevent->system = strdup_fatal(event_string); tevent->event = strstr(tevent->system, ":"); if (tevent->event) { @@ -296,31 +288,23 @@ struct trace_events *trace_event_alloc(const char *event_string) /* * trace_event_add_filter - record an event filter */ -int trace_event_add_filter(struct trace_events *event, char *filter) +void trace_event_add_filter(struct trace_events *event, char *filter) { if (event->filter) free(event->filter); - event->filter = strdup(filter); - if (!event->filter) - return 1; - - return 0; + event->filter = strdup_fatal(filter); } /* * trace_event_add_trigger - record an event trigger action */ -int trace_event_add_trigger(struct trace_events *event, char *trigger) +void trace_event_add_trigger(struct trace_events *event, char *trigger) { if (event->trigger) free(event->trigger); - event->trigger = strdup(trigger); - if (!event->trigger) - return 1; - - return 0; + event->trigger = strdup_fatal(trigger); } /* diff --git a/tools/tracing/rtla/src/trace.h b/tools/tracing/rtla/src/trace.h index 1e5aee4b828d..95b911a2228b 100644 --- a/tools/tracing/rtla/src/trace.h +++ b/tools/tracing/rtla/src/trace.h @@ -45,6 +45,6 @@ void trace_events_destroy(struct trace_instance *instance, int trace_events_enable(struct trace_instance *instance, struct trace_events *events); -int trace_event_add_filter(struct trace_events *event, char *filter); -int trace_event_add_trigger(struct trace_events *event, char *trigger); +void trace_event_add_filter(struct trace_events *event, char *filter); +void trace_event_add_trigger(struct trace_events *event, char *trigger); int trace_set_buffer_size(struct trace_instance *trace, int size); diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index 77593718766a..7da1913c5c02 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -1040,3 +1040,38 @@ int strtoi(const char *s, int *res) *res = (int) lres; return 0; } + +static inline void fatal_alloc(void) +{ + fatal("Error allocating memory\n"); +} + +void *calloc_fatal(size_t n, size_t size) +{ + void *p = calloc(n, size); + + if (!p) + fatal_alloc(); + + return p; +} + +void *reallocarray_fatal(void *p, size_t n, size_t size) +{ + p = reallocarray(p, n, size); + + if (!p) + fatal_alloc(); + + return p; +} + +char *strdup_fatal(const char *s) +{ + char *p = strdup(s); + + if (!p) + fatal_alloc(); + + return p; +} diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h index 80d5ec0cf934..163b4ec37d35 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h @@ -76,6 +76,9 @@ int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr); int set_comm_cgroup(const char *comm_prefix, const char *cgroup); int set_pid_cgroup(pid_t pid, const char *cgroup); int set_cpu_dma_latency(int32_t latency); +void *calloc_fatal(size_t n, size_t size); +void *reallocarray_fatal(void *p, size_t n, size_t size); +char *strdup_fatal(const char *s); #ifdef HAVE_LIBCPUPOWER_SUPPORT int save_cpu_idle_disable_state(unsigned int cpu); int restore_cpu_idle_disable_state(unsigned int cpu); From b8f7f49a23897fc772373da513a1cb6d51e5958e Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:15 -0300 Subject: [PATCH 11/28] rtla: Use strdup() to simplify code The actions_add_trace_output() and actions_add_shell() functions were using calloc() followed by strcpy() to allocate and copy a string. This can be simplified by using strdup(), which allocates memory and copies the string in a single step. Replace the calloc() and strcpy() calls with strdup(), making the code more concise and readable. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-3-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/actions.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/tracing/rtla/src/actions.c b/tools/tracing/rtla/src/actions.c index 22b8283a183f..0ac42ffd734a 100644 --- a/tools/tracing/rtla/src/actions.c +++ b/tools/tracing/rtla/src/actions.c @@ -69,8 +69,7 @@ actions_add_trace_output(struct actions *self, const char *trace_output) self->present[ACTION_TRACE_OUTPUT] = true; action->type = ACTION_TRACE_OUTPUT; - action->trace_output = calloc_fatal(strlen(trace_output) + 1, sizeof(char)); - strcpy(action->trace_output, trace_output); + action->trace_output = strdup_fatal(trace_output); } /* @@ -97,8 +96,7 @@ actions_add_shell(struct actions *self, const char *command) self->present[ACTION_SHELL] = true; action->type = ACTION_SHELL; - action->command = calloc_fatal(strlen(command) + 1, sizeof(char)); - strcpy(action->command, command); + action->command = strdup_fatal(command); } /* From 989e5b8f0c5302cfb233fa2db49f8877b5f1352d Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:16 -0300 Subject: [PATCH 12/28] rtla/actions: Simplify argument parsing The actions_parse() function uses open-coded logic to extract arguments from a string. This includes manual length checks and strncmp() calls, which can be verbose and error-prone. To simplify and improve the robustness of argument parsing, introduce a new extract_arg() helper macro. This macro extracts the value from a "key=value" pair, making the code more concise and readable. Also, introduce STRING_LENGTH() and strncmp_static() macros to perform compile-time calculations of string lengths and safer string comparisons. Refactor actions_parse() to use these new helpers, resulting in cleaner and more maintainable code. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-4-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/actions.c | 57 +++++++++++++++++++++++--------- tools/tracing/rtla/src/utils.h | 10 ++++++ 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/tools/tracing/rtla/src/actions.c b/tools/tracing/rtla/src/actions.c index 0ac42ffd734a..b0d68b5de08d 100644 --- a/tools/tracing/rtla/src/actions.c +++ b/tools/tracing/rtla/src/actions.c @@ -111,6 +111,29 @@ actions_add_continue(struct actions *self) action->type = ACTION_CONTINUE; } +static inline const char *__extract_arg(const char *token, const char *opt, size_t opt_len) +{ + const size_t tok_len = strlen(token); + + if (tok_len <= opt_len) + return NULL; + + if (strncmp(token, opt, opt_len)) + return NULL; + + return token + opt_len; +} + +/* + * extract_arg - extract argument value from option token + * @token: option token (e.g., "file=trace.txt") + * @opt: option name to match (e.g., "file") + * + * Returns pointer to argument value after "=" if token matches "opt=", + * otherwise returns NULL. + */ +#define extract_arg(token, opt) __extract_arg(token, opt "=", STRING_LENGTH(opt "=")) + /* * actions_parse - add an action based on text specification */ @@ -120,6 +143,7 @@ actions_parse(struct actions *self, const char *trigger, const char *tracefn) enum action_type type = ACTION_NONE; const char *token; char trigger_c[strlen(trigger) + 1]; + const char *arg_value; /* For ACTION_SIGNAL */ int signal = 0, pid = 0; @@ -152,12 +176,10 @@ actions_parse(struct actions *self, const char *trigger, const char *tracefn) if (token == NULL) trace_output = tracefn; else { - if (strlen(token) > 5 && strncmp(token, "file=", 5) == 0) { - trace_output = token + 5; - } else { + trace_output = extract_arg(token, "file"); + if (!trace_output) /* Invalid argument */ return -1; - } token = strtok(NULL, ","); if (token != NULL) @@ -169,17 +191,21 @@ actions_parse(struct actions *self, const char *trigger, const char *tracefn) case ACTION_SIGNAL: /* Takes two arguments, num (signal) and pid */ while (token != NULL) { - if (strlen(token) > 4 && strncmp(token, "num=", 4) == 0) { - if (strtoi(token + 4, &signal)) - return -1; - } else if (strlen(token) > 4 && strncmp(token, "pid=", 4) == 0) { - if (strncmp(token + 4, "parent", 7) == 0) - pid = -1; - else if (strtoi(token + 4, &pid)) + arg_value = extract_arg(token, "num"); + if (arg_value) { + if (strtoi(arg_value, &signal)) return -1; } else { - /* Invalid argument */ - return -1; + arg_value = extract_arg(token, "pid"); + if (arg_value) { + if (strncmp_static(arg_value, "parent") == 0) + pid = -1; + else if (strtoi(arg_value, &pid)) + return -1; + } else { + /* Invalid argument */ + return -1; + } } token = strtok(NULL, ","); @@ -194,9 +220,10 @@ actions_parse(struct actions *self, const char *trigger, const char *tracefn) case ACTION_SHELL: if (token == NULL) return -1; - if (strlen(token) > 8 && strncmp(token, "command=", 8)) + arg_value = extract_arg(token, "command"); + if (!arg_value) return -1; - actions_add_shell(self, token + 8); + actions_add_shell(self, arg_value); break; case ACTION_CONTINUE: /* Takes no argument */ diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h index 163b4ec37d35..b15a1a154076 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h @@ -14,6 +14,16 @@ #define MAX_NICE 20 #define MIN_NICE -19 +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) +#endif + +/* Calculate string length at compile time (excluding null terminator) */ +#define STRING_LENGTH(s) (ARRAY_SIZE(s) - sizeof(*(s))) + +/* Compare string with static string, length determined at compile time */ +#define strncmp_static(s1, s2) strncmp(s1, s2, ARRAY_SIZE(s2)) + #define container_of(ptr, type, member)({ \ const typeof(((type *)0)->member) *__mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)) ; }) From a50c53884db83189947f213d4c7f8385c522d141 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:17 -0300 Subject: [PATCH 13/28] rtla: Introduce common_threshold_handler() helper Several functions duplicate the logic for handling threshold actions. When a threshold is reached, these functions stop the trace, perform configured actions, and restart the trace if --on-threshold continue is set. Create common_threshold_handler() to centralize this shared logic and avoid code duplication. The function executes the configured threshold actions and restarts the necessary trace instances when appropriate. Also add should_continue_tracing() helper to encapsulate the check for whether tracing should continue after a threshold event, improving code readability at call sites. In timerlat_top_bpf_main_loop(), use common_params directly instead of casting through timerlat_params when only common fields are needed. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-5-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/common.c | 61 ++++++++++++++++++-------- tools/tracing/rtla/src/common.h | 18 ++++++++ tools/tracing/rtla/src/timerlat_hist.c | 19 ++++---- tools/tracing/rtla/src/timerlat_top.c | 32 +++++++------- 4 files changed, 86 insertions(+), 44 deletions(-) diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c index a31fbaea5da6..a00b5e553545 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -207,6 +207,38 @@ out_err: } +/** + * common_threshold_handler - handle latency threshold overflow + * @tool: pointer to the osnoise_tool instance containing trace contexts + * + * Executes the configured threshold actions (e.g., saving trace, printing, + * sending signals). If the continue flag is set (--on-threshold continue), + * restarts the auxiliary trace instances to continue monitoring. + * + * Return: 0 for success, -1 for error. + */ +int +common_threshold_handler(const struct osnoise_tool *tool) +{ + actions_perform(&tool->params->threshold_actions); + + if (!should_continue_tracing(tool->params)) + /* continue flag not set, break */ + return 0; + + /* continue action reached, re-enable tracing */ + if (tool->record && trace_instance_start(&tool->record->trace)) + goto err; + if (tool->aa && trace_instance_start(&tool->aa->trace)) + goto err; + + return 0; + +err: + err_msg("Error restarting trace\n"); + return -1; +} + int run_tool(struct tool_ops *ops, int argc, char *argv[]) { struct common_params *params; @@ -385,17 +417,14 @@ int top_main_loop(struct osnoise_tool *tool) /* stop tracing requested, do not perform actions */ return 0; - actions_perform(¶ms->threshold_actions); + retval = common_threshold_handler(tool); + if (retval) + return retval; - if (!params->threshold_actions.continue_flag) - /* continue flag not set, break */ + + if (!should_continue_tracing(params)) return 0; - /* continue action reached, re-enable tracing */ - if (record) - trace_instance_start(&record->trace); - if (tool->aa) - trace_instance_start(&tool->aa->trace); trace_instance_start(trace); } @@ -436,18 +465,14 @@ int hist_main_loop(struct osnoise_tool *tool) /* stop tracing requested, do not perform actions */ break; - actions_perform(¶ms->threshold_actions); + retval = common_threshold_handler(tool); + if (retval) + return retval; - if (!params->threshold_actions.continue_flag) - /* continue flag not set, break */ - break; + if (!should_continue_tracing(params)) + return 0; - /* continue action reached, re-enable tracing */ - if (tool->record) - trace_instance_start(&tool->record->trace); - if (tool->aa) - trace_instance_start(&tool->aa->trace); - trace_instance_start(&tool->trace); + trace_instance_start(trace); } /* is there still any user-threads ? */ diff --git a/tools/tracing/rtla/src/common.h b/tools/tracing/rtla/src/common.h index 22ec436a93cc..51665db4ffce 100644 --- a/tools/tracing/rtla/src/common.h +++ b/tools/tracing/rtla/src/common.h @@ -146,6 +146,24 @@ struct tool_ops { void (*free)(struct osnoise_tool *tool); }; +/** + * should_continue_tracing - check if tracing should continue after threshold + * @params: pointer to the common parameters structure + * + * Returns true if the continue action was configured (--on-threshold continue), + * indicating that tracing should be restarted after handling the threshold event. + * + * Return: 1 if tracing should continue, 0 otherwise. + */ +static inline int +should_continue_tracing(const struct common_params *params) +{ + return params->threshold_actions.continue_flag; +} + +int +common_threshold_handler(const struct osnoise_tool *tool); + int osnoise_set_cpus(struct osnoise_context *context, char *cpus); void osnoise_restore_cpus(struct osnoise_context *context); diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 6524be4e2bf7..79142af4f566 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -17,6 +17,7 @@ #include "timerlat.h" #include "timerlat_aa.h" #include "timerlat_bpf.h" +#include "common.h" struct timerlat_hist_cpu { int *irq; @@ -1047,7 +1048,6 @@ out_err: static int timerlat_hist_bpf_main_loop(struct osnoise_tool *tool) { - struct timerlat_params *params = to_timerlat_params(tool->params); int retval; while (!stop_tracing) { @@ -1055,18 +1055,17 @@ static int timerlat_hist_bpf_main_loop(struct osnoise_tool *tool) if (!stop_tracing) { /* Threshold overflow, perform actions on threshold */ - actions_perform(¶ms->common.threshold_actions); + retval = common_threshold_handler(tool); + if (retval) + return retval; - if (!params->common.threshold_actions.continue_flag) - /* continue flag not set, break */ + if (!should_continue_tracing(tool->params)) break; - /* continue action reached, re-enable tracing */ - if (tool->record) - trace_instance_start(&tool->record->trace); - if (tool->aa) - trace_instance_start(&tool->aa->trace); - timerlat_bpf_restart_tracing(); + if (timerlat_bpf_restart_tracing()) { + err_msg("Error restarting BPF trace\n"); + return -1; + } } } timerlat_bpf_detach(); diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 8dd04c5ffb68..64cbdcc878b0 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -17,6 +17,7 @@ #include "timerlat.h" #include "timerlat_aa.h" #include "timerlat_bpf.h" +#include "common.h" struct timerlat_top_cpu { unsigned long long irq_count; @@ -795,10 +796,10 @@ out_err: static int timerlat_top_bpf_main_loop(struct osnoise_tool *tool) { - struct timerlat_params *params = to_timerlat_params(tool->params); + const struct common_params *params = tool->params; int retval, wait_retval; - if (params->common.aa_only) { + if (params->aa_only) { /* Auto-analysis only, just wait for stop tracing */ timerlat_bpf_wait(-1); return 0; @@ -806,8 +807,8 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *tool) /* Pull and display data in a loop */ while (!stop_tracing) { - wait_retval = timerlat_bpf_wait(params->common.quiet ? -1 : - params->common.sleep_time); + wait_retval = timerlat_bpf_wait(params->quiet ? -1 : + params->sleep_time); retval = timerlat_top_bpf_pull_data(tool); if (retval) { @@ -815,28 +816,27 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *tool) return retval; } - if (!params->common.quiet) + if (!params->quiet) timerlat_print_stats(tool); if (wait_retval != 0) { /* Stopping requested by tracer */ - actions_perform(¶ms->common.threshold_actions); + retval = common_threshold_handler(tool); + if (retval) + return retval; - if (!params->common.threshold_actions.continue_flag) - /* continue flag not set, break */ + if (!should_continue_tracing(tool->params)) break; - /* continue action reached, re-enable tracing */ - if (tool->record) - trace_instance_start(&tool->record->trace); - if (tool->aa) - trace_instance_start(&tool->aa->trace); - timerlat_bpf_restart_tracing(); + if (timerlat_bpf_restart_tracing()) { + err_msg("Error restarting BPF trace\n"); + return -1; + } } /* is there still any user-threads ? */ - if (params->common.user_workload) { - if (params->common.user.stopped_running) { + if (params->user_workload) { + if (params->user.stopped_running) { debug_msg("timerlat user space threads stopped!\n"); break; } From a29430c2bc86b00e62f74299b866216390c7e418 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:18 -0300 Subject: [PATCH 14/28] rtla: Replace magic number with MAX_PATH The trace functions use a buffer to manipulate strings that will be written to tracefs files. These buffers are defined with a magic number of 1024, which is a common source of vulnerabilities. Replace the magic number 1024 with the MAX_PATH macro to make the code safer and more readable. While at it, replace other instances of the magic number with ARRAY_SIZE() when the buffer is locally defined. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-6-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/osnoise.c | 2 +- tools/tracing/rtla/src/timerlat_u.c | 4 ++-- tools/tracing/rtla/src/trace.c | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tools/tracing/rtla/src/osnoise.c b/tools/tracing/rtla/src/osnoise.c index ec074cd53dd8..9d1fd9981fe9 100644 --- a/tools/tracing/rtla/src/osnoise.c +++ b/tools/tracing/rtla/src/osnoise.c @@ -62,7 +62,7 @@ int osnoise_set_cpus(struct osnoise_context *context, char *cpus) if (!context->curr_cpus) return -1; - snprintf(buffer, 1024, "%s\n", cpus); + snprintf(buffer, ARRAY_SIZE(buffer), "%s\n", cpus); debug_msg("setting cpus to %s from %s", cpus, context->orig_cpus); diff --git a/tools/tracing/rtla/src/timerlat_u.c b/tools/tracing/rtla/src/timerlat_u.c index 03b4e68e8b1e..c80edaf07b00 100644 --- a/tools/tracing/rtla/src/timerlat_u.c +++ b/tools/tracing/rtla/src/timerlat_u.c @@ -32,7 +32,7 @@ static int timerlat_u_main(int cpu, struct timerlat_u_params *params) { struct sched_param sp = { .sched_priority = 95 }; - char buffer[1024]; + char buffer[MAX_PATH]; int timerlat_fd; cpu_set_t set; int retval; @@ -83,7 +83,7 @@ static int timerlat_u_main(int cpu, struct timerlat_u_params *params) /* add should continue with a signal handler */ while (true) { - retval = read(timerlat_fd, buffer, 1024); + retval = read(timerlat_fd, buffer, ARRAY_SIZE(buffer)); if (retval < 0) break; } diff --git a/tools/tracing/rtla/src/trace.c b/tools/tracing/rtla/src/trace.c index 211ca54b15b0..e1af54f9531b 100644 --- a/tools/tracing/rtla/src/trace.c +++ b/tools/tracing/rtla/src/trace.c @@ -313,7 +313,7 @@ void trace_event_add_trigger(struct trace_events *event, char *trigger) static void trace_event_disable_filter(struct trace_instance *instance, struct trace_events *tevent) { - char filter[1024]; + char filter[MAX_PATH]; int retval; if (!tevent->filter) @@ -325,7 +325,7 @@ static void trace_event_disable_filter(struct trace_instance *instance, debug_msg("Disabling %s:%s filter %s\n", tevent->system, tevent->event ? : "*", tevent->filter); - snprintf(filter, 1024, "!%s\n", tevent->filter); + snprintf(filter, ARRAY_SIZE(filter), "!%s\n", tevent->filter); retval = tracefs_event_file_write(instance->inst, tevent->system, tevent->event, "filter", filter); @@ -344,7 +344,7 @@ static void trace_event_save_hist(struct trace_instance *instance, { int retval, index, out_fd; mode_t mode = 0644; - char path[1024]; + char path[MAX_PATH]; char *hist; if (!tevent) @@ -359,7 +359,7 @@ static void trace_event_save_hist(struct trace_instance *instance, if (retval) return; - snprintf(path, 1024, "%s_%s_hist.txt", tevent->system, tevent->event); + snprintf(path, ARRAY_SIZE(path), "%s_%s_hist.txt", tevent->system, tevent->event); printf(" Saving event %s:%s hist to %s\n", tevent->system, tevent->event, path); @@ -391,7 +391,7 @@ out_close: static void trace_event_disable_trigger(struct trace_instance *instance, struct trace_events *tevent) { - char trigger[1024]; + char trigger[MAX_PATH]; int retval; if (!tevent->trigger) @@ -405,7 +405,7 @@ static void trace_event_disable_trigger(struct trace_instance *instance, trace_event_save_hist(instance, tevent); - snprintf(trigger, 1024, "!%s\n", tevent->trigger); + snprintf(trigger, ARRAY_SIZE(trigger), "!%s\n", tevent->trigger); retval = tracefs_event_file_write(instance->inst, tevent->system, tevent->event, "trigger", trigger); @@ -444,7 +444,7 @@ void trace_events_disable(struct trace_instance *instance, static int trace_event_enable_filter(struct trace_instance *instance, struct trace_events *tevent) { - char filter[1024]; + char filter[MAX_PATH]; int retval; if (!tevent->filter) @@ -456,7 +456,7 @@ static int trace_event_enable_filter(struct trace_instance *instance, return 1; } - snprintf(filter, 1024, "%s\n", tevent->filter); + snprintf(filter, ARRAY_SIZE(filter), "%s\n", tevent->filter); debug_msg("Enabling %s:%s filter %s\n", tevent->system, tevent->event ? : "*", tevent->filter); @@ -479,7 +479,7 @@ static int trace_event_enable_filter(struct trace_instance *instance, static int trace_event_enable_trigger(struct trace_instance *instance, struct trace_events *tevent) { - char trigger[1024]; + char trigger[MAX_PATH]; int retval; if (!tevent->trigger) @@ -491,7 +491,7 @@ static int trace_event_enable_trigger(struct trace_instance *instance, return 1; } - snprintf(trigger, 1024, "%s\n", tevent->trigger); + snprintf(trigger, ARRAY_SIZE(trigger), "%s\n", tevent->trigger); debug_msg("Enabling %s:%s trigger %s\n", tevent->system, tevent->event ? : "*", tevent->trigger); From f79720e25b793691dcc46e1f1cd64d01578075c2 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:19 -0300 Subject: [PATCH 15/28] rtla: Simplify code by caching string lengths Simplify trace_event_save_hist() and set_comm_cgroup() by computing string lengths once and storing them in local variables, rather than calling strlen() multiple times on the same unchanged strings. This makes the code clearer by eliminating redundant function calls and improving readability. In trace_event_save_hist(), the write loop previously called strlen() on the hist buffer twice per iteration for both the size calculation and loop condition. Store the length in hist_len before entering the loop. In set_comm_cgroup(), strlen() was called on cgroup_path up to three times in succession. Store the result in cg_path_len to use in both the offset calculation and size parameter for subsequent append operations. This simplification makes the code easier to read and maintain without changing program behavior. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-7-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/trace.c | 6 ++++-- tools/tracing/rtla/src/utils.c | 11 +++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tools/tracing/rtla/src/trace.c b/tools/tracing/rtla/src/trace.c index e1af54f9531b..2f529aaf8dee 100644 --- a/tools/tracing/rtla/src/trace.c +++ b/tools/tracing/rtla/src/trace.c @@ -346,6 +346,7 @@ static void trace_event_save_hist(struct trace_instance *instance, mode_t mode = 0644; char path[MAX_PATH]; char *hist; + size_t hist_len; if (!tevent) return; @@ -376,9 +377,10 @@ static void trace_event_save_hist(struct trace_instance *instance, } index = 0; + hist_len = strlen(hist); do { - index += write(out_fd, &hist[index], strlen(hist) - index); - } while (index < strlen(hist)); + index += write(out_fd, &hist[index], hist_len - index); + } while (index < hist_len); free(hist); out_close: diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index 7da1913c5c02..ce8b1aa012ec 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -819,6 +819,7 @@ static int open_cgroup_procs(const char *cgroup) char cgroup_procs[MAX_PATH]; int retval; int cg_fd; + size_t cg_path_len; retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path)); if (!retval) { @@ -826,16 +827,18 @@ static int open_cgroup_procs(const char *cgroup) return -1; } + cg_path_len = strlen(cgroup_path); + if (!cgroup) { - retval = get_self_cgroup(&cgroup_path[strlen(cgroup_path)], - sizeof(cgroup_path) - strlen(cgroup_path)); + retval = get_self_cgroup(&cgroup_path[cg_path_len], + sizeof(cgroup_path) - cg_path_len); if (!retval) { err_msg("Did not find self cgroup\n"); return -1; } } else { - snprintf(&cgroup_path[strlen(cgroup_path)], - sizeof(cgroup_path) - strlen(cgroup_path), "%s/", cgroup); + snprintf(&cgroup_path[cg_path_len], + sizeof(cgroup_path) - cg_path_len, "%s/", cgroup); } snprintf(cgroup_procs, MAX_PATH, "%s/cgroup.procs", cgroup_path); From d6515424e80583928ec1c55e9dd4e906bc90d9be Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:21 -0300 Subject: [PATCH 16/28] rtla/timerlat: Add bounds check for softirq vector Add bounds checking when accessing the softirq_name array using the vector value from kernel trace data. The vector field from the osnoise:softirq_noise event is used directly as an array index without validation, which could cause an out-of-bounds read if the kernel provides an unexpected vector value. The softirq_name array contains 10 elements corresponding to the standard Linux softirq vectors. While the kernel should only provide valid vector values in the range 0-9, defensive programming requires validating untrusted input before using it as an array index. If an out-of-range vector is encountered, display the word UNKNOWN instead of attempting to read beyond the array bounds. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-9-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/timerlat_aa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/tracing/rtla/src/timerlat_aa.c b/tools/tracing/rtla/src/timerlat_aa.c index 41d8d48c5b41..a121ff9d2c57 100644 --- a/tools/tracing/rtla/src/timerlat_aa.c +++ b/tools/tracing/rtla/src/timerlat_aa.c @@ -417,8 +417,8 @@ static int timerlat_aa_softirq_handler(struct trace_seq *s, struct tep_record *r taa_data->thread_softirq_sum += duration; trace_seq_printf(taa_data->softirqs_seq, " %24s:%-3llu %.*s %9.2f us\n", - softirq_name[vector], vector, - 24, spaces, + vector < ARRAY_SIZE(softirq_name) ? softirq_name[vector] : "UNKNOWN", + vector, 24, spaces, ns_to_usf(duration)); return 0; } From d847188bb92b14518a04d7542e44928a22060847 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:22 -0300 Subject: [PATCH 17/28] rtla: Handle pthread_create() failure properly Add proper error handling when pthread_create() fails to create the timerlat user-space dispatcher thread. Previously, the code only logged an error message but continued execution, which could lead to undefined behavior when the tool later expects the thread to be running. When pthread_create() returns an error, the function now jumps to the out_trace error path to properly clean up resources and exit. This ensures consistent error handling and prevents the tool from running in an invalid state without the required user-space thread. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-10-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/common.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c index a00b5e553545..50d8aafdfd3b 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -336,8 +336,10 @@ int run_tool(struct tool_ops *ops, int argc, char *argv[]) params->user.cgroup_name = params->cgroup_name; retval = pthread_create(&user_thread, NULL, timerlat_u_dispatcher, ¶ms->user); - if (retval) + if (retval) { err_msg("Error creating timerlat user-space threads\n"); + goto out_trace; + } } retval = ops->enable(tool); From 0f4bc9d67a643a6ea8b82faf724e44648b2c322d Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:23 -0300 Subject: [PATCH 18/28] rtla: Add str_has_prefix() helper function Add a str_has_prefix() helper function that tests whether a string starts with a given prefix. This function provides a cleaner interface for prefix matching compared to using strncmp() with strlen() directly. The function returns a boolean value indicating whether the string starts with the specified prefix. This helper will be used in subsequent changes to simplify prefix matching code throughout rtla. Also add the missing string.h include which is needed for the strlen() and strncmp() functions used by str_has_prefix() and the existing strncmp_static() macro. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-11-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/utils.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h index b15a1a154076..e794ede64b2c 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include #include #include @@ -24,6 +25,18 @@ /* Compare string with static string, length determined at compile time */ #define strncmp_static(s1, s2) strncmp(s1, s2, ARRAY_SIZE(s2)) +/** + * str_has_prefix - Test if a string has a given prefix + * @str: The string to test + * @prefix: The string to see if @str starts with + * + * Returns: true if @str starts with @prefix, false otherwise + */ +static inline bool str_has_prefix(const char *str, const char *prefix) +{ + return strncmp(str, prefix, strlen(prefix)) == 0; +} + #define container_of(ptr, type, member)({ \ const typeof(((type *)0)->member) *__mptr = (ptr); \ (type *)((char *)__mptr - offsetof(type, member)) ; }) From 265905df83a4c1e78c1a912e1699d7c81d9540e6 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:24 -0300 Subject: [PATCH 19/28] rtla: Use str_has_prefix() for prefix checks The code currently uses strncmp() combined with strlen() to check if a string starts with a specific prefix. This pattern is verbose and prone to errors if the length does not match the prefix string. Replace this pattern with the str_has_prefix() helper function in both trace.c and utils.c. This improves code readability and safety by handling the prefix length calculation automatically. In addition, remove the unused retval variable from trace_event_save_hist() in trace.c to clean up the function and silence potential compiler warnings. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-12-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/trace.c | 5 ++--- tools/tracing/rtla/src/utils.c | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tools/tracing/rtla/src/trace.c b/tools/tracing/rtla/src/trace.c index 2f529aaf8dee..ed7db5f4115c 100644 --- a/tools/tracing/rtla/src/trace.c +++ b/tools/tracing/rtla/src/trace.c @@ -342,7 +342,7 @@ static void trace_event_disable_filter(struct trace_instance *instance, static void trace_event_save_hist(struct trace_instance *instance, struct trace_events *tevent) { - int retval, index, out_fd; + int index, out_fd; mode_t mode = 0644; char path[MAX_PATH]; char *hist; @@ -356,8 +356,7 @@ static void trace_event_save_hist(struct trace_instance *instance, return; /* is this a hist: trigger? */ - retval = strncmp(tevent->trigger, "hist:", strlen("hist:")); - if (retval) + if (!str_has_prefix(tevent->trigger, "hist:")) return; snprintf(path, ARRAY_SIZE(path), "%s_%s_hist.txt", tevent->system, tevent->event); diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index ce8b1aa012ec..89ccacb97ef7 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -331,8 +331,7 @@ static int procfs_is_workload_pid(const char *comm_prefix, struct dirent *proc_e return 0; buffer[MAX_PATH-1] = '\0'; - retval = strncmp(comm_prefix, buffer, strlen(comm_prefix)); - if (retval) + if (!str_has_prefix(buffer, comm_prefix)) return 0; /* comm already have \n */ From b3910a739235f89f616345dda1f8303d9ccb99fb Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:25 -0300 Subject: [PATCH 20/28] rtla: Enforce exact match for time unit suffixes The parse_ns_duration() function currently uses prefix matching for detecting time units. This approach is problematic as it silently accepts malformed strings such as "100nsx" or "100us_invalid" by ignoring the trailing characters, leading to potential configuration errors. Introduce a match_time_unit() helper that checks the suffix matches exactly and is followed by either end-of-string or a ':' delimiter. The ':' is needed because parse_ns_duration() is also called from get_long_ns_after_colon() when parsing SCHED_DEADLINE priority specifications in the format "d:runtime:period" (e.g., "d:10ms:100ms"). A plain strcmp() would reject valid deadline strings because the suffix "ms" is followed by ":100ms", not end-of-string. Similarly, strncmp_static() would fail because ARRAY_SIZE() includes the NUL terminator, making it equivalent to strcmp() for this comparison. The match_time_unit() helper solves both problems: it rejects malformed input like "100msx" while correctly handling the colon-delimited deadline format. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-13-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/utils.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index 89ccacb97ef7..c28ca48a5af0 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -213,6 +213,21 @@ long parse_seconds_duration(char *val) return t; } +/* + * match_time_unit - check if str starts with unit followed by end-of-string or ':' + * + * This allows the time unit parser to work both in standalone duration strings + * like "100ms" and in colon-delimited SCHED_DEADLINE specifications like + * "d:10ms:100ms", while still rejecting malformed input like "100msx". + */ +static bool match_time_unit(const char *str, const char *unit) +{ + size_t len = strlen(unit); + + return strncmp(str, unit, len) == 0 && + (str[len] == '\0' || str[len] == ':'); +} + /* * parse_ns_duration - parse duration with ns/us/ms/s converting it to nanoseconds */ @@ -224,15 +239,15 @@ long parse_ns_duration(char *val) t = strtol(val, &end, 10); if (end) { - if (!strncmp(end, "ns", 2)) { + if (match_time_unit(end, "ns")) { return t; - } else if (!strncmp(end, "us", 2)) { + } else if (match_time_unit(end, "us")) { t *= 1000; return t; - } else if (!strncmp(end, "ms", 2)) { + } else if (match_time_unit(end, "ms")) { t *= 1000 * 1000; return t; - } else if (!strncmp(end, "s", 1)) { + } else if (match_time_unit(end, "s")) { t *= 1000 * 1000 * 1000; return t; } From ea5ea8359cd68037d8a093df67d55b750818ab8f Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:26 -0300 Subject: [PATCH 21/28] rtla: Use str_has_prefix() for option prefix check The argument parsing code in timerlat_main() and osnoise_main() uses strncmp() with a length of 1 to check if the first argument starts with a dash, indicating an option flag was passed. Replace this pattern with str_has_prefix() for consistency with the rest of the codebase. While character comparison would be slightly more efficient, using str_has_prefix() provides better readability and maintains a uniform coding style throughout the rtla tool. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-14-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/osnoise.c | 2 +- tools/tracing/rtla/src/timerlat.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/tracing/rtla/src/osnoise.c b/tools/tracing/rtla/src/osnoise.c index 9d1fd9981fe9..2db3db155c44 100644 --- a/tools/tracing/rtla/src/osnoise.c +++ b/tools/tracing/rtla/src/osnoise.c @@ -1210,7 +1210,7 @@ int osnoise_main(int argc, char *argv[]) if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { osnoise_usage(0); - } else if (strncmp(argv[1], "-", 1) == 0) { + } else if (str_has_prefix(argv[1], "-")) { /* the user skipped the tool, call the default one */ run_tool(&osnoise_top_ops, argc, argv); exit(0); diff --git a/tools/tracing/rtla/src/timerlat.c b/tools/tracing/rtla/src/timerlat.c index 8a44537e25cb..de3caea433ad 100644 --- a/tools/tracing/rtla/src/timerlat.c +++ b/tools/tracing/rtla/src/timerlat.c @@ -269,7 +269,7 @@ int timerlat_main(int argc, char *argv[]) if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { timerlat_usage(0); - } else if (strncmp(argv[1], "-", 1) == 0) { + } else if (str_has_prefix(argv[1], "-")) { /* the user skipped the tool, call the default one */ run_tool(&timerlat_top_ops, argc, argv); exit(0); From 48fbcd4db34b5ea9135801ffe4585a22681c0815 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:27 -0300 Subject: [PATCH 22/28] rtla/timerlat: Simplify RTLA_NO_BPF environment variable check The code that checks the RTLA_NO_BPF environment variable calls getenv() twice and uses strncmp() with a length of 2 to compare against the single-character string "1". This is inefficient and the comparison length is unnecessarily long. Store the result of getenv() in a local variable to avoid the redundant call, and replace strncmp() with strncmp_static() for the exact match comparison. This follows the same pattern established in recent commits that improved string comparison consistency throughout the rtla codebase. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-15-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/timerlat.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/tracing/rtla/src/timerlat.c b/tools/tracing/rtla/src/timerlat.c index de3caea433ad..f8c057518d22 100644 --- a/tools/tracing/rtla/src/timerlat.c +++ b/tools/tracing/rtla/src/timerlat.c @@ -28,12 +28,13 @@ int timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params) { int retval; + const char *const rtla_no_bpf = getenv("RTLA_NO_BPF"); /* * Try to enable BPF, unless disabled explicitly. * If BPF enablement fails, fall back to tracefs mode. */ - if (getenv("RTLA_NO_BPF") && strncmp(getenv("RTLA_NO_BPF"), "1", 2) == 0) { + if (rtla_no_bpf && strncmp_static(rtla_no_bpf, "1") == 0) { debug_msg("RTLA_NO_BPF set, disabling BPF\n"); params->mode = TRACING_MODE_TRACEFS; } else if (!tep_find_event_by_name(tool->trace.tep, "osnoise", "timerlat_sample")) { From 4bf4ef5292b9253d8607c61a875d9f6b14129976 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:28 -0300 Subject: [PATCH 23/28] rtla/trace: Fix write loop in trace_event_save_hist() The write loop in trace_event_save_hist() does not correctly handle errors from the write() system call. If write() returns -1, this value is added to the loop index, leading to an incorrect memory access on the next iteration and potentially an infinite loop. The loop also fails to handle EINTR. Fix the write loop by introducing proper error handling. The return value of write() is now stored in a ssize_t variable and checked for errors. The loop retries the call if interrupted by a signal and breaks on any other error after logging it with strerror(). Additionally, change the index variable type from int to size_t to match the type used for buffer sizes and by strlen(), improving type safety. Fixes: 761916fd02c2 ("rtla/trace: Save event histogram output to a file") Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-16-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/trace.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tools/tracing/rtla/src/trace.c b/tools/tracing/rtla/src/trace.c index ed7db5f4115c..fed3362527b0 100644 --- a/tools/tracing/rtla/src/trace.c +++ b/tools/tracing/rtla/src/trace.c @@ -342,11 +342,11 @@ static void trace_event_disable_filter(struct trace_instance *instance, static void trace_event_save_hist(struct trace_instance *instance, struct trace_events *tevent) { - int index, out_fd; + size_t index, hist_len; mode_t mode = 0644; char path[MAX_PATH]; char *hist; - size_t hist_len; + int out_fd; if (!tevent) return; @@ -378,7 +378,15 @@ static void trace_event_save_hist(struct trace_instance *instance, index = 0; hist_len = strlen(hist); do { - index += write(out_fd, &hist[index], hist_len - index); + const ssize_t written = write(out_fd, &hist[index], hist_len - index); + + if (written < 0) { + if (errno == EINTR) + continue; + err_msg(" Error writing hist file: %s\n", strerror(errno)); + break; + } + index += written; } while (index < hist_len); free(hist); From 47dd74f68c0c068fdd29cdf9fe1860a19209bc1f Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:29 -0300 Subject: [PATCH 24/28] rtla/trace: Fix I/O handling in save_trace_to_file() The read/write loop in save_trace_to_file() does not correctly handle errors from the read() and write() system calls. If either call is interrupted by a signal, it returns -1 with errno set to EINTR, but the code treats this as a fatal error and aborts the save operation. Additionally, write() may perform a partial write, returning fewer bytes than requested, which the code does not handle. Fix the I/O loop by introducing proper error handling. The return value of read() is now stored in a ssize_t variable and checked for errors, with EINTR causing a retry. For write(), an inner loop ensures all bytes are written, handling both EINTR and partial writes. Error messages now include strerror() output for better debugging. This follows the same pattern established in the previous commit that fixed trace_event_save_hist(), ensuring consistent and robust I/O handling throughout the trace saving code. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-17-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/trace.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/tools/tracing/rtla/src/trace.c b/tools/tracing/rtla/src/trace.c index fed3362527b0..e407447773d0 100644 --- a/tools/tracing/rtla/src/trace.c +++ b/tools/tracing/rtla/src/trace.c @@ -73,6 +73,8 @@ int save_trace_to_file(struct tracefs_instance *inst, const char *filename) char buffer[4096]; int out_fd, in_fd; int retval = -1; + ssize_t n_read; + ssize_t n_written; if (!inst || !filename) return 0; @@ -90,15 +92,30 @@ int save_trace_to_file(struct tracefs_instance *inst, const char *filename) goto out_close_in; } - do { - retval = read(in_fd, buffer, sizeof(buffer)); - if (retval <= 0) + for (;;) { + n_read = read(in_fd, buffer, sizeof(buffer)); + if (n_read < 0) { + if (errno == EINTR) + continue; + err_msg("Error reading trace file: %s\n", strerror(errno)); goto out_close; + } + if (n_read == 0) + break; - retval = write(out_fd, buffer, retval); - if (retval < 0) - goto out_close; - } while (retval > 0); + n_written = 0; + while (n_written < n_read) { + const ssize_t w = write(out_fd, buffer + n_written, n_read - n_written); + + if (w < 0) { + if (errno == EINTR) + continue; + err_msg("Error writing trace file: %s\n", strerror(errno)); + goto out_close; + } + n_written += w; + } + } retval = 0; out_close: From 5b6dc659ad792c72b3ff1be8039ae2945e030928 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:30 -0300 Subject: [PATCH 25/28] rtla/utils: Fix resource leak in set_comm_sched_attr() The set_comm_sched_attr() function opens the /proc directory via opendir() but fails to call closedir() on its successful exit path. If the function iterates through all processes without error, it returns 0 directly, leaking the DIR stream pointer. Fix this by refactoring the function to use a single exit path. A retval variable is introduced to track the success or failure status. All exit points now jump to a unified out label that calls closedir() before the function returns, ensuring the resource is always freed. Fixes: dada03db9bb19 ("rtla: Remove procps-ng dependency") Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-18-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/utils.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index c28ca48a5af0..80b7eb7b6a7a 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -390,22 +390,23 @@ int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr) if (strtoi(proc_entry->d_name, &pid)) { err_msg("'%s' is not a valid pid", proc_entry->d_name); - goto out_err; + retval = 1; + goto out; } /* procfs_is_workload_pid confirmed it is a pid */ retval = __set_sched_attr(pid, attr); if (retval) { err_msg("Error setting sched attributes for pid:%s\n", proc_entry->d_name); - goto out_err; + goto out; } debug_msg("Set sched attributes for pid:%s\n", proc_entry->d_name); } - return 0; -out_err: + retval = 0; +out: closedir(procfs); - return 1; + return retval; } #define INVALID_VAL (~0L) From 99261ccdc0f346b1f21ae3718b2b3c0dd69c04b4 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 9 Mar 2026 16:46:31 -0300 Subject: [PATCH 26/28] rtla/utils: Fix loop condition in PID validation The procfs_is_workload_pid() function iterates through a directory entry name to validate if it represents a process ID. The loop condition checks if the pointer t_name is non-NULL, but since incrementing a pointer never makes it NULL, this condition is always true within the loop's context. Although the inner isdigit() check catches the NUL terminator and breaks out of the loop, the condition is semantically misleading and not idiomatic for C string processing. Correct the loop condition from checking the pointer (t_name) to checking the character it points to (*t_name). This ensures the loop terminates when the NUL terminator is reached, aligning with standard C string iteration practices. While the original code functioned correctly due to the existing character validation, this change improves code clarity and maintainability. Signed-off-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260309195040.1019085-19-wander@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index 80b7eb7b6a7a..9cec5b3e02c8 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -324,7 +324,7 @@ static int procfs_is_workload_pid(const char *comm_prefix, struct dirent *proc_e return 0; /* check if the string is a pid */ - for (t_name = proc_entry->d_name; t_name; t_name++) { + for (t_name = proc_entry->d_name; *t_name; t_name++) { if (!isdigit(*t_name)) break; } From be8058f31b4e237604e4ce7599593ab68dc69ae7 Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Tue, 10 Mar 2026 17:07:25 +0100 Subject: [PATCH 27/28] rtla: Fix segfault on multiple SIGINTs Detach stop_trace() from SIGINT/SIGALRM on tool clean-up to prevent it from crashing RTLA by accessing freed memory. This prevents a crash when multiple SIGINTs are received. Fixes: d6899e560366 ("rtla/timerlat_hist: Abort event processing on second signal") Fixes: 80967b354a76 ("rtla/timerlat_top: Abort event processing on second signal") Reviewed-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260310160725.144443-1-tglozar@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/common.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c index 50d8aafdfd3b..35e3d3aa922e 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -41,6 +41,18 @@ static void set_signals(struct common_params *params) } } +/* + * unset_signals - unsets the signals to stop the tool + */ +static void unset_signals(struct common_params *params) +{ + signal(SIGINT, SIG_DFL); + if (params->duration) { + alarm(0); + signal(SIGALRM, SIG_DFL); + } +} + /* * getopt_auto - auto-generates optstring from long_options */ @@ -351,7 +363,7 @@ int run_tool(struct tool_ops *ops, int argc, char *argv[]) retval = ops->main(tool); if (retval) - goto out_trace; + goto out_signals; if (params->user_workload && !params->user.stopped_running) { params->user.should_run = 0; @@ -373,6 +385,8 @@ int run_tool(struct tool_ops *ops, int argc, char *argv[]) if (ops->analyze) ops->analyze(tool, stopped); +out_signals: + unset_signals(params); out_trace: trace_events_destroy(&tool->record->trace, params->events); params->events = NULL; From 82374995b63d2de21414163828a32d52610dcaf2 Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Tue, 24 Mar 2026 13:32:29 +0100 Subject: [PATCH 28/28] Documentation/rtla: Document SIGINT behavior The behavior of RTLA on receiving SIGINT is currently undocumented. Describe it in RTLA's common appendix that appears in man pages for all RTLA tools to avoid confusion. Suggested-by: Attila Fazekas Reviewed-by: Steven Rostedt (Google) Link: https://lore.kernel.org/r/20260324123229.152424-1-tglozar@redhat.com Signed-off-by: Tomas Glozar --- Documentation/tools/rtla/common_appendix.txt | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/tools/rtla/common_appendix.txt b/Documentation/tools/rtla/common_appendix.txt index 53cae7537537..8c90a02588e7 100644 --- a/Documentation/tools/rtla/common_appendix.txt +++ b/Documentation/tools/rtla/common_appendix.txt @@ -1,5 +1,26 @@ .. SPDX-License-Identifier: GPL-2.0 +SIGINT BEHAVIOR +=============== + +On the first SIGINT, RTLA exits after collecting all outstanding samples up to +the point of receiving the signal. + +When receiving more than one SIGINT, RTLA discards any outstanding samples, and +exits while displaying only samples that have already been processed. + +If SIGINT is received during RTLA cleanup, RTLA exits immediately via +the default signal handler. + +Note: For the purpose of SIGINT behavior, the expiry of duration specified via +the -d/--duration option is treated as equivalent to receiving a SIGINT. For +example, a SIGINT received after duration expired but samples have not been +processed yet will drop any outstanding samples. + +Also note that when using the timerlat tool in BPF mode, samples are processed +in-kernel; RTLA only copies them out to display them to the user. A second +SIGINT does not affect in-kernel sample aggregation. + EXIT STATUS ===========