Merge tag 'trace-tools-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace Pull RTLA tool updates from Steven Rostedt: - Fix discrepancy in --dump-tasks option Due to a mistake, rtla-timerlat-hist used the CLI syntax "--dump-task" instead of the documented "--dump-tasks". Change the option to match both documentation and the other timerlat tool, rtla-timerlat-top. - Extend coverage of runtime tests Cover both top and hist tools in all applicable test cases, add tests for a few uncovered options, and extend checks for some existing tests. - Add unit tests for actions rtla's actions feature is implemented in its source file and contains non-trivial parsing logic. Cover it with unit tests. - Stop record trace on interrupt Fix a bug where an interval exists after receiving a signal in which the main instance is stopped but the record instance is not, leading to discrepancies in reported results and sometimes rtla hanging. - Restore continue flag in actions_perform() Fix a bug where rtla always continues tracing after hitting a threshold even if the continue action was triggered just once, and add tests verifying that the flag is reset properly. - Migrate command line interface to libsubcmd Replace rtla's argument parsing using getopt_long() with libsubcmd, used by perf and objtool, to reuse existing code and auto-generate better help messages. Extensive unit tests are included to detect regressions. - Add -A/--aligned option to timerlat tools Add an option to align timerlat threads, based on the recently introduced TIMERLAT_ALIGN option of the timerlat tracer, together with unit tests and documentation. - Document tests in README Document how to run unit and runtime tests in rtla's README.txt, including the dependencies needed to run them. * tag 'trace-tools-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (26 commits) rtla: Document tests in README Documentation/rtla: Add -A/--aligned option rtla/tests: Add unit tests for -A/--aligned option rtla/timerlat: Add -A/--aligned CLI option rtla/tests: Add unit tests for CLI option callbacks rtla/tests: Add unit tests for _parse_args() functions rtla: Parse cmdline using libsubcmd tools subcmd: allow parsing distinct --opt and --no-opt tools subcmd: support optarg as separate argument rtla: Add libsubcmd dependency rtla/tests: Add runtime tests for restoring continue flag rtla/tests: Run runtime tests in temporary directory rtla/tests: Add unit test for restoring continue flag rtla/actions: Restore continue flag in actions_perform() rtla: Stop the record trace on interrupt rtla/tests: Add unit tests for actions module rtla/tests: Add runtime tests for -C/--cgroup rtla/tests: Add runtime test for -k and -u options rtla/tests: Add runtime test for -H/--house-keeping rtla/tests: Cover all hist options in runtime tests ...
diff --git a/Documentation/tools/rtla/common_appendix.txt b/Documentation/tools/rtla/common_appendix.txt index 8c90a025..68cb158 100644 --- a/Documentation/tools/rtla/common_appendix.txt +++ b/Documentation/tools/rtla/common_appendix.txt
@@ -26,9 +26,10 @@ :: - 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 ==============
diff --git a/Documentation/tools/rtla/common_timerlat_options.txt b/Documentation/tools/rtla/common_timerlat_options.txt index ab159b2..100840f 100644 --- a/Documentation/tools/rtla/common_timerlat_options.txt +++ b/Documentation/tools/rtla/common_timerlat_options.txt
@@ -95,3 +95,14 @@ * **full** Print the entire stack trace, including unknown addresses. For unknown addresses, the raw pointer is printed. + +**-A**, **--aligned** *us* + + Align wake-up of timerlat threads to a set offset in microseconds. + + The alignment will be applied when the threads wake up at the start of tracing while + the timer for the first cycle is armed. Each thread sets its timer to the wake-up time + of the previous thread plus the alignment. + + This option may be used with any non-negative argument, including zero, which will + align threads so that they wake up all at the same time.
diff --git a/tools/lib/subcmd/parse-options.c b/tools/lib/subcmd/parse-options.c index 555d617..e83200e 100644 --- a/tools/lib/subcmd/parse-options.c +++ b/tools/lib/subcmd/parse-options.c
@@ -72,6 +72,7 @@ static int get_value(struct parse_opt_ctx_t *p, const char *s, *arg = NULL; const int unset = flags & OPT_UNSET; int err; + bool force_defval = false; if (unset && p->opt) return opterror(opt, "takes no value", flags); @@ -123,6 +124,42 @@ static int get_value(struct parse_opt_ctx_t *p, } } + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + if (!(p->flags & PARSE_OPT_OPTARG_ALLOW_NEXT)) { + /* + * If the option has an optional argument, and the argument is not + * provided in the option itself, do not attempt to get it from + * the next argument, unless PARSE_OPT_OPTARG_ALLOW_NEXT is set. + * + * This prevents a non-option argument from being interpreted as an + * optional argument of a preceding option, for example: + * + * $ cmd --opt val + * -> is "val" argument of "--opt" or a separate non-option + * argument? + * + * With PARSE_OPT_OPTARG_ALLOW_NEXT, "val" is interpreted as + * the argument of "--opt", i.e. the same as "--opt=val". + * Without PARSE_OPT_OPTARG_ALLOW_NEXT, --opt is interpreted + * as having the default value, and "val" as a separate non-option + * argument. + * + * PARSE_OPT_OPTARG_ALLOW_NEXT is useful for commands that take no + * non-option arguments and want to allow more flexibility in + * optional argument passing. + */ + force_defval = true; + } + + if (p->argc <= 1 || p->argv[1][0] == '-') { + /* + * If next argument is an option or does not exist, + * use the default value. + */ + force_defval = true; + } + } + if (opt->flags & PARSE_OPT_NOBUILD) { char reason[128]; bool noarg = false; @@ -148,7 +185,7 @@ static int get_value(struct parse_opt_ctx_t *p, noarg = true; if (opt->flags & PARSE_OPT_NOARG) noarg = true; - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) + if (force_defval) noarg = true; switch (opt->type) { @@ -212,7 +249,7 @@ static int get_value(struct parse_opt_ctx_t *p, err = 0; if (unset) *(const char **)opt->value = NULL; - else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) + else if (force_defval) *(const char **)opt->value = (const char *)opt->defval; else err = get_arg(p, opt, flags, (const char **)opt->value); @@ -244,7 +281,7 @@ static int get_value(struct parse_opt_ctx_t *p, return (*opt->callback)(opt, NULL, 1) ? (-1) : 0; if (opt->flags & PARSE_OPT_NOARG) return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) + if (force_defval) return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; if (get_arg(p, opt, flags, &arg)) return -1; @@ -255,7 +292,7 @@ static int get_value(struct parse_opt_ctx_t *p, *(int *)opt->value = 0; return 0; } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + if (force_defval) { *(int *)opt->value = opt->defval; return 0; } @@ -271,7 +308,7 @@ static int get_value(struct parse_opt_ctx_t *p, *(unsigned int *)opt->value = 0; return 0; } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + if (force_defval) { *(unsigned int *)opt->value = opt->defval; return 0; } @@ -289,7 +326,7 @@ static int get_value(struct parse_opt_ctx_t *p, *(long *)opt->value = 0; return 0; } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + if (force_defval) { *(long *)opt->value = opt->defval; return 0; } @@ -305,7 +342,7 @@ static int get_value(struct parse_opt_ctx_t *p, *(unsigned long *)opt->value = 0; return 0; } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + if (force_defval) { *(unsigned long *)opt->value = opt->defval; return 0; } @@ -321,7 +358,7 @@ static int get_value(struct parse_opt_ctx_t *p, *(u64 *)opt->value = 0; return 0; } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + if (force_defval) { *(u64 *)opt->value = opt->defval; return 0; } @@ -390,7 +427,8 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, return 0; } if (!rest) { - if (strstarts(options->long_name, "no-")) { + if (strstarts(options->long_name, "no-") && + !(options->flags & PARSE_OPT_NOAUTONEG)) { /* * The long name itself starts with "no-", so * accept the option without "no-" so that users @@ -428,12 +466,12 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, continue; } /* negated and abbreviated very much? */ - if (strstarts("no-", arg)) { + if (strstarts("no-", arg) && !(options->flags & PARSE_OPT_NOAUTONEG)) { flags |= OPT_UNSET; goto is_abbreviated; } /* negated? */ - if (strncmp(arg, "no-", 3)) + if (strncmp(arg, "no-", 3) || (options->flags & PARSE_OPT_NOAUTONEG)) continue; flags |= OPT_UNSET; rest = skip_prefix(arg + 3, options->long_name); @@ -982,7 +1020,8 @@ int parse_options_usage(const char * const *usagestr, if (strstarts(opts->long_name, optstr)) print_option_help(opts, 0); if (strstarts("no-", optstr) && - strstarts(opts->long_name, optstr + 3)) + strstarts(opts->long_name, optstr + 3) && + !(opts->flags & PARSE_OPT_NOAUTONEG)) print_option_help(opts, 0); }
diff --git a/tools/lib/subcmd/parse-options.h b/tools/lib/subcmd/parse-options.h index 8e91473..38df5fd2 100644 --- a/tools/lib/subcmd/parse-options.h +++ b/tools/lib/subcmd/parse-options.h
@@ -33,6 +33,7 @@ enum parse_opt_flags { PARSE_OPT_KEEP_ARGV0 = 4, PARSE_OPT_KEEP_UNKNOWN = 8, PARSE_OPT_NO_INTERNAL_HELP = 16, + PARSE_OPT_OPTARG_ALLOW_NEXT = 32, }; enum parse_opt_option_flags { @@ -46,6 +47,7 @@ enum parse_opt_option_flags { PARSE_OPT_NOEMPTY = 128, PARSE_OPT_NOBUILD = 256, PARSE_OPT_CANSKIP = 512, + PARSE_OPT_NOAUTONEG = 1024, }; struct option; @@ -148,6 +150,8 @@ struct option { { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } #define OPT_CALLBACK(s, l, v, a, h, f) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f) } +#define OPT_CALLBACK_FLAG(s, l, v, a, h, f, fl) \ + { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f), .flags = (fl) } #define OPT_CALLBACK_SET(s, l, v, os, a, h, f) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f), .set = check_vtype(os, bool *)} #define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \
diff --git a/tools/tracing/rtla/.gitignore b/tools/tracing/rtla/.gitignore index 4d39d64..c7b4bf1 100644 --- a/tools/tracing/rtla/.gitignore +++ b/tools/tracing/rtla/.gitignore
@@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only rtla rtla-static +unit_tests fixdep feature FEATURE-DUMP @@ -9,3 +10,5 @@ osnoise_irq_noise_hist.txt osnoise_trace.txt timerlat_trace.txt +libsubcmd/ +lib/
diff --git a/tools/tracing/rtla/Makefile b/tools/tracing/rtla/Makefile index 45690ee..60a1025 100644 --- a/tools/tracing/rtla/Makefile +++ b/tools/tracing/rtla/Makefile
@@ -27,6 +27,30 @@ RTLA := $(OUTPUT)rtla RTLA_IN := $(RTLA)-in.o +LIBSUBCMD_DIR = $(srctree)/tools/lib/subcmd/ +ifneq ($(OUTPUT),) + LIBSUBCMD_OUTPUT = $(abspath $(OUTPUT))/libsubcmd +else + LIBSUBCMD_OUTPUT = $(CURDIR)/libsubcmd +endif +LIBSUBCMD = $(LIBSUBCMD_OUTPUT)/libsubcmd.a +LIBSUBCMD_INCLUDES = $(LIBSUBCMD_OUTPUT)/include +LIBSUBCMD_MAKEFLAGS = O=$(LIBSUBCMD_OUTPUT) DESTDIR=$(LIBSUBCMD_OUTPUT) prefix= subdir= + +TOOLS_INCLUDES = -I$(srctree)/tools/include + +ifneq ($(OUTPUT),) + LIB_OUTPUT = $(abspath $(OUTPUT))/lib +else + LIB_OUTPUT = $(CURDIR)/lib +endif + +LIB_STRING = $(LIB_OUTPUT)/string.o +LIB_STRING_SRC = $(srctree)/tools/lib/string.c + +LIB_STR_ERROR_R = $(LIB_OUTPUT)/str_error_r.o +LIB_STR_ERROR_R_SRC = $(srctree)/tools/lib/str_error_r.c + VERSION := $(shell sh -c "make -sC ../../.. kernelversion | grep -v make") DOCSRC := ../../../Documentation/tools/rtla/ @@ -66,7 +90,7 @@ include Makefile.config endif -CFLAGS += $(INCLUDES) $(LIB_INCLUDES) +CFLAGS += $(INCLUDES) $(LIB_INCLUDES) $(TOOLS_INCLUDES) -I$(LIBSUBCMD_INCLUDES) export CFLAGS OUTPUT srctree @@ -93,20 +117,48 @@ $(Q)echo "BPF skeleton support is disabled, skipping tests/bpf/bpf_action_map.o" endif -$(RTLA): $(RTLA_IN) - $(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RTLA) $(RTLA_IN) $(EXTLIBS) +$(RTLA): $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) + $(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RTLA) $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS) -static: $(RTLA_IN) +static: $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(eval LDFLAGS += -static) - $(QUIET_LINK)$(CC) -static $(LDFLAGS) -o $(RTLA)-static $(RTLA_IN) $(EXTLIBS) + $(QUIET_LINK)$(CC) -static $(LDFLAGS) -o $(RTLA)-static $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS) rtla.%: fixdep FORCE make -f $(srctree)/tools/build/Makefile.build dir=. $@ -$(RTLA_IN): fixdep FORCE src/timerlat.skel.h +$(RTLA_IN): fixdep FORCE src/timerlat.skel.h $(LIBSUBCMD_INCLUDES) make $(build)=rtla -clean: doc_clean fixdep-clean +$(LIBSUBCMD_OUTPUT): + $(Q)$(MKDIR) -p $@ + +$(LIBSUBCMD_INCLUDES): FORCE | $(LIBSUBCMD_OUTPUT) + $(Q)$(MAKE) -C $(LIBSUBCMD_DIR) $(LIBSUBCMD_MAKEFLAGS) \ + install_headers + +$(LIBSUBCMD): FORCE | $(LIBSUBCMD_OUTPUT) + $(Q)$(MAKE) -C $(LIBSUBCMD_DIR) $(LIBSUBCMD_MAKEFLAGS) \ + $@ + +$(LIB_OUTPUT): + $(Q)$(MKDIR) -p $@ + +$(LIB_STR_ERROR_R): $(LIB_STR_ERROR_R_SRC) | $(LIB_OUTPUT) + $(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $< + +$(LIB_STRING): $(LIB_STRING_SRC) | $(LIB_OUTPUT) + $(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $< + +libsubcmd-clean: + $(call QUIET_CLEAN, libsubcmd) + $(Q)$(RM) -r -- $(LIBSUBCMD_OUTPUT) + +lib-clean: + $(call QUIET_CLEAN, lib) + $(Q)$(RM) -r -- $(LIB_OUTPUT) + +clean: doc_clean fixdep-clean libsubcmd-clean lib-clean $(call QUIET_CLEAN, rtla) $(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete $(Q)rm -f rtla rtla-static fixdep FEATURE-DUMP rtla-*
diff --git a/tools/tracing/rtla/README.txt b/tools/tracing/rtla/README.txt index a9faee4d..13b4a79 100644 --- a/tools/tracing/rtla/README.txt +++ b/tools/tracing/rtla/README.txt
@@ -42,4 +42,34 @@ $ make $ sudo make install +Running tests + +RTLA has two test suites: a runtime test suite and a unit test suite. + +The runtime test suite is available as "make check" (root required) and has +the following dependencies, in addition to RTLA build dependencies: + +- Perl +- Test::Harness (libtest-harness-perl on Debian/Ubuntu, perl-Test-Harness on Fedora/RHEL) +- bash +- coreutils +- ldd +- util-linux +- procps(-ng) +- bpftool (if rtla is built against libbpf) + +as well as the following required system configuration: + +- CONFIG_OSNOISE_TRACER=y +- CONFIG_TIMERLAT_TRACER=y +- tracefs mounted and readable at /sys/kernel/tracing + +The unit test suite is available as "make unit-tests" and has the following +dependencies: + +- libcheck + +Unlike the runtime test suite, root is not required to run unit tests, nor is +a tracefs/osnoise/timerlat-capable kernel required. + For further information, please refer to the rtla man page.
diff --git a/tools/tracing/rtla/src/Build b/tools/tracing/rtla/src/Build index 329e24a..a1f3ab9 100644 --- a/tools/tracing/rtla/src/Build +++ b/tools/tracing/rtla/src/Build
@@ -11,4 +11,4 @@ rtla-y += timerlat_u.o rtla-y += timerlat_aa.o rtla-y += timerlat_bpf.o -rtla-y += rtla.o +rtla-y += cli.o
diff --git a/tools/tracing/rtla/src/actions.c b/tools/tracing/rtla/src/actions.c index b0d68b5..bf13d9d 100644 --- a/tools/tracing/rtla/src/actions.c +++ b/tools/tracing/rtla/src/actions.c
@@ -247,6 +247,8 @@ actions_perform(struct actions *self) int pid, retval; const struct action *action; + self->continue_flag = false; + for_each_action(self, action) { switch (action->type) { case ACTION_TRACE_OUTPUT:
diff --git a/tools/tracing/rtla/src/cli.c b/tools/tracing/rtla/src/cli.c new file mode 100644 index 0000000..c5279c9 --- /dev/null +++ b/tools/tracing/rtla/src/cli.c
@@ -0,0 +1,539 @@ +// 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() && !in_unit_test) + 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() && !in_unit_test) + fatal("rtla needs root permission"); + + if (params->common.hist.no_index && !params->common.hist.with_zeros) + fatal("no-index set and with-zeros not set - it does not make sense"); + + return ¶ms->common; +} + +struct common_params *timerlat_top_parse_args(int argc, char **argv) +{ + struct timerlat_params *params; + struct timerlat_cb_data cb_data; + + params = calloc_fatal(1, sizeof(*params)); + + cb_data.params = params; + cb_data.trace_output = NULL; + + const struct option timerlat_top_options[] = { + OPT_GROUP("Tracing Options:"), + TIMERLAT_OPT_PERIOD, + RTLA_OPT_STOP('i', "irq", "irq latency"), + RTLA_OPT_STOP_TOTAL('T', "thread", "thread latency"), + TIMERLAT_OPT_STACK, + RTLA_OPT_TRACE_OUTPUT("timerlat", opt_timerlat_trace_output_cb), + + OPT_GROUP("Event Configuration:"), + RTLA_OPT_EVENT, + RTLA_OPT_FILTER, + RTLA_OPT_TRIGGER, + + OPT_GROUP("CPU Configuration:"), + RTLA_OPT_CPUS, + RTLA_OPT_HOUSEKEEPING, + + OPT_GROUP("Thread Configuration:"), + RTLA_OPT_PRIORITY, + RTLA_OPT_CGROUP, + RTLA_OPT_USER_THREADS, + RTLA_OPT_KERNEL_THREADS, + RTLA_OPT_USER_LOAD, + TIMERLAT_OPT_ALIGNED, + + OPT_GROUP("Output:"), + TIMERLAT_OPT_NANO, + RTLA_OPT_QUIET, + + OPT_GROUP("System Tuning:"), + TIMERLAT_OPT_DMA_LATENCY, + TIMERLAT_OPT_DEEPEST_IDLE_STATE, + RTLA_OPT_TRACE_BUFFER_SIZE, + RTLA_OPT_WARM_UP, + + OPT_GROUP("Auto Analysis and Actions:"), + RTLA_OPT_AUTO(opt_timerlat_auto_cb), + TIMERLAT_OPT_AA_ONLY, + TIMERLAT_OPT_NO_AA, + TIMERLAT_OPT_DUMPS_TASKS, + RTLA_OPT_ON_THRESHOLD("latency", opt_timerlat_on_threshold_cb), + RTLA_OPT_ON_END(opt_timerlat_on_end_cb), + TIMERLAT_OPT_BPF_ACTION, + TIMERLAT_OPT_STACK_FORMAT, + + OPT_GROUP("General:"), + RTLA_OPT_DURATION, + RTLA_OPT_DEBUG, + + OPT_END(), + }; + + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); + + /* 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() && !in_unit_test) + 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, + TIMERLAT_OPT_ALIGNED, + + OPT_GROUP("Histogram Options:"), + HIST_OPT_BUCKET_SIZE, + HIST_OPT_ENTRIES, + HIST_OPT_NO_IRQ, + HIST_OPT_NO_THREAD, + HIST_OPT_NO_HEADER, + HIST_OPT_NO_SUMMARY, + HIST_OPT_NO_INDEX, + HIST_OPT_WITH_ZEROS, + + OPT_GROUP("Output:"), + TIMERLAT_OPT_NANO, + + OPT_GROUP("System Tuning:"), + TIMERLAT_OPT_DMA_LATENCY, + TIMERLAT_OPT_DEEPEST_IDLE_STATE, + RTLA_OPT_TRACE_BUFFER_SIZE, + RTLA_OPT_WARM_UP, + + OPT_GROUP("Auto Analysis and Actions:"), + RTLA_OPT_AUTO(opt_timerlat_auto_cb), + TIMERLAT_OPT_NO_AA, + TIMERLAT_OPT_DUMPS_TASKS, + RTLA_OPT_ON_THRESHOLD("latency", opt_timerlat_on_threshold_cb), + RTLA_OPT_ON_END(opt_timerlat_on_end_cb), + TIMERLAT_OPT_BPF_ACTION, + TIMERLAT_OPT_STACK_FORMAT, + + OPT_GROUP("General:"), + RTLA_OPT_DURATION, + RTLA_OPT_DEBUG, + + OPT_END(), + }; + + actions_init(¶ms->common.threshold_actions); + actions_init(¶ms->common.end_actions); + + /* 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() && !in_unit_test) + fatal("rtla needs root permission"); + + if (params->common.hist.no_irq && params->common.hist.no_thread) + fatal("no-irq and no-thread set, there is nothing to do here"); + + if (params->common.hist.no_index && !params->common.hist.with_zeros) + fatal("no-index set with with-zeros is not set - it does not make sense"); + + /* + * 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); +}
diff --git a/tools/tracing/rtla/src/cli.h b/tools/tracing/rtla/src/cli.h new file mode 100644 index 0000000..633a232 --- /dev/null +++ b/tools/tracing/rtla/src/cli.h
@@ -0,0 +1,9 @@ +/* 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); + +extern bool in_unit_test;
diff --git a/tools/tracing/rtla/src/cli_p.h b/tools/tracing/rtla/src/cli_p.h new file mode 100644 index 0000000..3c939de --- /dev/null +++ b/tools/tracing/rtla/src/cli_p.h
@@ -0,0 +1,687 @@ +/* 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) + +#define TIMERLAT_OPT_ALIGNED OPT_CALLBACK('A', "aligned", params, "us", \ + "align thread wakeups to a specific offset", \ + opt_timerlat_align_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; +} + +static int opt_timerlat_align_cb(const struct option *opt, const char *arg, int unset) +{ + struct timerlat_params *params = opt->value; + + if (unset || !arg) + return -1; + + params->timerlat_align = true; + params->timerlat_align_us = get_llong_from_str((char *)arg); + + 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; +}
diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c index bc9d01d..d0a8a6e 100644 --- a/tools/tracing/rtla/src/common.c +++ b/tools/tracing/rtla/src/common.c
@@ -5,12 +5,11 @@ #include <signal.h> #include <stdlib.h> #include <string.h> -#include <getopt.h> #include <sys/sysinfo.h> #include "common.h" -struct trace_instance *trace_inst; +struct osnoise_tool *trace_tool; volatile int stop_tracing; int nr_cpus; @@ -21,12 +20,16 @@ static void stop_trace(int sig) * Stop requested twice in a row; abort event processing and * exit immediately */ - tracefs_iterate_stop(trace_inst->inst); + if (trace_tool) + tracefs_iterate_stop(trace_tool->trace.inst); return; } stop_tracing = 1; - if (trace_inst) - trace_instance_stop(trace_inst); + if (trace_tool) { + trace_instance_stop(&trace_tool->trace); + if (trace_tool->record) + trace_instance_stop(&trace_tool->record->trace); + } } /* @@ -54,96 +57,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); -} - -/* - * set_common_option - set common options - * - * @c: option character - * @argc: argument count - * @argv: argument vector - * @common: common parameters structure - * - * Parse command line options that are common to all rtla tools. - * - * Returns: 1 if the option was set, 0 otherwise. - */ -int set_common_option(int c, int argc, char **argv, struct common_params *common) -{ - struct trace_events *tevent; - - 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: - return 0; - } - - return 1; -} - -/* * common_apply_config - apply common configs to the initialized tool */ int @@ -255,11 +168,10 @@ int run_tool(struct tool_ops *ops, int argc, char *argv[]) tool->params = params; /* - * Save trace instance into global variable so that SIGINT can stop - * the timerlat tracer. + * Expose the tool to signal handlers so they can stop the trace. * Otherwise, rtla could loop indefinitely when overloaded. */ - trace_inst = &tool->trace; + trace_tool = tool; retval = ops->apply_config(tool); if (retval) { @@ -267,7 +179,7 @@ int run_tool(struct tool_ops *ops, int argc, char *argv[]) goto out_free; } - retval = enable_tracer_by_name(trace_inst->inst, ops->tracer); + retval = enable_tracer_by_name(tool->trace.inst, ops->tracer); if (retval) { err_msg("Failed to enable %s tracer\n", ops->tracer); goto out_free;
diff --git a/tools/tracing/rtla/src/common.h b/tools/tracing/rtla/src/common.h index 8921807..04b287a 100644 --- a/tools/tracing/rtla/src/common.h +++ b/tools/tracing/rtla/src/common.h
@@ -1,7 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ #pragma once -#include <getopt.h> #include "actions.h" #include "timerlat_u.h" #include "trace.h" @@ -52,18 +51,25 @@ struct osnoise_context { /* -1 as init value because 0 is off */ int orig_opt_workload; int opt_workload; + + /* -1 as init value because 0 is off */ + int orig_opt_timerlat_align; + int opt_timerlat_align; + + /* 0 as init value */ + unsigned long long orig_timerlat_align_us; + unsigned long long timerlat_align_us; }; -extern struct trace_instance *trace_inst; extern volatile int stop_tracing; struct hist_params { - char no_irq; - char no_thread; - char no_header; - char no_summary; - char no_index; - char with_zeros; + bool no_irq; + bool no_thread; + bool no_header; + bool no_summary; + bool no_index; + bool with_zeros; int bucket_size; int entries; }; @@ -96,12 +102,12 @@ struct common_params { /* Other parameters */ struct hist_params hist; int output_divisor; - int pretty_output; - int quiet; - int user_workload; - int kernel_workload; - int user_data; - int aa_only; + bool pretty_output; + bool quiet; + bool user_workload; + bool kernel_workload; + bool user_data; + bool aa_only; struct actions threshold_actions; struct actions end_actions; @@ -177,18 +183,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); - -#define COMMON_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'} -int set_common_option(int c, 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);
diff --git a/tools/tracing/rtla/src/osnoise.c b/tools/tracing/rtla/src/osnoise.c index 2db3db1..4ff5dad 100644 --- a/tools/tracing/rtla/src/osnoise.c +++ b/tools/tracing/rtla/src/osnoise.c
@@ -15,6 +15,8 @@ #include <stdio.h> #include <sched.h> +#include <linux/compiler.h> + #include "osnoise.h" #define DEFAULT_SAMPLE_PERIOD 1000000 /* 1s */ @@ -422,6 +424,86 @@ void osnoise_put_timerlat_period_us(struct osnoise_context *context) } /* + * osnoise_get_timerlat_align_us - read and save the original "timerlat_align_us" + */ +static long long +osnoise_get_timerlat_align_us(struct osnoise_context *context) +{ + long long timerlat_align_us; + + if (context->timerlat_align_us != OSNOISE_OPTION_INIT_VAL) + return context->timerlat_align_us; + + if (context->orig_timerlat_align_us != OSNOISE_OPTION_INIT_VAL) + return context->orig_timerlat_align_us; + + timerlat_align_us = osnoise_read_ll_config("osnoise/timerlat_align_us"); + if (timerlat_align_us < 0) + goto out_err; + + context->orig_timerlat_align_us = timerlat_align_us; + return timerlat_align_us; + +out_err: + return OSNOISE_OPTION_INIT_VAL; +} + +/* + * osnoise_set_timerlat_align_us - set "timerlat_align_us" + */ +int osnoise_set_timerlat_align_us(struct osnoise_context *context, long long timerlat_align_us) +{ + long long curr_timerlat_align_us = osnoise_get_timerlat_align_us(context); + int retval; + + if (curr_timerlat_align_us == OSNOISE_OPTION_INIT_VAL) + return -1; + + retval = osnoise_write_ll_config("osnoise/timerlat_align_us", timerlat_align_us); + if (retval < 0) + return -1; + + context->timerlat_align_us = timerlat_align_us; + + return 0; +} + +/* + * osnoise_restore_timerlat_align_us - restore "timerlat_align_us" + */ +void osnoise_restore_timerlat_align_us(struct osnoise_context *context) +{ + int retval; + + if (context->orig_timerlat_align_us == OSNOISE_OPTION_INIT_VAL) + return; + + if (context->orig_timerlat_align_us == context->timerlat_align_us) + goto out_done; + + retval = osnoise_write_ll_config("osnoise/timerlat_align_us", + context->orig_timerlat_align_us); + if (retval < 0) + err_msg("Could not restore original osnoise timerlat_align_us\n"); + +out_done: + context->timerlat_align_us = OSNOISE_OPTION_INIT_VAL; +} + +/* + * osnoise_put_timerlat_align_us - restore original values and cleanup data + */ +void osnoise_put_timerlat_align_us(struct osnoise_context *context) +{ + osnoise_restore_timerlat_align_us(context); + + if (context->orig_timerlat_align_us == OSNOISE_OPTION_INIT_VAL) + return; + + context->orig_timerlat_align_us = OSNOISE_OPTION_INIT_VAL; +} + +/* * osnoise_get_stop_us - read and save the original "stop_tracing_us" */ static long long @@ -906,6 +988,67 @@ static void osnoise_put_workload(struct osnoise_context *context) context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL; } +static int osnoise_get_timerlat_align(struct osnoise_context *context) +{ + if (context->opt_timerlat_align != OSNOISE_OPTION_INIT_VAL) + return context->opt_timerlat_align; + + if (context->orig_opt_timerlat_align != OSNOISE_OPTION_INIT_VAL) + return context->orig_opt_timerlat_align; + + context->orig_opt_timerlat_align = osnoise_options_get_option("TIMERLAT_ALIGN"); + + return context->orig_opt_timerlat_align; +} + +int osnoise_set_timerlat_align(struct osnoise_context *context, bool onoff) +{ + int opt_timerlat_align = osnoise_get_timerlat_align(context); + int retval; + + if (opt_timerlat_align == OSNOISE_OPTION_INIT_VAL) + return -1; + + if (opt_timerlat_align == onoff) + return 0; + + retval = osnoise_options_set_option("TIMERLAT_ALIGN", onoff); + if (retval < 0) + return -2; + + context->opt_timerlat_align = onoff; + + return 0; +} + +static void osnoise_restore_timerlat_align(struct osnoise_context *context) +{ + int retval; + + if (context->orig_opt_timerlat_align == OSNOISE_OPTION_INIT_VAL) + return; + + if (context->orig_opt_timerlat_align == context->opt_timerlat_align) + goto out_done; + + retval = osnoise_options_set_option("TIMERLAT_ALIGN", context->orig_opt_timerlat_align); + if (retval < 0) + err_msg("Could not restore original TIMERLAT_ALIGN option\n"); + +out_done: + context->orig_opt_timerlat_align = OSNOISE_OPTION_INIT_VAL; +} + +static void osnoise_put_timerlat_align(struct osnoise_context *context) +{ + osnoise_restore_timerlat_align(context); + + if (context->orig_opt_timerlat_align == OSNOISE_OPTION_INIT_VAL) + return; + + context->orig_opt_timerlat_align = OSNOISE_OPTION_INIT_VAL; +} + enum { FLAG_CONTEXT_NEWLY_CREATED = (1 << 0), FLAG_CONTEXT_DELETED = (1 << 1), @@ -958,6 +1101,12 @@ struct osnoise_context *osnoise_context_alloc(void) context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL; context->opt_workload = OSNOISE_OPTION_INIT_VAL; + context->orig_opt_timerlat_align = OSNOISE_OPTION_INIT_VAL; + context->opt_timerlat_align = OSNOISE_OPTION_INIT_VAL; + + context->orig_timerlat_align_us = OSNOISE_OPTION_INIT_VAL; + context->timerlat_align_us = OSNOISE_OPTION_INIT_VAL; + osnoise_get_context(context); return context; @@ -986,6 +1135,8 @@ void osnoise_put_context(struct osnoise_context *context) osnoise_put_tracing_thresh(context); osnoise_put_irq_disable(context); osnoise_put_workload(context); + osnoise_put_timerlat_align(context); + osnoise_put_timerlat_align_us(context); free(context); } @@ -1171,7 +1322,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 +1360,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 +1374,7 @@ int osnoise_main(int argc, char *argv[]) } usage: - osnoise_usage(1); - exit(1); + osnoise_usage(129); } int hwnoise_main(int argc, char *argv[])
diff --git a/tools/tracing/rtla/src/osnoise.h b/tools/tracing/rtla/src/osnoise.h index 168669a..340ff5a 100644 --- a/tools/tracing/rtla/src/osnoise.h +++ b/tools/tracing/rtla/src/osnoise.h
@@ -49,6 +49,12 @@ void osnoise_restore_print_stack(struct osnoise_context *context); int osnoise_set_print_stack(struct osnoise_context *context, long long print_stack); +int osnoise_set_timerlat_align_us(struct osnoise_context *context, + long long timerlat_align_us); +void osnoise_restore_timerlat_align_us(struct osnoise_context *context); + +int osnoise_set_timerlat_align(struct osnoise_context *context, bool onoff); + int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff); void osnoise_report_missed_events(struct osnoise_tool *tool); int osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params);
diff --git a/tools/tracing/rtla/src/osnoise_hist.c b/tools/tracing/rtla/src/osnoise_hist.c index cb4ce58..dfa91d0 100644 --- a/tools/tracing/rtla/src/osnoise_hist.c +++ b/tools/tracing/rtla/src/osnoise_hist.c
@@ -4,7 +4,6 @@ */ #define _GNU_SOURCE -#include <getopt.h> #include <stdlib.h> #include <string.h> #include <signal.h> @@ -13,6 +12,7 @@ #include <time.h> #include "osnoise.h" +#include "cli.h" struct osnoise_hist_cpu { int *samples; @@ -401,226 +401,6 @@ osnoise_print_stats(struct osnoise_tool *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[] = { - COMMON_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} - }; - - c = getopt_auto(argc, argv, long_options); - - /* detect the end of the options. */ - if (c == -1) - break; - - if (set_common_option(c, argc, argv, ¶ms->common)) - continue; - - 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 */ static int
diff --git a/tools/tracing/rtla/src/osnoise_top.c b/tools/tracing/rtla/src/osnoise_top.c index e65312e..512a629 100644 --- a/tools/tracing/rtla/src/osnoise_top.c +++ b/tools/tracing/rtla/src/osnoise_top.c
@@ -4,7 +4,6 @@ */ #define _GNU_SOURCE -#include <getopt.h> #include <stdlib.h> #include <string.h> #include <signal.h> @@ -13,6 +12,7 @@ #include <time.h> #include "osnoise.h" +#include "cli.h" struct osnoise_top_cpu { unsigned long long sum_runtime; @@ -246,205 +246,6 @@ osnoise_print_stats(struct osnoise_tool *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[] = { - COMMON_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} - }; - - c = getopt_auto(argc, argv, long_options); - - /* Detect the end of the options. */ - if (c == -1) - break; - - if (set_common_option(c, argc, argv, ¶ms->common)) - continue; - - 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 */ static int
diff --git a/tools/tracing/rtla/src/rtla.c b/tools/tracing/rtla/src/rtla.c deleted file mode 100644 index 7635c70..0000000 --- a/tools/tracing/rtla/src/rtla.c +++ /dev/null
@@ -1,89 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> - */ - -#include <getopt.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> - -#include "osnoise.h" -#include "timerlat.h" - -/* - * rtla_usage - print rtla usage - */ -static void rtla_usage(int err) -{ - int i; - - static const char *msg[] = { - "", - "rtla version " VERSION, - "", - " usage: rtla COMMAND ...", - "", - " commands:", - " osnoise - gives information about the operating system noise (osnoise)", - " hwnoise - gives information about hardware-related noise", - " timerlat - measures the timer irq and thread latency", - "", - NULL, - }; - - for (i = 0; msg[i]; i++) - fprintf(stderr, "%s\n", msg[i]); - exit(err); -} - -/* - * run_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_command(int argc, char **argv, int start_position) -{ - if (strcmp(argv[start_position], "osnoise") == 0) { - osnoise_main(argc-start_position, &argv[start_position]); - goto ran; - } else if (strcmp(argv[start_position], "hwnoise") == 0) { - hwnoise_main(argc-start_position, &argv[start_position]); - goto ran; - } else if (strcmp(argv[start_position], "timerlat") == 0) { - timerlat_main(argc-start_position, &argv[start_position]); - goto ran; - } - - return 0; -ran: - return 1; -} - -int main(int argc, char *argv[]) -{ - int retval; - - /* is it an alias? */ - retval = run_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_command(argc, argv, 1); - if (retval) - exit(0); - -usage: - rtla_usage(1); - exit(1); -}
diff --git a/tools/tracing/rtla/src/timerlat.c b/tools/tracing/rtla/src/timerlat.c index f8c0575..169aa9a 100644 --- a/tools/tracing/rtla/src/timerlat.c +++ b/tools/tracing/rtla/src/timerlat.c
@@ -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" @@ -75,6 +77,24 @@ timerlat_apply_config(struct osnoise_tool *tool, struct timerlat_params *params) goto out_err; } + retval = osnoise_set_timerlat_align(tool->context, params->timerlat_align); + if (retval && params->timerlat_align) { + /* + * We might be running on a kernel that does not support timerlat align. + * Unless user requested it explicitly, ignore the error. + */ + err_msg("Failed to enable timerlat align\n"); + goto out_err; + } + + if (params->timerlat_align) { + retval = osnoise_set_timerlat_align_us(tool->context, params->timerlat_align_us); + if (retval) { + err_msg("Failed to set timerlat align us\n"); + goto out_err; + } + } + /* * If the user did not specify a type of thread, try user-threads first. * Fall back to kernel threads otherwise. @@ -202,7 +222,7 @@ void timerlat_analyze(struct osnoise_tool *tool, bool stopped) * If the trace did not stop with --aa-only, at least print * the max known latency. */ - max_lat = tracefs_instance_file_read(trace_inst->inst, "tracing_max_latency", NULL); + max_lat = tracefs_instance_file_read(tool->trace.inst, "tracing_max_latency", NULL); if (max_lat) { printf(" Max latency was %s\n", max_lat); free(max_lat); @@ -231,7 +251,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 +289,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 +303,5 @@ int timerlat_main(int argc, char *argv[]) } usage: - timerlat_usage(1); - exit(1); + timerlat_usage(129); }
diff --git a/tools/tracing/rtla/src/timerlat.h b/tools/tracing/rtla/src/timerlat.h index 364203a..84ec6d7 100644 --- a/tools/tracing/rtla/src/timerlat.h +++ b/tools/tracing/rtla/src/timerlat.h
@@ -1,4 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 +#pragma once + #include "osnoise.h" /* @@ -23,12 +25,14 @@ 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; enum stack_format stack_format; + bool timerlat_align; + unsigned long long timerlat_align_us; }; #define to_timerlat_params(ptr) container_of(ptr, struct timerlat_params, common)
diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 4b6708e..df7b139 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c
@@ -4,7 +4,6 @@ */ #define _GNU_SOURCE -#include <getopt.h> #include <stdlib.h> #include <string.h> #include <signal.h> @@ -17,6 +16,7 @@ #include "timerlat.h" #include "timerlat_aa.h" #include "timerlat_bpf.h" +#include "cli.h" #include "common.h" struct timerlat_hist_cpu { @@ -686,322 +686,6 @@ timerlat_print_stats(struct osnoise_tool *tool) } /* - * timerlat_hist_usage - prints timerlat top usage message - */ -static void timerlat_hist_usage(void) -{ - static const char * const msg_start[] = { - "[-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\", - " [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\", - " [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\", - " [--no-index] [--with-zeros] [--dma-latency us] [-C [cgroup_name]] [--no-aa] [--dump-task] [-u|-k]", - " [--warm-up s] [--deepest-idle-state n]", - NULL, - }; - - static const char * const msg_opts[] = { - " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit", - " -p/--period us: timerlat period in us", - " -i/--irq us: stop trace if the irq latency is higher than the argument in us", - " -T/--thread us: stop trace if the thread latency is higher than the argument in us", - " -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us", - " -c/--cpus cpus: run the tracer only on the given cpus", - " -H/--house-keeping cpus: run rtla control threads only on the given cpus", - " -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited", - " -d/--duration time[m|h|d]: duration of the session in seconds", - " --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)", - " -D/--debug: print debug info", - " -t/--trace [file]: save the stopped trace to [file|timerlat_trace.txt]", - " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed", - " --filter <filter>: enable a trace event filter to the previous -e event", - " --trigger <trigger>: enable a trace event trigger to the previous -e event", - " -n/--nano: display data in nanoseconds", - " --no-aa: disable auto-analysis, reducing rtla timerlat cpu usage", - " -b/--bucket-size N: set the histogram bucket size (default 1)", - " -E/--entries N: set the number of entries of the histogram (default 256)", - " --no-irq: ignore IRQ latencies", - " --no-thread: ignore thread latencies", - " --no-header: do not print header", - " --no-summary: do not print summary", - " --no-index: do not print index", - " --with-zeros: print zero only entries", - " --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", - " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters", - " o:prio - use SCHED_OTHER with prio", - " r:prio - use SCHED_RR with prio", - " f:prio - use SCHED_FIFO with prio", - " d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period", - " in nanoseconds", - " -u/--user-threads: use rtla user-space threads instead of kernel-space timerlat threads", - " -k/--kernel-threads: use timerlat kernel-space threads instead of rtla user-space threads", - " -U/--user-load: enable timerlat for user-defined user-space workload", - " --warm-up s: let the workload run for s seconds before collecting data", - " --trace-buffer-size kB: set the per-cpu trace buffer size in kB", - " --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", - " --on-threshold <action>: define action to be executed at latency threshold, multiple are allowed", - " --on-end <action>: define action to be executed at measurement end, multiple are allowed", - " --bpf-action <program>: load and execute BPF program when latency threshold is exceeded", - " --stack-format <format>: set the stack format (truncate, skip, full)", - NULL, - }; - - common_usage("timerlat", "hist", "a per-cpu histogram of the timer latency", - msg_start, msg_opts); -} - -/* - * timerlat_hist_parse_args - allocs, parse and fill the cmd line parameters - */ -static struct common_params -*timerlat_hist_parse_args(int argc, char *argv[]) -{ - struct timerlat_params *params; - int auto_thresh; - int retval; - int c; - char *trace_output = NULL; - - params = calloc_fatal(1, sizeof(*params)); - - actions_init(¶ms->common.threshold_actions); - actions_init(¶ms->common.end_actions); - - /* disabled by default */ - params->dma_latency = -1; - - /* disabled by default */ - params->deepest_idle_state = -2; - - /* display data in microseconds */ - params->common.output_divisor = 1000; - params->common.hist.bucket_size = 1; - params->common.hist.entries = 256; - - /* default to BPF mode */ - params->mode = TRACING_MODE_BPF; - - /* default to truncate stack format */ - params->stack_format = STACK_FORMAT_TRUNCATE; - - while (1) { - static struct option long_options[] = { - COMMON_OPTIONS, - {"auto", required_argument, 0, 'a'}, - {"bucket-size", required_argument, 0, 'b'}, - {"entries", required_argument, 0, 'E'}, - {"help", no_argument, 0, 'h'}, - {"irq", required_argument, 0, 'i'}, - {"nano", no_argument, 0, 'n'}, - {"period", required_argument, 0, 'p'}, - {"stack", required_argument, 0, 's'}, - {"thread", required_argument, 0, 'T'}, - {"trace", optional_argument, 0, 't'}, - {"user-threads", no_argument, 0, 'u'}, - {"kernel-threads", no_argument, 0, 'k'}, - {"user-load", no_argument, 0, 'U'}, - {"no-irq", no_argument, 0, '0'}, - {"no-thread", no_argument, 0, '1'}, - {"no-header", no_argument, 0, '2'}, - {"no-summary", no_argument, 0, '3'}, - {"no-index", no_argument, 0, '4'}, - {"with-zeros", no_argument, 0, '5'}, - {"trigger", required_argument, 0, '6'}, - {"filter", required_argument, 0, '7'}, - {"dma-latency", required_argument, 0, '8'}, - {"no-aa", no_argument, 0, '9'}, - {"dump-task", no_argument, 0, '\1'}, - {"warm-up", required_argument, 0, '\2'}, - {"trace-buffer-size", required_argument, 0, '\3'}, - {"deepest-idle-state", required_argument, 0, '\4'}, - {"on-threshold", required_argument, 0, '\5'}, - {"on-end", required_argument, 0, '\6'}, - {"bpf-action", required_argument, 0, '\7'}, - {"stack-format", required_argument, 0, '\10'}, - {0, 0, 0, 0} - }; - - c = getopt_auto(argc, argv, long_options); - - if (set_common_option(c, argc, argv, ¶ms->common)) - continue; - - /* 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 */ static int
diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 91f88bb..18e1071 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c
@@ -4,7 +4,6 @@ */ #define _GNU_SOURCE -#include <getopt.h> #include <stdlib.h> #include <string.h> #include <signal.h> @@ -17,6 +16,7 @@ #include "timerlat.h" #include "timerlat_aa.h" #include "timerlat_bpf.h" +#include "cli.h" #include "common.h" struct timerlat_top_cpu { @@ -460,290 +460,6 @@ timerlat_print_stats(struct osnoise_tool *top) } /* - * timerlat_top_usage - prints timerlat top usage message - */ -static void timerlat_top_usage(void) -{ - static const char *const msg_start[] = { - "[-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\", - " [[-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\", - " [-P priority] [--dma-latency us] [--aa-only us] [-C [cgroup_name]] [-u|-k] [--warm-up s] [--deepest-idle-state n]", - NULL, - }; - - static const char *const msg_opts[] = { - " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit", - " --aa-only us: stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)", - " -p/--period us: timerlat period in us", - " -i/--irq us: stop trace if the irq latency is higher than the argument in us", - " -T/--thread us: stop trace if the thread latency is higher than the argument in us", - " -s/--stack us: save the stack trace at the IRQ if a thread latency is higher than the argument in us", - " -c/--cpus cpus: run the tracer only on the given cpus", - " -H/--house-keeping cpus: run rtla control threads only on the given cpus", - " -C/--cgroup [cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited", - " -d/--duration time[s|m|h|d]: duration of the session", - " -D/--debug: print debug info", - " --dump-tasks: prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)", - " -t/--trace [file]: save the stopped trace to [file|timerlat_trace.txt]", - " -e/--event <sys:event>: enable the <sys:event> in the trace instance, multiple -e are allowed", - " --filter <command>: enable a trace event filter to the previous -e event", - " --trigger <command>: enable a trace event trigger to the previous -e event", - " -n/--nano: display data in nanoseconds", - " --no-aa: disable auto-analysis, reducing rtla timerlat cpu usage", - " -q/--quiet print only a summary at the end", - " --dma-latency us: set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", - " -P/--priority o:prio|r:prio|f:prio|d:runtime:period : set scheduling parameters", - " o:prio - use SCHED_OTHER with prio", - " r:prio - use SCHED_RR with prio", - " f:prio - use SCHED_FIFO with prio", - " d:runtime[us|ms|s]:period[us|ms|s] - use SCHED_DEADLINE with runtime and period", - " in nanoseconds", - " -u/--user-threads: use rtla user-space threads instead of kernel-space timerlat threads", - " -k/--kernel-threads: use timerlat kernel-space threads instead of rtla user-space threads", - " -U/--user-load: enable timerlat for user-defined user-space workload", - " --warm-up s: let the workload run for s seconds before collecting data", - " --trace-buffer-size kB: set the per-cpu trace buffer size in kB", - " --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", - " --on-threshold <action>: define action to be executed at latency threshold, multiple are allowed", - " --on-end: define action to be executed at measurement end, multiple are allowed", - " --bpf-action <program>: load and execute BPF program when latency threshold is exceeded", - " --stack-format <format>: set the stack format (truncate, skip, full)", - NULL, - }; - - common_usage("timerlat", "top", "a per-cpu summary of the timer latency", - msg_start, msg_opts); -} - -/* - * timerlat_top_parse_args - allocs, parse and fill the cmd line parameters - */ -static struct common_params -*timerlat_top_parse_args(int argc, char **argv) -{ - struct timerlat_params *params; - long long auto_thresh; - int retval; - int c; - char *trace_output = NULL; - - params = calloc_fatal(1, sizeof(*params)); - - actions_init(¶ms->common.threshold_actions); - actions_init(¶ms->common.end_actions); - - /* disabled by default */ - params->dma_latency = -1; - - /* disabled by default */ - params->deepest_idle_state = -2; - - /* display data in microseconds */ - params->common.output_divisor = 1000; - - /* default to BPF mode */ - params->mode = TRACING_MODE_BPF; - - /* default to truncate stack format */ - params->stack_format = STACK_FORMAT_TRUNCATE; - - while (1) { - static struct option long_options[] = { - COMMON_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} - }; - - c = getopt_auto(argc, argv, long_options); - - if (set_common_option(c, argc, argv, ¶ms->common)) - continue; - - /* 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 */ static int
diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index 9cec5b3..cb187e7 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c
@@ -22,7 +22,7 @@ #include "common.h" #define MAX_MSG_LENGTH 1024 -int config_debug; +bool config_debug; /* * err_msg - print an error message to the stderr @@ -1011,32 +1011,6 @@ int auto_house_keeping(cpu_set_t *monitored_cpus) return 1; } -/** - * parse_optional_arg - Parse optional argument value - * - * Parse optional argument value, which can be in the form of: - * -sarg, -s/--long=arg, -s/--long arg - * - * Returns arg value if found, NULL otherwise. - */ -char *parse_optional_arg(int argc, char **argv) -{ - if (optarg) { - if (optarg[0] == '=') { - /* skip the = */ - return &optarg[1]; - } else { - return optarg; - } - /* parse argument of form -s [arg] and --long [arg]*/ - } else if (optind < argc && argv[optind][0] != '-') { - /* consume optind */ - return argv[optind++]; - } else { - return NULL; - } -} - /* * strtoi - convert string to integer with error checking *
diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h index e794ede..2ba3333 100644 --- a/tools/tracing/rtla/src/utils.h +++ b/tools/tracing/rtla/src/utils.h
@@ -7,6 +7,8 @@ #include <stdbool.h> #include <stdlib.h> +#include <linux/container_of.h> + /* * '18446744073709551615\0' */ @@ -37,11 +39,7 @@ static inline bool str_has_prefix(const char *str, const char *prefix) return strncmp(str, prefix, strlen(prefix)) == 0; } -#define container_of(ptr, type, member)({ \ - const typeof(((type *)0)->member) *__mptr = (ptr); \ - (type *)((char *)__mptr - offsetof(type, member)) ; }) - -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, ...); @@ -49,7 +47,6 @@ void fatal(const char *fmt, ...); long parse_seconds_duration(char *val); void get_duration(time_t start_time, char *output, int output_size); -char *parse_optional_arg(int argc, char **argv); long long get_llong_from_str(char *start); static inline void
diff --git a/tools/tracing/rtla/tests/engine.sh b/tools/tracing/rtla/tests/engine.sh index ed261e0..5bf8453 100644 --- a/tools/tracing/rtla/tests/engine.sh +++ b/tools/tracing/rtla/tests/engine.sh
@@ -4,6 +4,9 @@ # Count tests to allow the test harness to double-check if all were # included correctly. ctr=0 + # Set test directory to the directory of the script + scriptfile=$(realpath "$0") + testdir=$(dirname "$scriptfile") [ -z "$RTLA" ] && RTLA="./rtla" [ -n "$TEST_COUNT" ] && echo "1..$TEST_COUNT" } @@ -51,6 +54,11 @@ then # Reset osnoise options before running test. [ "$NO_RESET_OSNOISE" == 1 ] || reset_osnoise + + # Create a temporary directory to contain rtla output + tmpdir=$(mktemp -d) + pushd $tmpdir >/dev/null + # Run rtla; in case of failure, include its output as comment # in the test results. result=$(eval stdbuf -oL $TIMEOUT "$RTLA" $2 2>&1); exitcode=$? @@ -82,6 +90,10 @@ echo "$result" | col -b | while read line; do echo "# $line"; done printf "#\n# exit code %s\n" $exitcode fi + + # Remove temporary directory + popd >/dev/null + rm -r $tmpdir fi } @@ -112,6 +124,21 @@ NO_RESET_OSNOISE=1 check "$arg1" "$arg2" "$arg3" } +check_top_hist() { + # Test one command with both "top" and "hist" tools, replacing "TOOL" in + # command with either "top" or "hist" respectively, and prefixing the test + # names with "top " and "hist ". + check "top $1" "$(echo "$2" | sed 's/TOOL/top/g')" "${@:3}" + check "hist $1" "$(echo "$2" | sed 's/TOOL/hist/g')" "${@:3}" +} + +check_top_q_hist() { + # Same as above, but pass "-q" to top so that strings printed in main + # loop are on their own line for top too, not only for hist. + check "top $1" "$(echo "$2" | sed 's/TOOL/top -q/g')" "${@:3}" + check "hist $1" "$(echo "$2" | sed 's/TOOL/hist/g')" "${@:3}" +} + set_timeout() { TIMEOUT="timeout -v -k 15s $1" }
diff --git a/tools/tracing/rtla/tests/hwnoise.t b/tools/tracing/rtla/tests/hwnoise.t index 23ce250..cfe687f 100644 --- a/tools/tracing/rtla/tests/hwnoise.t +++ b/tools/tracing/rtla/tests/hwnoise.t
@@ -6,7 +6,7 @@ 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" \
diff --git a/tools/tracing/rtla/tests/osnoise.t b/tools/tracing/rtla/tests/osnoise.t index 3963346..346a14a 100644 --- a/tools/tracing/rtla/tests/osnoise.t +++ b/tools/tracing/rtla/tests/osnoise.t
@@ -6,16 +6,41 @@ set_timeout 2m check "verify help page" \ - "osnoise --help" 0 "osnoise version" -check "verify the --priority/-P param" \ - "osnoise top -P F:1 -c 0 -r 900000 -d 10s -q -S 1 --on-threshold shell,command=\"tests/scripts/check-priority.sh osnoise/ SCHED_FIFO 1\"" \ + "osnoise --help" 129 "osnoise version" +check_top_hist "verify help page" \ + "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" \ + "osnoise TOOL -s 30 -T 1 -t" 2 "Saving trace to osnoise_trace.txt" + +# Thread tests +check_top_q_hist "verify the --priority/-P param" \ + "osnoise TOOL -P F:1 -c 0 -r 900000 -d 10s -S 1 --on-threshold shell,command=\"$testdir/scripts/check-priority.sh SCHED_FIFO 1\"" \ 2 "Priorities are set correctly" -check "verify the --stop/-s param" \ - "osnoise top -s 30 -T 1" 2 "osnoise hit stop tracing" -check "verify the --trace param" \ - "osnoise hist -s 30 -T 1 -t" 2 "Saving trace to osnoise_trace.txt" -check "verify the --entries/-E param" \ - "osnoise hist -P F:1 -c 0 -r 900000 -d 10s -b 10 -E 25" +check_top_q_hist "verify the -C/--cgroup param" \ + "osnoise TOOL -C -c 0 -r 900000 -d 10s -S 1 --on-threshold shell,command=\"$testdir/scripts/check-cgroup-match.sh\"" \ + 2 "cgroup matches for all workload PIDs" +check_top_q_hist "verify the -c/--cpus param" \ + "osnoise TOOL -P F:1 -c 0 -r 900000 -d 10s -S 1 --on-threshold shell,command=$testdir/scripts/check-cpus.sh" 2 "^Affinity of threads: 0$" +check_top_q_hist "verify the -H/--house-keeping param" \ + "osnoise TOOL -P F:1 -H 0 -r 900000 -d 10s -S 1 --on-threshold shell,command=$testdir/scripts/check-housekeeping-cpus.sh" 2 "^Affinity of threads: 0$" + +# Histogram tests +check "hist with -b/--bucket-size" \ + "osnoise hist -b 1 -d 1s" +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 < 10000000$" +check "hist with --no-header" \ + "osnoise hist --no-header -d 1s" 0 "" "RTLA osnoise histogram" +check "hist with --with-zeros" \ + "osnoise hist --with-zeros -b 100000 -E 21 -d 1s" 0 '^2000000\s+0\s+' +check "hist with --no-index" \ + "osnoise hist --no-index --with-zeros -d 1s" 0 "" "^count:" +check "hist with --no-summary" \ + "osnoise hist --no-summary -d 1s" 0 "" "^count:" # Test setting default period by putting an absurdly high period # and stopping on threshold. @@ -24,27 +49,25 @@ "osnoise hist -s 1" 2 period_us=600000000 # Actions tests -check "trace output through -t with custom filename" \ - "osnoise hist -S 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" -check "trace output through --on-threshold trace" \ - "osnoise hist -S 2 --on-threshold trace" 2 "^ Saving trace to osnoise_trace.txt$" -check "trace output through --on-threshold trace with custom filename" \ - "osnoise hist -S 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" -check "exec command" \ - "osnoise hist -S 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$" -check "multiple actions" \ - "osnoise hist -S 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$" +check_top_q_hist "trace output through -t with custom filename" \ + "osnoise TOOL -S 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" +check_top_q_hist "trace output through --on-threshold trace" \ + "osnoise TOOL -S 2 --on-threshold trace" 2 "^ Saving trace to osnoise_trace.txt$" +check_top_q_hist "trace output through --on-threshold trace with custom filename" \ + "osnoise TOOL -S 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" +check_top_q_hist "exec command" \ + "osnoise TOOL -S 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$" +check_top_q_hist "multiple actions" \ + "osnoise TOOL -S 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$" check "hist stop at failed action" \ "osnoise hist -S 2 --on-threshold shell,command='echo -n 1; false' --on-threshold shell,command='echo -n 2'" 2 "^1# RTLA osnoise histogram$" check "top stop at failed action" \ "osnoise top -S 2 --on-threshold shell,command='echo -n abc; false' --on-threshold shell,command='echo -n defgh'" 2 "^abc" "defgh" -check "hist with continue" \ - "osnoise hist -S 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$" -check "top with continue" \ - "osnoise top -q -S 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$" -check "hist with trace output at end" \ - "osnoise hist -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$" -check "top with trace output at end" \ - "osnoise top -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$" +check_top_q_hist "with continue" \ + "osnoise TOOL -S 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$" +check_top_q_hist "with conditional continue" \ + "osnoise TOOL -S 2 --on-threshold shell,command='if [ -f a ]; then echo 2; exit 1; else echo -n 1; touch a; fi' --on-threshold continue" 2 "^12$" "^2$" +check_top_hist "with trace output at end" \ + "osnoise TOOL -d 1s --on-end trace" 0 "^ Saving trace to osnoise_trace.txt$" test_end
diff --git a/tools/tracing/rtla/tests/scripts/check-cgroup-match.sh b/tools/tracing/rtla/tests/scripts/check-cgroup-match.sh new file mode 100755 index 0000000..fdc2c68 --- /dev/null +++ b/tools/tracing/rtla/tests/scripts/check-cgroup-match.sh
@@ -0,0 +1,17 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +. "$(dirname $0)/lib/get_workload_pids.sh" +rtla_pid=$(echo $(ps -o ppid= $$)) +rtla_cgroup=$(</proc/$rtla_pid/cgroup) +echo "RTLA cgroup: $rtla_cgroup" +for pid in $(get_workload_pids) +do + pid_cgroup=$(</proc/$pid/cgroup) + echo "PID $pid cgroup: $pid_cgroup" + if ! [ "$pid_cgroup" = "$rtla_cgroup" ] + then + echo "Mismatch!" + exit 0 + fi +done +echo "cgroup matches for all workload PIDs"
diff --git a/tools/tracing/rtla/tests/scripts/check-cpus.sh b/tools/tracing/rtla/tests/scripts/check-cpus.sh new file mode 100755 index 0000000..0b016d4 --- /dev/null +++ b/tools/tracing/rtla/tests/scripts/check-cpus.sh
@@ -0,0 +1,9 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +. "$(dirname $0)/lib/get_workload_pids.sh" +echo -n "Affinity of threads: " +for pid in $(get_workload_pids) +do + echo -n $(taskset -c -p $pid | cut -d ':' -f 2) +done +echo
diff --git a/tools/tracing/rtla/tests/scripts/check-housekeeping-cpus.sh b/tools/tracing/rtla/tests/scripts/check-housekeeping-cpus.sh new file mode 100755 index 0000000..4742f34 --- /dev/null +++ b/tools/tracing/rtla/tests/scripts/check-housekeeping-cpus.sh
@@ -0,0 +1,4 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +pid=$(ps -o ppid= $$) +echo "Affinity of threads:$(taskset -c -p $pid | cut -d ':' -f 2)"
diff --git a/tools/tracing/rtla/tests/scripts/check-priority.sh b/tools/tracing/rtla/tests/scripts/check-priority.sh index 79b702a..b51d523 100755 --- a/tools/tracing/rtla/tests/scripts/check-priority.sh +++ b/tools/tracing/rtla/tests/scripts/check-priority.sh
@@ -1,8 +1,8 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0 -pids="$(pgrep ^$1)" || exit 1 -for pid in $pids +. "$(dirname $0)/lib/get_workload_pids.sh" +for pid in $(get_workload_pids) do - chrt -p $pid | cut -d ':' -f 2 | head -n1 | grep "^ $2\$" >/dev/null - chrt -p $pid | cut -d ':' -f 2 | tail -n1 | grep "^ $3\$" >/dev/null + chrt -p $pid | cut -d ':' -f 2 | head -n1 | grep "^ $1\$" >/dev/null + chrt -p $pid | cut -d ':' -f 2 | tail -n1 | grep "^ $2\$" >/dev/null done && echo "Priorities are set correctly"
diff --git a/tools/tracing/rtla/tests/scripts/check-user-kernel-threads.sh b/tools/tracing/rtla/tests/scripts/check-user-kernel-threads.sh new file mode 100755 index 0000000..bb7ac51 --- /dev/null +++ b/tools/tracing/rtla/tests/scripts/check-user-kernel-threads.sh
@@ -0,0 +1,16 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +. "$(dirname $0)/lib/get_workload_pids.sh" +kthreadd_pid=$(pgrep ^kthreadd$) +cnt_kernel=0 +cnt_user=0 +for pid in $(get_workload_pids) +do + if [ "$(echo $(ps -o ppid= $pid))" = "$kthreadd_pid" ] + then + ((++cnt_kernel)) + else + ((++cnt_user)) + fi +done +echo "$cnt_kernel kernel threads, $cnt_user user threads"
diff --git a/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh b/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh new file mode 100644 index 0000000..8aff98c --- /dev/null +++ b/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh
@@ -0,0 +1,11 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +get_workload_pids() { + local shell_pid=$$ + local rtla_pid=$(ps -o ppid= $shell_pid) + + # kernel threads + pgrep -P $(pgrep ^kthreadd$) -f '^(osnoise|timerlat)/[0-9]+$' + # user threads + pgrep -P $rtla_pid | grep -v "^$shell_pid$" +}
diff --git a/tools/tracing/rtla/tests/timerlat.t b/tools/tracing/rtla/tests/timerlat.t index fd4935f..8193048 100644 --- a/tools/tracing/rtla/tests/timerlat.t +++ b/tools/tracing/rtla/tests/timerlat.t
@@ -21,65 +21,96 @@ # Basic tests check "verify help page" \ - "timerlat --help" 0 "timerlat version" -check "verify -s/--stack" \ - "timerlat top -s 3 -T 10 -t" 2 "Blocking thread stack trace" -check "verify -P/--priority" \ - "timerlat top -P F:1 -c 0 -d 10s -q -T 1 --on-threshold shell,command=\"tests/scripts/check-priority.sh timerlatu/ SCHED_FIFO 1\"" \ + "timerlat --help" 129 "timerlat version" +check_top_hist "verify help page" \ + "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" \ + "timerlat TOOL -i 2 -c 0 -n -d 10s" 2 "ns" +check_top_hist "set the automatic trace mode" \ + "timerlat TOOL -a 5" 2 "analyzing it" +check_top_hist "dump tasks" \ + "timerlat TOOL -a 5 --dump-tasks" 2 "Printing CPU tasks" +check "verify --aa-only stop on threshold" \ + "timerlat top --aa-only 5" 2 "analyzing it" "Timer Latency" +check "verify --aa-only max latency" \ + "timerlat top --aa-only 2000000 -d 1s" 0 "^ Max latency was" "Timer Latency" +check_top_hist "disable auto-analysis" \ + "timerlat TOOL -s 3 -T 10 -t --no-aa" 2 "" "analyzing it" + +# Thread tests +check_top_hist "verify -P/--priority" \ + "timerlat TOOL -P F:1 -c 0 -d 10s -T 1 --on-threshold shell,command=\"$testdir/scripts/check-priority.sh SCHED_FIFO 1\"" \ 2 "Priorities are set correctly" -check "test in nanoseconds" \ - "timerlat top -i 2 -c 0 -n -d 10s" 2 "ns" -check "set the automatic trace mode" \ - "timerlat top -a 5" 2 "analyzing it" -check "dump tasks" \ - "timerlat top -a 5 --dump-tasks" 2 "Printing CPU tasks" -check "print the auto-analysis if hits the stop tracing condition" \ - "timerlat top --aa-only 5" 2 -check "disable auto-analysis" \ - "timerlat top -s 3 -T 10 -t --no-aa" 2 -check "verify -c/--cpus" \ - "timerlat hist -c 0 -d 10s" -check "hist test in nanoseconds" \ - "timerlat hist -i 2 -c 0 -n -d 10s" 2 "ns" +check_top_hist "verify -C/--cgroup" \ + "timerlat TOOL -k -C -c 0 -d 10s -T 1 --on-threshold shell,command=\"$testdir/scripts/check-cgroup-match.sh\"" \ + 2 "cgroup matches for all workload PIDs" +check_top_q_hist "verify -c/--cpus" \ + "timerlat TOOL -c 0 -d 10s -T 1 --on-threshold shell,command=$testdir/scripts/check-cpus.sh" 2 "^Affinity of threads: 0$" +check_top_q_hist "verify -H/--house-keeping" \ + "timerlat TOOL -H 0 -d 10s -T 1 --on-threshold shell,command=$testdir/scripts/check-housekeeping-cpus.sh" 2 "^Affinity of threads: 0$" +check_top_q_hist "verify -k/--kernel-threads" \ + "timerlat TOOL -k -c 0 -d 10s -T 1 --on-threshold shell,command=$testdir/scripts/check-user-kernel-threads.sh" 2 "1 kernel threads, 0 user threads" +check_top_q_hist "verify -u/--user-threads" \ + "timerlat TOOL -u -c 0 -d 10s -T 1 --on-threshold shell,command=$testdir/scripts/check-user-kernel-threads.sh" 2 "0 kernel threads, 1 user threads" + +# Histogram tests +check "hist with -b/--bucket-size" \ + "timerlat hist -b 1 -d 1s" +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 < 10000000$" +check "hist with --no-header" \ + "timerlat hist --no-header -d 1s" 0 "" "RTLA timerlat histogram" +check "hist with --with-zeros" \ + "timerlat hist --with-zeros -b 100000 -E 21 -d 1s" 0 '^2000000\s+0\s+' +check "hist with --no-index" \ + "timerlat hist --no-index --with-zeros -d 1s" 0 "" "^count:" +check "hist with --no-summary" \ + "timerlat hist --no-summary -d 1s" 0 "" "^ALL:" +check "hist with --no-irq" \ + "timerlat hist --no-irq -d 1s" 0 "" "IRQ-" +check "hist with --no-thread" \ + "timerlat hist --no-thread -d 1s" 0 "" "Thr-" # Actions tests -check "trace output through -t" \ - "timerlat hist -T 2 -t" 2 "^ Saving trace to timerlat_trace.txt$" -check "trace output through -t with custom filename" \ - "timerlat hist -T 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" -check "trace output through --on-threshold trace" \ - "timerlat hist -T 2 --on-threshold trace" 2 "^ Saving trace to timerlat_trace.txt$" -check "trace output through --on-threshold trace with custom filename" \ - "timerlat hist -T 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" -check "exec command" \ - "timerlat hist -T 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$" -check "multiple actions" \ - "timerlat hist -T 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$" +check_top_q_hist "trace output through -t" \ + "timerlat TOOL -T 2 -t" 2 "^ Saving trace to timerlat_trace.txt$" +check_top_q_hist "trace output through -t with custom filename" \ + "timerlat TOOL -T 2 -t custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" +check_top_q_hist "trace output through --on-threshold trace" \ + "timerlat TOOL -T 2 --on-threshold trace" 2 "^ Saving trace to timerlat_trace.txt$" +check_top_q_hist "trace output through --on-threshold trace with custom filename" \ + "timerlat TOOL -T 2 --on-threshold trace,file=custom_filename.txt" 2 "^ Saving trace to custom_filename.txt$" +check_top_q_hist "exec command" \ + "timerlat TOOL -T 2 --on-threshold shell,command='echo TestOutput'" 2 "^TestOutput$" +check_top_q_hist "multiple actions" \ + "timerlat TOOL -T 2 --on-threshold shell,command='echo -n 1' --on-threshold shell,command='echo 2'" 2 "^12$" check "hist stop at failed action" \ "timerlat hist -T 2 --on-threshold shell,command='echo -n 1; false' --on-threshold shell,command='echo -n 2'" 2 "^1# RTLA timerlat histogram$" check "top stop at failed action" \ "timerlat top -T 2 --on-threshold shell,command='echo -n abc; false' --on-threshold shell,command='echo -n defgh'" 2 "^abc" "defgh" -check "hist with continue" \ - "timerlat hist -T 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$" -check "top with continue" \ - "timerlat top -q -T 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$" -check "hist with trace output at end" \ - "timerlat hist -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$" -check "top with trace output at end" \ - "timerlat top -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$" +check_top_q_hist "with continue" \ + "timerlat TOOL -T 2 -d 5s --on-threshold shell,command='echo TestOutput' --on-threshold continue" 0 "^TestOutput$" +check_top_q_hist "with conditional continue" \ + "timerlat TOOL -T 2 --on-threshold shell,command='if [ -f a ]; then echo 2; exit 1; else echo -n 1; touch a; fi' --on-threshold continue" 2 "^12$" "^2$" +check_top_hist "with trace output at end" \ + "timerlat TOOL -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$" # BPF action program tests if [ "$option" -eq 0 ] then # Test BPF action program properly in BPF mode [ -z "$BPFTOOL" ] && BPFTOOL=bpftool - check "hist with BPF action program (BPF mode)" \ - "timerlat hist -T 2 --bpf-action tests/bpf/bpf_action_map.o --on-threshold shell,command='$BPFTOOL map dump name rtla_test_map'" \ + check_top_q_hist "with BPF action program (BPF mode)" \ + "timerlat TOOL -T 2 --bpf-action $testdir/bpf/bpf_action_map.o --on-threshold shell,command='$BPFTOOL map dump name rtla_test_map'" \ 2 '"value": 42' else # Test BPF action program failure in non-BPF mode - check "hist with BPF action program (non-BPF mode)" \ - "timerlat hist -T 2 --bpf-action tests/bpf/bpf_action_map.o" \ + check_top_q_hist "with BPF action program (non-BPF mode)" \ + "timerlat TOOL -T 2 --bpf-action $testdir/bpf/bpf_action_map.o" \ 1 "BPF actions are not supported in tracefs-only mode" fi done
diff --git a/tools/tracing/rtla/tests/unit/Build b/tools/tracing/rtla/tests/unit/Build index 5f1e531..d5a0f13 100644 --- a/tools/tracing/rtla/tests/unit/Build +++ b/tools/tracing/rtla/tests/unit/Build
@@ -1,2 +1,8 @@ +unit_tests-y += utils.o +unit_tests-y += actions.o unit_tests-y += unit_tests.o -unit_tests-y +=../../src/utils.o +unit_tests-y += osnoise_top_cli.o +unit_tests-y += osnoise_hist_cli.o +unit_tests-y += timerlat_top_cli.o +unit_tests-y += timerlat_hist_cli.o +unit_tests-y += cli_opt_callback.o
diff --git a/tools/tracing/rtla/tests/unit/Makefile.unit b/tools/tracing/rtla/tests/unit/Makefile.unit index 2088c9c..839abda 100644 --- a/tools/tracing/rtla/tests/unit/Makefile.unit +++ b/tools/tracing/rtla/tests/unit/Makefile.unit
@@ -3,10 +3,10 @@ UNIT_TESTS := $(OUTPUT)unit_tests UNIT_TESTS_IN := $(UNIT_TESTS)-in.o -$(UNIT_TESTS): $(UNIT_TESTS_IN) - $(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $^ -lcheck +$(UNIT_TESTS): $(UNIT_TESTS_IN) $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) + $(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $^ $(EXTLIBS) -lcheck -$(UNIT_TESTS_IN): +$(UNIT_TESTS_IN): fixdep $(LIBSUBCMD_INCLUDES) make $(build)=unit_tests unit-tests: FORCE
diff --git a/tools/tracing/rtla/tests/unit/actions.c b/tools/tracing/rtla/tests/unit/actions.c new file mode 100644 index 0000000..94ad5ad --- /dev/null +++ b/tools/tracing/rtla/tests/unit/actions.c
@@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <check.h> +#include <signal.h> + +#include "../../src/actions.h" + +static struct actions actions_fixture; + +static void actions_fixture_setup(void) +{ + actions_init(&actions_fixture); +} + +static void actions_fixture_teardown(void) +{ + actions_destroy(&actions_fixture); +} + +START_TEST(test_actions_init) +{ + struct actions actions; + + actions_init(&actions); + + ck_assert_int_eq(actions.len, 0); + ck_assert_int_eq(actions.size, action_default_size); + ck_assert(!actions.continue_flag); + ck_assert_ptr_eq(actions.trace_output_inst, NULL); +} +END_TEST + +START_TEST(test_actions_destroy) +{ + struct actions actions; + + actions_init(&actions); + actions_destroy(&actions); +} +END_TEST + +START_TEST(test_actions_reallocate) +{ + struct actions actions; + int i; + + actions_init(&actions); + + ck_assert_int_eq(actions.len, 0); + ck_assert_int_eq(actions.size, action_default_size); + + /* Fill size of actions array */ + for (i = 0; i < action_default_size; i++) + actions_add_continue(&actions); + + ck_assert_int_eq(actions.len, action_default_size); + ck_assert_int_eq(actions.size, action_default_size); + + /* Add one more action to trigger reallocation */ + actions_add_continue(&actions); + + ck_assert_int_eq(actions.len, action_default_size + 1); + ck_assert_int_eq(actions.size, action_default_size * 2); + + actions_destroy(&actions); +} +END_TEST + +START_TEST(test_actions_add_trace_output) +{ + actions_add_trace_output(&actions_fixture, "trace_output.txt"); + + ck_assert_int_eq(actions_fixture.len, 1); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT); + ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace_output.txt"); + ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]); +} +END_TEST + +START_TEST(test_actions_add_signal) +{ + actions_add_signal(&actions_fixture, SIGINT, 1234); + + ck_assert_int_eq(actions_fixture.len, 1); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SIGNAL); + ck_assert_int_eq(actions_fixture.list[0].signal, SIGINT); + ck_assert_int_eq(actions_fixture.list[0].pid, 1234); + ck_assert(actions_fixture.present[ACTION_SIGNAL]); +} +END_TEST + +START_TEST(test_actions_add_shell) +{ + actions_add_shell(&actions_fixture, "echo Hello"); + + ck_assert_int_eq(actions_fixture.len, 1); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SHELL); + ck_assert_str_eq(actions_fixture.list[0].command, "echo Hello"); + ck_assert(actions_fixture.present[ACTION_SHELL]); +} +END_TEST + +START_TEST(test_actions_add_continue) +{ + actions_add_continue(&actions_fixture); + + ck_assert_int_eq(actions_fixture.len, 1); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_CONTINUE); + ck_assert(actions_fixture.present[ACTION_CONTINUE]); +} +END_TEST + +START_TEST(test_actions_add_multiple_same_action) +{ + actions_add_trace_output(&actions_fixture, "trace1.txt"); + actions_add_trace_output(&actions_fixture, "trace2.txt"); + + ck_assert_int_eq(actions_fixture.len, 2); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT); + ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace1.txt"); + ck_assert_int_eq(actions_fixture.list[1].type, ACTION_TRACE_OUTPUT); + ck_assert_str_eq(actions_fixture.list[1].trace_output, "trace2.txt"); + ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]); +} +END_TEST + +START_TEST(test_actions_add_multiple_different_action) +{ + actions_add_trace_output(&actions_fixture, "trace_output.txt"); + actions_add_signal(&actions_fixture, SIGINT, 1234); + + ck_assert_int_eq(actions_fixture.len, 2); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT); + ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace_output.txt"); + ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]); + ck_assert_int_eq(actions_fixture.list[1].type, ACTION_SIGNAL); + ck_assert_int_eq(actions_fixture.list[1].signal, SIGINT); + ck_assert_int_eq(actions_fixture.list[1].pid, 1234); + ck_assert(actions_fixture.present[ACTION_SIGNAL]); +} +END_TEST + +START_TEST(test_actions_parse_trace_output) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "trace", "trace.txt"), 0); + + ck_assert_int_eq(actions_fixture.len, 1); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT); + ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace.txt"); + ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]); +} +END_TEST + +START_TEST(test_actions_parse_trace_output_arg) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "trace,file=trace2.txt", "trace1.txt"), 0); + + ck_assert_int_eq(actions_fixture.len, 1); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_TRACE_OUTPUT); + ck_assert_str_eq(actions_fixture.list[0].trace_output, "trace2.txt"); + ck_assert(actions_fixture.present[ACTION_TRACE_OUTPUT]); +} +END_TEST + +START_TEST(test_actions_parse_trace_output_arg_bad) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "trace,foo=bar", "trace_output.txt"), -1); + + ck_assert_int_eq(actions_fixture.len, 0); + ck_assert(!actions_fixture.present[ACTION_TRACE_OUTPUT]); +} +END_TEST + +START_TEST(test_actions_parse_signal) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "signal,num=1,pid=1234", NULL), 0); + + ck_assert_int_eq(actions_fixture.len, 1); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SIGNAL); + ck_assert_int_eq(actions_fixture.list[0].signal, 1); + ck_assert_int_eq(actions_fixture.list[0].pid, 1234); + ck_assert(actions_fixture.present[ACTION_SIGNAL]); +} +END_TEST + +START_TEST(test_actions_parse_signal_swapped) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "signal,pid=1234,num=1", NULL), 0); + + ck_assert_int_eq(actions_fixture.len, 1); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SIGNAL); + ck_assert_int_eq(actions_fixture.list[0].signal, 1); + ck_assert_int_eq(actions_fixture.list[0].pid, 1234); + ck_assert(actions_fixture.present[ACTION_SIGNAL]); +} +END_TEST + +START_TEST(test_actions_parse_signal_parent) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "signal,pid=parent,num=1", NULL), 0); + + ck_assert_int_eq(actions_fixture.len, 1); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SIGNAL); + ck_assert_int_eq(actions_fixture.list[0].signal, 1); + ck_assert_int_eq(actions_fixture.list[0].pid, -1); + ck_assert(actions_fixture.present[ACTION_SIGNAL]); +} +END_TEST + +START_TEST(test_actions_parse_signal_no_arg) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "signal", NULL), -1); + + ck_assert_int_eq(actions_fixture.len, 0); + ck_assert(!actions_fixture.present[ACTION_SIGNAL]); +} +END_TEST + +START_TEST(test_actions_parse_signal_no_pid) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "signal,num=1", NULL), -1); + + ck_assert_int_eq(actions_fixture.len, 0); + ck_assert(!actions_fixture.present[ACTION_SIGNAL]); +} +END_TEST + +START_TEST(test_actions_parse_signal_no_num) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "signal,pid=1234", NULL), -1); + + ck_assert_int_eq(actions_fixture.len, 0); + ck_assert(!actions_fixture.present[ACTION_SIGNAL]); +} +END_TEST + +START_TEST(test_actions_parse_signal_arg_bad) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "signal,foo=bar", NULL), -1); + + ck_assert_int_eq(actions_fixture.len, 0); + ck_assert(!actions_fixture.present[ACTION_SIGNAL]); +} +END_TEST + +START_TEST(test_actions_parse_shell) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "shell,command=echo Hello", NULL), 0); + + ck_assert_int_eq(actions_fixture.len, 1); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_SHELL); + ck_assert_str_eq(actions_fixture.list[0].command, "echo Hello"); + ck_assert(actions_fixture.present[ACTION_SHELL]); +} +END_TEST + +START_TEST(test_actions_parse_shell_no_arg) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "shell", NULL), -1); + + ck_assert_int_eq(actions_fixture.len, 0); + ck_assert(!actions_fixture.present[ACTION_SHELL]); +} +END_TEST + +START_TEST(test_actions_parse_shell_arg_bad) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "shell,foo=bar", NULL), -1); + ck_assert_int_eq(actions_fixture.len, 0); + ck_assert(!actions_fixture.present[ACTION_SHELL]); +} +END_TEST + +START_TEST(test_actions_parse_continue) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "continue", NULL), 0); + + ck_assert_int_eq(actions_fixture.len, 1); + ck_assert_int_eq(actions_fixture.list[0].type, ACTION_CONTINUE); + ck_assert(actions_fixture.present[ACTION_CONTINUE]); +} +END_TEST + +START_TEST(test_actions_parse_continue_arg_bad) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "continue,foo=bar", NULL), -1); + + ck_assert_int_eq(actions_fixture.len, 0); + ck_assert(!actions_fixture.present[ACTION_CONTINUE]); +} +END_TEST + +START_TEST(test_actions_parse_invalid) +{ + ck_assert_int_eq(actions_parse(&actions_fixture, "foobar", NULL), -1); + + ck_assert_int_eq(actions_fixture.len, 0); +} +END_TEST + +START_TEST(test_actions_perform_continue) +{ + actions_add_continue(&actions_fixture); + ck_assert_int_eq(actions_perform(&actions_fixture), 0); + + ck_assert(actions_fixture.continue_flag); +} +END_TEST + +START_TEST(test_actions_perform_continue_after_successful_shell_command) +{ + actions_add_shell(&actions_fixture, "exit 0"); + actions_add_continue(&actions_fixture); + ck_assert_int_eq(actions_perform(&actions_fixture), 0 << 8); + + ck_assert(actions_fixture.continue_flag); +} +END_TEST + +START_TEST(test_actions_perform_continue_after_failed_shell_command) +{ + actions_add_shell(&actions_fixture, "exit 1"); + actions_add_continue(&actions_fixture); + ck_assert_int_eq(actions_perform(&actions_fixture), 1 << 8); + + ck_assert(!actions_fixture.continue_flag); +} +END_TEST + +START_TEST(test_actions_perform_continue_unset_flag) +{ + actions_fixture.continue_flag = true; + + actions_add_shell(&actions_fixture, "exit 1"); + actions_add_continue(&actions_fixture); + ck_assert_int_eq(actions_perform(&actions_fixture), 1 << 8); + + ck_assert(!actions_fixture.continue_flag); +} +END_TEST + +Suite *actions_suite(void) +{ + Suite *s = suite_create("actions"); + TCase *tc; + + tc = tcase_create("alloc"); + tcase_add_test(tc, test_actions_init); + tcase_add_test(tc, test_actions_destroy); + tcase_add_test(tc, test_actions_reallocate); + suite_add_tcase(s, tc); + + tc = tcase_create("add"); + tcase_add_checked_fixture(tc, actions_fixture_setup, actions_fixture_teardown); + tcase_add_test(tc, test_actions_add_trace_output); + tcase_add_test(tc, test_actions_add_signal); + tcase_add_test(tc, test_actions_add_shell); + tcase_add_test(tc, test_actions_add_continue); + tcase_add_test(tc, test_actions_add_multiple_same_action); + tcase_add_test(tc, test_actions_add_multiple_different_action); + suite_add_tcase(s, tc); + + tc = tcase_create("parse"); + tcase_add_checked_fixture(tc, actions_fixture_setup, actions_fixture_teardown); + tcase_add_test(tc, test_actions_parse_trace_output); + tcase_add_test(tc, test_actions_parse_trace_output_arg); + tcase_add_test(tc, test_actions_parse_trace_output_arg_bad); + tcase_add_test(tc, test_actions_parse_signal); + tcase_add_test(tc, test_actions_parse_signal_swapped); + tcase_add_test(tc, test_actions_parse_signal_parent); + tcase_add_test(tc, test_actions_parse_signal_no_arg); + tcase_add_test(tc, test_actions_parse_signal_no_pid); + tcase_add_test(tc, test_actions_parse_signal_no_num); + tcase_add_test(tc, test_actions_parse_signal_arg_bad); + tcase_add_test(tc, test_actions_parse_shell); + tcase_add_test(tc, test_actions_parse_shell_no_arg); + tcase_add_test(tc, test_actions_parse_shell_arg_bad); + tcase_add_test(tc, test_actions_parse_continue); + tcase_add_test(tc, test_actions_parse_continue_arg_bad); + tcase_add_test(tc, test_actions_parse_invalid); + suite_add_tcase(s, tc); + + tc = tcase_create("perform"); + tcase_add_checked_fixture(tc, actions_fixture_setup, actions_fixture_teardown); + tcase_add_test(tc, test_actions_perform_continue); + tcase_add_test(tc, test_actions_perform_continue_after_successful_shell_command); + tcase_add_test(tc, test_actions_perform_continue_after_failed_shell_command); + tcase_add_test(tc, test_actions_perform_continue_unset_flag); + suite_add_tcase(s, tc); + + return s; +}
diff --git a/tools/tracing/rtla/tests/unit/cli_opt_callback.c b/tools/tracing/rtla/tests/unit/cli_opt_callback.c new file mode 100644 index 0000000..4a406af --- /dev/null +++ b/tools/tracing/rtla/tests/unit/cli_opt_callback.c
@@ -0,0 +1,716 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <stdio.h> +#include <check.h> + +#define RTLA_ALLOW_CLI_P_H +#include "../../src/cli_p.h" +#include "cli_params_assert.h" + +#define TEST_CALLBACK(value, cb) OPT_CALLBACK('t', "test", value, "test value", "test help", cb) + +START_TEST(test_opt_llong_callback_simple) +{ + long long test_value = 0; + const struct option opt = TEST_CALLBACK(&test_value, opt_llong_callback); + + ck_assert_int_eq(opt_llong_callback(&opt, "1234567890", 0), 0); + ck_assert_int_eq(test_value, 1234567890); +} +END_TEST + +START_TEST(test_opt_llong_callback_max) +{ + long long test_value = 0; + const struct option opt = TEST_CALLBACK(&test_value, opt_llong_callback); + + ck_assert_int_eq(opt_llong_callback(&opt, "9223372036854775807", 0), 0); + ck_assert_int_eq(test_value, 9223372036854775807LL); +} +END_TEST + +START_TEST(test_opt_llong_callback_min) +{ + long long test_value = 0; + const struct option opt = TEST_CALLBACK(&test_value, opt_llong_callback); + + ck_assert_int_eq(opt_llong_callback(&opt, "-9223372036854775808", 0), 0); + ck_assert_int_eq(test_value, ~9223372036854775807LL); +} +END_TEST + +START_TEST(test_opt_int_callback_simple) +{ + int test_value = 0; + const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback); + + ck_assert_int_eq(opt_int_callback(&opt, "1234567890", 0), 0); + ck_assert_int_eq(test_value, 1234567890); +} +END_TEST + +START_TEST(test_opt_int_callback_max) +{ + int test_value = 0; + const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback); + + ck_assert_int_eq(opt_int_callback(&opt, "2147483647", 0), 0); + ck_assert_int_eq(test_value, 2147483647); +} +END_TEST + +START_TEST(test_opt_int_callback_min) +{ + int test_value = 0; + const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback); + + ck_assert_int_eq(opt_int_callback(&opt, "-2147483648", 0), 0); + ck_assert_int_eq(test_value, -2147483648); +} +END_TEST + +START_TEST(test_opt_int_callback_non_numeric) +{ + int test_value = 0; + const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback); + + ck_assert_int_eq(opt_int_callback(&opt, "abc", 0), -1); + ck_assert_int_eq(test_value, 0); +} +END_TEST + +START_TEST(test_opt_int_callback_non_numeric_suffix) +{ + int test_value = 0; + const struct option opt = TEST_CALLBACK(&test_value, opt_int_callback); + + ck_assert_int_eq(opt_int_callback(&opt, "1234567890abc", 0), -1); + ck_assert_int_eq(test_value, 0); +} +END_TEST + +START_TEST(test_opt_cpus_cb) +{ + struct common_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_cpus_cb); + + nr_cpus = 4; + ck_assert_int_eq(opt_cpus_cb(&opt, "0-3", 0), 0); + ck_assert_str_eq(params.cpus, "0-3"); +} +END_TEST + +START_TEST(test_opt_cpus_cb_invalid) +{ + struct common_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_cpus_cb); + + nr_cpus = 4; + assert(freopen("/dev/null", "w", stderr)); + opt_cpus_cb(&opt, "0-3,5", 0); +} +END_TEST + +START_TEST(test_opt_cgroup_cb) +{ + struct common_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_cgroup_cb); + + ck_assert_int_eq(opt_cgroup_cb(&opt, "cgroup", 0), 0); + ck_assert_int_eq(params.cgroup, 1); + ck_assert_str_eq(params.cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_opt_cgroup_cb_equals) +{ + struct common_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_cgroup_cb); + + ck_assert_int_eq(opt_cgroup_cb(&opt, "=cgroup", 0), 0); + ck_assert_int_eq(params.cgroup, 1); + ck_assert_str_eq(params.cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_opt_duration_cb) +{ + struct common_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_duration_cb); + + ck_assert_int_eq(opt_duration_cb(&opt, "1m", 0), 0); + ck_assert_int_eq(params.duration, 60); +} +END_TEST + +START_TEST(test_opt_duration_cb_invalid) +{ + struct common_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_duration_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_duration_cb(&opt, "abc", 0); +} +END_TEST + +START_TEST(test_opt_event_cb) +{ + struct trace_events *events = NULL; + const struct option opt = TEST_CALLBACK(&events, opt_event_cb); + + ck_assert_int_eq(opt_event_cb(&opt, "sched:sched_switch", 0), 0); + ck_assert_str_eq(events->system, "sched"); + ck_assert_str_eq(events->event, "sched_switch"); + ck_assert_ptr_eq(events->next, NULL); +} +END_TEST + +START_TEST(test_opt_event_cb_multiple) +{ + struct trace_events *events = NULL; + const struct option opt = TEST_CALLBACK(&events, opt_event_cb); + + ck_assert_int_eq(opt_event_cb(&opt, "sched:sched_switch", 0), 0); + ck_assert_int_eq(opt_event_cb(&opt, "sched:sched_wakeup", 0), 0); + ck_assert_str_eq(events->system, "sched"); + ck_assert_str_eq(events->event, "sched_wakeup"); + ck_assert_str_eq(events->next->system, "sched"); + ck_assert_str_eq(events->next->event, "sched_switch"); + ck_assert_ptr_eq(events->next->next, NULL); +} +END_TEST + +START_TEST(test_opt_housekeeping_cb) +{ + struct common_params __params = {0}; + struct common_params *params = &__params; + const struct option opt = TEST_CALLBACK(params, opt_housekeeping_cb); + + nr_cpus = 4; + ck_assert_int_eq(opt_housekeeping_cb(&opt, "0-3", 0), 0); + ck_assert_int_eq(params->hk_cpus, 1); + CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 2, 3); +} +END_TEST + +START_TEST(test_opt_housekeeping_cb_invalid) +{ + struct common_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_housekeeping_cb); + + nr_cpus = 4; + assert(freopen("/dev/null", "w", stderr)); + opt_housekeeping_cb(&opt, "0-3,5", 0); +} +END_TEST + +START_TEST(test_opt_priority_cb) +{ + struct common_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_priority_cb); + + ck_assert_int_eq(opt_priority_cb(&opt, "f:95", 0), 0); + ck_assert_int_eq(params.sched_param.sched_policy, SCHED_FIFO); + ck_assert_int_eq(params.sched_param.sched_priority, 95); +} +END_TEST + +START_TEST(test_opt_priority_cb_invalid) +{ + struct common_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_priority_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_priority_cb(&opt, "abc", 0); +} +END_TEST + +START_TEST(test_opt_trigger_cb) +{ + struct trace_events *events = trace_event_alloc("sched:sched_switch"); + const struct option opt = TEST_CALLBACK(&events, opt_trigger_cb); + + ck_assert_int_eq(opt_trigger_cb(&opt, "stacktrace", 0), 0); + ck_assert_str_eq(events->trigger, "stacktrace"); +} +END_TEST + +START_TEST(test_opt_trigger_cb_no_event) +{ + struct trace_events *events = NULL; + const struct option opt = TEST_CALLBACK(&events, opt_trigger_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_trigger_cb(&opt, "stacktrace", 0); +} +END_TEST + +START_TEST(test_opt_filter_cb) +{ + struct trace_events *events = trace_event_alloc("sched:sched_switch"); + const struct option opt = TEST_CALLBACK(&events, opt_filter_cb); + + ck_assert_int_eq(opt_filter_cb(&opt, "comm ~ \"rtla\"", 0), 0); + ck_assert_str_eq(events->filter, "comm ~ \"rtla\""); +} +END_TEST + +START_TEST(test_opt_filter_cb_no_event) +{ + struct trace_events *events = NULL; + const struct option opt = TEST_CALLBACK(&events, opt_filter_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_filter_cb(&opt, "comm ~ \"rtla\"", 0); +} +END_TEST + +START_TEST(test_opt_osnoise_auto_cb) +{ + struct osnoise_params params = {0}; + struct osnoise_cb_data cb_data = {¶ms}; + const struct option opt = TEST_CALLBACK(&cb_data, opt_osnoise_auto_cb); + + ck_assert_int_eq(opt_osnoise_auto_cb(&opt, "10", 0), 0); + ck_assert_int_eq(params.common.stop_us, 10); + ck_assert_int_eq(params.threshold, 1); + ck_assert_str_eq(cb_data.trace_output, "osnoise_trace.txt"); +} +END_TEST + +START_TEST(test_opt_osnoise_period_cb) +{ + unsigned long long period = 0; + const struct option opt = TEST_CALLBACK(&period, opt_osnoise_period_cb); + + ck_assert_int_eq(opt_osnoise_period_cb(&opt, "1000000", 0), 0); + ck_assert_int_eq(period, 1000000); +} +END_TEST + +START_TEST(test_opt_osnoise_period_cb_invalid) +{ + unsigned long long period = 0; + const struct option opt = TEST_CALLBACK(&period, opt_osnoise_period_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_osnoise_period_cb(&opt, "10000001", 0); +} +END_TEST + +START_TEST(test_opt_osnoise_runtime_cb) +{ + unsigned long long runtime = 0; + const struct option opt = TEST_CALLBACK(&runtime, opt_osnoise_runtime_cb); + + ck_assert_int_eq(opt_osnoise_runtime_cb(&opt, "900000", 0), 0); + ck_assert_int_eq(runtime, 900000); +} +END_TEST + +START_TEST(test_opt_osnoise_runtime_cb_invalid) +{ + unsigned long long runtime = 0; + const struct option opt = TEST_CALLBACK(&runtime, opt_osnoise_runtime_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_osnoise_runtime_cb(&opt, "99", 0); +} +END_TEST + +START_TEST(test_opt_osnoise_trace_output_cb) +{ + const char *trace_output = NULL; + const struct option opt = TEST_CALLBACK(&trace_output, opt_osnoise_trace_output_cb); + + ck_assert_int_eq(opt_osnoise_trace_output_cb(&opt, "trace.txt", 0), 0); + ck_assert_str_eq(trace_output, "trace.txt"); +} +END_TEST + +START_TEST(test_opt_osnoise_trace_output_cb_noarg) +{ + const char *trace_output = NULL; + const struct option opt = TEST_CALLBACK(&trace_output, opt_osnoise_trace_output_cb); + + ck_assert_int_eq(opt_osnoise_trace_output_cb(&opt, NULL, 0), 0); + ck_assert_str_eq(trace_output, "osnoise_trace.txt"); +} +END_TEST + +START_TEST(test_opt_osnoise_on_threshold_cb) +{ + struct actions actions = {0}; + const struct option opt = TEST_CALLBACK(&actions, opt_osnoise_on_threshold_cb); + + ck_assert_int_eq(opt_osnoise_on_threshold_cb(&opt, "trace", 0), 0); + ck_assert_int_eq(actions.len, 1); + ck_assert_int_eq(actions.list[0].type, ACTION_TRACE_OUTPUT); + ck_assert_str_eq(actions.list[0].trace_output, "osnoise_trace.txt"); +} +END_TEST + +START_TEST(test_opt_osnoise_on_threshold_cb_invalid) +{ + struct actions actions = {0}; + const struct option opt = TEST_CALLBACK(&actions, opt_osnoise_on_threshold_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_osnoise_on_threshold_cb(&opt, "abc", 0); +} +END_TEST + +START_TEST(test_opt_osnoise_on_end_cb) +{ + struct actions actions = {0}; + const struct option opt = TEST_CALLBACK(&actions, opt_osnoise_on_end_cb); + + ck_assert_int_eq(opt_osnoise_on_end_cb(&opt, "trace", 0), 0); + ck_assert_int_eq(actions.len, 1); + ck_assert_int_eq(actions.list[0].type, ACTION_TRACE_OUTPUT); + ck_assert_str_eq(actions.list[0].trace_output, "osnoise_trace.txt"); +} +END_TEST + +START_TEST(test_opt_osnoise_on_end_cb_invalid) +{ + struct actions actions = {0}; + const struct option opt = TEST_CALLBACK(&actions, opt_osnoise_on_end_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_osnoise_on_end_cb(&opt, "abc", 0); +} +END_TEST + +START_TEST(test_opt_timerlat_period_cb) +{ + long long period = 0; + const struct option opt = TEST_CALLBACK(&period, opt_timerlat_period_cb); + + ck_assert_int_eq(opt_timerlat_period_cb(&opt, "1000", 0), 0); + ck_assert_int_eq(period, 1000); +} +END_TEST + +START_TEST(test_opt_timerlat_period_cb_invalid) +{ + long long period = 0; + const struct option opt = TEST_CALLBACK(&period, opt_timerlat_period_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_timerlat_period_cb(&opt, "1000001", 0); +} +END_TEST + +START_TEST(test_opt_timerlat_auto_cb) +{ + struct timerlat_params params = {0}; + struct timerlat_cb_data cb_data = {¶ms}; + const struct option opt = TEST_CALLBACK(&cb_data, opt_timerlat_auto_cb); + + ck_assert_int_eq(opt_timerlat_auto_cb(&opt, "10", 0), 0); + ck_assert_int_eq(params.common.stop_us, 10); + ck_assert_int_eq(params.common.stop_total_us, 10); + ck_assert_int_eq(params.print_stack, 10); + ck_assert_str_eq(cb_data.trace_output, "timerlat_trace.txt"); +} +END_TEST + +START_TEST(test_opt_dma_latency_cb) +{ + int dma_latency = 0; + const struct option opt = TEST_CALLBACK(&dma_latency, opt_dma_latency_cb); + + ck_assert_int_eq(opt_dma_latency_cb(&opt, "1000", 0), 0); + ck_assert_int_eq(dma_latency, 1000); +} +END_TEST + +START_TEST(test_opt_dma_latency_cb_min) +{ + int dma_latency = 0; + const struct option opt = TEST_CALLBACK(&dma_latency, opt_dma_latency_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_dma_latency_cb(&opt, "-1", 0); +} +END_TEST + +START_TEST(test_opt_dma_latency_cb_max) +{ + int dma_latency = 0; + const struct option opt = TEST_CALLBACK(&dma_latency, opt_dma_latency_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_dma_latency_cb(&opt, "10001", 0); +} +END_TEST + +START_TEST(test_opt_aa_only_cb) +{ + struct timerlat_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_aa_only_cb); + + ck_assert_int_eq(opt_aa_only_cb(&opt, "10", 0), 0); + ck_assert_int_eq(params.common.stop_us, 10); + ck_assert_int_eq(params.common.stop_total_us, 10); + ck_assert_int_eq(params.print_stack, 10); + ck_assert_int_eq(params.common.aa_only, 1); +} +END_TEST + +START_TEST(test_opt_timerlat_trace_output_cb) +{ + const char *trace_output = NULL; + const struct option opt = TEST_CALLBACK(&trace_output, opt_timerlat_trace_output_cb); + + ck_assert_int_eq(opt_timerlat_trace_output_cb(&opt, "trace.txt", 0), 0); + ck_assert_str_eq(trace_output, "trace.txt"); +} +END_TEST + +START_TEST(test_opt_timerlat_trace_output_cb_noarg) +{ + const char *trace_output = NULL; + const struct option opt = TEST_CALLBACK(&trace_output, opt_timerlat_trace_output_cb); + + ck_assert_int_eq(opt_timerlat_trace_output_cb(&opt, NULL, 0), 0); + ck_assert_str_eq(trace_output, "timerlat_trace.txt"); +} +END_TEST + +START_TEST(test_opt_timerlat_on_threshold_cb) +{ + struct actions actions = {0}; + const struct option opt = TEST_CALLBACK(&actions, opt_timerlat_on_threshold_cb); + + ck_assert_int_eq(opt_timerlat_on_threshold_cb(&opt, "trace", 0), 0); + ck_assert_int_eq(actions.len, 1); + ck_assert_int_eq(actions.list[0].type, ACTION_TRACE_OUTPUT); + ck_assert_str_eq(actions.list[0].trace_output, "timerlat_trace.txt"); +} +END_TEST + +START_TEST(test_opt_timerlat_on_threshold_cb_invalid) +{ + struct actions actions = {0}; + const struct option opt = TEST_CALLBACK(&actions, opt_timerlat_on_threshold_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_timerlat_on_threshold_cb(&opt, "abc", 0); +} +END_TEST + +START_TEST(test_opt_timerlat_on_end_cb) +{ + struct actions actions = {0}; + const struct option opt = TEST_CALLBACK(&actions, opt_timerlat_on_end_cb); + + ck_assert_int_eq(opt_timerlat_on_end_cb(&opt, "trace", 0), 0); + ck_assert_int_eq(actions.len, 1); + ck_assert_int_eq(actions.list[0].type, ACTION_TRACE_OUTPUT); + ck_assert_str_eq(actions.list[0].trace_output, "timerlat_trace.txt"); +} +END_TEST + +START_TEST(test_opt_timerlat_on_end_cb_invalid) +{ + struct actions actions = {0}; + const struct option opt = TEST_CALLBACK(&actions, opt_timerlat_on_end_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_timerlat_on_end_cb(&opt, "abc", 0); +} +END_TEST + +START_TEST(test_opt_user_threads_cb) +{ + struct timerlat_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_user_threads_cb); + + ck_assert_int_eq(opt_user_threads_cb(&opt, NULL, 0), 0); + ck_assert_int_eq(params.common.user_workload, 1); + ck_assert_int_eq(params.common.user_data, 1); +} +END_TEST + +START_TEST(test_opt_nano_cb) +{ + struct timerlat_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_nano_cb); + + ck_assert_int_eq(opt_nano_cb(&opt, NULL, 0), 0); + ck_assert_int_eq(params.common.output_divisor, 1); +} +END_TEST + +START_TEST(test_opt_timerlat_align_cb) +{ + struct timerlat_params params = {0}; + const struct option opt = TEST_CALLBACK(¶ms, opt_timerlat_align_cb); + + ck_assert_int_eq(opt_timerlat_align_cb(&opt, "500", 0), 0); + ck_assert(params.timerlat_align); + ck_assert_int_eq(params.timerlat_align_us, 500); +} +END_TEST + +START_TEST(test_opt_stack_format_cb) +{ + int stack_format = 0; + const struct option opt = TEST_CALLBACK(&stack_format, opt_stack_format_cb); + + ck_assert_int_eq(opt_stack_format_cb(&opt, "full", 0), 0); + ck_assert_int_eq(stack_format, STACK_FORMAT_FULL); +} +END_TEST + +START_TEST(test_opt_stack_format_cb_invalid) +{ + int stack_format = 0; + const struct option opt = TEST_CALLBACK(&stack_format, opt_stack_format_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_stack_format_cb(&opt, "abc", 0); +} +END_TEST + +START_TEST(test_opt_bucket_size_cb) +{ + int bucket_size = 0; + const struct option opt = TEST_CALLBACK(&bucket_size, opt_bucket_size_cb); + + ck_assert_int_eq(opt_bucket_size_cb(&opt, "100", 0), 0); + ck_assert_int_eq(bucket_size, 100); +} +END_TEST + +START_TEST(test_opt_bucket_size_min) +{ + int bucket_size = 0; + const struct option opt = TEST_CALLBACK(&bucket_size, opt_bucket_size_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_bucket_size_cb(&opt, "0", 0); +} +END_TEST + +START_TEST(test_opt_bucket_size_max) +{ + int bucket_size = 0; + const struct option opt = TEST_CALLBACK(&bucket_size, opt_bucket_size_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_bucket_size_cb(&opt, "1000001", 0); +} +END_TEST + +START_TEST(test_opt_entries_cb) +{ + int entries = 0; + const struct option opt = TEST_CALLBACK(&entries, opt_entries_cb); + + ck_assert_int_eq(opt_entries_cb(&opt, "100", 0), 0); + ck_assert_int_eq(entries, 100); +} +END_TEST + +START_TEST(test_opt_entries_min) +{ + int entries = 0; + const struct option opt = TEST_CALLBACK(&entries, opt_entries_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_entries_cb(&opt, "9", 0); +} +END_TEST + +START_TEST(test_opt_entries_max) +{ + int entries = 0; + const struct option opt = TEST_CALLBACK(&entries, opt_entries_cb); + + assert(freopen("/dev/null", "w", stderr)); + opt_entries_cb(&opt, "10000000", 0); +} +END_TEST + +Suite *cli_opt_callback_suite(void) +{ + Suite *s = suite_create("cli_opt_callback"); + TCase *tc; + + tc = tcase_create("common"); + tcase_add_test(tc, test_opt_llong_callback_simple); + tcase_add_test(tc, test_opt_llong_callback_max); + tcase_add_test(tc, test_opt_llong_callback_min); + tcase_add_test(tc, test_opt_int_callback_simple); + tcase_add_test(tc, test_opt_int_callback_max); + tcase_add_test(tc, test_opt_int_callback_min); + tcase_add_test(tc, test_opt_int_callback_non_numeric); + tcase_add_test(tc, test_opt_int_callback_non_numeric_suffix); + tcase_add_test(tc, test_opt_cpus_cb); + tcase_add_exit_test(tc, test_opt_cpus_cb_invalid, EXIT_FAILURE); + tcase_add_test(tc, test_opt_cgroup_cb); + tcase_add_test(tc, test_opt_cgroup_cb_equals); + tcase_add_test(tc, test_opt_duration_cb); + tcase_add_exit_test(tc, test_opt_duration_cb_invalid, EXIT_FAILURE); + tcase_add_test(tc, test_opt_event_cb); + tcase_add_test(tc, test_opt_event_cb_multiple); + tcase_add_test(tc, test_opt_housekeeping_cb); + tcase_add_exit_test(tc, test_opt_housekeeping_cb_invalid, EXIT_FAILURE); + tcase_add_test(tc, test_opt_priority_cb); + tcase_add_exit_test(tc, test_opt_priority_cb_invalid, EXIT_FAILURE); + tcase_add_test(tc, test_opt_trigger_cb); + tcase_add_exit_test(tc, test_opt_trigger_cb_no_event, EXIT_FAILURE); + tcase_add_test(tc, test_opt_filter_cb); + tcase_add_exit_test(tc, test_opt_filter_cb_no_event, EXIT_FAILURE); + suite_add_tcase(s, tc); + + tc = tcase_create("osnoise"); + tcase_add_test(tc, test_opt_osnoise_auto_cb); + tcase_add_test(tc, test_opt_osnoise_period_cb); + tcase_add_exit_test(tc, test_opt_osnoise_period_cb_invalid, EXIT_FAILURE); + tcase_add_test(tc, test_opt_osnoise_runtime_cb); + tcase_add_exit_test(tc, test_opt_osnoise_runtime_cb_invalid, EXIT_FAILURE); + tcase_add_test(tc, test_opt_osnoise_trace_output_cb); + tcase_add_test(tc, test_opt_osnoise_trace_output_cb_noarg); + tcase_add_test(tc, test_opt_osnoise_on_threshold_cb); + tcase_add_exit_test(tc, test_opt_osnoise_on_threshold_cb_invalid, EXIT_FAILURE); + tcase_add_test(tc, test_opt_osnoise_on_end_cb); + tcase_add_exit_test(tc, test_opt_osnoise_on_end_cb_invalid, EXIT_FAILURE); + suite_add_tcase(s, tc); + + tc = tcase_create("timerlat"); + tcase_add_test(tc, test_opt_timerlat_period_cb); + tcase_add_exit_test(tc, test_opt_timerlat_period_cb_invalid, EXIT_FAILURE); + tcase_add_test(tc, test_opt_timerlat_auto_cb); + tcase_add_test(tc, test_opt_dma_latency_cb); + tcase_add_exit_test(tc, test_opt_dma_latency_cb_min, EXIT_FAILURE); + tcase_add_exit_test(tc, test_opt_dma_latency_cb_max, EXIT_FAILURE); + tcase_add_test(tc, test_opt_aa_only_cb); + tcase_add_test(tc, test_opt_timerlat_trace_output_cb); + tcase_add_test(tc, test_opt_timerlat_trace_output_cb_noarg); + tcase_add_test(tc, test_opt_timerlat_on_threshold_cb); + tcase_add_exit_test(tc, test_opt_timerlat_on_threshold_cb_invalid, EXIT_FAILURE); + tcase_add_test(tc, test_opt_timerlat_on_end_cb); + tcase_add_exit_test(tc, test_opt_timerlat_on_end_cb_invalid, EXIT_FAILURE); + tcase_add_test(tc, test_opt_user_threads_cb); + tcase_add_test(tc, test_opt_nano_cb); + tcase_add_test(tc, test_opt_stack_format_cb); + tcase_add_exit_test(tc, test_opt_stack_format_cb_invalid, EXIT_FAILURE); + tcase_add_test(tc, test_opt_timerlat_align_cb); + suite_add_tcase(s, tc); + + tc = tcase_create("histogram"); + tcase_add_test(tc, test_opt_bucket_size_cb); + tcase_add_exit_test(tc, test_opt_bucket_size_min, EXIT_FAILURE); + tcase_add_exit_test(tc, test_opt_bucket_size_max, EXIT_FAILURE); + tcase_add_test(tc, test_opt_entries_cb); + tcase_add_exit_test(tc, test_opt_entries_min, EXIT_FAILURE); + tcase_add_exit_test(tc, test_opt_entries_max, EXIT_FAILURE); + suite_add_tcase(s, tc); + + return s; +}
diff --git a/tools/tracing/rtla/tests/unit/cli_params_assert.h b/tools/tracing/rtla/tests/unit/cli_params_assert.h new file mode 100644 index 0000000..4bc7d58 --- /dev/null +++ b/tools/tracing/rtla/tests/unit/cli_params_assert.h
@@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#pragma once + +#include "../../src/timerlat.h" + +/* Tracing Options */ + +#define CLI_ASSERT_SINGLE_EVENT(_system, _event) do {\ + ck_assert_ptr_nonnull(params->events);\ + ck_assert_str_eq(params->events->system, _system);\ + ck_assert_str_eq(params->events->event, _event);\ + ck_assert_ptr_null(params->events->next);\ +} while (0) + +#define CLI_ASSERT_SINGLE_FILTER(_filter) do {\ + ck_assert_ptr_nonnull(params->events);\ + ck_assert_str_eq(params->events->filter, _filter);\ + ck_assert_ptr_null(params->events->next);\ +} while (0) + +#define CLI_ASSERT_SINGLE_TRIGGER(_trigger) do {\ + ck_assert_ptr_nonnull(params->events);\ + ck_assert_str_eq(params->events->trigger, _trigger);\ + ck_assert_ptr_null(params->events->next);\ +} while (0) + +/* CPU Configuration */ + +#define CLI_ASSERT_CPUSET(_field, ...) do {\ + int n;\ + int cpus[] = { __VA_ARGS__ };\ + for (n = 0; n < sizeof(cpus) / sizeof(int); n++)\ + ck_assert(CPU_ISSET(cpus[n], ¶ms->_field));\ + ck_assert_int_eq(CPU_COUNT(¶ms->_field), n);\ +} while (0) + +/* Auto Analysis and Actions */ + +#define CLI_OSNOISE_ASSERT_AUTO(_stop) do {\ + ck_assert_int_eq(params->stop_us, _stop);\ + ck_assert_int_eq(osn_params->threshold, 1);\ + ck_assert_int_eq(params->threshold_actions.len, 1);\ + ck_assert_int_eq(params->threshold_actions.list[0].type, ACTION_TRACE_OUTPUT);\ + ck_assert_str_eq(params->threshold_actions.list[0].trace_output, "osnoise_trace.txt");\ +} while (0) + +#define CLI_TIMERLAT_ASSERT_AUTO(_threshold) do {\ + ck_assert_int_eq(params->stop_us, _threshold);\ + ck_assert_int_eq(params->stop_total_us, _threshold);\ + ck_assert_int_eq(tlat_params->print_stack, _threshold);\ + ck_assert_int_eq(params->threshold_actions.len, 1);\ + ck_assert_int_eq(params->threshold_actions.list[0].type, ACTION_TRACE_OUTPUT);\ + ck_assert_str_eq(params->threshold_actions.list[0].trace_output, "timerlat_trace.txt");\ +} while (0) + +#define CLI_TIMERLAT_ASSERT_AA_ONLY(_threshold) do {\ + ck_assert_int_eq(params->stop_us, _threshold);\ + ck_assert_int_eq(params->stop_total_us, _threshold);\ + ck_assert_int_eq(tlat_params->print_stack, _threshold);\ + ck_assert_int_eq(params->threshold_actions.len, 0);\ + ck_assert(params->aa_only);\ +} while (0) + +#define CLI_ASSERT_SINGLE_ACTION(_actions, _type, _arg, _valtype, _value) do {\ + ck_assert_int_eq(params->_actions.len, 1);\ + ck_assert_int_eq(params->_actions.list[0].type, _type);\ + ck_assert_##_valtype##_eq(params->_actions.list[0]._arg, _value);\ +} while (0)
diff --git a/tools/tracing/rtla/tests/unit/osnoise_hist_cli.c b/tools/tracing/rtla/tests/unit/osnoise_hist_cli.c new file mode 100644 index 0000000..3661529 --- /dev/null +++ b/tools/tracing/rtla/tests/unit/osnoise_hist_cli.c
@@ -0,0 +1,557 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <check.h> +#include <stdio.h> +#include <stdlib.h> +#include <sched.h> +#include <limits.h> +#include <unistd.h> +#include <sys/sysinfo.h> + +#include "cli_params_assert.h" +#include "../../src/cli.h" + +#define PARSE_ARGS(...) char *argv[] = { __VA_ARGS__, NULL };\ + int argc = sizeof(argv) / sizeof(char *) - 1;\ + struct common_params *params =\ + osnoise_hist_parse_args(argc, argv);\ + struct osnoise_params *osn_params __maybe_unused =\ + to_osnoise_params(params) + +/* Tracing Options */ + +START_TEST(test_period_short) +{ + PARSE_ARGS("osnoise", "hist", "-p", "100000"); + + ck_assert_int_eq(osn_params->period, 100000); +} +END_TEST + +START_TEST(test_period_long) +{ + PARSE_ARGS("osnoise", "hist", "--period", "100000"); + + ck_assert_int_eq(osn_params->period, 100000); +} +END_TEST + +START_TEST(test_runtime_short) +{ + PARSE_ARGS("osnoise", "hist", "-r", "95000"); + + ck_assert_int_eq(osn_params->runtime, 95000); +} +END_TEST + +START_TEST(test_runtime_long) +{ + PARSE_ARGS("osnoise", "hist", "--runtime", "95000"); + + ck_assert_int_eq(osn_params->runtime, 95000); +} +END_TEST + +START_TEST(test_stop_short) +{ + PARSE_ARGS("osnoise", "hist", "-s", "20"); + + ck_assert_int_eq(params->stop_us, 20); +} +END_TEST + +START_TEST(test_stop_long) +{ + PARSE_ARGS("osnoise", "hist", "--stop", "20"); + + ck_assert_int_eq(params->stop_us, 20); +} +END_TEST + +START_TEST(test_stop_total_short) +{ + PARSE_ARGS("osnoise", "hist", "-S", "20"); + + ck_assert_int_eq(params->stop_total_us, 20); +} +END_TEST + +START_TEST(test_stop_total_long) +{ + PARSE_ARGS("osnoise", "hist", "--stop-total", "20"); + + ck_assert_int_eq(params->stop_total_us, 20); +} +END_TEST + +START_TEST(test_threshold_short) +{ + PARSE_ARGS("osnoise", "hist", "-T", "5"); + + ck_assert_int_eq(osn_params->threshold, 5); +} +END_TEST + +START_TEST(test_threshold_long) +{ + PARSE_ARGS("osnoise", "hist", "--threshold", "5"); + + ck_assert_int_eq(osn_params->threshold, 5); +} +END_TEST + +START_TEST(test_trace_short_noarg) +{ + PARSE_ARGS("osnoise", "hist", "-t"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "osnoise_trace.txt"); +} +END_TEST + +START_TEST(test_trace_short_followarg) +{ + PARSE_ARGS("osnoise", "hist", "-t", "-d", "20"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "osnoise_trace.txt"); + ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */ +} +END_TEST + +START_TEST(test_trace_short_space) +{ + PARSE_ARGS("osnoise", "hist", "-t", "tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +START_TEST(test_trace_short_equals) +{ + PARSE_ARGS("osnoise", "hist", "-t=tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +START_TEST(test_trace_long_noarg) +{ + PARSE_ARGS("osnoise", "hist", "--trace"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "osnoise_trace.txt"); +} +END_TEST + +START_TEST(test_trace_long_followarg) +{ + PARSE_ARGS("osnoise", "hist", "--trace", "-d", "20"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "osnoise_trace.txt"); + ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */ +} +END_TEST + +START_TEST(test_trace_long_space) +{ + PARSE_ARGS("osnoise", "hist", "--trace", "tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +START_TEST(test_trace_long_equals) +{ + PARSE_ARGS("osnoise", "hist", "--trace=tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +/* Event Configuration */ + +START_TEST(test_event_short) +{ + PARSE_ARGS("osnoise", "hist", "-e", "system:event"); + + CLI_ASSERT_SINGLE_EVENT("system", "event"); +} +END_TEST + +START_TEST(test_event_long) +{ + PARSE_ARGS("osnoise", "hist", "--event", "system:event"); + + CLI_ASSERT_SINGLE_EVENT("system", "event"); +} +END_TEST + +START_TEST(test_filter) +{ + PARSE_ARGS("osnoise", "hist", "-e", "system:event", "--filter", "filter"); + + CLI_ASSERT_SINGLE_FILTER("filter"); +} +END_TEST + +START_TEST(test_trigger) +{ + PARSE_ARGS("osnoise", "hist", "-e", "system:event", "--trigger", "trigger"); + + CLI_ASSERT_SINGLE_TRIGGER("trigger"); +} +END_TEST + +/* CPU Configuration */ + +START_TEST(test_cpus_short) +{ + nr_cpus = 4; + + PARSE_ARGS("osnoise", "hist", "-c", "0-1,3"); + + ck_assert_str_eq(params->cpus, "0-1,3"); + CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3); +} +END_TEST + +START_TEST(test_cpus_long) +{ + nr_cpus = 4; + + PARSE_ARGS("osnoise", "hist", "--cpus", "0-1,3"); + + ck_assert_str_eq(params->cpus, "0-1,3"); + CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3); +} +END_TEST + +START_TEST(test_housekeeping_short) +{ + nr_cpus = 4; + + PARSE_ARGS("osnoise", "hist", "-H", "0-1,3"); + + CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3); +} +END_TEST + +START_TEST(test_housekeeping_long) +{ + nr_cpus = 4; + + PARSE_ARGS("osnoise", "hist", "--house-keeping", "0-1,3"); + + CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3); +} +END_TEST + +/* Thread Configuration */ + +START_TEST(test_cgroup_short_noarg) +{ + PARSE_ARGS("osnoise", "hist", "-C"); + + ck_assert(params->cgroup); + ck_assert_ptr_null(params->cgroup_name); +} +END_TEST + +START_TEST(test_cgroup_short_space) +{ + PARSE_ARGS("osnoise", "hist", "-C", "cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_cgroup_short_equals) +{ + PARSE_ARGS("osnoise", "hist", "-C=cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_cgroup_long_noarg) +{ + PARSE_ARGS("osnoise", "hist", "--cgroup"); + + ck_assert(params->cgroup); + ck_assert_ptr_null(params->cgroup_name); +} +END_TEST + +START_TEST(test_cgroup_long_space) +{ + PARSE_ARGS("osnoise", "hist", "--cgroup", "cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_cgroup_long_equals) +{ + PARSE_ARGS("osnoise", "hist", "--cgroup=cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_priority_short) +{ + PARSE_ARGS("osnoise", "hist", "-P", "f:95"); + + ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO); + ck_assert_int_eq(params->sched_param.sched_priority, 95); +} +END_TEST + +START_TEST(test_priority_long) +{ + PARSE_ARGS("osnoise", "hist", "--priority", "f:95"); + + ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO); + ck_assert_int_eq(params->sched_param.sched_priority, 95); +} +END_TEST + +/* Histogram Options */ + +START_TEST(test_bucket_size_short) +{ + PARSE_ARGS("osnoise", "hist", "-b", "2"); + + ck_assert_int_eq(params->hist.bucket_size, 2); +} +END_TEST + +START_TEST(test_bucket_size_long) +{ + PARSE_ARGS("osnoise", "hist", "--bucket-size", "2"); + + ck_assert_int_eq(params->hist.bucket_size, 2); +} +END_TEST + +START_TEST(test_entries_short) +{ + PARSE_ARGS("osnoise", "hist", "-E", "512"); + + ck_assert_int_eq(params->hist.entries, 512); +} +END_TEST + +START_TEST(test_entries_long) +{ + PARSE_ARGS("osnoise", "hist", "--entries", "512"); + + ck_assert_int_eq(params->hist.entries, 512); +} +END_TEST + +START_TEST(test_no_header) +{ + PARSE_ARGS("osnoise", "hist", "--no-header"); + + ck_assert(params->hist.no_header); +} +END_TEST + +START_TEST(test_no_index) +{ + PARSE_ARGS("osnoise", "hist", "--with-zeros", "--no-index"); + + ck_assert(params->hist.no_index); +} +END_TEST + +START_TEST(test_no_summary) +{ + PARSE_ARGS("osnoise", "hist", "--no-summary"); + + ck_assert(params->hist.no_summary); +} +END_TEST + +START_TEST(test_with_zeros) +{ + PARSE_ARGS("osnoise", "hist", "--with-zeros"); + + ck_assert(params->hist.with_zeros); +} +END_TEST + +/* System Tuning */ + +START_TEST(test_trace_buffer_size) +{ + PARSE_ARGS("osnoise", "hist", "--trace-buffer-size", "200"); + + ck_assert_int_eq(params->buffer_size, 200); +} +END_TEST + +START_TEST(test_warm_up) +{ + PARSE_ARGS("osnoise", "hist", "--warm-up", "5"); + + ck_assert_int_eq(params->warmup, 5); +} +END_TEST + +/* Auto Analysis and Actions */ + +START_TEST(test_auto) +{ + PARSE_ARGS("osnoise", "hist", "-a", "20"); + + CLI_OSNOISE_ASSERT_AUTO(20); +} +END_TEST + +START_TEST(test_on_end) +{ + PARSE_ARGS("osnoise", "hist", "--on-end", "trace"); + + CLI_ASSERT_SINGLE_ACTION(end_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "osnoise_trace.txt"); +} +END_TEST + +START_TEST(test_on_threshold) +{ + PARSE_ARGS("osnoise", "hist", "--on-threshold", "trace"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "osnoise_trace.txt"); +} +END_TEST + +/* General */ + +START_TEST(test_debug_short) +{ + PARSE_ARGS("osnoise", "hist", "-D"); + + ck_assert(config_debug); +} +END_TEST + +START_TEST(test_debug_long) +{ + PARSE_ARGS("osnoise", "hist", "--debug"); + + ck_assert(config_debug); +} +END_TEST + +START_TEST(test_duration_short) +{ + PARSE_ARGS("osnoise", "hist", "-d", "1m"); + + ck_assert_int_eq(params->duration, 60); +} +END_TEST + +START_TEST(test_duration_long) +{ + PARSE_ARGS("osnoise", "hist", "--duration", "1m"); + + ck_assert_int_eq(params->duration, 60); +} +END_TEST + +Suite *osnoise_hist_cli_suite(void) +{ + Suite *s = suite_create("osnoise_hist_cli"); + TCase *tc; + + tc = tcase_create("tracing_options"); + tcase_add_test(tc, test_period_short); + tcase_add_test(tc, test_period_long); + tcase_add_test(tc, test_runtime_short); + tcase_add_test(tc, test_runtime_long); + tcase_add_test(tc, test_stop_short); + tcase_add_test(tc, test_stop_long); + tcase_add_test(tc, test_stop_total_short); + tcase_add_test(tc, test_stop_total_long); + tcase_add_test(tc, test_threshold_short); + tcase_add_test(tc, test_threshold_long); + tcase_add_test(tc, test_trace_short_noarg); + tcase_add_test(tc, test_trace_short_followarg); + tcase_add_test(tc, test_trace_short_space); + tcase_add_test(tc, test_trace_short_equals); + tcase_add_test(tc, test_trace_long_noarg); + tcase_add_test(tc, test_trace_long_followarg); + tcase_add_test(tc, test_trace_long_space); + tcase_add_test(tc, test_trace_long_equals); + suite_add_tcase(s, tc); + + tc = tcase_create("event_configuration"); + tcase_add_test(tc, test_event_short); + tcase_add_test(tc, test_event_long); + tcase_add_test(tc, test_filter); + tcase_add_test(tc, test_trigger); + suite_add_tcase(s, tc); + + tc = tcase_create("cpu_configuration"); + tcase_add_test(tc, test_cpus_short); + tcase_add_test(tc, test_cpus_long); + tcase_add_test(tc, test_housekeeping_short); + tcase_add_test(tc, test_housekeeping_long); + suite_add_tcase(s, tc); + + tc = tcase_create("thread_configuration"); + tcase_add_test(tc, test_cgroup_short_noarg); + tcase_add_test(tc, test_cgroup_short_space); + tcase_add_test(tc, test_cgroup_short_equals); + tcase_add_test(tc, test_cgroup_long_noarg); + tcase_add_test(tc, test_cgroup_long_space); + tcase_add_test(tc, test_cgroup_long_equals); + tcase_add_test(tc, test_priority_short); + tcase_add_test(tc, test_priority_long); + suite_add_tcase(s, tc); + + tc = tcase_create("histogram_options"); + tcase_add_test(tc, test_bucket_size_short); + tcase_add_test(tc, test_bucket_size_long); + tcase_add_test(tc, test_entries_short); + tcase_add_test(tc, test_entries_long); + tcase_add_test(tc, test_no_header); + tcase_add_test(tc, test_no_index); + tcase_add_test(tc, test_no_summary); + tcase_add_test(tc, test_with_zeros); + suite_add_tcase(s, tc); + + tc = tcase_create("system_tuning"); + tcase_add_test(tc, test_trace_buffer_size); + tcase_add_test(tc, test_warm_up); + suite_add_tcase(s, tc); + + tc = tcase_create("aa_actions"); + tcase_add_test(tc, test_auto); + tcase_add_test(tc, test_on_end); + tcase_add_test(tc, test_on_threshold); + suite_add_tcase(s, tc); + + tc = tcase_create("general"); + tcase_add_test(tc, test_debug_short); + tcase_add_test(tc, test_debug_long); + tcase_add_test(tc, test_duration_short); + tcase_add_test(tc, test_duration_long); + suite_add_tcase(s, tc); + + return s; +}
diff --git a/tools/tracing/rtla/tests/unit/osnoise_top_cli.c b/tools/tracing/rtla/tests/unit/osnoise_top_cli.c new file mode 100644 index 0000000..f3a8633 --- /dev/null +++ b/tools/tracing/rtla/tests/unit/osnoise_top_cli.c
@@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <check.h> +#include <stdio.h> +#include <stdlib.h> +#include <sched.h> +#include <limits.h> +#include <unistd.h> +#include <sys/sysinfo.h> + +#include "cli_params_assert.h" +#include "../../src/cli.h" + +#define PARSE_ARGS(...) char *argv[] = { __VA_ARGS__, NULL };\ + int argc = sizeof(argv) / sizeof(char *) - 1;\ + struct common_params *params =\ + osnoise_top_parse_args(argc, argv);\ + struct osnoise_params *osn_params __maybe_unused =\ + to_osnoise_params(params) + +/* Tracing Options */ + +START_TEST(test_period_short) +{ + PARSE_ARGS("osnoise", "top", "-p", "100000"); + + ck_assert_int_eq(osn_params->period, 100000); +} +END_TEST + +START_TEST(test_period_long) +{ + PARSE_ARGS("osnoise", "top", "--period", "100000"); + + ck_assert_int_eq(osn_params->period, 100000); +} +END_TEST + +START_TEST(test_runtime_short) +{ + PARSE_ARGS("osnoise", "top", "-r", "95000"); + + ck_assert_int_eq(osn_params->runtime, 95000); +} +END_TEST + +START_TEST(test_runtime_long) +{ + PARSE_ARGS("osnoise", "top", "--runtime", "95000"); + + ck_assert_int_eq(osn_params->runtime, 95000); +} +END_TEST + +START_TEST(test_stop_short) +{ + PARSE_ARGS("osnoise", "top", "-s", "20"); + + ck_assert_int_eq(params->stop_us, 20); +} +END_TEST + +START_TEST(test_stop_long) +{ + PARSE_ARGS("osnoise", "top", "--stop", "20"); + + ck_assert_int_eq(params->stop_us, 20); +} +END_TEST + +START_TEST(test_stop_total_short) +{ + PARSE_ARGS("osnoise", "top", "-S", "20"); + + ck_assert_int_eq(params->stop_total_us, 20); +} +END_TEST + +START_TEST(test_stop_total_long) +{ + PARSE_ARGS("osnoise", "top", "--stop-total", "20"); + + ck_assert_int_eq(params->stop_total_us, 20); +} +END_TEST + +START_TEST(test_threshold_short) +{ + PARSE_ARGS("osnoise", "top", "-T", "5"); + + ck_assert_int_eq(osn_params->threshold, 5); +} +END_TEST + +START_TEST(test_threshold_long) +{ + PARSE_ARGS("osnoise", "top", "--threshold", "5"); + + ck_assert_int_eq(osn_params->threshold, 5); +} +END_TEST + +START_TEST(test_trace_short_noarg) +{ + PARSE_ARGS("osnoise", "top", "-t"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "osnoise_trace.txt"); +} +END_TEST + +START_TEST(test_trace_short_followarg) +{ + PARSE_ARGS("osnoise", "top", "-t", "-d", "20"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "osnoise_trace.txt"); + ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */ +} +END_TEST + +START_TEST(test_trace_short_space) +{ + PARSE_ARGS("osnoise", "top", "-t", "tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +START_TEST(test_trace_short_equals) +{ + PARSE_ARGS("osnoise", "top", "-t=tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +START_TEST(test_trace_long_noarg) +{ + PARSE_ARGS("osnoise", "top", "--trace"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "osnoise_trace.txt"); +} +END_TEST + +START_TEST(test_trace_long_followarg) +{ + PARSE_ARGS("osnoise", "top", "--trace", "-d", "20"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "osnoise_trace.txt"); + ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */ +} +END_TEST + +START_TEST(test_trace_long_space) +{ + PARSE_ARGS("osnoise", "top", "--trace", "tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +START_TEST(test_trace_long_equals) +{ + PARSE_ARGS("osnoise", "top", "--trace=tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +/* Event Configuration */ + +START_TEST(test_event_short) +{ + PARSE_ARGS("osnoise", "top", "-e", "system:event"); + + CLI_ASSERT_SINGLE_EVENT("system", "event"); +} +END_TEST + +START_TEST(test_event_long) +{ + PARSE_ARGS("osnoise", "top", "--event", "system:event"); + + CLI_ASSERT_SINGLE_EVENT("system", "event"); +} +END_TEST + +START_TEST(test_filter) +{ + PARSE_ARGS("osnoise", "top", "-e", "system:event", "--filter", "filter"); + + CLI_ASSERT_SINGLE_FILTER("filter"); +} +END_TEST + +START_TEST(test_trigger) +{ + PARSE_ARGS("osnoise", "top", "-e", "system:event", "--trigger", "trigger"); + + CLI_ASSERT_SINGLE_TRIGGER("trigger"); +} +END_TEST + +/* CPU Configuration */ + +START_TEST(test_cpus_short) +{ + nr_cpus = 4; + + PARSE_ARGS("osnoise", "top", "-c", "0-1,3"); + + ck_assert_str_eq(params->cpus, "0-1,3"); + CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3); +} +END_TEST + +START_TEST(test_cpus_long) +{ + nr_cpus = 4; + + PARSE_ARGS("osnoise", "top", "--cpus", "0-1,3"); + + ck_assert_str_eq(params->cpus, "0-1,3"); + CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3); +} +END_TEST + +START_TEST(test_housekeeping_short) +{ + nr_cpus = 4; + + PARSE_ARGS("osnoise", "top", "-H", "0-1,3"); + + CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3); +} +END_TEST + +START_TEST(test_housekeeping_long) +{ + nr_cpus = 4; + + PARSE_ARGS("osnoise", "top", "--house-keeping", "0-1,3"); + + CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3); +} +END_TEST + +/* Thread Configuration */ + +START_TEST(test_cgroup_short_noarg) +{ + PARSE_ARGS("osnoise", "top", "-C"); + + ck_assert(params->cgroup); + ck_assert_ptr_null(params->cgroup_name); +} +END_TEST + +START_TEST(test_cgroup_short_space) +{ + PARSE_ARGS("osnoise", "top", "-C", "cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_cgroup_short_equals) +{ + PARSE_ARGS("osnoise", "top", "-C=cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_cgroup_long_noarg) +{ + PARSE_ARGS("osnoise", "top", "--cgroup"); + + ck_assert(params->cgroup); + ck_assert_ptr_null(params->cgroup_name); +} +END_TEST + +START_TEST(test_cgroup_long_space) +{ + PARSE_ARGS("osnoise", "top", "--cgroup", "cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_cgroup_long_equals) +{ + PARSE_ARGS("osnoise", "top", "--cgroup=cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_priority_short) +{ + PARSE_ARGS("osnoise", "top", "-P", "f:95"); + + ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO); + ck_assert_int_eq(params->sched_param.sched_priority, 95); +} +END_TEST + +START_TEST(test_priority_long) +{ + PARSE_ARGS("osnoise", "top", "--priority", "f:95"); + + ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO); + ck_assert_int_eq(params->sched_param.sched_priority, 95); +} +END_TEST + +/* Output */ + +START_TEST(test_quiet_short) +{ + PARSE_ARGS("osnoise", "top", "-q"); + + ck_assert(params->quiet); +} +END_TEST + +START_TEST(test_quiet_long) +{ + PARSE_ARGS("osnoise", "top", "--quiet"); + + ck_assert(params->quiet); +} +END_TEST + +/* Auto Analysis and Actions */ + +START_TEST(test_auto) +{ + PARSE_ARGS("osnoise", "top", "-a", "20"); + + CLI_OSNOISE_ASSERT_AUTO(20); +} +END_TEST + +START_TEST(test_on_end) +{ + PARSE_ARGS("osnoise", "top", "--on-end", "trace"); + + CLI_ASSERT_SINGLE_ACTION(end_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "osnoise_trace.txt"); +} +END_TEST + +START_TEST(test_on_threshold) +{ + PARSE_ARGS("osnoise", "top", "--on-threshold", "trace"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "osnoise_trace.txt"); +} +END_TEST + +/* System Tuning */ + +START_TEST(test_trace_buffer_size) +{ + PARSE_ARGS("osnoise", "top", "--trace-buffer-size", "200"); + + ck_assert_int_eq(params->buffer_size, 200); +} +END_TEST + +START_TEST(test_warm_up) +{ + PARSE_ARGS("osnoise", "top", "--warm-up", "5"); + + ck_assert_int_eq(params->warmup, 5); +} +END_TEST + +/* General */ + +START_TEST(test_debug_short) +{ + PARSE_ARGS("osnoise", "top", "-D"); + + ck_assert(config_debug); +} +END_TEST + +START_TEST(test_debug_long) +{ + PARSE_ARGS("osnoise", "top", "--debug"); + + ck_assert(config_debug); +} +END_TEST + +START_TEST(test_duration_short) +{ + PARSE_ARGS("osnoise", "top", "-d", "1m"); + + ck_assert_int_eq(params->duration, 60); +} +END_TEST + +START_TEST(test_duration_long) +{ + PARSE_ARGS("osnoise", "top", "--duration", "1m"); + + ck_assert_int_eq(params->duration, 60); +} +END_TEST + +Suite *osnoise_top_cli_suite(void) +{ + Suite *s = suite_create("osnoise_top_cli"); + TCase *tc; + + tc = tcase_create("tracing_options"); + tcase_add_test(tc, test_period_short); + tcase_add_test(tc, test_period_long); + tcase_add_test(tc, test_runtime_short); + tcase_add_test(tc, test_runtime_long); + tcase_add_test(tc, test_stop_short); + tcase_add_test(tc, test_stop_long); + tcase_add_test(tc, test_stop_total_short); + tcase_add_test(tc, test_stop_total_long); + tcase_add_test(tc, test_threshold_short); + tcase_add_test(tc, test_threshold_long); + tcase_add_test(tc, test_trace_short_noarg); + tcase_add_test(tc, test_trace_short_followarg); + tcase_add_test(tc, test_trace_short_space); + tcase_add_test(tc, test_trace_short_equals); + tcase_add_test(tc, test_trace_long_noarg); + tcase_add_test(tc, test_trace_long_followarg); + tcase_add_test(tc, test_trace_long_space); + tcase_add_test(tc, test_trace_long_equals); + suite_add_tcase(s, tc); + + tc = tcase_create("event_configuration"); + tcase_add_test(tc, test_event_short); + tcase_add_test(tc, test_event_long); + tcase_add_test(tc, test_filter); + tcase_add_test(tc, test_trigger); + suite_add_tcase(s, tc); + + tc = tcase_create("cpu_configuration"); + tcase_add_test(tc, test_cpus_short); + tcase_add_test(tc, test_cpus_long); + tcase_add_test(tc, test_housekeeping_short); + tcase_add_test(tc, test_housekeeping_long); + suite_add_tcase(s, tc); + + tc = tcase_create("thread_configuration"); + tcase_add_test(tc, test_cgroup_short_noarg); + tcase_add_test(tc, test_cgroup_short_space); + tcase_add_test(tc, test_cgroup_short_equals); + tcase_add_test(tc, test_cgroup_long_noarg); + tcase_add_test(tc, test_cgroup_long_space); + tcase_add_test(tc, test_cgroup_long_equals); + tcase_add_test(tc, test_priority_short); + tcase_add_test(tc, test_priority_long); + suite_add_tcase(s, tc); + + tc = tcase_create("output"); + tcase_add_test(tc, test_quiet_short); + tcase_add_test(tc, test_quiet_long); + suite_add_tcase(s, tc); + + tc = tcase_create("system_tuning"); + tcase_add_test(tc, test_trace_buffer_size); + tcase_add_test(tc, test_warm_up); + suite_add_tcase(s, tc); + + tc = tcase_create("aa_actions"); + tcase_add_test(tc, test_auto); + tcase_add_test(tc, test_on_end); + tcase_add_test(tc, test_on_threshold); + suite_add_tcase(s, tc); + + tc = tcase_create("general"); + tcase_add_test(tc, test_debug_short); + tcase_add_test(tc, test_debug_long); + tcase_add_test(tc, test_duration_short); + tcase_add_test(tc, test_duration_long); + suite_add_tcase(s, tc); + + return s; +}
diff --git a/tools/tracing/rtla/tests/unit/timerlat_hist_cli.c b/tools/tracing/rtla/tests/unit/timerlat_hist_cli.c new file mode 100644 index 0000000..968bf962 --- /dev/null +++ b/tools/tracing/rtla/tests/unit/timerlat_hist_cli.c
@@ -0,0 +1,722 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <check.h> +#include <stdio.h> +#include <stdlib.h> +#include <sched.h> +#include <limits.h> +#include <unistd.h> +#include <sys/sysinfo.h> + +#include <linux/container_of.h> + +#include "cli_params_assert.h" +#include "../../src/cli.h" + +#define PARSE_ARGS(...) char *argv[] = { __VA_ARGS__, NULL };\ + int argc = sizeof(argv) / sizeof(char *) - 1;\ + struct common_params *params =\ + timerlat_hist_parse_args(argc, argv);\ + struct timerlat_params *tlat_params __maybe_unused =\ + to_timerlat_params(params) + +/* Tracing Options */ + +START_TEST(test_irq_short) +{ + PARSE_ARGS("timerlat", "hist", "-i", "20"); + + ck_assert_int_eq(params->stop_us, 20); +} +END_TEST + +START_TEST(test_irq_long) +{ + PARSE_ARGS("timerlat", "hist", "--irq", "20"); + + ck_assert_int_eq(params->stop_us, 20); +} +END_TEST + +START_TEST(test_period_short) +{ + PARSE_ARGS("timerlat", "hist", "-p", "200"); + + ck_assert_int_eq(tlat_params->timerlat_period_us, 200); +} +END_TEST + +START_TEST(test_period_long) +{ + PARSE_ARGS("timerlat", "hist", "--period", "200"); + + ck_assert_int_eq(tlat_params->timerlat_period_us, 200); +} +END_TEST + +START_TEST(test_stack_short) +{ + PARSE_ARGS("timerlat", "hist", "-s", "20"); + + ck_assert_int_eq(tlat_params->print_stack, 20); +} +END_TEST + +START_TEST(test_stack_long) +{ + PARSE_ARGS("timerlat", "hist", "--stack", "20"); + + ck_assert_int_eq(tlat_params->print_stack, 20); +} +END_TEST + +START_TEST(test_thread_short) +{ + PARSE_ARGS("timerlat", "hist", "-T", "20"); + + ck_assert_int_eq(params->stop_total_us, 20); +} +END_TEST + +START_TEST(test_thread_long) +{ + PARSE_ARGS("timerlat", "hist", "--thread", "20"); + + ck_assert_int_eq(params->stop_total_us, 20); +} +END_TEST + +START_TEST(test_trace_short_noarg) +{ + PARSE_ARGS("timerlat", "hist", "-t"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "timerlat_trace.txt"); +} +END_TEST + +START_TEST(test_trace_short_followarg) +{ + PARSE_ARGS("timerlat", "hist", "-t", "-d", "20"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "timerlat_trace.txt"); + ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */ +} +END_TEST + +START_TEST(test_trace_short_space) +{ + PARSE_ARGS("timerlat", "hist", "-t", "tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +START_TEST(test_trace_short_equals) +{ + PARSE_ARGS("timerlat", "hist", "-t=tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +START_TEST(test_trace_long_noarg) +{ + PARSE_ARGS("timerlat", "hist", "--trace"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "timerlat_trace.txt"); +} +END_TEST + +START_TEST(test_trace_long_followarg) +{ + PARSE_ARGS("timerlat", "hist", "--trace", "-d", "20"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "timerlat_trace.txt"); + ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */ +} +END_TEST + +START_TEST(test_trace_long_space) +{ + PARSE_ARGS("timerlat", "hist", "--trace", "tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +START_TEST(test_trace_long_equals) +{ + PARSE_ARGS("timerlat", "hist", "--trace=tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +/* Event Configuration */ + +START_TEST(test_event_short) +{ + PARSE_ARGS("timerlat", "hist", "-e", "system:event"); + + CLI_ASSERT_SINGLE_EVENT("system", "event"); +} +END_TEST + +START_TEST(test_event_long) +{ + PARSE_ARGS("timerlat", "hist", "--event", "system:event"); + + CLI_ASSERT_SINGLE_EVENT("system", "event"); +} +END_TEST + +START_TEST(test_filter) +{ + PARSE_ARGS("timerlat", "hist", "-e", "system:event", "--filter", "filter"); + + CLI_ASSERT_SINGLE_FILTER("filter"); +} +END_TEST + +START_TEST(test_trigger) +{ + PARSE_ARGS("timerlat", "hist", "-e", "system:event", "--trigger", "trigger"); + + CLI_ASSERT_SINGLE_TRIGGER("trigger"); +} +END_TEST + +/* CPU Configuration */ + +START_TEST(test_cpus_short) +{ + nr_cpus = 4; + + PARSE_ARGS("timerlat", "hist", "-c", "0-1,3"); + + ck_assert_str_eq(params->cpus, "0-1,3"); + CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3); +} +END_TEST + +START_TEST(test_cpus_long) +{ + nr_cpus = 4; + + PARSE_ARGS("timerlat", "hist", "--cpus", "0-1,3"); + + ck_assert_str_eq(params->cpus, "0-1,3"); + CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3); +} +END_TEST + +START_TEST(test_housekeeping_short) +{ + nr_cpus = 4; + + PARSE_ARGS("timerlat", "hist", "-H", "0-1,3"); + + CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3); +} +END_TEST + +START_TEST(test_housekeeping_long) +{ + nr_cpus = 4; + + PARSE_ARGS("timerlat", "hist", "--house-keeping", "0-1,3"); + + CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3); +} +END_TEST + +/* Thread Configuration */ + +START_TEST(test_cgroup_short_noarg) +{ + PARSE_ARGS("timerlat", "hist", "-C"); + + ck_assert(params->cgroup); + ck_assert_ptr_null(params->cgroup_name); +} +END_TEST + +START_TEST(test_cgroup_short_space) +{ + PARSE_ARGS("timerlat", "hist", "-C", "cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_cgroup_short_equals) +{ + PARSE_ARGS("timerlat", "hist", "-C=cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_cgroup_long_noarg) +{ + PARSE_ARGS("timerlat", "hist", "--cgroup"); + + ck_assert(params->cgroup); + ck_assert_ptr_null(params->cgroup_name); +} +END_TEST + +START_TEST(test_cgroup_long_space) +{ + PARSE_ARGS("timerlat", "hist", "--cgroup", "cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_cgroup_long_equals) +{ + PARSE_ARGS("timerlat", "hist", "--cgroup=cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_kernel_threads_short) +{ + PARSE_ARGS("timerlat", "hist", "-k"); + + ck_assert(params->kernel_workload); + ck_assert(!params->user_workload); + ck_assert(!params->user_data); +} +END_TEST + +START_TEST(test_kernel_threads_long) +{ + PARSE_ARGS("timerlat", "hist", "--kernel-threads"); + + ck_assert(params->kernel_workload); + ck_assert(!params->user_workload); + ck_assert(!params->user_data); +} +END_TEST + +START_TEST(test_priority_short) +{ + PARSE_ARGS("timerlat", "hist", "-P", "f:95"); + + ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO); + ck_assert_int_eq(params->sched_param.sched_priority, 95); +} +END_TEST + +START_TEST(test_priority_long) +{ + PARSE_ARGS("timerlat", "hist", "--priority", "f:95"); + + ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO); + ck_assert_int_eq(params->sched_param.sched_priority, 95); +} +END_TEST + +START_TEST(test_user_load_short) +{ + PARSE_ARGS("timerlat", "hist", "-U"); + + ck_assert(!params->kernel_workload); + ck_assert(!params->user_workload); + ck_assert(params->user_data); +} +END_TEST + +START_TEST(test_user_load_long) +{ + PARSE_ARGS("timerlat", "hist", "--user-load"); + + ck_assert(!params->kernel_workload); + ck_assert(!params->user_workload); + ck_assert(params->user_data); +} +END_TEST + +START_TEST(test_user_threads_short) +{ + PARSE_ARGS("timerlat", "hist", "-u"); + + ck_assert(!params->kernel_workload); + ck_assert(params->user_workload); + ck_assert(params->user_data); +} +END_TEST + +START_TEST(test_user_threads_long) +{ + PARSE_ARGS("timerlat", "hist", "--user-threads"); + + ck_assert(!params->kernel_workload); + ck_assert(params->user_workload); + ck_assert(params->user_data); +} +END_TEST + +START_TEST(test_aligned_short) +{ + PARSE_ARGS("timerlat", "hist", "-A", "500"); + + ck_assert(tlat_params->timerlat_align); + ck_assert_int_eq(tlat_params->timerlat_align_us, 500); +} +END_TEST + +START_TEST(test_aligned_long) +{ + PARSE_ARGS("timerlat", "hist", "--aligned", "500"); + + ck_assert(tlat_params->timerlat_align); + ck_assert_int_eq(tlat_params->timerlat_align_us, 500); +} +END_TEST + +/* Histogram Options */ + +START_TEST(test_bucket_size_short) +{ + PARSE_ARGS("timerlat", "hist", "-b", "2"); + + ck_assert_int_eq(params->hist.bucket_size, 2); +} +END_TEST + +START_TEST(test_bucket_size_long) +{ + PARSE_ARGS("timerlat", "hist", "--bucket-size", "2"); + + ck_assert_int_eq(params->hist.bucket_size, 2); +} +END_TEST + +START_TEST(test_entries_short) +{ + PARSE_ARGS("timerlat", "hist", "-E", "512"); + + ck_assert_int_eq(params->hist.entries, 512); +} +END_TEST + +START_TEST(test_entries_long) +{ + PARSE_ARGS("timerlat", "hist", "--entries", "512"); + + ck_assert_int_eq(params->hist.entries, 512); +} +END_TEST + +START_TEST(test_no_header) +{ + PARSE_ARGS("timerlat", "hist", "--no-header"); + + ck_assert(params->hist.no_header); +} +END_TEST + +START_TEST(test_no_index) +{ + PARSE_ARGS("timerlat", "hist", "--with-zeros", "--no-index"); + + ck_assert(params->hist.no_index); +} +END_TEST + +START_TEST(test_no_irq) +{ + PARSE_ARGS("timerlat", "hist", "--no-irq"); + + ck_assert(params->hist.no_irq); +} +END_TEST + +START_TEST(test_no_summary) +{ + PARSE_ARGS("timerlat", "hist", "--no-summary"); + + ck_assert(params->hist.no_summary); +} +END_TEST + +START_TEST(test_no_thread) +{ + PARSE_ARGS("timerlat", "hist", "--no-thread"); + + ck_assert(params->hist.no_thread); +} +END_TEST + +START_TEST(test_with_zeros) +{ + PARSE_ARGS("timerlat", "hist", "--with-zeros"); + + ck_assert(params->hist.with_zeros); +} +END_TEST + +/* Output */ + +START_TEST(test_nano_short) +{ + PARSE_ARGS("timerlat", "hist", "-n"); + + ck_assert_int_eq(params->output_divisor, 1); +} +END_TEST + +START_TEST(test_nano_long) +{ + PARSE_ARGS("timerlat", "hist", "--nano"); + + ck_assert_int_eq(params->output_divisor, 1); +} +END_TEST + +/* System Tuning */ + +START_TEST(test_deepest_idle_state) +{ + PARSE_ARGS("timerlat", "hist", "--deepest-idle-state", "1"); + + ck_assert_int_eq(tlat_params->deepest_idle_state, 1); +} +END_TEST + +START_TEST(test_dma_latency) +{ + PARSE_ARGS("timerlat", "hist", "--dma-latency", "10"); + + ck_assert_int_eq(tlat_params->dma_latency, 10); +} +END_TEST + +START_TEST(test_trace_buffer_size) +{ + PARSE_ARGS("timerlat", "hist", "--trace-buffer-size", "200"); + + ck_assert_int_eq(params->buffer_size, 200); +} +END_TEST + +START_TEST(test_warm_up) +{ + PARSE_ARGS("timerlat", "hist", "--warm-up", "5"); + + ck_assert_int_eq(params->warmup, 5); +} +END_TEST + +/* Auto Analysis and Actions */ + +START_TEST(test_auto) +{ + PARSE_ARGS("timerlat", "hist", "-a", "20"); + + CLI_TIMERLAT_ASSERT_AUTO(20); +} +END_TEST + +START_TEST(test_bpf_action) +{ + PARSE_ARGS("timerlat", "hist", "--bpf-action", "program"); + + ck_assert_str_eq(tlat_params->bpf_action_program, "program"); +} +END_TEST + +START_TEST(test_dump_tasks) +{ + PARSE_ARGS("timerlat", "hist", "--dump-tasks"); + + ck_assert(tlat_params->dump_tasks); +} +END_TEST + +START_TEST(test_no_aa) +{ + PARSE_ARGS("timerlat", "hist", "--no-aa"); + + ck_assert(tlat_params->no_aa); +} +END_TEST + +START_TEST(test_on_end) +{ + PARSE_ARGS("timerlat", "hist", "--on-end", "trace"); + + CLI_ASSERT_SINGLE_ACTION(end_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "timerlat_trace.txt"); +} +END_TEST + +START_TEST(test_on_threshold) +{ + PARSE_ARGS("timerlat", "hist", "--on-threshold", "trace"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "timerlat_trace.txt"); +} +END_TEST + +START_TEST(test_stack_format) +{ + PARSE_ARGS("timerlat", "hist", "--stack-format", "truncate"); + + ck_assert_int_eq(tlat_params->stack_format, STACK_FORMAT_TRUNCATE); +} +END_TEST + +/* General */ + +START_TEST(test_debug_short) +{ + PARSE_ARGS("timerlat", "hist", "-D"); + + ck_assert(config_debug); +} +END_TEST + +START_TEST(test_debug_long) +{ + PARSE_ARGS("timerlat", "hist", "--debug"); + + ck_assert(config_debug); +} +END_TEST + +START_TEST(test_duration_short) +{ + PARSE_ARGS("timerlat", "hist", "-d", "1m"); + + ck_assert_int_eq(params->duration, 60); +} +END_TEST + +START_TEST(test_duration_long) +{ + PARSE_ARGS("timerlat", "hist", "--duration", "1m"); + + ck_assert_int_eq(params->duration, 60); +} +END_TEST + +Suite *timerlat_hist_cli_suite(void) +{ + Suite *s = suite_create("timerlat_hist_cli"); + TCase *tc; + + tc = tcase_create("tracing_options"); + tcase_add_test(tc, test_irq_short); + tcase_add_test(tc, test_irq_long); + tcase_add_test(tc, test_period_short); + tcase_add_test(tc, test_period_long); + tcase_add_test(tc, test_stack_short); + tcase_add_test(tc, test_stack_long); + tcase_add_test(tc, test_thread_short); + tcase_add_test(tc, test_thread_long); + tcase_add_test(tc, test_trace_short_noarg); + tcase_add_test(tc, test_trace_short_followarg); + tcase_add_test(tc, test_trace_short_space); + tcase_add_test(tc, test_trace_short_equals); + tcase_add_test(tc, test_trace_long_noarg); + tcase_add_test(tc, test_trace_long_followarg); + tcase_add_test(tc, test_trace_long_space); + tcase_add_test(tc, test_trace_long_equals); + suite_add_tcase(s, tc); + + tc = tcase_create("event_configuration"); + tcase_add_test(tc, test_event_short); + tcase_add_test(tc, test_event_long); + tcase_add_test(tc, test_filter); + tcase_add_test(tc, test_trigger); + suite_add_tcase(s, tc); + + tc = tcase_create("cpu_configuration"); + tcase_add_test(tc, test_cpus_short); + tcase_add_test(tc, test_cpus_long); + tcase_add_test(tc, test_housekeeping_short); + tcase_add_test(tc, test_housekeeping_long); + suite_add_tcase(s, tc); + + tc = tcase_create("thread_configuration"); + tcase_add_test(tc, test_cgroup_short_noarg); + tcase_add_test(tc, test_cgroup_short_space); + tcase_add_test(tc, test_cgroup_short_equals); + tcase_add_test(tc, test_cgroup_long_noarg); + tcase_add_test(tc, test_cgroup_long_space); + tcase_add_test(tc, test_cgroup_long_equals); + tcase_add_test(tc, test_kernel_threads_short); + tcase_add_test(tc, test_kernel_threads_long); + tcase_add_test(tc, test_priority_short); + tcase_add_test(tc, test_priority_long); + tcase_add_test(tc, test_user_load_short); + tcase_add_test(tc, test_user_load_long); + tcase_add_test(tc, test_user_threads_short); + tcase_add_test(tc, test_user_threads_long); + tcase_add_test(tc, test_aligned_short); + tcase_add_test(tc, test_aligned_long); + suite_add_tcase(s, tc); + + tc = tcase_create("histogram_options"); + tcase_add_test(tc, test_bucket_size_short); + tcase_add_test(tc, test_bucket_size_long); + tcase_add_test(tc, test_entries_short); + tcase_add_test(tc, test_entries_long); + tcase_add_test(tc, test_no_header); + tcase_add_test(tc, test_no_index); + tcase_add_test(tc, test_no_irq); + tcase_add_test(tc, test_no_summary); + tcase_add_test(tc, test_no_thread); + tcase_add_test(tc, test_with_zeros); + suite_add_tcase(s, tc); + + tc = tcase_create("output"); + tcase_add_test(tc, test_nano_short); + tcase_add_test(tc, test_nano_long); + suite_add_tcase(s, tc); + + tc = tcase_create("system_tuning"); + tcase_add_test(tc, test_deepest_idle_state); + tcase_add_test(tc, test_dma_latency); + tcase_add_test(tc, test_trace_buffer_size); + tcase_add_test(tc, test_warm_up); + suite_add_tcase(s, tc); + + tc = tcase_create("aa_actions"); + tcase_add_test(tc, test_auto); + tcase_add_test(tc, test_bpf_action); + tcase_add_test(tc, test_dump_tasks); + tcase_add_test(tc, test_no_aa); + tcase_add_test(tc, test_on_end); + tcase_add_test(tc, test_on_threshold); + tcase_add_test(tc, test_stack_format); + suite_add_tcase(s, tc); + + tc = tcase_create("general"); + tcase_add_test(tc, test_debug_short); + tcase_add_test(tc, test_debug_long); + tcase_add_test(tc, test_duration_short); + tcase_add_test(tc, test_duration_long); + suite_add_tcase(s, tc); + + return s; +}
diff --git a/tools/tracing/rtla/tests/unit/timerlat_top_cli.c b/tools/tracing/rtla/tests/unit/timerlat_top_cli.c new file mode 100644 index 0000000..33aa658 --- /dev/null +++ b/tools/tracing/rtla/tests/unit/timerlat_top_cli.c
@@ -0,0 +1,654 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <check.h> +#include <stdio.h> +#include <stdlib.h> +#include <sched.h> +#include <limits.h> +#include <unistd.h> +#include <sys/sysinfo.h> + +#include <linux/container_of.h> + +#include "cli_params_assert.h" +#include "../../src/cli.h" + +#define PARSE_ARGS(...) char *argv[] = { __VA_ARGS__, NULL };\ + int argc = sizeof(argv) / sizeof(char *) - 1;\ + struct common_params *params =\ + timerlat_top_parse_args(argc, argv);\ + struct timerlat_params *tlat_params __maybe_unused =\ + to_timerlat_params(params) + +/* Tracing Options */ + +START_TEST(test_irq_short) +{ + PARSE_ARGS("timerlat", "top", "-i", "20"); + + ck_assert_int_eq(params->stop_us, 20); +} +END_TEST + +START_TEST(test_irq_long) +{ + PARSE_ARGS("timerlat", "top", "--irq", "20"); + + ck_assert_int_eq(params->stop_us, 20); +} +END_TEST + +START_TEST(test_period_short) +{ + PARSE_ARGS("timerlat", "top", "-p", "200"); + + ck_assert_int_eq(tlat_params->timerlat_period_us, 200); +} +END_TEST + +START_TEST(test_period_long) +{ + PARSE_ARGS("timerlat", "top", "--period", "200"); + + ck_assert_int_eq(tlat_params->timerlat_period_us, 200); +} +END_TEST + +START_TEST(test_stack_short) +{ + PARSE_ARGS("timerlat", "top", "-s", "20"); + + ck_assert_int_eq(tlat_params->print_stack, 20); +} +END_TEST + +START_TEST(test_stack_long) +{ + PARSE_ARGS("timerlat", "top", "--stack", "20"); + + ck_assert_int_eq(tlat_params->print_stack, 20); +} +END_TEST + +START_TEST(test_thread_short) +{ + PARSE_ARGS("timerlat", "top", "-T", "20"); + + ck_assert_int_eq(params->stop_total_us, 20); +} +END_TEST + +START_TEST(test_thread_long) +{ + PARSE_ARGS("timerlat", "top", "--thread", "20"); + + ck_assert_int_eq(params->stop_total_us, 20); +} +END_TEST + +/* Event Configuration */ + +START_TEST(test_event_short) +{ + PARSE_ARGS("timerlat", "top", "-e", "system:event"); + + CLI_ASSERT_SINGLE_EVENT("system", "event"); +} +END_TEST + +START_TEST(test_event_long) +{ + PARSE_ARGS("timerlat", "top", "--event", "system:event"); + + CLI_ASSERT_SINGLE_EVENT("system", "event"); +} +END_TEST + +START_TEST(test_filter) +{ + PARSE_ARGS("timerlat", "top", "-e", "system:event", "--filter", "filter"); + + CLI_ASSERT_SINGLE_FILTER("filter"); +} +END_TEST + +START_TEST(test_trigger) +{ + PARSE_ARGS("timerlat", "top", "-e", "system:event", "--trigger", "trigger"); + + CLI_ASSERT_SINGLE_TRIGGER("trigger"); +} +END_TEST + +START_TEST(test_trace_short_noarg) +{ + PARSE_ARGS("timerlat", "top", "-t"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "timerlat_trace.txt"); +} +END_TEST + +START_TEST(test_trace_short_followarg) +{ + PARSE_ARGS("timerlat", "top", "-t", "-d", "20"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "timerlat_trace.txt"); + ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */ +} +END_TEST + +START_TEST(test_trace_short_space) +{ + PARSE_ARGS("timerlat", "top", "-t", "tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +START_TEST(test_trace_short_equals) +{ + PARSE_ARGS("timerlat", "top", "-t=tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +START_TEST(test_trace_long_noarg) +{ + PARSE_ARGS("timerlat", "top", "--trace"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "timerlat_trace.txt"); +} +END_TEST + +START_TEST(test_trace_long_followarg) +{ + PARSE_ARGS("timerlat", "top", "--trace", "-d", "20"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "timerlat_trace.txt"); + ck_assert_int_eq(params->duration, 20); /* check if next argument is read correctly */ +} +END_TEST + +START_TEST(test_trace_long_space) +{ + PARSE_ARGS("timerlat", "top", "--trace", "tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +START_TEST(test_trace_long_equals) +{ + PARSE_ARGS("timerlat", "top", "--trace=tracefile"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "tracefile"); +} +END_TEST + +/* CPU Configuration */ + +START_TEST(test_cpus_short) +{ + nr_cpus = 4; + + PARSE_ARGS("timerlat", "top", "-c", "0-1,3"); + + ck_assert_str_eq(params->cpus, "0-1,3"); + CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3); +} +END_TEST + +START_TEST(test_cpus_long) +{ + nr_cpus = 4; + + PARSE_ARGS("timerlat", "top", "--cpus", "0-1,3"); + + ck_assert_str_eq(params->cpus, "0-1,3"); + CLI_ASSERT_CPUSET(monitored_cpus, 0, 1, 3); +} +END_TEST + +START_TEST(test_housekeeping_short) +{ + nr_cpus = 4; + + PARSE_ARGS("timerlat", "top", "-H", "0-1,3"); + + CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3); +} +END_TEST + +START_TEST(test_housekeeping_long) +{ + nr_cpus = 4; + + PARSE_ARGS("timerlat", "top", "--house-keeping", "0-1,3"); + + CLI_ASSERT_CPUSET(hk_cpu_set, 0, 1, 3); +} +END_TEST + +/* Thread Configuration */ + +START_TEST(test_cgroup_short_noarg) +{ + PARSE_ARGS("timerlat", "top", "-C"); + + ck_assert(params->cgroup); + ck_assert_ptr_null(params->cgroup_name); +} +END_TEST + +START_TEST(test_cgroup_short_space) +{ + PARSE_ARGS("timerlat", "top", "-C", "cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_cgroup_short_equals) +{ + PARSE_ARGS("timerlat", "top", "-C=cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_cgroup_long_noarg) +{ + PARSE_ARGS("timerlat", "top", "--cgroup"); + + ck_assert(params->cgroup); + ck_assert_ptr_null(params->cgroup_name); +} +END_TEST + +START_TEST(test_cgroup_long_space) +{ + PARSE_ARGS("timerlat", "top", "--cgroup", "cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_cgroup_long_equals) +{ + PARSE_ARGS("timerlat", "top", "--cgroup=cgroup"); + + ck_assert(params->cgroup); + ck_assert_str_eq(params->cgroup_name, "cgroup"); +} +END_TEST + +START_TEST(test_kernel_threads_short) +{ + PARSE_ARGS("timerlat", "top", "-k"); + + ck_assert(params->kernel_workload); + ck_assert(!params->user_workload); + ck_assert(!params->user_data); +} +END_TEST + +START_TEST(test_kernel_threads_long) +{ + PARSE_ARGS("timerlat", "top", "--kernel-threads"); + + ck_assert(params->kernel_workload); + ck_assert(!params->user_workload); + ck_assert(!params->user_data); +} +END_TEST + +START_TEST(test_priority_short) +{ + PARSE_ARGS("timerlat", "top", "-P", "f:95"); + + ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO); + ck_assert_int_eq(params->sched_param.sched_priority, 95); +} +END_TEST + +START_TEST(test_priority_long) +{ + PARSE_ARGS("timerlat", "top", "--priority", "f:95"); + + ck_assert_int_eq(params->sched_param.sched_policy, SCHED_FIFO); + ck_assert_int_eq(params->sched_param.sched_priority, 95); +} +END_TEST + +START_TEST(test_user_load_short) +{ + PARSE_ARGS("timerlat", "top", "-U"); + + ck_assert(!params->kernel_workload); + ck_assert(!params->user_workload); + ck_assert(params->user_data); +} +END_TEST + +START_TEST(test_user_load_long) +{ + PARSE_ARGS("timerlat", "top", "--user-load"); + + ck_assert(!params->kernel_workload); + ck_assert(!params->user_workload); + ck_assert(params->user_data); +} +END_TEST + +START_TEST(test_user_threads_short) +{ + PARSE_ARGS("timerlat", "top", "-u"); + + ck_assert(!params->kernel_workload); + ck_assert(params->user_workload); + ck_assert(params->user_data); +} +END_TEST + +START_TEST(test_user_threads_long) +{ + PARSE_ARGS("timerlat", "top", "--user-threads"); + + ck_assert(!params->kernel_workload); + ck_assert(params->user_workload); + ck_assert(params->user_data); +} +END_TEST + +START_TEST(test_aligned_short) +{ + PARSE_ARGS("timerlat", "top", "-A", "500"); + + ck_assert(tlat_params->timerlat_align); + ck_assert_int_eq(tlat_params->timerlat_align_us, 500); +} +END_TEST + +START_TEST(test_aligned_long) +{ + PARSE_ARGS("timerlat", "top", "--aligned", "500"); + + ck_assert(tlat_params->timerlat_align); + ck_assert_int_eq(tlat_params->timerlat_align_us, 500); +} +END_TEST + +/* Output */ + +START_TEST(test_nano_short) +{ + PARSE_ARGS("timerlat", "top", "-n"); + + ck_assert_int_eq(params->output_divisor, 1); +} +END_TEST + +START_TEST(test_nano_long) +{ + PARSE_ARGS("timerlat", "top", "--nano"); + + ck_assert_int_eq(params->output_divisor, 1); +} +END_TEST + +START_TEST(test_quiet_short) +{ + PARSE_ARGS("timerlat", "top", "-q"); + + ck_assert(params->quiet); +} +END_TEST + +START_TEST(test_quiet_long) +{ + PARSE_ARGS("timerlat", "top", "--quiet"); + + ck_assert(params->quiet); +} +END_TEST + +/* System Tuning */ + +START_TEST(test_deepest_idle_state) +{ + PARSE_ARGS("timerlat", "top", "--deepest-idle-state", "1"); + + ck_assert_int_eq(tlat_params->deepest_idle_state, 1); +} +END_TEST + +START_TEST(test_dma_latency) +{ + PARSE_ARGS("timerlat", "top", "--dma-latency", "10"); + + ck_assert_int_eq(tlat_params->dma_latency, 10); +} +END_TEST + +START_TEST(test_trace_buffer_size) +{ + PARSE_ARGS("timerlat", "top", "--trace-buffer-size", "200"); + + ck_assert_int_eq(params->buffer_size, 200); +} +END_TEST + +START_TEST(test_warm_up) +{ + PARSE_ARGS("timerlat", "top", "--warm-up", "5"); + + ck_assert_int_eq(params->warmup, 5); +} +END_TEST + +/* Auto Analysis and Actions */ + +START_TEST(test_auto) +{ + PARSE_ARGS("timerlat", "top", "-a", "20"); + + CLI_TIMERLAT_ASSERT_AUTO(20); +} +END_TEST + +START_TEST(test_aa_only) +{ + PARSE_ARGS("timerlat", "top", "--aa-only", "20"); + + CLI_TIMERLAT_ASSERT_AA_ONLY(20); +} +END_TEST + +START_TEST(test_bpf_action) +{ + PARSE_ARGS("timerlat", "top", "--bpf-action", "program"); + + ck_assert_str_eq(tlat_params->bpf_action_program, "program"); +} +END_TEST + +START_TEST(test_dump_tasks) +{ + PARSE_ARGS("timerlat", "top", "--dump-tasks"); + + ck_assert(tlat_params->dump_tasks); +} +END_TEST + +START_TEST(test_no_aa) +{ + PARSE_ARGS("timerlat", "top", "--no-aa"); + + ck_assert(tlat_params->no_aa); +} +END_TEST + +START_TEST(test_on_end) +{ + PARSE_ARGS("timerlat", "top", "--on-end", "trace"); + + CLI_ASSERT_SINGLE_ACTION(end_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "timerlat_trace.txt"); +} +END_TEST + +START_TEST(test_on_threshold) +{ + PARSE_ARGS("timerlat", "top", "--on-threshold", "trace"); + + CLI_ASSERT_SINGLE_ACTION(threshold_actions, ACTION_TRACE_OUTPUT, trace_output, str, + "timerlat_trace.txt"); +} +END_TEST + +START_TEST(test_stack_format) +{ + PARSE_ARGS("timerlat", "top", "--stack-format", "truncate"); + + ck_assert_int_eq(tlat_params->stack_format, STACK_FORMAT_TRUNCATE); +} +END_TEST + +/* General */ + +START_TEST(test_debug_short) +{ + PARSE_ARGS("timerlat", "top", "-D"); + + ck_assert(config_debug); +} +END_TEST + +START_TEST(test_debug_long) +{ + PARSE_ARGS("timerlat", "top", "--debug"); + + ck_assert(config_debug); +} +END_TEST + +START_TEST(test_duration_short) +{ + PARSE_ARGS("timerlat", "top", "-d", "1m"); + + ck_assert_int_eq(params->duration, 60); +} +END_TEST + +START_TEST(test_duration_long) +{ + PARSE_ARGS("timerlat", "top", "--duration", "1m"); + + ck_assert_int_eq(params->duration, 60); +} +END_TEST + +Suite *timerlat_top_cli_suite(void) +{ + Suite *s = suite_create("timerlat_top_cli"); + TCase *tc; + + tc = tcase_create("tracing_options"); + tcase_add_test(tc, test_irq_short); + tcase_add_test(tc, test_irq_long); + tcase_add_test(tc, test_period_short); + tcase_add_test(tc, test_period_long); + tcase_add_test(tc, test_stack_short); + tcase_add_test(tc, test_stack_long); + tcase_add_test(tc, test_thread_short); + tcase_add_test(tc, test_thread_long); + tcase_add_test(tc, test_trace_short_noarg); + tcase_add_test(tc, test_trace_short_followarg); + tcase_add_test(tc, test_trace_short_space); + tcase_add_test(tc, test_trace_short_equals); + tcase_add_test(tc, test_trace_long_noarg); + tcase_add_test(tc, test_trace_long_followarg); + tcase_add_test(tc, test_trace_long_space); + tcase_add_test(tc, test_trace_long_equals); + suite_add_tcase(s, tc); + + tc = tcase_create("event_configuration"); + tcase_add_test(tc, test_event_short); + tcase_add_test(tc, test_event_long); + tcase_add_test(tc, test_filter); + tcase_add_test(tc, test_trigger); + suite_add_tcase(s, tc); + + tc = tcase_create("cpu_configuration"); + tcase_add_test(tc, test_cpus_short); + tcase_add_test(tc, test_cpus_long); + tcase_add_test(tc, test_housekeeping_short); + tcase_add_test(tc, test_housekeeping_long); + suite_add_tcase(s, tc); + + tc = tcase_create("thread_configuration"); + tcase_add_test(tc, test_cgroup_short_noarg); + tcase_add_test(tc, test_cgroup_short_space); + tcase_add_test(tc, test_cgroup_short_equals); + tcase_add_test(tc, test_cgroup_long_noarg); + tcase_add_test(tc, test_cgroup_long_space); + tcase_add_test(tc, test_cgroup_long_equals); + tcase_add_test(tc, test_kernel_threads_short); + tcase_add_test(tc, test_kernel_threads_long); + tcase_add_test(tc, test_priority_short); + tcase_add_test(tc, test_priority_long); + tcase_add_test(tc, test_user_load_short); + tcase_add_test(tc, test_user_load_long); + tcase_add_test(tc, test_user_threads_short); + tcase_add_test(tc, test_user_threads_long); + tcase_add_test(tc, test_aligned_short); + tcase_add_test(tc, test_aligned_long); + suite_add_tcase(s, tc); + + tc = tcase_create("output"); + tcase_add_test(tc, test_nano_short); + tcase_add_test(tc, test_nano_long); + tcase_add_test(tc, test_quiet_short); + tcase_add_test(tc, test_quiet_long); + suite_add_tcase(s, tc); + + tc = tcase_create("system_tuning"); + tcase_add_test(tc, test_deepest_idle_state); + tcase_add_test(tc, test_dma_latency); + tcase_add_test(tc, test_trace_buffer_size); + tcase_add_test(tc, test_warm_up); + suite_add_tcase(s, tc); + + tc = tcase_create("aa_actions"); + tcase_add_test(tc, test_auto); + tcase_add_test(tc, test_aa_only); + tcase_add_test(tc, test_bpf_action); + tcase_add_test(tc, test_dump_tasks); + tcase_add_test(tc, test_no_aa); + tcase_add_test(tc, test_on_end); + tcase_add_test(tc, test_on_threshold); + tcase_add_test(tc, test_stack_format); + suite_add_tcase(s, tc); + + tc = tcase_create("general"); + tcase_add_test(tc, test_debug_short); + tcase_add_test(tc, test_debug_long); + tcase_add_test(tc, test_duration_short); + tcase_add_test(tc, test_duration_long); + suite_add_tcase(s, tc); + + return s; +}
diff --git a/tools/tracing/rtla/tests/unit/unit_tests.c b/tools/tracing/rtla/tests/unit/unit_tests.c index f3c6d89..75ca813 100644 --- a/tools/tracing/rtla/tests/unit/unit_tests.c +++ b/tools/tracing/rtla/tests/unit/unit_tests.c
@@ -2,115 +2,35 @@ #define _GNU_SOURCE #include <check.h> -#include <stdio.h> -#include <stdlib.h> -#include <sched.h> -#include <limits.h> -#include <unistd.h> -#include <sys/sysinfo.h> +#include <stdbool.h> #include "../../src/utils.h" -int nr_cpus; +#include "../../src/cli.h" -START_TEST(test_strtoi) -{ - int result; - char buf[64]; +Suite *utils_suite(void); +Suite *actions_suite(void); +Suite *osnoise_top_cli_suite(void); +Suite *osnoise_hist_cli_suite(void); +Suite *timerlat_top_cli_suite(void); +Suite *timerlat_hist_cli_suite(void); +Suite *cli_opt_callback_suite(void); - ck_assert_int_eq(strtoi("123", &result), 0); - ck_assert_int_eq(result, 123); - ck_assert_int_eq(strtoi(" -456", &result), 0); - ck_assert_int_eq(result, -456); - - snprintf(buf, sizeof(buf), "%d", INT_MAX); - ck_assert_int_eq(strtoi(buf, &result), 0); - snprintf(buf, sizeof(buf), "%ld", (long)INT_MAX + 1); - ck_assert_int_eq(strtoi(buf, &result), -1); - - ck_assert_int_eq(strtoi("", &result), -1); - ck_assert_int_eq(strtoi("123abc", &result), -1); - ck_assert_int_eq(strtoi("123 ", &result), -1); -} -END_TEST - -START_TEST(test_parse_cpu_set) -{ - cpu_set_t set; - - nr_cpus = 8; - ck_assert_int_eq(parse_cpu_set("0", &set), 0); - ck_assert(CPU_ISSET(0, &set)); - ck_assert(!CPU_ISSET(1, &set)); - - ck_assert_int_eq(parse_cpu_set("0,2", &set), 0); - ck_assert(CPU_ISSET(0, &set)); - ck_assert(CPU_ISSET(2, &set)); - - ck_assert_int_eq(parse_cpu_set("0-3", &set), 0); - ck_assert(CPU_ISSET(0, &set)); - ck_assert(CPU_ISSET(1, &set)); - ck_assert(CPU_ISSET(2, &set)); - ck_assert(CPU_ISSET(3, &set)); - - ck_assert_int_eq(parse_cpu_set("1-3,5", &set), 0); - ck_assert(!CPU_ISSET(0, &set)); - ck_assert(CPU_ISSET(1, &set)); - ck_assert(CPU_ISSET(2, &set)); - ck_assert(CPU_ISSET(3, &set)); - ck_assert(!CPU_ISSET(4, &set)); - ck_assert(CPU_ISSET(5, &set)); - - ck_assert_int_eq(parse_cpu_set("-1", &set), 1); - ck_assert_int_eq(parse_cpu_set("abc", &set), 1); - ck_assert_int_eq(parse_cpu_set("9999", &set), 1); -} -END_TEST - -START_TEST(test_parse_prio) -{ - struct sched_attr attr; - - ck_assert_int_eq(parse_prio("f:50", &attr), 0); - ck_assert_uint_eq(attr.sched_policy, SCHED_FIFO); - ck_assert_uint_eq(attr.sched_priority, 50U); - - ck_assert_int_eq(parse_prio("r:30", &attr), 0); - ck_assert_uint_eq(attr.sched_policy, SCHED_RR); - - ck_assert_int_eq(parse_prio("o:0", &attr), 0); - ck_assert_uint_eq(attr.sched_policy, SCHED_OTHER); - ck_assert_int_eq(attr.sched_nice, 0); - - ck_assert_int_eq(parse_prio("d:10ms:100ms", &attr), 0); - ck_assert_uint_eq(attr.sched_policy, 6U); - - ck_assert_int_eq(parse_prio("f:999", &attr), -1); - ck_assert_int_eq(parse_prio("o:-20", &attr), -1); - ck_assert_int_eq(parse_prio("d:100ms:10ms", &attr), -1); - ck_assert_int_eq(parse_prio("x:50", &attr), -1); -} -END_TEST - -Suite *utils_suite(void) -{ - Suite *s = suite_create("utils"); - TCase *tc = tcase_create("core"); - - tcase_add_test(tc, test_strtoi); - tcase_add_test(tc, test_parse_cpu_set); - tcase_add_test(tc, test_parse_prio); - - suite_add_tcase(s, tc); - return s; -} - -int main(void) +int main(int argc, char *argv[]) { int num_failed; SRunner *sr; + in_unit_test = true; + sr = srunner_create(utils_suite()); - srunner_run_all(sr, CK_NORMAL); + srunner_add_suite(sr, cli_opt_callback_suite()); + srunner_add_suite(sr, actions_suite()); + srunner_add_suite(sr, osnoise_top_cli_suite()); + srunner_add_suite(sr, osnoise_hist_cli_suite()); + srunner_add_suite(sr, timerlat_top_cli_suite()); + srunner_add_suite(sr, timerlat_hist_cli_suite()); + + srunner_run_all(sr, CK_VERBOSE); num_failed = srunner_ntests_failed(sr); srunner_free(sr);
diff --git a/tools/tracing/rtla/tests/unit/utils.c b/tools/tracing/rtla/tests/unit/utils.c new file mode 100644 index 0000000..ce53cab --- /dev/null +++ b/tools/tracing/rtla/tests/unit/utils.c
@@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <check.h> +#include <stdio.h> +#include <stdlib.h> +#include <sched.h> +#include <limits.h> +#include <unistd.h> +#include <sys/sysinfo.h> + +#include "../../src/utils.h" + +extern int nr_cpus; + +START_TEST(test_strtoi) +{ + int result; + char buf[64]; + + ck_assert_int_eq(strtoi("123", &result), 0); + ck_assert_int_eq(result, 123); + ck_assert_int_eq(strtoi(" -456", &result), 0); + ck_assert_int_eq(result, -456); + + snprintf(buf, sizeof(buf), "%d", INT_MAX); + ck_assert_int_eq(strtoi(buf, &result), 0); + snprintf(buf, sizeof(buf), "%ld", (long)INT_MAX + 1); + ck_assert_int_eq(strtoi(buf, &result), -1); + + ck_assert_int_eq(strtoi("", &result), -1); + ck_assert_int_eq(strtoi("123abc", &result), -1); + ck_assert_int_eq(strtoi("123 ", &result), -1); +} +END_TEST + +START_TEST(test_parse_cpu_set) +{ + cpu_set_t set; + + nr_cpus = 8; + ck_assert_int_eq(parse_cpu_set("0", &set), 0); + ck_assert(CPU_ISSET(0, &set)); + ck_assert(!CPU_ISSET(1, &set)); + + ck_assert_int_eq(parse_cpu_set("0,2", &set), 0); + ck_assert(CPU_ISSET(0, &set)); + ck_assert(CPU_ISSET(2, &set)); + + ck_assert_int_eq(parse_cpu_set("0-3", &set), 0); + ck_assert(CPU_ISSET(0, &set)); + ck_assert(CPU_ISSET(1, &set)); + ck_assert(CPU_ISSET(2, &set)); + ck_assert(CPU_ISSET(3, &set)); + + ck_assert_int_eq(parse_cpu_set("1-3,5", &set), 0); + ck_assert(!CPU_ISSET(0, &set)); + ck_assert(CPU_ISSET(1, &set)); + ck_assert(CPU_ISSET(2, &set)); + ck_assert(CPU_ISSET(3, &set)); + ck_assert(!CPU_ISSET(4, &set)); + ck_assert(CPU_ISSET(5, &set)); + + ck_assert_int_eq(parse_cpu_set("-1", &set), 1); + ck_assert_int_eq(parse_cpu_set("abc", &set), 1); + ck_assert_int_eq(parse_cpu_set("9999", &set), 1); +} +END_TEST + +START_TEST(test_parse_prio) +{ + struct sched_attr attr; + + ck_assert_int_eq(parse_prio("f:50", &attr), 0); + ck_assert_uint_eq(attr.sched_policy, SCHED_FIFO); + ck_assert_uint_eq(attr.sched_priority, 50U); + + ck_assert_int_eq(parse_prio("r:30", &attr), 0); + ck_assert_uint_eq(attr.sched_policy, SCHED_RR); + + ck_assert_int_eq(parse_prio("o:0", &attr), 0); + ck_assert_uint_eq(attr.sched_policy, SCHED_OTHER); + ck_assert_int_eq(attr.sched_nice, 0); + + ck_assert_int_eq(parse_prio("d:10ms:100ms", &attr), 0); + ck_assert_uint_eq(attr.sched_policy, 6U); + + ck_assert_int_eq(parse_prio("f:999", &attr), -1); + ck_assert_int_eq(parse_prio("o:-20", &attr), -1); + ck_assert_int_eq(parse_prio("d:100ms:10ms", &attr), -1); + ck_assert_int_eq(parse_prio("x:50", &attr), -1); +} +END_TEST + +Suite *utils_suite(void) +{ + Suite *s = suite_create("utils"); + TCase *tc = tcase_create("core"); + + tcase_add_test(tc, test_strtoi); + tcase_add_test(tc, test_parse_cpu_set); + tcase_add_test(tc, test_parse_prio); + + suite_add_tcase(s, tc); + return s; +}