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