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