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