aboutsummaryrefslogtreecommitdiff
path: root/tracecmd/trace-stat.c
diff options
context:
space:
mode:
Diffstat (limited to 'tracecmd/trace-stat.c')
-rw-r--r--tracecmd/trace-stat.c926
1 files changed, 926 insertions, 0 deletions
diff --git a/tracecmd/trace-stat.c b/tracecmd/trace-stat.c
new file mode 100644
index 00000000..a5fb777b
--- /dev/null
+++ b/tracecmd/trace-stat.c
@@ -0,0 +1,926 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2014 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "tracefs.h"
+#include "trace-local.h"
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+
+static inline int is_top_instance(struct buffer_instance *instance)
+{
+ return instance == &top_instance;
+}
+
+static int get_instance_file_fd(struct buffer_instance *instance,
+ const char *file)
+{
+ char *path;
+ int fd;
+
+ path = tracefs_instance_get_file(instance->tracefs, file);
+ fd = open(path, O_RDONLY);
+ tracefs_put_tracing_file(path);
+
+ return fd;
+}
+
+char *strstrip(char *str)
+{
+ char *s;
+
+ if (!str)
+ return NULL;
+
+ s = str + strlen(str) - 1;
+ while (s >= str && isspace(*s))
+ s--;
+ s++;
+ *s = '\0';
+
+ for (s = str; *s && isspace(*s); s++)
+ ;
+
+ return s;
+}
+
+/* FIXME: append_file() is duplicated and could be consolidated */
+char *append_file(const char *dir, const char *name)
+{
+ char *file;
+ int ret;
+
+ ret = asprintf(&file, "%s/%s", dir, name);
+ if (ret < 0)
+ die("Failed to allocate %s/%s", dir, name);
+
+ return file;
+}
+
+static char *get_fd_content(int fd, const char *file)
+{
+ char *str = NULL;
+ int cnt = 0;
+ int ret;
+
+ for (;;) {
+ str = realloc(str, BUFSIZ * ++cnt);
+ if (!str)
+ die("malloc");
+ ret = read(fd, str + BUFSIZ * (cnt - 1), BUFSIZ);
+ if (ret < 0)
+ die("reading %s\n", file);
+ if (ret < BUFSIZ)
+ break;
+ }
+ str[BUFSIZ * (cnt-1) + ret] = 0;
+
+ return str;
+}
+
+char *get_file_content(const char *file)
+{
+ char *str;
+ int fd;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ str = get_fd_content(fd, file);
+ close(fd);
+
+ return str;
+}
+
+static char *get_instance_file_content(struct buffer_instance *instance,
+ const char *file)
+{
+ char *str = NULL;
+ int fd;
+
+ fd = get_instance_file_fd(instance, file);
+ if (fd < 0)
+ return NULL;
+
+ str = get_fd_content(fd, file);
+
+ close(fd);
+ return str;
+}
+
+static void report_file(struct buffer_instance *instance,
+ char *name, char *def_value, char *description)
+{
+ char *str;
+ char *cont;
+
+ if (!tracefs_file_exists(instance->tracefs, name))
+ return;
+ str = get_instance_file_content(instance, name);
+ if (!str)
+ return;
+ cont = strstrip(str);
+ if (cont[0] && strcmp(cont, def_value) != 0)
+ printf("\n%s%s\n", description, cont);
+
+ free(str);
+}
+
+static int report_instance(const char *name, void *data)
+{
+ bool *first = (bool *)data;
+
+ if (*first) {
+ *first = false;
+ printf("\nInstances:\n");
+ }
+ printf(" %s\n", name);
+ return 0;
+}
+
+static void report_instances(void)
+{
+ bool first = true;
+
+ tracefs_instances_walk(report_instance, &first);
+}
+
+struct event_iter *trace_event_iter_alloc(const char *path)
+{
+ struct event_iter *iter;
+
+ iter = malloc(sizeof(*iter));
+ if (!iter)
+ die("Failed to allocate event_iter for path %s", path);
+ memset(iter, 0, sizeof(*iter));
+
+ iter->system_dir = opendir(path);
+ if (!iter->system_dir)
+ die("opendir");
+
+ return iter;
+}
+
+enum event_iter_type
+trace_event_iter_next(struct event_iter *iter, const char *path, const char *system)
+{
+ struct dirent *dent;
+
+ if (system && !iter->event_dir) {
+ char *event;
+ struct stat st;
+
+ event = append_file(path, system);
+
+ stat(event, &st);
+ if (!S_ISDIR(st.st_mode)) {
+ free(event);
+ goto do_system;
+ }
+
+ iter->event_dir = opendir(event);
+ if (!iter->event_dir)
+ die("opendir %s", event);
+ free(event);
+ }
+
+ if (iter->event_dir) {
+ while ((dent = readdir(iter->event_dir))) {
+ const char *name = dent->d_name;
+
+ if (strcmp(name, ".") == 0 ||
+ strcmp(name, "..") == 0)
+ continue;
+
+ iter->event_dent = dent;
+ return EVENT_ITER_EVENT;
+ }
+ closedir(iter->event_dir);
+ iter->event_dir = NULL;
+ }
+
+ do_system:
+ while ((dent = readdir(iter->system_dir))) {
+ const char *name = dent->d_name;
+
+ if (strcmp(name, ".") == 0 ||
+ strcmp(name, "..") == 0)
+ continue;
+
+ iter->system_dent = dent;
+
+ return EVENT_ITER_SYSTEM;
+ }
+
+ return EVENT_ITER_NONE;
+}
+
+void trace_event_iter_free(struct event_iter *iter)
+{
+ if (!iter)
+ return;
+
+ if (iter->event_dir)
+ closedir(iter->event_dir);
+
+ closedir(iter->system_dir);
+ free(iter);
+}
+
+static void reset_event_iter(struct event_iter *iter)
+{
+ if (iter->event_dir) {
+ closedir(iter->event_dir);
+ iter->event_dir = NULL;
+ }
+
+ rewinddir(iter->system_dir);
+}
+
+static int process_individual_events(const char *path, struct event_iter *iter)
+{
+ struct stat st;
+ const char *system = iter->system_dent->d_name;
+ char *file;
+ char *enable = NULL;
+ char *str;
+ int ret = 0;
+
+ file = append_file(path, system);
+
+ stat(file, &st);
+ if (!S_ISDIR(st.st_mode))
+ goto out;
+
+ enable = append_file(file, "enable");
+ str = get_file_content(enable);
+ if (!str)
+ goto out;
+
+ if (*str != '1' && *str != '0')
+ ret = 1;
+ free(str);
+
+ out:
+ free(enable);
+ free(file);
+
+ return ret;
+}
+
+static void
+process_event_enable(char *path, const char *system, const char *name,
+ enum event_process *processed)
+{
+ struct stat st;
+ char *enable = NULL;
+ char *file;
+ char *str;
+
+ if (system)
+ path = append_file(path, system);
+
+ file = append_file(path, name);
+
+ if (system)
+ free(path);
+
+ stat(file, &st);
+ if (!S_ISDIR(st.st_mode))
+ goto out;
+
+ enable = append_file(file, "enable");
+ str = get_file_content(enable);
+ if (!str)
+ goto out;
+
+ if (*str == '1') {
+ if (!system) {
+ if (!*processed)
+ printf(" Individual systems:\n");
+ printf( " %s\n", name);
+ *processed = PROCESSED_SYSTEM;
+ } else {
+ if (!*processed) {
+ printf(" Individual events:\n");
+ *processed = PROCESSED_SYSTEM;
+ }
+ if (*processed == PROCESSED_SYSTEM) {
+ printf(" %s\n", system);
+ *processed = PROCESSED_EVENT;
+ }
+ printf( " %s\n", name);
+ }
+ }
+ free(str);
+
+ out:
+ free(enable);
+ free(file);
+}
+
+static void report_events(struct buffer_instance *instance)
+{
+ struct event_iter *iter;
+ char *str;
+ char *cont;
+ char *path;
+ char *system;
+ enum event_iter_type type;
+ enum event_process processed = PROCESSED_NONE;
+ enum event_process processed_part = PROCESSED_NONE;
+
+ str = get_instance_file_content(instance, "events/enable");
+ if (!str)
+ return;
+
+ cont = strstrip(str);
+
+ printf("\nEvents:\n");
+
+ switch(*cont) {
+ case '1':
+ printf(" All enabled\n");
+ free(str);
+ return;
+ case '0':
+ printf(" All disabled\n");
+ free(str);
+ return;
+ }
+
+ free(str);
+
+ path = tracefs_instance_get_file(instance->tracefs, "events");
+ if (!path)
+ die("malloc");
+
+ iter = trace_event_iter_alloc(path);
+
+ while (trace_event_iter_next(iter, path, NULL)) {
+ process_event_enable(path, NULL, iter->system_dent->d_name, &processed);
+ }
+
+ reset_event_iter(iter);
+
+ system = NULL;
+ while ((type = trace_event_iter_next(iter, path, system))) {
+
+ if (type == EVENT_ITER_SYSTEM) {
+
+ /* Only process systems that are not fully enabled */
+ if (!process_individual_events(path, iter))
+ continue;
+
+ system = iter->system_dent->d_name;
+ if (processed_part)
+ processed_part = PROCESSED_SYSTEM;
+ continue;
+ }
+
+ process_event_enable(path, iter->system_dent->d_name,
+ iter->event_dent->d_name, &processed_part);
+ }
+
+ trace_event_iter_free(iter);
+
+ if (!processed && !processed_part)
+ printf(" (none enabled)\n");
+
+ tracefs_put_tracing_file(path);
+}
+
+static void
+process_event_filter(char *path, struct event_iter *iter, enum event_process *processed)
+{
+ const char *system = iter->system_dent->d_name;
+ const char *event = iter->event_dent->d_name;
+ struct stat st;
+ char *filter = NULL;
+ char *file;
+ char *str;
+ char *cont;
+
+ path = append_file(path, system);
+ file = append_file(path, event);
+ free(path);
+
+ stat(file, &st);
+ if (!S_ISDIR(st.st_mode))
+ goto out;
+
+ filter = append_file(file, "filter");
+ str = get_file_content(filter);
+ if (!str)
+ goto out;
+
+ cont = strstrip(str);
+
+ if (strcmp(cont, "none") == 0) {
+ free(str);
+ goto out;
+ }
+
+ if (!*processed)
+ printf("\nFilters:\n");
+ printf( " %s:%s \"%s\"\n", system, event, cont);
+ *processed = PROCESSED_SYSTEM;
+ free(str);
+
+ out:
+ free(filter);
+ free(file);
+}
+
+static void report_event_filters(struct buffer_instance *instance)
+{
+ struct event_iter *iter;
+ char *path;
+ char *system;
+ enum event_iter_type type;
+ enum event_process processed = PROCESSED_NONE;
+
+ path = tracefs_instance_get_file(instance->tracefs, "events");
+ if (!path)
+ die("malloc");
+
+ iter = trace_event_iter_alloc(path);
+
+ processed = PROCESSED_NONE;
+ system = NULL;
+ while ((type = trace_event_iter_next(iter, path, system))) {
+
+ if (type == EVENT_ITER_SYSTEM) {
+ system = iter->system_dent->d_name;
+ continue;
+ }
+
+ process_event_filter(path, iter, &processed);
+ }
+
+ trace_event_iter_free(iter);
+
+ tracefs_put_tracing_file(path);
+}
+
+static void
+process_event_trigger(char *path, struct event_iter *iter, enum event_process *processed)
+{
+ const char *system = iter->system_dent->d_name;
+ const char *event = iter->event_dent->d_name;
+ struct stat st;
+ char *trigger = NULL;
+ char *file;
+ char *str;
+ char *cont;
+
+ path = append_file(path, system);
+ file = append_file(path, event);
+ free(path);
+
+ stat(file, &st);
+ if (!S_ISDIR(st.st_mode))
+ goto out;
+
+ trigger = append_file(file, "trigger");
+ str = get_file_content(trigger);
+ if (!str)
+ goto out;
+
+ cont = strstrip(str);
+
+ if (cont[0] == '#') {
+ free(str);
+ goto out;
+ }
+
+ if (!*processed)
+ printf("\nTriggers:\n");
+ printf( " %s:%s \"%s\"\n", system, event, cont);
+ *processed = PROCESSED_SYSTEM;
+ free(str);
+
+ out:
+ free(trigger);
+ free(file);
+}
+
+static void report_event_triggers(struct buffer_instance *instance)
+{
+ struct event_iter *iter;
+ char *path;
+ char *system;
+ enum event_iter_type type;
+ enum event_process processed = PROCESSED_NONE;
+
+ path = tracefs_instance_get_file(instance->tracefs, "events");
+ if (!path)
+ die("malloc");
+
+ iter = trace_event_iter_alloc(path);
+
+ processed = PROCESSED_NONE;
+ system = NULL;
+ while ((type = trace_event_iter_next(iter, path, system))) {
+
+ if (type == EVENT_ITER_SYSTEM) {
+ system = iter->system_dent->d_name;
+ continue;
+ }
+
+ process_event_trigger(path, iter, &processed);
+ }
+
+ trace_event_iter_free(iter);
+
+ tracefs_put_tracing_file(path);
+}
+
+enum func_states {
+ FUNC_STATE_START,
+ FUNC_STATE_SKIP,
+ FUNC_STATE_PRINT,
+};
+
+static void list_functions(const char *path, char *string)
+{
+ enum func_states state;
+ struct stat st;
+ char *str;
+ int ret = 0;
+ int len;
+ int i;
+ int first = 0;
+
+ /* Ignore if it does not exist. */
+ ret = stat(path, &st);
+ if (ret < 0)
+ return;
+
+ str = get_file_content(path);
+ if (!str)
+ return;
+
+ len = strlen(str);
+
+ state = FUNC_STATE_START;
+
+ /* Skip all lines that start with '#' */
+ for (i = 0; i < len; i++) {
+
+ if (state == FUNC_STATE_PRINT)
+ putchar(str[i]);
+
+ if (str[i] == '\n') {
+ state = FUNC_STATE_START;
+ continue;
+ }
+
+ if (state == FUNC_STATE_SKIP)
+ continue;
+
+ if (state == FUNC_STATE_START && str[i] == '#') {
+ state = FUNC_STATE_SKIP;
+ continue;
+ }
+
+ if (!first) {
+ printf("\n%s:\n", string);
+ first = 1;
+ }
+
+ if (state != FUNC_STATE_PRINT) {
+ state = FUNC_STATE_PRINT;
+ printf(" ");
+ putchar(str[i]);
+ }
+ }
+ free(str);
+}
+
+static void report_graph_funcs(struct buffer_instance *instance)
+{
+ char *path;
+
+ path = tracefs_instance_get_file(instance->tracefs, "set_graph_function");
+ if (!path)
+ die("malloc");
+
+ list_functions(path, "Function Graph Filter");
+
+ tracefs_put_tracing_file(path);
+
+ path = tracefs_instance_get_file(instance->tracefs, "set_graph_notrace");
+ if (!path)
+ die("malloc");
+
+ list_functions(path, "Function Graph No Trace");
+
+ tracefs_put_tracing_file(path);
+}
+
+static void report_ftrace_filters(struct buffer_instance *instance)
+{
+ char *path;
+
+ path = tracefs_instance_get_file(instance->tracefs, "set_ftrace_filter");
+ if (!path)
+ die("malloc");
+
+ list_functions(path, "Function Filter");
+
+ tracefs_put_tracing_file(path);
+
+ path = tracefs_instance_get_file(instance->tracefs, "set_ftrace_notrace");
+ if (!path)
+ die("malloc");
+
+ list_functions(path, "Function No Trace");
+
+ tracefs_put_tracing_file(path);
+}
+
+static void report_buffers(struct buffer_instance *instance)
+{
+#define FILE_SIZE 100
+ char *str;
+ char *cont;
+ char file[FILE_SIZE];
+ int cpu;
+
+ str = get_instance_file_content(instance, "buffer_size_kb");
+ if (!str)
+ return;
+
+ cont = strstrip(str);
+
+ /* If it's not expanded yet, just skip */
+ if (strstr(cont, "expanded") != NULL)
+ goto out;
+
+ if (strcmp(cont, "X") != 0) {
+ printf("\nBuffer size in kilobytes (per cpu):\n");
+ printf(" %s\n", str);
+ goto total;
+ }
+
+ /* Read the sizes of each CPU buffer */
+ for (cpu = 0; ; cpu++) {
+
+ snprintf(file, FILE_SIZE, "per_cpu/cpu%d/buffer_size_kb", cpu);
+ str = get_instance_file_content(instance, file);
+ if (!str)
+ break;
+
+ cont = strstrip(str);
+ if (!cpu)
+ putchar('\n');
+
+ printf("CPU %d buffer size (kb): %s\n", cpu, cont);
+ free(str);
+ }
+
+ total:
+ free(str);
+
+ str = get_instance_file_content(instance, "buffer_total_size_kb");
+ if (!str)
+ return;
+
+ cont = strstrip(str);
+ printf("\nBuffer total size in kilobytes:\n");
+ printf(" %s\n", str);
+
+ out:
+ free(str);
+}
+
+static void report_clock(struct buffer_instance *instance)
+{
+ struct tracefs_instance *tracefs = instance ? instance->tracefs : NULL;
+ char *clock;
+
+ clock = tracefs_get_clock(tracefs);
+
+ /* Default clock is "local", only show others */
+ if (clock && strcmp(clock, "local") != 0)
+ printf("\nClock: %s\n", clock);
+
+ free(clock);
+}
+
+static void report_cpumask(struct buffer_instance *instance)
+{
+ char *str;
+ char *cont;
+ int cpus;
+ int n;
+ int i;
+
+ str = get_instance_file_content(instance, "tracing_cpumask");
+ if (!str)
+ return;
+
+ cont = strstrip(str);
+
+ /* check to make sure all CPUs on this machine are set */
+ cpus = tracecmd_count_cpus();
+
+ for (i = strlen(cont) - 1; i >= 0 && cpus > 0; i--) {
+ if (cont[i] == ',')
+ continue;
+
+ if (cont[i] == 'f') {
+ cpus -= 4;
+ continue;
+ }
+
+ if (cpus >= 4)
+ break;
+
+ if (cont[i] >= '0' && cont[i] <= '9')
+ n = cont[i] - '0';
+ else
+ n = 10 + (cont[i] - 'a');
+
+ while (cpus > 0) {
+ if (!(n & 1))
+ break;
+ n >>= 1;
+ cpus--;
+ }
+ break;
+ }
+
+ /* If cpus is greater than zero, one isn't set */
+ if (cpus > 0)
+ printf("\nCPU mask: %s\n", cont);
+
+ free(str);
+}
+
+static void report_probes(struct buffer_instance *instance,
+ const char *file, const char *string)
+{
+ char *str;
+ char *cont;
+ int newline;
+ int i;
+
+ str = get_instance_file_content(instance, file);
+ if (!str)
+ return;
+
+ cont = strstrip(str);
+ if (strlen(cont) == 0)
+ goto out;
+
+ printf("\n%s:\n", string);
+
+ newline = 1;
+ for (i = 0; cont[i]; i++) {
+ if (newline)
+ printf(" ");
+ putchar(cont[i]);
+ if (cont[i] == '\n')
+ newline = 1;
+ else
+ newline = 0;
+ }
+ putchar('\n');
+ out:
+ free(str);
+}
+
+static void report_kprobes(struct buffer_instance *instance)
+{
+ report_probes(instance, "kprobe_events", "Kprobe events");
+}
+
+static void report_uprobes(struct buffer_instance *instance)
+{
+ report_probes(instance, "uprobe_events", "Uprobe events");
+}
+
+static void report_traceon(struct buffer_instance *instance)
+{
+ char *str;
+ char *cont;
+
+ str = get_instance_file_content(instance, "tracing_on");
+ if (!str)
+ return;
+
+ cont = strstrip(str);
+
+ /* double newline as this is the last thing printed */
+ if (strcmp(cont, "0") == 0)
+ printf("\nTracing is disabled\n\n");
+ else
+ printf("\nTracing is enabled\n\n");
+
+ free(str);
+}
+
+static void stat_instance(struct buffer_instance *instance, bool opt)
+{
+ if (instance != &top_instance) {
+ if (instance != first_instance)
+ printf("---------------\n");
+ printf("Instance: %s\n",
+ tracefs_instance_get_name(instance->tracefs));
+ }
+
+ report_file(instance, "current_tracer", "nop", "Tracer: ");
+ report_events(instance);
+ report_event_filters(instance);
+ report_event_triggers(instance);
+ report_ftrace_filters(instance);
+ report_graph_funcs(instance);
+ report_buffers(instance);
+ report_clock(instance);
+ report_cpumask(instance);
+ report_file(instance, "tracing_max_latency", "0", "Max Latency: ");
+ report_kprobes(instance);
+ report_uprobes(instance);
+ report_file(instance, "set_event_pid", "", "Filtered event PIDs:\n");
+ report_file(instance, "set_ftrace_pid", "no pid",
+ "Filtered function tracer PIDs:\n");
+ if (opt) {
+ printf("\nOptions:\n");
+ show_options(" ", instance);
+ }
+ report_traceon(instance);
+ report_file(instance, "error_log", "", "Error log:\n");
+ if (instance == &top_instance)
+ report_instances();
+}
+
+void trace_stat (int argc, char **argv)
+{
+ struct buffer_instance *instance = &top_instance;
+ bool opt = false;
+ int topt = 0;
+ int status;
+ int c;
+
+ init_top_instance();
+
+ for (;;) {
+ c = getopt(argc-1, argv+1, "htoB:");
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'h':
+ usage(argv);
+ break;
+ case 'B':
+ instance = allocate_instance(optarg);
+ if (!instance)
+ die("Failed to create instance");
+ add_instance(instance, tracecmd_count_cpus());
+ /* top instance requires direct access */
+ if (!topt && is_top_instance(first_instance))
+ first_instance = instance;
+ break;
+ case 't':
+ /* Force to use top instance */
+ topt = 1;
+ instance = &top_instance;
+ break;
+ case 'o':
+ opt = 1;
+ break;
+ default:
+ usage(argv);
+ }
+ }
+
+ update_first_instance(instance, topt);
+
+ for_all_instances(instance) {
+ stat_instance(instance, opt);
+ }
+
+ if (tracecmd_stack_tracer_status(&status) >= 0) {
+ if (status > 0)
+ printf("Stack tracing is enabled\n\n");
+ } else {
+ printf("Error reading stack tracer status\n\n");
+ }
+
+ exit(0);
+}