[PATCH 3/3] rtla: Parse cmdline using libsubcmd

Tomas Glozar posted 3 patches 2 weeks ago
[PATCH 3/3] rtla: Parse cmdline using libsubcmd
Posted by Tomas Glozar 2 weeks ago
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 <tglozar@redhat.com>
---
 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 <bristot@kernel.org>
+ */
+
+#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, \
+	&params->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, \
+	&params->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", &params->common, \
+	"cpu-list", \
+	"run the tracer only on the given cpus", \
+	opt_cpus_cb)
+
+#define RTLA_OPT_CGROUP OPT_CALLBACK_OPTARG('C', "cgroup", &params->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", \
+	&params->common.kernel_workload, \
+	"use timerlat kernel-space threads instead of rtla user-space threads")
+
+#define RTLA_OPT_USER_LOAD OPT_BOOLEAN('U', "user-load", &params->common.user_data, \
+	"enable timerlat for user-defined user-space workload")
+
+#define RTLA_OPT_DURATION OPT_CALLBACK('d', "duration", &params->common, \
+	"time[s|m|h|d]", \
+	"set the duration of the session", \
+	opt_duration_cb)
+
+#define RTLA_OPT_EVENT OPT_CALLBACK('e', "event", &params->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", &params->common, \
+	"cpu-list", \
+	"run rtla control threads only on the given cpus", \
+	opt_housekeeping_cb)
+
+#define RTLA_OPT_PRIORITY OPT_CALLBACK('P', "priority", &params->common, \
+	"o:prio|r:prio|f:prio|d:runtime:period", \
+	"set scheduling parameters", \
+	opt_priority_cb)
+
+#define RTLA_OPT_TRIGGER OPT_CALLBACK(0, "trigger", &params->common, \
+	"trigger", \
+	"enable a trace event trigger to the previous -e event", \
+	opt_trigger_cb)
+
+#define RTLA_OPT_FILTER OPT_CALLBACK(0, "filter", &params->common, \
+	"filter", \
+	"enable a trace event filter to the previous -e event", \
+	opt_filter_cb)
+
+#define RTLA_OPT_QUIET OPT_BOOLEAN('q', "quiet", &params->common.quiet, \
+	"print only a summary at the end")
+
+#define RTLA_OPT_TRACE_BUFFER_SIZE OPT_CALLBACK(0, "trace-buffer-size", \
+	&params->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", &params->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", \
+	&params->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", &params->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, &params->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, &params->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, &params->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", &params->period, "us", \
+	"osnoise period in us", \
+	opt_osnoise_period_cb)
+
+#define OSNOISE_OPT_RUNTIME OPT_CALLBACK('r', "runtime", &params->runtime, "us", \
+	"osnoise runtime in us", \
+	opt_osnoise_runtime_cb)
+
+#define OSNOISE_OPT_THRESHOLD OPT_CALLBACK('T', "threshold", &params->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", &params->timerlat_period_us, "us", \
+	"timerlat period in us", \
+	opt_timerlat_period_cb)
+
+#define TIMERLAT_OPT_STACK OPT_CALLBACK('s', "stack", &params->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", &params->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", \
+	&params->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", &params->no_aa, \
+	"disable auto-analysis, reducing rtla timerlat cpu usage")
+
+#define TIMERLAT_OPT_DUMPS_TASKS OPT_BOOLEAN(0, "dump-tasks", &params->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", &params->bpf_action_program, \
+	"program", \
+	"load and execute BPF program when latency threshold is exceeded")
+
+#define TIMERLAT_OPT_STACK_FORMAT OPT_CALLBACK(0, "stack-format", &params->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", \
+	&params->common.hist.bucket_size, "N", \
+	"set the histogram bucket size (default 1)", \
+	opt_bucket_size_cb)
+
+#define HIST_OPT_ENTRIES OPT_CALLBACK('E', "entries", &params->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", &params->common.hist.no_irq, \
+	"ignore IRQ latencies")
+
+#define HIST_OPT_NO_THREAD OPT_BOOLEAN(0, "no-thread", &params->common.hist.no_thread, \
+	"ignore thread latencies")
+
+#define HIST_OPT_NO_HEADER OPT_BOOLEAN(0, "no-header", &params->common.hist.no_header, \
+	"do not print header")
+
+#define HIST_OPT_NO_SUMMARY OPT_BOOLEAN(0, "no-summary", &params->common.hist.no_summary, \
+	"do not print summary")
+
+#define HIST_OPT_NO_INDEX OPT_BOOLEAN(0, "no-index", &params->common.hist.no_index, \
+	"do not print index")
+
+#define HIST_OPT_WITH_ZEROS OPT_BOOLEAN(0, "with-zeros", &params->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(&params->common.threshold_actions);
+	actions_init(&params->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(&params->common.threshold_actions, cb_data.trace_output);
+
+	if (geteuid())
+		fatal("osnoise needs root permission");
+
+	return &params->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(&params->common.threshold_actions);
+	actions_init(&params->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(&params->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 &params->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(&params->common.threshold_actions);
+	actions_init(&params->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(&params->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 &params->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(&params->common.threshold_actions);
+	actions_init(&params->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(&params->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 &params->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(&params->common.threshold_actions);
-	actions_init(&params->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, &params->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(&params->common.threshold_actions, optarg,
-					       "osnoise_trace.txt");
-			if (retval)
-				fatal("Invalid action %s", optarg);
-			break;
-		case '9':
-			retval = actions_parse(&params->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(&params->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 &params->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(&params->common.threshold_actions);
-	actions_init(&params->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, &params->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(&params->common.threshold_actions, optarg,
-					       "osnoise_trace.txt");
-			if (retval)
-				fatal("Invalid action %s", optarg);
-			break;
-		case '5':
-			retval = actions_parse(&params->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(&params->common.threshold_actions, trace_output);
-
-	if (geteuid())
-		fatal("osnoise needs root permission");
-
-	return &params->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 <bristot@kernel.org>
- */
-
-#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(&params->common.threshold_actions);
-	actions_init(&params->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, &params->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(&params->common.threshold_actions, optarg,
-					       "timerlat_trace.txt");
-			if (retval)
-				fatal("Invalid action %s", optarg);
-			break;
-		case '\6':
-			retval = actions_parse(&params->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(&params->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 &params->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(&params->common.threshold_actions);
-	actions_init(&params->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, &params->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(&params->common.threshold_actions, optarg,
-					       "timerlat_trace.txt");
-			if (retval)
-				fatal("Invalid action %s", optarg);
-			break;
-		case '\1':
-			retval = actions_parse(&params->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(&params->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 &params->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
Re: [PATCH 3/3] rtla: Parse cmdline using libsubcmd
Posted by Costa Shulyupin 1 week, 6 days ago
On Fri, 20 Mar 2026 at 17:07, Tomas Glozar <tglozar@redhat.com> wrote:
>> +#define TIMERLAT_OPT_NANO OPT_CALLBACK('n', "nano", params, NULL, \
> +       "display data in nanoseconds", \
> +       opt_nano_cb)

-n/--nano requires value incorrectly

 File: src/cli.c:463
 Cause: TIMERLAT_OPT_NANO used OPT_CALLBACK which expects an argument,
but -n is a flag.
 Fix: Changed to OPT_CALLBACK_NOOPT:

> +               HIST_OPT_NO_IRQ,
--no-irq clashes with auto-negation of --irq

 File: src/cli.c:1042
 Cause: libsubcmd auto-generates --no-X negations for every option.
--no-irq (histogram boolean) collides with the auto-negation of --irq
(stop threshold). The first match wins, so --irq was matched first and
its negation intercepted the call.
 Fix: Moved HIST_OPT_NO_IRQ before RTLA_OPT_STOP('i', "irq", ...) in
the options array so the explicit --no-irq boolean is found first.

> +               HIST_OPT_NO_THREAD,
--no-thread - same issue

 Cause: Same collision between --no-thread boolean and auto-negation
of --thread.
 Fix: Same reordering approach.

Costa and Claude

diff --git a/tools/tracing/rtla/src/cli.c b/tools/tracing/rtla/src/cli.c
index d029a698e8a7..c6b9ed920539 100644
--- a/tools/tracing/rtla/src/cli.c
+++ b/tools/tracing/rtla/src/cli.c
@@ -460,7 +460,7 @@ static int opt_osnoise_on_end_cb(const struct
option *opt, const char *arg, int
  "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, \
+#define TIMERLAT_OPT_NANO OPT_CALLBACK_NOOPT('n', "nano", params, NULL, \
  "display data in nanoseconds", \
  opt_nano_cb)

@@ -1011,6 +1011,12 @@ struct common_params
*timerlat_hist_parse_args(int argc, char **argv)
  cb_data.trace_output = NULL;

  const struct option timerlat_hist_options[] = {
+ OPT_GROUP("Histogram Options:"),
+ HIST_OPT_NO_IRQ,
+ HIST_OPT_NO_THREAD,
+ HIST_OPT_BUCKET_SIZE,
+ HIST_OPT_ENTRIES,
+
  OPT_GROUP("Tracing Options:"),
  TIMERLAT_OPT_PERIOD,
  RTLA_OPT_STOP('i', "irq", "irq latency"),
@@ -1034,11 +1040,7 @@ struct common_params
*timerlat_hist_parse_args(int argc, char **argv)
  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,
+ OPT_GROUP(""),
  HIST_OPT_NO_HEADER,
  HIST_OPT_NO_SUMMARY,
 HIST_OPT_NO_INDEX,
Re: [PATCH 3/3] rtla: Parse cmdline using libsubcmd
Posted by Tomas Glozar 1 week, 4 days ago
so 21. 3. 2026 v 17:08 odesílatel Costa Shulyupin
<costa.shul@redhat.com> napsal:
>
> On Fri, 20 Mar 2026 at 17:07, Tomas Glozar <tglozar@redhat.com> wrote:
> >> +#define TIMERLAT_OPT_NANO OPT_CALLBACK('n', "nano", params, NULL, \
> > +       "display data in nanoseconds", \
> > +       opt_nano_cb)
>
> -n/--nano requires value incorrectly
>

Yes, this is a mistake.

>  File: src/cli.c:463
>  Cause: TIMERLAT_OPT_NANO used OPT_CALLBACK which expects an argument,
> but -n is a flag.
>  Fix: Changed to OPT_CALLBACK_NOOPT:
>
> > +               HIST_OPT_NO_IRQ,
> --no-irq clashes with auto-negation of --irq
>
>  File: src/cli.c:1042
>  Cause: libsubcmd auto-generates --no-X negations for every option.
> --no-irq (histogram boolean) collides with the auto-negation of --irq
> (stop threshold). The first match wins, so --irq was matched first and
> its negation intercepted the call.
>  Fix: Moved HIST_OPT_NO_IRQ before RTLA_OPT_STOP('i', "irq", ...) in
> the options array so the explicit --no-irq boolean is found first.
>

Thank you for reporting that, I had no idea libsubcmd does this, I
haven't seen it documented anywhere, and I never used it in either
perf or bpftool.

It is a very strange thing to do for non-boolean options in my
opinion. If I understand the intention correctly, the option is
intended to unset another option specified before it, i.e. this:

$ ./rtla timerlat hist --event=timer --no-event

is supposed to behave the same as:

$ ./rtla timerlat hist

But it doesn't work:

$ ./rtla timerlat hist --event=timer --no-event

Usage: rtla timerlat hist [<options>]

perf fails even harder on this:

$ ./perf version
perf version 7.0.rc2.g61637af4f771
$ ./perf record --event=cycles --no-event
Segmentation fault (core dumped)

I'm thinking about modifying libsubcmd so this can be at least
disabled (not sure if the behavior is even intended in the non-boolean
case). Reshuffling the RTLA option array could work, too, but is
unintuitive from the developer's point of view, and will also force
histogram options to be before threshold options in the help message,
which is not what I wanted.

Tomas
Re: [PATCH 3/3] rtla: Parse cmdline using libsubcmd
Posted by Tomas Glozar 1 week, 3 days ago
po 23. 3. 2026 v 15:26 odesílatel Tomas Glozar <tglozar@redhat.com> napsal:
> > --no-irq clashes with auto-negation of --irq
> >
> >  File: src/cli.c:1042
> >  Cause: libsubcmd auto-generates --no-X negations for every option.
> > --no-irq (histogram boolean) collides with the auto-negation of --irq
> > (stop threshold). The first match wins, so --irq was matched first and
> > its negation intercepted the call.
> >  Fix: Moved HIST_OPT_NO_IRQ before RTLA_OPT_STOP('i', "irq", ...) in
> > the options array so the explicit --no-irq boolean is found first.
> >
> ...
> Reshuffling the RTLA option array could work, too, but is
> unintuitive from the developer's point of view, and will also force
> histogram options to be before threshold options in the help message,
> which is not what I wanted.
>

Actually, it doesn't. Just as defining --irq implicitly defines
--no-irq, defining --no-irq implicitly defines --irq, so the long
option --irq stops working when putting HIST_OPT_NO_IRQ before
RTLA_OPT_STOP('i', "irq", "irq latency").

So the only solution appears to be to add a flag to libsubcmd that can
disable this behavior (auto-parsing the counterpart of --opt/--no-opt)
in a new patch in v2. Since this patchset already has a change to
libsubcmd and CC to linux-perf-users, that should work.

Tomas
Re: [PATCH 3/3] rtla: Parse cmdline using libsubcmd
Posted by Wander Lairson Costa 2 weeks ago
On Fri, Mar 20, 2026 at 04:06:51PM +0100, Tomas Glozar wrote:
> 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

[snip]

> -int getopt_auto(int argc, char **argv, const struct option *long_opts);
>  int common_parse_options(int argc, char **argv, struct common_params *common);


The function common_parse_options() body was removed, but the declaration remains.

>  int common_apply_config(struct osnoise_tool *tool, struct common_params *params);

[snip]
Re: [PATCH 3/3] rtla: Parse cmdline using libsubcmd
Posted by Tomas Glozar 1 week, 4 days ago
pá 20. 3. 2026 v 18:32 odesílatel Wander Lairson Costa
<wander@redhat.com> napsal:
> [snip]
>
> > -int getopt_auto(int argc, char **argv, const struct option *long_opts);
> >  int common_parse_options(int argc, char **argv, struct common_params *common);
>
>
> The function common_parse_options() body was removed, but the declaration remains.
>
> >  int common_apply_config(struct osnoise_tool *tool, struct common_params *params);
>
> [snip]
>

Thanks for noticing that, I will fix that in v2 that will be needed either way.

Tomas