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