perf_counter: Add tracepoint support to perf list, perf stat
authorJason Baron <jbaron@redhat.com>
Tue, 21 Jul 2009 16:20:22 +0000 (12:20 -0400)
committerPeter Zijlstra <a.p.zijlstra@chello.nl>
Wed, 22 Jul 2009 16:05:57 +0000 (18:05 +0200)
Add support to 'perf list' and 'perf stat' for kernel tracepoints. The
implementation creates a 'for_each_subsystem' and 'for_each_event' for
easy iteration over the tracepoints.

Signed-off-by: Jason Baron <jbaron@redhat.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <426129bf9fcc8ee63bb094cf736e7316a7dcd77a.1248190728.git.jbaron@redhat.com>

tools/perf/util/parse-events.c
tools/perf/util/util.h

index d18c98edd00d839c5d3e361493e033222a180e30..5a3cd3a34af1176c66f7303c194eb1c6af4c9851 100644 (file)
@@ -12,6 +12,8 @@ int                                   nr_counters;
 
 struct perf_counter_attr               attrs[MAX_COUNTERS];
 
+static char default_debugfs_path[] = "/sys/kernel/debug/tracing/events";
+
 struct event_symbol {
        u8      type;
        u64     config;
@@ -110,6 +112,88 @@ static unsigned long hw_cache_stat[C(MAX)] = {
  [C(BPU)]      = (CACHE_READ),
 };
 
+#define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st)           \
+       while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next)        \
+       if (snprintf(file, MAXPATHLEN, "%s/%s", default_debugfs_path,          \
+                       sys_dirent.d_name) &&                                  \
+          (!stat(file, &st)) && (S_ISDIR(st.st_mode)) &&                      \
+          (strcmp(sys_dirent.d_name, ".")) &&                                 \
+          (strcmp(sys_dirent.d_name, "..")))
+
+#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st)    \
+       while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next)        \
+       if (snprintf(file, MAXPATHLEN, "%s/%s/%s", default_debugfs_path,       \
+                       sys_dirent.d_name, evt_dirent.d_name) &&               \
+          (!stat(file, &st)) && (S_ISDIR(st.st_mode)) &&                      \
+          (strcmp(evt_dirent.d_name, ".")) &&                                 \
+          (strcmp(evt_dirent.d_name, "..")))
+
+#define MAX_EVENT_LENGTH 30
+
+static int valid_debugfs_mount(void)
+{
+       struct statfs st_fs;
+
+       if (statfs(default_debugfs_path, &st_fs) < 0)
+               return -ENOENT;
+       else if (st_fs.f_type != (long) DEBUGFS_MAGIC)
+               return -ENOENT;
+       return 0;
+}
+
+static char *tracepoint_id_to_name(u64 config)
+{
+       static char tracepoint_name[2 * MAX_EVENT_LENGTH];
+       DIR *sys_dir, *evt_dir;
+       struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
+       struct stat st;
+       char id_buf[4];
+       int fd;
+       u64 id;
+       char evt_path[MAXPATHLEN];
+
+       if (valid_debugfs_mount())
+               return "unkown";
+
+       sys_dir = opendir(default_debugfs_path);
+       if (!sys_dir)
+               goto cleanup;
+
+       for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) {
+               evt_dir = opendir(evt_path);
+               if (!evt_dir)
+                       goto cleanup;
+               for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next,
+                                                               evt_path, st) {
+                       snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id",
+                                default_debugfs_path, sys_dirent.d_name,
+                                evt_dirent.d_name);
+                       fd = open(evt_path, O_RDONLY);
+                       if (fd < 0)
+                               continue;
+                       if (read(fd, id_buf, sizeof(id_buf)) < 0) {
+                               close(fd);
+                               continue;
+                       }
+                       close(fd);
+                       id = atoll(id_buf);
+                       if (id == config) {
+                               closedir(evt_dir);
+                               closedir(sys_dir);
+                               snprintf(tracepoint_name, 2 * MAX_EVENT_LENGTH,
+                                       "%s:%s", sys_dirent.d_name,
+                                       evt_dirent.d_name);
+                               return tracepoint_name;
+                       }
+               }
+               closedir(evt_dir);
+       }
+
+cleanup:
+       closedir(sys_dir);
+       return "unkown";
+}
+
 static int is_cache_op_valid(u8 cache_type, u8 cache_op)
 {
        if (hw_cache_stat[cache_type] & COP(cache_op))
@@ -177,6 +261,9 @@ char *event_name(int counter)
                        return sw_event_names[config];
                return "unknown-software";
 
+       case PERF_TYPE_TRACEPOINT:
+               return tracepoint_id_to_name(config);
+
        default:
                break;
        }
