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