Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'trace-rtla-v7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull RTLA updates from Steven Rostedt:

- Remove unused function declarations

Some functions were removed in recent code consolidation 6.18, but
their prototypes were not removed from headers. Remove them.

- Set stop threshold after enabling instances

Prefer recording samples without stopping on them on the start of
tracing to stopping on samples that are never recorded. This fixes
flakiness of some RTLA tests and unifies behavior of sample
collection between tracefs mode and BPF mode.

- Consolidate usage help message implementation

RTLA tools (osnoise-top, osnoise-hist, timerlat-top, timerlat-hist)
each implement usage help individually. Move common logic between
them into a new function to reduce code duplication.

- Add BPF actions feature

Add option --bpf-action to attach a BPF program (passed as filename
of its ELF representation) to be executed via BPF tail call at
latency threshold.

- Consolidate command line option parsing

Each RTLA tool implements the parsing of command line options
individually. Now that we have a common structure for parameters,
unify the parsing of those options common among all four tools into
one function.

- De-duplicate cgroup common code

Two functions in utils.c, setting cgroup for comm and setting cgroup
for pid, duplicate code for constructing the cgroup path. Extract it
to a new helper function.

- String and error handling fixes and cleanups

There were several instances of unsafe string and error handling that
could cause invalid memory access; fix them. Also, remove a few
unused headers, update .gitignore, and deduplicate code.

* tag 'trace-rtla-v7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (30 commits)
rtla: Fix parse_cpu_set() bug introduced by strtoi()
rtla: Fix parse_cpu_set() return value documentation
rtla: Ensure null termination after read operations in utils.c
rtla: Make stop_tracing variable volatile
rtla: Add generated output files to gitignore
rtla: Fix NULL pointer dereference in actions_parse
rtla: Remove unused headers
rtla: Remove redundant memset after calloc
rtla: Use standard exit codes for result enum
rtla: Replace atoi() with a robust strtoi()
rtla: Introduce for_each_action() helper
tools/rtla: Deduplicate cgroup path opening code
tools/rtla: Consolidate -H/--house-keeping option parsing
tools/rtla: Consolidate -P/--priority option parsing
tools/rtla: Consolidate -e/--event option parsing
tools/rtla: Consolidate -d/--duration option parsing
tools/rtla: Consolidate -D/--debug option parsing
tools/rtla: Consolidate -C/--cgroup option parsing
tools/rtla: Consolidate -c/--cpus option parsing
tools/rtla: Add common_parse_options()
...

