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