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 <linux/kernel.h> 9 #include <subcmd/parse-options.h> 10 11 #include "cli.h" 12 #include "osnoise.h" 13 #include "timerlat.h" 14 15 struct osnoise_cb_data { 16 struct osnoise_params *params; 17 char *trace_output; 18 }; 19 20 struct timerlat_cb_data { 21 struct timerlat_params *params; 22 char *trace_output; 23 }; 24 25 /* 26 * Macros for command line options common to all tools 27 * 28 * Note: Some of the options are common to both timerlat and osnoise, but 29 * have a slightly different meaning. Such options take additional arguments 30 * that have to be provided by the *_parse_args() function of the corresponding 31 * tool. 32 * 33 * All macros defined here assume the presence of a params variable of 34 * the corresponding tool type (i.e struct timerlat_params or struct osnoise_params) 35 * and a cb_data variable of the matching type. 36 */ 37 38 #define RTLA_OPT_STOP(short, long, name) OPT_CALLBACK_FLAG(short, long, \ 39 ¶ms->common.stop_us, \ 40 "us", \ 41 "stop trace if " name " is higher than the argument in us", \ 42 opt_llong_callback, PARSE_OPT_NOAUTONEG) 43 44 #define RTLA_OPT_STOP_TOTAL(short, long, name) OPT_CALLBACK_FLAG(short, long, \ 45 ¶ms->common.stop_total_us, \ 46 "us", \ 47 "stop trace if " name " is higher than the argument in us", \ 48 opt_llong_callback, PARSE_OPT_NOAUTONEG) 49 50 #define RTLA_OPT_TRACE_OUTPUT(tracer, cb) OPT_CALLBACK_OPTARG('t', "trace", \ 51 (const char **)&cb_data.trace_output, \ 52 tracer "_trace.txt", \ 53 "[file]", \ 54 "save the stopped trace to [file|" tracer "_trace.txt]", \ 55 cb) 56 57 #define RTLA_OPT_CPUS OPT_CALLBACK('c', "cpus", ¶ms->common, \ 58 "cpu-list", \ 59 "run the tracer only on the given cpus", \ 60 opt_cpus_cb) 61 62 #define RTLA_OPT_CGROUP OPT_CALLBACK_OPTARG('C', "cgroup", ¶ms->common, \ 63 "[cgroup_name]", NULL, \ 64 "set cgroup, no argument means rtla's cgroup will be inherited", \ 65 opt_cgroup_cb) 66 67 #define RTLA_OPT_USER_THREADS OPT_CALLBACK_NOOPT('u', "user-threads", params, NULL, \ 68 "use rtla user-space threads instead of kernel-space timerlat threads", \ 69 opt_user_threads_cb) 70 71 #define RTLA_OPT_KERNEL_THREADS OPT_BOOLEAN('k', "kernel-threads", \ 72 ¶ms->common.kernel_workload, \ 73 "use timerlat kernel-space threads instead of rtla user-space threads") 74 75 #define RTLA_OPT_USER_LOAD OPT_BOOLEAN('U', "user-load", ¶ms->common.user_data, \ 76 "enable timerlat for user-defined user-space workload") 77 78 #define RTLA_OPT_DURATION OPT_CALLBACK('d', "duration", ¶ms->common, \ 79 "time[s|m|h|d]", \ 80 "set the duration of the session", \ 81 opt_duration_cb) 82 83 #define RTLA_OPT_EVENT OPT_CALLBACK('e', "event", ¶ms->common.events, \ 84 "sys:event", \ 85 "enable the <sys:event> in the trace instance, multiple -e are allowed", \ 86 opt_event_cb) 87 88 #define RTLA_OPT_HOUSEKEEPING OPT_CALLBACK('H', "house-keeping", ¶ms->common, \ 89 "cpu-list", \ 90 "run rtla control threads only on the given cpus", \ 91 opt_housekeeping_cb) 92 93 #define RTLA_OPT_PRIORITY OPT_CALLBACK('P', "priority", ¶ms->common, \ 94 "o:prio|r:prio|f:prio|d:runtime:period", \ 95 "set scheduling parameters", \ 96 opt_priority_cb) 97 98 #define RTLA_OPT_TRIGGER OPT_CALLBACK(0, "trigger", ¶ms->common.events, \ 99 "trigger", \ 100 "enable a trace event trigger to the previous -e event", \ 101 opt_trigger_cb) 102 103 #define RTLA_OPT_FILTER OPT_CALLBACK(0, "filter", ¶ms->common.events, \ 104 "filter", \ 105 "enable a trace event filter to the previous -e event", \ 106 opt_filter_cb) 107 108 #define RTLA_OPT_QUIET OPT_BOOLEAN('q', "quiet", ¶ms->common.quiet, \ 109 "print only a summary at the end") 110 111 #define RTLA_OPT_TRACE_BUFFER_SIZE OPT_CALLBACK(0, "trace-buffer-size", \ 112 ¶ms->common.buffer_size, "kB", \ 113 "set the per-cpu trace buffer size in kB", \ 114 opt_int_callback) 115 116 #define RTLA_OPT_WARM_UP OPT_CALLBACK(0, "warm-up", ¶ms->common.warmup, "s", \ 117 "let the workload run for s seconds before collecting data", \ 118 opt_int_callback) 119 120 #define RTLA_OPT_AUTO(cb) OPT_CALLBACK('a', "auto", &cb_data, "us", \ 121 "set automatic trace mode, stopping the session if argument in us sample is hit", \ 122 cb) 123 124 #define RTLA_OPT_ON_THRESHOLD(threshold, cb) OPT_CALLBACK(0, "on-threshold", \ 125 ¶ms->common.threshold_actions, \ 126 "action", \ 127 "define action to be executed at " threshold " threshold, multiple are allowed", \ 128 cb) 129 130 #define RTLA_OPT_ON_END(cb) OPT_CALLBACK(0, "on-end", ¶ms->common.end_actions, \ 131 "action", \ 132 "define action to be executed at measurement end, multiple are allowed", \ 133 cb) 134 135 #define RTLA_OPT_DEBUG OPT_BOOLEAN('D', "debug", &config_debug, \ 136 "print debug info") 137 138 /* 139 * Common callback functions for command line options 140 */ 141 142 static int opt_llong_callback(const struct option *opt, const char *arg, int unset) 143 { 144 long long *value = opt->value; 145 146 if (unset || !arg) 147 return -1; 148 149 *value = get_llong_from_str((char *)arg); 150 return 0; 151 } 152 153 static int opt_int_callback(const struct option *opt, const char *arg, int unset) 154 { 155 int *value = opt->value; 156 157 if (unset || !arg) 158 return -1; 159 160 if (strtoi(arg, value)) 161 return -1; 162 163 return 0; 164 } 165 166 static int opt_cpus_cb(const struct option *opt, const char *arg, int unset) 167 { 168 struct common_params *params = opt->value; 169 int retval; 170 171 if (unset || !arg) 172 return -1; 173 174 retval = parse_cpu_set((char *)arg, ¶ms->monitored_cpus); 175 if (retval) 176 fatal("Invalid -c cpu list"); 177 params->cpus = (char *)arg; 178 179 return 0; 180 } 181 182 static int opt_cgroup_cb(const struct option *opt, const char *arg, int unset) 183 { 184 struct common_params *params = opt->value; 185 186 if (unset) 187 return -1; 188 189 params->cgroup = 1; 190 params->cgroup_name = (char *)arg; 191 if (params->cgroup_name && params->cgroup_name[0] == '=') 192 /* Allow -C=<cgroup_name> next to -C[ ]<cgroup_name> */ 193 ++params->cgroup_name; 194 195 return 0; 196 } 197 198 static int opt_duration_cb(const struct option *opt, const char *arg, int unset) 199 { 200 struct common_params *params = opt->value; 201 202 if (unset || !arg) 203 return -1; 204 205 params->duration = parse_seconds_duration((char *)arg); 206 if (!params->duration) 207 fatal("Invalid -d duration"); 208 209 return 0; 210 } 211 212 static int opt_event_cb(const struct option *opt, const char *arg, int unset) 213 { 214 struct trace_events **events = opt->value; 215 struct trace_events *tevent; 216 217 if (unset || !arg) 218 return -1; 219 220 tevent = trace_event_alloc((char *)arg); 221 if (!tevent) 222 fatal("Error alloc trace event"); 223 224 if (*events) 225 tevent->next = *events; 226 *events = tevent; 227 228 return 0; 229 } 230 231 static int opt_housekeeping_cb(const struct option *opt, const char *arg, int unset) 232 { 233 struct common_params *params = opt->value; 234 int retval; 235 236 if (unset || !arg) 237 return -1; 238 239 params->hk_cpus = 1; 240 retval = parse_cpu_set((char *)arg, ¶ms->hk_cpu_set); 241 if (retval) 242 fatal("Error parsing house keeping CPUs"); 243 244 return 0; 245 } 246 247 static int opt_priority_cb(const struct option *opt, const char *arg, int unset) 248 { 249 struct common_params *params = opt->value; 250 int retval; 251 252 if (unset || !arg) 253 return -1; 254 255 retval = parse_prio((char *)arg, ¶ms->sched_param); 256 if (retval == -1) 257 fatal("Invalid -P priority"); 258 params->set_sched = 1; 259 260 return 0; 261 } 262 263 static int opt_trigger_cb(const struct option *opt, const char *arg, int unset) 264 { 265 struct trace_events **events = opt->value; 266 267 if (unset || !arg) 268 return -1; 269 270 if (!*events) 271 fatal("--trigger requires a previous -e"); 272 273 trace_event_add_trigger(*events, (char *)arg); 274 275 return 0; 276 } 277 278 static int opt_filter_cb(const struct option *opt, const char *arg, int unset) 279 { 280 struct trace_events **events = opt->value; 281 282 if (unset || !arg) 283 return -1; 284 285 if (!*events) 286 fatal("--filter requires a previous -e"); 287 288 trace_event_add_filter(*events, (char *)arg); 289 290 return 0; 291 } 292 293 /* 294 * Macros for command line options specific to osnoise 295 */ 296 #define OSNOISE_OPT_PERIOD OPT_CALLBACK('p', "period", ¶ms->period, "us", \ 297 "osnoise period in us", \ 298 opt_osnoise_period_cb) 299 300 #define OSNOISE_OPT_RUNTIME OPT_CALLBACK('r', "runtime", ¶ms->runtime, "us", \ 301 "osnoise runtime in us", \ 302 opt_osnoise_runtime_cb) 303 304 #define OSNOISE_OPT_THRESHOLD OPT_CALLBACK('T', "threshold", ¶ms->threshold, "us", \ 305 "the minimum delta to be considered a noise", \ 306 opt_llong_callback) 307 308 /* 309 * Callback functions for command line options for osnoise tools 310 */ 311 312 static int opt_osnoise_auto_cb(const struct option *opt, const char *arg, int unset) 313 { 314 struct osnoise_cb_data *cb_data = opt->value; 315 struct osnoise_params *params = cb_data->params; 316 long long auto_thresh; 317 318 if (unset || !arg) 319 return -1; 320 321 auto_thresh = get_llong_from_str((char *)arg); 322 params->common.stop_us = auto_thresh; 323 params->threshold = 1; 324 325 if (!cb_data->trace_output) 326 cb_data->trace_output = "osnoise_trace.txt"; 327 328 return 0; 329 } 330 331 static int opt_osnoise_period_cb(const struct option *opt, const char *arg, int unset) 332 { 333 unsigned long long *period = opt->value; 334 335 if (unset || !arg) 336 return -1; 337 338 *period = get_llong_from_str((char *)arg); 339 if (*period > 10000000) 340 fatal("Period longer than 10 s"); 341 342 return 0; 343 } 344 345 static int opt_osnoise_runtime_cb(const struct option *opt, const char *arg, int unset) 346 { 347 unsigned long long *runtime = opt->value; 348 349 if (unset || !arg) 350 return -1; 351 352 *runtime = get_llong_from_str((char *)arg); 353 if (*runtime < 100) 354 fatal("Runtime shorter than 100 us"); 355 356 return 0; 357 } 358 359 static int opt_osnoise_trace_output_cb(const struct option *opt, const char *arg, int unset) 360 { 361 const char **trace_output = opt->value; 362 363 if (unset) 364 return -1; 365 366 if (!arg) { 367 *trace_output = "osnoise_trace.txt"; 368 } else { 369 *trace_output = (char *)arg; 370 if (*trace_output && (*trace_output)[0] == '=') 371 /* Allow -t=<trace_output> next to -t[ ]<trace_output> */ 372 ++*trace_output; 373 } 374 375 return 0; 376 } 377 378 static int opt_osnoise_on_threshold_cb(const struct option *opt, const char *arg, int unset) 379 { 380 struct actions *actions = opt->value; 381 int retval; 382 383 if (unset || !arg) 384 return -1; 385 386 retval = actions_parse(actions, (char *)arg, "osnoise_trace.txt"); 387 if (retval) 388 fatal("Invalid action %s", arg); 389 390 return 0; 391 } 392 393 static int opt_osnoise_on_end_cb(const struct option *opt, const char *arg, int unset) 394 { 395 struct actions *actions = opt->value; 396 int retval; 397 398 if (unset || !arg) 399 return -1; 400 401 retval = actions_parse(actions, (char *)arg, "osnoise_trace.txt"); 402 if (retval) 403 fatal("Invalid action %s", arg); 404 405 return 0; 406 } 407 408 /* 409 * Macros for command line options specific to timerlat 410 */ 411 #define TIMERLAT_OPT_PERIOD OPT_CALLBACK('p', "period", ¶ms->timerlat_period_us, "us", \ 412 "timerlat period in us", \ 413 opt_timerlat_period_cb) 414 415 #define TIMERLAT_OPT_STACK OPT_CALLBACK('s', "stack", ¶ms->print_stack, "us", \ 416 "save the stack trace at the IRQ if a thread latency is higher than the argument in us", \ 417 opt_llong_callback) 418 419 #define TIMERLAT_OPT_NANO OPT_CALLBACK_NOOPT('n', "nano", params, NULL, \ 420 "display data in nanoseconds", \ 421 opt_nano_cb) 422 423 #define TIMERLAT_OPT_DMA_LATENCY OPT_CALLBACK(0, "dma-latency", ¶ms->dma_latency, "us", \ 424 "set /dev/cpu_dma_latency latency <us> to reduce exit from idle latency", \ 425 opt_dma_latency_cb) 426 427 #define TIMERLAT_OPT_DEEPEST_IDLE_STATE OPT_CALLBACK(0, "deepest-idle-state", \ 428 ¶ms->deepest_idle_state, "n", \ 429 "only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", \ 430 opt_int_callback) 431 432 #define TIMERLAT_OPT_AA_ONLY OPT_CALLBACK(0, "aa-only", params, "us", \ 433 "stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)", \ 434 opt_aa_only_cb) 435 436 #define TIMERLAT_OPT_NO_AA OPT_BOOLEAN(0, "no-aa", ¶ms->no_aa, \ 437 "disable auto-analysis, reducing rtla timerlat cpu usage") 438 439 #define TIMERLAT_OPT_DUMPS_TASKS OPT_BOOLEAN(0, "dump-tasks", ¶ms->dump_tasks, \ 440 "prints the task running on all CPUs if stop conditions are met (depends on !--no-aa)") 441 442 #define TIMERLAT_OPT_BPF_ACTION OPT_STRING(0, "bpf-action", ¶ms->bpf_action_program, \ 443 "program", \ 444 "load and execute BPF program when latency threshold is exceeded") 445 446 #define TIMERLAT_OPT_STACK_FORMAT OPT_CALLBACK(0, "stack-format", ¶ms->stack_format, "format", \ 447 "set the stack format (truncate, skip, full)", \ 448 opt_stack_format_cb) 449 450 #define TIMERLAT_OPT_ALIGNED OPT_CALLBACK('A', "aligned", params, "us", \ 451 "align thread wakeups to a specific offset", \ 452 opt_timerlat_align_cb) 453 454 /* 455 * Callback functions for command line options for timerlat tools 456 */ 457 458 static int opt_timerlat_period_cb(const struct option *opt, const char *arg, int unset) 459 { 460 long long *period = opt->value; 461 462 if (unset || !arg) 463 return -1; 464 465 *period = get_llong_from_str((char *)arg); 466 if (*period > 1000000) 467 fatal("Period longer than 1 s"); 468 469 return 0; 470 } 471 472 static int opt_timerlat_auto_cb(const struct option *opt, const char *arg, int unset) 473 { 474 struct timerlat_cb_data *cb_data = opt->value; 475 struct timerlat_params *params = cb_data->params; 476 long long auto_thresh; 477 478 if (unset || !arg) 479 return -1; 480 481 auto_thresh = get_llong_from_str((char *)arg); 482 params->common.stop_total_us = auto_thresh; 483 params->common.stop_us = auto_thresh; 484 params->print_stack = auto_thresh; 485 486 if (!cb_data->trace_output) 487 cb_data->trace_output = "timerlat_trace.txt"; 488 489 return 0; 490 } 491 492 static int opt_dma_latency_cb(const struct option *opt, const char *arg, int unset) 493 { 494 int *dma_latency = opt->value; 495 int retval; 496 497 if (unset || !arg) 498 return -1; 499 500 retval = strtoi((char *)arg, dma_latency); 501 if (retval) 502 fatal("Invalid -dma-latency %s", arg); 503 if (*dma_latency < 0 || *dma_latency > 10000) 504 fatal("--dma-latency needs to be >= 0 and <= 10000"); 505 506 return 0; 507 } 508 509 static int opt_aa_only_cb(const struct option *opt, const char *arg, int unset) 510 { 511 struct timerlat_params *params = opt->value; 512 long long auto_thresh; 513 514 if (unset || !arg) 515 return -1; 516 517 auto_thresh = get_llong_from_str((char *)arg); 518 params->common.stop_total_us = auto_thresh; 519 params->common.stop_us = auto_thresh; 520 params->print_stack = auto_thresh; 521 params->common.aa_only = 1; 522 523 return 0; 524 } 525 526 static int opt_timerlat_trace_output_cb(const struct option *opt, const char *arg, int unset) 527 { 528 const char **trace_output = opt->value; 529 530 if (unset) 531 return -1; 532 533 if (!arg) { 534 *trace_output = "timerlat_trace.txt"; 535 } else { 536 *trace_output = (char *)arg; 537 if (*trace_output && (*trace_output)[0] == '=') 538 /* Allow -t=<trace_output> next to -t[ ]<trace_output> */ 539 ++*trace_output; 540 } 541 542 return 0; 543 } 544 545 static int opt_timerlat_on_threshold_cb(const struct option *opt, const char *arg, int unset) 546 { 547 struct actions *actions = opt->value; 548 int retval; 549 550 if (unset || !arg) 551 return -1; 552 553 retval = actions_parse(actions, (char *)arg, "timerlat_trace.txt"); 554 if (retval) 555 fatal("Invalid action %s", arg); 556 557 return 0; 558 } 559 560 static int opt_timerlat_on_end_cb(const struct option *opt, const char *arg, int unset) 561 { 562 struct actions *actions = opt->value; 563 int retval; 564 565 if (unset || !arg) 566 return -1; 567 568 retval = actions_parse(actions, (char *)arg, "timerlat_trace.txt"); 569 if (retval) 570 fatal("Invalid action %s", arg); 571 572 return 0; 573 } 574 575 static int opt_user_threads_cb(const struct option *opt, const char *arg, int unset) 576 { 577 struct timerlat_params *params = opt->value; 578 579 if (unset) 580 return -1; 581 582 params->common.user_workload = true; 583 params->common.user_data = true; 584 585 return 0; 586 } 587 588 static int opt_nano_cb(const struct option *opt, const char *arg, int unset) 589 { 590 struct timerlat_params *params = opt->value; 591 592 if (unset) 593 return -1; 594 595 params->common.output_divisor = 1; 596 597 return 0; 598 } 599 600 static int opt_stack_format_cb(const struct option *opt, const char *arg, int unset) 601 { 602 int *format = opt->value; 603 604 if (unset || !arg) 605 return -1; 606 607 *format = parse_stack_format((char *)arg); 608 609 if (*format == -1) 610 fatal("Invalid --stack-format option"); 611 612 return 0; 613 } 614 615 static int opt_timerlat_align_cb(const struct option *opt, const char *arg, int unset) 616 { 617 struct timerlat_params *params = opt->value; 618 619 if (unset || !arg) 620 return -1; 621 622 params->timerlat_align = true; 623 params->timerlat_align_us = get_llong_from_str((char *)arg); 624 625 return 0; 626 } 627 628 /* 629 * Macros for command line options specific to histogram-based tools 630 */ 631 632 #define HIST_OPT_BUCKET_SIZE OPT_CALLBACK('b', "bucket-size", \ 633 ¶ms->common.hist.bucket_size, "N", \ 634 "set the histogram bucket size (default 1)", \ 635 opt_bucket_size_cb) 636 637 #define HIST_OPT_ENTRIES OPT_CALLBACK('E', "entries", ¶ms->common.hist.entries, "N", \ 638 "set the number of entries of the histogram (default 256)", \ 639 opt_entries_cb) 640 641 #define HIST_OPT_NO_IRQ OPT_BOOLEAN_FLAG(0, "no-irq", ¶ms->common.hist.no_irq, \ 642 "ignore IRQ latencies", PARSE_OPT_NOAUTONEG) 643 644 #define HIST_OPT_NO_THREAD OPT_BOOLEAN_FLAG(0, "no-thread", ¶ms->common.hist.no_thread, \ 645 "ignore thread latencies", PARSE_OPT_NOAUTONEG) 646 647 #define HIST_OPT_NO_HEADER OPT_BOOLEAN(0, "no-header", ¶ms->common.hist.no_header, \ 648 "do not print header") 649 650 #define HIST_OPT_NO_SUMMARY OPT_BOOLEAN(0, "no-summary", ¶ms->common.hist.no_summary, \ 651 "do not print summary") 652 653 #define HIST_OPT_NO_INDEX OPT_BOOLEAN(0, "no-index", ¶ms->common.hist.no_index, \ 654 "do not print index") 655 656 #define HIST_OPT_WITH_ZEROS OPT_BOOLEAN(0, "with-zeros", ¶ms->common.hist.with_zeros, \ 657 "print zero only entries") 658 659 /* Histogram-specific callbacks */ 660 661 static int opt_bucket_size_cb(const struct option *opt, const char *arg, int unset) 662 { 663 int *bucket_size = opt->value; 664 665 if (unset || !arg) 666 return -1; 667 668 *bucket_size = get_llong_from_str((char *)arg); 669 if (*bucket_size == 0 || *bucket_size >= 1000000) 670 fatal("Bucket size needs to be > 0 and <= 1000000"); 671 672 return 0; 673 } 674 675 static int opt_entries_cb(const struct option *opt, const char *arg, int unset) 676 { 677 int *entries = opt->value; 678 679 if (unset || !arg) 680 return -1; 681 682 *entries = get_llong_from_str((char *)arg); 683 if (*entries < 10 || *entries > 9999999) 684 fatal("Entries must be > 10 and < 10000000"); 685 686 return 0; 687 } 688