+507 -356
+19 -1
Documentation/tools/rtla/common_timerlat_options.txt
··· 64 64 65 65 Set timerlat to run without workload, waiting for the user to dispatch a per-cpu 66 66 task that waits for a new period on the tracing/osnoise/per_cpu/cpu$ID/timerlat_fd. 67 - See linux/tools/rtla/sample/timerlat_load.py for an example of user-load code. 67 + See linux/tools/rtla/example/timerlat_load.py for an example of user-load code. 68 + 69 + **--bpf-action** *bpf-program* 70 + 71 + Loads a BPF program from an ELF file and executes it when a latency threshold is exceeded. 72 + 73 + The BPF program must be a valid ELF file loadable with libbpf. The program must contain 74 + a function named ``action_handler``, stored in an ELF section with the ``tp_`` prefix. 75 + The prefix is used by libbpf to set BPF program type to BPF_PROG_TYPE_TRACEPOINT. 76 + 77 + The program receives a ``struct trace_event_raw_timerlat_sample`` parameter 78 + containing timerlat sample data. 79 + 80 + An example is provided in ``tools/tracing/rtla/example/timerlat_bpf_action.c``. 81 + This example demonstrates how to create a BPF program that prints latency information using 82 + bpf_trace_printk() when a threshold is exceeded. 83 + 84 + **Note**: BPF actions require BPF support to be available. If BPF is not available 85 + or disabled, the tool falls back to tracefs mode and BPF actions are not supported.
+4
tools/tracing/rtla/.gitignore
··· 5 5 feature 6 6 FEATURE-DUMP 7 7 *.skel.h 8 + custom_filename.txt 9 + osnoise_irq_noise_hist.txt 10 + osnoise_trace.txt 11 + timerlat_trace.txt
+16 -3
tools/tracing/rtla/Makefile
··· 73 73 74 74 src/timerlat.skel.h: src/timerlat.bpf.o 75 75 $(QUIET_GENSKEL)$(SYSTEM_BPFTOOL) gen skeleton $< > $@ 76 + 77 + example/timerlat_bpf_action.o: example/timerlat_bpf_action.c 78 + $(QUIET_CLANG)$(CLANG) -g -O2 -target bpf -c $(filter %.c,$^) -o $@ 79 + 80 + tests/bpf/bpf_action_map.o: tests/bpf/bpf_action_map.c 81 + $(QUIET_CLANG)$(CLANG) -g -O2 -target bpf -c $(filter %.c,$^) -o $@ 76 82 else 77 83 src/timerlat.skel.h: 78 84 $(Q)echo '/* BPF skeleton is disabled */' > src/timerlat.skel.h 85 + 86 + example/timerlat_bpf_action.o: example/timerlat_bpf_action.c 87 + $(Q)echo "BPF skeleton support is disabled, skipping example/timerlat_bpf_action.o" 88 + 89 + tests/bpf/bpf_action_map.o: tests/bpf/bpf_action_map.c 90 + $(Q)echo "BPF skeleton support is disabled, skipping tests/bpf/bpf_action_map.o" 79 91 endif 80 92 81 93 $(RTLA): $(RTLA_IN) ··· 108 96 $(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete 109 97 $(Q)rm -f rtla rtla-static fixdep FEATURE-DUMP rtla-* 110 98 $(Q)rm -rf feature 111 - $(Q)rm -f src/timerlat.bpf.o src/timerlat.skel.h 112 - check: $(RTLA) 113 - RTLA=$(RTLA) prove -o -f tests/ 99 + $(Q)rm -f src/timerlat.bpf.o src/timerlat.skel.h example/timerlat_bpf_action.o 100 + check: $(RTLA) tests/bpf/bpf_action_map.o 101 + RTLA=$(RTLA) BPFTOOL=$(SYSTEM_BPFTOOL) prove -o -f -v tests/ 102 + examples: example/timerlat_bpf_action.o 114 103 .PHONY: FORCE clean check
+16
tools/tracing/rtla/example/timerlat_bpf_action.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/bpf.h> 3 + #include <bpf/bpf_tracing.h> 4 + 5 + char LICENSE[] SEC("license") = "GPL"; 6 + 7 + struct trace_event_raw_timerlat_sample { 8 + unsigned long long timer_latency; 9 + } __attribute__((preserve_access_index)); 10 + 11 + SEC("tp/timerlat_action") 12 + int action_handler(struct trace_event_raw_timerlat_sample *tp_args) 13 + { 14 + bpf_printk("Latency: %lld\n", tp_args->timer_latency); 15 + return 0; 16 + }
tools/tracing/rtla/sample/timerlat_load.py tools/tracing/rtla/example/timerlat_load.py
+10 -7
tools/tracing/rtla/src/actions.c
··· 19 19 self->len = 0; 20 20 self->continue_flag = false; 21 21 22 - memset(&self->present, 0, sizeof(self->present)); 23 - 24 22 /* This has to be set by the user */ 25 23 self->trace_output_inst = NULL; 26 24 } ··· 30 32 actions_destroy(struct actions *self) 31 33 { 32 34 /* Free any action-specific data */ 33 - for (struct action *action = self->list; action < self->list + self->len; action++) { 35 + struct action *action; 36 + 37 + for_each_action(self, action) { 34 38 if (action->type == ACTION_SHELL) 35 39 free(action->command); 36 40 if (action->type == ACTION_TRACE_OUTPUT) ··· 141 141 142 142 strcpy(trigger_c, trigger); 143 143 token = strtok(trigger_c, ","); 144 + if (!token) 145 + return -1; 144 146 145 147 if (strcmp(token, "trace") == 0) 146 148 type = ACTION_TRACE_OUTPUT; ··· 181 179 /* Takes two arguments, num (signal) and pid */ 182 180 while (token != NULL) { 183 181 if (strlen(token) > 4 && strncmp(token, "num=", 4) == 0) { 184 - signal = atoi(token + 4); 182 + if (strtoi(token + 4, &signal)) 183 + return -1; 185 184 } else if (strlen(token) > 4 && strncmp(token, "pid=", 4) == 0) { 186 185 if (strncmp(token + 4, "parent", 7) == 0) 187 186 pid = -1; 188 - else 189 - pid = atoi(token + 4); 187 + else if (strtoi(token + 4, &pid)) 188 + return -1; 190 189 } else { 191 190 /* Invalid argument */ 192 191 return -1; ··· 226 223 int pid, retval; 227 224 const struct action *action; 228 225 229 - for (action = self->list; action < self->list + self->len; action++) { 226 + for_each_action(self, action) { 230 227 switch (action->type) { 231 228 case ACTION_TRACE_OUTPUT: 232 229 retval = save_trace_to_file(self->trace_output_inst, action->trace_output);
+5
tools/tracing/rtla/src/actions.h
··· 42 42 struct tracefs_instance *trace_output_inst; 43 43 }; 44 44 45 + #define for_each_action(actions, action) \ 46 + for ((action) = (actions)->list; \ 47 + (action) < (actions)->list + (actions)->len; \ 48 + (action)++) 49 + 45 50 void actions_init(struct actions *self); 46 51 void actions_destroy(struct actions *self); 47 52 int actions_add_trace_output(struct actions *self, const char *trace_output);
+139 -1
tools/tracing/rtla/src/common.c
··· 4 4 #include <pthread.h> 5 5 #include <signal.h> 6 6 #include <stdlib.h> 7 + #include <string.h> 7 8 #include <unistd.h> 9 + #include <getopt.h> 8 10 #include "common.h" 9 11 10 12 struct trace_instance *trace_inst; 11 - int stop_tracing; 13 + volatile int stop_tracing; 12 14 13 15 static void stop_trace(int sig) 14 16 { ··· 37 35 signal(SIGALRM, stop_trace); 38 36 alarm(params->duration); 39 37 } 38 + } 39 + 40 + /* 41 + * common_parse_options - parse common command line options 42 + * 43 + * @argc: argument count 44 + * @argv: argument vector 45 + * @common: common parameters structure 46 + * 47 + * Parse command line options that are common to all rtla tools. 48 + * 49 + * Returns: non zero if a common option was parsed, or 0 50 + * if the option should be handled by tool-specific parsing. 51 + */ 52 + int common_parse_options(int argc, char **argv, struct common_params *common) 53 + { 54 + struct trace_events *tevent; 55 + int saved_state = optind; 56 + int c; 57 + 58 + static struct option long_options[] = { 59 + {"cpus", required_argument, 0, 'c'}, 60 + {"cgroup", optional_argument, 0, 'C'}, 61 + {"debug", no_argument, 0, 'D'}, 62 + {"duration", required_argument, 0, 'd'}, 63 + {"event", required_argument, 0, 'e'}, 64 + {"house-keeping", required_argument, 0, 'H'}, 65 + {"priority", required_argument, 0, 'P'}, 66 + {0, 0, 0, 0} 67 + }; 68 + 69 + opterr = 0; 70 + c = getopt_long(argc, argv, "c:C::Dd:e:H:P:", long_options, NULL); 71 + opterr = 1; 72 + 73 + switch (c) { 74 + case 'c': 75 + if (parse_cpu_set(optarg, &common->monitored_cpus)) 76 + fatal("Invalid -c cpu list"); 77 + common->cpus = optarg; 78 + break; 79 + case 'C': 80 + common->cgroup = 1; 81 + common->cgroup_name = parse_optional_arg(argc, argv); 82 + break; 83 + case 'D': 84 + config_debug = 1; 85 + break; 86 + case 'd': 87 + common->duration = parse_seconds_duration(optarg); 88 + if (!common->duration) 89 + fatal("Invalid -d duration"); 90 + break; 91 + case 'e': 92 + tevent = trace_event_alloc(optarg); 93 + if (!tevent) 94 + fatal("Error alloc trace event"); 95 + 96 + if (common->events) 97 + tevent->next = common->events; 98 + common->events = tevent; 99 + break; 100 + case 'H': 101 + common->hk_cpus = 1; 102 + if (parse_cpu_set(optarg, &common->hk_cpu_set)) 103 + fatal("Error parsing house keeping CPUs"); 104 + break; 105 + case 'P': 106 + if (parse_prio(optarg, &common->sched_param) == -1) 107 + fatal("Invalid -P priority"); 108 + common->set_sched = 1; 109 + break; 110 + default: 111 + optind = saved_state; 112 + return 0; 113 + } 114 + 115 + return c; 40 116 } 41 117 42 118 /* ··· 427 347 } 428 348 429 349 return retval; 350 + } 351 + 352 + int osn_set_stop(struct osnoise_tool *tool) 353 + { 354 + struct common_params *params = tool->params; 355 + int retval; 356 + 357 + retval = osnoise_set_stop_us(tool->context, params->stop_us); 358 + if (retval) { 359 + err_msg("Failed to set stop us\n"); 360 + return retval; 361 + } 362 + 363 + retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us); 364 + if (retval) { 365 + err_msg("Failed to set stop total us\n"); 366 + return retval; 367 + } 368 + 369 + return 0; 370 + } 371 + 372 + static void print_msg_array(const char * const *msgs) 373 + { 374 + if (!msgs) 375 + return; 376 + 377 + for (int i = 0; msgs[i]; i++) 378 + fprintf(stderr, "%s\n", msgs[i]); 379 + } 380 + 381 + /* 382 + * common_usage - print complete usage information 383 + */ 384 + void common_usage(const char *tool, const char *mode, 385 + const char *desc, const char * const *start_msgs, const char * const *opt_msgs) 386 + { 387 + static const char * const common_options[] = { 388 + " -h/--help: print this menu", 389 + NULL 390 + }; 391 + fprintf(stderr, "rtla %s", tool); 392 + if (strcmp(mode, "")) 393 + fprintf(stderr, " %s", mode); 394 + fprintf(stderr, ": %s (version %s)\n\n", desc, VERSION); 395 + fprintf(stderr, " usage: [rtla] %s ", tool); 396 + 397 + if (strcmp(mode, "top") == 0) 398 + fprintf(stderr, "[top] [-h] "); 399 + else 400 + fprintf(stderr, "%s [-h] ", mode); 401 + 402 + print_msg_array(start_msgs); 403 + fprintf(stderr, "\n"); 404 + print_msg_array(common_options); 405 + print_msg_array(opt_msgs); 406 + 407 + exit(EXIT_SUCCESS); 430 408 }
+9 -1
tools/tracing/rtla/src/common.h
··· 54 54 }; 55 55 56 56 extern struct trace_instance *trace_inst; 57 - extern int stop_tracing; 57 + extern volatile int stop_tracing; 58 58 59 59 struct hist_params { 60 60 char no_irq; ··· 152 152 struct osnoise_tool *osnoise_init_tool(char *tool_name); 153 153 struct osnoise_tool *osnoise_init_trace_tool(const char *tracer); 154 154 bool osnoise_trace_is_off(struct osnoise_tool *tool, struct osnoise_tool *record); 155 + int osnoise_set_stop_us(struct osnoise_context *context, long long stop_us); 156 + int osnoise_set_stop_total_us(struct osnoise_context *context, 157 + long long stop_total_us); 155 158 159 + int common_parse_options(int argc, char **argv, struct common_params *common); 156 160 int common_apply_config(struct osnoise_tool *tool, struct common_params *params); 157 161 int top_main_loop(struct osnoise_tool *tool); 158 162 int hist_main_loop(struct osnoise_tool *tool); 163 + int osn_set_stop(struct osnoise_tool *tool); 164 + 165 + void common_usage(const char *tool, const char *mode, 166 + const char *desc, const char * const *start_msgs, const char * const *opt_msgs);
+4 -13
tools/tracing/rtla/src/osnoise.c
··· 1128 1128 goto out_err; 1129 1129 } 1130 1130 1131 - retval = osnoise_set_stop_us(tool->context, params->common.stop_us); 1132 - if (retval) { 1133 - err_msg("Failed to set stop us\n"); 1134 - goto out_err; 1135 - } 1136 - 1137 - retval = osnoise_set_stop_total_us(tool->context, params->common.stop_total_us); 1138 - if (retval) { 1139 - err_msg("Failed to set stop total us\n"); 1140 - goto out_err; 1141 - } 1142 - 1143 1131 retval = osnoise_set_tracing_thresh(tool->context, params->threshold); 1144 1132 if (retval) { 1145 1133 err_msg("Failed to set tracing_thresh\n"); ··· 1172 1184 debug_msg("Error cleaning up the buffer"); 1173 1185 return retval; 1174 1186 } 1175 - 1176 1187 } 1188 + 1189 + retval = osn_set_stop(tool); 1190 + if (retval) 1191 + return retval; 1177 1192 1178 1193 return 0; 1179 1194 }
-8
tools/tracing/rtla/src/osnoise.h
··· 34 34 unsigned long long period); 35 35 void osnoise_restore_runtime_period(struct osnoise_context *context); 36 36 37 - int osnoise_set_stop_us(struct osnoise_context *context, 38 - long long stop_us); 39 37 void osnoise_restore_stop_us(struct osnoise_context *context); 40 - 41 - int osnoise_set_stop_total_us(struct osnoise_context *context, 42 - long long stop_total_us); 43 38 void osnoise_restore_stop_total_us(struct osnoise_context *context); 44 39 45 40 int osnoise_set_timerlat_period_us(struct osnoise_context *context, ··· 53 58 void osnoise_report_missed_events(struct osnoise_tool *tool); 54 59 int osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params); 55 60 56 - int osnoise_hist_main(int argc, char *argv[]); 57 - int osnoise_top_main(int argc, char **argv); 58 61 int osnoise_enable(struct osnoise_tool *tool); 59 62 int osnoise_main(int argc, char **argv); 60 63 int hwnoise_main(int argc, char **argv); ··· 61 68 extern struct tool_ops osnoise_top_ops, osnoise_hist_ops; 62 69 63 70 int run_tool(struct tool_ops *ops, int argc, char *argv[]); 64 - int hist_main_loop(struct osnoise_tool *tool);
+12 -64
tools/tracing/rtla/src/osnoise_hist.c
··· 9 9 #include <string.h> 10 10 #include <signal.h> 11 11 #include <unistd.h> 12 - #include <errno.h> 13 12 #include <stdio.h> 14 13 #include <time.h> 15 14 ··· 408 409 */ 409 410 static void osnoise_hist_usage(void) 410 411 { 411 - int i; 412 - 413 - static const char * const msg[] = { 414 - "", 415 - " usage: rtla osnoise hist [-h] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", 412 + static const char * const msg_start[] = { 413 + "[-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", 416 414 " [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\", 417 415 " [-c cpu-list] [-H cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] \\", 418 416 " [--no-index] [--with-zeros] [-C [cgroup_name]] [--warm-up]", 419 - "", 420 - " -h/--help: print this menu", 417 + NULL, 418 + }; 419 + 420 + static const char * const msg_opts[] = { 421 421 " -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit", 422 422 " -p/--period us: osnoise period in us", 423 423 " -r/--runtime us: osnoise runtime in us", ··· 451 453 NULL, 452 454 }; 453 455 454 - fprintf(stderr, "rtla osnoise hist: a per-cpu histogram of the OS noise (version %s)\n", 455 - VERSION); 456 - 457 - for (i = 0; msg[i]; i++) 458 - fprintf(stderr, "%s\n", msg[i]); 459 - 460 - exit(EXIT_SUCCESS); 456 + common_usage("osnoise", "hist", "a per-cpu histogram of the OS noise", 457 + msg_start, msg_opts); 461 458 } 462 459 463 460 /* ··· 462 469 *osnoise_hist_parse_args(int argc, char *argv[]) 463 470 { 464 471 struct osnoise_params *params; 465 - struct trace_events *tevent; 466 472 int retval; 467 473 int c; 468 474 char *trace_output = NULL; ··· 483 491 {"auto", required_argument, 0, 'a'}, 484 492 {"bucket-size", required_argument, 0, 'b'}, 485 493 {"entries", required_argument, 0, 'E'}, 486 - {"cpus", required_argument, 0, 'c'}, 487 - {"cgroup", optional_argument, 0, 'C'}, 488 - {"debug", no_argument, 0, 'D'}, 489 - {"duration", required_argument, 0, 'd'}, 490 - {"house-keeping", required_argument, 0, 'H'}, 491 494 {"help", no_argument, 0, 'h'}, 492 495 {"period", required_argument, 0, 'p'}, 493 - {"priority", required_argument, 0, 'P'}, 494 496 {"runtime", required_argument, 0, 'r'}, 495 497 {"stop", required_argument, 0, 's'}, 496 498 {"stop-total", required_argument, 0, 'S'}, 497 499 {"trace", optional_argument, 0, 't'}, 498 - {"event", required_argument, 0, 'e'}, 499 500 {"threshold", required_argument, 0, 'T'}, 500 501 {"no-header", no_argument, 0, '0'}, 501 502 {"no-summary", no_argument, 0, '1'}, ··· 503 518 {0, 0, 0, 0} 504 519 }; 505 520 506 - c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:p:P:r:s:S:t::T:01234:5:6:7:", 521 + if (common_parse_options(argc, argv, &params->common)) 522 + continue; 523 + 524 + c = getopt_long(argc, argv, "a:b:E:hp:r:s:S:t::T:01234:5:6:7:", 507 525 long_options, NULL); 508 526 509 527 /* detect the end of the options. */ ··· 532 544 params->common.hist.bucket_size >= 1000000) 533 545 fatal("Bucket size needs to be > 0 and <= 1000000"); 534 546 break; 535 - case 'c': 536 - retval = parse_cpu_set(optarg, &params->common.monitored_cpus); 537 - if (retval) 538 - fatal("Invalid -c cpu list"); 539 - params->common.cpus = optarg; 540 - break; 541 - case 'C': 542 - params->common.cgroup = 1; 543 - params->common.cgroup_name = parse_optional_arg(argc, argv); 544 - break; 545 - case 'D': 546 - config_debug = 1; 547 - break; 548 - case 'd': 549 - params->common.duration = parse_seconds_duration(optarg); 550 - if (!params->common.duration) 551 - fatal("Invalid -D duration"); 552 - break; 553 - case 'e': 554 - tevent = trace_event_alloc(optarg); 555 - if (!tevent) 556 - fatal("Error alloc trace event"); 557 - 558 - if (params->common.events) 559 - tevent->next = params->common.events; 560 - 561 - params->common.events = tevent; 562 - break; 563 547 case 'E': 564 548 params->common.hist.entries = get_llong_from_str(optarg); 565 549 if (params->common.hist.entries < 10 || ··· 542 582 case '?': 543 583 osnoise_hist_usage(); 544 584 break; 545 - case 'H': 546 - params->common.hk_cpus = 1; 547 - retval = parse_cpu_set(optarg, &params->common.hk_cpu_set); 548 - if (retval) 549 - fatal("Error parsing house keeping CPUs"); 550 - break; 551 585 case 'p': 552 586 params->period = get_llong_from_str(optarg); 553 587 if (params->period > 10000000) 554 588 fatal("Period longer than 10 s"); 555 - break; 556 - case 'P': 557 - retval = parse_prio(optarg, &params->common.sched_param); 558 - if (retval == -1) 559 - fatal("Invalid -P priority"); 560 - params->common.set_sched = 1; 561 589 break; 562 590 case 'r': 563 591 params->runtime = get_llong_from_str(optarg);
+19 -71
tools/tracing/rtla/src/osnoise_top.c
··· 257 257 */ 258 258 static void osnoise_top_usage(struct osnoise_params *params) 259 259 { 260 - int i; 260 + const char *tool, *mode, *desc; 261 261 262 - static const char * const msg[] = { 263 - " [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", 262 + static const char * const msg_start[] = { 263 + "[-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", 264 264 " [-T us] [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\", 265 265 " [-c cpu-list] [-H cpu-list] [-P priority] [-C [cgroup_name]] [--warm-up s]", 266 - "", 267 - " -h/--help: print this menu", 266 + NULL, 267 + }; 268 + 269 + static const char * const msg_opts[] = { 268 270 " -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit", 269 271 " -p/--period us: osnoise period in us", 270 272 " -r/--runtime us: osnoise runtime in us", ··· 297 295 }; 298 296 299 297 if (params->mode == MODE_OSNOISE) { 300 - fprintf(stderr, 301 - "rtla osnoise top: a per-cpu summary of the OS noise (version %s)\n", 302 - VERSION); 303 - 304 - fprintf(stderr, " usage: rtla osnoise [top]"); 298 + tool = "osnoise"; 299 + mode = "top"; 300 + desc = "a per-cpu summary of the OS noise"; 301 + } else { 302 + tool = "hwnoise"; 303 + mode = ""; 304 + desc = "a summary of hardware-related noise"; 305 305 } 306 306 307 - if (params->mode == MODE_HWNOISE) { 308 - fprintf(stderr, 309 - "rtla hwnoise: a summary of hardware-related noise (version %s)\n", 310 - VERSION); 311 - 312 - fprintf(stderr, " usage: rtla hwnoise"); 313 - } 314 - 315 - for (i = 0; msg[i]; i++) 316 - fprintf(stderr, "%s\n", msg[i]); 317 - 318 - exit(EXIT_SUCCESS); 307 + common_usage(tool, mode, desc, msg_start, msg_opts); 319 308 } 320 309 321 310 /* ··· 315 322 struct common_params *osnoise_top_parse_args(int argc, char **argv) 316 323 { 317 324 struct osnoise_params *params; 318 - struct trace_events *tevent; 319 325 int retval; 320 326 int c; 321 327 char *trace_output = NULL; ··· 338 346 while (1) { 339 347 static struct option long_options[] = { 340 348 {"auto", required_argument, 0, 'a'}, 341 - {"cpus", required_argument, 0, 'c'}, 342 - {"cgroup", optional_argument, 0, 'C'}, 343 - {"debug", no_argument, 0, 'D'}, 344 - {"duration", required_argument, 0, 'd'}, 345 - {"event", required_argument, 0, 'e'}, 346 - {"house-keeping", required_argument, 0, 'H'}, 347 349 {"help", no_argument, 0, 'h'}, 348 350 {"period", required_argument, 0, 'p'}, 349 - {"priority", required_argument, 0, 'P'}, 350 351 {"quiet", no_argument, 0, 'q'}, 351 352 {"runtime", required_argument, 0, 'r'}, 352 353 {"stop", required_argument, 0, 's'}, ··· 355 370 {0, 0, 0, 0} 356 371 }; 357 372 358 - c = getopt_long(argc, argv, "a:c:C::d:De:hH:p:P:qr:s:S:t::T:0:1:2:3:", 373 + if (common_parse_options(argc, argv, &params->common)) 374 + continue; 375 + 376 + c = getopt_long(argc, argv, "a:hp:qr:s:S:t::T:0:1:2:3:", 359 377 long_options, NULL); 360 378 361 379 /* Detect the end of the options. */ ··· 378 390 trace_output = "osnoise_trace.txt"; 379 391 380 392 break; 381 - case 'c': 382 - retval = parse_cpu_set(optarg, &params->common.monitored_cpus); 383 - if (retval) 384 - fatal("Invalid -c cpu list"); 385 - params->common.cpus = optarg; 386 - break; 387 - case 'C': 388 - params->common.cgroup = 1; 389 - params->common.cgroup_name = parse_optional_arg(argc, argv); 390 - break; 391 - case 'D': 392 - config_debug = 1; 393 - break; 394 - case 'd': 395 - params->common.duration = parse_seconds_duration(optarg); 396 - if (!params->common.duration) 397 - fatal("Invalid -d duration"); 398 - break; 399 - case 'e': 400 - tevent = trace_event_alloc(optarg); 401 - if (!tevent) 402 - fatal("Error alloc trace event"); 403 - 404 - if (params->common.events) 405 - tevent->next = params->common.events; 406 - params->common.events = tevent; 407 - 408 - break; 409 393 case 'h': 410 394 case '?': 411 395 osnoise_top_usage(params); 412 - break; 413 - case 'H': 414 - params->common.hk_cpus = 1; 415 - retval = parse_cpu_set(optarg, &params->common.hk_cpu_set); 416 - if (retval) 417 - fatal("Error parsing house keeping CPUs"); 418 396 break; 419 397 case 'p': 420 398 params->period = get_llong_from_str(optarg); 421 399 if (params->period > 10000000) 422 400 fatal("Period longer than 10 s"); 423 - break; 424 - case 'P': 425 - retval = parse_prio(optarg, &params->common.sched_param); 426 - if (retval == -1) 427 - fatal("Invalid -P priority"); 428 - params->common.set_sched = 1; 429 401 break; 430 402 case 'q': 431 403 params->common.quiet = 1;
+21 -4
tools/tracing/rtla/src/timerlat.bpf.c
··· 40 40 __uint(max_entries, 1); 41 41 } signal_stop_tracing SEC(".maps"); 42 42 43 + struct { 44 + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 45 + __uint(key_size, sizeof(unsigned int)); 46 + __uint(max_entries, 1); 47 + __array(values, unsigned int (void *)); 48 + } bpf_action SEC(".maps") = { 49 + .values = { 50 + [0] = 0 51 + }, 52 + }; 53 + 43 54 /* Params to be set by rtla */ 44 55 const volatile int bucket_size = 1; 45 56 const volatile int output_divisor = 1000; ··· 120 109 map_set(map, SUMMARY_SUM, map_get(map, SUMMARY_SUM) + latency); 121 110 } 122 111 123 - nosubprog void set_stop_tracing(void) 112 + nosubprog void set_stop_tracing(struct trace_event_raw_timerlat_sample *tp_args) 124 113 { 125 114 int value = 0; 126 115 ··· 129 118 130 119 /* Signal to userspace */ 131 120 bpf_ringbuf_output(&signal_stop_tracing, &value, sizeof(value), 0); 121 + 122 + /* 123 + * Call into BPF action program, if attached. 124 + * Otherwise, just silently fail. 125 + */ 126 + bpf_tail_call(tp_args, &bpf_action, 0); 132 127 } 133 128 134 129 SEC("tp/osnoise/timerlat_sample") ··· 155 138 update_summary(&summary_irq, latency, bucket); 156 139 157 140 if (irq_threshold != 0 && latency_us >= irq_threshold) 158 - set_stop_tracing(); 141 + set_stop_tracing(tp_args); 159 142 } else if (tp_args->context == 1) { 160 143 update_main_hist(&hist_thread, bucket); 161 144 update_summary(&summary_thread, latency, bucket); 162 145 163 146 if (thread_threshold != 0 && latency_us >= thread_threshold) 164 - set_stop_tracing(); 147 + set_stop_tracing(tp_args); 165 148 } else { 166 149 update_main_hist(&hist_user, bucket); 167 150 update_summary(&summary_user, latency, bucket); 168 151 169 152 if (thread_threshold != 0 && latency_us >= thread_threshold) 170 - set_stop_tracing(); 153 + set_stop_tracing(tp_args); 171 154 } 172 155 173 156 return 0;
+15 -14
tools/tracing/rtla/src/timerlat.c
··· 9 9 #include <stdlib.h> 10 10 #include <string.h> 11 11 #include <unistd.h> 12 - #include <errno.h> 13 12 #include <fcntl.h> 14 13 #include <stdio.h> 15 14 #include <sched.h> ··· 47 48 } 48 49 } 49 50 50 - if (params->mode != TRACING_MODE_BPF) { 51 - /* 52 - * In tracefs and mixed mode, timerlat tracer handles stopping 53 - * on threshold 54 - */ 55 - retval = osnoise_set_stop_us(tool->context, params->common.stop_us); 56 - if (retval) { 57 - err_msg("Failed to set stop us\n"); 51 + /* Check if BPF action program is requested but BPF is not available */ 52 + if (params->bpf_action_program) { 53 + if (params->mode == TRACING_MODE_TRACEFS) { 54 + err_msg("BPF actions are not supported in tracefs-only mode\n"); 58 55 goto out_err; 59 56 } 60 57 61 - retval = osnoise_set_stop_total_us(tool->context, params->common.stop_total_us); 62 - if (retval) { 63 - err_msg("Failed to set stop total us\n"); 58 + if (timerlat_load_bpf_action_program(params->bpf_action_program)) 64 59 goto out_err; 65 - } 66 60 } 67 - 68 61 69 62 retval = osnoise_set_timerlat_period_us(tool->context, 70 63 params->timerlat_period_us ? ··· 173 182 err_msg("Error attaching BPF program\n"); 174 183 return retval; 175 184 } 185 + } 186 + 187 + /* 188 + * In tracefs and mixed mode, timerlat tracer handles stopping 189 + * on threshold 190 + */ 191 + if (params->mode != TRACING_MODE_BPF) { 192 + retval = osn_set_stop(tool); 193 + if (retval) 194 + return retval; 176 195 } 177 196 178 197 return 0;
+1 -1
tools/tracing/rtla/src/timerlat.h
··· 27 27 int dump_tasks; 28 28 int deepest_idle_state; 29 29 enum timerlat_tracing_mode mode; 30 + const char *bpf_action_program; 30 31 }; 31 32 32 33 #define to_timerlat_params(ptr) container_of(ptr, struct timerlat_params, common) ··· 37 36 int timerlat_enable(struct osnoise_tool *tool); 38 37 void timerlat_analyze(struct osnoise_tool *tool, bool stopped); 39 38 void timerlat_free(struct osnoise_tool *tool); 40 -
+66
tools/tracing/rtla/src/timerlat_bpf.c
··· 7 7 8 8 static struct timerlat_bpf *bpf; 9 9 10 + /* BPF object and program for action program */ 11 + static struct bpf_object *obj; 12 + static struct bpf_program *prog; 13 + 10 14 /* 11 15 * timerlat_bpf_init - load and initialize BPF program to collect timerlat data 12 16 */ ··· 64 60 } 65 61 66 62 /* 63 + * timerlat_bpf_set_action - set action on threshold executed on BPF side 64 + */ 65 + static int timerlat_bpf_set_action(struct bpf_program *prog) 66 + { 67 + unsigned int key = 0, value = bpf_program__fd(prog); 68 + 69 + return bpf_map__update_elem(bpf->maps.bpf_action, 70 + &key, sizeof(key), 71 + &value, sizeof(value), 72 + BPF_ANY); 73 + } 74 + 75 + /* 67 76 * timerlat_bpf_attach - attach BPF program to collect timerlat data 68 77 */ 69 78 int timerlat_bpf_attach(void) ··· 100 83 void timerlat_bpf_destroy(void) 101 84 { 102 85 timerlat_bpf__destroy(bpf); 86 + bpf = NULL; 87 + if (obj) 88 + bpf_object__close(obj); 89 + obj = NULL; 90 + prog = NULL; 103 91 } 104 92 105 93 static int handle_rb_event(void *ctx, void *data, size_t data_sz) ··· 199 177 bpf->maps.summary_user, 200 178 key, value_irq, value_thread, value_user, cpus); 201 179 } 180 + 181 + /* 182 + * timerlat_load_bpf_action_program - load and register a BPF action program 183 + */ 184 + int timerlat_load_bpf_action_program(const char *program_path) 185 + { 186 + int err; 187 + 188 + obj = bpf_object__open_file(program_path, NULL); 189 + if (!obj) { 190 + err_msg("Failed to open BPF action program: %s\n", program_path); 191 + goto out_err; 192 + } 193 + 194 + err = bpf_object__load(obj); 195 + if (err) { 196 + err_msg("Failed to load BPF action program: %s\n", program_path); 197 + goto out_obj_err; 198 + } 199 + 200 + prog = bpf_object__find_program_by_name(obj, "action_handler"); 201 + if (!prog) { 202 + err_msg("BPF action program must have 'action_handler' function: %s\n", 203 + program_path); 204 + goto out_obj_err; 205 + } 206 + 207 + err = timerlat_bpf_set_action(prog); 208 + if (err) { 209 + err_msg("Failed to register BPF action program: %s\n", program_path); 210 + goto out_prog_err; 211 + } 212 + 213 + return 0; 214 + 215 + out_prog_err: 216 + prog = NULL; 217 + out_obj_err: 218 + bpf_object__close(obj); 219 + obj = NULL; 220 + out_err: 221 + return 1; 222 + } 223 + 202 224 #endif /* HAVE_BPF_SKEL */
+6 -1
tools/tracing/rtla/src/timerlat_bpf.h
··· 12 12 }; 13 13 14 14 #ifndef __bpf__ 15 + #include <bpf/libbpf.h> 15 16 #ifdef HAVE_BPF_SKEL 16 17 int timerlat_bpf_init(struct timerlat_params *params); 17 18 int timerlat_bpf_attach(void); ··· 30 29 long long *value_thread, 31 30 long long *value_user, 32 31 int cpus); 33 - 32 + int timerlat_load_bpf_action_program(const char *program_path); 34 33 static inline int have_libbpf_support(void) { return 1; } 35 34 #else 36 35 static inline int timerlat_bpf_init(struct timerlat_params *params) ··· 55 54 long long *value_thread, 56 55 long long *value_user, 57 56 int cpus) 57 + { 58 + return -1; 59 + } 60 + static inline int timerlat_load_bpf_action_program(const char *program_path) 58 61 { 59 62 return -1; 60 63 }
+17 -63
tools/tracing/rtla/src/timerlat_hist.c
··· 696 696 */ 697 697 static void timerlat_hist_usage(void) 698 698 { 699 - int i; 700 - 701 - char *msg[] = { 702 - "", 703 - " usage: [rtla] timerlat hist [-h] [-q] [-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\", 699 + static const char * const msg_start[] = { 700 + "[-d s] [-D] [-n] [-a us] [-p us] [-i us] [-T us] [-s us] \\", 704 701 " [-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\", 705 702 " [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\", 706 703 " [--no-index] [--with-zeros] [--dma-latency us] [-C [cgroup_name]] [--no-aa] [--dump-task] [-u|-k]", 707 704 " [--warm-up s] [--deepest-idle-state n]", 708 - "", 709 - " -h/--help: print this menu", 705 + NULL, 706 + }; 707 + 708 + static const char * const msg_opts[] = { 710 709 " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit", 711 710 " -p/--period us: timerlat period in us", 712 711 " -i/--irq us: stop trace if the irq latency is higher than the argument in us", ··· 746 747 " --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", 747 748 " --on-threshold <action>: define action to be executed at latency threshold, multiple are allowed", 748 749 " --on-end <action>: define action to be executed at measurement end, multiple are allowed", 750 + " --bpf-action <program>: load and execute BPF program when latency threshold is exceeded", 749 751 NULL, 750 752 }; 751 753 752 - fprintf(stderr, "rtla timerlat hist: a per-cpu histogram of the timer latency (version %s)\n", 753 - VERSION); 754 - 755 - for (i = 0; msg[i]; i++) 756 - fprintf(stderr, "%s\n", msg[i]); 757 - 758 - exit(EXIT_SUCCESS); 754 + common_usage("timerlat", "hist", "a per-cpu histogram of the timer latency", 755 + msg_start, msg_opts); 759 756 } 760 757 761 758 /* ··· 761 766 *timerlat_hist_parse_args(int argc, char *argv[]) 762 767 { 763 768 struct timerlat_params *params; 764 - struct trace_events *tevent; 765 769 int auto_thresh; 766 770 int retval; 767 771 int c; ··· 790 796 while (1) { 791 797 static struct option long_options[] = { 792 798 {"auto", required_argument, 0, 'a'}, 793 - {"cpus", required_argument, 0, 'c'}, 794 - {"cgroup", optional_argument, 0, 'C'}, 795 799 {"bucket-size", required_argument, 0, 'b'}, 796 - {"debug", no_argument, 0, 'D'}, 797 800 {"entries", required_argument, 0, 'E'}, 798 - {"duration", required_argument, 0, 'd'}, 799 - {"house-keeping", required_argument, 0, 'H'}, 800 801 {"help", no_argument, 0, 'h'}, 801 802 {"irq", required_argument, 0, 'i'}, 802 803 {"nano", no_argument, 0, 'n'}, 803 804 {"period", required_argument, 0, 'p'}, 804 - {"priority", required_argument, 0, 'P'}, 805 805 {"stack", required_argument, 0, 's'}, 806 806 {"thread", required_argument, 0, 'T'}, 807 807 {"trace", optional_argument, 0, 't'}, 808 808 {"user-threads", no_argument, 0, 'u'}, 809 809 {"kernel-threads", no_argument, 0, 'k'}, 810 810 {"user-load", no_argument, 0, 'U'}, 811 - {"event", required_argument, 0, 'e'}, 812 811 {"no-irq", no_argument, 0, '0'}, 813 812 {"no-thread", no_argument, 0, '1'}, 814 813 {"no-header", no_argument, 0, '2'}, ··· 818 831 {"deepest-idle-state", required_argument, 0, '\4'}, 819 832 {"on-threshold", required_argument, 0, '\5'}, 820 833 {"on-end", required_argument, 0, '\6'}, 834 + {"bpf-action", required_argument, 0, '\7'}, 821 835 {0, 0, 0, 0} 822 836 }; 823 837 824 - c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:i:knp:P:s:t::T:uU0123456:7:8:9\1\2:\3:", 838 + if (common_parse_options(argc, argv, &params->common)) 839 + continue; 840 + 841 + c = getopt_long(argc, argv, "a:b:E:hi:knp:s:t::T:uU0123456:7:8:9\1\2:\3:", 825 842 long_options, NULL); 826 843 827 844 /* detect the end of the options. */ ··· 848 857 trace_output = "timerlat_trace.txt"; 849 858 850 859 break; 851 - case 'c': 852 - retval = parse_cpu_set(optarg, &params->common.monitored_cpus); 853 - if (retval) 854 - fatal("Invalid -c cpu list"); 855 - params->common.cpus = optarg; 856 - break; 857 - case 'C': 858 - params->common.cgroup = 1; 859 - params->common.cgroup_name = parse_optional_arg(argc, argv); 860 - break; 861 860 case 'b': 862 861 params->common.hist.bucket_size = get_llong_from_str(optarg); 863 862 if (params->common.hist.bucket_size == 0 || 864 863 params->common.hist.bucket_size >= 1000000) 865 864 fatal("Bucket size needs to be > 0 and <= 1000000"); 866 - break; 867 - case 'D': 868 - config_debug = 1; 869 - break; 870 - case 'd': 871 - params->common.duration = parse_seconds_duration(optarg); 872 - if (!params->common.duration) 873 - fatal("Invalid -D duration"); 874 - break; 875 - case 'e': 876 - tevent = trace_event_alloc(optarg); 877 - if (!tevent) 878 - fatal("Error alloc trace event"); 879 - 880 - if (params->common.events) 881 - tevent->next = params->common.events; 882 - 883 - params->common.events = tevent; 884 865 break; 885 866 case 'E': 886 867 params->common.hist.entries = get_llong_from_str(optarg); ··· 863 900 case 'h': 864 901 case '?': 865 902 timerlat_hist_usage(); 866 - break; 867 - case 'H': 868 - params->common.hk_cpus = 1; 869 - retval = parse_cpu_set(optarg, &params->common.hk_cpu_set); 870 - if (retval) 871 - fatal("Error parsing house keeping CPUs"); 872 903 break; 873 904 case 'i': 874 905 params->common.stop_us = get_llong_from_str(optarg); ··· 877 920 params->timerlat_period_us = get_llong_from_str(optarg); 878 921 if (params->timerlat_period_us > 1000000) 879 922 fatal("Period longer than 1 s"); 880 - break; 881 - case 'P': 882 - retval = parse_prio(optarg, &params->common.sched_param); 883 - if (retval == -1) 884 - fatal("Invalid -P priority"); 885 - params->common.set_sched = 1; 886 923 break; 887 924 case 's': 888 925 params->print_stack = get_llong_from_str(optarg); ··· 962 1011 "timerlat_trace.txt"); 963 1012 if (retval) 964 1013 fatal("Invalid action %s", optarg); 1014 + break; 1015 + case '\7': 1016 + params->bpf_action_program = optarg; 965 1017 break; 966 1018 default: 967 1019 fatal("Invalid option");
+17 -63
tools/tracing/rtla/src/timerlat_top.c
··· 11 11 #include <unistd.h> 12 12 #include <stdio.h> 13 13 #include <time.h> 14 - #include <errno.h> 15 14 #include <sched.h> 16 15 #include <pthread.h> 17 16 ··· 475 476 */ 476 477 static void timerlat_top_usage(void) 477 478 { 478 - int i; 479 - 480 - static const char *const msg[] = { 481 - "", 482 - " usage: rtla timerlat [top] [-h] [-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\", 479 + static const char *const msg_start[] = { 480 + "[-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\", 483 481 " [[-t [file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\", 484 482 " [-P priority] [--dma-latency us] [--aa-only us] [-C [cgroup_name]] [-u|-k] [--warm-up s] [--deepest-idle-state n]", 485 - "", 486 - " -h/--help: print this menu", 483 + NULL, 484 + }; 485 + 486 + static const char *const msg_opts[] = { 487 487 " -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit", 488 488 " --aa-only us: stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)", 489 489 " -p/--period us: timerlat period in us", ··· 517 519 " --deepest-idle-state n: only go down to idle state n on cpus used by timerlat to reduce exit from idle latency", 518 520 " --on-threshold <action>: define action to be executed at latency threshold, multiple are allowed", 519 521 " --on-end: define action to be executed at measurement end, multiple are allowed", 522 + " --bpf-action <program>: load and execute BPF program when latency threshold is exceeded", 520 523 NULL, 521 524 }; 522 525 523 - fprintf(stderr, "rtla timerlat top: a per-cpu summary of the timer latency (version %s)\n", 524 - VERSION); 525 - 526 - for (i = 0; msg[i]; i++) 527 - fprintf(stderr, "%s\n", msg[i]); 528 - 529 - exit(EXIT_SUCCESS); 526 + common_usage("timerlat", "top", "a per-cpu summary of the timer latency", 527 + msg_start, msg_opts); 530 528 } 531 529 532 530 /* ··· 532 538 *timerlat_top_parse_args(int argc, char **argv) 533 539 { 534 540 struct timerlat_params *params; 535 - struct trace_events *tevent; 536 541 long long auto_thresh; 537 542 int retval; 538 543 int c; ··· 559 566 while (1) { 560 567 static struct option long_options[] = { 561 568 {"auto", required_argument, 0, 'a'}, 562 - {"cpus", required_argument, 0, 'c'}, 563 - {"cgroup", optional_argument, 0, 'C'}, 564 - {"debug", no_argument, 0, 'D'}, 565 - {"duration", required_argument, 0, 'd'}, 566 - {"event", required_argument, 0, 'e'}, 567 569 {"help", no_argument, 0, 'h'}, 568 - {"house-keeping", required_argument, 0, 'H'}, 569 570 {"irq", required_argument, 0, 'i'}, 570 571 {"nano", no_argument, 0, 'n'}, 571 572 {"period", required_argument, 0, 'p'}, 572 - {"priority", required_argument, 0, 'P'}, 573 573 {"quiet", no_argument, 0, 'q'}, 574 574 {"stack", required_argument, 0, 's'}, 575 575 {"thread", required_argument, 0, 'T'}, ··· 581 595 {"deepest-idle-state", required_argument, 0, '8'}, 582 596 {"on-threshold", required_argument, 0, '9'}, 583 597 {"on-end", required_argument, 0, '\1'}, 598 + {"bpf-action", required_argument, 0, '\2'}, 584 599 {0, 0, 0, 0} 585 600 }; 586 601 587 - c = getopt_long(argc, argv, "a:c:C::d:De:hH:i:knp:P:qs:t::T:uU0:1:2:345:6:7:", 602 + if (common_parse_options(argc, argv, &params->common)) 603 + continue; 604 + 605 + c = getopt_long(argc, argv, "a:hi:knp:qs:t::T:uU0:1:2:345:6:7:", 588 606 long_options, NULL); 589 607 590 608 /* detect the end of the options. */ ··· 625 635 /* set aa_only to avoid parsing the trace */ 626 636 params->common.aa_only = 1; 627 637 break; 628 - case 'c': 629 - retval = parse_cpu_set(optarg, &params->common.monitored_cpus); 630 - if (retval) 631 - fatal("Invalid -c cpu list"); 632 - params->common.cpus = optarg; 633 - break; 634 - case 'C': 635 - params->common.cgroup = 1; 636 - params->common.cgroup_name = optarg; 637 - break; 638 - case 'D': 639 - config_debug = 1; 640 - break; 641 - case 'd': 642 - params->common.duration = parse_seconds_duration(optarg); 643 - if (!params->common.duration) 644 - fatal("Invalid -d duration"); 645 - break; 646 - case 'e': 647 - tevent = trace_event_alloc(optarg); 648 - if (!tevent) 649 - fatal("Error alloc trace event"); 650 - 651 - if (params->common.events) 652 - tevent->next = params->common.events; 653 - params->common.events = tevent; 654 - break; 655 638 case 'h': 656 639 case '?': 657 640 timerlat_top_usage(); 658 - break; 659 - case 'H': 660 - params->common.hk_cpus = 1; 661 - retval = parse_cpu_set(optarg, &params->common.hk_cpu_set); 662 - if (retval) 663 - fatal("Error parsing house keeping CPUs"); 664 641 break; 665 642 case 'i': 666 643 params->common.stop_us = get_llong_from_str(optarg); ··· 642 685 params->timerlat_period_us = get_llong_from_str(optarg); 643 686 if (params->timerlat_period_us > 1000000) 644 687 fatal("Period longer than 1 s"); 645 - break; 646 - case 'P': 647 - retval = parse_prio(optarg, &params->common.sched_param); 648 - if (retval == -1) 649 - fatal("Invalid -P priority"); 650 - params->common.set_sched = 1; 651 688 break; 652 689 case 'q': 653 690 params->common.quiet = 1; ··· 712 761 "timerlat_trace.txt"); 713 762 if (retval) 714 763 fatal("Invalid action %s", optarg); 764 + break; 765 + case '\2': 766 + params->bpf_action_program = optarg; 715 767 break; 716 768 default: 717 769 fatal("Invalid option");
-1
tools/tracing/rtla/src/trace.c
··· 2 2 #define _GNU_SOURCE 3 3 #include <sys/sendfile.h> 4 4 #include <tracefs.h> 5 - #include <signal.h> 6 5 #include <stdlib.h> 7 6 #include <unistd.h> 8 7 #include <errno.h>
+65 -35
tools/tracing/rtla/src/utils.c
··· 17 17 #include <fcntl.h> 18 18 #include <sched.h> 19 19 #include <stdio.h> 20 + #include <limits.h> 20 21 21 22 #include "utils.h" 22 23 ··· 113 112 * Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set 114 113 * filling cpu_set_t argument. 115 114 * 116 - * Returns 1 on success, 0 otherwise. 115 + * Returns 0 on success, 1 otherwise. 117 116 */ 118 117 int parse_cpu_set(char *cpu_list, cpu_set_t *set) 119 118 { ··· 315 314 if (retval <= 0) 316 315 return 0; 317 316 317 + buffer[MAX_PATH-1] = '\0'; 318 318 retval = strncmp(comm_prefix, buffer, strlen(comm_prefix)); 319 319 if (retval) 320 320 return 0; ··· 339 337 struct dirent *proc_entry; 340 338 DIR *procfs; 341 339 int retval; 340 + int pid; 342 341 343 342 if (strlen(comm_prefix) >= MAX_PATH) { 344 343 err_msg("Command prefix is too long: %d < strlen(%s)\n", ··· 359 356 if (!retval) 360 357 continue; 361 358 359 + if (strtoi(proc_entry->d_name, &pid)) { 360 + err_msg("'%s' is not a valid pid", proc_entry->d_name); 361 + goto out_err; 362 + } 362 363 /* procfs_is_workload_pid confirmed it is a pid */ 363 - retval = __set_sched_attr(atoi(proc_entry->d_name), attr); 364 + retval = __set_sched_attr(pid, attr); 364 365 if (retval) { 365 366 err_msg("Error setting sched attributes for pid:%s\n", proc_entry->d_name); 366 367 goto out_err; ··· 749 742 if (fd < 0) 750 743 return 0; 751 744 745 + memset(path, 0, sizeof(path)); 752 746 retval = read(fd, path, MAX_PATH); 753 747 754 748 close(fd); ··· 757 749 if (retval <= 0) 758 750 return 0; 759 751 752 + path[MAX_PATH-1] = '\0'; 760 753 start = path; 761 754 762 755 start = strstr(start, ":"); ··· 793 784 } 794 785 795 786 /* 796 - * set_comm_cgroup - Set cgroup to pid_t pid 787 + * open_cgroup_procs - Open the cgroup.procs file for the given cgroup 797 788 * 798 - * If cgroup argument is not NULL, the threads will move to the given cgroup. 799 - * Otherwise, the cgroup of the calling, i.e., rtla, thread will be used. 789 + * If cgroup argument is not NULL, the cgroup.procs file for that cgroup 790 + * will be opened. Otherwise, the cgroup of the calling, i.e., rtla, thread 791 + * will be used. 800 792 * 801 793 * Supports cgroup v2. 802 794 * 803 - * Returns 1 on success, 0 otherwise. 795 + * Returns the file descriptor on success, -1 otherwise. 804 796 */ 805 - int set_pid_cgroup(pid_t pid, const char *cgroup) 797 + static int open_cgroup_procs(const char *cgroup) 806 798 { 807 799 char cgroup_path[MAX_PATH - strlen("/cgroup.procs")]; 808 800 char cgroup_procs[MAX_PATH]; 809 - char pid_str[24]; 810 801 int retval; 811 802 int cg_fd; 812 803 813 804 retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path)); 814 805 if (!retval) { 815 806 err_msg("Did not find cgroupv2 mount point\n"); 816 - return 0; 807 + return -1; 817 808 } 818 809 819 810 if (!cgroup) { ··· 821 812 sizeof(cgroup_path) - strlen(cgroup_path)); 822 813 if (!retval) { 823 814 err_msg("Did not find self cgroup\n"); 824 - return 0; 815 + return -1; 825 816 } 826 817 } else { 827 818 snprintf(&cgroup_path[strlen(cgroup_path)], ··· 833 824 debug_msg("Using cgroup path at: %s\n", cgroup_procs); 834 825 835 826 cg_fd = open(cgroup_procs, O_RDWR); 827 + if (cg_fd < 0) 828 + return -1; 829 + 830 + return cg_fd; 831 + } 832 + 833 + /* 834 + * set_pid_cgroup - Set cgroup to pid_t pid 835 + * 836 + * If cgroup argument is not NULL, the threads will move to the given cgroup. 837 + * Otherwise, the cgroup of the calling, i.e., rtla, thread will be used. 838 + * 839 + * Supports cgroup v2. 840 + * 841 + * Returns 1 on success, 0 otherwise. 842 + */ 843 + int set_pid_cgroup(pid_t pid, const char *cgroup) 844 + { 845 + char pid_str[24]; 846 + int retval; 847 + int cg_fd; 848 + 849 + cg_fd = open_cgroup_procs(cgroup); 836 850 if (cg_fd < 0) 837 851 return 0; 838 852 ··· 885 853 */ 886 854 int set_comm_cgroup(const char *comm_prefix, const char *cgroup) 887 855 { 888 - char cgroup_path[MAX_PATH - strlen("/cgroup.procs")]; 889 - char cgroup_procs[MAX_PATH]; 890 856 struct dirent *proc_entry; 891 857 DIR *procfs; 892 858 int retval; ··· 896 866 return 0; 897 867 } 898 868 899 - retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path)); 900 - if (!retval) { 901 - err_msg("Did not find cgroupv2 mount point\n"); 902 - return 0; 903 - } 904 - 905 - if (!cgroup) { 906 - retval = get_self_cgroup(&cgroup_path[strlen(cgroup_path)], 907 - sizeof(cgroup_path) - strlen(cgroup_path)); 908 - if (!retval) { 909 - err_msg("Did not find self cgroup\n"); 910 - return 0; 911 - } 912 - } else { 913 - snprintf(&cgroup_path[strlen(cgroup_path)], 914 - sizeof(cgroup_path) - strlen(cgroup_path), "%s/", cgroup); 915 - } 916 - 917 - snprintf(cgroup_procs, MAX_PATH, "%s/cgroup.procs", cgroup_path); 918 - 919 - debug_msg("Using cgroup path at: %s\n", cgroup_procs); 920 - 921 - cg_fd = open(cgroup_procs, O_RDWR); 869 + cg_fd = open_cgroup_procs(cgroup); 922 870 if (cg_fd < 0) 923 871 return 0; 924 872 ··· 1007 999 } else { 1008 1000 return NULL; 1009 1001 } 1002 + } 1003 + 1004 + /* 1005 + * strtoi - convert string to integer with error checking 1006 + * 1007 + * Returns 0 on success, -1 if conversion fails or result is out of int range. 1008 + */ 1009 + int strtoi(const char *s, int *res) 1010 + { 1011 + char *end_ptr; 1012 + long lres; 1013 + 1014 + if (!*s) 1015 + return -1; 1016 + 1017 + errno = 0; 1018 + lres = strtol(s, &end_ptr, 0); 1019 + if (errno || *end_ptr || lres > INT_MAX || lres < INT_MIN) 1020 + return -1; 1021 + 1022 + *res = (int) lres; 1023 + return 0; 1010 1024 }
+6 -4
tools/tracing/rtla/src/utils.h
··· 3 3 #include <stdint.h> 4 4 #include <time.h> 5 5 #include <sched.h> 6 + #include <stdbool.h> 7 + #include <stdlib.h> 6 8 7 9 /* 8 10 * '18446744073709551615\0' ··· 26 24 long parse_seconds_duration(char *val); 27 25 void get_duration(time_t start_time, char *output, int output_size); 28 26 29 - int parse_cpu_list(char *cpu_list, char **monitored_cpus); 30 27 char *parse_optional_arg(int argc, char **argv); 31 28 long long get_llong_from_str(char *start); 32 29 ··· 83 82 static inline int have_libcpupower_support(void) { return 0; } 84 83 #endif /* HAVE_LIBCPUPOWER_SUPPORT */ 85 84 int auto_house_keeping(cpu_set_t *monitored_cpus); 85 + __attribute__((__warn_unused_result__)) int strtoi(const char *s, int *res); 86 86 87 87 #define ns_to_usf(x) (((double)x/1000)) 88 88 #define ns_to_per(total, part) ((part * 100) / (double)total) 89 89 90 90 enum result { 91 - PASSED = 0, /* same as EXIT_SUCCESS */ 92 - ERROR = 1, /* same as EXIT_FAILURE, an error in arguments */ 93 - FAILED = 2, /* test hit the stop tracing condition */ 91 + PASSED = EXIT_SUCCESS, 92 + ERROR = EXIT_FAILURE, 93 + FAILED, /* test hit the stop tracing condition */ 94 94 };
+25
tools/tracing/rtla/tests/bpf/bpf_action_map.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/bpf.h> 3 + #include <bpf/bpf_tracing.h> 4 + 5 + char LICENSE[] SEC("license") = "GPL"; 6 + 7 + struct { 8 + __uint(type, BPF_MAP_TYPE_ARRAY); 9 + __uint(max_entries, 1); 10 + __type(key, unsigned int); 11 + __type(value, unsigned long long); 12 + } rtla_test_map SEC(".maps"); 13 + 14 + struct trace_event_raw_timerlat_sample; 15 + 16 + SEC("tp/timerlat_action") 17 + int action_handler(struct trace_event_raw_timerlat_sample *tp_args) 18 + { 19 + unsigned int key = 0; 20 + unsigned long long value = 42; 21 + 22 + bpf_map_update_elem(&rtla_test_map, &key, &value, BPF_ANY); 23 + 24 + return 0; 25 + }
-1
tools/tracing/rtla/tests/engine.sh
··· 105 105 [ "$1" == "" ] && continue 106 106 option=$(echo $1 | cut -d '=' -f 1) 107 107 value=$(echo $1 | cut -d '=' -f 2) 108 - echo "option: $option, value: $value" 109 108 echo "$value" > "/sys/kernel/tracing/osnoise/$option" || return 1 110 109 done 111 110 fi
+15
tools/tracing/rtla/tests/timerlat.t
··· 67 67 "timerlat hist -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$" 68 68 check "top with trace output at end" \ 69 69 "timerlat top -d 1s --on-end trace" 0 "^ Saving trace to timerlat_trace.txt$" 70 + 71 + # BPF action program tests 72 + if [ "$option" -eq 0 ] 73 + then 74 + # Test BPF action program properly in BPF mode 75 + [ -z "$BPFTOOL" ] && BPFTOOL=bpftool 76 + check "hist with BPF action program (BPF mode)" \ 77 + "timerlat hist -T 2 --bpf-action tests/bpf/bpf_action_map.o --on-threshold shell,command='$BPFTOOL map dump name rtla_test_map'" \ 78 + 2 '"value": 42' 79 + else 80 + # Test BPF action program failure in non-BPF mode 81 + check "hist with BPF action program (non-BPF mode)" \ 82 + "timerlat hist -T 2 --bpf-action tests/bpf/bpf_action_map.o" \ 83 + 1 "BPF actions are not supported in tracefs-only mode" 84 + fi 70 85 done 71 86 72 87 test_end