mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
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:
parent
da62fc3458
commit
5d9af63e80
|
|
@ -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
|
||||
==============
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
537
tools/tracing/rtla/src/cli.c
Normal file
537
tools/tracing/rtla/src/cli.c
Normal 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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->common.end_actions);
|
||||
|
||||
argc = parse_options(argc, (const char **)argv,
|
||||
osnoise_top_options,
|
||||
usage,
|
||||
common_parse_options_flags);
|
||||
if (argc < 0)
|
||||
return NULL;
|
||||
|
||||
if (cb_data.trace_output)
|
||||
actions_add_trace_output(¶ms->common.threshold_actions, cb_data.trace_output);
|
||||
|
||||
if (geteuid())
|
||||
fatal("osnoise needs root permission");
|
||||
|
||||
return ¶ms->common;
|
||||
}
|
||||
|
||||
/*
|
||||
* osnoise_hist_parse_args - allocs, parse and fill the cmd line parameters
|
||||
*/
|
||||
struct common_params *osnoise_hist_parse_args(int argc, char **argv)
|
||||
{
|
||||
struct osnoise_params *params;
|
||||
struct osnoise_cb_data cb_data;
|
||||
|
||||
params = calloc_fatal(1, sizeof(*params));
|
||||
|
||||
cb_data.params = params;
|
||||
cb_data.trace_output = NULL;
|
||||
|
||||
const struct option osnoise_hist_options[] = {
|
||||
OPT_GROUP("Tracing Options:"),
|
||||
OSNOISE_OPT_PERIOD,
|
||||
OSNOISE_OPT_RUNTIME,
|
||||
RTLA_OPT_STOP('s', "stop", "single sample"),
|
||||
RTLA_OPT_STOP_TOTAL('S', "stop-total", "total sample"),
|
||||
OSNOISE_OPT_THRESHOLD,
|
||||
RTLA_OPT_TRACE_OUTPUT("osnoise", opt_osnoise_trace_output_cb),
|
||||
|
||||
OPT_GROUP("Event Configuration:"),
|
||||
RTLA_OPT_EVENT,
|
||||
RTLA_OPT_FILTER,
|
||||
RTLA_OPT_TRIGGER,
|
||||
|
||||
OPT_GROUP("CPU Configuration:"),
|
||||
RTLA_OPT_CPUS,
|
||||
RTLA_OPT_HOUSEKEEPING,
|
||||
|
||||
OPT_GROUP("Thread Configuration:"),
|
||||
RTLA_OPT_PRIORITY,
|
||||
RTLA_OPT_CGROUP,
|
||||
|
||||
OPT_GROUP("Histogram Options:"),
|
||||
HIST_OPT_BUCKET_SIZE,
|
||||
HIST_OPT_ENTRIES,
|
||||
HIST_OPT_NO_HEADER,
|
||||
HIST_OPT_NO_SUMMARY,
|
||||
HIST_OPT_NO_INDEX,
|
||||
HIST_OPT_WITH_ZEROS,
|
||||
|
||||
OPT_GROUP("System Tuning:"),
|
||||
RTLA_OPT_TRACE_BUFFER_SIZE,
|
||||
RTLA_OPT_WARM_UP,
|
||||
|
||||
OPT_GROUP("Auto Analysis and Actions:"),
|
||||
RTLA_OPT_AUTO(opt_osnoise_auto_cb),
|
||||
RTLA_OPT_ON_THRESHOLD("stop-total", opt_osnoise_on_threshold_cb),
|
||||
RTLA_OPT_ON_END(opt_osnoise_on_end_cb),
|
||||
|
||||
OPT_GROUP("General:"),
|
||||
RTLA_OPT_DURATION,
|
||||
RTLA_OPT_DEBUG,
|
||||
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
actions_init(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->common.end_actions);
|
||||
|
||||
/* display data in microseconds */
|
||||
params->common.output_divisor = 1000;
|
||||
params->common.hist.bucket_size = 1;
|
||||
params->common.hist.entries = 256;
|
||||
|
||||
argc = parse_options(argc, (const char **)argv,
|
||||
osnoise_hist_options, osnoise_hist_usage,
|
||||
common_parse_options_flags);
|
||||
if (argc < 0)
|
||||
return NULL;
|
||||
|
||||
if (cb_data.trace_output)
|
||||
actions_add_trace_output(¶ms->common.threshold_actions, cb_data.trace_output);
|
||||
|
||||
if (geteuid())
|
||||
fatal("rtla needs root permission");
|
||||
|
||||
if (params->common.hist.no_index && !params->common.hist.with_zeros)
|
||||
fatal("no-index set and with-zeros not set - it does not make sense");
|
||||
|
||||
return ¶ms->common;
|
||||
}
|
||||
|
||||
struct common_params *timerlat_top_parse_args(int argc, char **argv)
|
||||
{
|
||||
struct timerlat_params *params;
|
||||
struct timerlat_cb_data cb_data;
|
||||
|
||||
params = calloc_fatal(1, sizeof(*params));
|
||||
|
||||
cb_data.params = params;
|
||||
cb_data.trace_output = NULL;
|
||||
|
||||
const struct option timerlat_top_options[] = {
|
||||
OPT_GROUP("Tracing Options:"),
|
||||
TIMERLAT_OPT_PERIOD,
|
||||
RTLA_OPT_STOP('i', "irq", "irq latency"),
|
||||
RTLA_OPT_STOP_TOTAL('T', "thread", "thread latency"),
|
||||
TIMERLAT_OPT_STACK,
|
||||
RTLA_OPT_TRACE_OUTPUT("timerlat", opt_timerlat_trace_output_cb),
|
||||
|
||||
OPT_GROUP("Event Configuration:"),
|
||||
RTLA_OPT_EVENT,
|
||||
RTLA_OPT_FILTER,
|
||||
RTLA_OPT_TRIGGER,
|
||||
|
||||
OPT_GROUP("CPU Configuration:"),
|
||||
RTLA_OPT_CPUS,
|
||||
RTLA_OPT_HOUSEKEEPING,
|
||||
|
||||
OPT_GROUP("Thread Configuration:"),
|
||||
RTLA_OPT_PRIORITY,
|
||||
RTLA_OPT_CGROUP,
|
||||
RTLA_OPT_USER_THREADS,
|
||||
RTLA_OPT_KERNEL_THREADS,
|
||||
RTLA_OPT_USER_LOAD,
|
||||
|
||||
OPT_GROUP("Output:"),
|
||||
TIMERLAT_OPT_NANO,
|
||||
RTLA_OPT_QUIET,
|
||||
|
||||
OPT_GROUP("System Tuning:"),
|
||||
TIMERLAT_OPT_DMA_LATENCY,
|
||||
TIMERLAT_OPT_DEEPEST_IDLE_STATE,
|
||||
RTLA_OPT_TRACE_BUFFER_SIZE,
|
||||
RTLA_OPT_WARM_UP,
|
||||
|
||||
OPT_GROUP("Auto Analysis and Actions:"),
|
||||
RTLA_OPT_AUTO(opt_timerlat_auto_cb),
|
||||
TIMERLAT_OPT_AA_ONLY,
|
||||
TIMERLAT_OPT_NO_AA,
|
||||
TIMERLAT_OPT_DUMPS_TASKS,
|
||||
RTLA_OPT_ON_THRESHOLD("latency", opt_timerlat_on_threshold_cb),
|
||||
RTLA_OPT_ON_END(opt_timerlat_on_end_cb),
|
||||
TIMERLAT_OPT_BPF_ACTION,
|
||||
TIMERLAT_OPT_STACK_FORMAT,
|
||||
|
||||
OPT_GROUP("General:"),
|
||||
RTLA_OPT_DURATION,
|
||||
RTLA_OPT_DEBUG,
|
||||
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
actions_init(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->common.end_actions);
|
||||
|
||||
/* disabled by default */
|
||||
params->dma_latency = -1;
|
||||
params->deepest_idle_state = -2;
|
||||
|
||||
/* display data in microseconds */
|
||||
params->common.output_divisor = 1000;
|
||||
|
||||
/* default to BPF mode */
|
||||
params->mode = TRACING_MODE_BPF;
|
||||
|
||||
/* default to truncate stack format */
|
||||
params->stack_format = STACK_FORMAT_TRUNCATE;
|
||||
|
||||
argc = parse_options(argc, (const char **)argv,
|
||||
timerlat_top_options, timerlat_top_usage,
|
||||
common_parse_options_flags);
|
||||
if (argc < 0)
|
||||
return NULL;
|
||||
|
||||
if (cb_data.trace_output)
|
||||
actions_add_trace_output(¶ms->common.threshold_actions, cb_data.trace_output);
|
||||
|
||||
if (geteuid())
|
||||
fatal("rtla needs root permission");
|
||||
|
||||
/*
|
||||
* Auto analysis only happens if stop tracing, thus:
|
||||
*/
|
||||
if (!params->common.stop_us && !params->common.stop_total_us)
|
||||
params->no_aa = 1;
|
||||
|
||||
if (params->no_aa && params->common.aa_only)
|
||||
fatal("--no-aa and --aa-only are mutually exclusive!");
|
||||
|
||||
if (params->common.kernel_workload && params->common.user_workload)
|
||||
fatal("--kernel-threads and --user-threads are mutually exclusive!");
|
||||
|
||||
/*
|
||||
* If auto-analysis or trace output is enabled, switch from BPF mode to
|
||||
* mixed mode
|
||||
*/
|
||||
if (params->mode == TRACING_MODE_BPF &&
|
||||
(params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] ||
|
||||
params->common.end_actions.present[ACTION_TRACE_OUTPUT] ||
|
||||
!params->no_aa))
|
||||
params->mode = TRACING_MODE_MIXED;
|
||||
|
||||
return ¶ms->common;
|
||||
}
|
||||
|
||||
struct common_params *timerlat_hist_parse_args(int argc, char **argv)
|
||||
{
|
||||
struct timerlat_params *params;
|
||||
struct timerlat_cb_data cb_data;
|
||||
|
||||
params = calloc_fatal(1, sizeof(*params));
|
||||
|
||||
cb_data.params = params;
|
||||
cb_data.trace_output = NULL;
|
||||
|
||||
const struct option timerlat_hist_options[] = {
|
||||
OPT_GROUP("Tracing Options:"),
|
||||
TIMERLAT_OPT_PERIOD,
|
||||
RTLA_OPT_STOP('i', "irq", "irq latency"),
|
||||
RTLA_OPT_STOP_TOTAL('T', "thread", "thread latency"),
|
||||
TIMERLAT_OPT_STACK,
|
||||
RTLA_OPT_TRACE_OUTPUT("timerlat", opt_timerlat_trace_output_cb),
|
||||
|
||||
OPT_GROUP("Event Configuration:"),
|
||||
RTLA_OPT_EVENT,
|
||||
RTLA_OPT_FILTER,
|
||||
RTLA_OPT_TRIGGER,
|
||||
|
||||
OPT_GROUP("CPU Configuration:"),
|
||||
RTLA_OPT_CPUS,
|
||||
RTLA_OPT_HOUSEKEEPING,
|
||||
|
||||
OPT_GROUP("Thread Configuration:"),
|
||||
RTLA_OPT_PRIORITY,
|
||||
RTLA_OPT_CGROUP,
|
||||
RTLA_OPT_USER_THREADS,
|
||||
RTLA_OPT_KERNEL_THREADS,
|
||||
RTLA_OPT_USER_LOAD,
|
||||
|
||||
OPT_GROUP("Histogram Options:"),
|
||||
HIST_OPT_BUCKET_SIZE,
|
||||
HIST_OPT_ENTRIES,
|
||||
HIST_OPT_NO_IRQ,
|
||||
HIST_OPT_NO_THREAD,
|
||||
HIST_OPT_NO_HEADER,
|
||||
HIST_OPT_NO_SUMMARY,
|
||||
HIST_OPT_NO_INDEX,
|
||||
HIST_OPT_WITH_ZEROS,
|
||||
|
||||
OPT_GROUP("Output:"),
|
||||
TIMERLAT_OPT_NANO,
|
||||
|
||||
OPT_GROUP("System Tuning:"),
|
||||
TIMERLAT_OPT_DMA_LATENCY,
|
||||
TIMERLAT_OPT_DEEPEST_IDLE_STATE,
|
||||
RTLA_OPT_TRACE_BUFFER_SIZE,
|
||||
RTLA_OPT_WARM_UP,
|
||||
|
||||
OPT_GROUP("Auto Analysis and Actions:"),
|
||||
RTLA_OPT_AUTO(opt_timerlat_auto_cb),
|
||||
TIMERLAT_OPT_NO_AA,
|
||||
TIMERLAT_OPT_DUMPS_TASKS,
|
||||
RTLA_OPT_ON_THRESHOLD("latency", opt_timerlat_on_threshold_cb),
|
||||
RTLA_OPT_ON_END(opt_timerlat_on_end_cb),
|
||||
TIMERLAT_OPT_BPF_ACTION,
|
||||
TIMERLAT_OPT_STACK_FORMAT,
|
||||
|
||||
OPT_GROUP("General:"),
|
||||
RTLA_OPT_DURATION,
|
||||
RTLA_OPT_DEBUG,
|
||||
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
actions_init(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->common.end_actions);
|
||||
|
||||
/* disabled by default */
|
||||
params->dma_latency = -1;
|
||||
|
||||
/* disabled by default */
|
||||
params->deepest_idle_state = -2;
|
||||
|
||||
/* display data in microseconds */
|
||||
params->common.output_divisor = 1000;
|
||||
params->common.hist.bucket_size = 1;
|
||||
params->common.hist.entries = 256;
|
||||
|
||||
/* default to BPF mode */
|
||||
params->mode = TRACING_MODE_BPF;
|
||||
|
||||
/* default to truncate stack format */
|
||||
params->stack_format = STACK_FORMAT_TRUNCATE;
|
||||
|
||||
argc = parse_options(argc, (const char **)argv,
|
||||
timerlat_hist_options, timerlat_hist_usage,
|
||||
common_parse_options_flags);
|
||||
if (argc < 0)
|
||||
return NULL;
|
||||
|
||||
if (cb_data.trace_output)
|
||||
actions_add_trace_output(¶ms->common.threshold_actions, cb_data.trace_output);
|
||||
|
||||
if (geteuid())
|
||||
fatal("rtla needs root permission");
|
||||
|
||||
if (params->common.hist.no_irq && params->common.hist.no_thread)
|
||||
fatal("no-irq and no-thread set, there is nothing to do here");
|
||||
|
||||
if (params->common.hist.no_index && !params->common.hist.with_zeros)
|
||||
fatal("no-index set with with-zeros is not set - it does not make sense");
|
||||
|
||||
/*
|
||||
* Auto analysis only happens if stop tracing, thus:
|
||||
*/
|
||||
if (!params->common.stop_us && !params->common.stop_total_us)
|
||||
params->no_aa = 1;
|
||||
|
||||
if (params->common.kernel_workload && params->common.user_workload)
|
||||
fatal("--kernel-threads and --user-threads are mutually exclusive!");
|
||||
|
||||
/*
|
||||
* If auto-analysis or trace output is enabled, switch from BPF mode to
|
||||
* mixed mode
|
||||
*/
|
||||
if (params->mode == TRACING_MODE_BPF &&
|
||||
(params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] ||
|
||||
params->common.end_actions.present[ACTION_TRACE_OUTPUT] ||
|
||||
!params->no_aa))
|
||||
params->mode = TRACING_MODE_MIXED;
|
||||
|
||||
return ¶ms->common;
|
||||
}
|
||||
|
||||
/*
|
||||
* rtla_usage - print rtla usage
|
||||
*/
|
||||
__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);
|
||||
}
|
||||
7
tools/tracing/rtla/src/cli.h
Normal file
7
tools/tracing/rtla/src/cli.h
Normal 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);
|
||||
670
tools/tracing/rtla/src/cli_p.h
Normal file
670
tools/tracing/rtla/src/cli_p.h
Normal 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, \
|
||||
¶ms->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, \
|
||||
¶ms->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", ¶ms->common, \
|
||||
"cpu-list", \
|
||||
"run the tracer only on the given cpus", \
|
||||
opt_cpus_cb)
|
||||
|
||||
#define RTLA_OPT_CGROUP OPT_CALLBACK_OPTARG('C', "cgroup", ¶ms->common, \
|
||||
"[cgroup_name]", NULL, \
|
||||
"set cgroup, no argument means rtla's cgroup will be inherited", \
|
||||
opt_cgroup_cb)
|
||||
|
||||
#define RTLA_OPT_USER_THREADS OPT_CALLBACK_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", \
|
||||
¶ms->common.kernel_workload, \
|
||||
"use timerlat kernel-space threads instead of rtla user-space threads")
|
||||
|
||||
#define RTLA_OPT_USER_LOAD OPT_BOOLEAN('U', "user-load", ¶ms->common.user_data, \
|
||||
"enable timerlat for user-defined user-space workload")
|
||||
|
||||
#define RTLA_OPT_DURATION OPT_CALLBACK('d', "duration", ¶ms->common, \
|
||||
"time[s|m|h|d]", \
|
||||
"set the duration of the session", \
|
||||
opt_duration_cb)
|
||||
|
||||
#define RTLA_OPT_EVENT OPT_CALLBACK('e', "event", ¶ms->common.events, \
|
||||
"sys:event", \
|
||||
"enable the <sys:event> in the trace instance, multiple -e are allowed", \
|
||||
opt_event_cb)
|
||||
|
||||
#define RTLA_OPT_HOUSEKEEPING OPT_CALLBACK('H', "house-keeping", ¶ms->common, \
|
||||
"cpu-list", \
|
||||
"run rtla control threads only on the given cpus", \
|
||||
opt_housekeeping_cb)
|
||||
|
||||
#define RTLA_OPT_PRIORITY OPT_CALLBACK('P', "priority", ¶ms->common, \
|
||||
"o:prio|r:prio|f:prio|d:runtime:period", \
|
||||
"set scheduling parameters", \
|
||||
opt_priority_cb)
|
||||
|
||||
#define RTLA_OPT_TRIGGER OPT_CALLBACK(0, "trigger", ¶ms->common.events, \
|
||||
"trigger", \
|
||||
"enable a trace event trigger to the previous -e event", \
|
||||
opt_trigger_cb)
|
||||
|
||||
#define RTLA_OPT_FILTER OPT_CALLBACK(0, "filter", ¶ms->common.events, \
|
||||
"filter", \
|
||||
"enable a trace event filter to the previous -e event", \
|
||||
opt_filter_cb)
|
||||
|
||||
#define RTLA_OPT_QUIET OPT_BOOLEAN('q', "quiet", ¶ms->common.quiet, \
|
||||
"print only a summary at the end")
|
||||
|
||||
#define RTLA_OPT_TRACE_BUFFER_SIZE OPT_CALLBACK(0, "trace-buffer-size", \
|
||||
¶ms->common.buffer_size, "kB", \
|
||||
"set the per-cpu trace buffer size in kB", \
|
||||
opt_int_callback)
|
||||
|
||||
#define RTLA_OPT_WARM_UP OPT_CALLBACK(0, "warm-up", ¶ms->common.warmup, "s", \
|
||||
"let the workload run for s seconds before collecting data", \
|
||||
opt_int_callback)
|
||||
|
||||
#define RTLA_OPT_AUTO(cb) OPT_CALLBACK('a', "auto", &cb_data, "us", \
|
||||
"set automatic trace mode, stopping the session if argument in us sample is hit", \
|
||||
cb)
|
||||
|
||||
#define RTLA_OPT_ON_THRESHOLD(threshold, cb) OPT_CALLBACK(0, "on-threshold", \
|
||||
¶ms->common.threshold_actions, \
|
||||
"action", \
|
||||
"define action to be executed at " threshold " threshold, multiple are allowed", \
|
||||
cb)
|
||||
|
||||
#define RTLA_OPT_ON_END(cb) OPT_CALLBACK(0, "on-end", ¶ms->common.end_actions, \
|
||||
"action", \
|
||||
"define action to be executed at measurement end, multiple are allowed", \
|
||||
cb)
|
||||
|
||||
#define RTLA_OPT_DEBUG OPT_BOOLEAN('D', "debug", &config_debug, \
|
||||
"print debug info")
|
||||
|
||||
/*
|
||||
* Common callback functions for command line options
|
||||
*/
|
||||
|
||||
static int opt_llong_callback(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
long long *value = opt->value;
|
||||
|
||||
if (unset || !arg)
|
||||
return -1;
|
||||
|
||||
*value = get_llong_from_str((char *)arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_int_callback(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
int *value = opt->value;
|
||||
|
||||
if (unset || !arg)
|
||||
return -1;
|
||||
|
||||
if (strtoi(arg, value))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_cpus_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct common_params *params = opt->value;
|
||||
int retval;
|
||||
|
||||
if (unset || !arg)
|
||||
return -1;
|
||||
|
||||
retval = parse_cpu_set((char *)arg, ¶ms->monitored_cpus);
|
||||
if (retval)
|
||||
fatal("Invalid -c cpu list");
|
||||
params->cpus = (char *)arg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_cgroup_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct common_params *params = opt->value;
|
||||
|
||||
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, ¶ms->hk_cpu_set);
|
||||
if (retval)
|
||||
fatal("Error parsing house keeping CPUs");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_priority_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct common_params *params = opt->value;
|
||||
int retval;
|
||||
|
||||
if (unset || !arg)
|
||||
return -1;
|
||||
|
||||
retval = parse_prio((char *)arg, ¶ms->sched_param);
|
||||
if (retval == -1)
|
||||
fatal("Invalid -P priority");
|
||||
params->set_sched = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_trigger_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct 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", ¶ms->period, "us", \
|
||||
"osnoise period in us", \
|
||||
opt_osnoise_period_cb)
|
||||
|
||||
#define OSNOISE_OPT_RUNTIME OPT_CALLBACK('r', "runtime", ¶ms->runtime, "us", \
|
||||
"osnoise runtime in us", \
|
||||
opt_osnoise_runtime_cb)
|
||||
|
||||
#define OSNOISE_OPT_THRESHOLD OPT_CALLBACK('T', "threshold", ¶ms->threshold, "us", \
|
||||
"the minimum delta to be considered a noise", \
|
||||
opt_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", ¶ms->timerlat_period_us, "us", \
|
||||
"timerlat period in us", \
|
||||
opt_timerlat_period_cb)
|
||||
|
||||
#define TIMERLAT_OPT_STACK OPT_CALLBACK('s', "stack", ¶ms->print_stack, "us", \
|
||||
"save the stack trace at the IRQ if a thread latency is higher than the argument in us", \
|
||||
opt_llong_callback)
|
||||
|
||||
#define TIMERLAT_OPT_NANO OPT_CALLBACK_NOOPT('n', "nano", params, NULL, \
|
||||
"display data in nanoseconds", \
|
||||
opt_nano_cb)
|
||||
|
||||
#define TIMERLAT_OPT_DMA_LATENCY OPT_CALLBACK(0, "dma-latency", ¶ms->dma_latency, "us", \
|
||||
"set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", \
|
||||
opt_dma_latency_cb)
|
||||
|
||||
#define TIMERLAT_OPT_DEEPEST_IDLE_STATE OPT_CALLBACK(0, "deepest-idle-state", \
|
||||
¶ms->deepest_idle_state, "n", \
|
||||
"only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", \
|
||||
opt_int_callback)
|
||||
|
||||
#define TIMERLAT_OPT_AA_ONLY OPT_CALLBACK(0, "aa-only", params, "us", \
|
||||
"stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)", \
|
||||
opt_aa_only_cb)
|
||||
|
||||
#define TIMERLAT_OPT_NO_AA OPT_BOOLEAN(0, "no-aa", ¶ms->no_aa, \
|
||||
"disable auto-analysis, reducing rtla timerlat cpu usage")
|
||||
|
||||
#define TIMERLAT_OPT_DUMPS_TASKS OPT_BOOLEAN(0, "dump-tasks", ¶ms->dump_tasks, \
|
||||
"prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)")
|
||||
|
||||
#define TIMERLAT_OPT_BPF_ACTION OPT_STRING(0, "bpf-action", ¶ms->bpf_action_program, \
|
||||
"program", \
|
||||
"load and execute BPF program when latency threshold is exceeded")
|
||||
|
||||
#define TIMERLAT_OPT_STACK_FORMAT OPT_CALLBACK(0, "stack-format", ¶ms->stack_format, "format", \
|
||||
"set the stack format (truncate, skip, full)", \
|
||||
opt_stack_format_cb)
|
||||
|
||||
/*
|
||||
* Callback functions for command line options for timerlat tools
|
||||
*/
|
||||
|
||||
static int opt_timerlat_period_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
long long *period = opt->value;
|
||||
|
||||
if (unset || !arg)
|
||||
return -1;
|
||||
|
||||
*period = get_llong_from_str((char *)arg);
|
||||
if (*period > 1000000)
|
||||
fatal("Period longer than 1 s");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_timerlat_auto_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct timerlat_cb_data *cb_data = opt->value;
|
||||
struct timerlat_params *params = cb_data->params;
|
||||
long long auto_thresh;
|
||||
|
||||
if (unset || !arg)
|
||||
return -1;
|
||||
|
||||
auto_thresh = get_llong_from_str((char *)arg);
|
||||
params->common.stop_total_us = auto_thresh;
|
||||
params->common.stop_us = auto_thresh;
|
||||
params->print_stack = auto_thresh;
|
||||
|
||||
if (!cb_data->trace_output)
|
||||
cb_data->trace_output = "timerlat_trace.txt";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_dma_latency_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
int *dma_latency = opt->value;
|
||||
int retval;
|
||||
|
||||
if (unset || !arg)
|
||||
return -1;
|
||||
|
||||
retval = strtoi((char *)arg, dma_latency);
|
||||
if (retval)
|
||||
fatal("Invalid -dma-latency %s", arg);
|
||||
if (*dma_latency < 0 || *dma_latency > 10000)
|
||||
fatal("--dma-latency needs to be >= 0 and <= 10000");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_aa_only_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct timerlat_params *params = opt->value;
|
||||
long long auto_thresh;
|
||||
|
||||
if (unset || !arg)
|
||||
return -1;
|
||||
|
||||
auto_thresh = get_llong_from_str((char *)arg);
|
||||
params->common.stop_total_us = auto_thresh;
|
||||
params->common.stop_us = auto_thresh;
|
||||
params->print_stack = auto_thresh;
|
||||
params->common.aa_only = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_timerlat_trace_output_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
const char **trace_output = opt->value;
|
||||
|
||||
if (unset)
|
||||
return -1;
|
||||
|
||||
if (!arg) {
|
||||
*trace_output = "timerlat_trace.txt";
|
||||
} else {
|
||||
*trace_output = (char *)arg;
|
||||
if (*trace_output && (*trace_output)[0] == '=')
|
||||
/* Allow -t=<trace_output> next to -t[ ]<trace_output> */
|
||||
++*trace_output;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_timerlat_on_threshold_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct actions *actions = opt->value;
|
||||
int retval;
|
||||
|
||||
if (unset || !arg)
|
||||
return -1;
|
||||
|
||||
retval = actions_parse(actions, (char *)arg, "timerlat_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", arg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_timerlat_on_end_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct actions *actions = opt->value;
|
||||
int retval;
|
||||
|
||||
if (unset || !arg)
|
||||
return -1;
|
||||
|
||||
retval = actions_parse(actions, (char *)arg, "timerlat_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", arg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_user_threads_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct timerlat_params *params = opt->value;
|
||||
|
||||
if (unset)
|
||||
return -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", \
|
||||
¶ms->common.hist.bucket_size, "N", \
|
||||
"set the histogram bucket size (default 1)", \
|
||||
opt_bucket_size_cb)
|
||||
|
||||
#define HIST_OPT_ENTRIES OPT_CALLBACK('E', "entries", ¶ms->common.hist.entries, "N", \
|
||||
"set the number of entries of the histogram (default 256)", \
|
||||
opt_entries_cb)
|
||||
|
||||
#define HIST_OPT_NO_IRQ OPT_BOOLEAN_FLAG(0, "no-irq", ¶ms->common.hist.no_irq, \
|
||||
"ignore IRQ latencies", PARSE_OPT_NOAUTONEG)
|
||||
|
||||
#define HIST_OPT_NO_THREAD OPT_BOOLEAN_FLAG(0, "no-thread", ¶ms->common.hist.no_thread, \
|
||||
"ignore thread latencies", PARSE_OPT_NOAUTONEG)
|
||||
|
||||
#define HIST_OPT_NO_HEADER OPT_BOOLEAN(0, "no-header", ¶ms->common.hist.no_header, \
|
||||
"do not print header")
|
||||
|
||||
#define HIST_OPT_NO_SUMMARY OPT_BOOLEAN(0, "no-summary", ¶ms->common.hist.no_summary, \
|
||||
"do not print summary")
|
||||
|
||||
#define HIST_OPT_NO_INDEX OPT_BOOLEAN(0, "no-index", ¶ms->common.hist.no_index, \
|
||||
"do not print index")
|
||||
|
||||
#define HIST_OPT_WITH_ZEROS OPT_BOOLEAN(0, "with-zeros", ¶ms->common.hist.with_zeros, \
|
||||
"print zero only entries")
|
||||
|
||||
/* Histogram-specific callbacks */
|
||||
|
||||
static int opt_bucket_size_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
int *bucket_size = opt->value;
|
||||
|
||||
if (unset || !arg)
|
||||
return -1;
|
||||
|
||||
*bucket_size = get_llong_from_str((char *)arg);
|
||||
if (*bucket_size == 0 || *bucket_size >= 1000000)
|
||||
fatal("Bucket size needs to be > 0 and <= 1000000");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_entries_cb(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
int *entries = opt->value;
|
||||
|
||||
if (unset || !arg)
|
||||
return -1;
|
||||
|
||||
*entries = get_llong_from_str((char *)arg);
|
||||
if (*entries < 10 || *entries > 9999999)
|
||||
fatal("Entries must be > 10 and < 10000000");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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[])
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
|
@ -13,6 +12,7 @@
|
|||
#include <time.h>
|
||||
|
||||
#include "osnoise.h"
|
||||
#include "cli.h"
|
||||
|
||||
struct osnoise_hist_cpu {
|
||||
int *samples;
|
||||
|
|
@ -400,225 +400,6 @@ osnoise_print_stats(struct osnoise_tool *tool)
|
|||
osnoise_report_missed_events(tool);
|
||||
}
|
||||
|
||||
/*
|
||||
* osnoise_hist_usage - prints osnoise hist usage message
|
||||
*/
|
||||
static void osnoise_hist_usage(void)
|
||||
{
|
||||
static const char * const msg_start[] = {
|
||||
"[-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
|
||||
" [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
|
||||
" [-c cpu-list] [-H cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] \\",
|
||||
" [--no-index] [--with-zeros] [-C [cgroup_name]] [--warm-up]",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char * const msg_opts[] = {
|
||||
" -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",
|
||||
" -p/--period us: osnoise period in us",
|
||||
" -r/--runtime us: osnoise runtime in us",
|
||||
" -s/--stop us: stop trace if a single sample is higher than the argument in us",
|
||||
" -S/--stop-total us: stop trace if the total sample is higher than the argument in us",
|
||||
" -T/--threshold us: the minimum delta to be considered a noise",
|
||||
" -c/--cpus cpu-list: list of cpus to run osnoise threads",
|
||||
" -H/--house-keeping cpus: run rtla control threads only on the given cpus",
|
||||
" -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",
|
||||
" -d/--duration time[s|m|h|d]: duration of the session",
|
||||
" -D/--debug: print debug info",
|
||||
" -t/--trace [file]: save the stopped trace to [file|osnoise_trace.txt]",
|
||||
" -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
|
||||
" --filter <filter>: enable a trace event filter to the previous -e event",
|
||||
" --trigger <trigger>: enable a trace event trigger to the previous -e event",
|
||||
" -b/--bucket-size N: set the histogram bucket size (default 1)",
|
||||
" -E/--entries N: set the number of entries of the histogram (default 256)",
|
||||
" --no-header: do not print header",
|
||||
" --no-summary: do not print summary",
|
||||
" --no-index: do not print index",
|
||||
" --with-zeros: print zero only entries",
|
||||
" -P/--priority o:prio|r:prio|f:prio|d:runtime:period: set scheduling parameters",
|
||||
" o:prio - use SCHED_OTHER with prio",
|
||||
" r:prio - use SCHED_RR with prio",
|
||||
" f:prio - use SCHED_FIFO with prio",
|
||||
" d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
|
||||
" in nanoseconds",
|
||||
" --warm-up: let the workload run for s seconds before collecting data",
|
||||
" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
|
||||
" --on-threshold <action>: define action to be executed at stop-total threshold, multiple are allowed",
|
||||
" --on-end <action>: define action to be executed at measurement end, multiple are allowed",
|
||||
NULL,
|
||||
};
|
||||
|
||||
common_usage("osnoise", "hist", "a per-cpu histogram of the OS noise",
|
||||
msg_start, msg_opts);
|
||||
}
|
||||
|
||||
/*
|
||||
* osnoise_hist_parse_args - allocs, parse and fill the cmd line parameters
|
||||
*/
|
||||
static struct common_params
|
||||
*osnoise_hist_parse_args(int argc, char *argv[])
|
||||
{
|
||||
struct osnoise_params *params;
|
||||
int retval;
|
||||
int c;
|
||||
char *trace_output = NULL;
|
||||
|
||||
params = calloc_fatal(1, sizeof(*params));
|
||||
|
||||
actions_init(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->common.end_actions);
|
||||
|
||||
/* display data in microseconds */
|
||||
params->common.output_divisor = 1000;
|
||||
params->common.hist.bucket_size = 1;
|
||||
params->common.hist.entries = 256;
|
||||
|
||||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{"auto", required_argument, 0, 'a'},
|
||||
{"bucket-size", required_argument, 0, 'b'},
|
||||
{"entries", required_argument, 0, 'E'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"period", required_argument, 0, 'p'},
|
||||
{"runtime", required_argument, 0, 'r'},
|
||||
{"stop", required_argument, 0, 's'},
|
||||
{"stop-total", required_argument, 0, 'S'},
|
||||
{"trace", optional_argument, 0, 't'},
|
||||
{"threshold", required_argument, 0, 'T'},
|
||||
{"no-header", no_argument, 0, '0'},
|
||||
{"no-summary", no_argument, 0, '1'},
|
||||
{"no-index", no_argument, 0, '2'},
|
||||
{"with-zeros", no_argument, 0, '3'},
|
||||
{"trigger", required_argument, 0, '4'},
|
||||
{"filter", required_argument, 0, '5'},
|
||||
{"warm-up", required_argument, 0, '6'},
|
||||
{"trace-buffer-size", required_argument, 0, '7'},
|
||||
{"on-threshold", required_argument, 0, '8'},
|
||||
{"on-end", required_argument, 0, '9'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
if (common_parse_options(argc, argv, ¶ms->common))
|
||||
continue;
|
||||
|
||||
c = getopt_auto(argc, argv, long_options);
|
||||
|
||||
/* detect the end of the options. */
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'a':
|
||||
/* set sample stop to auto_thresh */
|
||||
params->common.stop_us = get_llong_from_str(optarg);
|
||||
|
||||
/* set sample threshold to 1 */
|
||||
params->threshold = 1;
|
||||
|
||||
/* set trace */
|
||||
if (!trace_output)
|
||||
trace_output = "osnoise_trace.txt";
|
||||
|
||||
break;
|
||||
case 'b':
|
||||
params->common.hist.bucket_size = get_llong_from_str(optarg);
|
||||
if (params->common.hist.bucket_size == 0 ||
|
||||
params->common.hist.bucket_size >= 1000000)
|
||||
fatal("Bucket size needs to be > 0 and <= 1000000");
|
||||
break;
|
||||
case 'E':
|
||||
params->common.hist.entries = get_llong_from_str(optarg);
|
||||
if (params->common.hist.entries < 10 ||
|
||||
params->common.hist.entries > 9999999)
|
||||
fatal("Entries must be > 10 and < 9999999");
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
osnoise_hist_usage();
|
||||
break;
|
||||
case 'p':
|
||||
params->period = get_llong_from_str(optarg);
|
||||
if (params->period > 10000000)
|
||||
fatal("Period longer than 10 s");
|
||||
break;
|
||||
case 'r':
|
||||
params->runtime = get_llong_from_str(optarg);
|
||||
if (params->runtime < 100)
|
||||
fatal("Runtime shorter than 100 us");
|
||||
break;
|
||||
case 's':
|
||||
params->common.stop_us = get_llong_from_str(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
params->common.stop_total_us = get_llong_from_str(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
params->threshold = get_llong_from_str(optarg);
|
||||
break;
|
||||
case 't':
|
||||
trace_output = parse_optional_arg(argc, argv);
|
||||
if (!trace_output)
|
||||
trace_output = "osnoise_trace.txt";
|
||||
break;
|
||||
case '0': /* no header */
|
||||
params->common.hist.no_header = 1;
|
||||
break;
|
||||
case '1': /* no summary */
|
||||
params->common.hist.no_summary = 1;
|
||||
break;
|
||||
case '2': /* no index */
|
||||
params->common.hist.no_index = 1;
|
||||
break;
|
||||
case '3': /* with zeros */
|
||||
params->common.hist.with_zeros = 1;
|
||||
break;
|
||||
case '4': /* trigger */
|
||||
if (params->common.events)
|
||||
trace_event_add_trigger(params->common.events, optarg);
|
||||
else
|
||||
fatal("--trigger requires a previous -e");
|
||||
break;
|
||||
case '5': /* filter */
|
||||
if (params->common.events)
|
||||
trace_event_add_filter(params->common.events, optarg);
|
||||
else
|
||||
fatal("--filter requires a previous -e");
|
||||
break;
|
||||
case '6':
|
||||
params->common.warmup = get_llong_from_str(optarg);
|
||||
break;
|
||||
case '7':
|
||||
params->common.buffer_size = get_llong_from_str(optarg);
|
||||
break;
|
||||
case '8':
|
||||
retval = actions_parse(¶ms->common.threshold_actions, optarg,
|
||||
"osnoise_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
case '9':
|
||||
retval = actions_parse(¶ms->common.end_actions, optarg,
|
||||
"osnoise_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
default:
|
||||
fatal("Invalid option");
|
||||
}
|
||||
}
|
||||
|
||||
if (trace_output)
|
||||
actions_add_trace_output(¶ms->common.threshold_actions, trace_output);
|
||||
|
||||
if (geteuid())
|
||||
fatal("rtla needs root permission");
|
||||
|
||||
if (params->common.hist.no_index && !params->common.hist.with_zeros)
|
||||
fatal("no-index set and with-zeros not set - it does not make sense");
|
||||
|
||||
return ¶ms->common;
|
||||
}
|
||||
|
||||
/*
|
||||
* osnoise_hist_apply_config - apply the hist configs to the initialized tool
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
|
@ -13,6 +12,7 @@
|
|||
#include <time.h>
|
||||
|
||||
#include "osnoise.h"
|
||||
#include "cli.h"
|
||||
|
||||
struct osnoise_top_cpu {
|
||||
unsigned long long sum_runtime;
|
||||
|
|
@ -245,204 +245,6 @@ osnoise_print_stats(struct osnoise_tool *top)
|
|||
osnoise_report_missed_events(top);
|
||||
}
|
||||
|
||||
/*
|
||||
* osnoise_top_usage - prints osnoise top usage message
|
||||
*/
|
||||
static void osnoise_top_usage(struct osnoise_params *params)
|
||||
{
|
||||
const char *tool, *mode, *desc;
|
||||
|
||||
static const char * const msg_start[] = {
|
||||
"[-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
|
||||
" [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
|
||||
" [-c cpu-list] [-H cpu-list] [-P priority] [-C [cgroup_name]] [--warm-up s]",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char * const msg_opts[] = {
|
||||
" -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",
|
||||
" -p/--period us: osnoise period in us",
|
||||
" -r/--runtime us: osnoise runtime in us",
|
||||
" -s/--stop us: stop trace if a single sample is higher than the argument in us",
|
||||
" -S/--stop-total us: stop trace if the total sample is higher than the argument in us",
|
||||
" -T/--threshold us: the minimum delta to be considered a noise",
|
||||
" -c/--cpus cpu-list: list of cpus to run osnoise threads",
|
||||
" -H/--house-keeping cpus: run rtla control threads only on the given cpus",
|
||||
" -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",
|
||||
" -d/--duration time[s|m|h|d]: duration of the session",
|
||||
" -D/--debug: print debug info",
|
||||
" -t/--trace [file]: save the stopped trace to [file|osnoise_trace.txt]",
|
||||
" -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed",
|
||||
" --filter <filter>: enable a trace event filter to the previous -e event",
|
||||
" --trigger <trigger>: enable a trace event trigger to the previous -e event",
|
||||
" -q/--quiet print only a summary at the end",
|
||||
" -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters",
|
||||
" o:prio - use SCHED_OTHER with prio",
|
||||
" r:prio - use SCHED_RR with prio",
|
||||
" f:prio - use SCHED_FIFO with prio",
|
||||
" d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period",
|
||||
" in nanoseconds",
|
||||
" --warm-up s: let the workload run for s seconds before collecting data",
|
||||
" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
|
||||
" --on-threshold <action>: define action to be executed at stop-total threshold, multiple are allowed",
|
||||
" --on-end: define action to be executed at measurement end, multiple are allowed",
|
||||
NULL,
|
||||
};
|
||||
|
||||
if (params->mode == MODE_OSNOISE) {
|
||||
tool = "osnoise";
|
||||
mode = "top";
|
||||
desc = "a per-cpu summary of the OS noise";
|
||||
} else {
|
||||
tool = "hwnoise";
|
||||
mode = "";
|
||||
desc = "a summary of hardware-related noise";
|
||||
}
|
||||
|
||||
common_usage(tool, mode, desc, msg_start, msg_opts);
|
||||
}
|
||||
|
||||
/*
|
||||
* osnoise_top_parse_args - allocs, parse and fill the cmd line parameters
|
||||
*/
|
||||
struct common_params *osnoise_top_parse_args(int argc, char **argv)
|
||||
{
|
||||
struct osnoise_params *params;
|
||||
int retval;
|
||||
int c;
|
||||
char *trace_output = NULL;
|
||||
|
||||
params = calloc_fatal(1, sizeof(*params));
|
||||
|
||||
actions_init(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->common.end_actions);
|
||||
|
||||
if (strcmp(argv[0], "hwnoise") == 0) {
|
||||
params->mode = MODE_HWNOISE;
|
||||
/*
|
||||
* Reduce CPU usage for 75% to avoid killing the system.
|
||||
*/
|
||||
params->runtime = 750000;
|
||||
params->period = 1000000;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{"auto", required_argument, 0, 'a'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"period", required_argument, 0, 'p'},
|
||||
{"quiet", no_argument, 0, 'q'},
|
||||
{"runtime", required_argument, 0, 'r'},
|
||||
{"stop", required_argument, 0, 's'},
|
||||
{"stop-total", required_argument, 0, 'S'},
|
||||
{"threshold", required_argument, 0, 'T'},
|
||||
{"trace", optional_argument, 0, 't'},
|
||||
{"trigger", required_argument, 0, '0'},
|
||||
{"filter", required_argument, 0, '1'},
|
||||
{"warm-up", required_argument, 0, '2'},
|
||||
{"trace-buffer-size", required_argument, 0, '3'},
|
||||
{"on-threshold", required_argument, 0, '4'},
|
||||
{"on-end", required_argument, 0, '5'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
if (common_parse_options(argc, argv, ¶ms->common))
|
||||
continue;
|
||||
|
||||
c = getopt_auto(argc, argv, long_options);
|
||||
|
||||
/* Detect the end of the options. */
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'a':
|
||||
/* set sample stop to auto_thresh */
|
||||
params->common.stop_us = get_llong_from_str(optarg);
|
||||
|
||||
/* set sample threshold to 1 */
|
||||
params->threshold = 1;
|
||||
|
||||
/* set trace */
|
||||
if (!trace_output)
|
||||
trace_output = "osnoise_trace.txt";
|
||||
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
osnoise_top_usage(params);
|
||||
break;
|
||||
case 'p':
|
||||
params->period = get_llong_from_str(optarg);
|
||||
if (params->period > 10000000)
|
||||
fatal("Period longer than 10 s");
|
||||
break;
|
||||
case 'q':
|
||||
params->common.quiet = 1;
|
||||
break;
|
||||
case 'r':
|
||||
params->runtime = get_llong_from_str(optarg);
|
||||
if (params->runtime < 100)
|
||||
fatal("Runtime shorter than 100 us");
|
||||
break;
|
||||
case 's':
|
||||
params->common.stop_us = get_llong_from_str(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
params->common.stop_total_us = get_llong_from_str(optarg);
|
||||
break;
|
||||
case 't':
|
||||
trace_output = parse_optional_arg(argc, argv);
|
||||
if (!trace_output)
|
||||
trace_output = "osnoise_trace.txt";
|
||||
break;
|
||||
case 'T':
|
||||
params->threshold = get_llong_from_str(optarg);
|
||||
break;
|
||||
case '0': /* trigger */
|
||||
if (params->common.events)
|
||||
trace_event_add_trigger(params->common.events, optarg);
|
||||
else
|
||||
fatal("--trigger requires a previous -e");
|
||||
break;
|
||||
case '1': /* filter */
|
||||
if (params->common.events)
|
||||
trace_event_add_filter(params->common.events, optarg);
|
||||
else
|
||||
fatal("--filter requires a previous -e");
|
||||
break;
|
||||
case '2':
|
||||
params->common.warmup = get_llong_from_str(optarg);
|
||||
break;
|
||||
case '3':
|
||||
params->common.buffer_size = get_llong_from_str(optarg);
|
||||
break;
|
||||
case '4':
|
||||
retval = actions_parse(¶ms->common.threshold_actions, optarg,
|
||||
"osnoise_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
case '5':
|
||||
retval = actions_parse(¶ms->common.end_actions, optarg,
|
||||
"osnoise_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
default:
|
||||
fatal("Invalid option");
|
||||
}
|
||||
}
|
||||
|
||||
if (trace_output)
|
||||
actions_add_trace_output(¶ms->common.threshold_actions, trace_output);
|
||||
|
||||
if (geteuid())
|
||||
fatal("osnoise needs root permission");
|
||||
|
||||
return ¶ms->common;
|
||||
}
|
||||
|
||||
/*
|
||||
* osnoise_top_apply_config - apply the top configs to the initialized tool
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->common.end_actions);
|
||||
|
||||
/* disabled by default */
|
||||
params->dma_latency = -1;
|
||||
|
||||
/* disabled by default */
|
||||
params->deepest_idle_state = -2;
|
||||
|
||||
/* display data in microseconds */
|
||||
params->common.output_divisor = 1000;
|
||||
params->common.hist.bucket_size = 1;
|
||||
params->common.hist.entries = 256;
|
||||
|
||||
/* default to BPF mode */
|
||||
params->mode = TRACING_MODE_BPF;
|
||||
|
||||
/* default to truncate stack format */
|
||||
params->stack_format = STACK_FORMAT_TRUNCATE;
|
||||
|
||||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{"auto", required_argument, 0, 'a'},
|
||||
{"bucket-size", required_argument, 0, 'b'},
|
||||
{"entries", required_argument, 0, 'E'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"irq", required_argument, 0, 'i'},
|
||||
{"nano", no_argument, 0, 'n'},
|
||||
{"period", required_argument, 0, 'p'},
|
||||
{"stack", required_argument, 0, 's'},
|
||||
{"thread", required_argument, 0, 'T'},
|
||||
{"trace", optional_argument, 0, 't'},
|
||||
{"user-threads", no_argument, 0, 'u'},
|
||||
{"kernel-threads", no_argument, 0, 'k'},
|
||||
{"user-load", no_argument, 0, 'U'},
|
||||
{"no-irq", no_argument, 0, '0'},
|
||||
{"no-thread", no_argument, 0, '1'},
|
||||
{"no-header", no_argument, 0, '2'},
|
||||
{"no-summary", no_argument, 0, '3'},
|
||||
{"no-index", no_argument, 0, '4'},
|
||||
{"with-zeros", no_argument, 0, '5'},
|
||||
{"trigger", required_argument, 0, '6'},
|
||||
{"filter", required_argument, 0, '7'},
|
||||
{"dma-latency", required_argument, 0, '8'},
|
||||
{"no-aa", no_argument, 0, '9'},
|
||||
{"dump-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, ¶ms->common))
|
||||
continue;
|
||||
|
||||
c = getopt_auto(argc, argv, long_options);
|
||||
|
||||
/* detect the end of the options. */
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'a':
|
||||
auto_thresh = get_llong_from_str(optarg);
|
||||
|
||||
/* set thread stop to auto_thresh */
|
||||
params->common.stop_total_us = auto_thresh;
|
||||
params->common.stop_us = auto_thresh;
|
||||
|
||||
/* get stack trace */
|
||||
params->print_stack = auto_thresh;
|
||||
|
||||
/* set trace */
|
||||
if (!trace_output)
|
||||
trace_output = "timerlat_trace.txt";
|
||||
|
||||
break;
|
||||
case 'b':
|
||||
params->common.hist.bucket_size = get_llong_from_str(optarg);
|
||||
if (params->common.hist.bucket_size == 0 ||
|
||||
params->common.hist.bucket_size >= 1000000)
|
||||
fatal("Bucket size needs to be > 0 and <= 1000000");
|
||||
break;
|
||||
case 'E':
|
||||
params->common.hist.entries = get_llong_from_str(optarg);
|
||||
if (params->common.hist.entries < 10 ||
|
||||
params->common.hist.entries > 9999999)
|
||||
fatal("Entries must be > 10 and < 9999999");
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
timerlat_hist_usage();
|
||||
break;
|
||||
case 'i':
|
||||
params->common.stop_us = get_llong_from_str(optarg);
|
||||
break;
|
||||
case 'k':
|
||||
params->common.kernel_workload = 1;
|
||||
break;
|
||||
case 'n':
|
||||
params->common.output_divisor = 1;
|
||||
break;
|
||||
case 'p':
|
||||
params->timerlat_period_us = get_llong_from_str(optarg);
|
||||
if (params->timerlat_period_us > 1000000)
|
||||
fatal("Period longer than 1 s");
|
||||
break;
|
||||
case 's':
|
||||
params->print_stack = get_llong_from_str(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
params->common.stop_total_us = get_llong_from_str(optarg);
|
||||
break;
|
||||
case 't':
|
||||
trace_output = parse_optional_arg(argc, argv);
|
||||
if (!trace_output)
|
||||
trace_output = "timerlat_trace.txt";
|
||||
break;
|
||||
case 'u':
|
||||
params->common.user_workload = 1;
|
||||
/* fallback: -u implies in -U */
|
||||
case 'U':
|
||||
params->common.user_data = 1;
|
||||
break;
|
||||
case '0': /* no irq */
|
||||
params->common.hist.no_irq = 1;
|
||||
break;
|
||||
case '1': /* no thread */
|
||||
params->common.hist.no_thread = 1;
|
||||
break;
|
||||
case '2': /* no header */
|
||||
params->common.hist.no_header = 1;
|
||||
break;
|
||||
case '3': /* no summary */
|
||||
params->common.hist.no_summary = 1;
|
||||
break;
|
||||
case '4': /* no index */
|
||||
params->common.hist.no_index = 1;
|
||||
break;
|
||||
case '5': /* with zeros */
|
||||
params->common.hist.with_zeros = 1;
|
||||
break;
|
||||
case '6': /* trigger */
|
||||
if (params->common.events)
|
||||
trace_event_add_trigger(params->common.events, optarg);
|
||||
else
|
||||
fatal("--trigger requires a previous -e");
|
||||
break;
|
||||
case '7': /* filter */
|
||||
if (params->common.events)
|
||||
trace_event_add_filter(params->common.events, optarg);
|
||||
else
|
||||
fatal("--filter requires a previous -e");
|
||||
break;
|
||||
case '8':
|
||||
params->dma_latency = get_llong_from_str(optarg);
|
||||
if (params->dma_latency < 0 || params->dma_latency > 10000)
|
||||
fatal("--dma-latency needs to be >= 0 and < 10000");
|
||||
break;
|
||||
case '9':
|
||||
params->no_aa = 1;
|
||||
break;
|
||||
case '\1':
|
||||
params->dump_tasks = 1;
|
||||
break;
|
||||
case '\2':
|
||||
params->common.warmup = get_llong_from_str(optarg);
|
||||
break;
|
||||
case '\3':
|
||||
params->common.buffer_size = get_llong_from_str(optarg);
|
||||
break;
|
||||
case '\4':
|
||||
params->deepest_idle_state = get_llong_from_str(optarg);
|
||||
break;
|
||||
case '\5':
|
||||
retval = actions_parse(¶ms->common.threshold_actions, optarg,
|
||||
"timerlat_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
case '\6':
|
||||
retval = actions_parse(¶ms->common.end_actions, optarg,
|
||||
"timerlat_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
case '\7':
|
||||
params->bpf_action_program = optarg;
|
||||
break;
|
||||
case '\10':
|
||||
params->stack_format = parse_stack_format(optarg);
|
||||
if (params->stack_format == -1)
|
||||
fatal("Invalid --stack-format option");
|
||||
break;
|
||||
default:
|
||||
fatal("Invalid option");
|
||||
}
|
||||
}
|
||||
|
||||
if (trace_output)
|
||||
actions_add_trace_output(¶ms->common.threshold_actions, trace_output);
|
||||
|
||||
if (geteuid())
|
||||
fatal("rtla needs root permission");
|
||||
|
||||
if (params->common.hist.no_irq && params->common.hist.no_thread)
|
||||
fatal("no-irq and no-thread set, there is nothing to do here");
|
||||
|
||||
if (params->common.hist.no_index && !params->common.hist.with_zeros)
|
||||
fatal("no-index set with with-zeros is not set - it does not make sense");
|
||||
|
||||
/*
|
||||
* Auto analysis only happens if stop tracing, thus:
|
||||
*/
|
||||
if (!params->common.stop_us && !params->common.stop_total_us)
|
||||
params->no_aa = 1;
|
||||
|
||||
if (params->common.kernel_workload && params->common.user_workload)
|
||||
fatal("--kernel-threads and --user-threads are mutually exclusive!");
|
||||
|
||||
/*
|
||||
* If auto-analysis or trace output is enabled, switch from BPF mode to
|
||||
* mixed mode
|
||||
*/
|
||||
if (params->mode == TRACING_MODE_BPF &&
|
||||
(params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] ||
|
||||
params->common.end_actions.present[ACTION_TRACE_OUTPUT] ||
|
||||
!params->no_aa))
|
||||
params->mode = TRACING_MODE_MIXED;
|
||||
|
||||
return ¶ms->common;
|
||||
}
|
||||
|
||||
/*
|
||||
* timerlat_hist_apply_config - apply the hist configs to the initialized tool
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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(¶ms->common.threshold_actions);
|
||||
actions_init(¶ms->common.end_actions);
|
||||
|
||||
/* disabled by default */
|
||||
params->dma_latency = -1;
|
||||
|
||||
/* disabled by default */
|
||||
params->deepest_idle_state = -2;
|
||||
|
||||
/* display data in microseconds */
|
||||
params->common.output_divisor = 1000;
|
||||
|
||||
/* default to BPF mode */
|
||||
params->mode = TRACING_MODE_BPF;
|
||||
|
||||
/* default to truncate stack format */
|
||||
params->stack_format = STACK_FORMAT_TRUNCATE;
|
||||
|
||||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{"auto", required_argument, 0, 'a'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"irq", required_argument, 0, 'i'},
|
||||
{"nano", no_argument, 0, 'n'},
|
||||
{"period", required_argument, 0, 'p'},
|
||||
{"quiet", no_argument, 0, 'q'},
|
||||
{"stack", required_argument, 0, 's'},
|
||||
{"thread", required_argument, 0, 'T'},
|
||||
{"trace", optional_argument, 0, 't'},
|
||||
{"user-threads", no_argument, 0, 'u'},
|
||||
{"kernel-threads", no_argument, 0, 'k'},
|
||||
{"user-load", no_argument, 0, 'U'},
|
||||
{"trigger", required_argument, 0, '0'},
|
||||
{"filter", required_argument, 0, '1'},
|
||||
{"dma-latency", required_argument, 0, '2'},
|
||||
{"no-aa", no_argument, 0, '3'},
|
||||
{"dump-tasks", no_argument, 0, '4'},
|
||||
{"aa-only", required_argument, 0, '5'},
|
||||
{"warm-up", required_argument, 0, '6'},
|
||||
{"trace-buffer-size", required_argument, 0, '7'},
|
||||
{"deepest-idle-state", required_argument, 0, '8'},
|
||||
{"on-threshold", required_argument, 0, '9'},
|
||||
{"on-end", required_argument, 0, '\1'},
|
||||
{"bpf-action", required_argument, 0, '\2'},
|
||||
{"stack-format", required_argument, 0, '\3'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
if (common_parse_options(argc, argv, ¶ms->common))
|
||||
continue;
|
||||
|
||||
c = getopt_auto(argc, argv, long_options);
|
||||
|
||||
/* detect the end of the options. */
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'a':
|
||||
auto_thresh = get_llong_from_str(optarg);
|
||||
|
||||
/* set thread stop to auto_thresh */
|
||||
params->common.stop_total_us = auto_thresh;
|
||||
params->common.stop_us = auto_thresh;
|
||||
|
||||
/* get stack trace */
|
||||
params->print_stack = auto_thresh;
|
||||
|
||||
/* set trace */
|
||||
if (!trace_output)
|
||||
trace_output = "timerlat_trace.txt";
|
||||
|
||||
break;
|
||||
case '5':
|
||||
/* it is here because it is similar to -a */
|
||||
auto_thresh = get_llong_from_str(optarg);
|
||||
|
||||
/* set thread stop to auto_thresh */
|
||||
params->common.stop_total_us = auto_thresh;
|
||||
params->common.stop_us = auto_thresh;
|
||||
|
||||
/* get stack trace */
|
||||
params->print_stack = auto_thresh;
|
||||
|
||||
/* set aa_only to avoid parsing the trace */
|
||||
params->common.aa_only = 1;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
timerlat_top_usage();
|
||||
break;
|
||||
case 'i':
|
||||
params->common.stop_us = get_llong_from_str(optarg);
|
||||
break;
|
||||
case 'k':
|
||||
params->common.kernel_workload = true;
|
||||
break;
|
||||
case 'n':
|
||||
params->common.output_divisor = 1;
|
||||
break;
|
||||
case 'p':
|
||||
params->timerlat_period_us = get_llong_from_str(optarg);
|
||||
if (params->timerlat_period_us > 1000000)
|
||||
fatal("Period longer than 1 s");
|
||||
break;
|
||||
case 'q':
|
||||
params->common.quiet = 1;
|
||||
break;
|
||||
case 's':
|
||||
params->print_stack = get_llong_from_str(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
params->common.stop_total_us = get_llong_from_str(optarg);
|
||||
break;
|
||||
case 't':
|
||||
trace_output = parse_optional_arg(argc, argv);
|
||||
if (!trace_output)
|
||||
trace_output = "timerlat_trace.txt";
|
||||
break;
|
||||
case 'u':
|
||||
params->common.user_workload = true;
|
||||
/* fallback: -u implies -U */
|
||||
case 'U':
|
||||
params->common.user_data = true;
|
||||
break;
|
||||
case '0': /* trigger */
|
||||
if (params->common.events)
|
||||
trace_event_add_trigger(params->common.events, optarg);
|
||||
else
|
||||
fatal("--trigger requires a previous -e");
|
||||
break;
|
||||
case '1': /* filter */
|
||||
if (params->common.events)
|
||||
trace_event_add_filter(params->common.events, optarg);
|
||||
else
|
||||
fatal("--filter requires a previous -e");
|
||||
break;
|
||||
case '2': /* dma-latency */
|
||||
params->dma_latency = get_llong_from_str(optarg);
|
||||
if (params->dma_latency < 0 || params->dma_latency > 10000)
|
||||
fatal("--dma-latency needs to be >= 0 and < 10000");
|
||||
break;
|
||||
case '3': /* no-aa */
|
||||
params->no_aa = 1;
|
||||
break;
|
||||
case '4':
|
||||
params->dump_tasks = 1;
|
||||
break;
|
||||
case '6':
|
||||
params->common.warmup = get_llong_from_str(optarg);
|
||||
break;
|
||||
case '7':
|
||||
params->common.buffer_size = get_llong_from_str(optarg);
|
||||
break;
|
||||
case '8':
|
||||
params->deepest_idle_state = get_llong_from_str(optarg);
|
||||
break;
|
||||
case '9':
|
||||
retval = actions_parse(¶ms->common.threshold_actions, optarg,
|
||||
"timerlat_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
case '\1':
|
||||
retval = actions_parse(¶ms->common.end_actions, optarg,
|
||||
"timerlat_trace.txt");
|
||||
if (retval)
|
||||
fatal("Invalid action %s", optarg);
|
||||
break;
|
||||
case '\2':
|
||||
params->bpf_action_program = optarg;
|
||||
break;
|
||||
case '\3':
|
||||
params->stack_format = parse_stack_format(optarg);
|
||||
if (params->stack_format == -1)
|
||||
fatal("Invalid --stack-format option");
|
||||
break;
|
||||
default:
|
||||
fatal("Invalid option");
|
||||
}
|
||||
}
|
||||
|
||||
if (trace_output)
|
||||
actions_add_trace_output(¶ms->common.threshold_actions, trace_output);
|
||||
|
||||
if (geteuid())
|
||||
fatal("rtla needs root permission");
|
||||
|
||||
/*
|
||||
* Auto analysis only happens if stop tracing, thus:
|
||||
*/
|
||||
if (!params->common.stop_us && !params->common.stop_total_us)
|
||||
params->no_aa = 1;
|
||||
|
||||
if (params->no_aa && params->common.aa_only)
|
||||
fatal("--no-aa and --aa-only are mutually exclusive!");
|
||||
|
||||
if (params->common.kernel_workload && params->common.user_workload)
|
||||
fatal("--kernel-threads and --user-threads are mutually exclusive!");
|
||||
|
||||
/*
|
||||
* If auto-analysis or trace output is enabled, switch from BPF mode to
|
||||
* mixed mode
|
||||
*/
|
||||
if (params->mode == TRACING_MODE_BPF &&
|
||||
(params->common.threshold_actions.present[ACTION_TRACE_OUTPUT] ||
|
||||
params->common.end_actions.present[ACTION_TRACE_OUTPUT] ||
|
||||
!params->no_aa))
|
||||
params->mode = TRACING_MODE_MIXED;
|
||||
|
||||
return ¶ms->common;
|
||||
}
|
||||
|
||||
/*
|
||||
* timerlat_top_apply_config - apply the top configs to the initialized tool
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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" \
|
||||
|
|
|
|||
|
|
@ -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" \
|
||||
|
|
|
|||
|
|
@ -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" \
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user