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