diff options
Diffstat (limited to 'tools/perf/util/parse-events.c')
-rw-r--r-- | tools/perf/util/parse-events.c | 206 |
1 files changed, 201 insertions, 5 deletions
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 5184959e0615..4858d83b3b67 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "parse-events.h" | 5 | #include "parse-events.h" |
6 | #include "exec_cmd.h" | 6 | #include "exec_cmd.h" |
7 | #include "string.h" | 7 | #include "string.h" |
8 | #include "cache.h" | ||
8 | 9 | ||
9 | extern char *strcasestr(const char *haystack, const char *needle); | 10 | extern char *strcasestr(const char *haystack, const char *needle); |
10 | 11 | ||
@@ -19,6 +20,8 @@ struct event_symbol { | |||
19 | char *alias; | 20 | char *alias; |
20 | }; | 21 | }; |
21 | 22 | ||
23 | char debugfs_path[MAXPATHLEN]; | ||
24 | |||
22 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x | 25 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x |
23 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x | 26 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x |
24 | 27 | ||
@@ -71,8 +74,8 @@ static char *sw_event_names[] = { | |||
71 | #define MAX_ALIASES 8 | 74 | #define MAX_ALIASES 8 |
72 | 75 | ||
73 | static char *hw_cache[][MAX_ALIASES] = { | 76 | static char *hw_cache[][MAX_ALIASES] = { |
74 | { "L1-d$", "l1-d", "l1d", "L1-data", }, | 77 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, |
75 | { "L1-i$", "l1-i", "l1i", "L1-instruction", }, | 78 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, |
76 | { "LLC", "L2" }, | 79 | { "LLC", "L2" }, |
77 | { "dTLB", "d-tlb", "Data-TLB", }, | 80 | { "dTLB", "d-tlb", "Data-TLB", }, |
78 | { "iTLB", "i-tlb", "Instruction-TLB", }, | 81 | { "iTLB", "i-tlb", "Instruction-TLB", }, |
@@ -110,6 +113,104 @@ static unsigned long hw_cache_stat[C(MAX)] = { | |||
110 | [C(BPU)] = (CACHE_READ), | 113 | [C(BPU)] = (CACHE_READ), |
111 | }; | 114 | }; |
112 | 115 | ||
116 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st) \ | ||
117 | while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ | ||
118 | if (snprintf(file, MAXPATHLEN, "%s/%s", debugfs_path, \ | ||
119 | sys_dirent.d_name) && \ | ||
120 | (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ | ||
121 | (strcmp(sys_dirent.d_name, ".")) && \ | ||
122 | (strcmp(sys_dirent.d_name, ".."))) | ||
123 | |||
124 | static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) | ||
125 | { | ||
126 | char evt_path[MAXPATHLEN]; | ||
127 | int fd; | ||
128 | |||
129 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | ||
130 | sys_dir->d_name, evt_dir->d_name); | ||
131 | fd = open(evt_path, O_RDONLY); | ||
132 | if (fd < 0) | ||
133 | return -EINVAL; | ||
134 | close(fd); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | #define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st) \ | ||
140 | while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \ | ||
141 | if (snprintf(file, MAXPATHLEN, "%s/%s/%s", debugfs_path, \ | ||
142 | sys_dirent.d_name, evt_dirent.d_name) && \ | ||
143 | (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ | ||
144 | (strcmp(evt_dirent.d_name, ".")) && \ | ||
145 | (strcmp(evt_dirent.d_name, "..")) && \ | ||
146 | (!tp_event_has_id(&sys_dirent, &evt_dirent))) | ||
147 | |||
148 | #define MAX_EVENT_LENGTH 30 | ||
149 | |||
150 | int valid_debugfs_mount(const char *debugfs) | ||
151 | { | ||
152 | struct statfs st_fs; | ||
153 | |||
154 | if (statfs(debugfs, &st_fs) < 0) | ||
155 | return -ENOENT; | ||
156 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
157 | return -ENOENT; | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static char *tracepoint_id_to_name(u64 config) | ||
162 | { | ||
163 | static char tracepoint_name[2 * MAX_EVENT_LENGTH]; | ||
164 | DIR *sys_dir, *evt_dir; | ||
165 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
166 | struct stat st; | ||
167 | char id_buf[4]; | ||
168 | int fd; | ||
169 | u64 id; | ||
170 | char evt_path[MAXPATHLEN]; | ||
171 | |||
172 | if (valid_debugfs_mount(debugfs_path)) | ||
173 | return "unkown"; | ||
174 | |||
175 | sys_dir = opendir(debugfs_path); | ||
176 | if (!sys_dir) | ||
177 | goto cleanup; | ||
178 | |||
179 | for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { | ||
180 | evt_dir = opendir(evt_path); | ||
181 | if (!evt_dir) | ||
182 | goto cleanup; | ||
183 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, | ||
184 | evt_path, st) { | ||
185 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", | ||
186 | debugfs_path, sys_dirent.d_name, | ||
187 | evt_dirent.d_name); | ||
188 | fd = open(evt_path, O_RDONLY); | ||
189 | if (fd < 0) | ||
190 | continue; | ||
191 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
192 | close(fd); | ||
193 | continue; | ||
194 | } | ||
195 | close(fd); | ||
196 | id = atoll(id_buf); | ||
197 | if (id == config) { | ||
198 | closedir(evt_dir); | ||
199 | closedir(sys_dir); | ||
200 | snprintf(tracepoint_name, 2 * MAX_EVENT_LENGTH, | ||
201 | "%s:%s", sys_dirent.d_name, | ||
202 | evt_dirent.d_name); | ||
203 | return tracepoint_name; | ||
204 | } | ||
205 | } | ||
206 | closedir(evt_dir); | ||
207 | } | ||
208 | |||
209 | cleanup: | ||
210 | closedir(sys_dir); | ||
211 | return "unkown"; | ||
212 | } | ||
213 | |||
113 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) | 214 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) |
114 | { | 215 | { |
115 | if (hw_cache_stat[cache_type] & COP(cache_op)) | 216 | if (hw_cache_stat[cache_type] & COP(cache_op)) |
@@ -138,9 +239,15 @@ char *event_name(int counter) | |||
138 | { | 239 | { |
139 | u64 config = attrs[counter].config; | 240 | u64 config = attrs[counter].config; |
140 | int type = attrs[counter].type; | 241 | int type = attrs[counter].type; |
242 | |||
243 | return __event_name(type, config); | ||
244 | } | ||
245 | |||
246 | char *__event_name(int type, u64 config) | ||
247 | { | ||
141 | static char buf[32]; | 248 | static char buf[32]; |
142 | 249 | ||
143 | if (attrs[counter].type == PERF_TYPE_RAW) { | 250 | if (type == PERF_TYPE_RAW) { |
144 | sprintf(buf, "raw 0x%llx", config); | 251 | sprintf(buf, "raw 0x%llx", config); |
145 | return buf; | 252 | return buf; |
146 | } | 253 | } |
@@ -177,6 +284,9 @@ char *event_name(int counter) | |||
177 | return sw_event_names[config]; | 284 | return sw_event_names[config]; |
178 | return "unknown-software"; | 285 | return "unknown-software"; |
179 | 286 | ||
287 | case PERF_TYPE_TRACEPOINT: | ||
288 | return tracepoint_id_to_name(config); | ||
289 | |||
180 | default: | 290 | default: |
181 | break; | 291 | break; |
182 | } | 292 | } |
@@ -265,6 +375,53 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) | |||
265 | return 1; | 375 | return 1; |
266 | } | 376 | } |
267 | 377 | ||
378 | static int parse_tracepoint_event(const char **strp, | ||
379 | struct perf_counter_attr *attr) | ||
380 | { | ||
381 | const char *evt_name; | ||
382 | char sys_name[MAX_EVENT_LENGTH]; | ||
383 | char id_buf[4]; | ||
384 | int fd; | ||
385 | unsigned int sys_length, evt_length; | ||
386 | u64 id; | ||
387 | char evt_path[MAXPATHLEN]; | ||
388 | |||
389 | if (valid_debugfs_mount(debugfs_path)) | ||
390 | return 0; | ||
391 | |||
392 | evt_name = strchr(*strp, ':'); | ||
393 | if (!evt_name) | ||
394 | return 0; | ||
395 | |||
396 | sys_length = evt_name - *strp; | ||
397 | if (sys_length >= MAX_EVENT_LENGTH) | ||
398 | return 0; | ||
399 | |||
400 | strncpy(sys_name, *strp, sys_length); | ||
401 | sys_name[sys_length] = '\0'; | ||
402 | evt_name = evt_name + 1; | ||
403 | evt_length = strlen(evt_name); | ||
404 | if (evt_length >= MAX_EVENT_LENGTH) | ||
405 | return 0; | ||
406 | |||
407 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | ||
408 | sys_name, evt_name); | ||
409 | fd = open(evt_path, O_RDONLY); | ||
410 | if (fd < 0) | ||
411 | return 0; | ||
412 | |||
413 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
414 | close(fd); | ||
415 | return 0; | ||
416 | } | ||
417 | close(fd); | ||
418 | id = atoll(id_buf); | ||
419 | attr->config = id; | ||
420 | attr->type = PERF_TYPE_TRACEPOINT; | ||
421 | *strp = evt_name + evt_length; | ||
422 | return 1; | ||
423 | } | ||
424 | |||
268 | static int check_events(const char *str, unsigned int i) | 425 | static int check_events(const char *str, unsigned int i) |
269 | { | 426 | { |
270 | int n; | 427 | int n; |
@@ -374,7 +531,8 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr) | |||
374 | */ | 531 | */ |
375 | static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) | 532 | static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) |
376 | { | 533 | { |
377 | if (!(parse_raw_event(str, attr) || | 534 | if (!(parse_tracepoint_event(str, attr) || |
535 | parse_raw_event(str, attr) || | ||
378 | parse_numeric_event(str, attr) || | 536 | parse_numeric_event(str, attr) || |
379 | parse_symbolic_event(str, attr) || | 537 | parse_symbolic_event(str, attr) || |
380 | parse_generic_hw_event(str, attr))) | 538 | parse_generic_hw_event(str, attr))) |
@@ -423,6 +581,42 @@ static const char * const event_type_descriptors[] = { | |||
423 | }; | 581 | }; |
424 | 582 | ||
425 | /* | 583 | /* |
584 | * Print the events from <debugfs_mount_point>/tracing/events | ||
585 | */ | ||
586 | |||
587 | static void print_tracepoint_events(void) | ||
588 | { | ||
589 | DIR *sys_dir, *evt_dir; | ||
590 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
591 | struct stat st; | ||
592 | char evt_path[MAXPATHLEN]; | ||
593 | |||
594 | if (valid_debugfs_mount(debugfs_path)) | ||
595 | return; | ||
596 | |||
597 | sys_dir = opendir(debugfs_path); | ||
598 | if (!sys_dir) | ||
599 | goto cleanup; | ||
600 | |||
601 | for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { | ||
602 | evt_dir = opendir(evt_path); | ||
603 | if (!evt_dir) | ||
604 | goto cleanup; | ||
605 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, | ||
606 | evt_path, st) { | ||
607 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | ||
608 | sys_dirent.d_name, evt_dirent.d_name); | ||
609 | fprintf(stderr, " %-40s [%s]\n", evt_path, | ||
610 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | ||
611 | } | ||
612 | closedir(evt_dir); | ||
613 | } | ||
614 | |||
615 | cleanup: | ||
616 | closedir(sys_dir); | ||
617 | } | ||
618 | |||
619 | /* | ||
426 | * Print the help text for the event symbols: | 620 | * Print the help text for the event symbols: |
427 | */ | 621 | */ |
428 | void print_events(void) | 622 | void print_events(void) |
@@ -436,7 +630,7 @@ void print_events(void) | |||
436 | 630 | ||
437 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 631 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
438 | type = syms->type + 1; | 632 | type = syms->type + 1; |
439 | if (type > ARRAY_SIZE(event_type_descriptors)) | 633 | if (type >= ARRAY_SIZE(event_type_descriptors)) |
440 | type = 0; | 634 | type = 0; |
441 | 635 | ||
442 | if (type != prev_type) | 636 | if (type != prev_type) |
@@ -472,5 +666,7 @@ void print_events(void) | |||
472 | "rNNN"); | 666 | "rNNN"); |
473 | fprintf(stderr, "\n"); | 667 | fprintf(stderr, "\n"); |
474 | 668 | ||
669 | print_tracepoint_events(); | ||
670 | |||
475 | exit(129); | 671 | exit(129); |
476 | } | 672 | } |