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