558 lines · c
1// SPDX-License-Identifier: GPL-2.02#define _GNU_SOURCE3#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 instance15 */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 insta39 */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_name52 */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 data60 */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 file69 */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 event110 *111 * If an event has a registered callback function, call it.112 * Otherwise, ignore the event.113 */114int115collect_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 instance131 */132void trace_instance_destroy(struct trace_instance *trace)133{134 if (trace->inst) {135 disable_tracer(trace->inst);136 destroy_instance(trace->inst);137 trace->inst = NULL;138 }139 140 if (trace->seq) {141 free(trace->seq);142 trace->seq = NULL;143 }144 145 if (trace->tep) {146 tep_free(trace->tep);147 trace->tep = NULL;148 }149}150 151/*152 * trace_instance_init - create an rtla trace instance153 *154 * It is more than the tracefs instance, as it contains other155 * things required for the tracing, such as the local events and156 * a seq file.157 *158 * Note that the trace instance is returned disabled. This allows159 * the tool to apply some other configs, like setting priority160 * to the kernel threads, before starting generating trace entries.161 */162int trace_instance_init(struct trace_instance *trace, char *tool_name)163{164 trace->seq = calloc(1, sizeof(*trace->seq));165 if (!trace->seq)166 goto out_err;167 168 trace_seq_init(trace->seq);169 170 trace->inst = create_instance(tool_name);171 if (!trace->inst)172 goto out_err;173 174 trace->tep = tracefs_local_events(NULL);175 if (!trace->tep)176 goto out_err;177 178 /*179 * Let the main enable the record after setting some other180 * things such as the priority of the tracer's threads.181 */182 tracefs_trace_off(trace->inst);183 184 return 0;185 186out_err:187 trace_instance_destroy(trace);188 return 1;189}190 191/*192 * trace_instance_start - start tracing a given rtla instance193 */194int trace_instance_start(struct trace_instance *trace)195{196 return tracefs_trace_on(trace->inst);197}198 199/*200 * trace_events_free - free a list of trace events201 */202static void trace_events_free(struct trace_events *events)203{204 struct trace_events *tevent = events;205 struct trace_events *free_event;206 207 while (tevent) {208 free_event = tevent;209 210 tevent = tevent->next;211 212 if (free_event->filter)213 free(free_event->filter);214 if (free_event->trigger)215 free(free_event->trigger);216 free(free_event->system);217 free(free_event);218 }219}220 221/*222 * trace_event_alloc - alloc and parse a single trace event223 */224struct trace_events *trace_event_alloc(const char *event_string)225{226 struct trace_events *tevent;227 228 tevent = calloc(1, sizeof(*tevent));229 if (!tevent)230 return NULL;231 232 tevent->system = strdup(event_string);233 if (!tevent->system) {234 free(tevent);235 return NULL;236 }237 238 tevent->event = strstr(tevent->system, ":");239 if (tevent->event) {240 *tevent->event = '\0';241 tevent->event = &tevent->event[1];242 }243 244 return tevent;245}246 247/*248 * trace_event_add_filter - record an event filter249 */250int trace_event_add_filter(struct trace_events *event, char *filter)251{252 if (event->filter)253 free(event->filter);254 255 event->filter = strdup(filter);256 if (!event->filter)257 return 1;258 259 return 0;260}261 262/*263 * trace_event_add_trigger - record an event trigger action264 */265int trace_event_add_trigger(struct trace_events *event, char *trigger)266{267 if (event->trigger)268 free(event->trigger);269 270 event->trigger = strdup(trigger);271 if (!event->trigger)272 return 1;273 274 return 0;275}276 277/*278 * trace_event_disable_filter - disable an event filter279 */280static void trace_event_disable_filter(struct trace_instance *instance,281 struct trace_events *tevent)282{283 char filter[1024];284 int retval;285 286 if (!tevent->filter)287 return;288 289 if (!tevent->filter_enabled)290 return;291 292 debug_msg("Disabling %s:%s filter %s\n", tevent->system,293 tevent->event ? : "*", tevent->filter);294 295 snprintf(filter, 1024, "!%s\n", tevent->filter);296 297 retval = tracefs_event_file_write(instance->inst, tevent->system,298 tevent->event, "filter", filter);299 if (retval < 0)300 err_msg("Error disabling %s:%s filter %s\n", tevent->system,301 tevent->event ? : "*", tevent->filter);302}303 304/*305 * trace_event_save_hist - save the content of an event hist306 *307 * If the trigger is a hist: one, save the content of the hist file.308 */309static void trace_event_save_hist(struct trace_instance *instance,310 struct trace_events *tevent)311{312 int retval, index, out_fd;313 mode_t mode = 0644;314 char path[1024];315 char *hist;316 317 if (!tevent)318 return;319 320 /* trigger enables hist */321 if (!tevent->trigger)322 return;323 324 /* is this a hist: trigger? */325 retval = strncmp(tevent->trigger, "hist:", strlen("hist:"));326 if (retval)327 return;328 329 snprintf(path, 1024, "%s_%s_hist.txt", tevent->system, tevent->event);330 331 printf(" Saving event %s:%s hist to %s\n", tevent->system, tevent->event, path);332 333 out_fd = creat(path, mode);334 if (out_fd < 0) {335 err_msg(" Failed to create %s output file\n", path);336 return;337 }338 339 hist = tracefs_event_file_read(instance->inst, tevent->system, tevent->event, "hist", 0);340 if (!hist) {341 err_msg(" Failed to read %s:%s hist file\n", tevent->system, tevent->event);342 goto out_close;343 }344 345 index = 0;346 do {347 index += write(out_fd, &hist[index], strlen(hist) - index);348 } while (index < strlen(hist));349 350 free(hist);351out_close:352 close(out_fd);353}354 355/*356 * trace_event_disable_trigger - disable an event trigger357 */358static void trace_event_disable_trigger(struct trace_instance *instance,359 struct trace_events *tevent)360{361 char trigger[1024];362 int retval;363 364 if (!tevent->trigger)365 return;366 367 if (!tevent->trigger_enabled)368 return;369 370 debug_msg("Disabling %s:%s trigger %s\n", tevent->system,371 tevent->event ? : "*", tevent->trigger);372 373 trace_event_save_hist(instance, tevent);374 375 snprintf(trigger, 1024, "!%s\n", tevent->trigger);376 377 retval = tracefs_event_file_write(instance->inst, tevent->system,378 tevent->event, "trigger", trigger);379 if (retval < 0)380 err_msg("Error disabling %s:%s trigger %s\n", tevent->system,381 tevent->event ? : "*", tevent->trigger);382}383 384/*385 * trace_events_disable - disable all trace events386 */387void trace_events_disable(struct trace_instance *instance,388 struct trace_events *events)389{390 struct trace_events *tevent = events;391 392 if (!events)393 return;394 395 while (tevent) {396 debug_msg("Disabling event %s:%s\n", tevent->system, tevent->event ? : "*");397 if (tevent->enabled) {398 trace_event_disable_filter(instance, tevent);399 trace_event_disable_trigger(instance, tevent);400 tracefs_event_disable(instance->inst, tevent->system, tevent->event);401 }402 403 tevent->enabled = 0;404 tevent = tevent->next;405 }406}407 408/*409 * trace_event_enable_filter - enable an event filter associated with an event410 */411static int trace_event_enable_filter(struct trace_instance *instance,412 struct trace_events *tevent)413{414 char filter[1024];415 int retval;416 417 if (!tevent->filter)418 return 0;419 420 if (!tevent->event) {421 err_msg("Filter %s applies only for single events, not for all %s:* events\n",422 tevent->filter, tevent->system);423 return 1;424 }425 426 snprintf(filter, 1024, "%s\n", tevent->filter);427 428 debug_msg("Enabling %s:%s filter %s\n", tevent->system,429 tevent->event ? : "*", tevent->filter);430 431 retval = tracefs_event_file_write(instance->inst, tevent->system,432 tevent->event, "filter", filter);433 if (retval < 0) {434 err_msg("Error enabling %s:%s filter %s\n", tevent->system,435 tevent->event ? : "*", tevent->filter);436 return 1;437 }438 439 tevent->filter_enabled = 1;440 return 0;441}442 443/*444 * trace_event_enable_trigger - enable an event trigger associated with an event445 */446static int trace_event_enable_trigger(struct trace_instance *instance,447 struct trace_events *tevent)448{449 char trigger[1024];450 int retval;451 452 if (!tevent->trigger)453 return 0;454 455 if (!tevent->event) {456 err_msg("Trigger %s applies only for single events, not for all %s:* events\n",457 tevent->trigger, tevent->system);458 return 1;459 }460 461 snprintf(trigger, 1024, "%s\n", tevent->trigger);462 463 debug_msg("Enabling %s:%s trigger %s\n", tevent->system,464 tevent->event ? : "*", tevent->trigger);465 466 retval = tracefs_event_file_write(instance->inst, tevent->system,467 tevent->event, "trigger", trigger);468 if (retval < 0) {469 err_msg("Error enabling %s:%s trigger %s\n", tevent->system,470 tevent->event ? : "*", tevent->trigger);471 return 1;472 }473 474 tevent->trigger_enabled = 1;475 476 return 0;477}478 479/*480 * trace_events_enable - enable all events481 */482int trace_events_enable(struct trace_instance *instance,483 struct trace_events *events)484{485 struct trace_events *tevent = events;486 int retval;487 488 while (tevent) {489 debug_msg("Enabling event %s:%s\n", tevent->system, tevent->event ? : "*");490 retval = tracefs_event_enable(instance->inst, tevent->system, tevent->event);491 if (retval < 0) {492 err_msg("Error enabling event %s:%s\n", tevent->system,493 tevent->event ? : "*");494 return 1;495 }496 497 retval = trace_event_enable_filter(instance, tevent);498 if (retval)499 return 1;500 501 retval = trace_event_enable_trigger(instance, tevent);502 if (retval)503 return 1;504 505 tevent->enabled = 1;506 tevent = tevent->next;507 }508 509 return 0;510}511 512/*513 * trace_events_destroy - disable and free all trace events514 */515void trace_events_destroy(struct trace_instance *instance,516 struct trace_events *events)517{518 if (!events)519 return;520 521 trace_events_disable(instance, events);522 trace_events_free(events);523}524 525int trace_is_off(struct trace_instance *tool, struct trace_instance *trace)526{527 /*528 * The tool instance is always present, it is the one used to collect529 * data.530 */531 if (!tracefs_trace_is_on(tool->inst))532 return 1;533 534 /*535 * The trace instance is only enabled when -t is set. IOW, when the system536 * is tracing.537 */538 if (trace && !tracefs_trace_is_on(trace->inst))539 return 1;540 541 return 0;542}543 544/*545 * trace_set_buffer_size - set the per-cpu tracing buffer size.546 */547int trace_set_buffer_size(struct trace_instance *trace, int size)548{549 int retval;550 551 debug_msg("Setting trace buffer size to %d Kb\n", size);552 retval = tracefs_instance_set_buffer_size(trace->inst, size, -1);553 if (retval)554 err_msg("Error setting trace buffer size\n");555 556 return retval;557}558