// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * */ #include #include #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; }