diff options
Diffstat (limited to 'tools/tracing/rtla')
-rw-r--r-- | tools/tracing/rtla/src/Build | 1 | ||||
-rw-r--r-- | tools/tracing/rtla/src/actions.c | 12 | ||||
-rw-r--r-- | tools/tracing/rtla/src/actions.h | 2 | ||||
-rw-r--r-- | tools/tracing/rtla/src/common.c | 344 | ||||
-rw-r--r-- | tools/tracing/rtla/src/common.h | 154 | ||||
-rw-r--r-- | tools/tracing/rtla/src/osnoise.c | 101 | ||||
-rw-r--r-- | tools/tracing/rtla/src/osnoise.h | 114 | ||||
-rw-r--r-- | tools/tracing/rtla/src/osnoise_hist.c | 369 | ||||
-rw-r--r-- | tools/tracing/rtla/src/osnoise_top.c | 303 | ||||
-rw-r--r-- | tools/tracing/rtla/src/timerlat.c | 210 | ||||
-rw-r--r-- | tools/tracing/rtla/src/timerlat.h | 55 | ||||
-rw-r--r-- | tools/tracing/rtla/src/timerlat_bpf.c | 22 | ||||
-rw-r--r-- | tools/tracing/rtla/src/timerlat_hist.c | 642 | ||||
-rw-r--r-- | tools/tracing/rtla/src/timerlat_top.c | 568 | ||||
-rw-r--r-- | tools/tracing/rtla/src/trace.h | 3 | ||||
-rw-r--r-- | tools/tracing/rtla/tests/engine.sh | 26 | ||||
-rw-r--r-- | tools/tracing/rtla/tests/osnoise.t | 27 | ||||
-rw-r--r-- | tools/tracing/rtla/tests/timerlat.t | 4 |
18 files changed, 1308 insertions, 1649 deletions
diff --git a/tools/tracing/rtla/src/Build b/tools/tracing/rtla/src/Build index 66631280b75b..329e24a40cf7 100644 --- a/tools/tracing/rtla/src/Build +++ b/tools/tracing/rtla/src/Build @@ -1,6 +1,7 @@ rtla-y += trace.o rtla-y += utils.o rtla-y += actions.o +rtla-y += common.o rtla-y += osnoise.o rtla-y += osnoise_top.o rtla-y += osnoise_hist.o diff --git a/tools/tracing/rtla/src/actions.c b/tools/tracing/rtla/src/actions.c index aaf0808125d7..8945aee58d51 100644 --- a/tools/tracing/rtla/src/actions.c +++ b/tools/tracing/rtla/src/actions.c @@ -49,7 +49,7 @@ actions_destroy(struct actions *self) static struct action * actions_new(struct actions *self) { - if (self->size >= self->len) { + if (self->len >= self->size) { self->size *= 2; self->list = realloc(self->list, self->size * sizeof(struct action)); } @@ -127,17 +127,17 @@ actions_add_continue(struct actions *self) * actions_parse - add an action based on text specification */ int -actions_parse(struct actions *self, const char *trigger) +actions_parse(struct actions *self, const char *trigger, const char *tracefn) { enum action_type type = ACTION_NONE; - char *token; - char trigger_c[strlen(trigger)]; + const char *token; + char trigger_c[strlen(trigger) + 1]; /* For ACTION_SIGNAL */ int signal = 0, pid = 0; /* For ACTION_TRACE_OUTPUT */ - char *trace_output; + const char *trace_output; strcpy(trigger_c, trigger); token = strtok(trigger_c, ","); @@ -160,7 +160,7 @@ actions_parse(struct actions *self, const char *trigger) case ACTION_TRACE_OUTPUT: /* Takes no argument */ if (token == NULL) - trace_output = "timerlat_trace.txt"; + trace_output = tracefn; else { if (strlen(token) > 5 && strncmp(token, "file=", 5) == 0) { trace_output = token + 5; diff --git a/tools/tracing/rtla/src/actions.h b/tools/tracing/rtla/src/actions.h index b10a19d55c49..a4f9b570775b 100644 --- a/tools/tracing/rtla/src/actions.h +++ b/tools/tracing/rtla/src/actions.h @@ -48,5 +48,5 @@ 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); -int actions_parse(struct actions *self, const char *trigger); +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 new file mode 100644 index 000000000000..2e6e3dac1897 --- /dev/null +++ b/tools/tracing/rtla/src/common.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE + +#include <pthread.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include "common.h" + +struct trace_instance *trace_inst; +int stop_tracing; + +static void stop_trace(int sig) +{ + if (stop_tracing) { + /* + * Stop requested twice in a row; abort event processing and + * exit immediately + */ + tracefs_iterate_stop(trace_inst->inst); + return; + } + stop_tracing = 1; + if (trace_inst) + trace_instance_stop(trace_inst); +} + +/* + * set_signals - handles the signal to stop the tool + */ +static void set_signals(struct common_params *params) +{ + signal(SIGINT, stop_trace); + if (params->duration) { + signal(SIGALRM, stop_trace); + alarm(params->duration); + } +} + +/* + * common_apply_config - apply common configs to the initialized tool + */ +int +common_apply_config(struct osnoise_tool *tool, struct common_params *params) +{ + int retval, i; + + if (!params->sleep_time) + params->sleep_time = 1; + + retval = osnoise_set_cpus(tool->context, params->cpus ? params->cpus : "all"); + if (retval) { + err_msg("Failed to apply CPUs config\n"); + goto out_err; + } + + if (!params->cpus) { + for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++) + CPU_SET(i, ¶ms->monitored_cpus); + } + + if (params->hk_cpus) { + retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set), + ¶ms->hk_cpu_set); + if (retval == -1) { + err_msg("Failed to set rtla to the house keeping CPUs\n"); + goto out_err; + } + } else if (params->cpus) { + /* + * Even if the user do not set a house-keeping CPU, try to + * move rtla to a CPU set different to the one where the user + * set the workload to run. + * + * No need to check results as this is an automatic attempt. + */ + auto_house_keeping(¶ms->monitored_cpus); + } + + /* + * Set workload according to type of thread if the kernel supports it. + * On kernels without support, user threads will have already failed + * on missing fd, and kernel threads do not need it. + */ + retval = osnoise_set_workload(tool->context, params->kernel_workload); + if (retval < -1) { + err_msg("Failed to set OSNOISE_WORKLOAD option\n"); + goto out_err; + } + + return 0; + +out_err: + return -1; +} + + +int run_tool(struct tool_ops *ops, int argc, char *argv[]) +{ + struct common_params *params; + enum result return_value = ERROR; + struct osnoise_tool *tool; + bool stopped; + int retval; + + params = ops->parse_args(argc, argv); + if (!params) + exit(1); + + tool = ops->init_tool(params); + if (!tool) { + err_msg("Could not init osnoise tool\n"); + goto out_exit; + } + tool->ops = ops; + tool->params = params; + + /* + * Save trace instance into global variable so that SIGINT can stop + * the timerlat tracer. + * Otherwise, rtla could loop indefinitely when overloaded. + */ + trace_inst = &tool->trace; + + retval = ops->apply_config(tool); + if (retval) { + err_msg("Could not apply config\n"); + goto out_free; + } + + retval = enable_tracer_by_name(trace_inst->inst, ops->tracer); + if (retval) { + err_msg("Failed to enable %s tracer\n", ops->tracer); + goto out_free; + } + + if (params->set_sched) { + retval = set_comm_sched_attr(ops->comm_prefix, ¶ms->sched_param); + if (retval) { + err_msg("Failed to set sched parameters\n"); + goto out_free; + } + } + + if (params->cgroup && !params->user_data) { + retval = set_comm_cgroup(ops->comm_prefix, params->cgroup_name); + if (!retval) { + err_msg("Failed to move threads to cgroup\n"); + goto out_free; + } + } + + + if (params->threshold_actions.present[ACTION_TRACE_OUTPUT] || + params->end_actions.present[ACTION_TRACE_OUTPUT]) { + tool->record = osnoise_init_trace_tool(ops->tracer); + if (!tool->record) { + err_msg("Failed to enable the trace instance\n"); + goto out_free; + } + params->threshold_actions.trace_output_inst = tool->record->trace.inst; + params->end_actions.trace_output_inst = tool->record->trace.inst; + + if (params->events) { + retval = trace_events_enable(&tool->record->trace, params->events); + if (retval) + goto out_trace; + } + + if (params->buffer_size > 0) { + retval = trace_set_buffer_size(&tool->record->trace, params->buffer_size); + if (retval) + goto out_trace; + } + } + + if (params->user_workload) { + pthread_t user_thread; + + /* rtla asked to stop */ + params->user.should_run = 1; + /* all threads left */ + params->user.stopped_running = 0; + + params->user.set = ¶ms->monitored_cpus; + if (params->set_sched) + params->user.sched_param = ¶ms->sched_param; + else + params->user.sched_param = NULL; + + params->user.cgroup_name = params->cgroup_name; + + retval = pthread_create(&user_thread, NULL, timerlat_u_dispatcher, ¶ms->user); + if (retval) + err_msg("Error creating timerlat user-space threads\n"); + } + + retval = ops->enable(tool); + if (retval) + goto out_trace; + + tool->start_time = time(NULL); + set_signals(params); + + retval = ops->main(tool); + if (retval) + goto out_trace; + + if (params->user_workload && !params->user.stopped_running) { + params->user.should_run = 0; + sleep(1); + } + + ops->print_stats(tool); + + actions_perform(¶ms->end_actions); + + return_value = PASSED; + + stopped = osnoise_trace_is_off(tool, tool->record) && !stop_tracing; + if (stopped) { + printf("%s hit stop tracing\n", ops->tracer); + return_value = FAILED; + } + + if (ops->analyze) + ops->analyze(tool, stopped); + +out_trace: + trace_events_destroy(&tool->record->trace, params->events); + params->events = NULL; +out_free: + ops->free(tool); + osnoise_destroy_tool(tool->record); + osnoise_destroy_tool(tool); + actions_destroy(¶ms->threshold_actions); + actions_destroy(¶ms->end_actions); + free(params); +out_exit: + exit(return_value); +} + +int top_main_loop(struct osnoise_tool *tool) +{ + struct common_params *params = tool->params; + struct trace_instance *trace = &tool->trace; + struct osnoise_tool *record = tool->record; + int retval; + + while (!stop_tracing) { + sleep(params->sleep_time); + + if (params->aa_only && !osnoise_trace_is_off(tool, record)) + continue; + + retval = tracefs_iterate_raw_events(trace->tep, + trace->inst, + NULL, + 0, + collect_registered_events, + trace); + if (retval < 0) { + err_msg("Error iterating on events\n"); + return retval; + } + + if (!params->quiet) + tool->ops->print_stats(tool); + + if (osnoise_trace_is_off(tool, record)) { + actions_perform(¶ms->threshold_actions); + + if (!params->threshold_actions.continue_flag) + /* continue flag not set, break */ + 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); + } + + /* is there still any user-threads ? */ + if (params->user_workload) { + if (params->user.stopped_running) { + debug_msg("timerlat user space threads stopped!\n"); + break; + } + } + } + + return 0; +} + +int hist_main_loop(struct osnoise_tool *tool) +{ + struct common_params *params = tool->params; + struct trace_instance *trace = &tool->trace; + int retval = 0; + + while (!stop_tracing) { + sleep(params->sleep_time); + + retval = tracefs_iterate_raw_events(trace->tep, + trace->inst, + NULL, + 0, + collect_registered_events, + trace); + if (retval < 0) { + err_msg("Error iterating on events\n"); + break; + } + + if (osnoise_trace_is_off(tool, tool->record)) { + actions_perform(¶ms->threshold_actions); + + if (!params->threshold_actions.continue_flag) { + /* continue flag not set, break */ + 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); + trace_instance_start(&tool->trace); + } + break; + } + + /* is there still any user-threads ? */ + if (params->user_workload) { + if (params->user.stopped_running) { + debug_msg("user-space threads stopped!\n"); + break; + } + } + } + + return retval; +} diff --git a/tools/tracing/rtla/src/common.h b/tools/tracing/rtla/src/common.h new file mode 100644 index 000000000000..355f113a14a3 --- /dev/null +++ b/tools/tracing/rtla/src/common.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#pragma once + +#include "actions.h" +#include "timerlat_u.h" +#include "trace.h" +#include "utils.h" + +/* + * osnoise_context - read, store, write, restore osnoise configs. + */ +struct osnoise_context { + int flags; + int ref; + + char *curr_cpus; + char *orig_cpus; + + /* 0 as init value */ + unsigned long long orig_runtime_us; + unsigned long long runtime_us; + + /* 0 as init value */ + unsigned long long orig_period_us; + unsigned long long period_us; + + /* 0 as init value */ + long long orig_timerlat_period_us; + long long timerlat_period_us; + + /* 0 as init value */ + long long orig_tracing_thresh; + long long tracing_thresh; + + /* -1 as init value because 0 is disabled */ + long long orig_stop_us; + long long stop_us; + + /* -1 as init value because 0 is disabled */ + long long orig_stop_total_us; + long long stop_total_us; + + /* -1 as init value because 0 is disabled */ + long long orig_print_stack; + long long print_stack; + + /* -1 as init value because 0 is off */ + int orig_opt_irq_disable; + int opt_irq_disable; + + /* -1 as init value because 0 is off */ + int orig_opt_workload; + int opt_workload; +}; + +extern struct trace_instance *trace_inst; +extern int stop_tracing; + +struct hist_params { + char no_irq; + char no_thread; + char no_header; + char no_summary; + char no_index; + char with_zeros; + int bucket_size; + int entries; +}; + +/* + * common_params - Parameters shared between timerlat_params and osnoise_params + */ +struct common_params { + /* trace configuration */ + char *cpus; + cpu_set_t monitored_cpus; + struct trace_events *events; + int buffer_size; + + /* Timing parameters */ + int warmup; + long long stop_us; + long long stop_total_us; + int sleep_time; + int duration; + + /* Scheduling parameters */ + int set_sched; + struct sched_attr sched_param; + int cgroup; + char *cgroup_name; + int hk_cpus; + cpu_set_t hk_cpu_set; + + /* Other parameters */ + struct hist_params hist; + int output_divisor; + int pretty_output; + int quiet; + int user_workload; + int kernel_workload; + int user_data; + int aa_only; + + struct actions threshold_actions; + struct actions end_actions; + struct timerlat_u_params user; +}; + +struct tool_ops; + +/* + * osnoise_tool - osnoise based tool definition. + * + * Only the "trace" and "context" fields are used for + * the additional trace instances (record and aa). + */ +struct osnoise_tool { + struct tool_ops *ops; + struct trace_instance trace; + struct osnoise_context *context; + void *data; + struct common_params *params; + time_t start_time; + struct osnoise_tool *record; + struct osnoise_tool *aa; +}; + +struct tool_ops { + const char *tracer; + const char *comm_prefix; + struct common_params *(*parse_args)(int argc, char *argv[]); + struct osnoise_tool *(*init_tool)(struct common_params *params); + int (*apply_config)(struct osnoise_tool *tool); + int (*enable)(struct osnoise_tool *tool); + int (*main)(struct osnoise_tool *tool); + void (*print_stats)(struct osnoise_tool *tool); + void (*analyze)(struct osnoise_tool *tool, bool stopped); + void (*free)(struct osnoise_tool *tool); +}; + +int osnoise_set_cpus(struct osnoise_context *context, char *cpus); +void osnoise_restore_cpus(struct osnoise_context *context); + +int osnoise_set_workload(struct osnoise_context *context, bool onoff); + +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 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); diff --git a/tools/tracing/rtla/src/osnoise.c b/tools/tracing/rtla/src/osnoise.c index 2dc3e4539e99..312c511fa004 100644 --- a/tools/tracing/rtla/src/osnoise.c +++ b/tools/tracing/rtla/src/osnoise.c @@ -906,22 +906,6 @@ static void osnoise_put_workload(struct osnoise_context *context) context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL; } -/* - * enable_osnoise - enable osnoise tracer in the trace_instance - */ -int enable_osnoise(struct trace_instance *trace) -{ - return enable_tracer_by_name(trace->inst, "osnoise"); -} - -/* - * enable_timerlat - enable timerlat tracer in the trace_instance - */ -int enable_timerlat(struct trace_instance *trace) -{ - return enable_tracer_by_name(trace->inst, "timerlat"); -} - enum { FLAG_CONTEXT_NEWLY_CREATED = (1 << 0), FLAG_CONTEXT_DELETED = (1 << 1), @@ -1056,7 +1040,7 @@ out_err: /* * osnoise_init_trace_tool - init a tracer instance to trace osnoise events */ -struct osnoise_tool *osnoise_init_trace_tool(char *tracer) +struct osnoise_tool *osnoise_init_trace_tool(const char *tracer) { struct osnoise_tool *trace; int retval; @@ -1120,21 +1104,14 @@ osnoise_report_missed_events(struct osnoise_tool *tool) } /* - * osnoise_apply_config - apply common configs to the initialized tool + * osnoise_apply_config - apply osnoise configs to the initialized tool */ int osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params) { int retval; - if (!params->sleep_time) - params->sleep_time = 1; - - retval = osnoise_set_cpus(tool->context, params->cpus ? params->cpus : "all"); - if (retval) { - err_msg("Failed to apply CPUs config\n"); - goto out_err; - } + params->common.kernel_workload = true; if (params->runtime || params->period) { retval = osnoise_set_runtime_period(tool->context, @@ -1151,13 +1128,13 @@ osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params) goto out_err; } - retval = osnoise_set_stop_us(tool->context, params->stop_us); + 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->stop_total_us); + 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; @@ -1169,34 +1146,48 @@ osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params) goto out_err; } - if (params->hk_cpus) { - retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set), - ¶ms->hk_cpu_set); - if (retval == -1) { - err_msg("Failed to set rtla to the house keeping CPUs\n"); - goto out_err; - } - } else if (params->cpus) { + return common_apply_config(tool, ¶ms->common); + +out_err: + return -1; +} + +int osnoise_enable(struct osnoise_tool *tool) +{ + struct osnoise_params *params = to_osnoise_params(tool->params); + int retval; + + /* + * Start the tracer here, after having set all instances. + * + * Let the trace instance start first for the case of hitting a stop + * tracing while enabling other instances. The trace instance is the + * one with most valuable information. + */ + if (tool->record) + trace_instance_start(&tool->record->trace); + trace_instance_start(&tool->trace); + + if (params->common.warmup > 0) { + debug_msg("Warming up for %d seconds\n", params->common.warmup); + sleep(params->common.warmup); + if (stop_tracing) + return -1; + /* - * Even if the user do not set a house-keeping CPU, try to - * move rtla to a CPU set different to the one where the user - * set the workload to run. - * - * No need to check results as this is an automatic attempt. + * Clean up the buffer. The osnoise workload do not run + * with tracing off to avoid creating a performance penalty + * when not needed. */ - auto_house_keeping(¶ms->monitored_cpus); - } + retval = tracefs_instance_file_write(tool->trace.inst, "trace", ""); + if (retval < 0) { + debug_msg("Error cleaning up the buffer"); + return retval; + } - retval = osnoise_set_workload(tool->context, true); - if (retval < -1) { - err_msg("Failed to set OSNOISE_WORKLOAD option\n"); - goto out_err; } return 0; - -out_err: - return -1; } static void osnoise_usage(int err) @@ -1232,7 +1223,7 @@ int osnoise_main(int argc, char *argv[]) * default cmdline. */ if (argc == 1) { - osnoise_top_main(argc, argv); + run_tool(&osnoise_top_ops, argc, argv); exit(0); } @@ -1240,13 +1231,13 @@ int osnoise_main(int argc, char *argv[]) osnoise_usage(0); } else if (strncmp(argv[1], "-", 1) == 0) { /* the user skipped the tool, call the default one */ - osnoise_top_main(argc, argv); + run_tool(&osnoise_top_ops, argc, argv); exit(0); } else if (strcmp(argv[1], "top") == 0) { - osnoise_top_main(argc-1, &argv[1]); + run_tool(&osnoise_top_ops, argc-1, &argv[1]); exit(0); } else if (strcmp(argv[1], "hist") == 0) { - osnoise_hist_main(argc-1, &argv[1]); + run_tool(&osnoise_hist_ops, argc-1, &argv[1]); exit(0); } @@ -1257,6 +1248,6 @@ usage: int hwnoise_main(int argc, char *argv[]) { - osnoise_top_main(argc, argv); + 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 ac1c99910744..895687030c0b 100644 --- a/tools/tracing/rtla/src/osnoise.h +++ b/tools/tracing/rtla/src/osnoise.h @@ -1,8 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #pragma once -#include "utils.h" -#include "trace.h" +#include "common.h" enum osnoise_mode { MODE_OSNOISE = 0, @@ -10,92 +9,14 @@ enum osnoise_mode { }; struct osnoise_params { - /* Common params */ - char *cpus; - cpu_set_t monitored_cpus; - char *trace_output; - char *cgroup_name; + struct common_params common; unsigned long long runtime; unsigned long long period; long long threshold; - long long stop_us; - long long stop_total_us; - int sleep_time; - int duration; - int set_sched; - int cgroup; - int hk_cpus; - cpu_set_t hk_cpu_set; - struct sched_attr sched_param; - struct trace_events *events; - int warmup; - int buffer_size; - union { - struct { - /* top only */ - int quiet; - int pretty_output; - enum osnoise_mode mode; - }; - struct { - /* hist only */ - int output_divisor; - char no_header; - char no_summary; - char no_index; - char with_zeros; - int bucket_size; - int entries; - }; - }; + enum osnoise_mode mode; }; -/* - * osnoise_context - read, store, write, restore osnoise configs. - */ -struct osnoise_context { - int flags; - int ref; - - char *curr_cpus; - char *orig_cpus; - - /* 0 as init value */ - unsigned long long orig_runtime_us; - unsigned long long runtime_us; - - /* 0 as init value */ - unsigned long long orig_period_us; - unsigned long long period_us; - - /* 0 as init value */ - long long orig_timerlat_period_us; - long long timerlat_period_us; - - /* 0 as init value */ - long long orig_tracing_thresh; - long long tracing_thresh; - - /* -1 as init value because 0 is disabled */ - long long orig_stop_us; - long long stop_us; - - /* -1 as init value because 0 is disabled */ - long long orig_stop_total_us; - long long stop_total_us; - - /* -1 as init value because 0 is disabled */ - long long orig_print_stack; - long long print_stack; - - /* -1 as init value because 0 is off */ - int orig_opt_irq_disable; - int opt_irq_disable; - - /* -1 as init value because 0 is off */ - int orig_opt_workload; - int opt_workload; -}; +#define to_osnoise_params(ptr) container_of(ptr, struct osnoise_params, common) /* * *_INIT_VALs are also invalid values, they are used to @@ -108,9 +29,6 @@ struct osnoise_context *osnoise_context_alloc(void); int osnoise_get_context(struct osnoise_context *context); void osnoise_put_context(struct osnoise_context *context); -int osnoise_set_cpus(struct osnoise_context *context, char *cpus); -void osnoise_restore_cpus(struct osnoise_context *context); - int osnoise_set_runtime_period(struct osnoise_context *context, unsigned long long runtime, unsigned long long period); @@ -137,27 +55,17 @@ int osnoise_set_print_stack(struct osnoise_context *context, long long print_stack); int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff); -int osnoise_set_workload(struct osnoise_context *context, bool onoff); - -/* - * osnoise_tool - osnoise based tool definition. - */ -struct osnoise_tool { - struct trace_instance trace; - struct osnoise_context *context; - void *data; - void *params; - time_t start_time; -}; - -void osnoise_destroy_tool(struct osnoise_tool *top); -struct osnoise_tool *osnoise_init_tool(char *tool_name); -struct osnoise_tool *osnoise_init_trace_tool(char *tracer); void osnoise_report_missed_events(struct osnoise_tool *tool); -bool osnoise_trace_is_off(struct osnoise_tool *tool, struct osnoise_tool *record); 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); + +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 8d579bcee709..dffb6d0a98d7 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c @@ -54,6 +54,11 @@ osnoise_free_histogram(struct osnoise_hist_data *data) free(data); } +static void osnoise_free_hist_tool(struct osnoise_tool *tool) +{ + osnoise_free_histogram(tool->data); +} + /* * osnoise_alloc_histogram - alloc runtime data */ @@ -95,15 +100,15 @@ cleanup: static void osnoise_hist_update_multiple(struct osnoise_tool *tool, int cpu, unsigned long long duration, int count) { - struct osnoise_params *params = tool->params; + struct osnoise_params *params = to_osnoise_params(tool->params); struct osnoise_hist_data *data = tool->data; unsigned long long total_duration; int entries = data->entries; int bucket; int *hist; - if (params->output_divisor) - duration = duration / params->output_divisor; + if (params->common.output_divisor) + duration = duration / params->common.output_divisor; bucket = duration / data->bucket_size; @@ -137,7 +142,7 @@ static void osnoise_destroy_trace_hist(struct osnoise_tool *tool) */ static int osnoise_init_trace_hist(struct osnoise_tool *tool) { - struct osnoise_params *params = tool->params; + struct osnoise_params *params = to_osnoise_params(tool->params); struct osnoise_hist_data *data = tool->data; int bucket_size; char buff[128]; @@ -146,7 +151,7 @@ static int osnoise_init_trace_hist(struct osnoise_tool *tool) /* * Set the size of the bucket. */ - bucket_size = params->output_divisor * params->bucket_size; + bucket_size = params->common.output_divisor * params->common.hist.bucket_size; snprintf(buff, sizeof(buff), "duration.buckets=%d", bucket_size); data->trace_hist = tracefs_hist_alloc(tool->trace.tep, "osnoise", "sample_threshold", @@ -222,28 +227,28 @@ static void osnoise_read_trace_hist(struct osnoise_tool *tool) */ static void osnoise_hist_header(struct osnoise_tool *tool) { - struct osnoise_params *params = tool->params; + struct osnoise_params *params = to_osnoise_params(tool->params); struct osnoise_hist_data *data = tool->data; struct trace_seq *s = tool->trace.seq; char duration[26]; int cpu; - if (params->no_header) + if (params->common.hist.no_header) return; get_duration(tool->start_time, duration, sizeof(duration)); trace_seq_printf(s, "# RTLA osnoise histogram\n"); trace_seq_printf(s, "# Time unit is %s (%s)\n", - params->output_divisor == 1 ? "nanoseconds" : "microseconds", - params->output_divisor == 1 ? "ns" : "us"); + params->common.output_divisor == 1 ? "nanoseconds" : "microseconds", + params->common.output_divisor == 1 ? "ns" : "us"); trace_seq_printf(s, "# Duration: %s\n", duration); - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(s, "Index"); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].count) @@ -267,14 +272,14 @@ osnoise_print_summary(struct osnoise_params *params, { int cpu; - if (params->no_summary) + if (params->common.hist.no_summary) return; - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "count:"); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].count) @@ -284,11 +289,11 @@ osnoise_print_summary(struct osnoise_params *params, } trace_seq_printf(trace->seq, "\n"); - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "min: "); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].count) @@ -299,11 +304,11 @@ osnoise_print_summary(struct osnoise_params *params, } trace_seq_printf(trace->seq, "\n"); - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "avg: "); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].count) @@ -317,11 +322,11 @@ osnoise_print_summary(struct osnoise_params *params, } trace_seq_printf(trace->seq, "\n"); - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "max: "); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].count) @@ -339,8 +344,9 @@ osnoise_print_summary(struct osnoise_params *params, * osnoise_print_stats - print data for all CPUs */ static void -osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *tool) +osnoise_print_stats(struct osnoise_tool *tool) { + struct osnoise_params *params = to_osnoise_params(tool->params); struct osnoise_hist_data *data = tool->data; struct trace_instance *trace = &tool->trace; int has_samples = 0; @@ -352,12 +358,12 @@ osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *tool) for (bucket = 0; bucket < data->entries; bucket++) { total = 0; - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "%-6d", bucket * data->bucket_size); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].count) @@ -367,7 +373,7 @@ osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *tool) trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].samples[bucket]); } - if (total == 0 && !params->with_zeros) { + if (total == 0 && !params->common.hist.with_zeros) { trace_seq_reset(trace->seq); continue; } @@ -391,11 +397,11 @@ osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *tool) return; } - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "over: "); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].count) @@ -456,6 +462,8 @@ static void osnoise_hist_usage(char *usage) " in nanoseconds", " --warm-up: let the workload run for s seconds before collecting data", " --trace-buffer-size kB: set the per-cpu trace buffer size in kB", + " --on-threshold <action>: define action to be executed at stop-total threshold, multiple are allowed", + " --on-end <action>: define action to be executed at measurement end, multiple are allowed", NULL, }; @@ -477,22 +485,26 @@ static void osnoise_hist_usage(char *usage) /* * osnoise_hist_parse_args - allocs, parse and fill the cmd line parameters */ -static struct osnoise_params +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); + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); + /* display data in microseconds */ - params->output_divisor = 1000; - params->bucket_size = 1; - params->entries = 256; + params->common.output_divisor = 1000; + params->common.hist.bucket_size = 1; + params->common.hist.entries = 256; while (1) { static struct option long_options[] = { @@ -521,6 +533,8 @@ static struct osnoise_params {"filter", required_argument, 0, '5'}, {"warm-up", required_argument, 0, '6'}, {"trace-buffer-size", required_argument, 0, '7'}, + {"on-threshold", required_argument, 0, '8'}, + {"on-end", required_argument, 0, '9'}, {0, 0, 0, 0} }; @@ -537,42 +551,43 @@ static struct osnoise_params switch (c) { case 'a': /* set sample stop to auto_thresh */ - params->stop_us = get_llong_from_str(optarg); + params->common.stop_us = get_llong_from_str(optarg); /* set sample threshold to 1 */ params->threshold = 1; /* set trace */ - params->trace_output = "osnoise_trace.txt"; + trace_output = "osnoise_trace.txt"; break; case 'b': - params->bucket_size = get_llong_from_str(optarg); - if ((params->bucket_size == 0) || (params->bucket_size >= 1000000)) + params->common.hist.bucket_size = get_llong_from_str(optarg); + if (params->common.hist.bucket_size == 0 || + params->common.hist.bucket_size >= 1000000) osnoise_hist_usage("Bucket size needs to be > 0 and <= 1000000\n"); break; case 'c': - retval = parse_cpu_set(optarg, ¶ms->monitored_cpus); + retval = parse_cpu_set(optarg, ¶ms->common.monitored_cpus); if (retval) osnoise_hist_usage("\nInvalid -c cpu list\n"); - params->cpus = optarg; + params->common.cpus = optarg; break; case 'C': - params->cgroup = 1; + params->common.cgroup = 1; if (!optarg) { /* will inherit this cgroup */ - params->cgroup_name = NULL; + params->common.cgroup_name = NULL; } else if (*optarg == '=') { /* skip the = */ - params->cgroup_name = ++optarg; + params->common.cgroup_name = ++optarg; } break; case 'D': config_debug = 1; break; case 'd': - params->duration = parse_seconds_duration(optarg); - if (!params->duration) + params->common.duration = parse_seconds_duration(optarg); + if (!params->common.duration) osnoise_hist_usage("Invalid -D duration\n"); break; case 'e': @@ -582,14 +597,15 @@ static struct osnoise_params exit(EXIT_FAILURE); } - if (params->events) - tevent->next = params->events; + if (params->common.events) + tevent->next = params->common.events; - params->events = tevent; + params->common.events = tevent; break; case 'E': - params->entries = get_llong_from_str(optarg); - if ((params->entries < 10) || (params->entries > 9999999)) + params->common.hist.entries = get_llong_from_str(optarg); + if (params->common.hist.entries < 10 || + params->common.hist.entries > 9999999) osnoise_hist_usage("Entries must be > 10 and < 9999999\n"); break; case 'h': @@ -597,8 +613,8 @@ static struct osnoise_params osnoise_hist_usage(NULL); break; case 'H': - params->hk_cpus = 1; - retval = parse_cpu_set(optarg, ¶ms->hk_cpu_set); + params->common.hk_cpus = 1; + retval = parse_cpu_set(optarg, ¶ms->common.hk_cpu_set); if (retval) { err_msg("Error parsing house keeping CPUs\n"); exit(EXIT_FAILURE); @@ -610,10 +626,10 @@ static struct osnoise_params osnoise_hist_usage("Period longer than 10 s\n"); break; case 'P': - retval = parse_prio(optarg, ¶ms->sched_param); + retval = parse_prio(optarg, ¶ms->common.sched_param); if (retval == -1) osnoise_hist_usage("Invalid -P priority"); - params->set_sched = 1; + params->common.set_sched = 1; break; case 'r': params->runtime = get_llong_from_str(optarg); @@ -621,10 +637,10 @@ static struct osnoise_params osnoise_hist_usage("Runtime shorter than 100 us\n"); break; case 's': - params->stop_us = get_llong_from_str(optarg); + params->common.stop_us = get_llong_from_str(optarg); break; case 'S': - params->stop_total_us = get_llong_from_str(optarg); + params->common.stop_total_us = get_llong_from_str(optarg); break; case 'T': params->threshold = get_llong_from_str(optarg); @@ -632,29 +648,29 @@ static struct osnoise_params case 't': if (optarg) { if (optarg[0] == '=') - params->trace_output = &optarg[1]; + trace_output = &optarg[1]; else - params->trace_output = &optarg[0]; + trace_output = &optarg[0]; } else if (optind < argc && argv[optind][0] != '0') - params->trace_output = argv[optind]; + trace_output = argv[optind]; else - params->trace_output = "osnoise_trace.txt"; + trace_output = "osnoise_trace.txt"; break; case '0': /* no header */ - params->no_header = 1; + params->common.hist.no_header = 1; break; case '1': /* no summary */ - params->no_summary = 1; + params->common.hist.no_summary = 1; break; case '2': /* no index */ - params->no_index = 1; + params->common.hist.no_index = 1; break; case '3': /* with zeros */ - params->with_zeros = 1; + params->common.hist.with_zeros = 1; break; case '4': /* trigger */ - if (params->events) { - retval = trace_event_add_trigger(params->events, optarg); + if (params->common.events) { + retval = trace_event_add_trigger(params->common.events, optarg); if (retval) { err_msg("Error adding trigger %s\n", optarg); exit(EXIT_FAILURE); @@ -664,8 +680,8 @@ static struct osnoise_params } break; case '5': /* filter */ - if (params->events) { - retval = trace_event_add_filter(params->events, optarg); + if (params->common.events) { + retval = trace_event_add_filter(params->common.events, optarg); if (retval) { err_msg("Error adding filter %s\n", optarg); exit(EXIT_FAILURE); @@ -675,50 +691,60 @@ static struct osnoise_params } break; case '6': - params->warmup = get_llong_from_str(optarg); + params->common.warmup = get_llong_from_str(optarg); break; case '7': - params->buffer_size = get_llong_from_str(optarg); + params->common.buffer_size = get_llong_from_str(optarg); + break; + case '8': + retval = actions_parse(¶ms->common.threshold_actions, optarg, + "osnoise_trace.txt"); + if (retval) { + err_msg("Invalid action %s\n", optarg); + exit(EXIT_FAILURE); + } + break; + case '9': + retval = actions_parse(¶ms->common.end_actions, optarg, + "osnoise_trace.txt"); + if (retval) { + err_msg("Invalid action %s\n", optarg); + exit(EXIT_FAILURE); + } break; default: osnoise_hist_usage("Invalid option"); } } + if (trace_output) + actions_add_trace_output(¶ms->common.threshold_actions, trace_output); + if (geteuid()) { err_msg("rtla needs root permission\n"); exit(EXIT_FAILURE); } - if (params->no_index && !params->with_zeros) + if (params->common.hist.no_index && !params->common.hist.with_zeros) osnoise_hist_usage("no-index set and with-zeros not set - it does not make sense"); - return params; + return ¶ms->common; } /* * osnoise_hist_apply_config - apply the hist configs to the initialized tool */ static int -osnoise_hist_apply_config(struct osnoise_tool *tool, struct osnoise_params *params) +osnoise_hist_apply_config(struct osnoise_tool *tool) { - int retval; - - retval = osnoise_apply_config(tool, params); - if (retval) - goto out_err; - - return 0; - -out_err: - return -1; + return osnoise_apply_config(tool, to_osnoise_params(tool->params)); } /* * osnoise_init_hist - initialize a osnoise hist tool with parameters */ static struct osnoise_tool -*osnoise_init_hist(struct osnoise_params *params) +*osnoise_init_hist(struct common_params *params) { struct osnoise_tool *tool; int nr_cpus; @@ -729,12 +755,11 @@ static struct osnoise_tool if (!tool) return NULL; - tool->data = osnoise_alloc_histogram(nr_cpus, params->entries, params->bucket_size); + tool->data = osnoise_alloc_histogram(nr_cpus, params->hist.entries, + params->hist.bucket_size); if (!tool->data) goto out_err; - tool->params = params; - return tool; out_err: @@ -742,171 +767,35 @@ out_err: return NULL; } -static int stop_tracing; -static void stop_hist(int sig) -{ - stop_tracing = 1; -} - -/* - * osnoise_hist_set_signals - handles the signal to stop the tool - */ -static void -osnoise_hist_set_signals(struct osnoise_params *params) -{ - signal(SIGINT, stop_hist); - if (params->duration) { - signal(SIGALRM, stop_hist); - alarm(params->duration); - } -} - -int osnoise_hist_main(int argc, char *argv[]) +static int osnoise_hist_enable(struct osnoise_tool *tool) { - struct osnoise_params *params; - struct osnoise_tool *record = NULL; - struct osnoise_tool *tool = NULL; - enum result return_value = ERROR; - struct trace_instance *trace; int retval; - params = osnoise_hist_parse_args(argc, argv); - if (!params) - exit(1); - - tool = osnoise_init_hist(params); - if (!tool) { - err_msg("Could not init osnoise hist\n"); - goto out_exit; - } - - retval = osnoise_hist_apply_config(tool, params); - if (retval) { - err_msg("Could not apply config\n"); - goto out_destroy; - } - - trace = &tool->trace; - - retval = enable_osnoise(trace); - if (retval) { - err_msg("Failed to enable osnoise tracer\n"); - goto out_destroy; - } - retval = osnoise_init_trace_hist(tool); if (retval) - goto out_destroy; - - if (params->set_sched) { - retval = set_comm_sched_attr("osnoise/", ¶ms->sched_param); - if (retval) { - err_msg("Failed to set sched parameters\n"); - goto out_free; - } - } - - if (params->cgroup) { - retval = set_comm_cgroup("timerlat/", params->cgroup_name); - if (!retval) { - err_msg("Failed to move threads to cgroup\n"); - goto out_free; - } - } + return retval; - if (params->trace_output) { - record = osnoise_init_trace_tool("osnoise"); - if (!record) { - err_msg("Failed to enable the trace instance\n"); - goto out_free; - } - - if (params->events) { - retval = trace_events_enable(&record->trace, params->events); - if (retval) - goto out_hist; - } - - if (params->buffer_size > 0) { - retval = trace_set_buffer_size(&record->trace, params->buffer_size); - if (retval) - goto out_hist; - } - } - - /* - * Start the tracer here, after having set all instances. - * - * Let the trace instance start first for the case of hitting a stop - * tracing while enabling other instances. The trace instance is the - * one with most valuable information. - */ - if (params->trace_output) - trace_instance_start(&record->trace); - trace_instance_start(trace); - - if (params->warmup > 0) { - debug_msg("Warming up for %d seconds\n", params->warmup); - sleep(params->warmup); - if (stop_tracing) - goto out_hist; - - /* - * Clean up the buffer. The osnoise workload do not run - * with tracing off to avoid creating a performance penalty - * when not needed. - */ - retval = tracefs_instance_file_write(trace->inst, "trace", ""); - if (retval < 0) { - debug_msg("Error cleaning up the buffer"); - goto out_hist; - } - - } - - tool->start_time = time(NULL); - osnoise_hist_set_signals(params); - - while (!stop_tracing) { - sleep(params->sleep_time); - - retval = tracefs_iterate_raw_events(trace->tep, - trace->inst, - NULL, - 0, - collect_registered_events, - trace); - if (retval < 0) { - err_msg("Error iterating on events\n"); - goto out_hist; - } + return osnoise_enable(tool); +} - if (osnoise_trace_is_off(tool, record)) - break; - } +static int osnoise_hist_main_loop(struct osnoise_tool *tool) +{ + int retval; + retval = hist_main_loop(tool); osnoise_read_trace_hist(tool); - osnoise_print_stats(params, tool); - - return_value = PASSED; - - if (osnoise_trace_is_off(tool, record)) { - printf("rtla osnoise hit stop tracing\n"); - save_trace_to_file(record ? record->trace.inst : NULL, - params->trace_output); - return_value = FAILED; - } - -out_hist: - trace_events_destroy(&record->trace, params->events); - params->events = NULL; -out_free: - osnoise_free_histogram(tool->data); -out_destroy: - osnoise_destroy_tool(record); - osnoise_destroy_tool(tool); - free(params); -out_exit: - exit(return_value); + return retval; } + +struct tool_ops osnoise_hist_ops = { + .tracer = "osnoise", + .comm_prefix = "osnoise/", + .parse_args = osnoise_hist_parse_args, + .init_tool = osnoise_init_hist, + .apply_config = osnoise_hist_apply_config, + .enable = osnoise_hist_enable, + .main = osnoise_hist_main_loop, + .print_stats = osnoise_print_stats, + .free = osnoise_free_hist_tool, +}; diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/osnoise_top.c index 2c12780c8aa9..95418f7ecc96 100644 --- a/tools/tracing/rtla/src/osnoise_top.c +++ b/tools/tracing/rtla/src/osnoise_top.c @@ -37,13 +37,17 @@ struct osnoise_top_data { /* * osnoise_free_top - free runtime data */ -static void -osnoise_free_top(struct osnoise_top_data *data) +static void osnoise_free_top(struct osnoise_top_data *data) { free(data->cpu_data); free(data); } +static void osnoise_free_top_tool(struct osnoise_tool *tool) +{ + osnoise_free_top(tool->data); +} + /* * osnoise_alloc_histogram - alloc runtime data */ @@ -123,13 +127,14 @@ osnoise_top_handler(struct trace_seq *s, struct tep_record *record, */ static void osnoise_top_header(struct osnoise_tool *top) { - struct osnoise_params *params = top->params; + struct osnoise_params *params = to_osnoise_params(top->params); struct trace_seq *s = top->trace.seq; + bool pretty = params->common.pretty_output; char duration[26]; get_duration(top->start_time, duration, sizeof(duration)); - if (params->pretty_output) + if (pretty) trace_seq_printf(s, "\033[2;37;40m"); trace_seq_printf(s, " "); @@ -143,13 +148,13 @@ static void osnoise_top_header(struct osnoise_tool *top) trace_seq_printf(s, " "); - if (params->pretty_output) + if (pretty) trace_seq_printf(s, "\033[0;0;0m"); trace_seq_printf(s, "\n"); trace_seq_printf(s, "duration: %9s | time is in us\n", duration); - if (params->pretty_output) + if (pretty) trace_seq_printf(s, "\033[2;30;47m"); trace_seq_printf(s, "CPU Period Runtime "); @@ -164,7 +169,7 @@ static void osnoise_top_header(struct osnoise_tool *top) trace_seq_printf(s, " IRQ Softirq Thread"); eol: - if (params->pretty_output) + if (pretty) trace_seq_printf(s, "\033[0;0;0m"); trace_seq_printf(s, "\n"); } @@ -183,7 +188,7 @@ static void clear_terminal(struct trace_seq *seq) */ static void osnoise_top_print(struct osnoise_tool *tool, int cpu) { - struct osnoise_params *params = tool->params; + struct osnoise_params *params = to_osnoise_params(tool->params); struct trace_seq *s = tool->trace.seq; struct osnoise_top_cpu *cpu_data; struct osnoise_top_data *data; @@ -223,8 +228,9 @@ static void osnoise_top_print(struct osnoise_tool *tool, int cpu) * osnoise_print_stats - print data for all cpus */ static void -osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *top) +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; @@ -232,13 +238,13 @@ osnoise_print_stats(struct osnoise_params *params, struct osnoise_tool *top) if (nr_cpus == -1) nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - if (!params->quiet) + if (!params->common.quiet) clear_terminal(trace->seq); osnoise_top_header(top); for (i = 0; i < nr_cpus; i++) { - if (params->cpus && !CPU_ISSET(i, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(i, ¶ms->common.monitored_cpus)) continue; osnoise_top_print(top, i); } @@ -285,6 +291,8 @@ static void osnoise_top_usage(struct osnoise_params *params, char *usage) " in nanoseconds", " --warm-up s: let the workload run for s seconds before collecting data", " --trace-buffer-size kB: set the per-cpu trace buffer size in kB", + " --on-threshold <action>: define action to be executed at stop-total threshold, multiple are allowed", + " --on-end: define action to be executed at measurement end, multiple are allowed", NULL, }; @@ -319,17 +327,21 @@ static void osnoise_top_usage(struct osnoise_params *params, char *usage) /* * osnoise_top_parse_args - allocs, parse and fill the cmd line parameters */ -struct osnoise_params *osnoise_top_parse_args(int argc, char **argv) +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); + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); + if (strcmp(argv[0], "hwnoise") == 0) { params->mode = MODE_HWNOISE; /* @@ -361,6 +373,8 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv) {"filter", required_argument, 0, '1'}, {"warm-up", required_argument, 0, '2'}, {"trace-buffer-size", required_argument, 0, '3'}, + {"on-threshold", required_argument, 0, '4'}, + {"on-end", required_argument, 0, '5'}, {0, 0, 0, 0} }; @@ -377,37 +391,37 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv) switch (c) { case 'a': /* set sample stop to auto_thresh */ - params->stop_us = get_llong_from_str(optarg); + params->common.stop_us = get_llong_from_str(optarg); /* set sample threshold to 1 */ params->threshold = 1; /* set trace */ - params->trace_output = "osnoise_trace.txt"; + trace_output = "osnoise_trace.txt"; break; case 'c': - retval = parse_cpu_set(optarg, ¶ms->monitored_cpus); + retval = parse_cpu_set(optarg, ¶ms->common.monitored_cpus); if (retval) osnoise_top_usage(params, "\nInvalid -c cpu list\n"); - params->cpus = optarg; + params->common.cpus = optarg; break; case 'C': - params->cgroup = 1; + params->common.cgroup = 1; if (!optarg) { /* will inherit this cgroup */ - params->cgroup_name = NULL; + params->common.cgroup_name = NULL; } else if (*optarg == '=') { /* skip the = */ - params->cgroup_name = ++optarg; + params->common.cgroup_name = ++optarg; } break; case 'D': config_debug = 1; break; case 'd': - params->duration = parse_seconds_duration(optarg); - if (!params->duration) + params->common.duration = parse_seconds_duration(optarg); + if (!params->common.duration) osnoise_top_usage(params, "Invalid -d duration\n"); break; case 'e': @@ -417,9 +431,9 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv) exit(EXIT_FAILURE); } - if (params->events) - tevent->next = params->events; - params->events = tevent; + if (params->common.events) + tevent->next = params->common.events; + params->common.events = tevent; break; case 'h': @@ -427,8 +441,8 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv) osnoise_top_usage(params, NULL); break; case 'H': - params->hk_cpus = 1; - retval = parse_cpu_set(optarg, ¶ms->hk_cpu_set); + params->common.hk_cpus = 1; + retval = parse_cpu_set(optarg, ¶ms->common.hk_cpu_set); if (retval) { err_msg("Error parsing house keeping CPUs\n"); exit(EXIT_FAILURE); @@ -440,13 +454,13 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv) osnoise_top_usage(params, "Period longer than 10 s\n"); break; case 'P': - retval = parse_prio(optarg, ¶ms->sched_param); + retval = parse_prio(optarg, ¶ms->common.sched_param); if (retval == -1) osnoise_top_usage(params, "Invalid -P priority"); - params->set_sched = 1; + params->common.set_sched = 1; break; case 'q': - params->quiet = 1; + params->common.quiet = 1; break; case 'r': params->runtime = get_llong_from_str(optarg); @@ -454,28 +468,28 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv) osnoise_top_usage(params, "Runtime shorter than 100 us\n"); break; case 's': - params->stop_us = get_llong_from_str(optarg); + params->common.stop_us = get_llong_from_str(optarg); break; case 'S': - params->stop_total_us = get_llong_from_str(optarg); + params->common.stop_total_us = get_llong_from_str(optarg); break; case 't': if (optarg) { if (optarg[0] == '=') - params->trace_output = &optarg[1]; + trace_output = &optarg[1]; else - params->trace_output = &optarg[0]; + trace_output = &optarg[0]; } else if (optind < argc && argv[optind][0] != '-') - params->trace_output = argv[optind]; + trace_output = argv[optind]; else - params->trace_output = "osnoise_trace.txt"; + trace_output = "osnoise_trace.txt"; break; case 'T': params->threshold = get_llong_from_str(optarg); break; case '0': /* trigger */ - if (params->events) { - retval = trace_event_add_trigger(params->events, optarg); + if (params->common.events) { + retval = trace_event_add_trigger(params->common.events, optarg); if (retval) { err_msg("Error adding trigger %s\n", optarg); exit(EXIT_FAILURE); @@ -485,8 +499,8 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv) } break; case '1': /* filter */ - if (params->events) { - retval = trace_event_add_filter(params->events, optarg); + if (params->common.events) { + retval = trace_event_add_filter(params->common.events, optarg); if (retval) { err_msg("Error adding filter %s\n", optarg); exit(EXIT_FAILURE); @@ -496,30 +510,50 @@ struct osnoise_params *osnoise_top_parse_args(int argc, char **argv) } break; case '2': - params->warmup = get_llong_from_str(optarg); + params->common.warmup = get_llong_from_str(optarg); break; case '3': - params->buffer_size = get_llong_from_str(optarg); + params->common.buffer_size = get_llong_from_str(optarg); + break; + case '4': + retval = actions_parse(¶ms->common.threshold_actions, optarg, + "osnoise_trace.txt"); + if (retval) { + err_msg("Invalid action %s\n", optarg); + exit(EXIT_FAILURE); + } + break; + case '5': + retval = actions_parse(¶ms->common.end_actions, optarg, + "osnoise_trace.txt"); + if (retval) { + err_msg("Invalid action %s\n", optarg); + exit(EXIT_FAILURE); + } break; default: osnoise_top_usage(params, "Invalid option"); } } + if (trace_output) + actions_add_trace_output(¶ms->common.threshold_actions, trace_output); + if (geteuid()) { err_msg("osnoise needs root permission\n"); exit(EXIT_FAILURE); } - return params; + return ¶ms->common; } /* * osnoise_top_apply_config - apply the top configs to the initialized tool */ static int -osnoise_top_apply_config(struct osnoise_tool *tool, struct osnoise_params *params) +osnoise_top_apply_config(struct osnoise_tool *tool) { + struct osnoise_params *params = to_osnoise_params(tool->params); int retval; retval = osnoise_apply_config(tool, params); @@ -534,8 +568,8 @@ osnoise_top_apply_config(struct osnoise_tool *tool, struct osnoise_params *param } } - if (isatty(STDOUT_FILENO) && !params->quiet) - params->pretty_output = 1; + if (isatty(STDOUT_FILENO) && !params->common.quiet) + params->common.pretty_output = 1; return 0; @@ -546,7 +580,7 @@ out_err: /* * osnoise_init_top - initialize a osnoise top tool with parameters */ -struct osnoise_tool *osnoise_init_top(struct osnoise_params *params) +struct osnoise_tool *osnoise_init_top(struct common_params *params) { struct osnoise_tool *tool; int nr_cpus; @@ -563,175 +597,20 @@ struct osnoise_tool *osnoise_init_top(struct osnoise_params *params) return NULL; } - tool->params = params; - tep_register_event_handler(tool->trace.tep, -1, "ftrace", "osnoise", osnoise_top_handler, NULL); return tool; } -static int stop_tracing; -static void stop_top(int sig) -{ - stop_tracing = 1; -} - -/* - * osnoise_top_set_signals - handles the signal to stop the tool - */ -static void osnoise_top_set_signals(struct osnoise_params *params) -{ - signal(SIGINT, stop_top); - if (params->duration) { - signal(SIGALRM, stop_top); - alarm(params->duration); - } -} - -int osnoise_top_main(int argc, char **argv) -{ - struct osnoise_params *params; - struct osnoise_tool *record = NULL; - struct osnoise_tool *tool = NULL; - enum result return_value = ERROR; - struct trace_instance *trace; - int retval; - - params = osnoise_top_parse_args(argc, argv); - if (!params) - exit(1); - - tool = osnoise_init_top(params); - if (!tool) { - err_msg("Could not init osnoise top\n"); - goto out_exit; - } - - retval = osnoise_top_apply_config(tool, params); - if (retval) { - err_msg("Could not apply config\n"); - goto out_free; - } - - trace = &tool->trace; - - retval = enable_osnoise(trace); - if (retval) { - err_msg("Failed to enable osnoise tracer\n"); - goto out_free; - } - - if (params->set_sched) { - retval = set_comm_sched_attr("osnoise/", ¶ms->sched_param); - if (retval) { - err_msg("Failed to set sched parameters\n"); - goto out_free; - } - } - - if (params->cgroup) { - retval = set_comm_cgroup("osnoise/", params->cgroup_name); - if (!retval) { - err_msg("Failed to move threads to cgroup\n"); - goto out_free; - } - } - - if (params->trace_output) { - record = osnoise_init_trace_tool("osnoise"); - if (!record) { - err_msg("Failed to enable the trace instance\n"); - goto out_free; - } - - if (params->events) { - retval = trace_events_enable(&record->trace, params->events); - if (retval) - goto out_top; - } - - if (params->buffer_size > 0) { - retval = trace_set_buffer_size(&record->trace, params->buffer_size); - if (retval) - goto out_top; - } - } - - /* - * Start the tracer here, after having set all instances. - * - * Let the trace instance start first for the case of hitting a stop - * tracing while enabling other instances. The trace instance is the - * one with most valuable information. - */ - if (params->trace_output) - trace_instance_start(&record->trace); - trace_instance_start(trace); - - if (params->warmup > 0) { - debug_msg("Warming up for %d seconds\n", params->warmup); - sleep(params->warmup); - if (stop_tracing) - goto out_top; - - /* - * Clean up the buffer. The osnoise workload do not run - * with tracing off to avoid creating a performance penalty - * when not needed. - */ - retval = tracefs_instance_file_write(trace->inst, "trace", ""); - if (retval < 0) { - debug_msg("Error cleaning up the buffer"); - goto out_top; - } - - } - - tool->start_time = time(NULL); - osnoise_top_set_signals(params); - - while (!stop_tracing) { - sleep(params->sleep_time); - - retval = tracefs_iterate_raw_events(trace->tep, - trace->inst, - NULL, - 0, - collect_registered_events, - trace); - if (retval < 0) { - err_msg("Error iterating on events\n"); - goto out_top; - } - - if (!params->quiet) - osnoise_print_stats(params, tool); - - if (osnoise_trace_is_off(tool, record)) - break; - - } - - osnoise_print_stats(params, tool); - - return_value = PASSED; - - if (osnoise_trace_is_off(tool, record)) { - printf("osnoise hit stop tracing\n"); - save_trace_to_file(record ? record->trace.inst : NULL, - params->trace_output); - return_value = FAILED; - } - -out_top: - trace_events_destroy(&record->trace, params->events); - params->events = NULL; -out_free: - osnoise_free_top(tool->data); - osnoise_destroy_tool(record); - osnoise_destroy_tool(tool); - free(params); -out_exit: - exit(return_value); -} +struct tool_ops osnoise_top_ops = { + .tracer = "osnoise", + .comm_prefix = "osnoise/", + .parse_args = osnoise_top_parse_args, + .init_tool = osnoise_init_top, + .apply_config = osnoise_top_apply_config, + .enable = osnoise_enable, + .main = top_main_loop, + .print_stats = osnoise_print_stats, + .free = osnoise_free_top_tool, +}; diff --git a/tools/tracing/rtla/src/timerlat.c b/tools/tracing/rtla/src/timerlat.c index 63d6d43eafff..b69212874127 100644 --- a/tools/tracing/rtla/src/timerlat.c +++ b/tools/tracing/rtla/src/timerlat.c @@ -15,29 +15,37 @@ #include <sched.h> #include "timerlat.h" +#include "timerlat_aa.h" +#include "timerlat_bpf.h" #define DEFAULT_TIMERLAT_PERIOD 1000 /* 1ms */ +static int dma_latency_fd = -1; + /* * timerlat_apply_config - apply common configs to the initialized tool */ int timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params) { - int retval, i; - - if (!params->sleep_time) - params->sleep_time = 1; - - retval = osnoise_set_cpus(tool->context, params->cpus ? params->cpus : "all"); - if (retval) { - err_msg("Failed to apply CPUs config\n"); - goto out_err; - } + int retval; - if (!params->cpus) { - for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++) - CPU_SET(i, ¶ms->monitored_cpus); + /* + * 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) { + 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")) { + debug_msg("osnoise:timerlat_sample missing, disabling BPF\n"); + params->mode = TRACING_MODE_TRACEFS; + } else { + retval = timerlat_bpf_init(params); + if (retval) { + debug_msg("Could not enable BPF\n"); + params->mode = TRACING_MODE_TRACEFS; + } } if (params->mode != TRACING_MODE_BPF) { @@ -45,13 +53,13 @@ timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params) * In tracefs and mixed mode, timerlat tracer handles stopping * on threshold */ - retval = osnoise_set_stop_us(tool->context, params->stop_us); + 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->stop_total_us); + 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; @@ -75,55 +83,157 @@ timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params) goto out_err; } - if (params->hk_cpus) { - retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set), - ¶ms->hk_cpu_set); - if (retval == -1) { - err_msg("Failed to set rtla to the house keeping CPUs\n"); - goto out_err; - } - } else if (params->cpus) { - /* - * Even if the user do not set a house-keeping CPU, try to - * move rtla to a CPU set different to the one where the user - * set the workload to run. - * - * No need to check results as this is an automatic attempt. - */ - auto_house_keeping(¶ms->monitored_cpus); - } - /* * If the user did not specify a type of thread, try user-threads first. * Fall back to kernel threads otherwise. */ - if (!params->kernel_workload && !params->user_data) { + if (!params->common.kernel_workload && !params->common.user_data) { retval = tracefs_file_exists(NULL, "osnoise/per_cpu/cpu0/timerlat_fd"); if (retval) { debug_msg("User-space interface detected, setting user-threads\n"); - params->user_workload = 1; - params->user_data = 1; + params->common.user_workload = 1; + params->common.user_data = 1; } else { debug_msg("User-space interface not detected, setting kernel-threads\n"); - params->kernel_workload = 1; + params->common.kernel_workload = 1; + } + } + + return common_apply_config(tool, ¶ms->common); + +out_err: + return -1; +} + +int timerlat_enable(struct osnoise_tool *tool) +{ + struct timerlat_params *params = to_timerlat_params(tool->params); + int retval, nr_cpus, i; + + if (params->dma_latency >= 0) { + dma_latency_fd = set_cpu_dma_latency(params->dma_latency); + if (dma_latency_fd < 0) { + err_msg("Could not set /dev/cpu_dma_latency.\n"); + return -1; + } + } + + if (params->deepest_idle_state >= -1) { + if (!have_libcpupower_support()) { + err_msg("rtla built without libcpupower, --deepest-idle-state is not supported\n"); + return -1; + } + + nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + + for (i = 0; i < nr_cpus; i++) { + if (params->common.cpus && !CPU_ISSET(i, ¶ms->common.monitored_cpus)) + continue; + if (save_cpu_idle_disable_state(i) < 0) { + err_msg("Could not save cpu idle state.\n"); + return -1; + } + if (set_deepest_cpu_idle_state(i, params->deepest_idle_state) < 0) { + err_msg("Could not set deepest cpu idle state.\n"); + return -1; + } } } + if (!params->no_aa) { + tool->aa = osnoise_init_tool("timerlat_aa"); + if (!tool->aa) + return -1; + + retval = timerlat_aa_init(tool->aa, params->dump_tasks); + if (retval) { + err_msg("Failed to enable the auto analysis instance\n"); + return retval; + } + + retval = enable_tracer_by_name(tool->aa->trace.inst, "timerlat"); + if (retval) { + err_msg("Failed to enable aa tracer\n"); + return retval; + } + } + + if (params->common.warmup > 0) { + debug_msg("Warming up for %d seconds\n", params->common.warmup); + sleep(params->common.warmup); + if (stop_tracing) + return -1; + } + /* - * Set workload according to type of thread if the kernel supports it. - * On kernels without support, user threads will have already failed - * on missing timerlat_fd, and kernel threads do not need it. + * Start the tracers here, after having set all instances. + * + * Let the trace instance start first for the case of hitting a stop + * tracing while enabling other instances. The trace instance is the + * one with most valuable information. */ - retval = osnoise_set_workload(tool->context, params->kernel_workload); - if (retval < -1) { - err_msg("Failed to set OSNOISE_WORKLOAD option\n"); - goto out_err; + if (tool->record) + trace_instance_start(&tool->record->trace); + if (!params->no_aa) + trace_instance_start(&tool->aa->trace); + if (params->mode == TRACING_MODE_TRACEFS) { + trace_instance_start(&tool->trace); + } else { + retval = timerlat_bpf_attach(); + if (retval) { + err_msg("Error attaching BPF program\n"); + return retval; + } } return 0; +} -out_err: - return -1; +void timerlat_analyze(struct osnoise_tool *tool, bool stopped) +{ + struct timerlat_params *params = to_timerlat_params(tool->params); + + if (stopped) { + if (!params->no_aa) + timerlat_auto_analysis(params->common.stop_us, + params->common.stop_total_us); + } else if (params->common.aa_only) { + char *max_lat; + + /* + * If the trace did not stop with --aa-only, at least print + * the max known latency. + */ + max_lat = tracefs_instance_file_read(trace_inst->inst, "tracing_max_latency", NULL); + if (max_lat) { + printf(" Max latency was %s\n", max_lat); + free(max_lat); + } + } +} + +void timerlat_free(struct osnoise_tool *tool) +{ + struct timerlat_params *params = to_timerlat_params(tool->params); + int nr_cpus, i; + + timerlat_aa_destroy(); + if (dma_latency_fd >= 0) + close(dma_latency_fd); + if (params->deepest_idle_state >= -1) { + for (i = 0; i < nr_cpus; i++) { + if (params->common.cpus && + !CPU_ISSET(i, ¶ms->common.monitored_cpus)) + continue; + restore_cpu_idle_disable_state(i); + } + } + + osnoise_destroy_tool(tool->aa); + + if (params->mode != TRACING_MODE_TRACEFS) + timerlat_bpf_destroy(); + free_cpu_idle_disable_states(); } static void timerlat_usage(int err) @@ -159,7 +269,7 @@ int timerlat_main(int argc, char *argv[]) * default cmdline. */ if (argc == 1) { - timerlat_top_main(argc, argv); + run_tool(&timerlat_top_ops, argc, argv); exit(0); } @@ -167,13 +277,13 @@ int timerlat_main(int argc, char *argv[]) timerlat_usage(0); } else if (strncmp(argv[1], "-", 1) == 0) { /* the user skipped the tool, call the default one */ - timerlat_top_main(argc, argv); + run_tool(&timerlat_top_ops, argc, argv); exit(0); } else if (strcmp(argv[1], "top") == 0) { - timerlat_top_main(argc-1, &argv[1]); + run_tool(&timerlat_top_ops, argc-1, &argv[1]); exit(0); } else if (strcmp(argv[1], "hist") == 0) { - timerlat_hist_main(argc-1, &argv[1]); + run_tool(&timerlat_hist_ops, argc-1, &argv[1]); exit(0); } diff --git a/tools/tracing/rtla/src/timerlat.h b/tools/tracing/rtla/src/timerlat.h index bc55ed04fc96..fd6065f48bb7 100644 --- a/tools/tracing/rtla/src/timerlat.h +++ b/tools/tracing/rtla/src/timerlat.h @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#include "actions.h" #include "osnoise.h" /* @@ -20,61 +19,21 @@ enum timerlat_tracing_mode { }; struct timerlat_params { - /* Common params */ - char *cpus; - cpu_set_t monitored_cpus; - char *cgroup_name; - unsigned long long runtime; - long long stop_us; - long long stop_total_us; + struct common_params common; long long timerlat_period_us; long long print_stack; - int sleep_time; - int output_divisor; - int duration; - int set_sched; int dma_latency; int no_aa; int dump_tasks; - int cgroup; - int hk_cpus; - int user_workload; - int kernel_workload; - int user_data; - int warmup; - int buffer_size; int deepest_idle_state; - cpu_set_t hk_cpu_set; - struct sched_attr sched_param; - struct trace_events *events; enum timerlat_tracing_mode mode; - - struct actions threshold_actions; - struct actions end_actions; - - union { - struct { - /* top only */ - int quiet; - int aa_only; - int pretty_output; - }; - struct { - /* hist only */ - char no_irq; - char no_thread; - char no_header; - char no_summary; - char no_index; - char with_zeros; - int bucket_size; - int entries; - }; - }; }; -int timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params); +#define to_timerlat_params(ptr) container_of(ptr, struct timerlat_params, common) -int timerlat_hist_main(int argc, char *argv[]); -int timerlat_top_main(int argc, char *argv[]); +int timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params); 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_bpf.c b/tools/tracing/rtla/src/timerlat_bpf.c index 1666215dd687..e97d16646bcd 100644 --- a/tools/tracing/rtla/src/timerlat_bpf.c +++ b/tools/tracing/rtla/src/timerlat_bpf.c @@ -21,20 +21,20 @@ int timerlat_bpf_init(struct timerlat_params *params) return 1; /* Pass common options */ - bpf->rodata->output_divisor = params->output_divisor; - bpf->rodata->entries = params->entries; - bpf->rodata->irq_threshold = params->stop_us; - bpf->rodata->thread_threshold = params->stop_total_us; - bpf->rodata->aa_only = params->aa_only; + bpf->rodata->output_divisor = params->common.output_divisor; + bpf->rodata->entries = params->common.hist.entries; + bpf->rodata->irq_threshold = params->common.stop_us; + bpf->rodata->thread_threshold = params->common.stop_total_us; + bpf->rodata->aa_only = params->common.aa_only; - if (params->entries != 0) { + if (params->common.hist.entries != 0) { /* Pass histogram options */ - bpf->rodata->bucket_size = params->bucket_size; + bpf->rodata->bucket_size = params->common.hist.bucket_size; /* Set histogram array sizes */ - bpf_map__set_max_entries(bpf->maps.hist_irq, params->entries); - bpf_map__set_max_entries(bpf->maps.hist_thread, params->entries); - bpf_map__set_max_entries(bpf->maps.hist_user, params->entries); + bpf_map__set_max_entries(bpf->maps.hist_irq, params->common.hist.entries); + bpf_map__set_max_entries(bpf->maps.hist_thread, params->common.hist.entries); + bpf_map__set_max_entries(bpf->maps.hist_user, params->common.hist.entries); } else { /* No entries, disable histogram */ bpf_map__set_autocreate(bpf->maps.hist_irq, false); @@ -42,7 +42,7 @@ int timerlat_bpf_init(struct timerlat_params *params) bpf_map__set_autocreate(bpf->maps.hist_user, false); } - if (params->aa_only) { + if (params->common.aa_only) { /* Auto-analysis only, disable summary */ bpf_map__set_autocreate(bpf->maps.summary_irq, false); bpf_map__set_autocreate(bpf->maps.summary_thread, false); diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 9baea1b251ed..606c1688057b 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -16,7 +16,6 @@ #include "timerlat.h" #include "timerlat_aa.h" -#include "timerlat_u.h" #include "timerlat_bpf.h" struct timerlat_hist_cpu { @@ -72,8 +71,12 @@ timerlat_free_histogram(struct timerlat_hist_data *data) /* one set of histograms per CPU */ if (data->hist) free(data->hist); +} - free(data); +static void timerlat_free_histogram_tool(struct osnoise_tool *tool) +{ + timerlat_free_histogram(tool->data); + timerlat_free(tool); } /* @@ -135,14 +138,14 @@ timerlat_hist_update(struct osnoise_tool *tool, int cpu, unsigned long long context, unsigned long long latency) { - struct timerlat_params *params = tool->params; + struct timerlat_params *params = to_timerlat_params(tool->params); struct timerlat_hist_data *data = tool->data; int entries = data->entries; int bucket; int *hist; - if (params->output_divisor) - latency = latency / params->output_divisor; + if (params->common.output_divisor) + latency = latency / params->common.output_divisor; bucket = latency / data->bucket_size; @@ -282,40 +285,40 @@ static int timerlat_hist_bpf_pull_data(struct osnoise_tool *tool) */ static void timerlat_hist_header(struct osnoise_tool *tool) { - struct timerlat_params *params = tool->params; + struct timerlat_params *params = to_timerlat_params(tool->params); struct timerlat_hist_data *data = tool->data; struct trace_seq *s = tool->trace.seq; char duration[26]; int cpu; - if (params->no_header) + if (params->common.hist.no_header) return; get_duration(tool->start_time, duration, sizeof(duration)); trace_seq_printf(s, "# RTLA timerlat histogram\n"); trace_seq_printf(s, "# Time unit is %s (%s)\n", - params->output_divisor == 1 ? "nanoseconds" : "microseconds", - params->output_divisor == 1 ? "ns" : "us"); + params->common.output_divisor == 1 ? "nanoseconds" : "microseconds", + params->common.output_divisor == 1 ? "ns" : "us"); trace_seq_printf(s, "# Duration: %s\n", duration); - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(s, "Index"); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; - if (!params->no_irq) + if (!params->common.hist.no_irq) trace_seq_printf(s, " IRQ-%03d", cpu); - if (!params->no_thread) + if (!params->common.hist.no_thread) trace_seq_printf(s, " Thr-%03d", cpu); - if (params->user_data) + if (params->common.user_data) trace_seq_printf(s, " Usr-%03d", cpu); } trace_seq_printf(s, "\n"); @@ -350,56 +353,56 @@ timerlat_print_summary(struct timerlat_params *params, { int cpu; - if (params->no_summary) + if (params->common.hist.no_summary) return; - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "count:"); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; - if (!params->no_irq) + if (!params->common.hist.no_irq) trace_seq_printf(trace->seq, "%9llu ", data->hist[cpu].irq_count); - if (!params->no_thread) + if (!params->common.hist.no_thread) trace_seq_printf(trace->seq, "%9llu ", data->hist[cpu].thread_count); - if (params->user_data) + if (params->common.user_data) trace_seq_printf(trace->seq, "%9llu ", data->hist[cpu].user_count); } trace_seq_printf(trace->seq, "\n"); - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "min: "); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; - if (!params->no_irq) + if (!params->common.hist.no_irq) format_summary_value(trace->seq, data->hist[cpu].irq_count, data->hist[cpu].min_irq, false); - if (!params->no_thread) + if (!params->common.hist.no_thread) format_summary_value(trace->seq, data->hist[cpu].thread_count, data->hist[cpu].min_thread, false); - if (params->user_data) + if (params->common.user_data) format_summary_value(trace->seq, data->hist[cpu].user_count, data->hist[cpu].min_user, @@ -407,29 +410,29 @@ timerlat_print_summary(struct timerlat_params *params, } trace_seq_printf(trace->seq, "\n"); - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "avg: "); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; - if (!params->no_irq) + if (!params->common.hist.no_irq) format_summary_value(trace->seq, data->hist[cpu].irq_count, data->hist[cpu].sum_irq, true); - if (!params->no_thread) + if (!params->common.hist.no_thread) format_summary_value(trace->seq, data->hist[cpu].thread_count, data->hist[cpu].sum_thread, true); - if (params->user_data) + if (params->common.user_data) format_summary_value(trace->seq, data->hist[cpu].user_count, data->hist[cpu].sum_user, @@ -437,29 +440,29 @@ timerlat_print_summary(struct timerlat_params *params, } trace_seq_printf(trace->seq, "\n"); - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "max: "); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; - if (!params->no_irq) + if (!params->common.hist.no_irq) format_summary_value(trace->seq, data->hist[cpu].irq_count, data->hist[cpu].max_irq, false); - if (!params->no_thread) + if (!params->common.hist.no_thread) format_summary_value(trace->seq, data->hist[cpu].thread_count, data->hist[cpu].max_thread, false); - if (params->user_data) + if (params->common.user_data) format_summary_value(trace->seq, data->hist[cpu].user_count, data->hist[cpu].max_user, @@ -479,7 +482,7 @@ timerlat_print_stats_all(struct timerlat_params *params, struct timerlat_hist_cpu sum; int cpu; - if (params->no_summary) + if (params->common.hist.no_summary) return; memset(&sum, 0, sizeof(sum)); @@ -488,7 +491,7 @@ timerlat_print_stats_all(struct timerlat_params *params, sum.min_user = ~0; for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) @@ -512,53 +515,53 @@ timerlat_print_stats_all(struct timerlat_params *params, update_max(&sum.max_user, &cpu_data->max_user); } - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "ALL: "); - if (!params->no_irq) + if (!params->common.hist.no_irq) trace_seq_printf(trace->seq, " IRQ"); - if (!params->no_thread) + if (!params->common.hist.no_thread) trace_seq_printf(trace->seq, " Thr"); - if (params->user_data) + if (params->common.user_data) trace_seq_printf(trace->seq, " Usr"); trace_seq_printf(trace->seq, "\n"); - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "count:"); - if (!params->no_irq) + if (!params->common.hist.no_irq) trace_seq_printf(trace->seq, "%9llu ", sum.irq_count); - if (!params->no_thread) + if (!params->common.hist.no_thread) trace_seq_printf(trace->seq, "%9llu ", sum.thread_count); - if (params->user_data) + if (params->common.user_data) trace_seq_printf(trace->seq, "%9llu ", sum.user_count); trace_seq_printf(trace->seq, "\n"); - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "min: "); - if (!params->no_irq) + if (!params->common.hist.no_irq) format_summary_value(trace->seq, sum.irq_count, sum.min_irq, false); - if (!params->no_thread) + if (!params->common.hist.no_thread) format_summary_value(trace->seq, sum.thread_count, sum.min_thread, false); - if (params->user_data) + if (params->common.user_data) format_summary_value(trace->seq, sum.user_count, sum.min_user, @@ -566,22 +569,22 @@ timerlat_print_stats_all(struct timerlat_params *params, trace_seq_printf(trace->seq, "\n"); - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "avg: "); - if (!params->no_irq) + if (!params->common.hist.no_irq) format_summary_value(trace->seq, sum.irq_count, sum.sum_irq, true); - if (!params->no_thread) + if (!params->common.hist.no_thread) format_summary_value(trace->seq, sum.thread_count, sum.sum_thread, true); - if (params->user_data) + if (params->common.user_data) format_summary_value(trace->seq, sum.user_count, sum.sum_user, @@ -589,22 +592,22 @@ timerlat_print_stats_all(struct timerlat_params *params, trace_seq_printf(trace->seq, "\n"); - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "max: "); - if (!params->no_irq) + if (!params->common.hist.no_irq) format_summary_value(trace->seq, sum.irq_count, sum.max_irq, false); - if (!params->no_thread) + if (!params->common.hist.no_thread) format_summary_value(trace->seq, sum.thread_count, sum.max_thread, false); - if (params->user_data) + if (params->common.user_data) format_summary_value(trace->seq, sum.user_count, sum.max_user, @@ -619,8 +622,9 @@ timerlat_print_stats_all(struct timerlat_params *params, * timerlat_print_stats - print data for each CPUs */ static void -timerlat_print_stats(struct timerlat_params *params, struct osnoise_tool *tool) +timerlat_print_stats(struct osnoise_tool *tool) { + struct timerlat_params *params = to_timerlat_params(tool->params); struct timerlat_hist_data *data = tool->data; struct trace_instance *trace = &tool->trace; int bucket, cpu; @@ -631,30 +635,30 @@ timerlat_print_stats(struct timerlat_params *params, struct osnoise_tool *tool) for (bucket = 0; bucket < data->entries; bucket++) { total = 0; - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "%-6d", bucket * data->bucket_size); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; - if (!params->no_irq) { + if (!params->common.hist.no_irq) { total += data->hist[cpu].irq[bucket]; trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].irq[bucket]); } - if (!params->no_thread) { + if (!params->common.hist.no_thread) { total += data->hist[cpu].thread[bucket]; trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].thread[bucket]); } - if (params->user_data) { + if (params->common.user_data) { total += data->hist[cpu].user[bucket]; trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].user[bucket]); @@ -662,7 +666,7 @@ timerlat_print_stats(struct timerlat_params *params, struct osnoise_tool *tool) } - if (total == 0 && !params->with_zeros) { + if (total == 0 && !params->common.hist.with_zeros) { trace_seq_reset(trace->seq); continue; } @@ -672,25 +676,25 @@ timerlat_print_stats(struct timerlat_params *params, struct osnoise_tool *tool) trace_seq_reset(trace->seq); } - if (!params->no_index) + if (!params->common.hist.no_index) trace_seq_printf(trace->seq, "over: "); for (cpu = 0; cpu < data->nr_cpus; cpu++) { - if (params->cpus && !CPU_ISSET(cpu, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(cpu, ¶ms->common.monitored_cpus)) continue; if (!data->hist[cpu].irq_count && !data->hist[cpu].thread_count) continue; - if (!params->no_irq) + if (!params->common.hist.no_irq) trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].irq[data->entries]); - if (!params->no_thread) + if (!params->common.hist.no_thread) trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].thread[data->entries]); - if (params->user_data) + if (params->common.user_data) trace_seq_printf(trace->seq, "%9d ", data->hist[cpu].user[data->entries]); } @@ -780,7 +784,7 @@ static void timerlat_hist_usage(char *usage) /* * timerlat_hist_parse_args - allocs, parse and fill the cmd line parameters */ -static struct timerlat_params +static struct common_params *timerlat_hist_parse_args(int argc, char *argv[]) { struct timerlat_params *params; @@ -794,8 +798,8 @@ static struct timerlat_params if (!params) exit(1); - actions_init(¶ms->threshold_actions); - actions_init(¶ms->end_actions); + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); /* disabled by default */ params->dma_latency = -1; @@ -804,9 +808,9 @@ static struct timerlat_params params->deepest_idle_state = -2; /* display data in microseconds */ - params->output_divisor = 1000; - params->bucket_size = 1; - params->entries = 256; + params->common.output_divisor = 1000; + params->common.hist.bucket_size = 1; + params->common.hist.entries = 256; /* default to BPF mode */ params->mode = TRACING_MODE_BPF; @@ -867,8 +871,8 @@ static struct timerlat_params auto_thresh = get_llong_from_str(optarg); /* set thread stop to auto_thresh */ - params->stop_total_us = auto_thresh; - params->stop_us = auto_thresh; + params->common.stop_total_us = auto_thresh; + params->common.stop_us = auto_thresh; /* get stack trace */ params->print_stack = auto_thresh; @@ -878,32 +882,33 @@ static struct timerlat_params break; case 'c': - retval = parse_cpu_set(optarg, ¶ms->monitored_cpus); + retval = parse_cpu_set(optarg, ¶ms->common.monitored_cpus); if (retval) timerlat_hist_usage("\nInvalid -c cpu list\n"); - params->cpus = optarg; + params->common.cpus = optarg; break; case 'C': - params->cgroup = 1; + params->common.cgroup = 1; if (!optarg) { /* will inherit this cgroup */ - params->cgroup_name = NULL; + params->common.cgroup_name = NULL; } else if (*optarg == '=') { /* skip the = */ - params->cgroup_name = ++optarg; + params->common.cgroup_name = ++optarg; } break; case 'b': - params->bucket_size = get_llong_from_str(optarg); - if ((params->bucket_size == 0) || (params->bucket_size >= 1000000)) + params->common.hist.bucket_size = get_llong_from_str(optarg); + if (params->common.hist.bucket_size == 0 || + params->common.hist.bucket_size >= 1000000) timerlat_hist_usage("Bucket size needs to be > 0 and <= 1000000\n"); break; case 'D': config_debug = 1; break; case 'd': - params->duration = parse_seconds_duration(optarg); - if (!params->duration) + params->common.duration = parse_seconds_duration(optarg); + if (!params->common.duration) timerlat_hist_usage("Invalid -D duration\n"); break; case 'e': @@ -913,36 +918,37 @@ static struct timerlat_params exit(EXIT_FAILURE); } - if (params->events) - tevent->next = params->events; + if (params->common.events) + tevent->next = params->common.events; - params->events = tevent; + params->common.events = tevent; break; case 'E': - params->entries = get_llong_from_str(optarg); - if ((params->entries < 10) || (params->entries > 9999999)) - timerlat_hist_usage("Entries must be > 10 and < 9999999\n"); + params->common.hist.entries = get_llong_from_str(optarg); + if (params->common.hist.entries < 10 || + params->common.hist.entries > 9999999) + timerlat_hist_usage("Entries must be > 10 and < 9999999\n"); break; case 'h': case '?': timerlat_hist_usage(NULL); break; case 'H': - params->hk_cpus = 1; - retval = parse_cpu_set(optarg, ¶ms->hk_cpu_set); + params->common.hk_cpus = 1; + retval = parse_cpu_set(optarg, ¶ms->common.hk_cpu_set); if (retval) { err_msg("Error parsing house keeping CPUs\n"); exit(EXIT_FAILURE); } break; case 'i': - params->stop_us = get_llong_from_str(optarg); + params->common.stop_us = get_llong_from_str(optarg); break; case 'k': - params->kernel_workload = 1; + params->common.kernel_workload = 1; break; case 'n': - params->output_divisor = 1; + params->common.output_divisor = 1; break; case 'p': params->timerlat_period_us = get_llong_from_str(optarg); @@ -950,16 +956,16 @@ static struct timerlat_params timerlat_hist_usage("Period longer than 1 s\n"); break; case 'P': - retval = parse_prio(optarg, ¶ms->sched_param); + retval = parse_prio(optarg, ¶ms->common.sched_param); if (retval == -1) timerlat_hist_usage("Invalid -P priority"); - params->set_sched = 1; + params->common.set_sched = 1; break; case 's': params->print_stack = get_llong_from_str(optarg); break; case 'T': - params->stop_total_us = get_llong_from_str(optarg); + params->common.stop_total_us = get_llong_from_str(optarg); break; case 't': if (optarg) { @@ -973,32 +979,32 @@ static struct timerlat_params trace_output = "timerlat_trace.txt"; break; case 'u': - params->user_workload = 1; + params->common.user_workload = 1; /* fallback: -u implies in -U */ case 'U': - params->user_data = 1; + params->common.user_data = 1; break; case '0': /* no irq */ - params->no_irq = 1; + params->common.hist.no_irq = 1; break; case '1': /* no thread */ - params->no_thread = 1; + params->common.hist.no_thread = 1; break; case '2': /* no header */ - params->no_header = 1; + params->common.hist.no_header = 1; break; case '3': /* no summary */ - params->no_summary = 1; + params->common.hist.no_summary = 1; break; case '4': /* no index */ - params->no_index = 1; + params->common.hist.no_index = 1; break; case '5': /* with zeros */ - params->with_zeros = 1; + params->common.hist.with_zeros = 1; break; case '6': /* trigger */ - if (params->events) { - retval = trace_event_add_trigger(params->events, optarg); + if (params->common.events) { + retval = trace_event_add_trigger(params->common.events, optarg); if (retval) { err_msg("Error adding trigger %s\n", optarg); exit(EXIT_FAILURE); @@ -1008,8 +1014,8 @@ static struct timerlat_params } break; case '7': /* filter */ - if (params->events) { - retval = trace_event_add_filter(params->events, optarg); + if (params->common.events) { + retval = trace_event_add_filter(params->common.events, optarg); if (retval) { err_msg("Error adding filter %s\n", optarg); exit(EXIT_FAILURE); @@ -1032,23 +1038,25 @@ static struct timerlat_params params->dump_tasks = 1; break; case '\2': - params->warmup = get_llong_from_str(optarg); + params->common.warmup = get_llong_from_str(optarg); break; case '\3': - params->buffer_size = get_llong_from_str(optarg); + params->common.buffer_size = get_llong_from_str(optarg); break; case '\4': params->deepest_idle_state = get_llong_from_str(optarg); break; case '\5': - retval = actions_parse(¶ms->threshold_actions, optarg); + retval = actions_parse(¶ms->common.threshold_actions, optarg, + "timerlat_trace.txt"); if (retval) { err_msg("Invalid action %s\n", optarg); exit(EXIT_FAILURE); } break; case '\6': - retval = actions_parse(¶ms->end_actions, optarg); + retval = actions_parse(¶ms->common.end_actions, optarg, + "timerlat_trace.txt"); if (retval) { err_msg("Invalid action %s\n", optarg); exit(EXIT_FAILURE); @@ -1060,26 +1068,26 @@ static struct timerlat_params } if (trace_output) - actions_add_trace_output(¶ms->threshold_actions, trace_output); + actions_add_trace_output(¶ms->common.threshold_actions, trace_output); if (geteuid()) { err_msg("rtla needs root permission\n"); exit(EXIT_FAILURE); } - if (params->no_irq && params->no_thread) + if (params->common.hist.no_irq && params->common.hist.no_thread) timerlat_hist_usage("no-irq and no-thread set, there is nothing to do here"); - if (params->no_index && !params->with_zeros) + if (params->common.hist.no_index && !params->common.hist.with_zeros) timerlat_hist_usage("no-index set with with-zeros is not set - it does not make sense"); /* * Auto analysis only happens if stop tracing, thus: */ - if (!params->stop_us && !params->stop_total_us) + if (!params->common.stop_us && !params->common.stop_total_us) params->no_aa = 1; - if (params->kernel_workload && params->user_workload) + if (params->common.kernel_workload && params->common.user_workload) timerlat_hist_usage("--kernel-threads and --user-threads are mutually exclusive!"); /* @@ -1087,19 +1095,21 @@ static struct timerlat_params * mixed mode */ if (params->mode == TRACING_MODE_BPF && - (params->threshold_actions.present[ACTION_TRACE_OUTPUT] || - params->end_actions.present[ACTION_TRACE_OUTPUT] || !params->no_aa)) + (params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] || + params->common.end_actions.present[ACTION_TRACE_OUTPUT] || + !params->no_aa)) params->mode = TRACING_MODE_MIXED; - return params; + return ¶ms->common; } /* * timerlat_hist_apply_config - apply the hist configs to the initialized tool */ static int -timerlat_hist_apply_config(struct osnoise_tool *tool, struct timerlat_params *params) +timerlat_hist_apply_config(struct osnoise_tool *tool) { + struct timerlat_params *params = to_timerlat_params(tool->params); int retval; retval = timerlat_apply_config(tool, params); @@ -1116,7 +1126,7 @@ out_err: * timerlat_init_hist - initialize a timerlat hist tool with parameters */ static struct osnoise_tool -*timerlat_init_hist(struct timerlat_params *params) +*timerlat_init_hist(struct common_params *params) { struct osnoise_tool *tool; int nr_cpus; @@ -1127,12 +1137,11 @@ static struct osnoise_tool if (!tool) return NULL; - tool->data = timerlat_alloc_histogram(nr_cpus, params->entries, params->bucket_size); + tool->data = timerlat_alloc_histogram(nr_cpus, params->hist.entries, + params->hist.bucket_size); if (!tool->data) goto out_err; - tool->params = params; - tep_register_event_handler(tool->trace.tep, -1, "ftrace", "timerlat", timerlat_hist_handler, tool); @@ -1143,348 +1152,61 @@ out_err: return NULL; } -static int stop_tracing; -static struct trace_instance *hist_inst = NULL; -static void stop_hist(int sig) -{ - if (stop_tracing) { - /* - * Stop requested twice in a row; abort event processing and - * exit immediately - */ - tracefs_iterate_stop(hist_inst->inst); - return; - } - stop_tracing = 1; - if (hist_inst) - trace_instance_stop(hist_inst); -} - -/* - * timerlat_hist_set_signals - handles the signal to stop the tool - */ -static void -timerlat_hist_set_signals(struct timerlat_params *params) +static int timerlat_hist_bpf_main_loop(struct osnoise_tool *tool) { - signal(SIGINT, stop_hist); - if (params->duration) { - signal(SIGALRM, stop_hist); - alarm(params->duration); - } -} - -int timerlat_hist_main(int argc, char *argv[]) -{ - struct timerlat_params *params; - struct osnoise_tool *record = NULL; - struct timerlat_u_params params_u; - enum result return_value = ERROR; - struct osnoise_tool *tool = NULL; - struct osnoise_tool *aa = NULL; - struct trace_instance *trace; - int dma_latency_fd = -1; - pthread_t timerlat_u; + struct timerlat_params *params = to_timerlat_params(tool->params); int retval; - int nr_cpus, i; - - params = timerlat_hist_parse_args(argc, argv); - if (!params) - exit(1); - - tool = timerlat_init_hist(params); - if (!tool) { - err_msg("Could not init osnoise hist\n"); - goto out_exit; - } - - trace = &tool->trace; - /* - * Save trace instance into global variable so that SIGINT can stop - * the timerlat tracer. - * Otherwise, rtla could loop indefinitely when overloaded. - */ - hist_inst = trace; - - /* - * 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) { - debug_msg("RTLA_NO_BPF set, disabling BPF\n"); - params->mode = TRACING_MODE_TRACEFS; - } else if (!tep_find_event_by_name(trace->tep, "osnoise", "timerlat_sample")) { - debug_msg("osnoise:timerlat_sample missing, disabling BPF\n"); - params->mode = TRACING_MODE_TRACEFS; - } else { - retval = timerlat_bpf_init(params); - if (retval) { - debug_msg("Could not enable BPF\n"); - params->mode = TRACING_MODE_TRACEFS; - } - } - - retval = timerlat_hist_apply_config(tool, params); - if (retval) { - err_msg("Could not apply config\n"); - goto out_free; - } - - retval = enable_timerlat(trace); - if (retval) { - err_msg("Failed to enable timerlat tracer\n"); - goto out_free; - } - - if (params->set_sched) { - retval = set_comm_sched_attr("timerlat/", ¶ms->sched_param); - if (retval) { - err_msg("Failed to set sched parameters\n"); - goto out_free; - } - } - - if (params->cgroup && !params->user_workload) { - retval = set_comm_cgroup("timerlat/", params->cgroup_name); - if (!retval) { - err_msg("Failed to move threads to cgroup\n"); - goto out_free; - } - } - - if (params->dma_latency >= 0) { - dma_latency_fd = set_cpu_dma_latency(params->dma_latency); - if (dma_latency_fd < 0) { - err_msg("Could not set /dev/cpu_dma_latency.\n"); - goto out_free; - } - } - - if (params->deepest_idle_state >= -1) { - if (!have_libcpupower_support()) { - err_msg("rtla built without libcpupower, --deepest-idle-state is not supported\n"); - goto out_free; - } - - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - - for (i = 0; i < nr_cpus; i++) { - if (params->cpus && !CPU_ISSET(i, ¶ms->monitored_cpus)) - continue; - if (save_cpu_idle_disable_state(i) < 0) { - err_msg("Could not save cpu idle state.\n"); - goto out_free; - } - if (set_deepest_cpu_idle_state(i, params->deepest_idle_state) < 0) { - err_msg("Could not set deepest cpu idle state.\n"); - goto out_free; - } - } - } - - if (params->threshold_actions.present[ACTION_TRACE_OUTPUT] || - params->end_actions.present[ACTION_TRACE_OUTPUT]) { - record = osnoise_init_trace_tool("timerlat"); - if (!record) { - err_msg("Failed to enable the trace instance\n"); - goto out_free; - } - params->threshold_actions.trace_output_inst = record->trace.inst; - params->end_actions.trace_output_inst = record->trace.inst; - - if (params->events) { - retval = trace_events_enable(&record->trace, params->events); - if (retval) - goto out_hist; - } - if (params->buffer_size > 0) { - retval = trace_set_buffer_size(&record->trace, params->buffer_size); - if (retval) - goto out_hist; - } - } + while (!stop_tracing) { + timerlat_bpf_wait(-1); - if (!params->no_aa) { - aa = osnoise_init_tool("timerlat_aa"); - if (!aa) - goto out_hist; + if (!stop_tracing) { + /* Threshold overflow, perform actions on threshold */ + actions_perform(¶ms->common.threshold_actions); - retval = timerlat_aa_init(aa, params->dump_tasks); - if (retval) { - err_msg("Failed to enable the auto analysis instance\n"); - goto out_hist; - } + if (!params->common.threshold_actions.continue_flag) + /* continue flag not set, break */ + break; - retval = enable_timerlat(&aa->trace); - if (retval) { - err_msg("Failed to enable timerlat tracer\n"); - goto out_hist; + /* 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(); } } + timerlat_bpf_detach(); - if (params->user_workload) { - /* rtla asked to stop */ - params_u.should_run = 1; - /* all threads left */ - params_u.stopped_running = 0; - - params_u.set = ¶ms->monitored_cpus; - if (params->set_sched) - params_u.sched_param = ¶ms->sched_param; - else - params_u.sched_param = NULL; - - params_u.cgroup_name = params->cgroup_name; - - retval = pthread_create(&timerlat_u, NULL, timerlat_u_dispatcher, ¶ms_u); - if (retval) - err_msg("Error creating timerlat user-space threads\n"); - } - - if (params->warmup > 0) { - debug_msg("Warming up for %d seconds\n", params->warmup); - sleep(params->warmup); - if (stop_tracing) - goto out_hist; - } - - /* - * Start the tracers here, after having set all instances. - * - * Let the trace instance start first for the case of hitting a stop - * tracing while enabling other instances. The trace instance is the - * one with most valuable information. - */ - if (record) - trace_instance_start(&record->trace); - if (!params->no_aa) - trace_instance_start(&aa->trace); - if (params->mode == TRACING_MODE_TRACEFS) { - trace_instance_start(trace); - } else { - retval = timerlat_bpf_attach(); - if (retval) { - err_msg("Error attaching BPF program\n"); - goto out_hist; - } - } - - tool->start_time = time(NULL); - timerlat_hist_set_signals(params); - - if (params->mode == TRACING_MODE_TRACEFS) { - while (!stop_tracing) { - sleep(params->sleep_time); - - retval = tracefs_iterate_raw_events(trace->tep, - trace->inst, - NULL, - 0, - collect_registered_events, - trace); - if (retval < 0) { - err_msg("Error iterating on events\n"); - goto out_hist; - } - - if (osnoise_trace_is_off(tool, record)) { - actions_perform(¶ms->threshold_actions); - - if (!params->threshold_actions.continue_flag) - /* continue flag not set, break */ - break; - - /* continue action reached, re-enable tracing */ - if (record) - trace_instance_start(&record->trace); - if (!params->no_aa) - trace_instance_start(&aa->trace); - trace_instance_start(trace); - } - - /* is there still any user-threads ? */ - if (params->user_workload) { - if (params_u.stopped_running) { - debug_msg("timerlat user-space threads stopped!\n"); - break; - } - } - } - } else { - while (!stop_tracing) { - timerlat_bpf_wait(-1); - - if (!stop_tracing) { - /* Threshold overflow, perform actions on threshold */ - actions_perform(¶ms->threshold_actions); - - if (!params->threshold_actions.continue_flag) - /* continue flag not set, break */ - break; - - /* continue action reached, re-enable tracing */ - if (record) - trace_instance_start(&record->trace); - if (!params->no_aa) - trace_instance_start(&aa->trace); - timerlat_bpf_restart_tracing(); - } - } - } - - if (params->mode != TRACING_MODE_TRACEFS) { - timerlat_bpf_detach(); - retval = timerlat_hist_bpf_pull_data(tool); - if (retval) { - err_msg("Error pulling BPF data\n"); - goto out_hist; - } - } - - if (params->user_workload && !params_u.stopped_running) { - params_u.should_run = 0; - sleep(1); - } - - timerlat_print_stats(params, tool); - - actions_perform(¶ms->end_actions); - - return_value = PASSED; + retval = timerlat_hist_bpf_pull_data(tool); + if (retval) + err_msg("Error pulling BPF data\n"); - if (osnoise_trace_is_off(tool, record) && !stop_tracing) { - printf("rtla timerlat hit stop tracing\n"); + return retval; +} - if (!params->no_aa) - timerlat_auto_analysis(params->stop_us, params->stop_total_us); +static int timerlat_hist_main(struct osnoise_tool *tool) +{ + struct timerlat_params *params = to_timerlat_params(tool->params); + int retval; - return_value = FAILED; - } + if (params->mode == TRACING_MODE_TRACEFS) + retval = hist_main_loop(tool); + else + retval = timerlat_hist_bpf_main_loop(tool); -out_hist: - timerlat_aa_destroy(); - if (dma_latency_fd >= 0) - close(dma_latency_fd); - if (params->deepest_idle_state >= -1) { - for (i = 0; i < nr_cpus; i++) { - if (params->cpus && !CPU_ISSET(i, ¶ms->monitored_cpus)) - continue; - restore_cpu_idle_disable_state(i); - } - } - trace_events_destroy(&record->trace, params->events); - params->events = NULL; -out_free: - timerlat_free_histogram(tool->data); - osnoise_destroy_tool(aa); - osnoise_destroy_tool(record); - osnoise_destroy_tool(tool); - actions_destroy(¶ms->threshold_actions); - actions_destroy(¶ms->end_actions); - if (params->mode != TRACING_MODE_TRACEFS) - timerlat_bpf_destroy(); - free(params); - free_cpu_idle_disable_states(); -out_exit: - exit(return_value); + return retval; } + +struct tool_ops timerlat_hist_ops = { + .tracer = "timerlat", + .comm_prefix = "timerlat/", + .parse_args = timerlat_hist_parse_args, + .init_tool = timerlat_init_hist, + .apply_config = timerlat_hist_apply_config, + .enable = timerlat_enable, + .main = timerlat_hist_main, + .print_stats = timerlat_print_stats, + .analyze = timerlat_analyze, + .free = timerlat_free_histogram_tool, +}; diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index c80b81c0b4da..fc479a0dcb59 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -17,7 +17,6 @@ #include "timerlat.h" #include "timerlat_aa.h" -#include "timerlat_u.h" #include "timerlat_bpf.h" struct timerlat_top_cpu { @@ -49,13 +48,18 @@ struct timerlat_top_data { /* * timerlat_free_top - free runtime data */ -static void -timerlat_free_top(struct timerlat_top_data *data) +static void timerlat_free_top(struct timerlat_top_data *data) { free(data->cpu_data); free(data); } +static void timerlat_free_top_tool(struct osnoise_tool *tool) +{ + timerlat_free_top(tool->data); + timerlat_free(tool); +} + /* * timerlat_alloc_histogram - alloc runtime data */ @@ -128,12 +132,12 @@ timerlat_top_update(struct osnoise_tool *tool, int cpu, unsigned long long thread, unsigned long long latency) { - struct timerlat_params *params = tool->params; + struct timerlat_params *params = to_timerlat_params(tool->params); struct timerlat_top_data *data = tool->data; struct timerlat_top_cpu *cpu_data = &data->cpu_data[cpu]; - if (params->output_divisor) - latency = latency / params->output_divisor; + if (params->common.output_divisor) + latency = latency / params->common.output_divisor; if (!thread) { cpu_data->irq_count++; @@ -164,15 +168,13 @@ timerlat_top_handler(struct trace_seq *s, struct tep_record *record, struct tep_event *event, void *context) { struct trace_instance *trace = context; - struct timerlat_params *params; unsigned long long latency, thread; struct osnoise_tool *top; int cpu = record->cpu; top = container_of(trace, struct osnoise_tool, trace); - params = top->params; - if (!params->aa_only) { + if (!top->params->aa_only) { tep_get_field_val(s, event, "context", record, &thread, 1); tep_get_field_val(s, event, "timer_latency", record, &latency, 1); @@ -258,39 +260,40 @@ static int timerlat_top_bpf_pull_data(struct osnoise_tool *tool) static void timerlat_top_header(struct timerlat_params *params, struct osnoise_tool *top) { struct trace_seq *s = top->trace.seq; + bool pretty = params->common.pretty_output; char duration[26]; get_duration(top->start_time, duration, sizeof(duration)); - if (params->pretty_output) + if (pretty) trace_seq_printf(s, "\033[2;37;40m"); trace_seq_printf(s, " Timer Latency "); - if (params->user_data) + if (params->common.user_data) trace_seq_printf(s, " "); - if (params->pretty_output) + if (pretty) trace_seq_printf(s, "\033[0;0;0m"); trace_seq_printf(s, "\n"); trace_seq_printf(s, "%-6s | IRQ Timer Latency (%s) | Thread Timer Latency (%s)", duration, - params->output_divisor == 1 ? "ns" : "us", - params->output_divisor == 1 ? "ns" : "us"); + params->common.output_divisor == 1 ? "ns" : "us", + params->common.output_divisor == 1 ? "ns" : "us"); - if (params->user_data) { + if (params->common.user_data) { trace_seq_printf(s, " | Ret user Timer Latency (%s)", - params->output_divisor == 1 ? "ns" : "us"); + params->common.output_divisor == 1 ? "ns" : "us"); } trace_seq_printf(s, "\n"); - if (params->pretty_output) + if (pretty) trace_seq_printf(s, "\033[2;30;47m"); trace_seq_printf(s, "CPU COUNT | cur min avg max | cur min avg max"); - if (params->user_data) + if (params->common.user_data) trace_seq_printf(s, " | cur min avg max"); - if (params->pretty_output) + if (pretty) trace_seq_printf(s, "\033[0;0;0m"); trace_seq_printf(s, "\n"); } @@ -302,8 +305,7 @@ static const char *no_value = " -"; */ static void timerlat_top_print(struct osnoise_tool *top, int cpu) { - - struct timerlat_params *params = top->params; + struct timerlat_params *params = to_timerlat_params(top->params); struct timerlat_top_data *data = top->data; struct timerlat_top_cpu *cpu_data = &data->cpu_data[cpu]; struct trace_seq *s = top->trace.seq; @@ -338,7 +340,7 @@ static void timerlat_top_print(struct osnoise_tool *top, int cpu) trace_seq_printf(s, "%9llu", cpu_data->max_thread); } - if (!params->user_data) { + if (!params->common.user_data) { trace_seq_printf(s, "\n"); return; } @@ -363,7 +365,7 @@ static void timerlat_top_print_sum(struct osnoise_tool *top, struct timerlat_top_cpu *summary) { const char *split = "----------------------------------------"; - struct timerlat_params *params = top->params; + struct timerlat_params *params = to_timerlat_params(top->params); unsigned long long count = summary->irq_count; struct trace_seq *s = top->trace.seq; int e = 0; @@ -380,7 +382,7 @@ timerlat_top_print_sum(struct osnoise_tool *top, struct timerlat_top_cpu *summar } trace_seq_printf(s, "%.*s|%.*s|%.*s", 15, split, 40, split, 39, split); - if (params->user_data) + if (params->common.user_data) trace_seq_printf(s, "-|%.*s", 39, split); trace_seq_printf(s, "\n"); @@ -405,7 +407,7 @@ timerlat_top_print_sum(struct osnoise_tool *top, struct timerlat_top_cpu *summar trace_seq_printf(s, "%9llu", summary->max_thread); } - if (!params->user_data) { + if (!params->common.user_data) { trace_seq_printf(s, "\n"); return; } @@ -436,20 +438,21 @@ static void clear_terminal(struct trace_seq *seq) * timerlat_print_stats - print data for all cpus */ static void -timerlat_print_stats(struct timerlat_params *params, struct osnoise_tool *top) +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->aa_only) + if (params->common.aa_only) return; if (nr_cpus == -1) nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - if (!params->quiet) + if (!params->common.quiet) clear_terminal(trace->seq); timerlat_top_reset_sum(&summary); @@ -457,7 +460,7 @@ timerlat_print_stats(struct timerlat_params *params, struct osnoise_tool *top) timerlat_top_header(params, top); for (i = 0; i < nr_cpus; i++) { - if (params->cpus && !CPU_ISSET(i, ¶ms->monitored_cpus)) + if (params->common.cpus && !CPU_ISSET(i, ¶ms->common.monitored_cpus)) continue; timerlat_top_print(top, i); timerlat_top_update_sum(top, i, &summary); @@ -539,7 +542,7 @@ static void timerlat_top_usage(char *usage) /* * timerlat_top_parse_args - allocs, parse and fill the cmd line parameters */ -static struct timerlat_params +static struct common_params *timerlat_top_parse_args(int argc, char **argv) { struct timerlat_params *params; @@ -553,8 +556,8 @@ static struct timerlat_params if (!params) exit(1); - actions_init(¶ms->threshold_actions); - actions_init(¶ms->end_actions); + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); /* disabled by default */ params->dma_latency = -1; @@ -563,7 +566,7 @@ static struct timerlat_params params->deepest_idle_state = -2; /* display data in microseconds */ - params->output_divisor = 1000; + params->common.output_divisor = 1000; /* default to BPF mode */ params->mode = TRACING_MODE_BPF; @@ -618,8 +621,8 @@ static struct timerlat_params auto_thresh = get_llong_from_str(optarg); /* set thread stop to auto_thresh */ - params->stop_total_us = auto_thresh; - params->stop_us = auto_thresh; + params->common.stop_total_us = auto_thresh; + params->common.stop_us = auto_thresh; /* get stack trace */ params->print_stack = auto_thresh; @@ -633,37 +636,37 @@ static struct timerlat_params auto_thresh = get_llong_from_str(optarg); /* set thread stop to auto_thresh */ - params->stop_total_us = auto_thresh; - params->stop_us = auto_thresh; + params->common.stop_total_us = auto_thresh; + params->common.stop_us = auto_thresh; /* get stack trace */ params->print_stack = auto_thresh; /* set aa_only to avoid parsing the trace */ - params->aa_only = 1; + params->common.aa_only = 1; break; case 'c': - retval = parse_cpu_set(optarg, ¶ms->monitored_cpus); + retval = parse_cpu_set(optarg, ¶ms->common.monitored_cpus); if (retval) timerlat_top_usage("\nInvalid -c cpu list\n"); - params->cpus = optarg; + params->common.cpus = optarg; break; case 'C': - params->cgroup = 1; + params->common.cgroup = 1; if (!optarg) { /* will inherit this cgroup */ - params->cgroup_name = NULL; + params->common.cgroup_name = NULL; } else if (*optarg == '=') { /* skip the = */ - params->cgroup_name = ++optarg; + params->common.cgroup_name = ++optarg; } break; case 'D': config_debug = 1; break; case 'd': - params->duration = parse_seconds_duration(optarg); - if (!params->duration) + params->common.duration = parse_seconds_duration(optarg); + if (!params->common.duration) timerlat_top_usage("Invalid -d duration\n"); break; case 'e': @@ -673,30 +676,30 @@ static struct timerlat_params exit(EXIT_FAILURE); } - if (params->events) - tevent->next = params->events; - params->events = tevent; + if (params->common.events) + tevent->next = params->common.events; + params->common.events = tevent; break; case 'h': case '?': timerlat_top_usage(NULL); break; case 'H': - params->hk_cpus = 1; - retval = parse_cpu_set(optarg, ¶ms->hk_cpu_set); + params->common.hk_cpus = 1; + retval = parse_cpu_set(optarg, ¶ms->common.hk_cpu_set); if (retval) { err_msg("Error parsing house keeping CPUs\n"); exit(EXIT_FAILURE); } break; case 'i': - params->stop_us = get_llong_from_str(optarg); + params->common.stop_us = get_llong_from_str(optarg); break; case 'k': - params->kernel_workload = true; + params->common.kernel_workload = true; break; case 'n': - params->output_divisor = 1; + params->common.output_divisor = 1; break; case 'p': params->timerlat_period_us = get_llong_from_str(optarg); @@ -704,19 +707,19 @@ static struct timerlat_params timerlat_top_usage("Period longer than 1 s\n"); break; case 'P': - retval = parse_prio(optarg, ¶ms->sched_param); + retval = parse_prio(optarg, ¶ms->common.sched_param); if (retval == -1) timerlat_top_usage("Invalid -P priority"); - params->set_sched = 1; + params->common.set_sched = 1; break; case 'q': - params->quiet = 1; + params->common.quiet = 1; break; case 's': params->print_stack = get_llong_from_str(optarg); break; case 'T': - params->stop_total_us = get_llong_from_str(optarg); + params->common.stop_total_us = get_llong_from_str(optarg); break; case 't': if (optarg) { @@ -730,14 +733,14 @@ static struct timerlat_params trace_output = "timerlat_trace.txt"; break; case 'u': - params->user_workload = true; + params->common.user_workload = true; /* fallback: -u implies -U */ case 'U': - params->user_data = true; + params->common.user_data = true; break; case '0': /* trigger */ - if (params->events) { - retval = trace_event_add_trigger(params->events, optarg); + if (params->common.events) { + retval = trace_event_add_trigger(params->common.events, optarg); if (retval) { err_msg("Error adding trigger %s\n", optarg); exit(EXIT_FAILURE); @@ -747,8 +750,8 @@ static struct timerlat_params } break; case '1': /* filter */ - if (params->events) { - retval = trace_event_add_filter(params->events, optarg); + if (params->common.events) { + retval = trace_event_add_filter(params->common.events, optarg); if (retval) { err_msg("Error adding filter %s\n", optarg); exit(EXIT_FAILURE); @@ -771,23 +774,25 @@ static struct timerlat_params params->dump_tasks = 1; break; case '6': - params->warmup = get_llong_from_str(optarg); + params->common.warmup = get_llong_from_str(optarg); break; case '7': - params->buffer_size = get_llong_from_str(optarg); + params->common.buffer_size = get_llong_from_str(optarg); break; case '8': params->deepest_idle_state = get_llong_from_str(optarg); break; case '9': - retval = actions_parse(¶ms->threshold_actions, optarg); + retval = actions_parse(¶ms->common.threshold_actions, optarg, + "timerlat_trace.txt"); if (retval) { err_msg("Invalid action %s\n", optarg); exit(EXIT_FAILURE); } break; case '\1': - retval = actions_parse(¶ms->end_actions, optarg); + retval = actions_parse(¶ms->common.end_actions, optarg, + "timerlat_trace.txt"); if (retval) { err_msg("Invalid action %s\n", optarg); exit(EXIT_FAILURE); @@ -799,7 +804,7 @@ static struct timerlat_params } if (trace_output) - actions_add_trace_output(¶ms->threshold_actions, trace_output); + actions_add_trace_output(¶ms->common.threshold_actions, trace_output); if (geteuid()) { err_msg("rtla needs root permission\n"); @@ -809,13 +814,13 @@ static struct timerlat_params /* * Auto analysis only happens if stop tracing, thus: */ - if (!params->stop_us && !params->stop_total_us) + if (!params->common.stop_us && !params->common.stop_total_us) params->no_aa = 1; - if (params->no_aa && params->aa_only) + if (params->no_aa && params->common.aa_only) timerlat_top_usage("--no-aa and --aa-only are mutually exclusive!"); - if (params->kernel_workload && params->user_workload) + if (params->common.kernel_workload && params->common.user_workload) timerlat_top_usage("--kernel-threads and --user-threads are mutually exclusive!"); /* @@ -823,27 +828,29 @@ static struct timerlat_params * mixed mode */ if (params->mode == TRACING_MODE_BPF && - (params->threshold_actions.present[ACTION_TRACE_OUTPUT] || - params->end_actions.present[ACTION_TRACE_OUTPUT] || !params->no_aa)) + (params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] || + params->common.end_actions.present[ACTION_TRACE_OUTPUT] || + !params->no_aa)) params->mode = TRACING_MODE_MIXED; - return params; + return ¶ms->common; } /* * timerlat_top_apply_config - apply the top configs to the initialized tool */ static int -timerlat_top_apply_config(struct osnoise_tool *top, struct timerlat_params *params) +timerlat_top_apply_config(struct osnoise_tool *top) { + struct timerlat_params *params = to_timerlat_params(top->params); int retval; retval = timerlat_apply_config(top, params); if (retval) goto out_err; - if (isatty(STDOUT_FILENO) && !params->quiet) - params->pretty_output = 1; + if (isatty(STDOUT_FILENO) && !params->common.quiet) + params->common.pretty_output = 1; return 0; @@ -855,7 +862,7 @@ out_err: * timerlat_init_top - initialize a timerlat top tool with parameters */ static struct osnoise_tool -*timerlat_init_top(struct timerlat_params *params) +*timerlat_init_top(struct common_params *params) { struct osnoise_tool *top; int nr_cpus; @@ -870,8 +877,6 @@ static struct osnoise_tool if (!top->data) goto out_err; - top->params = params; - tep_register_event_handler(top->trace.tep, -1, "ftrace", "timerlat", timerlat_top_handler, top); @@ -882,109 +887,16 @@ out_err: return NULL; } -static int stop_tracing; -static struct trace_instance *top_inst = NULL; -static void stop_top(int sig) -{ - if (stop_tracing) { - /* - * Stop requested twice in a row; abort event processing and - * exit immediately - */ - tracefs_iterate_stop(top_inst->inst); - return; - } - stop_tracing = 1; - if (top_inst) - trace_instance_stop(top_inst); -} - -/* - * timerlat_top_set_signals - handles the signal to stop the tool - */ -static void -timerlat_top_set_signals(struct timerlat_params *params) -{ - signal(SIGINT, stop_top); - if (params->duration) { - signal(SIGALRM, stop_top); - alarm(params->duration); - } -} - -/* - * timerlat_top_main_loop - main loop to process events - */ -static int -timerlat_top_main_loop(struct osnoise_tool *top, - struct osnoise_tool *record, - struct osnoise_tool *aa, - struct timerlat_params *params, - struct timerlat_u_params *params_u) -{ - struct trace_instance *trace = &top->trace; - int retval; - - while (!stop_tracing) { - sleep(params->sleep_time); - - if (params->aa_only && !osnoise_trace_is_off(top, record)) - continue; - - retval = tracefs_iterate_raw_events(trace->tep, - trace->inst, - NULL, - 0, - collect_registered_events, - trace); - if (retval < 0) { - err_msg("Error iterating on events\n"); - return retval; - } - - if (!params->quiet) - timerlat_print_stats(params, top); - - if (osnoise_trace_is_off(top, record)) { - actions_perform(¶ms->threshold_actions); - - if (!params->threshold_actions.continue_flag) - /* continue flag not set, break */ - break; - - /* continue action reached, re-enable tracing */ - if (record) - trace_instance_start(&record->trace); - if (!params->no_aa) - trace_instance_start(&aa->trace); - trace_instance_start(trace); - } - - /* is there still any user-threads ? */ - if (params->user_workload) { - if (params_u->stopped_running) { - debug_msg("timerlat user space threads stopped!\n"); - break; - } - } - } - - return 0; -} - /* * timerlat_top_bpf_main_loop - main loop to process events (BPF variant) */ static int -timerlat_top_bpf_main_loop(struct osnoise_tool *top, - struct osnoise_tool *record, - struct osnoise_tool *aa, - struct timerlat_params *params, - struct timerlat_u_params *params_u) +timerlat_top_bpf_main_loop(struct osnoise_tool *tool) { + struct timerlat_params *params = to_timerlat_params(tool->params); int retval, wait_retval; - if (params->aa_only) { + if (params->common.aa_only) { /* Auto-analysis only, just wait for stop tracing */ timerlat_bpf_wait(-1); return 0; @@ -992,36 +904,37 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *top, /* Pull and display data in a loop */ while (!stop_tracing) { - wait_retval = timerlat_bpf_wait(params->quiet ? -1 : params->sleep_time); + wait_retval = timerlat_bpf_wait(params->common.quiet ? -1 : + params->common.sleep_time); - retval = timerlat_top_bpf_pull_data(top); + retval = timerlat_top_bpf_pull_data(tool); if (retval) { err_msg("Error pulling BPF data\n"); return retval; } - if (!params->quiet) - timerlat_print_stats(params, top); + if (!params->common.quiet) + timerlat_print_stats(tool); if (wait_retval == 1) { /* Stopping requested by tracer */ - actions_perform(¶ms->threshold_actions); + actions_perform(¶ms->common.threshold_actions); - if (!params->threshold_actions.continue_flag) + if (!params->common.threshold_actions.continue_flag) /* continue flag not set, break */ break; /* continue action reached, re-enable tracing */ - if (record) - trace_instance_start(&record->trace); - if (!params->no_aa) - trace_instance_start(&aa->trace); + if (tool->record) + trace_instance_start(&tool->record->trace); + if (tool->aa) + trace_instance_start(&tool->aa->trace); timerlat_bpf_restart_tracing(); } /* is there still any user-threads ? */ - if (params->user_workload) { - if (params_u->stopped_running) { + if (params->common.user_workload) { + if (params->common.user.stopped_running) { debug_msg("timerlat user space threads stopped!\n"); break; } @@ -1031,273 +944,30 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *top, return 0; } -int timerlat_top_main(int argc, char *argv[]) +static int timerlat_top_main_loop(struct osnoise_tool *tool) { - struct timerlat_params *params; - struct osnoise_tool *record = NULL; - struct timerlat_u_params params_u; - enum result return_value = ERROR; - struct osnoise_tool *top = NULL; - struct osnoise_tool *aa = NULL; - struct trace_instance *trace; - int dma_latency_fd = -1; - pthread_t timerlat_u; - char *max_lat; + struct timerlat_params *params = to_timerlat_params(tool->params); int retval; - int nr_cpus, i; - params = timerlat_top_parse_args(argc, argv); - if (!params) - exit(1); - - top = timerlat_init_top(params); - if (!top) { - err_msg("Could not init osnoise top\n"); - goto out_exit; - } - - trace = &top->trace; - /* - * Save trace instance into global variable so that SIGINT can stop - * the timerlat tracer. - * Otherwise, rtla could loop indefinitely when overloaded. - */ - top_inst = trace; - - /* - * 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) { - debug_msg("RTLA_NO_BPF set, disabling BPF\n"); - params->mode = TRACING_MODE_TRACEFS; - } else if (!tep_find_event_by_name(trace->tep, "osnoise", "timerlat_sample")) { - debug_msg("osnoise:timerlat_sample missing, disabling BPF\n"); - params->mode = TRACING_MODE_TRACEFS; - } else { - retval = timerlat_bpf_init(params); - if (retval) { - debug_msg("Could not enable BPF\n"); - params->mode = TRACING_MODE_TRACEFS; - } - } - - retval = timerlat_top_apply_config(top, params); - if (retval) { - err_msg("Could not apply config\n"); - goto out_free; - } - - retval = enable_timerlat(trace); - if (retval) { - err_msg("Failed to enable timerlat tracer\n"); - goto out_free; - } - - if (params->set_sched) { - retval = set_comm_sched_attr("timerlat/", ¶ms->sched_param); - if (retval) { - err_msg("Failed to set sched parameters\n"); - goto out_free; - } - } - - if (params->cgroup && !params->user_data) { - retval = set_comm_cgroup("timerlat/", params->cgroup_name); - if (!retval) { - err_msg("Failed to move threads to cgroup\n"); - goto out_free; - } - } - - if (params->dma_latency >= 0) { - dma_latency_fd = set_cpu_dma_latency(params->dma_latency); - if (dma_latency_fd < 0) { - err_msg("Could not set /dev/cpu_dma_latency.\n"); - goto out_free; - } - } - - if (params->deepest_idle_state >= -1) { - if (!have_libcpupower_support()) { - err_msg("rtla built without libcpupower, --deepest-idle-state is not supported\n"); - goto out_free; - } - - nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - - for (i = 0; i < nr_cpus; i++) { - if (params->cpus && !CPU_ISSET(i, ¶ms->monitored_cpus)) - continue; - if (save_cpu_idle_disable_state(i) < 0) { - err_msg("Could not save cpu idle state.\n"); - goto out_free; - } - if (set_deepest_cpu_idle_state(i, params->deepest_idle_state) < 0) { - err_msg("Could not set deepest cpu idle state.\n"); - goto out_free; - } - } - } - - if (params->threshold_actions.present[ACTION_TRACE_OUTPUT] || - params->end_actions.present[ACTION_TRACE_OUTPUT]) { - record = osnoise_init_trace_tool("timerlat"); - if (!record) { - err_msg("Failed to enable the trace instance\n"); - goto out_free; - } - params->threshold_actions.trace_output_inst = record->trace.inst; - params->end_actions.trace_output_inst = record->trace.inst; - - if (params->events) { - retval = trace_events_enable(&record->trace, params->events); - if (retval) - goto out_top; - } - - if (params->buffer_size > 0) { - retval = trace_set_buffer_size(&record->trace, params->buffer_size); - if (retval) - goto out_top; - } - } - - if (!params->no_aa) { - aa = osnoise_init_tool("timerlat_aa"); - if (!aa) - goto out_top; - - retval = timerlat_aa_init(aa, params->dump_tasks); - if (retval) { - err_msg("Failed to enable the auto analysis instance\n"); - goto out_top; - } - - /* if it is re-using the main instance, there is no need to start it */ - if (aa != top) { - retval = enable_timerlat(&aa->trace); - if (retval) { - err_msg("Failed to enable timerlat tracer\n"); - goto out_top; - } - } - } - - if (params->user_workload) { - /* rtla asked to stop */ - params_u.should_run = 1; - /* all threads left */ - params_u.stopped_running = 0; - - params_u.set = ¶ms->monitored_cpus; - if (params->set_sched) - params_u.sched_param = ¶ms->sched_param; - else - params_u.sched_param = NULL; - - params_u.cgroup_name = params->cgroup_name; - - retval = pthread_create(&timerlat_u, NULL, timerlat_u_dispatcher, ¶ms_u); - if (retval) - err_msg("Error creating timerlat user-space threads\n"); - } - - if (params->warmup > 0) { - debug_msg("Warming up for %d seconds\n", params->warmup); - sleep(params->warmup); - } - - /* - * Start the tracers here, after having set all instances. - * - * Let the trace instance start first for the case of hitting a stop - * tracing while enabling other instances. The trace instance is the - * one with most valuable information. - */ - if (record) - trace_instance_start(&record->trace); - if (!params->no_aa) - trace_instance_start(&aa->trace); if (params->mode == TRACING_MODE_TRACEFS) { - trace_instance_start(trace); + retval = top_main_loop(tool); } else { - retval = timerlat_bpf_attach(); - if (retval) { - err_msg("Error attaching BPF program\n"); - goto out_top; - } - } - - top->start_time = time(NULL); - timerlat_top_set_signals(params); - - if (params->mode == TRACING_MODE_TRACEFS) - retval = timerlat_top_main_loop(top, record, aa, params, ¶ms_u); - else - retval = timerlat_top_bpf_main_loop(top, record, aa, params, ¶ms_u); - - if (retval) - goto out_top; - - if (params->mode != TRACING_MODE_TRACEFS) + retval = timerlat_top_bpf_main_loop(tool); timerlat_bpf_detach(); - - if (params->user_workload && !params_u.stopped_running) { - params_u.should_run = 0; - sleep(1); } - timerlat_print_stats(params, top); - - actions_perform(¶ms->end_actions); - - return_value = PASSED; - - if (osnoise_trace_is_off(top, record) && !stop_tracing) { - printf("rtla timerlat hit stop tracing\n"); - - if (!params->no_aa) - timerlat_auto_analysis(params->stop_us, params->stop_total_us); - - return_value = FAILED; - } else if (params->aa_only) { - /* - * If the trace did not stop with --aa-only, at least print the - * max known latency. - */ - max_lat = tracefs_instance_file_read(trace->inst, "tracing_max_latency", NULL); - if (max_lat) { - printf(" Max latency was %s\n", max_lat); - free(max_lat); - } - } - -out_top: - timerlat_aa_destroy(); - if (dma_latency_fd >= 0) - close(dma_latency_fd); - if (params->deepest_idle_state >= -1) { - for (i = 0; i < nr_cpus; i++) { - if (params->cpus && !CPU_ISSET(i, ¶ms->monitored_cpus)) - continue; - restore_cpu_idle_disable_state(i); - } - } - trace_events_destroy(&record->trace, params->events); - params->events = NULL; -out_free: - timerlat_free_top(top->data); - if (aa && aa != top) - osnoise_destroy_tool(aa); - osnoise_destroy_tool(record); - osnoise_destroy_tool(top); - actions_destroy(¶ms->threshold_actions); - actions_destroy(¶ms->end_actions); - if (params->mode != TRACING_MODE_TRACEFS) - timerlat_bpf_destroy(); - free(params); - free_cpu_idle_disable_states(); -out_exit: - exit(return_value); + return retval; } + +struct tool_ops timerlat_top_ops = { + .tracer = "timerlat", + .comm_prefix = "timerlat/", + .parse_args = timerlat_top_parse_args, + .init_tool = timerlat_init_top, + .apply_config = timerlat_top_apply_config, + .enable = timerlat_enable, + .main = timerlat_top_main_loop, + .print_stats = timerlat_print_stats, + .analyze = timerlat_analyze, + .free = timerlat_free_top_tool, +}; diff --git a/tools/tracing/rtla/src/trace.h b/tools/tracing/rtla/src/trace.h index 3cd40dd3f06c..1e5aee4b828d 100644 --- a/tools/tracing/rtla/src/trace.h +++ b/tools/tracing/rtla/src/trace.h @@ -30,9 +30,6 @@ struct trace_seq *get_trace_seq(void); int enable_tracer_by_name(struct tracefs_instance *inst, const char *tracer_name); void disable_tracer(struct tracefs_instance *inst); -int enable_osnoise(struct trace_instance *trace); -int enable_timerlat(struct trace_instance *trace); - struct tracefs_instance *create_instance(char *instance_name); void destroy_instance(struct tracefs_instance *inst); diff --git a/tools/tracing/rtla/tests/engine.sh b/tools/tracing/rtla/tests/engine.sh index a97d644ead99..c7de3d6ed6a8 100644 --- a/tools/tracing/rtla/tests/engine.sh +++ b/tools/tracing/rtla/tests/engine.sh @@ -43,6 +43,7 @@ check() { tested_command=$1 expected_exitcode=${3:-0} expected_output=$4 + unexpected_output=$5 # Simple check: run rtla with given arguments and test exit code. # If TEST_COUNT is set, run the test. Otherwise, just count. ctr=$(($ctr + 1)) @@ -53,24 +54,33 @@ check() { # Run rtla; in case of failure, include its output as comment # in the test results. result=$(eval stdbuf -oL $TIMEOUT "$RTLA" $2 2>&1); exitcode=$? + failbuf='' + fail=0 + # Test if the results matches if requested - if [ -n "$expected_output" ] + if [ -n "$expected_output" ] && ! grep -qE "$expected_output" <<< "$result" then - grep -E "$expected_output" <<< "$result" > /dev/null; grep_result=$? - else - grep_result=0 + fail=1 + failbuf+=$(printf "# Output match failed: \"%s\"" "$expected_output") + failbuf+=$'\n' fi - if [ $exitcode -eq $expected_exitcode ] && [ $grep_result -eq 0 ] + if [ -n "$unexpected_output" ] && grep -qE "$unexpected_output" <<< "$result" + then + fail=1 + failbuf+=$(printf "# Output non-match failed: \"%s\"" "$unexpected_output") + failbuf+=$'\n' + fi + + if [ $exitcode -eq $expected_exitcode ] && [ $fail -eq 0 ] then echo "ok $ctr - $1" else - echo "not ok $ctr - $1" # Add rtla output and exit code as comments in case of failure + echo "not ok $ctr - $1" + echo -n "$failbuf" echo "$result" | col -b | while read line; do echo "# $line"; done printf "#\n# exit code %s\n" $exitcode - [ -n "$expected_output" ] && [ $grep_result -ne 0 ] && \ - printf "# Output match failed: \"%s\"\n" "$expected_output" fi fi } diff --git a/tools/tracing/rtla/tests/osnoise.t b/tools/tracing/rtla/tests/osnoise.t index 7574ec6a5a53..e3c89d45a6bb 100644 --- a/tools/tracing/rtla/tests/osnoise.t +++ b/tools/tracing/rtla/tests/osnoise.t @@ -8,7 +8,8 @@ set_timeout 2m check "verify help page" \ "osnoise --help" 0 "osnoise version" check "verify the --priority/-P param" \ - "osnoise top -P F:1 -c 0 -r 900000 -d 10s -q" + "osnoise top -P F:1 -c 0 -r 900000 -d 10s -q -S 1 --on-threshold shell,command=\"tests/scripts/check-priority.sh osnoise/ SCHED_FIFO 1\"" \ + 2 "Priorities are set correctly" check "verify the --stop/-s param" \ "osnoise top -s 30 -T 1" 2 "osnoise hit stop tracing" check "verify the --trace param" \ @@ -22,4 +23,28 @@ check "verify the --entries/-E param" \ check_with_osnoise_options "apply default period" \ "osnoise hist -s 1" 2 period_us=600000000 +# Actions tests +check "trace output through -t with custom filename" \ + "osnoise hist -S 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" +check "trace output through --on-threshold trace" \ + "osnoise hist -S 2 --on-threshold trace" 2 "^ Saving trace to osnoise_trace.txt$" +check "trace output through --on-threshold trace with custom filename" \ + "osnoise hist -S 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" +check "exec command" \ + "osnoise hist -S 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$" +check "multiple actions" \ + "osnoise hist -S 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$" +check "hist stop at failed action" \ + "osnoise hist -S 2 --on-threshold shell,command='echo -n 1; false' --on-threshold shell,command='echo -n 2'" 2 "^1# RTLA osnoise histogram$" +check "top stop at failed action" \ + "timerlat top -T 2 --on-threshold shell,command='echo -n abc; false' --on-threshold shell,command='echo -n defgh'" 2 "^abc" "defgh" +check "hist with continue" \ + "osnoise hist -S 2 -d 1s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$" +check "top with continue" \ + "osnoise top -q -S 2 -d 1s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$" +check "hist with trace output at end" \ + "osnoise hist -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$" +check "top with trace output at end" \ + "osnoise top -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$" + test_end diff --git a/tools/tracing/rtla/tests/timerlat.t b/tools/tracing/rtla/tests/timerlat.t index c71aed5534bf..b5d1e7260a9b 100644 --- a/tools/tracing/rtla/tests/timerlat.t +++ b/tools/tracing/rtla/tests/timerlat.t @@ -47,9 +47,9 @@ check "trace output through -t" \ "timerlat hist -T 2 -t" 2 "^ Saving trace to timerlat_trace.txt$" check "trace output through -t with custom filename" \ "timerlat hist -T 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" -check "trace output through -A trace" \ +check "trace output through --on-threshold trace" \ "timerlat hist -T 2 --on-threshold trace" 2 "^ Saving trace to timerlat_trace.txt$" -check "trace output through -A trace with custom filename" \ +check "trace output through --on-threshold trace with custom filename" \ "timerlat hist -T 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" check "exec command" \ "timerlat hist -T 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$" |