diff options
author | Jason Baron <jbaron@redhat.com> | 2009-07-21 12:20:22 -0400 |
---|---|---|
committer | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2009-07-22 12:05:57 -0400 |
commit | f6bdafef2ab911f03321fa83d8da1df99878009e (patch) | |
tree | a948428dcda80abccbe6071036662b7cf07db268 | |
parent | 28ac909b49a155856c957d080f8a796b3c1d1f3e (diff) |
perf_counter: Add tracepoint support to perf list, perf stat
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>
-rw-r--r-- | tools/perf/util/parse-events.c | 175 | ||||
-rw-r--r-- | tools/perf/util/util.h | 2 |
2 files changed, 176 insertions, 1 deletions
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index d18c98edd00d..5a3cd3a34af1 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -12,6 +12,8 @@ int nr_counters; | |||
12 | 12 | ||
13 | struct perf_counter_attr attrs[MAX_COUNTERS]; | 13 | struct perf_counter_attr attrs[MAX_COUNTERS]; |
14 | 14 | ||
15 | static char default_debugfs_path[] = "/sys/kernel/debug/tracing/events"; | ||
16 | |||
15 | struct event_symbol { | 17 | struct event_symbol { |
16 | u8 type; | 18 | u8 type; |
17 | u64 config; | 19 | u64 config; |
@@ -110,6 +112,88 @@ static unsigned long hw_cache_stat[C(MAX)] = { | |||
110 | [C(BPU)] = (CACHE_READ), | 112 | [C(BPU)] = (CACHE_READ), |
111 | }; | 113 | }; |
112 | 114 | ||
115 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st) \ | ||
116 | while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ | ||
117 | if (snprintf(file, MAXPATHLEN, "%s/%s", default_debugfs_path, \ | ||
118 | sys_dirent.d_name) && \ | ||
119 | (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ | ||
120 | (strcmp(sys_dirent.d_name, ".")) && \ | ||
121 | (strcmp(sys_dirent.d_name, ".."))) | ||
122 | |||
123 | #define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st) \ | ||
124 | while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \ | ||
125 | if (snprintf(file, MAXPATHLEN, "%s/%s/%s", default_debugfs_path, \ | ||
126 | sys_dirent.d_name, evt_dirent.d_name) && \ | ||
127 | (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ | ||
128 | (strcmp(evt_dirent.d_name, ".")) && \ | ||
129 | (strcmp(evt_dirent.d_name, ".."))) | ||
130 | |||
131 | #define MAX_EVENT_LENGTH 30 | ||
132 | |||
133 | static int valid_debugfs_mount(void) | ||
134 | { | ||
135 | struct statfs st_fs; | ||
136 | |||
137 | if (statfs(default_debugfs_path, &st_fs) < 0) | ||
138 | return -ENOENT; | ||
139 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
140 | return -ENOENT; | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static char *tracepoint_id_to_name(u64 config) | ||
145 | { | ||
146 | static char tracepoint_name[2 * MAX_EVENT_LENGTH]; | ||
147 | DIR *sys_dir, *evt_dir; | ||
148 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
149 | struct stat st; | ||
150 | char id_buf[4]; | ||
151 | int fd; | ||
152 | u64 id; | ||
153 | char evt_path[MAXPATHLEN]; | ||
154 | |||
155 | if (valid_debugfs_mount()) | ||
156 | return "unkown"; | ||
157 | |||
158 | sys_dir = opendir(default_debugfs_path); | ||
159 | if (!sys_dir) | ||
160 | goto cleanup; | ||
161 | |||
162 | for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { | ||
163 | evt_dir = opendir(evt_path); | ||
164 | if (!evt_dir) | ||
165 | goto cleanup; | ||
166 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, | ||
167 | evt_path, st) { | ||
168 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", | ||
169 | default_debugfs_path, sys_dirent.d_name, | ||
170 | evt_dirent.d_name); | ||
171 | fd = open(evt_path, O_RDONLY); | ||
172 | if (fd < 0) | ||
173 | continue; | ||
174 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
175 | close(fd); | ||
176 | continue; | ||
177 | } | ||
178 | close(fd); | ||
179 | id = atoll(id_buf); | ||
180 | if (id == config) { | ||
181 | closedir(evt_dir); | ||
182 | closedir(sys_dir); | ||
183 | snprintf(tracepoint_name, 2 * MAX_EVENT_LENGTH, | ||
184 | "%s:%s", sys_dirent.d_name, | ||
185 | evt_dirent.d_name); | ||
186 | return tracepoint_name; | ||
187 | } | ||
188 | } | ||
189 | closedir(evt_dir); | ||
190 | } | ||
191 | |||
192 | cleanup: | ||
193 | closedir(sys_dir); | ||
194 | return "unkown"; | ||
195 | } | ||
196 | |||
113 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) | 197 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) |
114 | { | 198 | { |
115 | if (hw_cache_stat[cache_type] & COP(cache_op)) | 199 | if (hw_cache_stat[cache_type] & COP(cache_op)) |
@@ -177,6 +261,9 @@ char *event_name(int counter) | |||
177 | return sw_event_names[config]; | 261 | return sw_event_names[config]; |
178 | return "unknown-software"; | 262 | return "unknown-software"; |
179 | 263 | ||
264 | case PERF_TYPE_TRACEPOINT: | ||
265 | return tracepoint_id_to_name(config); | ||
266 | |||
180 | default: | 267 | default: |
181 | break; | 268 | break; |
182 | } | 269 | } |
@@ -265,6 +352,53 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) | |||
265 | return 1; | 352 | return 1; |
266 | } | 353 | } |
267 | 354 | ||
355 | static int parse_tracepoint_event(const char **strp, | ||
356 | struct perf_counter_attr *attr) | ||
357 | { | ||
358 | const char *evt_name; | ||
359 | char sys_name[MAX_EVENT_LENGTH]; | ||
360 | char id_buf[4]; | ||
361 | int fd; | ||
362 | unsigned int sys_length, evt_length; | ||
363 | u64 id; | ||
364 | char evt_path[MAXPATHLEN]; | ||
365 | |||
366 | if (valid_debugfs_mount()) | ||
367 | return 0; | ||
368 | |||
369 | evt_name = strchr(*strp, ':'); | ||
370 | if (!evt_name) | ||
371 | return 0; | ||
372 | |||
373 | sys_length = evt_name - *strp; | ||
374 | if (sys_length >= MAX_EVENT_LENGTH) | ||
375 | return 0; | ||
376 | |||
377 | strncpy(sys_name, *strp, sys_length); | ||
378 | sys_name[sys_length] = '\0'; | ||
379 | evt_name = evt_name + 1; | ||
380 | evt_length = strlen(evt_name); | ||
381 | if (evt_length >= MAX_EVENT_LENGTH) | ||
382 | return 0; | ||
383 | |||
384 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", default_debugfs_path, | ||
385 | sys_name, evt_name); | ||
386 | fd = open(evt_path, O_RDONLY); | ||
387 | if (fd < 0) | ||
388 | return 0; | ||
389 | |||
390 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
391 | close(fd); | ||
392 | return 0; | ||
393 | } | ||
394 | close(fd); | ||
395 | id = atoll(id_buf); | ||
396 | attr->config = id; | ||
397 | attr->type = PERF_TYPE_TRACEPOINT; | ||
398 | *strp = evt_name + evt_length; | ||
399 | return 1; | ||
400 | } | ||
401 | |||
268 | static int check_events(const char *str, unsigned int i) | 402 | static int check_events(const char *str, unsigned int i) |
269 | { | 403 | { |
270 | int n; | 404 | int n; |
@@ -374,7 +508,8 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr) | |||
374 | */ | 508 | */ |
375 | static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) | 509 | static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) |
376 | { | 510 | { |
377 | if (!(parse_raw_event(str, attr) || | 511 | if (!(parse_tracepoint_event(str, attr) || |
512 | parse_raw_event(str, attr) || | ||
378 | parse_numeric_event(str, attr) || | 513 | parse_numeric_event(str, attr) || |
379 | parse_symbolic_event(str, attr) || | 514 | parse_symbolic_event(str, attr) || |
380 | parse_generic_hw_event(str, attr))) | 515 | parse_generic_hw_event(str, attr))) |
@@ -423,6 +558,42 @@ static const char * const event_type_descriptors[] = { | |||
423 | }; | 558 | }; |
424 | 559 | ||
425 | /* | 560 | /* |
561 | * Print the events from <debugfs_mount_point>/tracing/events | ||
562 | */ | ||
563 | |||
564 | static void print_tracepoint_events(void) | ||
565 | { | ||
566 | DIR *sys_dir, *evt_dir; | ||
567 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
568 | struct stat st; | ||
569 | char evt_path[MAXPATHLEN]; | ||
570 | |||
571 | if (valid_debugfs_mount()) | ||
572 | return; | ||
573 | |||
574 | sys_dir = opendir(default_debugfs_path); | ||
575 | if (!sys_dir) | ||
576 | goto cleanup; | ||
577 | |||
578 | for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { | ||
579 | evt_dir = opendir(evt_path); | ||
580 | if (!evt_dir) | ||
581 | goto cleanup; | ||
582 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, | ||
583 | evt_path, st) { | ||
584 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | ||
585 | sys_dirent.d_name, evt_dirent.d_name); | ||
586 | fprintf(stderr, " %-40s [%s]\n", evt_path, | ||
587 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | ||
588 | } | ||
589 | closedir(evt_dir); | ||
590 | } | ||
591 | |||
592 | cleanup: | ||
593 | closedir(sys_dir); | ||
594 | } | ||
595 | |||
596 | /* | ||
426 | * Print the help text for the event symbols: | 597 | * Print the help text for the event symbols: |
427 | */ | 598 | */ |
428 | void print_events(void) | 599 | void print_events(void) |
@@ -472,5 +643,7 @@ void print_events(void) | |||
472 | "rNNN"); | 643 | "rNNN"); |
473 | fprintf(stderr, "\n"); | 644 | fprintf(stderr, "\n"); |
474 | 645 | ||
646 | print_tracepoint_events(); | ||
647 | |||
475 | exit(129); | 648 | exit(129); |
476 | } | 649 | } |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index b4be6071c105..68fe157d72fb 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -50,6 +50,7 @@ | |||
50 | #include <unistd.h> | 50 | #include <unistd.h> |
51 | #include <stdio.h> | 51 | #include <stdio.h> |
52 | #include <sys/stat.h> | 52 | #include <sys/stat.h> |
53 | #include <sys/statfs.h> | ||
53 | #include <fcntl.h> | 54 | #include <fcntl.h> |
54 | #include <stddef.h> | 55 | #include <stddef.h> |
55 | #include <stdlib.h> | 56 | #include <stdlib.h> |
@@ -80,6 +81,7 @@ | |||
80 | #include <netdb.h> | 81 | #include <netdb.h> |
81 | #include <pwd.h> | 82 | #include <pwd.h> |
82 | #include <inttypes.h> | 83 | #include <inttypes.h> |
84 | #include "../../../include/linux/magic.h" | ||
83 | 85 | ||
84 | #ifndef NO_ICONV | 86 | #ifndef NO_ICONV |
85 | #include <iconv.h> | 87 | #include <iconv.h> |