rtla: Parse cmdline using libsubcmd

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. A private header, cli_p.h, is added
alongside the public header cli.h, so that unit tests are able to test
statically declared option callbacks.

Minor changes:

- 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
  is set to 129 as well for consistency, and the change is reflected in
  exit value documentation.
- Related to the above, {rtla,osnoise,timerlat}_usage() are marked
  __noreturn and exit() is removed from after they are called for
  cleaner code.
- The error messages for invalid argument for options --dma-latency and
  -E/--entries were corrected, fixing off-by-one in the limits.

Note that unsetting options (using --no-<opt> syntax) is currently not
implemented for options that use custom callbacks. For --irq and
--thread, it will never be implemented, as they conflict with already
existing --no-irq and --no-thread with a different meaning.

Assisted-by: Composer:composer-1.5
Link: https://lore.kernel.org/r/20260528103254.2990068-5-tglozar@redhat.com
Signed-off-by: Tomas Glozar <tglozar@redhat.com>
This commit is contained in:
Tomas Glozar 2026-05-28 12:32:52 +02:00
parent da62fc3458
commit 5d9af63e80
20 changed files with 1256 additions and 1286 deletions

View File

@ -26,9 +26,10 @@ EXIT STATUS
::
0 Passed: the test did not hit the stop tracing condition
1 Error: invalid argument
2 Failed: the test hit the stop tracing condition
0 Passed: the test did not hit the stop tracing condition
1 Error: invalid argument
2 Failed: the test hit the stop tracing condition
129 Help: either user requested help or incorrect option was specified
REPORTING BUGS
==============

View File

@ -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

View File

@ -0,0 +1,537 @@
// 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/compiler.h>
#define RTLA_ALLOW_CLI_P_H
#include "cli_p.h"
static const char * const osnoise_top_usage[] = {
"rtla osnoise [top] [<options>] [-h|--help]",
NULL,
};
static const char * const osnoise_hist_usage[] = {
"rtla osnoise hist [<options>] [-h|--help]",
NULL,
};
static const char * const timerlat_top_usage[] = {
"rtla timerlat [top] [<options>] [-h|--help]",
NULL,
};
static const char * const timerlat_hist_usage[] = {
"rtla timerlat hist [<options>] [-h|--help]",
NULL,
};
static const char * const hwnoise_usage[] = {
"rtla hwnoise [<options>] [-h|--help]",
NULL,
};
static const int common_parse_options_flags = PARSE_OPT_OPTARG_ALLOW_NEXT;
bool in_unit_test;
/*
* 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,
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,
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,
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,
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
*/
__noreturn static void rtla_usage(int err)
{
int i;
static const char * const 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;
}
/* Set main as weak to allow overriding it for building unit test binary */
#pragma weak main
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(129);
else if (strcmp(argv[1], "--help") == 0)
rtla_usage(129);
retval = run_tool_command(argc, argv, 1);
if (retval)
exit(0);
usage:
rtla_usage(129);
}

View File

@ -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);

View File

@ -0,0 +1,670 @@
/* SPDX-License-Identifier: GPL-2.0 */
#pragma once
#ifndef RTLA_ALLOW_CLI_P_H
#error "Private header file included outside of cli.c module"
#endif
#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;
};
/*
* 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_FLAG(short, long, \
&params->common.stop_us, \
"us", \
"stop trace if " name " is higher than the argument in us", \
opt_llong_callback, PARSE_OPT_NOAUTONEG)
#define RTLA_OPT_STOP_TOTAL(short, long, name) OPT_CALLBACK_FLAG(short, long, \
&params->common.stop_total_us, \
"us", \
"stop trace if " name " is higher than the argument in us", \
opt_llong_callback, PARSE_OPT_NOAUTONEG)
#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_NOOPT('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.events, \
"trigger", \
"enable a trace event trigger to the previous -e event", \
opt_trigger_cb)
#define RTLA_OPT_FILTER OPT_CALLBACK(0, "filter", &params->common.events, \
"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")
/*
* 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;
if (unset)
return -1;
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 trace_events **events = opt->value;
if (unset || !arg)
return -1;
if (!*events)
fatal("--trigger requires a previous -e");
trace_event_add_trigger(*events, (char *)arg);
return 0;
}
static int opt_filter_cb(const struct option *opt, const char *arg, int unset)
{
struct trace_events **events = opt->value;
if (unset || !arg)
return -1;
if (!*events)
fatal("--filter requires a previous -e");
trace_event_add_filter(*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_llong_callback)
/*
* 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_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_NOOPT('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 -1;
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 -1;
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_FLAG(0, "no-irq", &params->common.hist.no_irq, \
"ignore IRQ latencies", PARSE_OPT_NOAUTONEG)
#define HIST_OPT_NO_THREAD OPT_BOOLEAN_FLAG(0, "no-thread", &params->common.hist.no_thread, \
"ignore thread latencies", PARSE_OPT_NOAUTONEG)
#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 < 10000000");
return 0;
}

View File

@ -5,7 +5,6 @@
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <sys/sysinfo.h>
#include "common.h"
@ -57,114 +56,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
*/

