// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2021, VMware, Tzvetomir Stoyanov * */ #include #include #include #include #include "tracefs.h" #include "tracefs-local.h" /* File descriptors for Top level trace markers */ static int ftrace_marker_fd = -1; static int ftrace_marker_raw_fd = -1; static inline int *get_marker_fd(struct tracefs_instance *instance, bool raw) { if (raw) return instance ? &instance->ftrace_marker_raw_fd : &ftrace_marker_raw_fd; return instance ? &instance->ftrace_marker_fd : &ftrace_marker_fd; } static int marker_init(struct tracefs_instance *instance, bool raw) { const char *file = raw ? "trace_marker_raw" : "trace_marker"; pthread_mutex_t *lock = trace_get_lock(instance); int *fd = get_marker_fd(instance, raw); int ret; if (*fd >= 0) return 0; /* * The mutex is only to hold the integrity of the file descriptor * to prevent opening it more than once, or closing the same * file descriptor more than once. It does not protect against * one thread closing the file descriptor and another thread * writing to it. That is up to the application to prevent * from happening. */ pthread_mutex_lock(lock); /* The file could have been opened since we taken the lock */ if (*fd < 0) *fd = tracefs_instance_file_open(instance, file, O_WRONLY | O_CLOEXEC); ret = *fd < 0 ? -1 : 0; pthread_mutex_unlock(lock); return ret; } static void marker_close(struct tracefs_instance *instance, bool raw) { pthread_mutex_t *lock = trace_get_lock(instance); int *fd = get_marker_fd(instance, raw); pthread_mutex_lock(lock); if (*fd >= 0) { close(*fd); *fd = -1; } pthread_mutex_unlock(lock); } static int marker_write(struct tracefs_instance *instance, bool raw, void *data, int len) { int *fd = get_marker_fd(instance, raw); int ret; /* * The lock does not need to be taken for writes. As a write * does not modify the file descriptor. It's up to the application * to prevent it from being closed if another thread is doing a write. */ if (!data || len < 1) return -1; if (*fd < 0) { ret = marker_init(instance, raw); if (ret < 0) return ret; } ret = write(*fd, data, len); return ret == len ? 0 : -1; } /** * tracefs_print_init - Open trace marker of selected instance for writing * @instance: ftrace instance, can be NULL for top tracing instance. * * Returns 0 if the trace marker is opened successfully, or -1 in case of an error */ int tracefs_print_init(struct tracefs_instance *instance) { return marker_init(instance, false); } /** * tracefs_vprintf - Write a formatted string in the trace marker * @instance: ftrace instance, can be NULL for top tracing instance. * @fmt: pritnf formatted string * @ap: list of arguments for the formatted string * * If the trace marker of the desired instance is not open already, * this API will open it for writing. It will stay open until * tracefs_print_close() is called. * * Returns 0 if the string is written correctly, or -1 in case of an error */ int tracefs_vprintf(struct tracefs_instance *instance, const char *fmt, va_list ap) { char *str = NULL; int ret; ret = vasprintf(&str, fmt, ap); if (ret < 0) return ret; ret = marker_write(instance, false, str, strlen(str)); free(str); return ret; } /** * tracefs_printf - Write a formatted string in the trace marker * @instance: ftrace instance, can be NULL for top tracing instance. * @fmt: pritnf formatted string with variable arguments ... * * If the trace marker of the desired instance is not open already, * this API will open it for writing. It will stay open until * tracefs_print_close() is called. * * Returns 0 if the string is written correctly, or -1 in case of an error */ int tracefs_printf(struct tracefs_instance *instance, const char *fmt, ...) { va_list ap; int ret; va_start(ap, fmt); ret = tracefs_vprintf(instance, fmt, ap); va_end(ap); return ret; } /** * tracefs_print_close - Close trace marker of selected instance * @instance: ftrace instance, can be NULL for top tracing instance. * * Closes the trace marker, previously opened with any of the other tracefs_print APIs */ void tracefs_print_close(struct tracefs_instance *instance) { marker_close(instance, false); } /** * tracefs_binary_init - Open raw trace marker of selected instance for writing * @instance: ftrace instance, can be NULL for top tracing instance. * * Returns 0 if the raw trace marker is opened successfully, or -1 in case of an error */ int tracefs_binary_init(struct tracefs_instance *instance) { return marker_init(instance, true); } /** * tracefs_binary_write - Write binary data in the raw trace marker * @instance: ftrace instance, can be NULL for top tracing instance. * @data: binary data, that is going to be written in the trace marker * @len: length of the @data * * If the raw trace marker of the desired instance is not open already, * this API will open it for writing. It will stay open until * tracefs_binary_close() is called. * * Returns 0 if the data is written correctly, or -1 in case of an error */ int tracefs_binary_write(struct tracefs_instance *instance, void *data, int len) { return marker_write(instance, true, data, len); } /** * tracefs_binary_close - Close raw trace marker of selected instance * @instance: ftrace instance, can be NULL for top tracing instance. * * Closes the raw trace marker, previously opened with any of the other tracefs_binary APIs */ void tracefs_binary_close(struct tracefs_instance *instance) { marker_close(instance, true); }