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