1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <sys/sendfile.h> 4 #include <tracefs.h> 5 #include <signal.h> 6 #include <stdlib.h> 7 #include <unistd.h> 8 #include <errno.h> 9 10 #include "trace.h" 11 #include "utils.h" 12 13 /* 14 * enable_tracer_by_name - enable a tracer on the given instance 15 */ 16 int enable_tracer_by_name(struct tracefs_instance *inst, const char *tracer_name) 17 { 18 enum tracefs_tracers tracer; 19 int retval; 20 21 tracer = TRACEFS_TRACER_CUSTOM; 22 23 debug_msg("Enabling %s tracer\n", tracer_name); 24 25 retval = tracefs_tracer_set(inst, tracer, tracer_name); 26 if (retval < 0) { 27 if (errno == ENODEV) 28 err_msg("Tracer %s not found!\n", tracer_name); 29 30 err_msg("Failed to enable the %s tracer\n", tracer_name); 31 return -1; 32 } 33 34 return 0; 35 } 36 37 /* 38 * disable_tracer - set nop tracer to the insta 39 */ 40 void disable_tracer(struct tracefs_instance *inst) 41 { 42 enum tracefs_tracers t = TRACEFS_TRACER_NOP; 43 int retval; 44 45 retval = tracefs_tracer_set(inst, t); 46 if (retval < 0) 47 err_msg("Oops, error disabling tracer\n"); 48 } 49 50 /* 51 * create_instance - create a trace instance with *instance_name 52 */ 53 struct tracefs_instance *create_instance(char *instance_name) 54 { 55 return tracefs_instance_create(instance_name); 56 } 57 58 /* 59 * destroy_instance - remove a trace instance and free the data 60 */ 61 void destroy_instance(struct tracefs_instance *inst) 62 { 63 tracefs_instance_destroy(inst); 64 tracefs_instance_free(inst); 65 } 66 67 /* 68 * save_trace_to_file - save the trace output of the instance to the file 69 */ 70 int save_trace_to_file(struct tracefs_instance *inst, const char *filename) 71 { 72 const char *file = "trace"; 73 mode_t mode = 0644; 74 char buffer[4096]; 75 int out_fd, in_fd; 76 int retval = -1; 77 78 in_fd = tracefs_instance_file_open(inst, file, O_RDONLY); 79 if (in_fd < 0) { 80 err_msg("Failed to open trace file\n"); 81 return -1; 82 } 83 84 out_fd = creat(filename, mode); 85 if (out_fd < 0) { 86 err_msg("Failed to create output file %s\n", filename); 87 goto out_close_in; 88 } 89 90 do { 91 retval = read(in_fd, buffer, sizeof(buffer)); 92 if (retval <= 0) 93 goto out_close; 94 95 retval = write(out_fd, buffer, retval); 96 if (retval < 0) 97 goto out_close; 98 } while (retval > 0); 99 100 retval = 0; 101 out_close: 102 close(out_fd); 103 out_close_in: 104 close(in_fd); 105 return retval; 106 } 107 108 /* 109 * collect_registered_events - call the existing callback function for the event 110 * 111 * If an event has a registered callback function, call it. 112 * Otherwise, ignore the event. 113 */ 114 int 115 collect_registered_events(struct tep_event *event, struct tep_record *record, 116 int cpu, void *context) 117 { 118 struct trace_instance *trace = context; 119 struct trace_seq *s = trace->seq; 120 121 if (!event->handler) 122 return 0; 123 124 event->handler(s, record, event, context); 125 126 return 0; 127 } 128 129 /* 130 * trace_instance_destroy - destroy and free a rtla trace instance 131 */ 132 void trace_instance_destroy(struct trace_instance *trace) 133 { 134 if (trace->inst) { 135 disable_tracer(trace->inst); 136 destroy_instance(trace->inst); 137 } 138 139 if (trace->seq) 140 free(trace->seq); 141 142 if (trace->tep) 143 tep_free(trace->tep); 144 } 145 146 /* 147 * trace_instance_init - create an rtla trace instance 148 * 149 * It is more than the tracefs instance, as it contains other 150 * things required for the tracing, such as the local events and 151 * a seq file. 152 * 153 * Note that the trace instance is returned disabled. This allows 154 * the tool to apply some other configs, like setting priority 155 * to the kernel threads, before starting generating trace entries. 156 */ 157 int trace_instance_init(struct trace_instance *trace, char *tool_name) 158 { 159 trace->seq = calloc(1, sizeof(*trace->seq)); 160 if (!trace->seq) 161 goto out_err; 162 163 trace_seq_init(trace->seq); 164 165 trace->inst = create_instance(tool_name); 166 if (!trace->inst) 167 goto out_err; 168 169 trace->tep = tracefs_local_events(NULL); 170 if (!trace->tep) 171 goto out_err; 172 173 /* 174 * Let the main enable the record after setting some other 175 * things such as the priority of the tracer's threads. 176 */ 177 tracefs_trace_off(trace->inst); 178 179 return 0; 180 181 out_err: 182 trace_instance_destroy(trace); 183 return 1; 184 } 185 186 /* 187 * trace_instance_start - start tracing a given rtla instance 188 */ 189 int trace_instance_start(struct trace_instance *trace) 190 { 191 return tracefs_trace_on(trace->inst); 192 } 193 194 /* 195 * trace_events_free - free a list of trace events 196 */ 197 static void trace_events_free(struct trace_events *events) 198 { 199 struct trace_events *tevent = events; 200 struct trace_events *free_event; 201 202 while (tevent) { 203 free_event = tevent; 204 205 tevent = tevent->next; 206 207 if (free_event->filter) 208 free(free_event->filter); 209 if (free_event->trigger) 210 free(free_event->trigger); 211 free(free_event->system); 212 free(free_event); 213 } 214 } 215 216 /* 217 * trace_event_alloc - alloc and parse a single trace event 218 */ 219 struct trace_events *trace_event_alloc(const char *event_string) 220 { 221 struct trace_events *tevent; 222 223 tevent = calloc(1, sizeof(*tevent)); 224 if (!tevent) 225 return NULL; 226 227 tevent->system = strdup(event_string); 228 if (!tevent->system) { 229 free(tevent); 230 return NULL; 231 } 232 233 tevent->event = strstr(tevent->system, ":"); 234 if (tevent->event) { 235 *tevent->event = '\0'; 236 tevent->event = &tevent->event[1]; 237 } 238 239 return tevent; 240 } 241 242 /* 243 * trace_event_add_filter - record an event filter 244 */ 245 int trace_event_add_filter(struct trace_events *event, char *filter) 246 { 247 if (event->filter) 248 free(event->filter); 249 250 event->filter = strdup(filter); 251 if (!event->filter) 252 return 1; 253 254 return 0; 255 } 256 257 /* 258 * trace_event_add_trigger - record an event trigger action 259 */ 260 int trace_event_add_trigger(struct trace_events *event, char *trigger) 261 { 262 if (event->trigger) 263 free(event->trigger); 264 265 event->trigger = strdup(trigger); 266 if (!event->trigger) 267 return 1; 268 269 return 0; 270 } 271 272 /* 273 * trace_event_disable_filter - disable an event filter 274 */ 275 static void trace_event_disable_filter(struct trace_instance *instance, 276 struct trace_events *tevent) 277 { 278 char filter[1024]; 279 int retval; 280 281 if (!tevent->filter) 282 return; 283 284 if (!tevent->filter_enabled) 285 return; 286 287 debug_msg("Disabling %s:%s filter %s\n", tevent->system, 288 tevent->event ? : "*", tevent->filter); 289 290 snprintf(filter, 1024, "!%s\n", tevent->filter); 291 292 retval = tracefs_event_file_write(instance->inst, tevent->system, 293 tevent->event, "filter", filter); 294 if (retval < 0) 295 err_msg("Error disabling %s:%s filter %s\n", tevent->system, 296 tevent->event ? : "*", tevent->filter); 297 } 298 299 /* 300 * trace_event_save_hist - save the content of an event hist 301 * 302 * If the trigger is a hist: one, save the content of the hist file. 303 */ 304 static void trace_event_save_hist(struct trace_instance *instance, 305 struct trace_events *tevent) 306 { 307 int retval, index, out_fd; 308 mode_t mode = 0644; 309 char path[1024]; 310 char *hist; 311 312 if (!tevent) 313 return; 314 315 /* trigger enables hist */ 316 if (!tevent->trigger) 317 return; 318 319 /* is this a hist: trigger? */ 320 retval = strncmp(tevent->trigger, "hist:", strlen("hist:")); 321 if (retval) 322 return; 323 324 snprintf(path, 1024, "%s_%s_hist.txt", tevent->system, tevent->event); 325 326 printf(" Saving event %s:%s hist to %s\n", tevent->system, tevent->event, path); 327 328 out_fd = creat(path, mode); 329 if (out_fd < 0) { 330 err_msg(" Failed to create %s output file\n", path); 331 return; 332 } 333 334 hist = tracefs_event_file_read(instance->inst, tevent->system, tevent->event, "hist", 0); 335 if (!hist) { 336 err_msg(" Failed to read %s:%s hist file\n", tevent->system, tevent->event); 337 goto out_close; 338 } 339 340 index = 0; 341 do { 342 index += write(out_fd, &hist[index], strlen(hist) - index); 343 } while (index < strlen(hist)); 344 345 free(hist); 346 out_close: 347 close(out_fd); 348 } 349 350 /* 351 * trace_event_disable_trigger - disable an event trigger 352 */ 353 static void trace_event_disable_trigger(struct trace_instance *instance, 354 struct trace_events *tevent) 355 { 356 char trigger[1024]; 357 int retval; 358 359 if (!tevent->trigger) 360 return; 361 362 if (!tevent->trigger_enabled) 363 return; 364 365 debug_msg("Disabling %s:%s trigger %s\n", tevent->system, 366 tevent->event ? : "*", tevent->trigger); 367 368 trace_event_save_hist(instance, tevent); 369 370 snprintf(trigger, 1024, "!%s\n", tevent->trigger); 371 372 retval = tracefs_event_file_write(instance->inst, tevent->system, 373 tevent->event, "trigger", trigger); 374 if (retval < 0) 375 err_msg("Error disabling %s:%s trigger %s\n", tevent->system, 376 tevent->event ? : "*", tevent->trigger); 377 } 378 379 /* 380 * trace_events_disable - disable all trace events 381 */ 382 void trace_events_disable(struct trace_instance *instance, 383 struct trace_events *events) 384 { 385 struct trace_events *tevent = events; 386 387 if (!events) 388 return; 389 390 while (tevent) { 391 debug_msg("Disabling event %s:%s\n", tevent->system, tevent->event ? : "*"); 392 if (tevent->enabled) { 393 trace_event_disable_filter(instance, tevent); 394 trace_event_disable_trigger(instance, tevent); 395 tracefs_event_disable(instance->inst, tevent->system, tevent->event); 396 } 397 398 tevent->enabled = 0; 399 tevent = tevent->next; 400 } 401 } 402 403 /* 404 * trace_event_enable_filter - enable an event filter associated with an event 405 */ 406 static int trace_event_enable_filter(struct trace_instance *instance, 407 struct trace_events *tevent) 408 { 409 char filter[1024]; 410 int retval; 411 412 if (!tevent->filter) 413 return 0; 414 415 if (!tevent->event) { 416 err_msg("Filter %s applies only for single events, not for all %s:* events\n", 417 tevent->filter, tevent->system); 418 return 1; 419 } 420 421 snprintf(filter, 1024, "%s\n", tevent->filter); 422 423 debug_msg("Enabling %s:%s filter %s\n", tevent->system, 424 tevent->event ? : "*", tevent->filter); 425 426 retval = tracefs_event_file_write(instance->inst, tevent->system, 427 tevent->event, "filter", filter); 428 if (retval < 0) { 429 err_msg("Error enabling %s:%s filter %s\n", tevent->system, 430 tevent->event ? : "*", tevent->filter); 431 return 1; 432 } 433 434 tevent->filter_enabled = 1; 435 return 0; 436 } 437 438 /* 439 * trace_event_enable_trigger - enable an event trigger associated with an event 440 */ 441 static int trace_event_enable_trigger(struct trace_instance *instance, 442 struct trace_events *tevent) 443 { 444 char trigger[1024]; 445 int retval; 446 447 if (!tevent->trigger) 448 return 0; 449 450 if (!tevent->event) { 451 err_msg("Trigger %s applies only for single events, not for all %s:* events\n", 452 tevent->trigger, tevent->system); 453 return 1; 454 } 455 456 snprintf(trigger, 1024, "%s\n", tevent->trigger); 457 458 debug_msg("Enabling %s:%s trigger %s\n", tevent->system, 459 tevent->event ? : "*", tevent->trigger); 460 461 retval = tracefs_event_file_write(instance->inst, tevent->system, 462 tevent->event, "trigger", trigger); 463 if (retval < 0) { 464 err_msg("Error enabling %s:%s trigger %s\n", tevent->system, 465 tevent->event ? : "*", tevent->trigger); 466 return 1; 467 } 468 469 tevent->trigger_enabled = 1; 470 471 return 0; 472 } 473 474 /* 475 * trace_events_enable - enable all events 476 */ 477 int trace_events_enable(struct trace_instance *instance, 478 struct trace_events *events) 479 { 480 struct trace_events *tevent = events; 481 int retval; 482 483 while (tevent) { 484 debug_msg("Enabling event %s:%s\n", tevent->system, tevent->event ? : "*"); 485 retval = tracefs_event_enable(instance->inst, tevent->system, tevent->event); 486 if (retval < 0) { 487 err_msg("Error enabling event %s:%s\n", tevent->system, 488 tevent->event ? : "*"); 489 return 1; 490 } 491 492 retval = trace_event_enable_filter(instance, tevent); 493 if (retval) 494 return 1; 495 496 retval = trace_event_enable_trigger(instance, tevent); 497 if (retval) 498 return 1; 499 500 tevent->enabled = 1; 501 tevent = tevent->next; 502 } 503 504 return 0; 505 } 506 507 /* 508 * trace_events_destroy - disable and free all trace events 509 */ 510 void trace_events_destroy(struct trace_instance *instance, 511 struct trace_events *events) 512 { 513 if (!events) 514 return; 515 516 trace_events_disable(instance, events); 517 trace_events_free(events); 518 } 519 520 int trace_is_off(struct trace_instance *tool, struct trace_instance *trace) 521 { 522 /* 523 * The tool instance is always present, it is the one used to collect 524 * data. 525 */ 526 if (!tracefs_trace_is_on(tool->inst)) 527 return 1; 528 529 /* 530 * The trace instance is only enabled when -t is set. IOW, when the system 531 * is tracing. 532 */ 533 if (trace && !tracefs_trace_is_on(trace->inst)) 534 return 1; 535 536 return 0; 537 } 538