blob: 02e43168a810f51fed2089da2571baf039f8a020 [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* Updates:
* Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
*
*/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#include <limits.h>
#include <errno.h>
#include "tracefs.h"
#include "tracefs-local.h"
#define TRACE_CTRL "tracing_on"
#define TRACE_FILTER "set_ftrace_filter"
static const char * const options_map[] = {
"unknown",
"annotate",
"bin",
"blk_cgname",
"blk_cgroup",
"blk_classic",
"block",
"context-info",
"disable_on_free",
"display-graph",
"event-fork",
"funcgraph-abstime",
"funcgraph-cpu",
"funcgraph-duration",
"funcgraph-irqs",
"funcgraph-overhead",
"funcgraph-overrun",
"funcgraph-proc",
"funcgraph-tail",
"func_stack_trace",
"function-fork",
"function-trace",
"graph-time",
"hex",
"irq-info",
"latency-format",
"markers",
"overwrite",
"pause-on-trace",
"printk-msg-only",
"print-parent",
"raw",
"record-cmd",
"record-tgid",
"sleep-time",
"stacktrace",
"sym-addr",
"sym-offset",
"sym-userobj",
"trace_printk",
"userstacktrace",
"verbose" };
static int trace_on_off(int fd, bool on)
{
const char *val = on ? "1" : "0";
int ret;
ret = write(fd, val, 1);
if (ret == 1)
return 0;
return -1;
}
static int trace_on_off_file(struct tracefs_instance *instance, bool on)
{
int ret;
int fd;
fd = tracefs_instance_file_open(instance, TRACE_CTRL, O_WRONLY);
if (fd < 0)
return -1;
ret = trace_on_off(fd, on);
close(fd);
return ret;
}
/**
* tracefs_trace_is_on - Check if writing traces to the ring buffer is enabled
* @instance: ftrace instance, can be NULL for the top instance
*
* Returns -1 in case of an error, 0 if tracing is disable or 1 if tracing
* is enabled.
*/
int tracefs_trace_is_on(struct tracefs_instance *instance)
{
long long res;
if (tracefs_instance_file_read_number(instance, TRACE_CTRL, &res) == 0)
return (int)res;
return -1;
}
/**
* tracefs_trace_on - Enable writing traces to the ring buffer of the given instance
* @instance: ftrace instance, can be NULL for the top instance
*
* Returns -1 in case of an error or 0 otherwise
*/
int tracefs_trace_on(struct tracefs_instance *instance)
{
return trace_on_off_file(instance, true);
}
/**
* tracefs_trace_off - Disable writing traces to the ring buffer of the given instance
* @instance: ftrace instance, can be NULL for the top instance
*
* Returns -1 in case of an error or 0 otherwise
*/
int tracefs_trace_off(struct tracefs_instance *instance)
{
return trace_on_off_file(instance, false);
}
/**
* tracefs_trace_on_fd - Enable writing traces to the ring buffer
* @fd: File descriptor to ftrace tracing_on file, previously opened
* with tracefs_trace_on_get_fd()
*
* Returns -1 in case of an error or 0 otherwise
*/
int tracefs_trace_on_fd(int fd)
{
if (fd < 0)
return -1;
return trace_on_off(fd, true);
}
/**
* tracefs_trace_off_fd - Disable writing traces to the ring buffer
* @fd: File descriptor to ftrace tracing_on file, previously opened
* with tracefs_trace_on_get_fd()
*
* Returns -1 in case of an error or 0 otherwise
*/
int tracefs_trace_off_fd(int fd)
{
if (fd < 0)
return -1;
return trace_on_off(fd, false);
}
/**
* tracefs_option_name - Get trace option name from id
* @id: trace option id
*
* Returns string with option name, or "unknown" in case of not known option id.
* The returned string must *not* be freed.
*/
const char *tracefs_option_name(enum tracefs_option_id id)
{
/* Make sure options map contains all the options */
BUILD_BUG_ON(ARRAY_SIZE(options_map) != TRACEFS_OPTION_MAX);
if (id < TRACEFS_OPTION_MAX)
return options_map[id];
return options_map[0];
}
/**
* tracefs_option_id - Get trace option ID from name
* @name: trace option name
*
* Returns trace option ID or TRACEFS_OPTION_INVALID in case of an error or
* unknown option name.
*/
enum tracefs_option_id tracefs_option_id(char *name)
{
int i;
if (!name)
return TRACEFS_OPTION_INVALID;
for (i = 0; i < TRACEFS_OPTION_MAX; i++) {
if (strlen(name) == strlen(options_map[i]) &&
!strcmp(options_map[i], name))
return i;
}
return TRACEFS_OPTION_INVALID;
}
static struct tracefs_options_mask *trace_get_options(struct tracefs_instance *instance,
bool enabled)
{
struct tracefs_options_mask *bitmask;
enum tracefs_option_id id;
char file[PATH_MAX];
struct dirent *dent;
char *dname = NULL;
DIR *dir = NULL;
long long val;
bitmask = calloc(1, sizeof(struct tracefs_options_mask));
if (!bitmask)
return NULL;
dname = tracefs_instance_get_file(instance, "options");
if (!dname)
goto error;
dir = opendir(dname);
if (!dir)
goto error;
while ((dent = readdir(dir))) {
if (*dent->d_name == '.')
continue;
if (enabled) {
snprintf(file, PATH_MAX, "options/%s", dent->d_name);
if (tracefs_instance_file_read_number(instance, file, &val) != 0 ||
val != 1)
continue;
}
id = tracefs_option_id(dent->d_name);
if (id != TRACEFS_OPTION_INVALID)
tracefs_option_set(bitmask, id);
}
closedir(dir);
tracefs_put_tracing_file(dname);
return bitmask;
error:
if (dir)
closedir(dir);
tracefs_put_tracing_file(dname);
free(bitmask);
return NULL;
}
/**
* tracefs_options_get_supported - Get all supported trace options in given instance
* @instance: ftrace instance, can be NULL for the top instance
*
* Returns allocated bitmask structure with all trace options, supported in given
* instance, or NULL in case of an error. The returned structure must be freed with free()
*/
struct tracefs_options_mask *tracefs_options_get_supported(struct tracefs_instance *instance)
{
return trace_get_options(instance, false);
}
/**
* tracefs_options_get_enabled - Get all currently enabled trace options in given instance
* @instance: ftrace instance, can be NULL for the top instance
*
* Returns allocated bitmask structure with all trace options, enabled in given
* instance, or NULL in case of an error. The returned structure must be freed with free()
*/
struct tracefs_options_mask *tracefs_options_get_enabled(struct tracefs_instance *instance)
{
return trace_get_options(instance, true);
}
static int trace_config_option(struct tracefs_instance *instance,
enum tracefs_option_id id, bool set)
{
char *set_str = set ? "1" : "0";
char file[PATH_MAX];
const char *name;
name = tracefs_option_name(id);
if (!name)
return -1;
snprintf(file, PATH_MAX, "options/%s", name);
if (strlen(set_str) != tracefs_instance_file_write(instance, file, set_str))
return -1;
return 0;
}
/**
* tracefs_option_enable - Enable trace option
* @instance: ftrace instance, can be NULL for the top instance
* @id: trace option id
*
* Returns -1 in case of an error or 0 otherwise
*/
int tracefs_option_enable(struct tracefs_instance *instance, enum tracefs_option_id id)
{
return trace_config_option(instance, id, true);
}
/**
* tracefs_option_diasble - Disable trace option
* @instance: ftrace instance, can be NULL for the top instance
* @id: trace option id
*
* Returns -1 in case of an error or 0 otherwise
*/
int tracefs_option_diasble(struct tracefs_instance *instance, enum tracefs_option_id id)
{
return trace_config_option(instance, id, false);
}
/**
* tracefs_option_is_supported - Check if an option is supported
* @instance: ftrace instance, can be NULL for the top instance
* @id: trace option id
*
* Returns true if an option with given id is supported by the system, false if
* it is not supported.
*/
bool tracefs_option_is_supported(struct tracefs_instance *instance, enum tracefs_option_id id)
{
const char *name = tracefs_option_name(id);
char file[PATH_MAX];
if (!name)
return false;
snprintf(file, PATH_MAX, "options/%s", name);
return tracefs_file_exists(instance, file);
}
/**
* tracefs_option_is_enabled - Check if an option is enabled in given instance
* @instance: ftrace instance, can be NULL for the top instance
* @id: trace option id
*
* Returns true if an option with given id is enabled in the given instance,
* false if it is not enabled.
*/
bool tracefs_option_is_enabled(struct tracefs_instance *instance, enum tracefs_option_id id)
{
const char *name = tracefs_option_name(id);
char file[PATH_MAX];
long long res;
if (!name)
return false;
snprintf(file, PATH_MAX, "options/%s", name);
if (!tracefs_instance_file_read_number(instance, file, &res) && res)
return true;
return false;
}
/**
* tracefs_option_is_set - Check if given option is set in the bitmask
* @options: Options bitmask
* @id: trace option id
*
* Returns true if an option with given id is set in the bitmask,
* false if it is not set.
*/
bool tracefs_option_is_set(struct tracefs_options_mask options, enum tracefs_option_id id)
{
if (id > TRACEFS_OPTION_INVALID)
return options.mask & (1ULL << (id - 1));
return false;
}
/**
* tracefs_option_set - Set option in options bitmask
* @options: Pointer to a bitmask with options
* @id: trace option id
*/
void tracefs_option_set(struct tracefs_options_mask *options, enum tracefs_option_id id)
{
if (options && id > TRACEFS_OPTION_INVALID)
options->mask |= (1ULL << (id - 1));
}
/**
* tracefs_option_clear - Clear option from options bitmask
* @options: Pointer to a bitmask with options
* @id: trace option id
*/
void tracefs_option_clear(struct tracefs_options_mask *options, enum tracefs_option_id id)
{
if (options && id > TRACEFS_OPTION_INVALID)
options->mask &= ~(1ULL << (id - 1));
}
static int controlled_write(int fd, const char **filters,
const char *module, const char ***errs)
{
const char **temp = NULL;
const char **e = NULL;
char *each_str = NULL;
int write_size = 0;
int size = 0;
int ret = 0;
int j = 0;
int i;
for (i = 0; filters[i]; i++) {
if (module)
write_size = asprintf(&each_str, "%s:mod:%s ", filters[i], module);
else
write_size = asprintf(&each_str, "%s ", filters[i]);
if (write_size < 0) {
ret = 1;
goto error;
}
size = write(fd, each_str, write_size);
/* compare written bytes*/
if (size < write_size) {
if (errs) {
temp = realloc(e, (j + 1) * (sizeof(char *)));
if (!temp) {
free(e);
ret = 1;
goto error;
} else
e = temp;
e[j++] = filters[i];
ret -= 1;
}
}
free(each_str);
each_str = NULL;
}
if (errs) {
temp = realloc(e, (j + 1) * (sizeof(char *)));
if (!temp) {
free(e);
ret = 1;
goto error;
} else
e = temp;
e[j] = NULL;
*errs = e;
}
error:
if (each_str)
free(each_str);
return ret;
}
/**
* tracefs_function_filter - write to set_ftrace_filter file to trace
* particular functions
* @instance: ftrace instance, can be NULL for top tracing instance
* @filters: An array of function names ending with a NULL pointer
* @module: Module to be traced
* @reset: set to true to reset the file before applying the filter
* @errs: A pointer to array of constant strings that will be allocated
* on negative return of this function, pointing to the filters that
* failed.May be NULL, in which case this field will be ignored.
*
* The @filters is an array of strings, where each string will be used
* to set a function or functions to be traced.
*
* If @reset is true, then all functions in the filter are cleared
* before adding functions from @filters. Otherwise, the functions set
* by @filters will be appended to the filter file
*
* returns -x on filter errors (where x is number of failed filter
* srtings) and if @errs is not NULL will be an allocated string array
* pointing to the strings in @filters that failed and must be freed
* with free().
*
* returns 1 on general errors not realted to setting the filter.
* @errs is not set even if supplied.
*
* return 0 on success and @errs is not set.
*/
int tracefs_function_filter(struct tracefs_instance *instance, const char **filters,
const char *module, bool reset, const char ***errs)
{
char *ftrace_filter_path;
int ret = 0;
int flags;
int fd;
if (!filters)
return 1;
ftrace_filter_path = tracefs_instance_get_file(instance, TRACE_FILTER);
if (!ftrace_filter_path)
return 1;
flags = reset ? O_TRUNC : O_APPEND;
fd = open(ftrace_filter_path, O_WRONLY | flags);
tracefs_put_tracing_file(ftrace_filter_path);
if (fd < 0)
return 1;
ret = controlled_write(fd, filters, module, errs);
close(fd);
return ret;
}