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