|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include <stdlib.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 = get_instance_file(instance, name); | 
|  | dump_file_content(path); | 
|  | tracecmd_put_tracing_file(path); | 
|  | } | 
|  |  | 
|  | enum { | 
|  | SHOW_EVENT_FORMAT		= 1 << 0, | 
|  | SHOW_EVENT_FILTER		= 1 << 1, | 
|  | SHOW_EVENT_TRIGGER		= 1 << 2, | 
|  | }; | 
|  |  | 
|  |  | 
|  | void show_file(const char *name) | 
|  | { | 
|  | char *path; | 
|  |  | 
|  | path = tracecmd_get_tracing_file(name); | 
|  | dump_file_content(path); | 
|  | tracecmd_put_tracing_file(path); | 
|  | } | 
|  |  | 
|  | typedef int (*process_file_func)(char *buf, int len); | 
|  |  | 
|  | static void process_file_re(process_file_func func, | 
|  | const char *name, const char *re) | 
|  | { | 
|  | 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 = tracecmd_get_tracing_file(name); | 
|  | fp = fopen(path, "r"); | 
|  | if (!fp) | 
|  | die("reading %s", path); | 
|  | tracecmd_put_tracing_file(path); | 
|  |  | 
|  | do { | 
|  | n = getline(&buf, &l, fp); | 
|  | if (n > 0 && regexec(®, buf, 0, NULL, 0) == 0) | 
|  | func(buf, n); | 
|  | } while (n > 0); | 
|  | free(buf); | 
|  | fclose(fp); | 
|  |  | 
|  | regfree(®); | 
|  | } | 
|  |  | 
|  | static int show_file_write(char *buf, int len) | 
|  | { | 
|  | 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); | 
|  | } | 
|  |  | 
|  | 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 = tracecmd_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); | 
|  |  | 
|  | tracecmd_put_tracing_file(path); | 
|  |  | 
|  | return file; | 
|  | } | 
|  |  | 
|  | static int event_filter_write(char *buf, int len) | 
|  | { | 
|  | 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) | 
|  | { | 
|  | 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) | 
|  | { | 
|  | char *file = get_event_file("format", fbuf, len); | 
|  | char *buf = NULL; | 
|  | size_t l; | 
|  | FILE *fp; | 
|  | int n; | 
|  |  | 
|  | /* 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 (strncmp(buf, "print fmt", 9) == 0) | 
|  | break; | 
|  | fwrite(buf, 1, n, stdout); | 
|  | } | 
|  | } while (n > 0); | 
|  | fclose(fp); | 
|  | free(buf); | 
|  | free(file); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void show_event_filter_re(const char *re) | 
|  | { | 
|  | process_file_re(event_filter_write, "available_events", re); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void show_event_trigger_re(const char *re) | 
|  | { | 
|  | process_file_re(event_trigger_write, "available_events", re); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void show_event_format_re(const char *re) | 
|  | { | 
|  | process_file_re(event_format_write, "available_events", re); | 
|  | } | 
|  |  | 
|  |  | 
|  | 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); | 
|  |  | 
|  | else if (flags & SHOW_EVENT_FILTER) | 
|  | show_event_filter_re(eventre); | 
|  |  | 
|  | else if (flags & SHOW_EVENT_TRIGGER) | 
|  | show_event_trigger_re(eventre); | 
|  | else | 
|  | show_file_re("available_events", eventre); | 
|  | } else | 
|  | show_file("available_events"); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void show_tracers(void) | 
|  | { | 
|  | show_file("available_tracers"); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void show_options(void) | 
|  | { | 
|  | show_file("trace_options"); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void show_clocks(void) | 
|  | { | 
|  | show_file("trace_clock"); | 
|  | } | 
|  |  | 
|  |  | 
|  | 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 = tracecmd_get_tracing_file("instances"); | 
|  | dir = opendir(path); | 
|  | tracecmd_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_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 = tracecmd_load_plugins(pevent); | 
|  | trace_util_print_plugin_options(&s); | 
|  | trace_seq_do_printf(&s); | 
|  | tracecmd_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 = tracecmd_load_plugins(pevent); | 
|  | trace_util_print_plugins(&s, "  ", "\n", list); | 
|  | trace_seq_do_printf(&s); | 
|  | tracecmd_unload_plugins(list, pevent); | 
|  | tep_free(pevent); | 
|  | } | 
|  |  | 
|  |  | 
|  | 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 show_all = 1; | 
|  | 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 '-': | 
|  | if (strcmp(argv[i], "--debug") == 0) { | 
|  | debug = true; | 
|  | 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(); | 
|  |  | 
|  | if (plug) | 
|  | show_plugins(); | 
|  |  | 
|  | if (plug_op) | 
|  | show_plugin_options(); | 
|  |  | 
|  | if (funcs) | 
|  | show_functions(funcre); | 
|  |  | 
|  | if (buffers) | 
|  | show_buffers(); | 
|  |  | 
|  | if (clocks) | 
|  | show_clocks(); | 
|  |  | 
|  | if (show_all) { | 
|  | printf("events:\n"); | 
|  | show_events(NULL, 0); | 
|  | printf("\ntracers:\n"); | 
|  | show_tracers(); | 
|  | printf("\noptions:\n"); | 
|  | show_options(); | 
|  | } | 
|  |  | 
|  | return; | 
|  |  | 
|  | } |