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