1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 4 #include <pthread.h> 5 #include <signal.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <getopt.h> 9 #include <sys/sysinfo.h> 10 11 #include "common.h" 12 13 struct osnoise_tool *trace_tool; 14 volatile int stop_tracing; 15 int nr_cpus; 16 17 static void stop_trace(int sig) 18 { 19 if (stop_tracing) { 20 /* 21 * Stop requested twice in a row; abort event processing and 22 * exit immediately 23 */ 24 if (trace_tool) 25 tracefs_iterate_stop(trace_tool->trace.inst); 26 return; 27 } 28 stop_tracing = 1; 29 if (trace_tool) { 30 trace_instance_stop(&trace_tool->trace); 31 if (trace_tool->record) 32 trace_instance_stop(&trace_tool->record->trace); 33 } 34 } 35 36 /* 37 * set_signals - handles the signal to stop the tool 38 */ 39 static void set_signals(struct common_params *params) 40 { 41 signal(SIGINT, stop_trace); 42 if (params->duration) { 43 signal(SIGALRM, stop_trace); 44 alarm(params->duration); 45 } 46 } 47 48 /* 49 * unset_signals - unsets the signals to stop the tool 50 */ 51 static void unset_signals(struct common_params *params) 52 { 53 signal(SIGINT, SIG_DFL); 54 if (params->duration) { 55 alarm(0); 56 signal(SIGALRM, SIG_DFL); 57 } 58 } 59 60 /* 61 * getopt_auto - auto-generates optstring from long_options 62 */ 63 int getopt_auto(int argc, char **argv, const struct option *long_opts) 64 { 65 char opts[256]; 66 int n = 0; 67 68 for (int i = 0; long_opts[i].name; i++) { 69 if (long_opts[i].val < 32 || long_opts[i].val > 127) 70 continue; 71 72 if (n + 4 >= sizeof(opts)) 73 fatal("optstring buffer overflow"); 74 75 opts[n++] = long_opts[i].val; 76 77 if (long_opts[i].has_arg == required_argument) 78 opts[n++] = ':'; 79 else if (long_opts[i].has_arg == optional_argument) { 80 opts[n++] = ':'; 81 opts[n++] = ':'; 82 } 83 } 84 85 opts[n] = '\0'; 86 87 return getopt_long(argc, argv, opts, long_opts, NULL); 88 } 89 90 /* 91 * common_parse_options - parse common command line options 92 * 93 * @argc: argument count 94 * @argv: argument vector 95 * @common: common parameters structure 96 * 97 * Parse command line options that are common to all rtla tools. 98 * 99 * Returns: non zero if a common option was parsed, or 0 100 * if the option should be handled by tool-specific parsing. 101 */ 102 int common_parse_options(int argc, char **argv, struct common_params *common) 103 { 104 struct trace_events *tevent; 105 int saved_state = optind; 106 int c; 107 108 static struct option long_options[] = { 109 {"cpus", required_argument, 0, 'c'}, 110 {"cgroup", optional_argument, 0, 'C'}, 111 {"debug", no_argument, 0, 'D'}, 112 {"duration", required_argument, 0, 'd'}, 113 {"event", required_argument, 0, 'e'}, 114 {"house-keeping", required_argument, 0, 'H'}, 115 {"priority", required_argument, 0, 'P'}, 116 {0, 0, 0, 0} 117 }; 118 119 opterr = 0; 120 c = getopt_auto(argc, argv, long_options); 121 opterr = 1; 122 123 switch (c) { 124 case 'c': 125 if (parse_cpu_set(optarg, &common->monitored_cpus)) 126 fatal("Invalid -c cpu list"); 127 common->cpus = optarg; 128 break; 129 case 'C': 130 common->cgroup = 1; 131 common->cgroup_name = parse_optional_arg(argc, argv); 132 break; 133 case 'D': 134 config_debug = 1; 135 break; 136 case 'd': 137 common->duration = parse_seconds_duration(optarg); 138 if (!common->duration) 139 fatal("Invalid -d duration"); 140 break; 141 case 'e': 142 tevent = trace_event_alloc(optarg); 143 if (!tevent) 144 fatal("Error alloc trace event"); 145 146 if (common->events) 147 tevent->next = common->events; 148 common->events = tevent; 149 break; 150 case 'H': 151 common->hk_cpus = 1; 152 if (parse_cpu_set(optarg, &common->hk_cpu_set)) 153 fatal("Error parsing house keeping CPUs"); 154 break; 155 case 'P': 156 if (parse_prio(optarg, &common->sched_param) == -1) 157 fatal("Invalid -P priority"); 158 common->set_sched = 1; 159 break; 160 default: 161 optind = saved_state; 162 return 0; 163 } 164 165 return c; 166 } 167 168 /* 169 * common_apply_config - apply common configs to the initialized tool 170 */ 171 int 172 common_apply_config(struct osnoise_tool *tool, struct common_params *params) 173 { 174 int retval, i; 175 176 if (!params->sleep_time) 177 params->sleep_time = 1; 178 179 retval = osnoise_set_cpus(tool->context, params->cpus ? params->cpus : "all"); 180 if (retval) { 181 err_msg("Failed to apply CPUs config\n"); 182 goto out_err; 183 } 184 185 if (!params->cpus) { 186 for (i = 0; i < nr_cpus; i++) 187 CPU_SET(i, ¶ms->monitored_cpus); 188 } 189 190 if (params->hk_cpus) { 191 retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set), 192 ¶ms->hk_cpu_set); 193 if (retval == -1) { 194 err_msg("Failed to set rtla to the house keeping CPUs\n"); 195 goto out_err; 196 } 197 } else if (params->cpus) { 198 /* 199 * Even if the user do not set a house-keeping CPU, try to 200 * move rtla to a CPU set different to the one where the user 201 * set the workload to run. 202 * 203 * No need to check results as this is an automatic attempt. 204 */ 205 auto_house_keeping(¶ms->monitored_cpus); 206 } 207 208 /* 209 * Set workload according to type of thread if the kernel supports it. 210 * On kernels without support, user threads will have already failed 211 * on missing fd, and kernel threads do not need it. 212 */ 213 retval = osnoise_set_workload(tool->context, params->kernel_workload); 214 if (retval < -1) { 215 err_msg("Failed to set OSNOISE_WORKLOAD option\n"); 216 goto out_err; 217 } 218 219 return 0; 220 221 out_err: 222 return -1; 223 } 224 225 226 /** 227 * common_threshold_handler - handle latency threshold overflow 228 * @tool: pointer to the osnoise_tool instance containing trace contexts 229 * 230 * Executes the configured threshold actions (e.g., saving trace, printing, 231 * sending signals). If the continue flag is set (--on-threshold continue), 232 * restarts the auxiliary trace instances to continue monitoring. 233 * 234 * Return: 0 for success, -1 for error. 235 */ 236 int 237 common_threshold_handler(const struct osnoise_tool *tool) 238 { 239 actions_perform(&tool->params->threshold_actions); 240 241 if (!should_continue_tracing(tool->params)) 242 /* continue flag not set, break */ 243 return 0; 244 245 /* continue action reached, re-enable tracing */ 246 if (tool->record && trace_instance_start(&tool->record->trace)) 247 goto err; 248 if (tool->aa && trace_instance_start(&tool->aa->trace)) 249 goto err; 250 251 return 0; 252 253 err: 254 err_msg("Error restarting trace\n"); 255 return -1; 256 } 257 258 int run_tool(struct tool_ops *ops, int argc, char *argv[]) 259 { 260 struct common_params *params; 261 enum result return_value = ERROR; 262 struct osnoise_tool *tool; 263 bool stopped; 264 int retval; 265 266 nr_cpus = get_nprocs_conf(); 267 params = ops->parse_args(argc, argv); 268 if (!params) 269 exit(1); 270 271 tool = ops->init_tool(params); 272 if (!tool) { 273 err_msg("Could not init osnoise tool\n"); 274 goto out_exit; 275 } 276 tool->ops = ops; 277 tool->params = params; 278 279 /* 280 * Expose the tool to signal handlers so they can stop the trace. 281 * Otherwise, rtla could loop indefinitely when overloaded. 282 */ 283 trace_tool = tool; 284 285 retval = ops->apply_config(tool); 286 if (retval) { 287 err_msg("Could not apply config\n"); 288 goto out_free; 289 } 290 291 retval = enable_tracer_by_name(tool->trace.inst, ops->tracer); 292 if (retval) { 293 err_msg("Failed to enable %s tracer\n", ops->tracer); 294 goto out_free; 295 } 296 297 if (params->set_sched) { 298 retval = set_comm_sched_attr(ops->comm_prefix, ¶ms->sched_param); 299 if (retval) { 300 err_msg("Failed to set sched parameters\n"); 301 goto out_free; 302 } 303 } 304 305 if (params->cgroup && !params->user_data) { 306 retval = set_comm_cgroup(ops->comm_prefix, params->cgroup_name); 307 if (!retval) { 308 err_msg("Failed to move threads to cgroup\n"); 309 goto out_free; 310 } 311 } 312 313 314 if (params->threshold_actions.present[ACTION_TRACE_OUTPUT] || 315 params->end_actions.present[ACTION_TRACE_OUTPUT]) { 316 tool->record = osnoise_init_trace_tool(ops->tracer); 317 if (!tool->record) { 318 err_msg("Failed to enable the trace instance\n"); 319 goto out_free; 320 } 321 params->threshold_actions.trace_output_inst = tool->record->trace.inst; 322 params->end_actions.trace_output_inst = tool->record->trace.inst; 323 324 if (params->events) { 325 retval = trace_events_enable(&tool->record->trace, params->events); 326 if (retval) 327 goto out_trace; 328 } 329 330 if (params->buffer_size > 0) { 331 retval = trace_set_buffer_size(&tool->record->trace, params->buffer_size); 332 if (retval) 333 goto out_trace; 334 } 335 } 336 337 if (params->user_workload) { 338 pthread_t user_thread; 339 340 /* rtla asked to stop */ 341 params->user.should_run = 1; 342 /* all threads left */ 343 params->user.stopped_running = 0; 344 345 params->user.set = ¶ms->monitored_cpus; 346 if (params->set_sched) 347 params->user.sched_param = ¶ms->sched_param; 348 else 349 params->user.sched_param = NULL; 350 351 params->user.cgroup_name = params->cgroup_name; 352 353 retval = pthread_create(&user_thread, NULL, timerlat_u_dispatcher, ¶ms->user); 354 if (retval) { 355 err_msg("Error creating timerlat user-space threads\n"); 356 goto out_trace; 357 } 358 } 359 360 retval = ops->enable(tool); 361 if (retval) 362 goto out_trace; 363 364 tool->start_time = time(NULL); 365 set_signals(params); 366 367 retval = ops->main(tool); 368 if (retval) 369 goto out_signals; 370 371 if (params->user_workload && !params->user.stopped_running) { 372 params->user.should_run = 0; 373 sleep(1); 374 } 375 376 ops->print_stats(tool); 377 378 actions_perform(¶ms->end_actions); 379 380 return_value = PASSED; 381 382 stopped = osnoise_trace_is_off(tool, tool->record) && !stop_tracing; 383 if (stopped) { 384 printf("%s hit stop tracing\n", ops->tracer); 385 return_value = FAILED; 386 } 387 388 if (ops->analyze) 389 ops->analyze(tool, stopped); 390 391 out_signals: 392 unset_signals(params); 393 out_trace: 394 trace_events_destroy(&tool->record->trace, params->events); 395 params->events = NULL; 396 out_free: 397 ops->free(tool); 398 osnoise_destroy_tool(tool->record); 399 osnoise_destroy_tool(tool); 400 actions_destroy(¶ms->threshold_actions); 401 actions_destroy(¶ms->end_actions); 402 free(params); 403 out_exit: 404 exit(return_value); 405 } 406 407 int top_main_loop(struct osnoise_tool *tool) 408 { 409 struct common_params *params = tool->params; 410 struct trace_instance *trace = &tool->trace; 411 struct osnoise_tool *record = tool->record; 412 int retval; 413 414 while (!stop_tracing) { 415 sleep(params->sleep_time); 416 417 if (params->aa_only && !osnoise_trace_is_off(tool, record)) 418 continue; 419 420 retval = tracefs_iterate_raw_events(trace->tep, 421 trace->inst, 422 NULL, 423 0, 424 collect_registered_events, 425 trace); 426 if (retval < 0) { 427 err_msg("Error iterating on events\n"); 428 return retval; 429 } 430 431 if (!params->quiet) 432 tool->ops->print_stats(tool); 433 434 if (osnoise_trace_is_off(tool, record)) { 435 if (stop_tracing) 436 /* stop tracing requested, do not perform actions */ 437 return 0; 438 439 retval = common_threshold_handler(tool); 440 if (retval) 441 return retval; 442 443 444 if (!should_continue_tracing(params)) 445 return 0; 446 447 trace_instance_start(trace); 448 } 449 450 /* is there still any user-threads ? */ 451 if (params->user_workload) { 452 if (params->user.stopped_running) { 453 debug_msg("timerlat user space threads stopped!\n"); 454 break; 455 } 456 } 457 } 458 459 return 0; 460 } 461 462 int hist_main_loop(struct osnoise_tool *tool) 463 { 464 struct common_params *params = tool->params; 465 struct trace_instance *trace = &tool->trace; 466 int retval = 0; 467 468 while (!stop_tracing) { 469 sleep(params->sleep_time); 470 471 retval = tracefs_iterate_raw_events(trace->tep, 472 trace->inst, 473 NULL, 474 0, 475 collect_registered_events, 476 trace); 477 if (retval < 0) { 478 err_msg("Error iterating on events\n"); 479 break; 480 } 481 482 if (osnoise_trace_is_off(tool, tool->record)) { 483 if (stop_tracing) 484 /* stop tracing requested, do not perform actions */ 485 break; 486 487 retval = common_threshold_handler(tool); 488 if (retval) 489 return retval; 490 491 if (!should_continue_tracing(params)) 492 return 0; 493 494 trace_instance_start(trace); 495 } 496 497 /* is there still any user-threads ? */ 498 if (params->user_workload) { 499 if (params->user.stopped_running) { 500 debug_msg("user-space threads stopped!\n"); 501 break; 502 } 503 } 504 } 505 506 return retval; 507 } 508 509 int osn_set_stop(struct osnoise_tool *tool) 510 { 511 struct common_params *params = tool->params; 512 int retval; 513 514 retval = osnoise_set_stop_us(tool->context, params->stop_us); 515 if (retval) { 516 err_msg("Failed to set stop us\n"); 517 return retval; 518 } 519 520 retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us); 521 if (retval) { 522 err_msg("Failed to set stop total us\n"); 523 return retval; 524 } 525 526 return 0; 527 } 528 529 static void print_msg_array(const char * const *msgs) 530 { 531 if (!msgs) 532 return; 533 534 for (int i = 0; msgs[i]; i++) 535 fprintf(stderr, "%s\n", msgs[i]); 536 } 537 538 /* 539 * common_usage - print complete usage information 540 */ 541 void common_usage(const char *tool, const char *mode, 542 const char *desc, const char * const *start_msgs, const char * const *opt_msgs) 543 { 544 static const char * const common_options[] = { 545 " -h/--help: print this menu", 546 NULL 547 }; 548 fprintf(stderr, "rtla %s", tool); 549 if (strcmp(mode, "")) 550 fprintf(stderr, " %s", mode); 551 fprintf(stderr, ": %s (version %s)\n\n", desc, VERSION); 552 fprintf(stderr, " usage: [rtla] %s ", tool); 553 554 if (strcmp(mode, "top") == 0) 555 fprintf(stderr, "[top] [-h] "); 556 else 557 fprintf(stderr, "%s [-h] ", mode); 558 559 print_msg_array(start_msgs); 560 fprintf(stderr, "\n"); 561 print_msg_array(common_options); 562 print_msg_array(opt_msgs); 563 564 exit(EXIT_SUCCESS); 565 } 566