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