| // SPDX-License-Identifier: LGPL-2.1 | |
| /* | |
| * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com> | |
| * | |
| */ | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #include <stdio.h> | |
| #include <pthread.h> | |
| #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); | |
| } |