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