#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;
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;
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(¶ms->common.threshold_actions);
actions_init(¶ms->common.end_actions);
argc = parse_options(argc, (const char **)argv,
osnoise_top_options,
usage,
common_parse_options_flags);
if (argc < 0)
return NULL;
if (cb_data.trace_output)
actions_add_trace_output(¶ms->common.threshold_actions, cb_data.trace_output);
if (geteuid() && !in_unit_test)
fatal("osnoise needs root permission");
return ¶ms->common;
}
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(¶ms->common.threshold_actions);
actions_init(¶ms->common.end_actions);
params->common.output_divisor = 1000;
params->common.hist.bucket_size = 1;
params->common.hist.entries = 256;
argc = parse_options(argc, (const char **)argv,
osnoise_hist_options, osnoise_hist_usage,
common_parse_options_flags);
if (argc < 0)
return NULL;
if (cb_data.trace_output)
actions_add_trace_output(¶ms->common.threshold_actions, cb_data.trace_output);
if (geteuid() && !in_unit_test)
fatal("rtla needs root permission");
if (params->common.hist.no_index && !params->common.hist.with_zeros)
fatal("no-index set and with-zeros not set - it does not make sense");
return ¶ms->common;
}
struct common_params *timerlat_top_parse_args(int argc, char **argv)
{
struct timerlat_params *params;
struct timerlat_cb_data cb_data;
params = calloc_fatal(1, sizeof(*params));
cb_data.params = params;
cb_data.trace_output = NULL;
const struct option timerlat_top_options[] = {
OPT_GROUP("Tracing Options:"),
TIMERLAT_OPT_PERIOD,
RTLA_OPT_STOP('i', "irq", "irq latency"),
RTLA_OPT_STOP_TOTAL('T', "thread", "thread latency"),
TIMERLAT_OPT_STACK,
RTLA_OPT_TRACE_OUTPUT("timerlat", opt_timerlat_trace_output_cb),
OPT_GROUP("Event Configuration:"),
RTLA_OPT_EVENT,
RTLA_OPT_FILTER,
RTLA_OPT_TRIGGER,
OPT_GROUP("CPU Configuration:"),
RTLA_OPT_CPUS,
RTLA_OPT_HOUSEKEEPING,
OPT_GROUP("Thread Configuration:"),
RTLA_OPT_PRIORITY,
RTLA_OPT_CGROUP,
RTLA_OPT_USER_THREADS,
RTLA_OPT_KERNEL_THREADS,
RTLA_OPT_USER_LOAD,
TIMERLAT_OPT_ALIGNED,
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(¶ms->common.threshold_actions);
actions_init(¶ms->common.end_actions);
params->dma_latency = -1;
params->deepest_idle_state = -2;
params->common.output_divisor = 1000;
params->mode = TRACING_MODE_BPF;
params->stack_format = STACK_FORMAT_TRUNCATE;
argc = parse_options(argc, (const char **)argv,
timerlat_top_options, timerlat_top_usage,
common_parse_options_flags);
if (argc < 0)
return NULL;
if (cb_data.trace_output)
actions_add_trace_output(¶ms->common.threshold_actions, cb_data.trace_output);
if (geteuid() && !in_unit_test)
fatal("rtla needs root permission");
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 (params->mode == TRACING_MODE_BPF &&
(params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] ||
params->common.end_actions.present[ACTION_TRACE_OUTPUT] ||
!params->no_aa))
params->mode = TRACING_MODE_MIXED;
return ¶ms->common;
}
struct common_params *timerlat_hist_parse_args(int argc, char **argv)
{
struct timerlat_params *params;
struct timerlat_cb_data cb_data;
params = calloc_fatal(1, sizeof(*params));
cb_data.params = params;
cb_data.trace_output = NULL;
const struct option timerlat_hist_options[] = {
OPT_GROUP("Tracing Options:"),
TIMERLAT_OPT_PERIOD,
RTLA_OPT_STOP('i', "irq", "irq latency"),
RTLA_OPT_STOP_TOTAL('T', "thread", "thread latency"),
TIMERLAT_OPT_STACK,
RTLA_OPT_TRACE_OUTPUT("timerlat", opt_timerlat_trace_output_cb),
OPT_GROUP("Event Configuration:"),
RTLA_OPT_EVENT,
RTLA_OPT_FILTER,
RTLA_OPT_TRIGGER,
OPT_GROUP("CPU Configuration:"),
RTLA_OPT_CPUS,
RTLA_OPT_HOUSEKEEPING,
OPT_GROUP("Thread Configuration:"),
RTLA_OPT_PRIORITY,
RTLA_OPT_CGROUP,
RTLA_OPT_USER_THREADS,
RTLA_OPT_KERNEL_THREADS,
RTLA_OPT_USER_LOAD,
TIMERLAT_OPT_ALIGNED,
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(¶ms->common.threshold_actions);
actions_init(¶ms->common.end_actions);
params->dma_latency = -1;
params->deepest_idle_state = -2;
params->common.output_divisor = 1000;
params->common.hist.bucket_size = 1;
params->common.hist.entries = 256;
params->mode = TRACING_MODE_BPF;
params->stack_format = STACK_FORMAT_TRUNCATE;
argc = parse_options(argc, (const char **)argv,
timerlat_hist_options, timerlat_hist_usage,
common_parse_options_flags);
if (argc < 0)
return NULL;
if (cb_data.trace_output)
actions_add_trace_output(¶ms->common.threshold_actions, cb_data.trace_output);
if (geteuid() && !in_unit_test)
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");
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 (params->mode == TRACING_MODE_BPF &&
(params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] ||
params->common.end_actions.present[ACTION_TRACE_OUTPUT] ||
!params->no_aa))
params->mode = TRACING_MODE_MIXED;
return ¶ms->common;
}
__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);
}
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;
}
#pragma weak main
int main(int argc, char *argv[])
{
int retval;
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);
}