@@ -265,6 +352,53 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
        return 1;
 }
 
+static int parse_tracepoint_event(const char **strp,
+                                   struct perf_counter_attr *attr)
+{
+       const char *evt_name;
+       char sys_name[MAX_EVENT_LENGTH];
+       char id_buf[4];
+       int fd;
+       unsigned int sys_length, evt_length;
+       u64 id;
+       char evt_path[MAXPATHLEN];
+
+       if (valid_debugfs_mount())
+               return 0;
+
+       evt_name = strchr(*strp, ':');
+       if (!evt_name)
+               return 0;
+
+       sys_length = evt_name - *strp;
+       if (sys_length >= MAX_EVENT_LENGTH)
+               return 0;
+
+       strncpy(sys_name, *strp, sys_length);
+       sys_name[sys_length] = '\0';
+       evt_name = evt_name + 1;
+       evt_length = strlen(evt_name);
+       if (evt_length >= MAX_EVENT_LENGTH)
+               return 0;
+
+       snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", default_debugfs_path,
+                               sys_name, evt_name);
+       fd = open(evt_path, O_RDONLY);
+       if (fd < 0)
+               return 0;
+
+       if (read(fd, id_buf, sizeof(id_buf)) < 0) {
+               close(fd);
+               return 0;
+       }
+       close(fd);
+       id = atoll(id_buf);
+       attr->config = id;
+       attr->type = PERF_TYPE_TRACEPOINT;
+       *strp = evt_name + evt_length;
+       return 1;
+}
+
 static int check_events(const char *str, unsigned int i)
 {
        int n;
@@ -374,7 +508,8 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr)
  */
 static int parse_event_symbols(const char **str, struct perf_counter_attr *attr)
 {
-       if (!(parse_raw_event(str, attr) ||
+       if (!(parse_tracepoint_event(str, attr) ||
+             parse_raw_event(str, attr) ||
              parse_numeric_event(str, attr) ||
              parse_symbolic_event(str, attr) ||
              parse_generic_hw_event(str, attr)))
@@ -422,6 +557,42 @@ static const char * const event_type_descriptors[] = {
        "Hardware cache event",
 };
 
+/*
+ * Print the events from <debugfs_mount_point>/tracing/events
+ */
+
+static void print_tracepoint_events(void)
+{
+       DIR *sys_dir, *evt_dir;
+       struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
+       struct stat st;
+       char evt_path[MAXPATHLEN];
+
+       if (valid_debugfs_mount())
+               return;
+
+       sys_dir = opendir(default_debugfs_path);
+       if (!sys_dir)
+               goto cleanup;
+
+       for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) {
+               evt_dir = opendir(evt_path);
+               if (!evt_dir)
+                       goto cleanup;
+               for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next,
+                                                               evt_path, st) {
+                       snprintf(evt_path, MAXPATHLEN, "%s:%s",
+                                sys_dirent.d_name, evt_dirent.d_name);
+                       fprintf(stderr, "  %-40s [%s]\n", evt_path,
+                               event_type_descriptors[PERF_TYPE_TRACEPOINT+1]);
+               }
+               closedir(evt_dir);
+       }
+
+cleanup:
+       closedir(sys_dir);
+}
+
 /*
  * Print the help text for the event symbols:
  */
@@ -472,5 +643,7 @@ void print_events(void)
                "rNNN");
        fprintf(stderr, "\n");
 
+       print_tracepoint_events();
+
        exit(129);
 }
index b4be6071c105754a171b5a83272b07ebe300c15b..68fe157d72fb9ddae6991fdfa9b1c69ac7f51fa4 100644 (file)
@@ -50,6 +50,7 @@
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/stat.h>
+#include <sys/statfs.h>
 #include <fcntl.h>
 #include <stddef.h>
 #include <stdlib.h>
@@ -80,6 +81,7 @@
 #include <netdb.h>
 #include <pwd.h>
 #include <inttypes.h>
+#include "../../../include/linux/magic.h"
 
 #ifndef NO_ICONV
 #include <iconv.h>