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