projects/linux·blob master · refs

854 lines · 21.5 KiB

1/* SPDX-License-Identifier: GPL-2.0 */
2#pragma once
3
4#ifndef RTLA_ALLOW_CLI_P_H
5#error "Private header file included outside of cli.c module"
6#endif
7
8#include <errno.h>
9#include <limits.h>
10#include <linux/kernel.h>
11#include <subcmd/parse-options.h>
12
13#include "cli.h"
14#include "osnoise.h"
15#include "timerlat.h"
16
17struct osnoise_cb_data {
18 struct osnoise_params *params;
19 char *trace_output;
20};
21
22struct timerlat_cb_data {
23 struct timerlat_params *params;
24 char *trace_output;
25};
26
27/*
28 * Non-zero default values for parameters
29 */
30static const int default_dma_latency = -1; /* -1 = unset */
31static const int default_deepest_idle_state = -2; /* -1 = disable all, -2 = unset */
32static const int default_output_divisor = 1000;
33static const int default_bucket_size = 1;
34static const int default_entries = 256;
35static const enum stack_format default_stack_format = STACK_FORMAT_TRUNCATE;
36
37/*
38 * Range checking for long long and int option callbacks.
39 *
40 * Pass a pointer to a const struct as opt->data to enable range checking.
41 * If opt->data is NULL, no range check is performed.
42 */
43struct llong_range {
44 long long min;
45 long long max;
46};
47
48struct int_range {
49 int min;
50 int max;
51};
52
53#define LLONG_RANGE(lo, hi) \
54 (&(const struct llong_range){ .min = (lo), .max = (hi) })
55
56#define INT_RANGE(lo, hi) \
57 (&(const struct int_range){ .min = (lo), .max = (hi) })
58
59static int check_llong_range(const struct option *opt, long long value)
60{
61 const struct llong_range *range = opt->data;
62
63 if (!range)
64 return 0;
65 if (value < range->min || value > range->max) {
66 fprintf(stderr, " Error: --%s value %lld is out of range [%lld, %lld]\n",
67 opt->long_name, value, range->min, range->max);
68 return -1;
69 }
70 return 0;
71}
72
73static int check_int_range(const struct option *opt, int value)
74{
75 const struct int_range *range = opt->data;
76
77 if (!range)
78 return 0;
79 if (value < range->min || value > range->max) {
80 fprintf(stderr, " Error: --%s value %d is out of range [%d, %d]\n",
81 opt->long_name, value, range->min, range->max);
82 return -1;
83 }
84 return 0;
85}
86
87/*
88 * OPT_CALLBACK variant that populates .data (for range checking).
89 */
90#define RTLA_OPT_CALLBACK_DATA(s, l, v, a, h, f, d) \
91 { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), \
92 .value = (v), .argh = (a), .help = (h), .callback = (f), \
93 .data = (void *)(d) }
94
95#define RTLA_OPT_CALLBACK_DATA_DEFVAL(s, l, v, a, h, f, d, dv) \
96 { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), \
97 .value = (v), .argh = (a), .help = (h), .callback = (f), \
98 .data = (void *)(d), .defval = (intptr_t)(dv) }
99
100/*
101 * Shorthand macros for integer/long long command line options using
102 * opt_int_callback/opt_llong_callback, with variants that set defval
103 * and/or data (for range checking).
104 *
105 * Note: defval's type is intptr_t. opt_int_callback interprets it directly as
106 * an int, opt_llong_callback interprets it as a pointer to a long long, as
107 * long long does not fit into intptr_t on 32-bit architectures.
108 */
109#define RTLA_OPT_LLONG(s, l, v, a, h) \
110 OPT_CALLBACK(s, l, v, a, h, opt_llong_callback)
111
112#define RTLA_OPT_LLONG_DEFVAL(s, l, v, a, h, d) { .type = OPTION_CALLBACK, \
113 .short_name = (s), .long_name = (l), .value = (v), .argh = (a), \
114 .help = (h), .callback = opt_llong_callback, .defval = (intptr_t)(d) }
115
116#define RTLA_OPT_LLONG_DATA(s, l, v, a, h, d) { .type = OPTION_CALLBACK, \
117 .short_name = (s), .long_name = (l), .value = (v), .argh = (a), \
118 .help = (h), .callback = opt_llong_callback, .data = (void *)(d) }
119
120#define RTLA_OPT_INT(s, l, v, a, h) \
121 OPT_CALLBACK(s, l, v, a, h, opt_int_callback)
122
123#define RTLA_OPT_INT_DEFVAL(s, l, v, a, h, d) { .type = OPTION_CALLBACK, \
124 .short_name = (s), .long_name = (l), .value = (v), .argh = (a), \
125 .help = (h), .callback = opt_int_callback, .defval = (intptr_t)(d) }
126
127#define RTLA_OPT_INT_DATA_DEFVAL(s, l, v, a, h, d, dv) { .type = OPTION_CALLBACK, \
128 .short_name = (s), .long_name = (l), .value = (v), .argh = (a), \
129 .help = (h), .callback = opt_int_callback, \
130 .data = (void *)(d), .defval = (intptr_t)(dv) }
131
132/*
133 * Macros for command line options common to all tools
134 *
135 * Note: Some of the options are common to both timerlat and osnoise, but
136 * have a slightly different meaning. Such options take additional arguments
137 * that have to be provided by the *_parse_args() function of the corresponding
138 * tool.
139 *
140 * All macros defined here assume the presence of a params variable of
141 * the corresponding tool type (i.e struct timerlat_params or struct osnoise_params)
142 * and a cb_data variable of the matching type.
143 */
144
145 #define RTLA_OPT_STOP(short, long, name) OPT_CALLBACK_FLAG(short, long, \
146 &params->common.stop_us, \
147 "us", \
148 "stop trace if " name " is higher than the argument in us", \
149 opt_llong_callback, PARSE_OPT_NOAUTONEG)
150
151#define RTLA_OPT_STOP_TOTAL(short, long, name) OPT_CALLBACK_FLAG(short, long, \
152 &params->common.stop_total_us, \
153 "us", \
154 "stop trace if " name " is higher than the argument in us", \
155 opt_llong_callback, PARSE_OPT_NOAUTONEG)
156
157#define RTLA_OPT_TRACE_OUTPUT(tracer, cb) OPT_CALLBACK_OPTARG('t', "trace", \
158 (const char **)&cb_data.trace_output, \
159 tracer "_trace.txt", \
160 "[file]", \
161 "save the stopped trace to [file|" tracer "_trace.txt]", \
162 cb)
163
164#define RTLA_OPT_CPUS OPT_CALLBACK('c', "cpus", &params->common, \
165 "cpu-list", \
166 "run the tracer only on the given cpus", \
167 opt_cpus_cb)
168
169#define RTLA_OPT_CGROUP OPT_CALLBACK_OPTARG('C', "cgroup", &params->common, \
170 "[cgroup_name]", NULL, \
171 "set cgroup, no argument means rtla's cgroup will be inherited", \
172 opt_cgroup_cb)
173
174#define RTLA_OPT_USER_THREADS OPT_CALLBACK_NOOPT('u', "user-threads", params, NULL, \
175 "use rtla user-space threads instead of kernel-space timerlat threads", \
176 opt_user_threads_cb)
177
178#define RTLA_OPT_KERNEL_THREADS OPT_BOOLEAN('k', "kernel-threads", \
179 &params->common.kernel_workload, \
180 "use timerlat kernel-space threads instead of rtla user-space threads")
181
182#define RTLA_OPT_USER_LOAD OPT_BOOLEAN('U', "user-load", &params->common.user_data, \
183 "enable timerlat for user-defined user-space workload")
184
185#define RTLA_OPT_DURATION OPT_CALLBACK('d', "duration", &params->common, \
186 "time[s|m|h|d]", \
187 "set the duration of the session", \
188 opt_duration_cb)
189
190#define RTLA_OPT_EVENT OPT_CALLBACK('e', "event", &params->common.events, \
191 "sys:event", \
192 "enable the <sys:event> in the trace instance, multiple -e are allowed", \
193 opt_event_cb)
194
195#define RTLA_OPT_HOUSEKEEPING OPT_CALLBACK('H', "house-keeping", &params->common, \
196 "cpu-list", \
197 "run rtla control threads only on the given cpus", \
198 opt_housekeeping_cb)
199
200#define RTLA_OPT_PRIORITY OPT_CALLBACK('P', "priority", &params->common, \
201 "o:prio|r:prio|f:prio|d:runtime:period", \
202 "set scheduling parameters", \
203 opt_priority_cb)
204
205#define RTLA_OPT_TRIGGER OPT_CALLBACK(0, "trigger", &params->common.events, \
206 "trigger", \
207 "enable a trace event trigger to the previous -e event", \
208 opt_trigger_cb)
209
210#define RTLA_OPT_FILTER OPT_CALLBACK(0, "filter", &params->common.events, \
211 "filter", \
212 "enable a trace event filter to the previous -e event", \
213 opt_filter_cb)
214
215#define RTLA_OPT_QUIET OPT_BOOLEAN('q', "quiet", &params->common.quiet, \
216 "print only a summary at the end")
217
218#define RTLA_OPT_TRACE_BUFFER_SIZE RTLA_OPT_INT(0, "trace-buffer-size", \
219 &params->common.buffer_size, "kB", \
220 "set the per-cpu trace buffer size in kB")
221
222#define RTLA_OPT_WARM_UP RTLA_OPT_INT(0, "warm-up", &params->common.warmup, "s", \
223 "let the workload run for s seconds before collecting data")
224
225#define RTLA_OPT_AUTO(cb) OPT_CALLBACK('a', "auto", &cb_data, "us", \
226 "set automatic trace mode, stopping the session if argument in us sample is hit", \
227 cb)
228
229#define RTLA_OPT_ON_THRESHOLD(threshold, cb) OPT_CALLBACK(0, "on-threshold", \
230 &params->common.threshold_actions, \
231 "action", \
232 "define action to be executed at " threshold " threshold, multiple are allowed", \
233 cb)
234
235#define RTLA_OPT_ON_END(cb) OPT_CALLBACK(0, "on-end", &params->common.end_actions, \
236 "action", \
237 "define action to be executed at measurement end, multiple are allowed", \
238 cb)
239
240#define RTLA_OPT_DEBUG OPT_BOOLEAN('D', "debug", &config_debug, \
241 "print debug info")
242
243/*
244 * Helper functions for parsing numeric option arguments.
245 */
246static void opt_err(const struct option *opt, const char *arg, const char *msg)
247{
248 fprintf(stderr, " Error: --%s: '%s' %s\n", opt->long_name, arg, msg);
249}
250
251static int strtoll_safe(const struct option *opt, const char *arg, long long *value)
252{
253 long long tmp;
254 char *end;
255
256 errno = 0;
257 tmp = strtoll(arg, &end, 10);
258 if (errno || *end || end == arg) {
259 opt_err(opt, arg, "is not a valid number");
260 return -1;
261 }
262 *value = tmp;
263 return 0;
264}
265
266static int strtoi_safe(const struct option *opt, const char *arg, int *value)
267{
268 int tmp;
269
270 if (strtoi(arg, &tmp)) {
271 opt_err(opt, arg, "is not a valid number");
272 return -1;
273 }
274 *value = tmp;
275 return 0;
276}
277
278/*
279 * Common callback functions for command line options
280 */
281
282static int opt_llong_callback(const struct option *opt, const char *arg, int unset)
283{
284 long long *value = opt->value;
285
286 if (unset) {
287 *value = opt->defval ? *(long long *)opt->defval : 0;
288 return 0;
289 }
290
291 if (!arg)
292 return -1;
293
294 if (strtoll_safe(opt, arg, value))
295 return -1;
296 if (check_llong_range(opt, *value))
297 return -1;
298 return 0;
299}
300
301static int opt_int_callback(const struct option *opt, const char *arg, int unset)
302{
303 int *value = opt->value;
304
305 if (unset) {
306 *value = (int)opt->defval;
307 return 0;
308 }
309
310 if (!arg)
311 return -1;
312
313 if (strtoi_safe(opt, arg, value))
314 return -1;
315 if (check_int_range(opt, *value))
316 return -1;
317
318 return 0;
319}
320
321static int opt_cpus_cb(const struct option *opt, const char *arg, int unset)
322{
323 struct common_params *params = opt->value;
324 int retval;
325
326 if (unset) {
327 CPU_ZERO(&params->monitored_cpus);
328 params->cpus = NULL;
329 return 0;
330 }
331
332 if (!arg)
333 return -1;
334
335 retval = parse_cpu_set((char *)arg, &params->monitored_cpus);
336 if (retval) {
337 opt_err(opt, arg, "is not a valid cpu set");
338 return -1;
339 }
340 params->cpus = (char *)arg;
341
342 return 0;
343}
344
345static int opt_cgroup_cb(const struct option *opt, const char *arg, int unset)
346{
347 struct common_params *params = opt->value;
348
349 if (unset) {
350 params->cgroup = 0;
351 params->cgroup_name = NULL;
352 return 0;
353 }
354
355 params->cgroup = 1;
356 params->cgroup_name = (char *)arg;
357 if (params->cgroup_name && params->cgroup_name[0] == '=')
358 /* Allow -C=<cgroup_name> next to -C[ ]<cgroup_name> */
359 ++params->cgroup_name;
360
361 return 0;
362}
363
364static int opt_duration_cb(const struct option *opt, const char *arg, int unset)
365{
366 struct common_params *params = opt->value;
367
368 if (unset) {
369 params->duration = 0;
370 return 0;
371 }
372
373 if (!arg)
374 return -1;
375
376 params->duration = parse_seconds_duration((char *)arg);
377 if (!params->duration) {
378 opt_err(opt, arg, "is not a valid duration");
379 return -1;
380 }
381
382 return 0;
383}
384
385static int opt_event_cb(const struct option *opt, const char *arg, int unset)
386{
387 struct trace_events **events = opt->value;
388 struct trace_events *tevent;
389
390 if (unset || !arg)
391 return -1;
392
393 tevent = trace_event_alloc((char *)arg);
394 if (!tevent)
395 fatal("Error alloc trace event");
396
397 if (*events)
398 tevent->next = *events;
399 *events = tevent;
400
401 return 0;
402}
403
404static int opt_housekeeping_cb(const struct option *opt, const char *arg, int unset)
405{
406 struct common_params *params = opt->value;
407 int retval;
408
409 if (unset) {
410 params->hk_cpus = 0;
411 CPU_ZERO(&params->hk_cpu_set);
412 return 0;
413 }
414
415 if (!arg)
416 return -1;
417
418 params->hk_cpus = 1;
419 retval = parse_cpu_set((char *)arg, &params->hk_cpu_set);
420 if (retval) {
421 opt_err(opt, arg, "is not a valid cpu set");
422 return -1;
423 }
424
425 return 0;
426}
427
428static int opt_priority_cb(const struct option *opt, const char *arg, int unset)
429{
430 struct common_params *params = opt->value;
431 int retval;
432
433 if (unset) {
434 memset(&params->sched_param, 0, sizeof(params->sched_param));
435 params->set_sched = 0;
436 return 0;
437 }
438
439 if (!arg)
440 return -1;
441
442 retval = parse_prio((char *)arg, &params->sched_param);
443 if (retval == -1) {
444 opt_err(opt, arg, "is not a valid priority");
445 return -1;
446 }
447 params->set_sched = 1;
448
449 return 0;
450}
451
452static int opt_trigger_cb(const struct option *opt, const char *arg, int unset)
453{
454 struct trace_events **events = opt->value;
455
456 if (unset || !arg)
457 return -1;
458
459 if (!*events) {
460 opt_err(opt, arg, "has no previous event to apply to");
461 return -1;
462 }
463
464 trace_event_add_trigger(*events, (char *)arg);
465
466 return 0;
467}
468
469static int opt_filter_cb(const struct option *opt, const char *arg, int unset)
470{
471 struct trace_events **events = opt->value;
472
473 if (unset || !arg)
474 return -1;
475
476 if (!*events) {
477 opt_err(opt, arg, "has no previous event to apply to");
478 return -1;
479 }
480
481 trace_event_add_filter(*events, (char *)arg);
482
483 return 0;
484}
485
486/*
487 * Macros for command line options specific to osnoise
488 */
489#define OSNOISE_OPT_PERIOD RTLA_OPT_LLONG_DATA('p', "period", &params->period, "us", \
490 "osnoise period in us", \
491 LLONG_RANGE(1, 10000000))
492
493#define OSNOISE_OPT_RUNTIME RTLA_OPT_LLONG_DATA('r', "runtime", &params->runtime, "us", \
494 "osnoise runtime in us", \
495 LLONG_RANGE(100, LLONG_MAX))
496
497#define OSNOISE_OPT_THRESHOLD RTLA_OPT_LLONG('T', "threshold", &params->threshold, "us", \
498 "the minimum delta to be considered a noise")
499
500/*
501 * Callback functions for command line options for osnoise tools
502 */
503
504static int opt_osnoise_auto_cb(const struct option *opt, const char *arg, int unset)
505{
506 struct osnoise_cb_data *cb_data = opt->value;
507 struct osnoise_params *params = cb_data->params;
508 long long auto_thresh;
509
510 if (unset) {
511 params->common.stop_us = 0;
512 params->threshold = 0;
513 cb_data->trace_output = NULL;
514 return 0;
515 }
516
517 if (!arg)
518 return -1;
519
520 if (strtoll_safe(opt, arg, &auto_thresh))
521 return -1;
522 params->common.stop_us = auto_thresh;
523 params->threshold = 1;
524
525 if (!cb_data->trace_output)
526 cb_data->trace_output = "osnoise_trace.txt";
527
528 return 0;
529}
530
531static int opt_osnoise_trace_output_cb(const struct option *opt, const char *arg, int unset)
532{
533 const char **trace_output = opt->value;
534
535 if (unset) {
536 *trace_output = NULL;
537 return 0;
538 }
539
540 if (!arg) {
541 *trace_output = "osnoise_trace.txt";
542 } else {
543 *trace_output = (char *)arg;
544 if (*trace_output && (*trace_output)[0] == '=')
545 /* Allow -t=<trace_output> next to -t[ ]<trace_output> */
546 ++*trace_output;
547 }
548
549 return 0;
550}
551
552static int opt_osnoise_on_threshold_cb(const struct option *opt, const char *arg, int unset)
553{
554 struct actions *actions = opt->value;
555 int retval;
556
557 if (unset || !arg)
558 return -1;
559
560 retval = actions_parse(actions, (char *)arg, "osnoise_trace.txt");
561 if (retval) {
562 opt_err(opt, arg, "is not a valid action");
563 return -1;
564 }
565
566 return 0;
567}
568
569static int opt_osnoise_on_end_cb(const struct option *opt, const char *arg, int unset)
570{
571 struct actions *actions = opt->value;
572 int retval;
573
574 if (unset || !arg)
575 return -1;
576
577 retval = actions_parse(actions, (char *)arg, "osnoise_trace.txt");
578 if (retval) {
579 opt_err(opt, arg, "is not a valid action");
580 return -1;
581 }
582
583 return 0;
584}
585
586/*
587 * Macros for command line options specific to timerlat
588 */
589#define TIMERLAT_OPT_PERIOD RTLA_OPT_LLONG_DATA('p', "period", &params->timerlat_period_us, "us", \
590 "timerlat period in us", \
591 LLONG_RANGE(100, 1000000))
592
593#define TIMERLAT_OPT_STACK RTLA_OPT_LLONG('s', "stack", &params->print_stack, "us", \
594 "save the stack trace at the IRQ if a thread latency is higher than the argument in us")
595
596#define TIMERLAT_OPT_NANO OPT_CALLBACK_NOOPT('n', "nano", params, NULL, \
597 "display data in nanoseconds", \
598 opt_nano_cb)
599
600#define TIMERLAT_OPT_DMA_LATENCY RTLA_OPT_INT_DATA_DEFVAL(0, "dma-latency", \
601 &params->dma_latency, "us", \
602 "set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", \
603 INT_RANGE(0, 10000), default_dma_latency)
604
605#define TIMERLAT_OPT_DEEPEST_IDLE_STATE RTLA_OPT_INT_DATA_DEFVAL(0, "deepest-idle-state", \
606 &params->deepest_idle_state, "n", \
607 "only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", \
608 INT_RANGE(-1, INT_MAX), default_deepest_idle_state)
609
610#define TIMERLAT_OPT_AA_ONLY OPT_CALLBACK(0, "aa-only", params, "us", \
611 "stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)", \
612 opt_aa_only_cb)
613
614#define TIMERLAT_OPT_NO_AA OPT_BOOLEAN(0, "no-aa", &params->no_aa, \
615 "disable auto-analysis, reducing rtla timerlat cpu usage")
616
617#define TIMERLAT_OPT_DUMPS_TASKS OPT_BOOLEAN(0, "dump-tasks", &params->dump_tasks, \
618 "prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)")
619
620#define TIMERLAT_OPT_BPF_ACTION OPT_STRING(0, "bpf-action", &params->bpf_action_program, \
621 "program", \
622 "load and execute BPF program when latency threshold is exceeded")
623
624#define TIMERLAT_OPT_STACK_FORMAT OPT_CALLBACK(0, "stack-format", &params->stack_format, "format", \
625 "set the stack format (truncate, skip, full)", \
626 opt_stack_format_cb)
627
628#define TIMERLAT_OPT_ALIGNED RTLA_OPT_CALLBACK_DATA('A', "aligned", params, "us", \
629 "align thread wakeups to a specific offset", \
630 opt_timerlat_align_cb, LLONG_RANGE(0, LLONG_MAX))
631
632/*
633 * Callback functions for command line options for timerlat tools
634 */
635
636static int opt_timerlat_auto_cb(const struct option *opt, const char *arg, int unset)
637{
638 struct timerlat_cb_data *cb_data = opt->value;
639 struct timerlat_params *params = cb_data->params;
640 long long auto_thresh;
641
642 if (unset) {
643 params->common.stop_total_us = 0;
644 params->common.stop_us = 0;
645 params->print_stack = 0;
646 cb_data->trace_output = NULL;
647 return 0;
648 }
649
650 if (!arg)
651 return -1;
652
653 if (strtoll_safe(opt, arg, &auto_thresh))
654 return -1;
655 params->common.stop_total_us = auto_thresh;
656 params->common.stop_us = auto_thresh;
657 params->print_stack = auto_thresh;
658
659 if (!cb_data->trace_output)
660 cb_data->trace_output = "timerlat_trace.txt";
661
662 return 0;
663}
664
665static int opt_aa_only_cb(const struct option *opt, const char *arg, int unset)
666{
667 struct timerlat_params *params = opt->value;
668 long long auto_thresh;
669
670 if (unset) {
671 params->common.stop_total_us = 0;
672 params->common.stop_us = 0;
673 params->print_stack = 0;
674 params->common.aa_only = 0;
675 return 0;
676 }
677
678 if (!arg)
679 return -1;
680
681 if (strtoll_safe(opt, arg, &auto_thresh))
682 return -1;
683 params->common.stop_total_us = auto_thresh;
684 params->common.stop_us = auto_thresh;
685 params->print_stack = auto_thresh;
686 params->common.aa_only = 1;
687
688 return 0;
689}
690
691static int opt_timerlat_trace_output_cb(const struct option *opt, const char *arg, int unset)
692{
693 const char **trace_output = opt->value;
694
695 if (unset) {
696 *trace_output = NULL;
697 return 0;
698 }
699
700 if (!arg) {
701 *trace_output = "timerlat_trace.txt";
702 } else {
703 *trace_output = (char *)arg;
704 if (*trace_output && (*trace_output)[0] == '=')
705 /* Allow -t=<trace_output> next to -t[ ]<trace_output> */
706 ++*trace_output;
707 }
708
709 return 0;
710}
711
712static int opt_timerlat_on_threshold_cb(const struct option *opt, const char *arg, int unset)
713{
714 struct actions *actions = opt->value;
715 int retval;
716
717 if (unset || !arg)
718 return -1;
719
720 retval = actions_parse(actions, (char *)arg, "timerlat_trace.txt");
721 if (retval) {
722 opt_err(opt, arg, "is not a valid action");
723 return -1;
724 }
725
726 return 0;
727}
728
729static int opt_timerlat_on_end_cb(const struct option *opt, const char *arg, int unset)
730{
731 struct actions *actions = opt->value;
732 int retval;
733
734 if (unset || !arg)
735 return -1;
736
737 retval = actions_parse(actions, (char *)arg, "timerlat_trace.txt");
738 if (retval) {
739 opt_err(opt, arg, "is not a valid action");
740 return -1;
741 }
742
743 return 0;
744}
745
746static int opt_user_threads_cb(const struct option *opt, const char *arg, int unset)
747{
748 struct timerlat_params *params = opt->value;
749
750 if (unset) {
751 params->common.user_workload = false;
752 params->common.user_data = false;
753 return 0;
754 }
755
756 params->common.user_workload = true;
757 params->common.user_data = true;
758
759 return 0;
760}
761
762static int opt_nano_cb(const struct option *opt, const char *arg, int unset)
763{
764 struct timerlat_params *params = opt->value;
765
766 if (unset) {
767 params->common.output_divisor = default_output_divisor;
768 return 0;
769 }
770
771 params->common.output_divisor = 1;
772
773 return 0;
774}
775
776static int opt_stack_format_cb(const struct option *opt, const char *arg, int unset)
777{
778 int *format = opt->value;
779
780 if (unset) {
781 *format = default_stack_format;
782 return 0;
783 }
784
785 if (!arg)
786 return -1;
787
788 *format = parse_stack_format((char *)arg);
789
790 if (*format == -1) {
791 opt_err(opt, arg, "is not a valid stack format");
792 return -1;
793 }
794
795 return 0;
796}
797
798static int opt_timerlat_align_cb(const struct option *opt, const char *arg, int unset)
799{
800 struct timerlat_params *params = opt->value;
801 long long val;
802
803 if (unset) {
804 params->timerlat_align = false;
805 params->timerlat_align_us = 0;
806 return 0;
807 }
808
809 if (!arg)
810 return -1;
811
812 if (strtoll_safe(opt, arg, &val))
813 return -1;
814 if (check_llong_range(opt, val))
815 return -1;
816
817 params->timerlat_align = true;
818 params->timerlat_align_us = val;
819
820 return 0;
821}
822
823/*
824 * Macros for command line options specific to histogram-based tools
825 */
826
827#define HIST_OPT_BUCKET_SIZE RTLA_OPT_INT_DATA_DEFVAL('b', "bucket-size", \
828 &params->common.hist.bucket_size, "N", \
829 "set the histogram bucket size (default 1)", \
830 INT_RANGE(1, 999999), default_bucket_size)
831
832#define HIST_OPT_ENTRIES RTLA_OPT_INT_DATA_DEFVAL('E', "entries", \
833 &params->common.hist.entries, "N", \
834 "set the number of entries of the histogram (default 256)", \
835 INT_RANGE(10, 9999999), default_entries)
836
837#define HIST_OPT_NO_IRQ OPT_BOOLEAN_FLAG(0, "no-irq", &params->common.hist.no_irq, \
838 "ignore IRQ latencies", PARSE_OPT_NOAUTONEG)
839
840#define HIST_OPT_NO_THREAD OPT_BOOLEAN_FLAG(0, "no-thread", &params->common.hist.no_thread, \
841 "ignore thread latencies", PARSE_OPT_NOAUTONEG)
842
843#define HIST_OPT_NO_HEADER OPT_BOOLEAN(0, "no-header", &params->common.hist.no_header, \
844 "do not print header")
845
846#define HIST_OPT_NO_SUMMARY OPT_BOOLEAN(0, "no-summary", &params->common.hist.no_summary, \
847 "do not print summary")
848
849#define HIST_OPT_NO_INDEX OPT_BOOLEAN(0, "no-index", &params->common.hist.no_index, \
850 "do not print index")
851
852#define HIST_OPT_WITH_ZEROS OPT_BOOLEAN(0, "with-zeros", &params->common.hist.with_zeros, \
853 "print zero only entries")
854