Instead of using getopt_long() directly to parse the command line arguments given to an RTLA tool, use libsubcmd's parse_options().
Utilizing libsubcmd for parsing command line arguments has several benefits: - A help message is automatically generated by libsubcmd from the specification, removing the need of writing it by hand. - Options are sorted into groups based on which part of tracing (CPU, thread, auto-analysis, tuning, histogram) they relate to. - Common parsing patterns for numerical and boolean values now share code, with the target variable being stored in the option array. To avoid duplication of the option parsing logic, RTLA-specific macros defining struct option values are created: - RTLA_OPT_* for options common to all tools - OSNOISE_OPT_* and TIMERLAT_OPT_* for options specific to osnoise/timerlat tools , HIST_OPT_* macros for options specific to histogram-based tools. Individual *_parse_args() functions then construct an array out of these macros that is then passed to libsubcmd's parse_options(). All code specific to command line options parsing is moved out of the individual tool files into a new file, cli.c, which also contains the contents of the rtla.c file. The return value of tool-level help option changes to 129, as this is the value set by libsubcmd; this is reflected in affected test cases. The implementation of help for command-level and tracer-level help remains the same. Assisted-by: Composer:composer-1.5 Signed-off-by: Tomas Glozar <[email protected]> --- tools/tracing/rtla/src/Build | 2 +- tools/tracing/rtla/src/cli.c | 1207 ++++++++++++++++++++++++ tools/tracing/rtla/src/cli.h | 7 + tools/tracing/rtla/src/common.c | 109 --- tools/tracing/rtla/src/common.h | 26 +- tools/tracing/rtla/src/osnoise_hist.c | 221 +---- tools/tracing/rtla/src/osnoise_top.c | 200 +--- tools/tracing/rtla/src/rtla.c | 89 -- tools/tracing/rtla/src/timerlat.h | 4 +- tools/tracing/rtla/src/timerlat_hist.c | 317 +------ tools/tracing/rtla/src/timerlat_top.c | 285 +----- tools/tracing/rtla/src/utils.c | 28 +- tools/tracing/rtla/src/utils.h | 3 +- tools/tracing/rtla/tests/hwnoise.t | 2 +- 14 files changed, 1236 insertions(+), 1264 deletions(-) create mode 100644 tools/tracing/rtla/src/cli.c create mode 100644 tools/tracing/rtla/src/cli.h delete mode 100644 tools/tracing/rtla/src/rtla.c diff --git a/tools/tracing/rtla/src/Build b/tools/tracing/rtla/src/Build index 329e24a40cf7..a1f3ab927207 100644 --- a/tools/tracing/rtla/src/Build +++ b/tools/tracing/rtla/src/Build @@ -11,4 +11,4 @@ rtla-y += timerlat_hist.o rtla-y += timerlat_u.o rtla-y += timerlat_aa.o rtla-y += timerlat_bpf.o -rtla-y += rtla.o +rtla-y += cli.o diff --git a/tools/tracing/rtla/src/cli.c b/tools/tracing/rtla/src/cli.c new file mode 100644 index 000000000000..d029a698e8a7 --- /dev/null +++ b/tools/tracing/rtla/src/cli.c @@ -0,0 +1,1207 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <[email protected]> + */ + +#define _GNU_SOURCE +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> + +#include <linux/kernel.h> +#include <subcmd/parse-options.h> + +#include "cli.h" +#include "osnoise.h" +#include "timerlat.h" + +struct osnoise_cb_data { + struct osnoise_params *params; + char *trace_output; +}; + +struct timerlat_cb_data { + struct timerlat_params *params; + char *trace_output; +}; + +static const char * const osnoise_top_usage[] = { + "rtla osnoise [top] [<options>]", + NULL, +}; + +static const char * const osnoise_hist_usage[] = { + "rtla osnoise hist [<options>]", + NULL, +}; + +static const char * const timerlat_top_usage[] = { + "rtla timerlat [top] [<options>]", + NULL, +}; + +static const char * const timerlat_hist_usage[] = { + "rtla timerlat hist [<options>]", + NULL, +}; + +static const char * const hwnoise_usage[] = { + "rtla hwnoise [<options>]", + NULL, +}; + +static const int common_parse_options_flags = PARSE_OPT_OPTARG_ALLOW_NEXT; + +/* + * Macros for command line options common to all tools + * + * Note: Some of the options are common to both timerlat and osnoise, but + * have a slightly different meaning. Such options take additional arguments + * that have to be provided by the *_parse_args() function of the corresponding + * tool. + * + * All macros defined here assume the presence of a params variable of + * the corresponding tool type (i.e struct timerlat_params or struct osnoise_params) + * and a cb_data variable of the matching type. + */ + +#define RTLA_OPT_STOP(short, long, name) OPT_CALLBACK(short, long, \ + ¶ms->common.stop_us, \ + "us", \ + "stop trace if " name " is higher than the argument in us", \ + opt_llong_callback) + +#define RTLA_OPT_STOP_TOTAL(short, long, name) OPT_CALLBACK(short, long, \ + ¶ms->common.stop_total_us, \ + "us", \ + "stop trace if " name " is higher than the argument in us", \ + opt_llong_callback) + +#define RTLA_OPT_TRACE_OUTPUT(tracer, cb) OPT_CALLBACK_OPTARG('t', "trace", \ + (const char **)&cb_data.trace_output, \ + tracer "_trace.txt", \ + "[file]", \ + "save the stopped trace to [file|" tracer "_trace.txt]", \ + cb) + +#define RTLA_OPT_CPUS OPT_CALLBACK('c', "cpus", ¶ms->common, \ + "cpu-list", \ + "run the tracer only on the given cpus", \ + opt_cpus_cb) + +#define RTLA_OPT_CGROUP OPT_CALLBACK_OPTARG('C', "cgroup", ¶ms->common, \ + "[cgroup_name]", NULL, \ + "set cgroup, no argument means rtla's cgroup will be inherited", \ + opt_cgroup_cb) + +#define RTLA_OPT_USER_THREADS OPT_CALLBACK('u', "user-threads", params, NULL, \ + "use rtla user-space threads instead of kernel-space timerlat threads", \ + opt_user_threads_cb) + +#define RTLA_OPT_KERNEL_THREADS OPT_BOOLEAN('k', "kernel-threads", \ + ¶ms->common.kernel_workload, \ + "use timerlat kernel-space threads instead of rtla user-space threads") + +#define RTLA_OPT_USER_LOAD OPT_BOOLEAN('U', "user-load", ¶ms->common.user_data, \ + "enable timerlat for user-defined user-space workload") + +#define RTLA_OPT_DURATION OPT_CALLBACK('d', "duration", ¶ms->common, \ + "time[s|m|h|d]", \ + "set the duration of the session", \ + opt_duration_cb) + +#define RTLA_OPT_EVENT OPT_CALLBACK('e', "event", ¶ms->common.events, \ + "sys:event", \ + "enable the <sys:event> in the trace instance, multiple -e are allowed", \ + opt_event_cb) + +#define RTLA_OPT_HOUSEKEEPING OPT_CALLBACK('H', "house-keeping", ¶ms->common, \ + "cpu-list", \ + "run rtla control threads only on the given cpus", \ + opt_housekeeping_cb) + +#define RTLA_OPT_PRIORITY OPT_CALLBACK('P', "priority", ¶ms->common, \ + "o:prio|r:prio|f:prio|d:runtime:period", \ + "set scheduling parameters", \ + opt_priority_cb) + +#define RTLA_OPT_TRIGGER OPT_CALLBACK(0, "trigger", ¶ms->common, \ + "trigger", \ + "enable a trace event trigger to the previous -e event", \ + opt_trigger_cb) + +#define RTLA_OPT_FILTER OPT_CALLBACK(0, "filter", ¶ms->common, \ + "filter", \ + "enable a trace event filter to the previous -e event", \ + opt_filter_cb) + +#define RTLA_OPT_QUIET OPT_BOOLEAN('q', "quiet", ¶ms->common.quiet, \ + "print only a summary at the end") + +#define RTLA_OPT_TRACE_BUFFER_SIZE OPT_CALLBACK(0, "trace-buffer-size", \ + ¶ms->common.buffer_size, "kB", \ + "set the per-cpu trace buffer size in kB", \ + opt_int_callback) + +#define RTLA_OPT_WARM_UP OPT_CALLBACK(0, "warm-up", ¶ms->common.warmup, "s", \ + "let the workload run for s seconds before collecting data", \ + opt_int_callback) + +#define RTLA_OPT_AUTO(cb) OPT_CALLBACK('a', "auto", &cb_data, "us", \ + "set automatic trace mode, stopping the session if argument in us sample is hit", \ + cb) + +#define RTLA_OPT_ON_THRESHOLD(threshold, cb) OPT_CALLBACK(0, "on-threshold", \ + ¶ms->common.threshold_actions, \ + "action", \ + "define action to be executed at " threshold " threshold, multiple are allowed", \ + cb) + +#define RTLA_OPT_ON_END(cb) OPT_CALLBACK(0, "on-end", ¶ms->common.end_actions, \ + "action", \ + "define action to be executed at measurement end, multiple are allowed", \ + cb) + +#define RTLA_OPT_DEBUG OPT_BOOLEAN('D', "debug", &config_debug, \ + "print debug info") + +#define RTLA_OPT_HELP OPT_BOOLEAN('h', "help", (bool *)NULL, \ + "show help") + +/* + * Common callback functions for command line options + */ + +static int opt_llong_callback(const struct option *opt, const char *arg, int unset) +{ + long long *value = opt->value; + + if (unset || !arg) + return -1; + + *value = get_llong_from_str((char *)arg); + return 0; +} + +static int opt_int_callback(const struct option *opt, const char *arg, int unset) +{ + int *value = opt->value; + + if (unset || !arg) + return -1; + + if (strtoi(arg, value)) + return -1; + + return 0; +} + +static int opt_cpus_cb(const struct option *opt, const char *arg, int unset) +{ + struct common_params *params = opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval = parse_cpu_set((char *)arg, ¶ms->monitored_cpus); + if (retval) + fatal("Invalid -c cpu list"); + params->cpus = (char *)arg; + + return 0; +} + +static int opt_cgroup_cb(const struct option *opt, const char *arg, int unset) +{ + struct common_params *params = opt->value; + + params->cgroup = 1; + params->cgroup_name = (char *)arg; + if (params->cgroup_name && params->cgroup_name[0] == '=') + /* Allow -C=<cgroup_name> next to -C[ ]<cgroup_name> */ + ++params->cgroup_name; + + return 0; +} + +static int opt_duration_cb(const struct option *opt, const char *arg, int unset) +{ + struct common_params *params = opt->value; + + if (unset || !arg) + return -1; + + params->duration = parse_seconds_duration((char *)arg); + if (!params->duration) + fatal("Invalid -d duration"); + + return 0; +} + +static int opt_event_cb(const struct option *opt, const char *arg, int unset) +{ + struct trace_events **events = opt->value; + struct trace_events *tevent; + + if (unset || !arg) + return -1; + + tevent = trace_event_alloc((char *)arg); + if (!tevent) + fatal("Error alloc trace event"); + + if (*events) + tevent->next = *events; + *events = tevent; + + return 0; +} + +static int opt_housekeeping_cb(const struct option *opt, const char *arg, int unset) +{ + struct common_params *params = opt->value; + int retval; + + if (unset || !arg) + return -1; + + params->hk_cpus = 1; + retval = parse_cpu_set((char *)arg, ¶ms->hk_cpu_set); + if (retval) + fatal("Error parsing house keeping CPUs"); + + return 0; +} + +static int opt_priority_cb(const struct option *opt, const char *arg, int unset) +{ + struct common_params *params = opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval = parse_prio((char *)arg, ¶ms->sched_param); + if (retval == -1) + fatal("Invalid -P priority"); + params->set_sched = 1; + + return 0; +} + +static int opt_trigger_cb(const struct option *opt, const char *arg, int unset) +{ + struct common_params *params = opt->value; + + if (unset || !arg) + return -1; + + if (!params->events) + fatal("--trigger requires a previous -e"); + + trace_event_add_trigger(params->events, (char *)arg); + + return 0; +} + +static int opt_filter_cb(const struct option *opt, const char *arg, int unset) +{ + struct common_params *params = opt->value; + + if (unset || !arg) + return -1; + + if (!params->events) + fatal("--filter requires a previous -e"); + + trace_event_add_filter(params->events, (char *)arg); + + return 0; +} + +/* + * Macros for command line options specific to osnoise + */ +#define OSNOISE_OPT_PERIOD OPT_CALLBACK('p', "period", ¶ms->period, "us", \ + "osnoise period in us", \ + opt_osnoise_period_cb) + +#define OSNOISE_OPT_RUNTIME OPT_CALLBACK('r', "runtime", ¶ms->runtime, "us", \ + "osnoise runtime in us", \ + opt_osnoise_runtime_cb) + +#define OSNOISE_OPT_THRESHOLD OPT_CALLBACK('T', "threshold", ¶ms->threshold, "us", \ + "the minimum delta to be considered a noise", \ + opt_osnoise_threshold_cb) + +/* + * Callback functions for command line options for osnoise tools + */ + +static int opt_osnoise_auto_cb(const struct option *opt, const char *arg, int unset) +{ + struct osnoise_cb_data *cb_data = opt->value; + struct osnoise_params *params = cb_data->params; + long long auto_thresh; + + if (unset || !arg) + return -1; + + auto_thresh = get_llong_from_str((char *)arg); + params->common.stop_us = auto_thresh; + params->threshold = 1; + + if (!cb_data->trace_output) + cb_data->trace_output = "osnoise_trace.txt"; + + return 0; +} + +static int opt_osnoise_period_cb(const struct option *opt, const char *arg, int unset) +{ + unsigned long long *period = opt->value; + + if (unset || !arg) + return -1; + + *period = get_llong_from_str((char *)arg); + if (*period > 10000000) + fatal("Period longer than 10 s"); + + return 0; +} + +static int opt_osnoise_runtime_cb(const struct option *opt, const char *arg, int unset) +{ + unsigned long long *runtime = opt->value; + + if (unset || !arg) + return -1; + + *runtime = get_llong_from_str((char *)arg); + if (*runtime < 100) + fatal("Runtime shorter than 100 us"); + + return 0; +} + +static int opt_osnoise_trace_output_cb(const struct option *opt, const char *arg, int unset) +{ + const char **trace_output = opt->value; + + if (unset) + return -1; + + if (!arg) { + *trace_output = "osnoise_trace.txt"; + } else { + *trace_output = (char *)arg; + if (*trace_output && (*trace_output)[0] == '=') + /* Allow -t=<trace_output> next to -t[ ]<trace_output> */ + ++*trace_output; + } + + return 0; +} + +static int opt_osnoise_threshold_cb(const struct option *opt, const char *arg, int unset) +{ + long long *threshold = opt->value; + + if (unset || !arg) + return -1; + + *threshold = get_llong_from_str((char *)arg); + + return 0; +} + +static int opt_osnoise_on_threshold_cb(const struct option *opt, const char *arg, int unset) +{ + struct actions *actions = opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval = actions_parse(actions, (char *)arg, "osnoise_trace.txt"); + if (retval) + fatal("Invalid action %s", arg); + + return 0; +} + +static int opt_osnoise_on_end_cb(const struct option *opt, const char *arg, int unset) +{ + struct actions *actions = opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval = actions_parse(actions, (char *)arg, "osnoise_trace.txt"); + if (retval) + fatal("Invalid action %s", arg); + + return 0; +} + +/* + * Macros for command line options specific to timerlat + */ +#define TIMERLAT_OPT_PERIOD OPT_CALLBACK('p', "period", ¶ms->timerlat_period_us, "us", \ + "timerlat period in us", \ + opt_timerlat_period_cb) + +#define TIMERLAT_OPT_STACK OPT_CALLBACK('s', "stack", ¶ms->print_stack, "us", \ + "save the stack trace at the IRQ if a thread latency is higher than the argument in us", \ + opt_llong_callback) + +#define TIMERLAT_OPT_NANO OPT_CALLBACK('n', "nano", params, NULL, \ + "display data in nanoseconds", \ + opt_nano_cb) + +#define TIMERLAT_OPT_DMA_LATENCY OPT_CALLBACK(0, "dma-latency", ¶ms->dma_latency, "us", \ + "set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", \ + opt_dma_latency_cb) + +#define TIMERLAT_OPT_DEEPEST_IDLE_STATE OPT_CALLBACK(0, "deepest-idle-state", \ + ¶ms->deepest_idle_state, "n", \ + "only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", \ + opt_int_callback) + +#define TIMERLAT_OPT_AA_ONLY OPT_CALLBACK(0, "aa-only", params, "us", \ + "stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)", \ + opt_aa_only_cb) + +#define TIMERLAT_OPT_NO_AA OPT_BOOLEAN(0, "no-aa", ¶ms->no_aa, \ + "disable auto-analysis, reducing rtla timerlat cpu usage") + +#define TIMERLAT_OPT_DUMPS_TASKS OPT_BOOLEAN(0, "dump-tasks", ¶ms->dump_tasks, \ + "prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)") + +#define TIMERLAT_OPT_BPF_ACTION OPT_STRING(0, "bpf-action", ¶ms->bpf_action_program, \ + "program", \ + "load and execute BPF program when latency threshold is exceeded") + +#define TIMERLAT_OPT_STACK_FORMAT OPT_CALLBACK(0, "stack-format", ¶ms->stack_format, "format", \ + "set the stack format (truncate, skip, full)", \ + opt_stack_format_cb) + +/* + * Callback functions for command line options for timerlat tools + */ + +static int opt_timerlat_period_cb(const struct option *opt, const char *arg, int unset) +{ + long long *period = opt->value; + + if (unset || !arg) + return -1; + + *period = get_llong_from_str((char *)arg); + if (*period > 1000000) + fatal("Period longer than 1 s"); + + return 0; +} + +static int opt_timerlat_auto_cb(const struct option *opt, const char *arg, int unset) +{ + struct timerlat_cb_data *cb_data = opt->value; + struct timerlat_params *params = cb_data->params; + long long auto_thresh; + + if (unset || !arg) + return -1; + + auto_thresh = get_llong_from_str((char *)arg); + params->common.stop_total_us = auto_thresh; + params->common.stop_us = auto_thresh; + params->print_stack = auto_thresh; + + if (!cb_data->trace_output) + cb_data->trace_output = "timerlat_trace.txt"; + + return 0; +} + +static int opt_dma_latency_cb(const struct option *opt, const char *arg, int unset) +{ + int *dma_latency = opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval = strtoi((char *)arg, dma_latency); + if (retval) + fatal("Invalid -dma-latency %s", arg); + if (*dma_latency < 0 || *dma_latency > 10000) + fatal("--dma-latency needs to be >= 0 and < 10000"); + + return 0; +} + +static int opt_aa_only_cb(const struct option *opt, const char *arg, int unset) +{ + struct timerlat_params *params = opt->value; + long long auto_thresh; + + if (unset || !arg) + return -1; + + auto_thresh = get_llong_from_str((char *)arg); + params->common.stop_total_us = auto_thresh; + params->common.stop_us = auto_thresh; + params->print_stack = auto_thresh; + params->common.aa_only = 1; + + return 0; +} + +static int opt_timerlat_trace_output_cb(const struct option *opt, const char *arg, int unset) +{ + const char **trace_output = opt->value; + + if (unset) + return -1; + + if (!arg) { + *trace_output = "timerlat_trace.txt"; + } else { + *trace_output = (char *)arg; + if (*trace_output && (*trace_output)[0] == '=') + /* Allow -t=<trace_output> next to -t[ ]<trace_output> */ + ++*trace_output; + } + + return 0; +} + +static int opt_timerlat_on_threshold_cb(const struct option *opt, const char *arg, int unset) +{ + struct actions *actions = opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval = actions_parse(actions, (char *)arg, "timerlat_trace.txt"); + if (retval) + fatal("Invalid action %s", arg); + + return 0; +} + +static int opt_timerlat_on_end_cb(const struct option *opt, const char *arg, int unset) +{ + struct actions *actions = opt->value; + int retval; + + if (unset || !arg) + return -1; + + retval = actions_parse(actions, (char *)arg, "timerlat_trace.txt"); + if (retval) + fatal("Invalid action %s", arg); + + return 0; +} + +static int opt_user_threads_cb(const struct option *opt, const char *arg, int unset) +{ + struct timerlat_params *params = opt->value; + + if (unset) + return 0; + + params->common.user_workload = true; + params->common.user_data = true; + + return 0; +} + +static int opt_nano_cb(const struct option *opt, const char *arg, int unset) +{ + struct timerlat_params *params = opt->value; + + if (unset) + return 0; + + params->common.output_divisor = 1; + + return 0; +} + +static int opt_stack_format_cb(const struct option *opt, const char *arg, int unset) +{ + int *format = opt->value; + + if (unset || !arg) + return -1; + + *format = parse_stack_format((char *)arg); + + if (*format == -1) + fatal("Invalid --stack-format option"); + + return 0; +} + +/* + * Macros for command line options specific to histogram-based tools + */ +#define HIST_OPT_BUCKET_SIZE OPT_CALLBACK('b', "bucket-size", \ + ¶ms->common.hist.bucket_size, "N", \ + "set the histogram bucket size (default 1)", \ + opt_bucket_size_cb) + +#define HIST_OPT_ENTRIES OPT_CALLBACK('E', "entries", ¶ms->common.hist.entries, "N", \ + "set the number of entries of the histogram (default 256)", \ + opt_entries_cb) + +#define HIST_OPT_NO_IRQ OPT_BOOLEAN(0, "no-irq", ¶ms->common.hist.no_irq, \ + "ignore IRQ latencies") + +#define HIST_OPT_NO_THREAD OPT_BOOLEAN(0, "no-thread", ¶ms->common.hist.no_thread, \ + "ignore thread latencies") + +#define HIST_OPT_NO_HEADER OPT_BOOLEAN(0, "no-header", ¶ms->common.hist.no_header, \ + "do not print header") + +#define HIST_OPT_NO_SUMMARY OPT_BOOLEAN(0, "no-summary", ¶ms->common.hist.no_summary, \ + "do not print summary") + +#define HIST_OPT_NO_INDEX OPT_BOOLEAN(0, "no-index", ¶ms->common.hist.no_index, \ + "do not print index") + +#define HIST_OPT_WITH_ZEROS OPT_BOOLEAN(0, "with-zeros", ¶ms->common.hist.with_zeros, \ + "print zero only entries") + +/* Histogram-specific callbacks */ + +static int opt_bucket_size_cb(const struct option *opt, const char *arg, int unset) +{ + int *bucket_size = opt->value; + + if (unset || !arg) + return -1; + + *bucket_size = get_llong_from_str((char *)arg); + if (*bucket_size == 0 || *bucket_size >= 1000000) + fatal("Bucket size needs to be > 0 and <= 1000000"); + + return 0; +} + +static int opt_entries_cb(const struct option *opt, const char *arg, int unset) +{ + int *entries = opt->value; + + if (unset || !arg) + return -1; + + *entries = get_llong_from_str((char *)arg); + if (*entries < 10 || *entries > 9999999) + fatal("Entries must be > 10 and < 9999999"); + + return 0; +} + +/* + * osnoise_top_parse_args - allocs, parse and fill the cmd line parameters + */ +struct common_params *osnoise_top_parse_args(int argc, char **argv) +{ + struct osnoise_params *params; + struct osnoise_cb_data cb_data; + const char * const *usage; + + params = calloc_fatal(1, sizeof(*params)); + + cb_data.params = params; + cb_data.trace_output = NULL; + + if (strcmp(argv[0], "hwnoise") == 0) { + params->mode = MODE_HWNOISE; + /* + * Reduce CPU usage for 75% to avoid killing the system. + */ + params->runtime = 750000; + params->period = 1000000; + usage = hwnoise_usage; + } else { + usage = osnoise_top_usage; + } + + const struct option osnoise_top_options[] = { + OPT_GROUP("Tracing Options:"), + OSNOISE_OPT_PERIOD, + OSNOISE_OPT_RUNTIME, + RTLA_OPT_STOP('s', "stop", "single sample"), + RTLA_OPT_STOP_TOTAL('S', "stop-total", "total sample"), + OSNOISE_OPT_THRESHOLD, + RTLA_OPT_TRACE_OUTPUT("osnoise", opt_osnoise_trace_output_cb), + + OPT_GROUP("Event Configuration:"), + RTLA_OPT_EVENT, + RTLA_OPT_FILTER, + RTLA_OPT_TRIGGER, + + OPT_GROUP("CPU Configuration:"), + RTLA_OPT_CPUS, + RTLA_OPT_HOUSEKEEPING, + + OPT_GROUP("Thread Configuration:"), + RTLA_OPT_PRIORITY, + RTLA_OPT_CGROUP, + + OPT_GROUP("Output:"), + RTLA_OPT_QUIET, + + OPT_GROUP("System Tuning:"), + RTLA_OPT_TRACE_BUFFER_SIZE, + RTLA_OPT_WARM_UP, + + OPT_GROUP("Auto Analysis and Actions:"), + RTLA_OPT_AUTO(opt_osnoise_auto_cb), + RTLA_OPT_ON_THRESHOLD("stop-total", opt_osnoise_on_threshold_cb), + RTLA_OPT_ON_END(opt_osnoise_on_end_cb), + + OPT_GROUP("General:"), + RTLA_OPT_DURATION, + RTLA_OPT_DEBUG, + RTLA_OPT_HELP, + + OPT_END(), + }; + + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); + + argc = parse_options(argc, (const char **)argv, + osnoise_top_options, + usage, + common_parse_options_flags); + if (argc < 0) + return NULL; + + if (cb_data.trace_output) + actions_add_trace_output(¶ms->common.threshold_actions, cb_data.trace_output); + + if (geteuid()) + fatal("osnoise needs root permission"); + + return ¶ms->common; +} + +/* + * osnoise_hist_parse_args - allocs, parse and fill the cmd line parameters + */ +struct common_params *osnoise_hist_parse_args(int argc, char *argv[]) +{ + struct osnoise_params *params; + struct osnoise_cb_data cb_data; + + params = calloc_fatal(1, sizeof(*params)); + + cb_data.params = params; + cb_data.trace_output = NULL; + + const struct option osnoise_hist_options[] = { + OPT_GROUP("Tracing Options:"), + OSNOISE_OPT_PERIOD, + OSNOISE_OPT_RUNTIME, + RTLA_OPT_STOP('s', "stop", "single sample"), + RTLA_OPT_STOP_TOTAL('S', "stop-total", "total sample"), + OSNOISE_OPT_THRESHOLD, + RTLA_OPT_TRACE_OUTPUT("osnoise", opt_osnoise_trace_output_cb), + + OPT_GROUP("Event Configuration:"), + RTLA_OPT_EVENT, + RTLA_OPT_FILTER, + RTLA_OPT_TRIGGER, + + OPT_GROUP("CPU Configuration:"), + RTLA_OPT_CPUS, + RTLA_OPT_HOUSEKEEPING, + + OPT_GROUP("Thread Configuration:"), + RTLA_OPT_PRIORITY, + RTLA_OPT_CGROUP, + + OPT_GROUP("Histogram Options:"), + HIST_OPT_BUCKET_SIZE, + HIST_OPT_ENTRIES, + HIST_OPT_NO_HEADER, + HIST_OPT_NO_SUMMARY, + HIST_OPT_NO_INDEX, + HIST_OPT_WITH_ZEROS, + + OPT_GROUP("System Tuning:"), + RTLA_OPT_TRACE_BUFFER_SIZE, + RTLA_OPT_WARM_UP, + + OPT_GROUP("Auto Analysis and Actions:"), + RTLA_OPT_AUTO(opt_osnoise_auto_cb), + RTLA_OPT_ON_THRESHOLD("stop-total", opt_osnoise_on_threshold_cb), + RTLA_OPT_ON_END(opt_osnoise_on_end_cb), + + OPT_GROUP("General:"), + RTLA_OPT_DURATION, + RTLA_OPT_DEBUG, + RTLA_OPT_HELP, + + OPT_END(), + }; + + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); + + /* display data in microseconds */ + params->common.output_divisor = 1000; + params->common.hist.bucket_size = 1; + params->common.hist.entries = 256; + + argc = parse_options(argc, (const char **)argv, + osnoise_hist_options, osnoise_hist_usage, + common_parse_options_flags); + if (argc < 0) + return NULL; + + if (cb_data.trace_output) + actions_add_trace_output(¶ms->common.threshold_actions, cb_data.trace_output); + + if (geteuid()) + fatal("rtla needs root permission"); + + if (params->common.hist.no_index && !params->common.hist.with_zeros) + fatal("no-index set and with-zeros not set - it does not make sense"); + + return ¶ms->common; +} + +struct common_params *timerlat_top_parse_args(int argc, char **argv) +{ + struct timerlat_params *params; + struct timerlat_cb_data cb_data; + + params = calloc_fatal(1, sizeof(*params)); + + cb_data.params = params; + cb_data.trace_output = NULL; + + const struct option timerlat_top_options[] = { + OPT_GROUP("Tracing Options:"), + TIMERLAT_OPT_PERIOD, + RTLA_OPT_STOP('i', "irq", "irq latency"), + RTLA_OPT_STOP_TOTAL('T', "thread", "thread latency"), + TIMERLAT_OPT_STACK, + RTLA_OPT_TRACE_OUTPUT("timerlat", opt_timerlat_trace_output_cb), + + OPT_GROUP("Event Configuration:"), + RTLA_OPT_EVENT, + RTLA_OPT_FILTER, + RTLA_OPT_TRIGGER, + + OPT_GROUP("CPU Configuration:"), + RTLA_OPT_CPUS, + RTLA_OPT_HOUSEKEEPING, + + OPT_GROUP("Thread Configuration:"), + RTLA_OPT_PRIORITY, + RTLA_OPT_CGROUP, + RTLA_OPT_USER_THREADS, + RTLA_OPT_KERNEL_THREADS, + RTLA_OPT_USER_LOAD, + + OPT_GROUP("Output:"), + TIMERLAT_OPT_NANO, + RTLA_OPT_QUIET, + + OPT_GROUP("System Tuning:"), + TIMERLAT_OPT_DMA_LATENCY, + TIMERLAT_OPT_DEEPEST_IDLE_STATE, + RTLA_OPT_TRACE_BUFFER_SIZE, + RTLA_OPT_WARM_UP, + + OPT_GROUP("Auto Analysis and Actions:"), + RTLA_OPT_AUTO(opt_timerlat_auto_cb), + TIMERLAT_OPT_AA_ONLY, + TIMERLAT_OPT_NO_AA, + TIMERLAT_OPT_DUMPS_TASKS, + RTLA_OPT_ON_THRESHOLD("latency", opt_timerlat_on_threshold_cb), + RTLA_OPT_ON_END(opt_timerlat_on_end_cb), + TIMERLAT_OPT_BPF_ACTION, + TIMERLAT_OPT_STACK_FORMAT, + + OPT_GROUP("General:"), + RTLA_OPT_DURATION, + RTLA_OPT_DEBUG, + RTLA_OPT_HELP, + + OPT_END(), + }; + + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); + + /* disabled by default */ + params->dma_latency = -1; + params->deepest_idle_state = -2; + + /* display data in microseconds */ + params->common.output_divisor = 1000; + + /* default to BPF mode */ + params->mode = TRACING_MODE_BPF; + + /* default to truncate stack format */ + params->stack_format = STACK_FORMAT_TRUNCATE; + + argc = parse_options(argc, (const char **)argv, + timerlat_top_options, timerlat_top_usage, + common_parse_options_flags); + if (argc < 0) + return NULL; + + if (cb_data.trace_output) + actions_add_trace_output(¶ms->common.threshold_actions, cb_data.trace_output); + + if (geteuid()) + fatal("rtla needs root permission"); + + /* + * Auto analysis only happens if stop tracing, thus: + */ + if (!params->common.stop_us && !params->common.stop_total_us) + params->no_aa = 1; + + if (params->no_aa && params->common.aa_only) + fatal("--no-aa and --aa-only are mutually exclusive!"); + + if (params->common.kernel_workload && params->common.user_workload) + fatal("--kernel-threads and --user-threads are mutually exclusive!"); + + /* + * If auto-analysis or trace output is enabled, switch from BPF mode to + * mixed mode + */ + if (params->mode == TRACING_MODE_BPF && + (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 ¶ms->common; +} + +struct common_params *timerlat_hist_parse_args(int argc, char **argv) +{ + struct timerlat_params *params; + struct timerlat_cb_data cb_data; + + params = calloc_fatal(1, sizeof(*params)); + + cb_data.params = params; + cb_data.trace_output = NULL; + + const struct option timerlat_hist_options[] = { + OPT_GROUP("Tracing Options:"), + TIMERLAT_OPT_PERIOD, + RTLA_OPT_STOP('i', "irq", "irq latency"), + RTLA_OPT_STOP_TOTAL('T', "thread", "thread latency"), + TIMERLAT_OPT_STACK, + RTLA_OPT_TRACE_OUTPUT("timerlat", opt_timerlat_trace_output_cb), + + OPT_GROUP("Event Configuration:"), + RTLA_OPT_EVENT, + RTLA_OPT_FILTER, + RTLA_OPT_TRIGGER, + + OPT_GROUP("CPU Configuration:"), + RTLA_OPT_CPUS, + RTLA_OPT_HOUSEKEEPING, + + OPT_GROUP("Thread Configuration:"), + RTLA_OPT_PRIORITY, + RTLA_OPT_CGROUP, + RTLA_OPT_USER_THREADS, + RTLA_OPT_KERNEL_THREADS, + RTLA_OPT_USER_LOAD, + + OPT_GROUP("Histogram Options:"), + HIST_OPT_BUCKET_SIZE, + HIST_OPT_ENTRIES, + HIST_OPT_NO_IRQ, + HIST_OPT_NO_THREAD, + HIST_OPT_NO_HEADER, + HIST_OPT_NO_SUMMARY, + HIST_OPT_NO_INDEX, + HIST_OPT_WITH_ZEROS, + + OPT_GROUP("Output:"), + TIMERLAT_OPT_NANO, + + OPT_GROUP("System Tuning:"), + TIMERLAT_OPT_DMA_LATENCY, + TIMERLAT_OPT_DEEPEST_IDLE_STATE, + RTLA_OPT_TRACE_BUFFER_SIZE, + RTLA_OPT_WARM_UP, + + OPT_GROUP("Auto Analysis and Actions:"), + RTLA_OPT_AUTO(opt_timerlat_auto_cb), + TIMERLAT_OPT_NO_AA, + TIMERLAT_OPT_DUMPS_TASKS, + RTLA_OPT_ON_THRESHOLD("latency", opt_timerlat_on_threshold_cb), + RTLA_OPT_ON_END(opt_timerlat_on_end_cb), + TIMERLAT_OPT_BPF_ACTION, + TIMERLAT_OPT_STACK_FORMAT, + + OPT_GROUP("General:"), + RTLA_OPT_DURATION, + RTLA_OPT_DEBUG, + RTLA_OPT_HELP, + + OPT_END(), + }; + + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); + + /* disabled by default */ + params->dma_latency = -1; + + /* disabled by default */ + params->deepest_idle_state = -2; + + /* display data in microseconds */ + 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; + + /* default to truncate stack format */ + params->stack_format = STACK_FORMAT_TRUNCATE; + + argc = parse_options(argc, (const char **)argv, + timerlat_hist_options, timerlat_hist_usage, + common_parse_options_flags); + if (argc < 0) + return NULL; + + if (cb_data.trace_output) + actions_add_trace_output(¶ms->common.threshold_actions, cb_data.trace_output); + + if (geteuid()) + fatal("rtla needs root permission"); + + if (params->common.hist.no_irq && params->common.hist.no_thread) + fatal("no-irq and no-thread set, there is nothing to do here"); + + if (params->common.hist.no_index && !params->common.hist.with_zeros) + fatal("no-index set with with-zeros is not set - it does not make sense"); + + /* + * Auto analysis only happens if stop tracing, thus: + */ + if (!params->common.stop_us && !params->common.stop_total_us) + params->no_aa = 1; + + if (params->common.kernel_workload && params->common.user_workload) + fatal("--kernel-threads and --user-threads are mutually exclusive!"); + + /* + * If auto-analysis or trace output is enabled, switch from BPF mode to + * mixed mode + */ + if (params->mode == TRACING_MODE_BPF && + (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 ¶ms->common; +} + +/* + * rtla_usage - print rtla usage + */ +static void rtla_usage(int err) +{ + int i; + + static const char *msg[] = { + "", + "rtla version " VERSION, + "", + " usage: rtla COMMAND ...", + "", + " commands:", + " osnoise - gives information about the operating system noise (osnoise)", + " hwnoise - gives information about hardware-related noise", + " timerlat - measures the timer irq and thread latency", + "", + NULL, + }; + + for (i = 0; msg[i]; i++) + fprintf(stderr, "%s\n", msg[i]); + exit(err); +} + +/* + * run_tool_command - try to run a rtla tool command + * + * It returns 0 if it fails. The tool's main will generally not + * return as they should call exit(). + */ +int run_tool_command(int argc, char **argv, int start_position) +{ + if (strcmp(argv[start_position], "osnoise") == 0) { + osnoise_main(argc-start_position, &argv[start_position]); + goto ran; + } else if (strcmp(argv[start_position], "hwnoise") == 0) { + hwnoise_main(argc-start_position, &argv[start_position]); + goto ran; + } else if (strcmp(argv[start_position], "timerlat") == 0) { + timerlat_main(argc-start_position, &argv[start_position]); + goto ran; + } + + return 0; +ran: + return 1; +} + +int main(int argc, char *argv[]) +{ + int retval; + + /* is it an alias? */ + retval = run_tool_command(argc, argv, 0); + if (retval) + exit(0); + + if (argc < 2) + goto usage; + + if (strcmp(argv[1], "-h") == 0) + rtla_usage(0); + else if (strcmp(argv[1], "--help") == 0) + rtla_usage(0); + + retval = run_tool_command(argc, argv, 1); + if (retval) + exit(0); + +usage: + rtla_usage(1); + exit(1); +} diff --git a/tools/tracing/rtla/src/cli.h b/tools/tracing/rtla/src/cli.h new file mode 100644 index 000000000000..c49ccb3e92f5 --- /dev/null +++ b/tools/tracing/rtla/src/cli.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#pragma once + +struct common_params *osnoise_top_parse_args(int argc, char **argv); +struct common_params *osnoise_hist_parse_args(int argc, char **argv); +struct common_params *timerlat_top_parse_args(int argc, char **argv); +struct common_params *timerlat_hist_parse_args(int argc, char **argv); diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c index 35e3d3aa922e..7403dcc8f6c1 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c @@ -5,7 +5,6 @@ #include <signal.h> #include <stdlib.h> #include <string.h> -#include <getopt.h> #include <sys/sysinfo.h> #include "common.h" @@ -53,114 +52,6 @@ static void unset_signals(struct common_params *params) } } -/* - * getopt_auto - auto-generates optstring from long_options - */ -int getopt_auto(int argc, char **argv, const struct option *long_opts) -{ - char opts[256]; - int n = 0; - - for (int i = 0; long_opts[i].name; i++) { - if (long_opts[i].val < 32 || long_opts[i].val > 127) - continue; - - if (n + 4 >= sizeof(opts)) - fatal("optstring buffer overflow"); - - opts[n++] = long_opts[i].val; - - if (long_opts[i].has_arg == required_argument) - opts[n++] = ':'; - else if (long_opts[i].has_arg == optional_argument) { - opts[n++] = ':'; - opts[n++] = ':'; - } - } - - opts[n] = '\0'; - - return getopt_long(argc, argv, opts, long_opts, NULL); -} - -/* - * common_parse_options - parse common command line options - * - * @argc: argument count - * @argv: argument vector - * @common: common parameters structure - * - * Parse command line options that are common to all rtla tools. - * - * Returns: non zero if a common option was parsed, or 0 - * if the option should be handled by tool-specific parsing. - */ -int common_parse_options(int argc, char **argv, struct common_params *common) -{ - struct trace_events *tevent; - int saved_state = optind; - int c; - - static struct option long_options[] = { - {"cpus", required_argument, 0, 'c'}, - {"cgroup", optional_argument, 0, 'C'}, - {"debug", no_argument, 0, 'D'}, - {"duration", required_argument, 0, 'd'}, - {"event", required_argument, 0, 'e'}, - {"house-keeping", required_argument, 0, 'H'}, - {"priority", required_argument, 0, 'P'}, - {0, 0, 0, 0} - }; - - opterr = 0; - c = getopt_auto(argc, argv, long_options); - opterr = 1; - - switch (c) { - case 'c': - if (parse_cpu_set(optarg, &common->monitored_cpus)) - fatal("Invalid -c cpu list"); - common->cpus = optarg; - break; - case 'C': - common->cgroup = 1; - common->cgroup_name = parse_optional_arg(argc, argv); - break; - case 'D': - config_debug = 1; - break; - case 'd': - common->duration = parse_seconds_duration(optarg); - if (!common->duration) - fatal("Invalid -d duration"); - break; - case 'e': - tevent = trace_event_alloc(optarg); - if (!tevent) - fatal("Error alloc trace event"); - - if (common->events) - tevent->next = common->events; - common->events = tevent; - break; - case 'H': - common->hk_cpus = 1; - if (parse_cpu_set(optarg, &common->hk_cpu_set)) - fatal("Error parsing house keeping CPUs"); - break; - case 'P': - if (parse_prio(optarg, &common->sched_param) == -1) - fatal("Invalid -P priority"); - common->set_sched = 1; - break; - default: - optind = saved_state; - return 0; - } - - return c; -} - /* * common_apply_config - apply common configs to the initialized tool */ diff --git a/tools/tracing/rtla/src/common.h b/tools/tracing/rtla/src/common.h index 51665db4ffce..27439b10ffd5 100644 --- a/tools/tracing/rtla/src/common.h +++ b/tools/tracing/rtla/src/common.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ #pragma once -#include <getopt.h> #include "actions.h" #include "timerlat_u.h" #include "trace.h" @@ -58,12 +57,12 @@ extern struct trace_instance *trace_inst; extern volatile int stop_tracing; struct hist_params { - char no_irq; - char no_thread; - char no_header; - char no_summary; - char no_index; - char with_zeros; + bool no_irq; + bool no_thread; + bool no_header; + bool no_summary; + bool no_index; + bool with_zeros; int bucket_size; int entries; }; @@ -96,12 +95,12 @@ struct common_params { /* 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; + bool pretty_output; + bool quiet; + bool user_workload; + bool kernel_workload; + bool user_data; + bool aa_only; struct actions threshold_actions; struct actions end_actions; @@ -177,7 +176,6 @@ int osnoise_set_stop_us(struct osnoise_context *context, long long stop_us); int osnoise_set_stop_total_us(struct osnoise_context *context, long long stop_total_us); -int getopt_auto(int argc, char **argv, const struct option *long_opts); int common_parse_options(int argc, char **argv, struct common_params *common); int common_apply_config(struct osnoise_tool *tool, struct common_params *params); int top_main_loop(struct osnoise_tool *tool); diff --git a/tools/tracing/rtla/src/osnoise_hist.c b/tools/tracing/rtla/src/osnoise_hist.c index 8ad816b80265..dfa91d0681f8 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c @@ -4,7 +4,6 @@ */ #define _GNU_SOURCE -#include <getopt.h> #include <stdlib.h> #include <string.h> #include <signal.h> @@ -13,6 +12,7 @@ #include <time.h> #include "osnoise.h" +#include "cli.h" struct osnoise_hist_cpu { int *samples; @@ -400,225 +400,6 @@ osnoise_print_stats(struct osnoise_tool *tool) osnoise_report_missed_events(tool); } -/* - * osnoise_hist_usage - prints osnoise hist usage message - */ -static void osnoise_hist_usage(void) -{ - static const char * const msg_start[] = { - "[-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", - " [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\", - " [-c cpu-list] [-H cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] \\", - " [--no-index] [--with-zeros] [-C [cgroup_name]] [--warm-up]", - NULL, - }; - - static const char * const msg_opts[] = { - " -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit", - " -p/--period us: osnoise period in us", - " -r/--runtime us: osnoise runtime in us", - " -s/--stop us: stop trace if a single sample is higher than the argument in us", - " -S/--stop-total us: stop trace if the total sample is higher than the argument in us", - " -T/--threshold us: the minimum delta to be considered a noise", - " -c/--cpus cpu-list: list of cpus to run osnoise threads", - " -H/--house-keeping cpus: run rtla control threads only on the given cpus", - " -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited", - " -d/--duration time[s|m|h|d]: duration of the session", - " -D/--debug: print debug info", - " -t/--trace [file]: save the stopped trace to [file|osnoise_trace.txt]", - " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed", - " --filter <filter>: enable a trace event filter to the previous -e event", - " --trigger <trigger>: enable a trace event trigger to the previous -e event", - " -b/--bucket-size N: set the histogram bucket size (default 1)", - " -E/--entries N: set the number of entries of the histogram (default 256)", - " --no-header: do not print header", - " --no-summary: do not print summary", - " --no-index: do not print index", - " --with-zeros: print zero only entries", - " -P/--priority o:prio|r:prio|f:prio|d:runtime:period: set scheduling parameters", - " o:prio - use SCHED_OTHER with prio", - " r:prio - use SCHED_RR with prio", - " f:prio - use SCHED_FIFO with prio", - " d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period", - " 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, - }; - - common_usage("osnoise", "hist", "a per-cpu histogram of the OS noise", - msg_start, msg_opts); -} - -/* - * osnoise_hist_parse_args - allocs, parse and fill the cmd line parameters - */ -static struct common_params -*osnoise_hist_parse_args(int argc, char *argv[]) -{ - struct osnoise_params *params; - int retval; - int c; - char *trace_output = NULL; - - params = calloc_fatal(1, sizeof(*params)); - - actions_init(¶ms->common.threshold_actions); - actions_init(¶ms->common.end_actions); - - /* display data in microseconds */ - params->common.output_divisor = 1000; - params->common.hist.bucket_size = 1; - params->common.hist.entries = 256; - - while (1) { - static struct option long_options[] = { - {"auto", required_argument, 0, 'a'}, - {"bucket-size", required_argument, 0, 'b'}, - {"entries", required_argument, 0, 'E'}, - {"help", no_argument, 0, 'h'}, - {"period", required_argument, 0, 'p'}, - {"runtime", required_argument, 0, 'r'}, - {"stop", required_argument, 0, 's'}, - {"stop-total", required_argument, 0, 'S'}, - {"trace", optional_argument, 0, 't'}, - {"threshold", required_argument, 0, 'T'}, - {"no-header", no_argument, 0, '0'}, - {"no-summary", no_argument, 0, '1'}, - {"no-index", no_argument, 0, '2'}, - {"with-zeros", no_argument, 0, '3'}, - {"trigger", required_argument, 0, '4'}, - {"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} - }; - - if (common_parse_options(argc, argv, ¶ms->common)) - continue; - - c = getopt_auto(argc, argv, long_options); - - /* detect the end of the options. */ - if (c == -1) - break; - - switch (c) { - case 'a': - /* set sample stop to auto_thresh */ - params->common.stop_us = get_llong_from_str(optarg); - - /* set sample threshold to 1 */ - params->threshold = 1; - - /* set trace */ - if (!trace_output) - trace_output = "osnoise_trace.txt"; - - break; - case 'b': - params->common.hist.bucket_size = get_llong_from_str(optarg); - if (params->common.hist.bucket_size == 0 || - params->common.hist.bucket_size >= 1000000) - fatal("Bucket size needs to be > 0 and <= 1000000"); - break; - case 'E': - params->common.hist.entries = get_llong_from_str(optarg); - if (params->common.hist.entries < 10 || - params->common.hist.entries > 9999999) - fatal("Entries must be > 10 and < 9999999"); - break; - case 'h': - case '?': - osnoise_hist_usage(); - break; - case 'p': - params->period = get_llong_from_str(optarg); - if (params->period > 10000000) - fatal("Period longer than 10 s"); - break; - case 'r': - params->runtime = get_llong_from_str(optarg); - if (params->runtime < 100) - fatal("Runtime shorter than 100 us"); - break; - case 's': - params->common.stop_us = get_llong_from_str(optarg); - break; - case 'S': - params->common.stop_total_us = get_llong_from_str(optarg); - break; - case 'T': - params->threshold = get_llong_from_str(optarg); - break; - case 't': - trace_output = parse_optional_arg(argc, argv); - if (!trace_output) - trace_output = "osnoise_trace.txt"; - break; - case '0': /* no header */ - params->common.hist.no_header = 1; - break; - case '1': /* no summary */ - params->common.hist.no_summary = 1; - break; - case '2': /* no index */ - params->common.hist.no_index = 1; - break; - case '3': /* with zeros */ - params->common.hist.with_zeros = 1; - break; - case '4': /* trigger */ - if (params->common.events) - trace_event_add_trigger(params->common.events, optarg); - else - fatal("--trigger requires a previous -e"); - break; - case '5': /* filter */ - if (params->common.events) - trace_event_add_filter(params->common.events, optarg); - else - fatal("--filter requires a previous -e"); - break; - case '6': - params->common.warmup = get_llong_from_str(optarg); - break; - case '7': - 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) - fatal("Invalid action %s", optarg); - break; - case '9': - retval = actions_parse(¶ms->common.end_actions, optarg, - "osnoise_trace.txt"); - if (retval) - fatal("Invalid action %s", optarg); - break; - default: - fatal("Invalid option"); - } - } - - if (trace_output) - actions_add_trace_output(¶ms->common.threshold_actions, trace_output); - - if (geteuid()) - fatal("rtla needs root permission"); - - if (params->common.hist.no_index && !params->common.hist.with_zeros) - fatal("no-index set and with-zeros not set - it does not make sense"); - - return ¶ms->common; -} - /* * osnoise_hist_apply_config - apply the hist configs to the initialized tool */ diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/osnoise_top.c index 244bdce022ad..512a6299cb01 100644 --- a/tools/tracing/rtla/src/osnoise_top.c +++ b/tools/tracing/rtla/src/osnoise_top.c @@ -4,7 +4,6 @@ */ #define _GNU_SOURCE -#include <getopt.h> #include <stdlib.h> #include <string.h> #include <signal.h> @@ -13,6 +12,7 @@ #include <time.h> #include "osnoise.h" +#include "cli.h" struct osnoise_top_cpu { unsigned long long sum_runtime; @@ -245,204 +245,6 @@ osnoise_print_stats(struct osnoise_tool *top) osnoise_report_missed_events(top); } -/* - * osnoise_top_usage - prints osnoise top usage message - */ -static void osnoise_top_usage(struct osnoise_params *params) -{ - const char *tool, *mode, *desc; - - static const char * const msg_start[] = { - "[-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", - " [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\", - " [-c cpu-list] [-H cpu-list] [-P priority] [-C [cgroup_name]] [--warm-up s]", - NULL, - }; - - static const char * const msg_opts[] = { - " -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit", - " -p/--period us: osnoise period in us", - " -r/--runtime us: osnoise runtime in us", - " -s/--stop us: stop trace if a single sample is higher than the argument in us", - " -S/--stop-total us: stop trace if the total sample is higher than the argument in us", - " -T/--threshold us: the minimum delta to be considered a noise", - " -c/--cpus cpu-list: list of cpus to run osnoise threads", - " -H/--house-keeping cpus: run rtla control threads only on the given cpus", - " -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited", - " -d/--duration time[s|m|h|d]: duration of the session", - " -D/--debug: print debug info", - " -t/--trace [file]: save the stopped trace to [file|osnoise_trace.txt]", - " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed", - " --filter <filter>: enable a trace event filter to the previous -e event", - " --trigger <trigger>: enable a trace event trigger to the previous -e event", - " -q/--quiet print only a summary at the end", - " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters", - " o:prio - use SCHED_OTHER with prio", - " r:prio - use SCHED_RR with prio", - " f:prio - use SCHED_FIFO with prio", - " d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period", - " 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, - }; - - if (params->mode == MODE_OSNOISE) { - tool = "osnoise"; - mode = "top"; - desc = "a per-cpu summary of the OS noise"; - } else { - tool = "hwnoise"; - mode = ""; - desc = "a summary of hardware-related noise"; - } - - common_usage(tool, mode, desc, msg_start, msg_opts); -} - -/* - * osnoise_top_parse_args - allocs, parse and fill the cmd line parameters - */ -struct common_params *osnoise_top_parse_args(int argc, char **argv) -{ - struct osnoise_params *params; - int retval; - int c; - char *trace_output = NULL; - - params = calloc_fatal(1, sizeof(*params)); - - actions_init(¶ms->common.threshold_actions); - actions_init(¶ms->common.end_actions); - - if (strcmp(argv[0], "hwnoise") == 0) { - params->mode = MODE_HWNOISE; - /* - * Reduce CPU usage for 75% to avoid killing the system. - */ - params->runtime = 750000; - params->period = 1000000; - } - - while (1) { - static struct option long_options[] = { - {"auto", required_argument, 0, 'a'}, - {"help", no_argument, 0, 'h'}, - {"period", required_argument, 0, 'p'}, - {"quiet", no_argument, 0, 'q'}, - {"runtime", required_argument, 0, 'r'}, - {"stop", required_argument, 0, 's'}, - {"stop-total", required_argument, 0, 'S'}, - {"threshold", required_argument, 0, 'T'}, - {"trace", optional_argument, 0, 't'}, - {"trigger", required_argument, 0, '0'}, - {"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} - }; - - if (common_parse_options(argc, argv, ¶ms->common)) - continue; - - c = getopt_auto(argc, argv, long_options); - - /* Detect the end of the options. */ - if (c == -1) - break; - - switch (c) { - case 'a': - /* set sample stop to auto_thresh */ - params->common.stop_us = get_llong_from_str(optarg); - - /* set sample threshold to 1 */ - params->threshold = 1; - - /* set trace */ - if (!trace_output) - trace_output = "osnoise_trace.txt"; - - break; - case 'h': - case '?': - osnoise_top_usage(params); - break; - case 'p': - params->period = get_llong_from_str(optarg); - if (params->period > 10000000) - fatal("Period longer than 10 s"); - break; - case 'q': - params->common.quiet = 1; - break; - case 'r': - params->runtime = get_llong_from_str(optarg); - if (params->runtime < 100) - fatal("Runtime shorter than 100 us"); - break; - case 's': - params->common.stop_us = get_llong_from_str(optarg); - break; - case 'S': - params->common.stop_total_us = get_llong_from_str(optarg); - break; - case 't': - trace_output = parse_optional_arg(argc, argv); - if (!trace_output) - trace_output = "osnoise_trace.txt"; - break; - case 'T': - params->threshold = get_llong_from_str(optarg); - break; - case '0': /* trigger */ - if (params->common.events) - trace_event_add_trigger(params->common.events, optarg); - else - fatal("--trigger requires a previous -e"); - break; - case '1': /* filter */ - if (params->common.events) - trace_event_add_filter(params->common.events, optarg); - else - fatal("--filter requires a previous -e"); - break; - case '2': - params->common.warmup = get_llong_from_str(optarg); - break; - case '3': - 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) - fatal("Invalid action %s", optarg); - break; - case '5': - retval = actions_parse(¶ms->common.end_actions, optarg, - "osnoise_trace.txt"); - if (retval) - fatal("Invalid action %s", optarg); - break; - default: - fatal("Invalid option"); - } - } - - if (trace_output) - actions_add_trace_output(¶ms->common.threshold_actions, trace_output); - - if (geteuid()) - fatal("osnoise needs root permission"); - - return ¶ms->common; -} - /* * osnoise_top_apply_config - apply the top configs to the initialized tool */ diff --git a/tools/tracing/rtla/src/rtla.c b/tools/tracing/rtla/src/rtla.c deleted file mode 100644 index 845932f902ef..000000000000 --- a/tools/tracing/rtla/src/rtla.c +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <[email protected]> - */ - -#include <getopt.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> - -#include "osnoise.h" -#include "timerlat.h" - -/* - * rtla_usage - print rtla usage - */ -static void rtla_usage(int err) -{ - int i; - - static const char *msg[] = { - "", - "rtla version " VERSION, - "", - " usage: rtla COMMAND ...", - "", - " commands:", - " osnoise - gives information about the operating system noise (osnoise)", - " hwnoise - gives information about hardware-related noise", - " timerlat - measures the timer irq and thread latency", - "", - NULL, - }; - - for (i = 0; msg[i]; i++) - fprintf(stderr, "%s\n", msg[i]); - exit(err); -} - -/* - * run_tool_command - try to run a rtla tool command - * - * It returns 0 if it fails. The tool's main will generally not - * return as they should call exit(). - */ -int run_tool_command(int argc, char **argv, int start_position) -{ - if (strcmp(argv[start_position], "osnoise") == 0) { - osnoise_main(argc-start_position, &argv[start_position]); - goto ran; - } else if (strcmp(argv[start_position], "hwnoise") == 0) { - hwnoise_main(argc-start_position, &argv[start_position]); - goto ran; - } else if (strcmp(argv[start_position], "timerlat") == 0) { - timerlat_main(argc-start_position, &argv[start_position]); - goto ran; - } - - return 0; -ran: - return 1; -} - -int main(int argc, char *argv[]) -{ - int retval; - - /* is it an alias? */ - retval = run_tool_command(argc, argv, 0); - if (retval) - exit(0); - - if (argc < 2) - goto usage; - - if (strcmp(argv[1], "-h") == 0) { - rtla_usage(0); - } else if (strcmp(argv[1], "--help") == 0) { - rtla_usage(0); - } - - retval = run_tool_command(argc, argv, 1); - if (retval) - exit(0); - -usage: - rtla_usage(1); - exit(1); -} diff --git a/tools/tracing/rtla/src/timerlat.h b/tools/tracing/rtla/src/timerlat.h index 364203a29abd..37a808f1611e 100644 --- a/tools/tracing/rtla/src/timerlat.h +++ b/tools/tracing/rtla/src/timerlat.h @@ -23,8 +23,8 @@ struct timerlat_params { long long timerlat_period_us; long long print_stack; int dma_latency; - int no_aa; - int dump_tasks; + bool no_aa; + bool dump_tasks; int deepest_idle_state; enum timerlat_tracing_mode mode; const char *bpf_action_program; diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 79142af4f566..df7b1398a966 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -4,7 +4,6 @@ */ #define _GNU_SOURCE -#include <getopt.h> #include <stdlib.h> #include <string.h> #include <signal.h> @@ -17,6 +16,7 @@ #include "timerlat.h" #include "timerlat_aa.h" #include "timerlat_bpf.h" +#include "cli.h" #include "common.h" struct timerlat_hist_cpu { @@ -685,321 +685,6 @@ timerlat_print_stats(struct osnoise_tool *tool) osnoise_report_missed_events(tool); } -/* - * timerlat_hist_usage - prints timerlat top usage message - */ -static void timerlat_hist_usage(void) -{ - static const char * const msg_start[] = { - "[-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\", - " [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\", - " [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\", - " [--no-index] [--with-zeros] [--dma-latency us] [-C [cgroup_name]] [--no-aa] [--dump-task] [-u|-k]", - " [--warm-up s] [--deepest-idle-state n]", - NULL, - }; - - static const char * const msg_opts[] = { - " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit", - " -p/--period us: timerlat period in us", - " -i/--irq us: stop trace if the irq latency is higher than the argument in us", - " -T/--thread us: stop trace if the thread latency is higher than the argument in us", - " -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us", - " -c/--cpus cpus: run the tracer only on the given cpus", - " -H/--house-keeping cpus: run rtla control threads only on the given cpus", - " -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited", - " -d/--duration time[m|h|d]: duration of the session in seconds", - " --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)", - " -D/--debug: print debug info", - " -t/--trace [file]: save the stopped trace to [file|timerlat_trace.txt]", - " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed", - " --filter <filter>: enable a trace event filter to the previous -e event", - " --trigger <trigger>: enable a trace event trigger to the previous -e event", - " -n/--nano: display data in nanoseconds", - " --no-aa: disable auto-analysis, reducing rtla timerlat cpu usage", - " -b/--bucket-size N: set the histogram bucket size (default 1)", - " -E/--entries N: set the number of entries of the histogram (default 256)", - " --no-irq: ignore IRQ latencies", - " --no-thread: ignore thread latencies", - " --no-header: do not print header", - " --no-summary: do not print summary", - " --no-index: do not print index", - " --with-zeros: print zero only entries", - " --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", - " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters", - " o:prio - use SCHED_OTHER with prio", - " r:prio - use SCHED_RR with prio", - " f:prio - use SCHED_FIFO with prio", - " d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period", - " in nanoseconds", - " -u/--user-threads: use rtla user-space threads instead of kernel-space timerlat threads", - " -k/--kernel-threads: use timerlat kernel-space threads instead of rtla user-space threads", - " -U/--user-load: enable timerlat for user-defined user-space workload", - " --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", - " --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", - " --on-threshold <action>: define action to be executed at latency threshold, multiple are allowed", - " --on-end <action>: define action to be executed at measurement end, multiple are allowed", - " --bpf-action <program>: load and execute BPF program when latency threshold is exceeded", - " --stack-format <format>: set the stack format (truncate, skip, full)", - NULL, - }; - - common_usage("timerlat", "hist", "a per-cpu histogram of the timer latency", - msg_start, msg_opts); -} - -/* - * timerlat_hist_parse_args - allocs, parse and fill the cmd line parameters - */ -static struct common_params -*timerlat_hist_parse_args(int argc, char *argv[]) -{ - struct timerlat_params *params; - int auto_thresh; - int retval; - int c; - char *trace_output = NULL; - - params = calloc_fatal(1, sizeof(*params)); - - actions_init(¶ms->common.threshold_actions); - actions_init(¶ms->common.end_actions); - - /* disabled by default */ - params->dma_latency = -1; - - /* disabled by default */ - params->deepest_idle_state = -2; - - /* display data in microseconds */ - 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; - - /* default to truncate stack format */ - params->stack_format = STACK_FORMAT_TRUNCATE; - - while (1) { - static struct option long_options[] = { - {"auto", required_argument, 0, 'a'}, - {"bucket-size", required_argument, 0, 'b'}, - {"entries", required_argument, 0, 'E'}, - {"help", no_argument, 0, 'h'}, - {"irq", required_argument, 0, 'i'}, - {"nano", no_argument, 0, 'n'}, - {"period", required_argument, 0, 'p'}, - {"stack", required_argument, 0, 's'}, - {"thread", required_argument, 0, 'T'}, - {"trace", optional_argument, 0, 't'}, - {"user-threads", no_argument, 0, 'u'}, - {"kernel-threads", no_argument, 0, 'k'}, - {"user-load", no_argument, 0, 'U'}, - {"no-irq", no_argument, 0, '0'}, - {"no-thread", no_argument, 0, '1'}, - {"no-header", no_argument, 0, '2'}, - {"no-summary", no_argument, 0, '3'}, - {"no-index", no_argument, 0, '4'}, - {"with-zeros", no_argument, 0, '5'}, - {"trigger", required_argument, 0, '6'}, - {"filter", required_argument, 0, '7'}, - {"dma-latency", required_argument, 0, '8'}, - {"no-aa", no_argument, 0, '9'}, - {"dump-task", no_argument, 0, '\1'}, - {"warm-up", required_argument, 0, '\2'}, - {"trace-buffer-size", required_argument, 0, '\3'}, - {"deepest-idle-state", required_argument, 0, '\4'}, - {"on-threshold", required_argument, 0, '\5'}, - {"on-end", required_argument, 0, '\6'}, - {"bpf-action", required_argument, 0, '\7'}, - {"stack-format", required_argument, 0, '\10'}, - {0, 0, 0, 0} - }; - - if (common_parse_options(argc, argv, ¶ms->common)) - continue; - - c = getopt_auto(argc, argv, long_options); - - /* detect the end of the options. */ - if (c == -1) - break; - - switch (c) { - case 'a': - auto_thresh = get_llong_from_str(optarg); - - /* set thread stop to auto_thresh */ - params->common.stop_total_us = auto_thresh; - params->common.stop_us = auto_thresh; - - /* get stack trace */ - params->print_stack = auto_thresh; - - /* set trace */ - if (!trace_output) - trace_output = "timerlat_trace.txt"; - - break; - case 'b': - params->common.hist.bucket_size = get_llong_from_str(optarg); - if (params->common.hist.bucket_size == 0 || - params->common.hist.bucket_size >= 1000000) - fatal("Bucket size needs to be > 0 and <= 1000000"); - break; - case 'E': - params->common.hist.entries = get_llong_from_str(optarg); - if (params->common.hist.entries < 10 || - params->common.hist.entries > 9999999) - fatal("Entries must be > 10 and < 9999999"); - break; - case 'h': - case '?': - timerlat_hist_usage(); - break; - case 'i': - params->common.stop_us = get_llong_from_str(optarg); - break; - case 'k': - params->common.kernel_workload = 1; - break; - case 'n': - params->common.output_divisor = 1; - break; - case 'p': - params->timerlat_period_us = get_llong_from_str(optarg); - if (params->timerlat_period_us > 1000000) - fatal("Period longer than 1 s"); - break; - case 's': - params->print_stack = get_llong_from_str(optarg); - break; - case 'T': - params->common.stop_total_us = get_llong_from_str(optarg); - break; - case 't': - trace_output = parse_optional_arg(argc, argv); - if (!trace_output) - trace_output = "timerlat_trace.txt"; - break; - case 'u': - params->common.user_workload = 1; - /* fallback: -u implies in -U */ - case 'U': - params->common.user_data = 1; - break; - case '0': /* no irq */ - params->common.hist.no_irq = 1; - break; - case '1': /* no thread */ - params->common.hist.no_thread = 1; - break; - case '2': /* no header */ - params->common.hist.no_header = 1; - break; - case '3': /* no summary */ - params->common.hist.no_summary = 1; - break; - case '4': /* no index */ - params->common.hist.no_index = 1; - break; - case '5': /* with zeros */ - params->common.hist.with_zeros = 1; - break; - case '6': /* trigger */ - if (params->common.events) - trace_event_add_trigger(params->common.events, optarg); - else - fatal("--trigger requires a previous -e"); - break; - case '7': /* filter */ - if (params->common.events) - trace_event_add_filter(params->common.events, optarg); - else - fatal("--filter requires a previous -e"); - break; - case '8': - params->dma_latency = get_llong_from_str(optarg); - if (params->dma_latency < 0 || params->dma_latency > 10000) - fatal("--dma-latency needs to be >= 0 and < 10000"); - break; - case '9': - params->no_aa = 1; - break; - case '\1': - params->dump_tasks = 1; - break; - case '\2': - params->common.warmup = get_llong_from_str(optarg); - break; - case '\3': - 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->common.threshold_actions, optarg, - "timerlat_trace.txt"); - if (retval) - fatal("Invalid action %s", optarg); - break; - case '\6': - retval = actions_parse(¶ms->common.end_actions, optarg, - "timerlat_trace.txt"); - if (retval) - fatal("Invalid action %s", optarg); - break; - case '\7': - params->bpf_action_program = optarg; - break; - case '\10': - params->stack_format = parse_stack_format(optarg); - if (params->stack_format == -1) - fatal("Invalid --stack-format option"); - break; - default: - fatal("Invalid option"); - } - } - - if (trace_output) - actions_add_trace_output(¶ms->common.threshold_actions, trace_output); - - if (geteuid()) - fatal("rtla needs root permission"); - - if (params->common.hist.no_irq && params->common.hist.no_thread) - fatal("no-irq and no-thread set, there is nothing to do here"); - - if (params->common.hist.no_index && !params->common.hist.with_zeros) - fatal("no-index set with with-zeros is not set - it does not make sense"); - - /* - * Auto analysis only happens if stop tracing, thus: - */ - if (!params->common.stop_us && !params->common.stop_total_us) - params->no_aa = 1; - - if (params->common.kernel_workload && params->common.user_workload) - fatal("--kernel-threads and --user-threads are mutually exclusive!"); - - /* - * If auto-analysis or trace output is enabled, switch from BPF mode to - * mixed mode - */ - if (params->mode == TRACING_MODE_BPF && - (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 ¶ms->common; -} - /* * timerlat_hist_apply_config - apply the hist configs to the initialized tool */ diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 64cbdcc878b0..18e1071a2e24 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -4,7 +4,6 @@ */ #define _GNU_SOURCE -#include <getopt.h> #include <stdlib.h> #include <string.h> #include <signal.h> @@ -17,6 +16,7 @@ #include "timerlat.h" #include "timerlat_aa.h" #include "timerlat_bpf.h" +#include "cli.h" #include "common.h" struct timerlat_top_cpu { @@ -459,289 +459,6 @@ timerlat_print_stats(struct osnoise_tool *top) osnoise_report_missed_events(top); } -/* - * timerlat_top_usage - prints timerlat top usage message - */ -static void timerlat_top_usage(void) -{ - static const char *const msg_start[] = { - "[-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\", - " [[-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\", - " [-P priority] [--dma-latency us] [--aa-only us] [-C [cgroup_name]] [-u|-k] [--warm-up s] [--deepest-idle-state n]", - NULL, - }; - - static const char *const msg_opts[] = { - " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit", - " --aa-only us: stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)", - " -p/--period us: timerlat period in us", - " -i/--irq us: stop trace if the irq latency is higher than the argument in us", - " -T/--thread us: stop trace if the thread latency is higher than the argument in us", - " -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us", - " -c/--cpus cpus: run the tracer only on the given cpus", - " -H/--house-keeping cpus: run rtla control threads only on the given cpus", - " -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited", - " -d/--duration time[s|m|h|d]: duration of the session", - " -D/--debug: print debug info", - " --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)", - " -t/--trace [file]: save the stopped trace to [file|timerlat_trace.txt]", - " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed", - " --filter <command>: enable a trace event filter to the previous -e event", - " --trigger <command>: enable a trace event trigger to the previous -e event", - " -n/--nano: display data in nanoseconds", - " --no-aa: disable auto-analysis, reducing rtla timerlat cpu usage", - " -q/--quiet print only a summary at the end", - " --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", - " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters", - " o:prio - use SCHED_OTHER with prio", - " r:prio - use SCHED_RR with prio", - " f:prio - use SCHED_FIFO with prio", - " d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period", - " in nanoseconds", - " -u/--user-threads: use rtla user-space threads instead of kernel-space timerlat threads", - " -k/--kernel-threads: use timerlat kernel-space threads instead of rtla user-space threads", - " -U/--user-load: enable timerlat for user-defined user-space workload", - " --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", - " --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", - " --on-threshold <action>: define action to be executed at latency threshold, multiple are allowed", - " --on-end: define action to be executed at measurement end, multiple are allowed", - " --bpf-action <program>: load and execute BPF program when latency threshold is exceeded", - " --stack-format <format>: set the stack format (truncate, skip, full)", - NULL, - }; - - common_usage("timerlat", "top", "a per-cpu summary of the timer latency", - msg_start, msg_opts); -} - -/* - * timerlat_top_parse_args - allocs, parse and fill the cmd line parameters - */ -static struct common_params -*timerlat_top_parse_args(int argc, char **argv) -{ - struct timerlat_params *params; - long long auto_thresh; - int retval; - int c; - char *trace_output = NULL; - - params = calloc_fatal(1, sizeof(*params)); - - actions_init(¶ms->common.threshold_actions); - actions_init(¶ms->common.end_actions); - - /* disabled by default */ - params->dma_latency = -1; - - /* disabled by default */ - params->deepest_idle_state = -2; - - /* display data in microseconds */ - params->common.output_divisor = 1000; - - /* default to BPF mode */ - params->mode = TRACING_MODE_BPF; - - /* default to truncate stack format */ - params->stack_format = STACK_FORMAT_TRUNCATE; - - while (1) { - static struct option long_options[] = { - {"auto", required_argument, 0, 'a'}, - {"help", no_argument, 0, 'h'}, - {"irq", required_argument, 0, 'i'}, - {"nano", no_argument, 0, 'n'}, - {"period", required_argument, 0, 'p'}, - {"quiet", no_argument, 0, 'q'}, - {"stack", required_argument, 0, 's'}, - {"thread", required_argument, 0, 'T'}, - {"trace", optional_argument, 0, 't'}, - {"user-threads", no_argument, 0, 'u'}, - {"kernel-threads", no_argument, 0, 'k'}, - {"user-load", no_argument, 0, 'U'}, - {"trigger", required_argument, 0, '0'}, - {"filter", required_argument, 0, '1'}, - {"dma-latency", required_argument, 0, '2'}, - {"no-aa", no_argument, 0, '3'}, - {"dump-tasks", no_argument, 0, '4'}, - {"aa-only", required_argument, 0, '5'}, - {"warm-up", required_argument, 0, '6'}, - {"trace-buffer-size", required_argument, 0, '7'}, - {"deepest-idle-state", required_argument, 0, '8'}, - {"on-threshold", required_argument, 0, '9'}, - {"on-end", required_argument, 0, '\1'}, - {"bpf-action", required_argument, 0, '\2'}, - {"stack-format", required_argument, 0, '\3'}, - {0, 0, 0, 0} - }; - - if (common_parse_options(argc, argv, ¶ms->common)) - continue; - - c = getopt_auto(argc, argv, long_options); - - /* detect the end of the options. */ - if (c == -1) - break; - - switch (c) { - case 'a': - auto_thresh = get_llong_from_str(optarg); - - /* set thread stop to auto_thresh */ - params->common.stop_total_us = auto_thresh; - params->common.stop_us = auto_thresh; - - /* get stack trace */ - params->print_stack = auto_thresh; - - /* set trace */ - if (!trace_output) - trace_output = "timerlat_trace.txt"; - - break; - case '5': - /* it is here because it is similar to -a */ - auto_thresh = get_llong_from_str(optarg); - - /* set thread stop to 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->common.aa_only = 1; - break; - case 'h': - case '?': - timerlat_top_usage(); - break; - case 'i': - params->common.stop_us = get_llong_from_str(optarg); - break; - case 'k': - params->common.kernel_workload = true; - break; - case 'n': - params->common.output_divisor = 1; - break; - case 'p': - params->timerlat_period_us = get_llong_from_str(optarg); - if (params->timerlat_period_us > 1000000) - fatal("Period longer than 1 s"); - break; - case 'q': - params->common.quiet = 1; - break; - case 's': - params->print_stack = get_llong_from_str(optarg); - break; - case 'T': - params->common.stop_total_us = get_llong_from_str(optarg); - break; - case 't': - trace_output = parse_optional_arg(argc, argv); - if (!trace_output) - trace_output = "timerlat_trace.txt"; - break; - case 'u': - params->common.user_workload = true; - /* fallback: -u implies -U */ - case 'U': - params->common.user_data = true; - break; - case '0': /* trigger */ - if (params->common.events) - trace_event_add_trigger(params->common.events, optarg); - else - fatal("--trigger requires a previous -e"); - break; - case '1': /* filter */ - if (params->common.events) - trace_event_add_filter(params->common.events, optarg); - else - fatal("--filter requires a previous -e"); - break; - case '2': /* dma-latency */ - params->dma_latency = get_llong_from_str(optarg); - if (params->dma_latency < 0 || params->dma_latency > 10000) - fatal("--dma-latency needs to be >= 0 and < 10000"); - break; - case '3': /* no-aa */ - params->no_aa = 1; - break; - case '4': - params->dump_tasks = 1; - break; - case '6': - params->common.warmup = get_llong_from_str(optarg); - break; - case '7': - 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->common.threshold_actions, optarg, - "timerlat_trace.txt"); - if (retval) - fatal("Invalid action %s", optarg); - break; - case '\1': - retval = actions_parse(¶ms->common.end_actions, optarg, - "timerlat_trace.txt"); - if (retval) - fatal("Invalid action %s", optarg); - break; - case '\2': - params->bpf_action_program = optarg; - break; - case '\3': - params->stack_format = parse_stack_format(optarg); - if (params->stack_format == -1) - fatal("Invalid --stack-format option"); - break; - default: - fatal("Invalid option"); - } - } - - if (trace_output) - actions_add_trace_output(¶ms->common.threshold_actions, trace_output); - - if (geteuid()) - fatal("rtla needs root permission"); - - /* - * Auto analysis only happens if stop tracing, thus: - */ - if (!params->common.stop_us && !params->common.stop_total_us) - params->no_aa = 1; - - if (params->no_aa && params->common.aa_only) - fatal("--no-aa and --aa-only are mutually exclusive!"); - - if (params->common.kernel_workload && params->common.user_workload) - fatal("--kernel-threads and --user-threads are mutually exclusive!"); - - /* - * If auto-analysis or trace output is enabled, switch from BPF mode to - * mixed mode - */ - if (params->mode == TRACING_MODE_BPF && - (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 ¶ms->common; -} - /* * timerlat_top_apply_config - apply the top configs to the initialized tool */ diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index 9cec5b3e02c8..cb187e7d48d1 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -22,7 +22,7 @@ #include "common.h" #define MAX_MSG_LENGTH 1024 -int config_debug; +bool config_debug; /* * err_msg - print an error message to the stderr @@ -1011,32 +1011,6 @@ int auto_house_keeping(cpu_set_t *monitored_cpus) return 1; } -/** - * parse_optional_arg - Parse optional argument value - * - * Parse optional argument value, which can be in the form of: - * -sarg, -s/--long=arg, -s/--long arg - * - * Returns arg value if found, NULL otherwise. - */ -char *parse_optional_arg(int argc, char **argv) -{ - if (optarg) { - if (optarg[0] == '=') { - /* skip the = */ - return &optarg[1]; - } else { - return optarg; - } - /* parse argument of form -s [arg] and --long [arg]*/ - } else if (optind < argc && argv[optind][0] != '-') { - /* consume optind */ - return argv[optind++]; - } else { - return NULL; - } -} - /* * strtoi - convert string to integer with error checking * diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h index 96fd72042717..2ba3333669bb 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h @@ -39,7 +39,7 @@ static inline bool str_has_prefix(const char *str, const char *prefix) return strncmp(str, prefix, strlen(prefix)) == 0; } -extern int config_debug; +extern bool config_debug; void debug_msg(const char *fmt, ...); void err_msg(const char *fmt, ...); void fatal(const char *fmt, ...); @@ -47,7 +47,6 @@ void fatal(const char *fmt, ...); long parse_seconds_duration(char *val); void get_duration(time_t start_time, char *output, int output_size); -char *parse_optional_arg(int argc, char **argv); long long get_llong_from_str(char *start); static inline void diff --git a/tools/tracing/rtla/tests/hwnoise.t b/tools/tracing/rtla/tests/hwnoise.t index 23ce250a6852..cfe687ff5ee1 100644 --- a/tools/tracing/rtla/tests/hwnoise.t +++ b/tools/tracing/rtla/tests/hwnoise.t @@ -6,7 +6,7 @@ test_begin set_timeout 2m check "verify help page" \ - "hwnoise --help" 0 "summary of hardware-related noise" + "hwnoise --help" 129 "Usage: rtla hwnoise" check "detect noise higher than one microsecond" \ "hwnoise -c 0 -T 1 -d 5s -q" 0 check "set the automatic trace mode" \ -- 2.53.0
