| // 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); |
| } |