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 --- 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 a42615011962d..22b8283a183f3 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 fb77069c972ba..034048682fefb 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 945eb61efc465..ec074cd53dd84 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 9d70ea34807ff..efbd2e834cf0e 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c @@ -466,9 +466,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); @@ -579,22 +577,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 d54d47947fb44..d2b4ac64e77b4 100644 --- a/tools/tracing/rtla/src/osnoise_top.c +++ b/tools/tracing/rtla/src/osnoise_top.c @@ -319,9 +319,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); @@ -410,22 +408,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 4e8c38a61197c..6ea397421f1c9 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -766,9 +766,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); @@ -914,22 +912,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 284b74773c2b5..dd727cb48b551 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -537,9 +537,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); @@ -664,22 +662,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 b8be3e28680ee..211ca54b15b0e 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 1e5aee4b828dd..95b911a2228b2 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 18986a5aed3c1..75cdcc63d5a15 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -1032,3 +1032,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 f7c2a52a0ab54..e29c2eb5d569d 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h @@ -69,6 +69,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); -- 2.52.0 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 --- 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 22b8283a183f3..0ac42ffd734a3 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); } /* -- 2.52.0 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 --- tools/tracing/rtla/src/actions.c | 57 +++++++++++++++++++++++--------- tools/tracing/rtla/src/utils.h | 14 ++++++-- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/tools/tracing/rtla/src/actions.c b/tools/tracing/rtla/src/actions.c index 0ac42ffd734a3..b0d68b5de08db 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 e29c2eb5d569d..8323c999260c2 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h @@ -14,8 +14,18 @@ #define MAX_NICE 20 #define MIN_NICE -19 -#define container_of(ptr, type, member)({ \ - const typeof(((type *)0)->member) *__mptr = (ptr); \ +#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)) ; }) extern int config_debug; -- 2.52.0 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 --- 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 ceff76a62a30b..cbc207fa58707 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -175,6 +175,38 @@ common_apply_config(struct osnoise_tool *tool, struct common_params *params) } +/** + * 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; @@ -352,17 +384,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); } @@ -403,18 +432,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 7602c5593ef5d..c548decd3c40f 100644 --- a/tools/tracing/rtla/src/common.h +++ b/tools/tracing/rtla/src/common.h @@ -143,6 +143,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 6ea397421f1c9..6b8eaef8a3a09 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; @@ -1048,7 +1049,6 @@ static struct osnoise_tool 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) { @@ -1056,18 +1056,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 dd727cb48b551..c6f6757c3fb6b 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; @@ -801,10 +802,10 @@ static struct osnoise_tool 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; @@ -812,8 +813,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) { @@ -821,28 +822,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; } -- 2.52.0 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 --- tools/tracing/rtla/src/osnoise.c | 4 ++-- tools/tracing/rtla/src/timerlat_u.c | 4 ++-- tools/tracing/rtla/src/trace.c | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tools/tracing/rtla/src/osnoise.c b/tools/tracing/rtla/src/osnoise.c index ec074cd53dd84..4890a9a9d6466 100644 --- a/tools/tracing/rtla/src/osnoise.c +++ b/tools/tracing/rtla/src/osnoise.c @@ -52,7 +52,7 @@ char *osnoise_get_cpus(struct osnoise_context *context) int osnoise_set_cpus(struct osnoise_context *context, char *cpus) { char *orig_cpus = osnoise_get_cpus(context); - char buffer[1024]; + char buffer[MAX_PATH]; int retval; if (!orig_cpus) @@ -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 ce68e39d25fde..efe2f72686486 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 211ca54b15b0e..e1af54f9531b8 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 @@ static void trace_event_save_hist(struct trace_instance *instance, 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); -- 2.52.0 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 --- 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 e1af54f9531b8..2f529aaf8deef 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 75cdcc63d5a15..b5a6007b108d2 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -811,6 +811,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) { @@ -818,16 +819,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); -- 2.52.0 Introduce a userspace strscpy() implementation that matches the Linux kernel's strscpy() semantics. The function is built on top of glibc's strlcpy() and provides guaranteed NUL-termination along with proper truncation detection through its return value. The previous strncpy() calls had potential issues: strncpy() does not guarantee NUL-termination when the source string length equals or exceeds the destination buffer size. This required defensive patterns like pre-zeroing buffers or manually setting the last byte to NUL. The new strscpy() function always NUL-terminates the destination buffer unless the size is zero, and returns -E2BIG on truncation, making error handling cleaner and more consistent with kernel code. Note that unlike the kernel's strscpy(), this implementation uses strlcpy() internally, which reads the entire source string to determine its length. The kernel avoids this to prevent potential DoS attacks from extremely long untrusted strings. This is harmless for a userspace CLI tool like rtla where input sources are bounded and trusted. Replace all strncpy() calls in rtla with strscpy(), using sizeof() for buffer sizes instead of magic constants to ensure the sizes stay in sync with the actual buffer declarations. Also remove a now-redundant memset() call that was previously needed to work around strncpy() behavior. Signed-off-by: Wander Lairson Costa --- tools/tracing/rtla/src/timerlat_aa.c | 6 ++--- tools/tracing/rtla/src/utils.c | 34 ++++++++++++++++++++++++++-- tools/tracing/rtla/src/utils.h | 1 + 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/tools/tracing/rtla/src/timerlat_aa.c b/tools/tracing/rtla/src/timerlat_aa.c index 31e66ea2b144c..30ef56d644f9c 100644 --- a/tools/tracing/rtla/src/timerlat_aa.c +++ b/tools/tracing/rtla/src/timerlat_aa.c @@ -455,9 +455,9 @@ static int timerlat_aa_thread_handler(struct trace_seq *s, struct tep_record *re taa_data->thread_blocking_duration = duration; if (comm) - strncpy(taa_data->run_thread_comm, comm, MAX_COMM); + strscpy(taa_data->run_thread_comm, comm, sizeof(taa_data->run_thread_comm)); else - sprintf(taa_data->run_thread_comm, "<...>"); + strscpy(taa_data->run_thread_comm, "<...>", sizeof(taa_data->run_thread_comm)); } else { taa_data->thread_thread_sum += duration; @@ -519,7 +519,7 @@ static int timerlat_aa_sched_switch_handler(struct trace_seq *s, struct tep_reco tep_get_field_val(s, event, "next_pid", record, &taa_data->current_pid, 1); comm = tep_get_field_raw(s, event, "next_comm", record, &val, 1); - strncpy(taa_data->current_comm, comm, MAX_COMM); + strscpy(taa_data->current_comm, comm, sizeof(taa_data->current_comm)); /* * If this was a kworker, clean the last kworkers that ran. diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index b5a6007b108d2..e98288e55db15 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -722,8 +722,7 @@ static const int find_mount(const char *fs, char *mp, int sizeof_mp) if (!found) return 0; - memset(mp, 0, sizeof_mp); - strncpy(mp, mount_point, sizeof_mp - 1); + strscpy(mp, mount_point, sizeof_mp); debug_msg("Fs %s found at %s\n", fs, mp); return 1; @@ -1036,6 +1035,37 @@ int strtoi(const char *s, int *res) return 0; } +/** + * strscpy - Copy a C-string into a sized buffer + * @dst: Where to copy the string to + * @src: Where to copy the string from + * @count: Size of destination buffer + * + * Copy the source string @src, or as much of it as fits, into the destination + * @dst buffer. The destination @dst buffer is always NUL-terminated, unless + * it's zero-sized. + * + * This is a userspace implementation matching the kernel's strscpy() semantics, + * built on top of glibc's strlcpy(). + * + * Returns the number of characters copied (not including the trailing NUL) + * or -E2BIG if @count is 0 or the copy was truncated. + */ +ssize_t strscpy(char *dst, const char *src, size_t count) +{ + size_t len; + + if (count == 0) + return -E2BIG; + + len = strlcpy(dst, src, count); + + if (len >= count) + return -E2BIG; + + return (ssize_t) len; +} + static inline void fatal_alloc(void) { fatal("Error allocating memory\n"); diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h index 8323c999260c2..25b08fc5e199a 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h @@ -97,6 +97,7 @@ static inline int have_libcpupower_support(void) { return 0; } #endif /* HAVE_LIBCPUPOWER_SUPPORT */ int auto_house_keeping(cpu_set_t *monitored_cpus); __attribute__((__warn_unused_result__)) int strtoi(const char *s, int *res); +ssize_t strscpy(char *dst, const char *src, size_t count); #define ns_to_usf(x) (((double)x/1000)) #define ns_to_per(total, part) ((part * 100) / (double)total) -- 2.52.0 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 --- 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 30ef56d644f9c..bc421637cc19b 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; } -- 2.52.0 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 --- 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 cbc207fa58707..73906065e7772 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -303,8 +303,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); -- 2.52.0 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 --- 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 25b08fc5e199a..1235d0f3a7bfd 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)) ; }) -- 2.52.0 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 --- 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 2f529aaf8deef..ed7db5f4115ce 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 e98288e55db15..486d96e8290fb 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -318,8 +318,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 */ -- 2.52.0 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. Switch to using strcmp() for suffix comparison to enforce exact matches. This ensures that the parser strictly validates the time unit and rejects any input containing invalid trailing characters, thereby improving the robustness of the configuration parsing. Signed-off-by: Wander Lairson Costa --- tools/tracing/rtla/src/utils.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index 486d96e8290fb..b029fe5970c31 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -211,15 +211,15 @@ long parse_ns_duration(char *val) t = strtol(val, &end, 10); if (end) { - if (!strncmp(end, "ns", 2)) { + if (strcmp(end, "ns") == 0) { return t; - } else if (!strncmp(end, "us", 2)) { + } else if (strcmp(end, "us") == 0) { t *= 1000; return t; - } else if (!strncmp(end, "ms", 2)) { + } else if (strcmp(end, "ms") == 0) { t *= 1000 * 1000; return t; - } else if (!strncmp(end, "s", 1)) { + } else if (strcmp(end, "s") == 0) { t *= 1000 * 1000 * 1000; return t; } -- 2.52.0 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 --- 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 4890a9a9d6466..5b342d945c5f6 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 8f8811f7a13bd..84577daadd668 100644 --- a/tools/tracing/rtla/src/timerlat.c +++ b/tools/tracing/rtla/src/timerlat.c @@ -272,7 +272,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); -- 2.52.0 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 strcmp() 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 --- 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 84577daadd668..d7aeaad975386 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 && strcmp(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")) { -- 2.52.0 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. Signed-off-by: Wander Lairson Costa --- 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 ed7db5f4115ce..fed3362527b08 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); -- 2.52.0 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 --- tools/tracing/rtla/src/trace.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/tools/tracing/rtla/src/trace.c b/tools/tracing/rtla/src/trace.c index fed3362527b08..8e93b48d33ef8 100644 --- a/tools/tracing/rtla/src/trace.c +++ b/tools/tracing/rtla/src/trace.c @@ -73,6 +73,7 @@ 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; if (!inst || !filename) return 0; @@ -90,15 +91,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); + ssize_t n_written = 0; + while (n_written < n_read) { + 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: -- 2.52.0 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 --- 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 b029fe5970c31..1ea9980d8ecd3 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -362,22 +362,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) -- 2.52.0 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 --- 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 1ea9980d8ecd3..3d47f3ed52dee 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -296,7 +296,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; } -- 2.52.0