trace.c (11839B)
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 */ 16int 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 */ 40void 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 */ 53struct 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 */ 61void 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 */ 70int 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; 101out_close: 102 close(out_fd); 103out_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 */ 114int 115collect_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 */ 132void 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 */ 157int 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 181out_err: 182 trace_instance_destroy(trace); 183 return 1; 184} 185 186/* 187 * trace_instance_start - start tracing a given rtla instance 188 */ 189int 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 */ 197static 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 */ 219struct 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 */ 245int 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 */ 260int 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 */ 275static 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 */ 304static 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); 346out_close: 347 close(out_fd); 348} 349 350/* 351 * trace_event_disable_trigger - disable an event trigger 352 */ 353static 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 */ 382void 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 */ 406static 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 */ 441static 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 */ 477int 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 */ 510void 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 520int 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}