View File

@ -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"
@ -57,12 +56,12 @@ struct osnoise_context {
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;
};
@ -95,12 +94,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;
@ -176,8 +175,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);
int hist_main_loop(struct osnoise_tool *tool);

View File

@ -15,6 +15,8 @@
#include <stdio.h>
#include <sched.h>
#include <linux/compiler.h>
#include "osnoise.h"
#define DEFAULT_SAMPLE_PERIOD 1000000 /* 1s */
@ -1171,7 +1173,7 @@ int osnoise_enable(struct osnoise_tool *tool)
return 0;
}
static void osnoise_usage(int err)
__noreturn static void osnoise_usage(int err)
{
int i;
@ -1209,7 +1211,7 @@ int osnoise_main(int argc, char *argv[])
}
if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) {
osnoise_usage(0);
osnoise_usage(129);
} else if (str_has_prefix(argv[1], "-")) {
/* the user skipped the tool, call the default one */
run_tool(&osnoise_top_ops, argc, argv);
@ -1223,8 +1225,7 @@ int osnoise_main(int argc, char *argv[])
}
usage:
osnoise_usage(1);
exit(1);
osnoise_usage(129);
}
int hwnoise_main(int argc, char *argv[])

View File

@ -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
*/

View File

@ -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
*/

View File

@ -1,92 +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;
}
/* Set main as weak to allow overriding it for building unit test binary */
#pragma weak main
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);
}

View File

@ -13,6 +13,8 @@
#include <stdio.h>
#include <sched.h>
#include <linux/compiler.h>
#include "timerlat.h"
#include "timerlat_aa.h"
#include "timerlat_bpf.h"
@ -231,7 +233,7 @@ void timerlat_free(struct osnoise_tool *tool)
free_cpu_idle_disable_states();
}
static void timerlat_usage(int err)
__noreturn static void timerlat_usage(int err)
{
int i;
@ -269,7 +271,7 @@ int timerlat_main(int argc, char *argv[])
}
if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) {
timerlat_usage(0);
timerlat_usage(129);
} else if (str_has_prefix(argv[1], "-")) {
/* the user skipped the tool, call the default one */
run_tool(&timerlat_top_ops, argc, argv);
@ -283,6 +285,5 @@ int timerlat_main(int argc, char *argv[])
}
usage:
timerlat_usage(1);
exit(1);
timerlat_usage(129);
}

View File

@ -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;

View File

@ -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-tasks] [-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-tasks", 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
*/

View File

@ -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,290 +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]] [--dump-tasks] [-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
*/

View File

@ -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
*

View File

@ -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

View File

@ -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" \

View File

@ -6,9 +6,9 @@ test_begin
set_timeout 2m
check "verify help page" \
"osnoise --help" 0 "osnoise version"
"osnoise --help" 129 "osnoise version"
check_top_hist "verify help page" \
"osnoise TOOL --help" 0 "rtla osnoise"
"osnoise TOOL --help" 129 "rtla osnoise"
check_top_q_hist "verify the --stop/-s param" \
"osnoise TOOL -s 30 -T 1" 2 "osnoise hit stop tracing"
check_top_q_hist "verify the --trace param" \
@ -32,7 +32,7 @@ check "hist with -b/--bucket-size" \
check "hist with -E/--entries" \
"osnoise hist -E 10 -d 1s"
check "hist with -E/--entries out of range" \
"osnoise hist -E 1 -d 1s" 1 "^Entries must be > 10 and < 9999999$"
"osnoise hist -E 1 -d 1s" 1 "^Entries must be > 10 and < 10000000$"
check "hist with --no-header" \
"osnoise hist --no-header -d 1s" 0 "" "RTLA osnoise histogram"
check "hist with --with-zeros" \

View File

@ -21,9 +21,9 @@ export RTLA_NO_BPF=$option
# Basic tests
check "verify help page" \
"timerlat --help" 0 "timerlat version"
"timerlat --help" 129 "timerlat version"
check_top_hist "verify help page" \
"timerlat TOOL --help" 0 "rtla timerlat"
"timerlat TOOL --help" 129 "rtla timerlat"
check_top_hist "verify -s/--stack" \
"timerlat TOOL -s 3 -T 10 -t" 2 "Blocking thread stack trace"
check_top_hist "test in nanoseconds" \
@ -61,7 +61,7 @@ check "hist with -b/--bucket-size" \
check "hist with -E/--entries" \
"timerlat hist -E 10 -d 1s"
check "hist with -E/--entries out of range" \
"timerlat hist -E 1 -d 1s" 1 "^Entries must be > 10 and < 9999999$"
"timerlat hist -E 1 -d 1s" 1 "^Entries must be > 10 and < 10000000$"
check "hist with --no-header" \
"timerlat hist --no-header -d 1s" 0 "" "RTLA timerlat histogram"
check "hist with --with-zeros" \