diff options
Diffstat (limited to 'tracecmd/trace-list.c')
-rw-r--r-- | tracecmd/trace-list.c | 760 |
1 files changed, 760 insertions, 0 deletions
diff --git a/tracecmd/trace-list.c b/tracecmd/trace-list.c new file mode 100644 index 00000000..fbf2882e --- /dev/null +++ b/tracecmd/trace-list.c @@ -0,0 +1,760 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> + * + */ + +#include <stdlib.h> +#include <sys/stat.h> + +#include "tracefs.h" +#include "trace-local.h" + + +static void dump_file_content(const char *path) +{ + char buf[BUFSIZ]; + ssize_t n; + FILE *fp; + + fp = fopen(path, "r"); + if (!fp) + die("reading %s", path); + + do { + n = fread(buf, 1, BUFSIZ, fp); + if (n > 0) + fwrite(buf, 1, n, stdout); + } while (n > 0); + fclose(fp); +} + + + +void show_instance_file(struct buffer_instance *instance, const char *name) +{ + char *path; + + path = tracefs_instance_get_file(instance->tracefs, name); + dump_file_content(path); + tracefs_put_tracing_file(path); +} + +enum { + SHOW_EVENT_FORMAT = 1 << 0, + SHOW_EVENT_FILTER = 1 << 1, + SHOW_EVENT_TRIGGER = 1 << 2, + SHOW_EVENT_FULL = 1 << 3, +}; + + +void show_file(const char *name) +{ + char *path; + + path = tracefs_get_tracing_file(name); + dump_file_content(path); + tracefs_put_tracing_file(path); +} + +typedef int (*process_file_func)(char *buf, int len, int flags); + +static void process_file_re(process_file_func func, + const char *name, const char *re, int flags) +{ + regex_t reg; + char *path; + char *buf = NULL; + char *str; + FILE *fp; + ssize_t n; + size_t l = strlen(re); + + /* Just in case :-p */ + if (!re || l == 0) { + show_file(name); + return; + } + + /* Handle the newline at end of names for the user */ + str = malloc(l + 3); + if (!str) + die("Failed to allocate reg ex %s", re); + strcpy(str, re); + if (re[l-1] == '$') + strcpy(&str[l-1], "\n*$"); + + if (regcomp(®, str, REG_ICASE|REG_NOSUB)) + die("invalid function regex '%s'", re); + + free(str); + + path = tracefs_get_tracing_file(name); + fp = fopen(path, "r"); + if (!fp) + die("reading %s", path); + tracefs_put_tracing_file(path); + + do { + n = getline(&buf, &l, fp); + if (n > 0 && regexec(®, buf, 0, NULL, 0) == 0) + func(buf, n, flags); + } while (n > 0); + free(buf); + fclose(fp); + + regfree(®); +} + +static void show_event(process_file_func func, const char *system, + const char *event, int flags) +{ + char *buf; + int ret; + + ret = asprintf(&buf, "%s:%s", system, event); + if (ret < 0) + die("Can not allocate event"); + func(buf, strlen(buf), flags); + free(buf); +} + +static void show_system(process_file_func func, const char *system, int flags) +{ + char **events; + int e; + + events = tracefs_system_events(NULL, system); + if (!events) /* die? */ + return; + + for (e = 0; events[e]; e++) + show_event(func, system, events[e], flags); +} + +static void show_event_systems(process_file_func func, char **systems, int flags) +{ + int s; + + for (s = 0; systems[s]; s++) + show_system(func, systems[s], flags); +} + +static void match_system_events(process_file_func func, const char *system, + regex_t *reg, int flags) +{ + char **events; + int e; + + events = tracefs_system_events(NULL, system); + if (!events) /* die? */ + return; + for (e = 0; events[e]; e++) { + if (regexec(reg, events[e], 0, NULL, 0) == 0) + show_event(func, system, events[e], flags); + } + tracefs_list_free(events); +} + +static void process_events(process_file_func func, const char *re, int flags) +{ + const char *ftrace = "ftrace"; + regex_t system_reg; + regex_t event_reg; + char *str; + size_t l = strlen(re); + bool just_systems = true; + char **systems; + char *system; + char *event; + int s; + + systems = tracefs_event_systems(NULL); + if (!systems) + return process_file_re(func, "available_events", re, flags); + + if (!re || l == 0) { + show_event_systems(func, systems, flags); + return; + } + + str = strdup(re); + if (!str) + die("Can not allocate momory for regex"); + + system = strtok(str, ":"); + event = strtok(NULL, ""); + + if (regcomp(&system_reg, system, REG_ICASE|REG_NOSUB)) + die("invalid regex '%s'", system); + + if (event) { + if (regcomp(&event_reg, event, REG_ICASE|REG_NOSUB)) + die("invalid regex '%s'", event); + } else { + /* + * If the regex ends with ":", then event would be null, + * but we do not want to match events. + */ + if (re[l-1] != ':') + just_systems = false; + } + free(str); + + /* + * See if this matches the special ftrace system, as ftrace is not included + * in the systems list, but can get events from tracefs_system_events(). + */ + if (regexec(&system_reg, ftrace, 0, NULL, 0) == 0) { + if (!event) + show_system(func, ftrace, flags); + else + match_system_events(func, ftrace, &event_reg, flags); + } else if (!just_systems) { + match_system_events(func, ftrace, &system_reg, flags); + } + + for (s = 0; systems[s]; s++) { + + if (regexec(&system_reg, systems[s], 0, NULL, 0) == 0) { + if (!event) { + show_system(func, systems[s], flags); + continue; + } + match_system_events(func, systems[s], &event_reg, flags); + continue; + } + if (just_systems) + continue; + + match_system_events(func, systems[s], &system_reg, flags); + } + tracefs_list_free(systems); + + regfree(&system_reg); + if (event) + regfree(&event_reg); +} + +static int show_file_write(char *buf, int len, int flags) +{ + return fwrite(buf, 1, len, stdout); +} + +static void show_file_re(const char *name, const char *re) +{ + process_file_re(show_file_write, name, re, 0); +} + +static char *get_event_file(const char *type, char *buf, int len) +{ + char *system; + char *event; + char *path; + char *file; + int ret; + + if (buf[len-1] == '\n') + buf[len-1] = '\0'; + + system = strtok(buf, ":"); + if (!system) + die("no system found in %s", buf); + + event = strtok(NULL, ":"); + if (!event) + die("no event found in %s\n", buf); + + path = tracefs_get_tracing_file("events"); + ret = asprintf(&file, "%s/%s/%s/%s", path, system, event, type); + if (ret < 0) + die("Failed to allocate event file %s %s", system, event); + + tracefs_put_tracing_file(path); + + return file; +} + +static int event_filter_write(char *buf, int len, int flags) +{ + char *file; + + if (buf[len-1] == '\n') + buf[len-1] = '\0'; + + printf("%s\n", buf); + + file = get_event_file("filter", buf, len); + dump_file_content(file); + free(file); + printf("\n"); + + return 0; +} + +static int event_trigger_write(char *buf, int len, int flags) +{ + char *file; + + if (buf[len-1] == '\n') + buf[len-1] = '\0'; + + printf("%s\n", buf); + + file = get_event_file("trigger", buf, len); + dump_file_content(file); + free(file); + printf("\n"); + + return 0; +} + +static int event_format_write(char *fbuf, int len, int flags) +{ + char *file = get_event_file("format", fbuf, len); + char *buf = NULL; + size_t l; + FILE *fp; + bool full; + int n; + + full = flags & SHOW_EVENT_FULL; + + /* The get_event_file() crops system in fbuf */ + printf("system: %s\n", fbuf); + + /* Don't print the print fmt, it's ugly */ + + fp = fopen(file, "r"); + if (!fp) + die("reading %s", file); + + do { + n = getline(&buf, &l, fp); + if (n > 0) { + if (!full && strncmp(buf, "print fmt", 9) == 0) + break; + fwrite(buf, 1, n, stdout); + } + } while (n > 0); + fclose(fp); + free(buf); + free(file); + + return 0; +} + +static int event_name(char *buf, int len, int flags) +{ + printf("%s\n", buf); + + return 0; +} + +static void show_event_filter_re(const char *re) +{ + process_events(event_filter_write, re, 0); +} + + +static void show_event_trigger_re(const char *re) +{ + process_events(event_trigger_write, re, 0); +} + + +static void show_event_format_re(const char *re, int flags) +{ + process_events(event_format_write, re, flags); +} + +static void show_event_names_re(const char *re) +{ + process_events(event_name, re, 0); +} + +static void show_events(const char *eventre, int flags) +{ + if (flags && !eventre) + die("When specifying event files, an event must be named"); + + if (eventre) { + if (flags & SHOW_EVENT_FORMAT) + show_event_format_re(eventre, flags); + + else if (flags & SHOW_EVENT_FILTER) + show_event_filter_re(eventre); + + else if (flags & SHOW_EVENT_TRIGGER) + show_event_trigger_re(eventre); + else + show_event_names_re(eventre); + } else + show_file("available_events"); +} + + +static void show_tracers(void) +{ + show_file("available_tracers"); +} + +void show_options(const char *prefix, struct buffer_instance *buffer) +{ + struct tracefs_instance *instance = buffer ? buffer->tracefs : NULL; + struct dirent *dent; + struct stat st; + char *path; + DIR *dir; + + if (!prefix) + prefix = ""; + + path = tracefs_instance_get_file(instance, "options"); + if (!path) + goto show_file; + if (stat(path, &st) < 0) + goto show_file; + + if ((st.st_mode & S_IFMT) != S_IFDIR) + goto show_file; + + dir = opendir(path); + if (!dir) + die("Can not read instance directory"); + + while ((dent = readdir(dir))) { + const char *name = dent->d_name; + long long val; + char *file; + int ret; + + if (strcmp(name, ".") == 0 || + strcmp(name, "..") == 0) + continue; + + ret = asprintf(&file, "options/%s", name); + if (ret < 0) + die("Failed to allocate file name"); + ret = tracefs_instance_file_read_number(instance, file, &val); + if (!ret) { + if (val) + printf("%s%s\n", prefix, name); + else + printf("%sno%s\n", prefix, name); + } + free(file); + } + closedir(dir); + tracefs_put_tracing_file(path); + return; + + show_file: + tracefs_put_tracing_file(path); + show_file("trace_options"); +} + +static void show_clocks(void) +{ + char *clocks; + int size; + + clocks = tracefs_instance_file_read(NULL, "trace_clock", &size); + if (!clocks) + die("getting clocks"); + if (clocks[size - 1] == '\n') + clocks[size - 1] = 0; + + if (trace_tsc2nsec_is_supported()) + printf("%s %s\n", clocks, TSCNSEC_CLOCK); + else + printf("%s\n", clocks); + + free(clocks); +} + + +static void show_functions(const char *funcre) +{ + if (funcre) + show_file_re("available_filter_functions", funcre); + else + show_file("available_filter_functions"); +} + + +static void show_buffers(void) +{ + struct dirent *dent; + DIR *dir; + char *path; + int printed = 0; + + path = tracefs_get_tracing_file("instances"); + dir = opendir(path); + tracefs_put_tracing_file(path); + if (!dir) + die("Can not read instance directory"); + + while ((dent = readdir(dir))) { + const char *name = dent->d_name; + + if (strcmp(name, ".") == 0 || + strcmp(name, "..") == 0) + continue; + + printf("%s\n", name); + printed = 1; + } + closedir(dir); + + if (!printed) + printf("No buffer instances defined\n"); +} + + +static void show_systems(void) +{ + struct dirent *dent; + char *path; + DIR *dir; + + path = tracefs_get_tracing_file("events"); + dir = opendir(path); + + if (!dir) + die("Can not read events directory"); + + while ((dent = readdir(dir))) { + const char *name = dent->d_name; + struct stat st; + char *spath; + int ret; + + if (strcmp(name, ".") == 0 || + strcmp(name, "..") == 0) + continue; + + if (asprintf(&spath, "%s/%s", path, name) < 0) + continue; + ret = stat(spath, &st); + if (!ret && S_ISDIR(st.st_mode)) + printf("%s\n", name); + + free(spath); + } + + printf("\n"); + closedir(dir); + tracefs_put_tracing_file(path); +} + +static void show_plugin_options(void) +{ + struct tep_handle *pevent; + struct tep_plugin_list *list; + struct trace_seq s; + + tracecmd_ftrace_load_options(); + + pevent = tep_alloc(); + if (!pevent) + die("Can not allocate pevent\n"); + + trace_seq_init(&s); + + list = trace_load_plugins(pevent, 0); + tep_plugin_print_options(&s); + trace_seq_do_printf(&s); + tep_unload_plugins(list, pevent); + tep_free(pevent); +} + + +void trace_option(int argc, char **argv) +{ + show_plugin_options(); +} + + +static void show_plugins(void) +{ + struct tep_handle *pevent; + struct tep_plugin_list *list; + struct trace_seq s; + + pevent = tep_alloc(); + if (!pevent) + die("Can not allocate pevent\n"); + + trace_seq_init(&s); + + list = trace_load_plugins(pevent, 0); + tep_print_plugins(&s, " ", "\n", list); + + trace_seq_do_printf(&s); + tep_unload_plugins(list, pevent); + tep_free(pevent); +} + +static void show_compression(void) +{ + char **versions, **names; + int c, i; + + c = tracecmd_compress_protos_get(&names, &versions); + if (c <= 0) { + printf("No compression algorithms are supported\n"); + return; + } + printf("Supported compression algorithms:\n"); + for (i = 0; i < c; i++) + printf("\t%s, %s\n", names[i], versions[i]); + + free(names); + free(versions); +} + +void trace_list(int argc, char **argv) +{ + int events = 0; + int tracer = 0; + int options = 0; + int funcs = 0; + int buffers = 0; + int clocks = 0; + int plug = 0; + int plug_op = 0; + int flags = 0; + int systems = 0; + int show_all = 1; + int compression = 0; + int i; + const char *arg; + const char *funcre = NULL; + const char *eventre = NULL; + + for (i = 2; i < argc; i++) { + arg = NULL; + if (argv[i][0] == '-') { + if (i < argc - 1) { + if (argv[i+1][0] != '-') + arg = argv[i+1]; + } + switch (argv[i][1]) { + case 'h': + usage(argv); + break; + case 'e': + events = 1; + eventre = arg; + show_all = 0; + break; + case 'B': + buffers = 1; + show_all = 0; + break; + case 'C': + clocks = 1; + show_all = 0; + break; + case 'F': + flags |= SHOW_EVENT_FORMAT; + break; + case 'R': + flags |= SHOW_EVENT_TRIGGER; + break; + case 'l': + flags |= SHOW_EVENT_FILTER; + break; + case 'p': + case 't': + tracer = 1; + show_all = 0; + break; + case 'P': + plug = 1; + show_all = 0; + break; + case 'O': + plug_op = 1; + show_all = 0; + break; + case 'o': + options = 1; + show_all = 0; + break; + case 'f': + funcs = 1; + funcre = arg; + show_all = 0; + break; + case 's': + systems = 1; + show_all = 0; + break; + case 'c': + compression = 1; + show_all = 0; + break; + case '-': + if (strcmp(argv[i], "--debug") == 0) { + tracecmd_set_debug(true); + break; + } + if (strcmp(argv[i], "--full") == 0) { + flags |= SHOW_EVENT_FULL; + break; + } + fprintf(stderr, "list: invalid option -- '%s'\n", + argv[i]); + default: + fprintf(stderr, "list: invalid option -- '%c'\n", + argv[i][1]); + usage(argv); + } + } + } + + if (events) + show_events(eventre, flags); + + if (tracer) + show_tracers(); + + if (options) + show_options(NULL, NULL); + + if (plug) + show_plugins(); + + if (plug_op) + show_plugin_options(); + + if (funcs) + show_functions(funcre); + + if (buffers) + show_buffers(); + + if (clocks) + show_clocks(); + if (systems) + show_systems(); + if (compression) + show_compression(); + if (show_all) { + printf("event systems:\n"); + show_systems(); + printf("events:\n"); + show_events(NULL, 0); + printf("\ntracers:\n"); + show_tracers(); + printf("\noptions:\n"); + show_options(NULL, NULL); + show_compression(); + } + + return; + +} |