diff options
Diffstat (limited to 'tools/tracing/rtla/src')
23 files changed, 970 insertions, 744 deletions
diff --git a/tools/tracing/rtla/src/actions.c b/tools/tracing/rtla/src/actions.c index 8945aee58d51..b0d68b5de08d 100644 --- a/tools/tracing/rtla/src/actions.c +++ b/tools/tracing/rtla/src/actions.c @@ -15,12 +15,10 @@ 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; - memset(&self->present, 0, sizeof(self->present)); - /* This has to be set by the user */ self->trace_output_inst = NULL; } @@ -32,7 +30,9 @@ void actions_destroy(struct actions *self) { /* Free any action-specific data */ - for (struct action *action = self->list; action < self->list + self->len; action++) { + struct action *action; + + for_each_action(self, action) { if (action->type == ACTION_SHELL) free(action->command); if (action->type == ACTION_TRACE_OUTPUT) @@ -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,20 @@ 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; - strcpy(action->trace_output, trace_output); - - return 0; + action->trace_output = strdup_fatal(trace_output); } /* * 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,43 +84,57 @@ 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; - strcpy(action->command, command); - - return 0; + action->command = strdup_fatal(command); } /* * 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; +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 */ int @@ -132,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; @@ -141,6 +153,8 @@ actions_parse(struct actions *self, const char *trigger, const char *tracefn) strcpy(trigger_c, trigger); token = strtok(trigger_c, ","); + if (!token) + return -1; if (strcmp(token, "trace") == 0) type = ACTION_TRACE_OUTPUT; @@ -162,32 +176,36 @@ 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) /* 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) { - if (strlen(token) > 4 && strncmp(token, "num=", 4) == 0) { - signal = atoi(token + 4); - } else if (strlen(token) > 4 && strncmp(token, "pid=", 4) == 0) { - if (strncmp(token + 4, "parent", 7) == 0) - pid = -1; - else - pid = atoi(token + 4); + 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, ","); @@ -197,21 +215,27 @@ 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; + arg_value = extract_arg(token, "command"); + if (!arg_value) + return -1; + actions_add_shell(self, arg_value); + 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; } /* @@ -223,7 +247,7 @@ actions_perform(struct actions *self) int pid, retval; const struct action *action; - for (action = self->list; action < self->list + self->len; action++) { + for_each_action(self, action) { switch (action->type) { case ACTION_TRACE_OUTPUT: retval = save_trace_to_file(self->trace_output_inst, action->trace_output); diff --git a/tools/tracing/rtla/src/actions.h b/tools/tracing/rtla/src/actions.h index a4f9b570775b..034048682fef 100644 --- a/tools/tracing/rtla/src/actions.h +++ b/tools/tracing/rtla/src/actions.h @@ -42,11 +42,16 @@ struct actions { struct tracefs_instance *trace_output_inst; }; +#define for_each_action(actions, action) \ + for ((action) = (actions)->list; \ + (action) < (actions)->list + (actions)->len; \ + (action)++) + 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/common.c b/tools/tracing/rtla/src/common.c index b197037fc58b..35e3d3aa922e 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -4,11 +4,15 @@ #include <pthread.h> #include <signal.h> #include <stdlib.h> -#include <unistd.h> +#include <string.h> +#include <getopt.h> +#include <sys/sysinfo.h> + #include "common.h" struct trace_instance *trace_inst; -int stop_tracing; +volatile int stop_tracing; +int nr_cpus; static void stop_trace(int sig) { @@ -38,6 +42,126 @@ 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 + */ +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 + * + * @argc: argument count + * @argv: argument vector + * @common: common parameters structure + * + * Parse command line options that are common to all rtla tools. + * + * Returns: non zero if a common option was parsed, or 0 + * if the option should be handled by tool-specific parsing. + */ +int common_parse_options(int argc, char **argv, struct common_params *common) +{ + struct trace_events *tevent; + int saved_state = optind; + int c; + + static struct option long_options[] = { + {"cpus", required_argument, 0, 'c'}, + {"cgroup", optional_argument, 0, 'C'}, + {"debug", no_argument, 0, 'D'}, + {"duration", required_argument, 0, 'd'}, + {"event", required_argument, 0, 'e'}, + {"house-keeping", required_argument, 0, 'H'}, + {"priority", required_argument, 0, 'P'}, + {0, 0, 0, 0} + }; + + opterr = 0; + c = getopt_auto(argc, argv, long_options); + opterr = 1; + + switch (c) { + case 'c': + if (parse_cpu_set(optarg, &common->monitored_cpus)) + fatal("Invalid -c cpu list"); + common->cpus = optarg; + break; + case 'C': + common->cgroup = 1; + common->cgroup_name = parse_optional_arg(argc, argv); + break; + case 'D': + config_debug = 1; + break; + case 'd': + common->duration = parse_seconds_duration(optarg); + if (!common->duration) + fatal("Invalid -d duration"); + break; + case 'e': + tevent = trace_event_alloc(optarg); + if (!tevent) + fatal("Error alloc trace event"); + + if (common->events) + tevent->next = common->events; + common->events = tevent; + break; + case 'H': + common->hk_cpus = 1; + if (parse_cpu_set(optarg, &common->hk_cpu_set)) + fatal("Error parsing house keeping CPUs"); + break; + case 'P': + if (parse_prio(optarg, &common->sched_param) == -1) + fatal("Invalid -P priority"); + common->set_sched = 1; + break; + default: + optind = saved_state; + return 0; + } + + return c; +} + +/* * common_apply_config - apply common configs to the initialized tool */ int @@ -55,7 +179,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); } @@ -95,6 +219,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; @@ -103,6 +259,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); @@ -191,8 +348,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); @@ -204,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; @@ -226,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; @@ -272,17 +433,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); } @@ -323,18 +481,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 ? */ @@ -348,3 +502,61 @@ int hist_main_loop(struct osnoise_tool *tool) return retval; } + +int osn_set_stop(struct osnoise_tool *tool) +{ + struct common_params *params = tool->params; + int retval; + + retval = osnoise_set_stop_us(tool->context, params->stop_us); + if (retval) { + err_msg("Failed to set stop us\n"); + return retval; + } + + retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us); + if (retval) { + err_msg("Failed to set stop total us\n"); + return retval; + } + + return 0; +} + +static void print_msg_array(const char * const *msgs) +{ + if (!msgs) + return; + + for (int i = 0; msgs[i]; i++) + fprintf(stderr, "%s\n", msgs[i]); +} + +/* + * common_usage - print complete usage information + */ +void common_usage(const char *tool, const char *mode, + const char *desc, const char * const *start_msgs, const char * const *opt_msgs) +{ + static const char * const common_options[] = { + " -h/--help: print this menu", + NULL + }; + fprintf(stderr, "rtla %s", tool); + if (strcmp(mode, "")) + fprintf(stderr, " %s", mode); + fprintf(stderr, ": %s (version %s)\n\n", desc, VERSION); + fprintf(stderr, " usage: [rtla] %s ", tool); + + if (strcmp(mode, "top") == 0) + fprintf(stderr, "[top] [-h] "); + else + fprintf(stderr, "%s [-h] ", mode); + + print_msg_array(start_msgs); + fprintf(stderr, "\n"); + print_msg_array(common_options); + print_msg_array(opt_msgs); + + exit(EXIT_SUCCESS); +} diff --git a/tools/tracing/rtla/src/common.h b/tools/tracing/rtla/src/common.h index 9ec2b7632c37..51665db4ffce 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 <getopt.h> #include "actions.h" #include "timerlat_u.h" #include "trace.h" @@ -54,7 +55,7 @@ struct osnoise_context { }; extern struct trace_instance *trace_inst; -extern int stop_tracing; +extern volatile int stop_tracing; struct hist_params { char no_irq; @@ -107,7 +108,9 @@ struct common_params { struct timerlat_u_params user; }; -#define for_each_monitored_cpu(cpu, nr_cpus, common) \ +extern int nr_cpus; + +#define for_each_monitored_cpu(cpu, common) \ for (cpu = 0; cpu < nr_cpus; cpu++) \ if (!(common)->cpus || CPU_ISSET(cpu, &(common)->monitored_cpus)) @@ -143,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); @@ -152,7 +173,16 @@ void osnoise_destroy_tool(struct osnoise_tool *top); struct osnoise_tool *osnoise_init_tool(char *tool_name); struct osnoise_tool *osnoise_init_trace_tool(const char *tracer); bool osnoise_trace_is_off(struct osnoise_tool *tool, struct osnoise_tool *record); +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); int hist_main_loop(struct osnoise_tool *tool); +int osn_set_stop(struct osnoise_tool *tool); + +void common_usage(const char *tool, const char *mode, + const char *desc, const char * const *start_msgs, const char * const *opt_msgs); diff --git a/tools/tracing/rtla/src/osnoise.c b/tools/tracing/rtla/src/osnoise.c index 312c511fa004..2db3db155c44 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); @@ -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; } /* @@ -1128,18 +1118,6 @@ osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params) goto out_err; } - retval = osnoise_set_stop_us(tool->context, params->common.stop_us); - if (retval) { - err_msg("Failed to set stop us\n"); - goto out_err; - } - - retval = osnoise_set_stop_total_us(tool->context, params->common.stop_total_us); - if (retval) { - err_msg("Failed to set stop total us\n"); - goto out_err; - } - retval = osnoise_set_tracing_thresh(tool->context, params->threshold); if (retval) { err_msg("Failed to set tracing_thresh\n"); @@ -1184,9 +1162,12 @@ int osnoise_enable(struct osnoise_tool *tool) debug_msg("Error cleaning up the buffer"); return retval; } - } + retval = osn_set_stop(tool); + if (retval) + return retval; + return 0; } @@ -1229,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/osnoise.h b/tools/tracing/rtla/src/osnoise.h index 895687030c0b..168669aa7e0d 100644 --- a/tools/tracing/rtla/src/osnoise.h +++ b/tools/tracing/rtla/src/osnoise.h @@ -34,12 +34,7 @@ int osnoise_set_runtime_period(struct osnoise_context *context, unsigned long long period); void osnoise_restore_runtime_period(struct osnoise_context *context); -int osnoise_set_stop_us(struct osnoise_context *context, - long long stop_us); void osnoise_restore_stop_us(struct osnoise_context *context); - -int osnoise_set_stop_total_us(struct osnoise_context *context, - long long stop_total_us); void osnoise_restore_stop_total_us(struct osnoise_context *context); int osnoise_set_timerlat_period_us(struct osnoise_context *context, @@ -58,8 +53,6 @@ int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff); void osnoise_report_missed_events(struct osnoise_tool *tool); int osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params); -int osnoise_hist_main(int argc, char *argv[]); -int osnoise_top_main(int argc, char **argv); int osnoise_enable(struct osnoise_tool *tool); int osnoise_main(int argc, char **argv); int hwnoise_main(int argc, char **argv); @@ -68,4 +61,3 @@ extern struct tool_ops timerlat_top_ops, timerlat_hist_ops; extern struct tool_ops osnoise_top_ops, osnoise_hist_ops; int run_tool(struct tool_ops *ops, int argc, char *argv[]); -int hist_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 ff8c231e47c4..8ad816b80265 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c @@ -9,7 +9,6 @@ #include <string.h> #include <signal.h> #include <unistd.h> -#include <errno.h> #include <stdio.h> #include <time.h> @@ -30,7 +29,6 @@ struct osnoise_hist_data { struct osnoise_hist_cpu *hist; int entries; int bucket_size; - int nr_cpus; }; /* @@ -42,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); } @@ -63,7 +61,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; @@ -74,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) @@ -247,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, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -276,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, data->nr_cpus, ¶ms->common) { - + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -288,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, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -301,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, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -317,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, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -352,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, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -388,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, data->nr_cpus, ¶ms->common) { + for_each_monitored_cpu(cpu, ¶ms->common) { if (!data->hist[cpu].count) continue; @@ -409,16 +405,15 @@ osnoise_print_stats(struct osnoise_tool *tool) */ static void osnoise_hist_usage(void) { - int i; - - static const char * const msg[] = { - "", - " usage: rtla osnoise hist [-h] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", + static const char * const msg_start[] = { + "[-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", " [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\", " [-c cpu-list] [-H cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] \\", " [--no-index] [--with-zeros] [-C [cgroup_name]] [--warm-up]", - "", - " -h/--help: print this menu", + NULL, + }; + + static const char * const msg_opts[] = { " -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit", " -p/--period us: osnoise period in us", " -r/--runtime us: osnoise runtime in us", @@ -453,13 +448,8 @@ static void osnoise_hist_usage(void) NULL, }; - fprintf(stderr, "rtla osnoise hist: a per-cpu histogram of the OS noise (version %s)\n", - VERSION); - - for (i = 0; msg[i]; i++) - fprintf(stderr, "%s\n", msg[i]); - - exit(EXIT_SUCCESS); + common_usage("osnoise", "hist", "a per-cpu histogram of the OS noise", + msg_start, msg_opts); } /* @@ -469,14 +459,11 @@ static struct common_params *osnoise_hist_parse_args(int argc, char *argv[]) { struct osnoise_params *params; - struct trace_events *tevent; int retval; 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); @@ -491,19 +478,12 @@ static struct common_params {"auto", required_argument, 0, 'a'}, {"bucket-size", required_argument, 0, 'b'}, {"entries", required_argument, 0, 'E'}, - {"cpus", required_argument, 0, 'c'}, - {"cgroup", optional_argument, 0, 'C'}, - {"debug", no_argument, 0, 'D'}, - {"duration", required_argument, 0, 'd'}, - {"house-keeping", required_argument, 0, 'H'}, {"help", no_argument, 0, 'h'}, {"period", required_argument, 0, 'p'}, - {"priority", required_argument, 0, 'P'}, {"runtime", required_argument, 0, 'r'}, {"stop", required_argument, 0, 's'}, {"stop-total", required_argument, 0, 'S'}, {"trace", optional_argument, 0, 't'}, - {"event", required_argument, 0, 'e'}, {"threshold", required_argument, 0, 'T'}, {"no-header", no_argument, 0, '0'}, {"no-summary", no_argument, 0, '1'}, @@ -518,8 +498,10 @@ static struct common_params {0, 0, 0, 0} }; - c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:p:P:r:s:S:t::T:01234:5:6:7:", - long_options, NULL); + if (common_parse_options(argc, argv, ¶ms->common)) + continue; + + c = getopt_auto(argc, argv, long_options); /* detect the end of the options. */ if (c == -1) @@ -544,34 +526,6 @@ static struct common_params params->common.hist.bucket_size >= 1000000) fatal("Bucket size needs to be > 0 and <= 1000000"); break; - case 'c': - retval = parse_cpu_set(optarg, ¶ms->common.monitored_cpus); - if (retval) - fatal("Invalid -c cpu list"); - params->common.cpus = optarg; - break; - case 'C': - params->common.cgroup = 1; - params->common.cgroup_name = parse_optional_arg(argc, argv); - break; - case 'D': - config_debug = 1; - break; - case 'd': - params->common.duration = parse_seconds_duration(optarg); - if (!params->common.duration) - fatal("Invalid -D duration"); - break; - case 'e': - tevent = trace_event_alloc(optarg); - if (!tevent) - fatal("Error alloc trace event"); - - if (params->common.events) - tevent->next = params->common.events; - - params->common.events = tevent; - break; case 'E': params->common.hist.entries = get_llong_from_str(optarg); if (params->common.hist.entries < 10 || @@ -582,23 +536,11 @@ static struct common_params case '?': osnoise_hist_usage(); break; - case 'H': - params->common.hk_cpus = 1; - retval = parse_cpu_set(optarg, ¶ms->common.hk_cpu_set); - if (retval) - fatal("Error parsing house keeping CPUs"); - break; case 'p': params->period = get_llong_from_str(optarg); if (params->period > 10000000) fatal("Period longer than 10 s"); break; - case 'P': - retval = parse_prio(optarg, ¶ms->common.sched_param); - if (retval == -1) - fatal("Invalid -P priority"); - params->common.set_sched = 1; - break; case 'r': params->runtime = get_llong_from_str(optarg); if (params->runtime < 100) @@ -631,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); @@ -699,15 +635,12 @@ 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) 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 04c699bdd736..244bdce022ad 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; }; /* @@ -51,7 +50,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; @@ -59,8 +58,6 @@ static struct osnoise_top_data *osnoise_alloc_top(int nr_cpus) 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) @@ -232,18 +229,14 @@ 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); 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); } @@ -257,14 +250,16 @@ osnoise_print_stats(struct osnoise_tool *top) */ static void osnoise_top_usage(struct osnoise_params *params) { - int i; + const char *tool, *mode, *desc; - static const char * const msg[] = { - " [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", + static const char * const msg_start[] = { + "[-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", " [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\", " [-c cpu-list] [-H cpu-list] [-P priority] [-C [cgroup_name]] [--warm-up s]", - "", - " -h/--help: print this menu", + NULL, + }; + + static const char * const msg_opts[] = { " -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit", " -p/--period us: osnoise period in us", " -r/--runtime us: osnoise runtime in us", @@ -295,25 +290,16 @@ static void osnoise_top_usage(struct osnoise_params *params) }; if (params->mode == MODE_OSNOISE) { - fprintf(stderr, - "rtla osnoise top: a per-cpu summary of the OS noise (version %s)\n", - VERSION); - - fprintf(stderr, " usage: rtla osnoise [top]"); + tool = "osnoise"; + mode = "top"; + desc = "a per-cpu summary of the OS noise"; + } else { + tool = "hwnoise"; + mode = ""; + desc = "a summary of hardware-related noise"; } - if (params->mode == MODE_HWNOISE) { - fprintf(stderr, - "rtla hwnoise: a summary of hardware-related noise (version %s)\n", - VERSION); - - fprintf(stderr, " usage: rtla hwnoise"); - } - - for (i = 0; msg[i]; i++) - fprintf(stderr, "%s\n", msg[i]); - - exit(EXIT_SUCCESS); + common_usage(tool, mode, desc, msg_start, msg_opts); } /* @@ -322,14 +308,11 @@ static void osnoise_top_usage(struct osnoise_params *params) struct common_params *osnoise_top_parse_args(int argc, char **argv) { struct osnoise_params *params; - struct trace_events *tevent; int retval; 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); @@ -346,15 +329,8 @@ struct common_params *osnoise_top_parse_args(int argc, char **argv) while (1) { static struct option long_options[] = { {"auto", required_argument, 0, 'a'}, - {"cpus", required_argument, 0, 'c'}, - {"cgroup", optional_argument, 0, 'C'}, - {"debug", no_argument, 0, 'D'}, - {"duration", required_argument, 0, 'd'}, - {"event", required_argument, 0, 'e'}, - {"house-keeping", required_argument, 0, 'H'}, {"help", no_argument, 0, 'h'}, {"period", required_argument, 0, 'p'}, - {"priority", required_argument, 0, 'P'}, {"quiet", no_argument, 0, 'q'}, {"runtime", required_argument, 0, 'r'}, {"stop", required_argument, 0, 's'}, @@ -370,8 +346,10 @@ struct common_params *osnoise_top_parse_args(int argc, char **argv) {0, 0, 0, 0} }; - c = getopt_long(argc, argv, "a:c:C::d:De:hH:p:P:qr:s:S:t::T:0:1:2:3:", - long_options, NULL); + if (common_parse_options(argc, argv, ¶ms->common)) + continue; + + c = getopt_auto(argc, argv, long_options); /* Detect the end of the options. */ if (c == -1) @@ -390,55 +368,15 @@ struct common_params *osnoise_top_parse_args(int argc, char **argv) trace_output = "osnoise_trace.txt"; break; - case 'c': - retval = parse_cpu_set(optarg, ¶ms->common.monitored_cpus); - if (retval) - fatal("Invalid -c cpu list"); - params->common.cpus = optarg; - break; - case 'C': - params->common.cgroup = 1; - params->common.cgroup_name = parse_optional_arg(argc, argv); - break; - case 'D': - config_debug = 1; - break; - case 'd': - params->common.duration = parse_seconds_duration(optarg); - if (!params->common.duration) - fatal("Invalid -d duration"); - break; - case 'e': - tevent = trace_event_alloc(optarg); - if (!tevent) - fatal("Error alloc trace event"); - - if (params->common.events) - tevent->next = params->common.events; - params->common.events = tevent; - - break; case 'h': case '?': osnoise_top_usage(params); break; - case 'H': - params->common.hk_cpus = 1; - retval = parse_cpu_set(optarg, ¶ms->common.hk_cpu_set); - if (retval) - fatal("Error parsing house keeping CPUs"); - break; case 'p': params->period = get_llong_from_str(optarg); if (params->period > 10000000) fatal("Period longer than 10 s"); break; - case 'P': - retval = parse_prio(optarg, ¶ms->common.sched_param); - if (retval == -1) - fatal("Invalid -P priority"); - params->common.set_sched = 1; - break; case 'q': params->common.quiet = 1; break; @@ -462,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); @@ -547,15 +479,12 @@ 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) 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 e2265b5d6491..549d2d2191d2 100644 --- a/tools/tracing/rtla/src/timerlat.bpf.c +++ b/tools/tracing/rtla/src/timerlat.bpf.c @@ -40,6 +40,17 @@ struct { __uint(max_entries, 1); } signal_stop_tracing SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(key_size, sizeof(unsigned int)); + __uint(max_entries, 1); + __array(values, unsigned int (void *)); +} bpf_action SEC(".maps") = { + .values = { + [0] = 0 + }, +}; + /* Params to be set by rtla */ const volatile int bucket_size = 1; const volatile int output_divisor = 1000; @@ -109,7 +120,7 @@ nosubprog void update_summary(void *map, map_set(map, SUMMARY_SUM, map_get(map, SUMMARY_SUM) + latency); } -nosubprog void set_stop_tracing(void) +nosubprog void set_stop_tracing(struct trace_event_raw_timerlat_sample *tp_args) { int value = 0; @@ -118,6 +129,12 @@ nosubprog void set_stop_tracing(void) /* Signal to userspace */ bpf_ringbuf_output(&signal_stop_tracing, &value, sizeof(value), 0); + + /* + * Call into BPF action program, if attached. + * Otherwise, just silently fail. + */ + bpf_tail_call(tp_args, &bpf_action, 0); } SEC("tp/osnoise/timerlat_sample") @@ -138,19 +155,19 @@ int handle_timerlat_sample(struct trace_event_raw_timerlat_sample *tp_args) update_summary(&summary_irq, latency, bucket); if (irq_threshold != 0 && latency_us >= irq_threshold) - set_stop_tracing(); + set_stop_tracing(tp_args); } else if (tp_args->context == 1) { update_main_hist(&hist_thread, bucket); update_summary(&summary_thread, latency, bucket); if (thread_threshold != 0 && latency_us >= thread_threshold) - set_stop_tracing(); + set_stop_tracing(tp_args); } else { update_main_hist(&hist_user, bucket); update_summary(&summary_user, latency, bucket); if (thread_threshold != 0 && latency_us >= thread_threshold) - set_stop_tracing(); + set_stop_tracing(tp_args); } return 0; diff --git a/tools/tracing/rtla/src/timerlat.c b/tools/tracing/rtla/src/timerlat.c index df4f9bfe3433..f8c057518d22 100644 --- a/tools/tracing/rtla/src/timerlat.c +++ b/tools/tracing/rtla/src/timerlat.c @@ -9,7 +9,6 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <errno.h> #include <fcntl.h> #include <stdio.h> #include <sched.h> @@ -29,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")) { @@ -48,25 +48,17 @@ timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params) } } - if (params->mode != TRACING_MODE_BPF) { - /* - * In tracefs and mixed mode, timerlat tracer handles stopping - * on threshold - */ - retval = osnoise_set_stop_us(tool->context, params->common.stop_us); - if (retval) { - err_msg("Failed to set stop us\n"); + /* Check if BPF action program is requested but BPF is not available */ + if (params->bpf_action_program) { + if (params->mode == TRACING_MODE_TRACEFS) { + err_msg("BPF actions are not supported in tracefs-only mode\n"); goto out_err; } - retval = osnoise_set_stop_total_us(tool->context, params->common.stop_total_us); - if (retval) { - err_msg("Failed to set stop total us\n"); + if (timerlat_load_bpf_action_program(params->bpf_action_program)) goto out_err; - } } - retval = osnoise_set_timerlat_period_us(tool->context, params->timerlat_period_us ? params->timerlat_period_us : @@ -108,7 +100,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); @@ -124,9 +116,7 @@ int timerlat_enable(struct osnoise_tool *tool) return -1; } - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - - 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; @@ -143,7 +133,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; @@ -184,6 +174,16 @@ int timerlat_enable(struct osnoise_tool *tool) } } + /* + * In tracefs and mixed mode, timerlat tracer handles stopping + * on threshold + */ + if (params->mode != TRACING_MODE_BPF) { + retval = osn_set_stop(tool); + if (retval) + return retval; + } + return 0; } @@ -213,14 +213,13 @@ 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(); 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); } } @@ -271,7 +270,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); diff --git a/tools/tracing/rtla/src/timerlat.h b/tools/tracing/rtla/src/timerlat.h index fd6065f48bb7..364203a29abd 100644 --- a/tools/tracing/rtla/src/timerlat.h +++ b/tools/tracing/rtla/src/timerlat.h @@ -27,6 +27,8 @@ struct timerlat_params { int dump_tasks; 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) @@ -36,4 +38,3 @@ int timerlat_main(int argc, char *argv[]); int timerlat_enable(struct osnoise_tool *tool); void timerlat_analyze(struct osnoise_tool *tool, bool stopped); void timerlat_free(struct osnoise_tool *tool); - diff --git a/tools/tracing/rtla/src/timerlat_aa.c b/tools/tracing/rtla/src/timerlat_aa.c index 31e66ea2b144..a121ff9d2c57 100644 --- a/tools/tracing/rtla/src/timerlat_aa.c +++ b/tools/tracing/rtla/src/timerlat_aa.c @@ -102,8 +102,8 @@ 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; /* per CPU data */ struct timerlat_aa_data *taa_data; @@ -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; } @@ -481,23 +481,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; } @@ -738,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) { @@ -766,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; @@ -792,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) { @@ -842,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); @@ -1020,9 +1040,8 @@ 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; int retval; @@ -1032,9 +1051,9 @@ int timerlat_aa_init(struct osnoise_tool *tool, int dump_tasks) __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; 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 <bristot@kernel.org> */ -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_bpf.c b/tools/tracing/rtla/src/timerlat_bpf.c index e97d16646bcd..dd3cf71d74e5 100644 --- a/tools/tracing/rtla/src/timerlat_bpf.c +++ b/tools/tracing/rtla/src/timerlat_bpf.c @@ -7,6 +7,10 @@ static struct timerlat_bpf *bpf; +/* BPF object and program for action program */ +static struct bpf_object *obj; +static struct bpf_program *prog; + /* * timerlat_bpf_init - load and initialize BPF program to collect timerlat data */ @@ -60,6 +64,19 @@ int timerlat_bpf_init(struct timerlat_params *params) } /* + * timerlat_bpf_set_action - set action on threshold executed on BPF side + */ +static int timerlat_bpf_set_action(struct bpf_program *prog) +{ + unsigned int key = 0, value = bpf_program__fd(prog); + + return bpf_map__update_elem(bpf->maps.bpf_action, + &key, sizeof(key), + &value, sizeof(value), + BPF_ANY); +} + +/* * timerlat_bpf_attach - attach BPF program to collect timerlat data */ int timerlat_bpf_attach(void) @@ -83,6 +100,11 @@ void timerlat_bpf_detach(void) void timerlat_bpf_destroy(void) { timerlat_bpf__destroy(bpf); + bpf = NULL; + if (obj) + bpf_object__close(obj); + obj = NULL; + prog = NULL; } static int handle_rb_event(void *ctx, void *data, size_t data_sz) @@ -125,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; @@ -154,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); } /* @@ -169,12 +189,55 @@ 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); +} + +/* + * timerlat_load_bpf_action_program - load and register a BPF action program + */ +int timerlat_load_bpf_action_program(const char *program_path) +{ + int err; + + obj = bpf_object__open_file(program_path, NULL); + if (!obj) { + err_msg("Failed to open BPF action program: %s\n", program_path); + goto out_err; + } + + err = bpf_object__load(obj); + if (err) { + err_msg("Failed to load BPF action program: %s\n", program_path); + goto out_obj_err; + } + + prog = bpf_object__find_program_by_name(obj, "action_handler"); + if (!prog) { + err_msg("BPF action program must have 'action_handler' function: %s\n", + program_path); + goto out_obj_err; + } + + err = timerlat_bpf_set_action(prog); + if (err) { + err_msg("Failed to register BPF action program: %s\n", program_path); + goto out_prog_err; + } + + return 0; + +out_prog_err: + prog = NULL; +out_obj_err: + bpf_object__close(obj); + obj = NULL; +out_err: + return 1; } + #endif /* HAVE_BPF_SKEL */ diff --git a/tools/tracing/rtla/src/timerlat_bpf.h b/tools/tracing/rtla/src/timerlat_bpf.h index 118487436d30..33f0df6a9060 100644 --- a/tools/tracing/rtla/src/timerlat_bpf.h +++ b/tools/tracing/rtla/src/timerlat_bpf.h @@ -22,14 +22,12 @@ 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 static inline int timerlat_bpf_init(struct timerlat_params *params) @@ -44,16 +42,18 @@ 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; +} +static inline int timerlat_load_bpf_action_program(const char *program_path) { return -1; } diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 1fb471a787b7..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; @@ -44,7 +45,6 @@ struct timerlat_hist_data { struct timerlat_hist_cpu *hist; int entries; int bucket_size; - int nr_cpus; }; /* @@ -56,7 +56,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); @@ -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; @@ -94,7 +94,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,17 +203,17 @@ 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++) { 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++) { + 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]; @@ -223,55 +222,50 @@ 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++) { + 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]; } 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++) { + 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]; } 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++) { + 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]; } 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++) { + 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]; } 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++) { + 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]; @@ -305,7 +299,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, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -357,7 +351,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, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -379,7 +373,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, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -407,7 +401,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, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -435,7 +429,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, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -480,7 +474,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, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -627,7 +621,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, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -665,7 +659,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, ¶ms->common) { if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; @@ -696,17 +690,16 @@ timerlat_print_stats(struct osnoise_tool *tool) */ static void timerlat_hist_usage(void) { - int i; - - char *msg[] = { - "", - " usage: [rtla] timerlat hist [-h] [-q] [-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\", + static const char * const msg_start[] = { + "[-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\", " [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\", " [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\", " [--no-index] [--with-zeros] [--dma-latency us] [-C [cgroup_name]] [--no-aa] [--dump-task] [-u|-k]", " [--warm-up s] [--deepest-idle-state n]", - "", - " -h/--help: print this menu", + NULL, + }; + + static const char * const msg_opts[] = { " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit", " -p/--period us: timerlat period in us", " -i/--irq us: stop trace if the irq latency is higher than the argument in us", @@ -747,16 +740,13 @@ static void timerlat_hist_usage(void) " --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", " --on-threshold <action>: define action to be executed at latency threshold, multiple are allowed", " --on-end <action>: define action to be executed at measurement end, multiple are allowed", + " --bpf-action <program>: load and execute BPF program when latency threshold is exceeded", + " --stack-format <format>: set the stack format (truncate, skip, full)", NULL, }; - fprintf(stderr, "rtla timerlat hist: a per-cpu histogram of the timer latency (version %s)\n", - VERSION); - - for (i = 0; msg[i]; i++) - fprintf(stderr, "%s\n", msg[i]); - - exit(EXIT_SUCCESS); + common_usage("timerlat", "hist", "a per-cpu histogram of the timer latency", + msg_start, msg_opts); } /* @@ -766,15 +756,12 @@ static struct common_params *timerlat_hist_parse_args(int argc, char *argv[]) { struct timerlat_params *params; - struct trace_events *tevent; int auto_thresh; int retval; 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); @@ -793,28 +780,24 @@ 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'}, - {"cpus", required_argument, 0, 'c'}, - {"cgroup", optional_argument, 0, 'C'}, {"bucket-size", required_argument, 0, 'b'}, - {"debug", no_argument, 0, 'D'}, {"entries", required_argument, 0, 'E'}, - {"duration", required_argument, 0, 'd'}, - {"house-keeping", required_argument, 0, 'H'}, {"help", no_argument, 0, 'h'}, {"irq", required_argument, 0, 'i'}, {"nano", no_argument, 0, 'n'}, {"period", required_argument, 0, 'p'}, - {"priority", required_argument, 0, 'P'}, {"stack", required_argument, 0, 's'}, {"thread", required_argument, 0, 'T'}, {"trace", optional_argument, 0, 't'}, {"user-threads", no_argument, 0, 'u'}, {"kernel-threads", no_argument, 0, 'k'}, {"user-load", no_argument, 0, 'U'}, - {"event", required_argument, 0, 'e'}, {"no-irq", no_argument, 0, '0'}, {"no-thread", no_argument, 0, '1'}, {"no-header", no_argument, 0, '2'}, @@ -831,11 +814,15 @@ static struct common_params {"deepest-idle-state", required_argument, 0, '\4'}, {"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} }; - c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:i:knp:P:s:t::T:uU0123456:7:8:9\1\2:\3:", - long_options, NULL); + if (common_parse_options(argc, argv, ¶ms->common)) + continue; + + c = getopt_auto(argc, argv, long_options); /* detect the end of the options. */ if (c == -1) @@ -857,40 +844,12 @@ static struct common_params trace_output = "timerlat_trace.txt"; break; - case 'c': - retval = parse_cpu_set(optarg, ¶ms->common.monitored_cpus); - if (retval) - fatal("Invalid -c cpu list"); - params->common.cpus = optarg; - break; - case 'C': - params->common.cgroup = 1; - params->common.cgroup_name = parse_optional_arg(argc, argv); - break; case 'b': params->common.hist.bucket_size = get_llong_from_str(optarg); if (params->common.hist.bucket_size == 0 || params->common.hist.bucket_size >= 1000000) fatal("Bucket size needs to be > 0 and <= 1000000"); break; - case 'D': - config_debug = 1; - break; - case 'd': - params->common.duration = parse_seconds_duration(optarg); - if (!params->common.duration) - fatal("Invalid -D duration"); - break; - case 'e': - tevent = trace_event_alloc(optarg); - if (!tevent) - fatal("Error alloc trace event"); - - if (params->common.events) - tevent->next = params->common.events; - - params->common.events = tevent; - break; case 'E': params->common.hist.entries = get_llong_from_str(optarg); if (params->common.hist.entries < 10 || @@ -901,12 +860,6 @@ static struct common_params case '?': timerlat_hist_usage(); break; - case 'H': - params->common.hk_cpus = 1; - retval = parse_cpu_set(optarg, ¶ms->common.hk_cpu_set); - if (retval) - fatal("Error parsing house keeping CPUs"); - break; case 'i': params->common.stop_us = get_llong_from_str(optarg); break; @@ -921,12 +874,6 @@ static struct common_params if (params->timerlat_period_us > 1000000) fatal("Period longer than 1 s"); break; - case 'P': - retval = parse_prio(optarg, ¶ms->common.sched_param); - if (retval == -1) - fatal("Invalid -P priority"); - params->common.set_sched = 1; - break; case 's': params->print_stack = get_llong_from_str(optarg); break; @@ -963,22 +910,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); @@ -1012,6 +953,14 @@ static struct common_params if (retval) fatal("Invalid action %s", optarg); break; + 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"); } @@ -1077,15 +1026,12 @@ 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) 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; @@ -1102,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) { @@ -1110,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 29c2c1f717ed..64cbdcc878b0 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -11,13 +11,13 @@ #include <unistd.h> #include <stdio.h> #include <time.h> -#include <errno.h> #include <sched.h> #include <pthread.h> #include "timerlat.h" #include "timerlat_aa.h" #include "timerlat_bpf.h" +#include "common.h" struct timerlat_top_cpu { unsigned long long irq_count; @@ -42,7 +42,6 @@ struct timerlat_top_cpu { struct timerlat_top_data { struct timerlat_top_cpu *cpu_data; - int nr_cpus; }; /* @@ -63,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; @@ -72,8 +71,6 @@ static struct timerlat_top_data *timerlat_alloc_top(int nr_cpus) 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) @@ -191,61 +188,56 @@ 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, - data->nr_cpus); + 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]; } 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++) { + 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]; } 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++) { + 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]; } 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++) { + 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]; } 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++) { + 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]; @@ -443,15 +435,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); @@ -459,7 +447,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); } @@ -476,15 +464,14 @@ timerlat_print_stats(struct osnoise_tool *top) */ static void timerlat_top_usage(void) { - int i; - - static const char *const msg[] = { - "", - " usage: rtla timerlat [top] [-h] [-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\", + static const char *const msg_start[] = { + "[-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\", " [[-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\", " [-P priority] [--dma-latency us] [--aa-only us] [-C [cgroup_name]] [-u|-k] [--warm-up s] [--deepest-idle-state n]", - "", - " -h/--help: print this menu", + NULL, + }; + + static const char *const msg_opts[] = { " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit", " --aa-only us: stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)", " -p/--period us: timerlat period in us", @@ -519,16 +506,13 @@ static void timerlat_top_usage(void) " --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", " --on-threshold <action>: 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 <program>: load and execute BPF program when latency threshold is exceeded", + " --stack-format <format>: set the stack format (truncate, skip, full)", NULL, }; - fprintf(stderr, "rtla timerlat top: a per-cpu summary of the timer latency (version %s)\n", - VERSION); - - for (i = 0; msg[i]; i++) - fprintf(stderr, "%s\n", msg[i]); - - exit(EXIT_SUCCESS); + common_usage("timerlat", "top", "a per-cpu summary of the timer latency", + msg_start, msg_opts); } /* @@ -538,15 +522,12 @@ static struct common_params *timerlat_top_parse_args(int argc, char **argv) { struct timerlat_params *params; - struct trace_events *tevent; long long auto_thresh; int retval; 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); @@ -563,20 +544,16 @@ 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'}, - {"cpus", required_argument, 0, 'c'}, - {"cgroup", optional_argument, 0, 'C'}, - {"debug", no_argument, 0, 'D'}, - {"duration", required_argument, 0, 'd'}, - {"event", required_argument, 0, 'e'}, {"help", no_argument, 0, 'h'}, - {"house-keeping", required_argument, 0, 'H'}, {"irq", required_argument, 0, 'i'}, {"nano", no_argument, 0, 'n'}, {"period", required_argument, 0, 'p'}, - {"priority", required_argument, 0, 'P'}, {"quiet", no_argument, 0, 'q'}, {"stack", required_argument, 0, 's'}, {"thread", required_argument, 0, 'T'}, @@ -595,11 +572,15 @@ static struct common_params {"deepest-idle-state", required_argument, 0, '8'}, {"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} }; - c = getopt_long(argc, argv, "a:c:C::d:De:hH:i:knp:P:qs:t::T:uU0:1:2:345:6:7:", - long_options, NULL); + if (common_parse_options(argc, argv, ¶ms->common)) + continue; + + c = getopt_auto(argc, argv, long_options); /* detect the end of the options. */ if (c == -1) @@ -635,43 +616,10 @@ static struct common_params /* set aa_only to avoid parsing the trace */ params->common.aa_only = 1; break; - case 'c': - retval = parse_cpu_set(optarg, ¶ms->common.monitored_cpus); - if (retval) - fatal("Invalid -c cpu list"); - params->common.cpus = optarg; - break; - case 'C': - params->common.cgroup = 1; - params->common.cgroup_name = optarg; - break; - case 'D': - config_debug = 1; - break; - case 'd': - params->common.duration = parse_seconds_duration(optarg); - if (!params->common.duration) - fatal("Invalid -d duration"); - break; - case 'e': - tevent = trace_event_alloc(optarg); - if (!tevent) - fatal("Error alloc trace event"); - - if (params->common.events) - tevent->next = params->common.events; - params->common.events = tevent; - break; case 'h': case '?': timerlat_top_usage(); break; - case 'H': - params->common.hk_cpus = 1; - retval = parse_cpu_set(optarg, ¶ms->common.hk_cpu_set); - if (retval) - fatal("Error parsing house keeping CPUs"); - break; case 'i': params->common.stop_us = get_llong_from_str(optarg); break; @@ -686,12 +634,6 @@ static struct common_params if (params->timerlat_period_us > 1000000) fatal("Period longer than 1 s"); break; - case 'P': - retval = parse_prio(optarg, ¶ms->common.sched_param); - if (retval == -1) - fatal("Invalid -P priority"); - params->common.set_sched = 1; - break; case 'q': params->common.quiet = 1; break; @@ -713,22 +655,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); @@ -762,6 +698,14 @@ static struct common_params if (retval) fatal("Invalid action %s", optarg); break; + 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"); } @@ -827,15 +771,12 @@ 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) return NULL; - top->data = timerlat_alloc_top(nr_cpus); + top->data = timerlat_alloc_top(); if (!top->data) goto out_err; @@ -855,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; @@ -866,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) { @@ -875,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; } diff --git a/tools/tracing/rtla/src/timerlat_u.c b/tools/tracing/rtla/src/timerlat_u.c index ce68e39d25fd..c80edaf07b00 100644 --- a/tools/tracing/rtla/src/timerlat_u.c +++ b/tools/tracing/rtla/src/timerlat_u.c @@ -16,7 +16,7 @@ #include <sys/wait.h> #include <sys/prctl.h> -#include "utils.h" +#include "common.h" #include "timerlat_u.h" /* @@ -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; } @@ -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; @@ -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; @@ -170,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); } @@ -197,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); 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 <bristot@kernel.org> */ diff --git a/tools/tracing/rtla/src/trace.c b/tools/tracing/rtla/src/trace.c index 69cbc48d53d3..e407447773d0 100644 --- a/tools/tracing/rtla/src/trace.c +++ b/tools/tracing/rtla/src/trace.c @@ -2,7 +2,6 @@ #define _GNU_SOURCE #include <sys/sendfile.h> #include <tracefs.h> -#include <signal.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> @@ -74,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; @@ -91,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) - goto out_close; - - retval = write(out_fd, buffer, retval); - 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; - } while (retval > 0); + } + if (n_read == 0) + break; + + 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: @@ -192,9 +208,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); @@ -275,15 +289,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) { @@ -297,31 +305,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); } /* @@ -330,7 +330,7 @@ int 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) @@ -342,7 +342,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); @@ -359,10 +359,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 retval, index, out_fd; + size_t index, hist_len; mode_t mode = 0644; - char path[1024]; + char path[MAX_PATH]; char *hist; + int out_fd; if (!tevent) return; @@ -372,11 +373,10 @@ 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, 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); @@ -393,9 +393,18 @@ 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)); + 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); out_close: @@ -408,7 +417,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) @@ -422,7 +431,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); @@ -461,7 +470,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) @@ -473,7 +482,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); @@ -496,7 +505,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) @@ -508,7 +517,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); 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 9cf5a0098e9a..9cec5b3e02c8 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -17,8 +17,9 @@ #include <fcntl.h> #include <sched.h> #include <stdio.h> +#include <limits.h> -#include "utils.h" +#include "common.h" #define MAX_MSG_LENGTH 1024 int config_debug; @@ -112,20 +113,17 @@ void get_duration(time_t start_time, char *output, int output_size) * Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set * filling cpu_set_t argument. * - * Returns 1 on success, 0 otherwise. + * Returns 0 on success, 1 otherwise. */ 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) @@ -164,6 +162,24 @@ err: } /* + * 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 */ long parse_seconds_duration(char *val) @@ -198,6 +214,21 @@ long parse_seconds_duration(char *val) } /* + * 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 */ long parse_ns_duration(char *val) @@ -208,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; } @@ -293,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; } @@ -314,8 +345,8 @@ static int procfs_is_workload_pid(const char *comm_prefix, struct dirent *proc_e if (retval <= 0) return 0; - retval = strncmp(comm_prefix, buffer, strlen(comm_prefix)); - if (retval) + buffer[MAX_PATH-1] = '\0'; + if (!str_has_prefix(buffer, comm_prefix)) return 0; /* comm already have \n */ @@ -337,6 +368,7 @@ int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr) struct dirent *proc_entry; DIR *procfs; int retval; + int pid; if (strlen(comm_prefix) >= MAX_PATH) { err_msg("Command prefix is too long: %d < strlen(%s)\n", @@ -356,20 +388,25 @@ int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr) if (!retval) continue; + if (strtoi(proc_entry->d_name, &pid)) { + err_msg("'%s' is not a valid pid", proc_entry->d_name); + retval = 1; + goto out; + } /* procfs_is_workload_pid confirmed it is a pid */ - retval = __set_sched_attr(atoi(proc_entry->d_name), attr); + 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) @@ -552,7 +589,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); @@ -560,7 +596,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; @@ -637,13 +672,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; @@ -742,6 +774,7 @@ static int get_self_cgroup(char *self_cg, int sizeof_self_cg) if (fd < 0) return 0; + memset(path, 0, sizeof(path)); retval = read(fd, path, MAX_PATH); close(fd); @@ -749,6 +782,7 @@ static int get_self_cgroup(char *self_cg, int sizeof_self_cg) if (retval <= 0) return 0; + path[MAX_PATH-1] = '\0'; start = path; start = strstr(start, ":"); @@ -784,39 +818,42 @@ static int get_self_cgroup(char *self_cg, int sizeof_self_cg) } /* - * set_comm_cgroup - Set cgroup to pid_t pid + * open_cgroup_procs - Open the cgroup.procs file for the given cgroup * - * If cgroup argument is not NULL, the threads will move to the given cgroup. - * Otherwise, the cgroup of the calling, i.e., rtla, thread will be used. + * If cgroup argument is not NULL, the cgroup.procs file for that cgroup + * will be opened. Otherwise, the cgroup of the calling, i.e., rtla, thread + * will be used. * * Supports cgroup v2. * - * Returns 1 on success, 0 otherwise. + * Returns the file descriptor on success, -1 otherwise. */ -int set_pid_cgroup(pid_t pid, const char *cgroup) +static int open_cgroup_procs(const char *cgroup) { char cgroup_path[MAX_PATH - strlen("/cgroup.procs")]; char cgroup_procs[MAX_PATH]; - char pid_str[24]; int retval; int cg_fd; + size_t cg_path_len; retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path)); if (!retval) { err_msg("Did not find cgroupv2 mount point\n"); - return 0; + 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 0; + 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); @@ -825,6 +862,29 @@ int set_pid_cgroup(pid_t pid, const char *cgroup) cg_fd = open(cgroup_procs, O_RDWR); if (cg_fd < 0) + return -1; + + return cg_fd; +} + +/* + * set_pid_cgroup - Set cgroup to pid_t pid + * + * If cgroup argument is not NULL, the threads will move to the given cgroup. + * Otherwise, the cgroup of the calling, i.e., rtla, thread will be used. + * + * Supports cgroup v2. + * + * Returns 1 on success, 0 otherwise. + */ +int set_pid_cgroup(pid_t pid, const char *cgroup) +{ + char pid_str[24]; + int retval; + int cg_fd; + + cg_fd = open_cgroup_procs(cgroup); + if (cg_fd < 0) return 0; snprintf(pid_str, sizeof(pid_str), "%d\n", pid); @@ -853,8 +913,6 @@ int set_pid_cgroup(pid_t pid, const char *cgroup) */ int set_comm_cgroup(const char *comm_prefix, const char *cgroup) { - char cgroup_path[MAX_PATH - strlen("/cgroup.procs")]; - char cgroup_procs[MAX_PATH]; struct dirent *proc_entry; DIR *procfs; int retval; @@ -866,29 +924,7 @@ int set_comm_cgroup(const char *comm_prefix, const char *cgroup) return 0; } - retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path)); - if (!retval) { - err_msg("Did not find cgroupv2 mount point\n"); - return 0; - } - - if (!cgroup) { - retval = get_self_cgroup(&cgroup_path[strlen(cgroup_path)], - sizeof(cgroup_path) - strlen(cgroup_path)); - if (!retval) { - err_msg("Did not find self cgroup\n"); - return 0; - } - } else { - snprintf(&cgroup_path[strlen(cgroup_path)], - sizeof(cgroup_path) - strlen(cgroup_path), "%s/", cgroup); - } - - snprintf(cgroup_procs, MAX_PATH, "%s/cgroup.procs", cgroup_path); - - debug_msg("Using cgroup path at: %s\n", cgroup_procs); - - cg_fd = open(cgroup_procs, O_RDWR); + cg_fd = open_cgroup_procs(cgroup); if (cg_fd < 0) return 0; @@ -1000,3 +1036,60 @@ char *parse_optional_arg(int argc, char **argv) return NULL; } } + +/* + * strtoi - convert string to integer with error checking + * + * Returns 0 on success, -1 if conversion fails or result is out of int range. + */ +int strtoi(const char *s, int *res) +{ + char *end_ptr; + long lres; + + if (!*s) + return -1; + + errno = 0; + lres = strtol(s, &end_ptr, 0); + if (errno || *end_ptr || lres > INT_MAX || lres < INT_MIN) + return -1; + + *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 091df4ba4587..e794ede64b2c 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h @@ -1,8 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 #include <stdint.h> +#include <string.h> #include <time.h> #include <sched.h> +#include <stdbool.h> +#include <stdlib.h> /* * '18446744073709551615\0' @@ -12,6 +15,28 @@ #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)) + +/** + * 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)) ; }) @@ -24,7 +49,6 @@ void fatal(const char *fmt, ...); long parse_seconds_duration(char *val); void get_duration(time_t start_time, char *output, int output_size); -int parse_cpu_list(char *cpu_list, char **monitored_cpus); char *parse_optional_arg(int argc, char **argv); long long get_llong_from_str(char *start); @@ -61,13 +85,23 @@ 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); 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); @@ -82,12 +116,13 @@ static inline int set_deepest_cpu_idle_state(unsigned int cpu, unsigned int stat 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); #define ns_to_usf(x) (((double)x/1000)) #define ns_to_per(total, part) ((part * 100) / (double)total) enum result { - PASSED = 0, /* same as EXIT_SUCCESS */ - ERROR = 1, /* same as EXIT_FAILURE, an error in arguments */ - FAILED = 2, /* test hit the stop tracing condition */ + PASSED = EXIT_SUCCESS, + ERROR = EXIT_FAILURE, + FAILED, /* test hit the stop tracing condition */ }; |
