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