diff options
| author | Paul Mundt <lethal@linux-sh.org> | 2009-07-23 05:43:48 -0400 |
|---|---|---|
| committer | Paul Mundt <lethal@linux-sh.org> | 2009-07-23 05:43:48 -0400 |
| commit | a3beddd0aa267986de7b13b6d9cd0e1869fcf1fc (patch) | |
| tree | bfdc42183f5aac21f605a30a4a511298ab91e3c5 /tools/perf/util | |
| parent | 955c9863bb5855a994751843e7066017edc00410 (diff) | |
| parent | 5bdef865eb358b6f3760e25e591ae115e9eeddef (diff) | |
Merge branch 'sh/kfr2r09'
Diffstat (limited to 'tools/perf/util')
| -rw-r--r-- | tools/perf/util/cache.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/header.h | 2 | ||||
| -rw-r--r-- | tools/perf/util/include/linux/kernel.h | 8 | ||||
| -rw-r--r-- | tools/perf/util/parse-events.c | 182 | ||||
| -rw-r--r-- | tools/perf/util/parse-events.h | 5 | ||||
| -rw-r--r-- | tools/perf/util/string.h | 3 | ||||
| -rw-r--r-- | tools/perf/util/strlist.c | 20 | ||||
| -rw-r--r-- | tools/perf/util/strlist.h | 11 | ||||
| -rw-r--r-- | tools/perf/util/symbol.c | 145 | ||||
| -rw-r--r-- | tools/perf/util/symbol.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/util.h | 2 |
11 files changed, 316 insertions, 64 deletions
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 161d5f413e28..4b50c412b9c5 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #define PERFATTRIBUTES_FILE ".perfattributes" | 18 | #define PERFATTRIBUTES_FILE ".perfattributes" |
| 19 | #define INFOATTRIBUTES_FILE "info/attributes" | 19 | #define INFOATTRIBUTES_FILE "info/attributes" |
| 20 | #define ATTRIBUTE_MACRO_PREFIX "[attr]" | 20 | #define ATTRIBUTE_MACRO_PREFIX "[attr]" |
| 21 | #define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR" | ||
| 21 | 22 | ||
| 22 | typedef int (*config_fn_t)(const char *, const char *, void *); | 23 | typedef int (*config_fn_t)(const char *, const char *, void *); |
| 23 | extern int perf_default_config(const char *, const char *, void *); | 24 | extern int perf_default_config(const char *, const char *, void *); |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index b5ef53ad4c7a..bf280449fcfd 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
| @@ -16,7 +16,7 @@ struct perf_header { | |||
| 16 | int frozen; | 16 | int frozen; |
| 17 | int attrs, size; | 17 | int attrs, size; |
| 18 | struct perf_header_attr **attr; | 18 | struct perf_header_attr **attr; |
| 19 | off_t attr_offset; | 19 | s64 attr_offset; |
| 20 | u64 data_offset; | 20 | u64 data_offset; |
| 21 | u64 data_size; | 21 | u64 data_size; |
| 22 | }; | 22 | }; |
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index 99c1b3d1edd9..a6b87390cb52 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h | |||
| @@ -18,4 +18,12 @@ | |||
| 18 | (type *)((char *)__mptr - offsetof(type, member)); }) | 18 | (type *)((char *)__mptr - offsetof(type, member)); }) |
| 19 | #endif | 19 | #endif |
| 20 | 20 | ||
| 21 | #ifndef max | ||
| 22 | #define max(x, y) ({ \ | ||
| 23 | typeof(x) _max1 = (x); \ | ||
| 24 | typeof(y) _max2 = (y); \ | ||
| 25 | (void) (&_max1 == &_max2); \ | ||
| 26 | _max1 > _max2 ? _max1 : _max2; }) | ||
| 27 | #endif | ||
| 28 | |||
| 21 | #endif | 29 | #endif |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 5184959e0615..7bdad8df22a6 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,88 @@ 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 | #define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st) \ | ||
| 125 | while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \ | ||
| 126 | if (snprintf(file, MAXPATHLEN, "%s/%s/%s", debugfs_path, \ | ||
| 127 | sys_dirent.d_name, evt_dirent.d_name) && \ | ||
| 128 | (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ | ||
| 129 | (strcmp(evt_dirent.d_name, ".")) && \ | ||
| 130 | (strcmp(evt_dirent.d_name, ".."))) | ||
| 131 | |||
| 132 | #define MAX_EVENT_LENGTH 30 | ||
| 133 | |||
| 134 | int valid_debugfs_mount(const char *debugfs) | ||
| 135 | { | ||
| 136 | struct statfs st_fs; | ||
| 137 | |||
| 138 | if (statfs(debugfs, &st_fs) < 0) | ||
| 139 | return -ENOENT; | ||
| 140 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
| 141 | return -ENOENT; | ||
| 142 | return 0; | ||
| 143 | } | ||
| 144 | |||
| 145 | static char *tracepoint_id_to_name(u64 config) | ||
| 146 | { | ||
| 147 | static char tracepoint_name[2 * MAX_EVENT_LENGTH]; | ||
| 148 | DIR *sys_dir, *evt_dir; | ||
| 149 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
| 150 | struct stat st; | ||
| 151 | char id_buf[4]; | ||
| 152 | int fd; | ||
| 153 | u64 id; | ||
| 154 | char evt_path[MAXPATHLEN]; | ||
| 155 | |||
| 156 | if (valid_debugfs_mount(debugfs_path)) | ||
| 157 | return "unkown"; | ||
| 158 | |||
| 159 | sys_dir = opendir(debugfs_path); | ||
| 160 | if (!sys_dir) | ||
| 161 | goto cleanup; | ||
| 162 | |||
| 163 | for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { | ||
| 164 | evt_dir = opendir(evt_path); | ||
| 165 | if (!evt_dir) | ||
| 166 | goto cleanup; | ||
| 167 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, | ||
| 168 | evt_path, st) { | ||
| 169 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", | ||
| 170 | debugfs_path, sys_dirent.d_name, | ||
| 171 | evt_dirent.d_name); | ||
| 172 | fd = open(evt_path, O_RDONLY); | ||
| 173 | if (fd < 0) | ||
| 174 | continue; | ||
| 175 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
| 176 | close(fd); | ||
| 177 | continue; | ||
| 178 | } | ||
| 179 | close(fd); | ||
| 180 | id = atoll(id_buf); | ||
| 181 | if (id == config) { | ||
| 182 | closedir(evt_dir); | ||
| 183 | closedir(sys_dir); | ||
| 184 | snprintf(tracepoint_name, 2 * MAX_EVENT_LENGTH, | ||
| 185 | "%s:%s", sys_dirent.d_name, | ||
| 186 | evt_dirent.d_name); | ||
| 187 | return tracepoint_name; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | closedir(evt_dir); | ||
| 191 | } | ||
| 192 | |||
| 193 | cleanup: | ||
| 194 | closedir(sys_dir); | ||
| 195 | return "unkown"; | ||
| 196 | } | ||
| 197 | |||
| 113 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) | 198 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) |
| 114 | { | 199 | { |
| 115 | if (hw_cache_stat[cache_type] & COP(cache_op)) | 200 | if (hw_cache_stat[cache_type] & COP(cache_op)) |
| @@ -177,6 +262,9 @@ char *event_name(int counter) | |||
| 177 | return sw_event_names[config]; | 262 | return sw_event_names[config]; |
| 178 | return "unknown-software"; | 263 | return "unknown-software"; |
| 179 | 264 | ||
| 265 | case PERF_TYPE_TRACEPOINT: | ||
| 266 | return tracepoint_id_to_name(config); | ||
| 267 | |||
| 180 | default: | 268 | default: |
| 181 | break; | 269 | break; |
| 182 | } | 270 | } |
| @@ -265,6 +353,53 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) | |||
| 265 | return 1; | 353 | return 1; |
| 266 | } | 354 | } |
| 267 | 355 | ||
| 356 | static int parse_tracepoint_event(const char **strp, | ||
| 357 | struct perf_counter_attr *attr) | ||
| 358 | { | ||
| 359 | const char *evt_name; | ||
| 360 | char sys_name[MAX_EVENT_LENGTH]; | ||
| 361 | char id_buf[4]; | ||
| 362 | int fd; | ||
| 363 | unsigned int sys_length, evt_length; | ||
| 364 | u64 id; | ||
| 365 | char evt_path[MAXPATHLEN]; | ||
| 366 | |||
| 367 | if (valid_debugfs_mount(debugfs_path)) | ||
| 368 | return 0; | ||
| 369 | |||
| 370 | evt_name = strchr(*strp, ':'); | ||
| 371 | if (!evt_name) | ||
| 372 | return 0; | ||
| 373 | |||
| 374 | sys_length = evt_name - *strp; | ||
| 375 | if (sys_length >= MAX_EVENT_LENGTH) | ||
| 376 | return 0; | ||
| 377 | |||
| 378 | strncpy(sys_name, *strp, sys_length); | ||
| 379 | sys_name[sys_length] = '\0'; | ||
| 380 | evt_name = evt_name + 1; | ||
| 381 | evt_length = strlen(evt_name); | ||
| 382 | if (evt_length >= MAX_EVENT_LENGTH) | ||
| 383 | return 0; | ||
| 384 | |||
| 385 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | ||
| 386 | sys_name, evt_name); | ||
| 387 | fd = open(evt_path, O_RDONLY); | ||
| 388 | if (fd < 0) | ||
| 389 | return 0; | ||
| 390 | |||
| 391 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
| 392 | close(fd); | ||
| 393 | return 0; | ||
| 394 | } | ||
| 395 | close(fd); | ||
| 396 | id = atoll(id_buf); | ||
| 397 | attr->config = id; | ||
| 398 | attr->type = PERF_TYPE_TRACEPOINT; | ||
| 399 | *strp = evt_name + evt_length; | ||
| 400 | return 1; | ||
| 401 | } | ||
| 402 | |||
| 268 | static int check_events(const char *str, unsigned int i) | 403 | static int check_events(const char *str, unsigned int i) |
| 269 | { | 404 | { |
| 270 | int n; | 405 | int n; |
| @@ -374,7 +509,8 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr) | |||
| 374 | */ | 509 | */ |
| 375 | static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) | 510 | static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) |
| 376 | { | 511 | { |
| 377 | if (!(parse_raw_event(str, attr) || | 512 | if (!(parse_tracepoint_event(str, attr) || |
| 513 | parse_raw_event(str, attr) || | ||
| 378 | parse_numeric_event(str, attr) || | 514 | parse_numeric_event(str, attr) || |
| 379 | parse_symbolic_event(str, attr) || | 515 | parse_symbolic_event(str, attr) || |
| 380 | parse_generic_hw_event(str, attr))) | 516 | parse_generic_hw_event(str, attr))) |
| @@ -423,6 +559,42 @@ static const char * const event_type_descriptors[] = { | |||
| 423 | }; | 559 | }; |
| 424 | 560 | ||
| 425 | /* | 561 | /* |
| 562 | * Print the events from <debugfs_mount_point>/tracing/events | ||
| 563 | */ | ||
| 564 | |||
| 565 | static void print_tracepoint_events(void) | ||
| 566 | { | ||
| 567 | DIR *sys_dir, *evt_dir; | ||
| 568 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
| 569 | struct stat st; | ||
| 570 | char evt_path[MAXPATHLEN]; | ||
| 571 | |||
| 572 | if (valid_debugfs_mount(debugfs_path)) | ||
| 573 | return; | ||
| 574 | |||
| 575 | sys_dir = opendir(debugfs_path); | ||
| 576 | if (!sys_dir) | ||
| 577 | goto cleanup; | ||
| 578 | |||
| 579 | for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { | ||
| 580 | evt_dir = opendir(evt_path); | ||
| 581 | if (!evt_dir) | ||
| 582 | goto cleanup; | ||
| 583 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, | ||
| 584 | evt_path, st) { | ||
| 585 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | ||
| 586 | sys_dirent.d_name, evt_dirent.d_name); | ||
| 587 | fprintf(stderr, " %-40s [%s]\n", evt_path, | ||
| 588 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | ||
| 589 | } | ||
| 590 | closedir(evt_dir); | ||
| 591 | } | ||
| 592 | |||
| 593 | cleanup: | ||
| 594 | closedir(sys_dir); | ||
| 595 | } | ||
| 596 | |||
| 597 | /* | ||
| 426 | * Print the help text for the event symbols: | 598 | * Print the help text for the event symbols: |
| 427 | */ | 599 | */ |
| 428 | void print_events(void) | 600 | void print_events(void) |
| @@ -436,7 +608,7 @@ void print_events(void) | |||
| 436 | 608 | ||
| 437 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 609 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
| 438 | type = syms->type + 1; | 610 | type = syms->type + 1; |
| 439 | if (type > ARRAY_SIZE(event_type_descriptors)) | 611 | if (type >= ARRAY_SIZE(event_type_descriptors)) |
| 440 | type = 0; | 612 | type = 0; |
| 441 | 613 | ||
| 442 | if (type != prev_type) | 614 | if (type != prev_type) |
| @@ -472,5 +644,7 @@ void print_events(void) | |||
| 472 | "rNNN"); | 644 | "rNNN"); |
| 473 | fprintf(stderr, "\n"); | 645 | fprintf(stderr, "\n"); |
| 474 | 646 | ||
| 647 | print_tracepoint_events(); | ||
| 648 | |||
| 475 | exit(129); | 649 | exit(129); |
| 476 | } | 650 | } |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index e3d552908e60..1ea5d09b6eb1 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | * Parse symbolic events/counts passed in as options: | 3 | * Parse symbolic events/counts passed in as options: |
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| 6 | struct option; | ||
| 7 | |||
| 6 | extern int nr_counters; | 8 | extern int nr_counters; |
| 7 | 9 | ||
| 8 | extern struct perf_counter_attr attrs[MAX_COUNTERS]; | 10 | extern struct perf_counter_attr attrs[MAX_COUNTERS]; |
| @@ -15,3 +17,6 @@ extern int parse_events(const struct option *opt, const char *str, int unset); | |||
| 15 | 17 | ||
| 16 | extern void print_events(void); | 18 | extern void print_events(void); |
| 17 | 19 | ||
| 20 | extern char debugfs_path[]; | ||
| 21 | extern int valid_debugfs_mount(const char *debugfs); | ||
| 22 | |||
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index 3dca2f654cd0..bf39dfadfd24 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
| @@ -5,4 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | int hex2u64(const char *ptr, u64 *val); | 6 | int hex2u64(const char *ptr, u64 *val); |
| 7 | 7 | ||
| 8 | #define _STR(x) #x | ||
| 9 | #define STR(x) _STR(x) | ||
| 10 | |||
| 8 | #endif | 11 | #endif |
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 025a78edfffe..7ad38171dc2b 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c | |||
| @@ -64,6 +64,7 @@ int strlist__add(struct strlist *self, const char *new_entry) | |||
| 64 | 64 | ||
| 65 | rb_link_node(&sn->rb_node, parent, p); | 65 | rb_link_node(&sn->rb_node, parent, p); |
| 66 | rb_insert_color(&sn->rb_node, &self->entries); | 66 | rb_insert_color(&sn->rb_node, &self->entries); |
| 67 | ++self->nr_entries; | ||
| 67 | 68 | ||
| 68 | return 0; | 69 | return 0; |
| 69 | } | 70 | } |
| @@ -155,8 +156,9 @@ struct strlist *strlist__new(bool dupstr, const char *slist) | |||
| 155 | struct strlist *self = malloc(sizeof(*self)); | 156 | struct strlist *self = malloc(sizeof(*self)); |
| 156 | 157 | ||
| 157 | if (self != NULL) { | 158 | if (self != NULL) { |
| 158 | self->entries = RB_ROOT; | 159 | self->entries = RB_ROOT; |
| 159 | self->dupstr = dupstr; | 160 | self->dupstr = dupstr; |
| 161 | self->nr_entries = 0; | ||
| 160 | if (slist && strlist__parse_list(self, slist) != 0) | 162 | if (slist && strlist__parse_list(self, slist) != 0) |
| 161 | goto out_error; | 163 | goto out_error; |
| 162 | } | 164 | } |
| @@ -182,3 +184,17 @@ void strlist__delete(struct strlist *self) | |||
| 182 | free(self); | 184 | free(self); |
| 183 | } | 185 | } |
| 184 | } | 186 | } |
| 187 | |||
| 188 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx) | ||
| 189 | { | ||
| 190 | struct rb_node *nd; | ||
| 191 | |||
| 192 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | ||
| 193 | struct str_node *pos = rb_entry(nd, struct str_node, rb_node); | ||
| 194 | |||
| 195 | if (!idx--) | ||
| 196 | return pos; | ||
| 197 | } | ||
| 198 | |||
| 199 | return NULL; | ||
| 200 | } | ||
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index 2fdcfee87586..921818e44a54 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h | |||
| @@ -11,7 +11,8 @@ struct str_node { | |||
| 11 | 11 | ||
| 12 | struct strlist { | 12 | struct strlist { |
| 13 | struct rb_root entries; | 13 | struct rb_root entries; |
| 14 | bool dupstr; | 14 | unsigned int nr_entries; |
| 15 | bool dupstr; | ||
| 15 | }; | 16 | }; |
| 16 | 17 | ||
| 17 | struct strlist *strlist__new(bool dupstr, const char *slist); | 18 | struct strlist *strlist__new(bool dupstr, const char *slist); |
| @@ -21,11 +22,17 @@ void strlist__remove(struct strlist *self, struct str_node *sn); | |||
| 21 | int strlist__load(struct strlist *self, const char *filename); | 22 | int strlist__load(struct strlist *self, const char *filename); |
| 22 | int strlist__add(struct strlist *self, const char *str); | 23 | int strlist__add(struct strlist *self, const char *str); |
| 23 | 24 | ||
| 25 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); | ||
| 24 | bool strlist__has_entry(struct strlist *self, const char *entry); | 26 | bool strlist__has_entry(struct strlist *self, const char *entry); |
| 25 | 27 | ||
| 26 | static inline bool strlist__empty(const struct strlist *self) | 28 | static inline bool strlist__empty(const struct strlist *self) |
| 27 | { | 29 | { |
| 28 | return rb_first(&self->entries) == NULL; | 30 | return self->nr_entries == 0; |
| 31 | } | ||
| 32 | |||
| 33 | static inline unsigned int strlist__nr_entries(const struct strlist *self) | ||
| 34 | { | ||
| 35 | return self->nr_entries; | ||
| 29 | } | 36 | } |
| 30 | 37 | ||
| 31 | int strlist__parse_list(struct strlist *self, const char *s); | 38 | int strlist__parse_list(struct strlist *self, const char *s); |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 4683b67b5ee4..28106059bf12 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -6,9 +6,15 @@ | |||
| 6 | #include <libelf.h> | 6 | #include <libelf.h> |
| 7 | #include <gelf.h> | 7 | #include <gelf.h> |
| 8 | #include <elf.h> | 8 | #include <elf.h> |
| 9 | #include <bfd.h> | ||
| 9 | 10 | ||
| 10 | const char *sym_hist_filter; | 11 | const char *sym_hist_filter; |
| 11 | 12 | ||
| 13 | #ifndef DMGL_PARAMS | ||
| 14 | #define DMGL_PARAMS (1 << 0) /* Include function args */ | ||
| 15 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | ||
| 16 | #endif | ||
| 17 | |||
| 12 | static struct symbol *symbol__new(u64 start, u64 len, | 18 | static struct symbol *symbol__new(u64 start, u64 len, |
| 13 | const char *name, unsigned int priv_size, | 19 | const char *name, unsigned int priv_size, |
| 14 | u64 obj_start, int verbose) | 20 | u64 obj_start, int verbose) |
| @@ -65,6 +71,7 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size) | |||
| 65 | self->syms = RB_ROOT; | 71 | self->syms = RB_ROOT; |
| 66 | self->sym_priv_size = sym_priv_size; | 72 | self->sym_priv_size = sym_priv_size; |
| 67 | self->find_symbol = dso__find_symbol; | 73 | self->find_symbol = dso__find_symbol; |
| 74 | self->slen_calculated = 0; | ||
| 68 | } | 75 | } |
| 69 | 76 | ||
| 70 | return self; | 77 | return self; |
| @@ -373,36 +380,61 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
| 373 | idx < nr_entries; \ | 380 | idx < nr_entries; \ |
| 374 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | 381 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) |
| 375 | 382 | ||
| 376 | static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | 383 | /* |
| 377 | GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym, | 384 | * We need to check if we have a .dynsym, so that we can handle the |
| 378 | GElf_Shdr *shdr_dynsym, | 385 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it |
| 379 | size_t dynsym_idx, int verbose) | 386 | * .dynsym or .symtab). |
| 387 | * And always look at the original dso, not at debuginfo packages, that | ||
| 388 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | ||
| 389 | */ | ||
| 390 | static int dso__synthesize_plt_symbols(struct dso *self, int verbose) | ||
| 380 | { | 391 | { |
| 381 | uint32_t nr_rel_entries, idx; | 392 | uint32_t nr_rel_entries, idx; |
| 382 | GElf_Sym sym; | 393 | GElf_Sym sym; |
| 383 | u64 plt_offset; | 394 | u64 plt_offset; |
| 384 | GElf_Shdr shdr_plt; | 395 | GElf_Shdr shdr_plt; |
| 385 | struct symbol *f; | 396 | struct symbol *f; |
| 386 | GElf_Shdr shdr_rel_plt; | 397 | GElf_Shdr shdr_rel_plt, shdr_dynsym; |
| 387 | Elf_Data *reldata, *syms, *symstrs; | 398 | Elf_Data *reldata, *syms, *symstrs; |
| 388 | Elf_Scn *scn_plt_rel, *scn_symstrs; | 399 | Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; |
| 400 | size_t dynsym_idx; | ||
| 401 | GElf_Ehdr ehdr; | ||
| 389 | char sympltname[1024]; | 402 | char sympltname[1024]; |
| 390 | int nr = 0, symidx; | 403 | Elf *elf; |
| 404 | int nr = 0, symidx, fd, err = 0; | ||
| 405 | |||
| 406 | fd = open(self->name, O_RDONLY); | ||
| 407 | if (fd < 0) | ||
| 408 | goto out; | ||
| 409 | |||
| 410 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | ||
| 411 | if (elf == NULL) | ||
| 412 | goto out_close; | ||
| 413 | |||
| 414 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
| 415 | goto out_elf_end; | ||
| 416 | |||
| 417 | scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, | ||
| 418 | ".dynsym", &dynsym_idx); | ||
| 419 | if (scn_dynsym == NULL) | ||
| 420 | goto out_elf_end; | ||
| 391 | 421 | ||
| 392 | scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, | 422 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, |
| 393 | ".rela.plt", NULL); | 423 | ".rela.plt", NULL); |
| 394 | if (scn_plt_rel == NULL) { | 424 | if (scn_plt_rel == NULL) { |
| 395 | scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, | 425 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, |
| 396 | ".rel.plt", NULL); | 426 | ".rel.plt", NULL); |
| 397 | if (scn_plt_rel == NULL) | 427 | if (scn_plt_rel == NULL) |
| 398 | return 0; | 428 | goto out_elf_end; |
| 399 | } | 429 | } |
| 400 | 430 | ||
| 431 | err = -1; | ||
| 432 | |||
| 401 | if (shdr_rel_plt.sh_link != dynsym_idx) | 433 | if (shdr_rel_plt.sh_link != dynsym_idx) |
| 402 | return 0; | 434 | goto out_elf_end; |
| 403 | 435 | ||
| 404 | if (elf_section_by_name(elf, ehdr, &shdr_plt, ".plt", NULL) == NULL) | 436 | if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) |
| 405 | return 0; | 437 | goto out_elf_end; |
| 406 | 438 | ||
| 407 | /* | 439 | /* |
| 408 | * Fetch the relocation section to find the indexes to the GOT | 440 | * Fetch the relocation section to find the indexes to the GOT |
| @@ -410,19 +442,19 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 410 | */ | 442 | */ |
| 411 | reldata = elf_getdata(scn_plt_rel, NULL); | 443 | reldata = elf_getdata(scn_plt_rel, NULL); |
| 412 | if (reldata == NULL) | 444 | if (reldata == NULL) |
| 413 | return -1; | 445 | goto out_elf_end; |
| 414 | 446 | ||
| 415 | syms = elf_getdata(scn_dynsym, NULL); | 447 | syms = elf_getdata(scn_dynsym, NULL); |
| 416 | if (syms == NULL) | 448 | if (syms == NULL) |
| 417 | return -1; | 449 | goto out_elf_end; |
| 418 | 450 | ||
| 419 | scn_symstrs = elf_getscn(elf, shdr_dynsym->sh_link); | 451 | scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link); |
| 420 | if (scn_symstrs == NULL) | 452 | if (scn_symstrs == NULL) |
| 421 | return -1; | 453 | goto out_elf_end; |
| 422 | 454 | ||
| 423 | symstrs = elf_getdata(scn_symstrs, NULL); | 455 | symstrs = elf_getdata(scn_symstrs, NULL); |
| 424 | if (symstrs == NULL) | 456 | if (symstrs == NULL) |
| 425 | return -1; | 457 | goto out_elf_end; |
| 426 | 458 | ||
| 427 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; | 459 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; |
| 428 | plt_offset = shdr_plt.sh_offset; | 460 | plt_offset = shdr_plt.sh_offset; |
| @@ -441,7 +473,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 441 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 473 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
| 442 | sympltname, self->sym_priv_size, 0, verbose); | 474 | sympltname, self->sym_priv_size, 0, verbose); |
| 443 | if (!f) | 475 | if (!f) |
| 444 | return -1; | 476 | goto out_elf_end; |
| 445 | 477 | ||
| 446 | dso__insert_symbol(self, f); | 478 | dso__insert_symbol(self, f); |
| 447 | ++nr; | 479 | ++nr; |
| @@ -459,19 +491,25 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 459 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 491 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
| 460 | sympltname, self->sym_priv_size, 0, verbose); | 492 | sympltname, self->sym_priv_size, 0, verbose); |
| 461 | if (!f) | 493 | if (!f) |
| 462 | return -1; | 494 | goto out_elf_end; |
| 463 | 495 | ||
| 464 | dso__insert_symbol(self, f); | 496 | dso__insert_symbol(self, f); |
| 465 | ++nr; | 497 | ++nr; |
| 466 | } | 498 | } |
| 467 | } else { | ||
| 468 | /* | ||
| 469 | * TODO: There are still one more shdr_rel_plt.sh_type | ||
| 470 | * I have to investigate, but probably should be ignored. | ||
| 471 | */ | ||
| 472 | } | 499 | } |
| 473 | 500 | ||
| 474 | return nr; | 501 | err = 0; |
| 502 | out_elf_end: | ||
| 503 | elf_end(elf); | ||
| 504 | out_close: | ||
| 505 | close(fd); | ||
| 506 | |||
| 507 | if (err == 0) | ||
| 508 | return nr; | ||
| 509 | out: | ||
| 510 | fprintf(stderr, "%s: problems reading %s PLT info.\n", | ||
| 511 | __func__, self->name); | ||
| 512 | return 0; | ||
| 475 | } | 513 | } |
| 476 | 514 | ||
| 477 | static int dso__load_sym(struct dso *self, int fd, const char *name, | 515 | static int dso__load_sym(struct dso *self, int fd, const char *name, |
| @@ -485,10 +523,9 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 485 | GElf_Shdr shdr; | 523 | GElf_Shdr shdr; |
| 486 | Elf_Data *syms; | 524 | Elf_Data *syms; |
| 487 | GElf_Sym sym; | 525 | GElf_Sym sym; |
| 488 | Elf_Scn *sec, *sec_dynsym, *sec_strndx; | 526 | Elf_Scn *sec, *sec_strndx; |
| 489 | Elf *elf; | 527 | Elf *elf; |
| 490 | size_t dynsym_idx; | 528 | int nr = 0, kernel = !strcmp("[kernel]", self->name); |
| 491 | int nr = 0; | ||
| 492 | 529 | ||
| 493 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | 530 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); |
| 494 | if (elf == NULL) { | 531 | if (elf == NULL) { |
| @@ -504,32 +541,11 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 504 | goto out_elf_end; | 541 | goto out_elf_end; |
| 505 | } | 542 | } |
| 506 | 543 | ||
| 507 | /* | ||
| 508 | * We need to check if we have a .dynsym, so that we can handle the | ||
| 509 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it | ||
| 510 | * .dynsym or .symtab) | ||
| 511 | */ | ||
| 512 | sec_dynsym = elf_section_by_name(elf, &ehdr, &shdr, | ||
| 513 | ".dynsym", &dynsym_idx); | ||
| 514 | if (sec_dynsym != NULL) { | ||
| 515 | nr = dso__synthesize_plt_symbols(self, elf, &ehdr, | ||
| 516 | sec_dynsym, &shdr, | ||
| 517 | dynsym_idx, verbose); | ||
| 518 | if (nr < 0) | ||
| 519 | goto out_elf_end; | ||
| 520 | } | ||
| 521 | |||
| 522 | /* | ||
| 523 | * But if we have a full .symtab (that is a superset of .dynsym) we | ||
| 524 | * should add the symbols not in the .dynsyn | ||
| 525 | */ | ||
| 526 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | 544 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); |
| 527 | if (sec == NULL) { | 545 | if (sec == NULL) { |
| 528 | if (sec_dynsym == NULL) | 546 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); |
| 547 | if (sec == NULL) | ||
| 529 | goto out_elf_end; | 548 | goto out_elf_end; |
| 530 | |||
| 531 | sec = sec_dynsym; | ||
| 532 | gelf_getshdr(sec, &shdr); | ||
| 533 | } | 549 | } |
| 534 | 550 | ||
| 535 | syms = elf_getdata(sec, NULL); | 551 | syms = elf_getdata(sec, NULL); |
| @@ -555,12 +571,17 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 555 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 571 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
| 556 | 572 | ||
| 557 | memset(&sym, 0, sizeof(sym)); | 573 | memset(&sym, 0, sizeof(sym)); |
| 558 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | 574 | if (!kernel) { |
| 575 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | ||
| 559 | elf_section_by_name(elf, &ehdr, &shdr, | 576 | elf_section_by_name(elf, &ehdr, &shdr, |
| 560 | ".gnu.prelink_undo", | 577 | ".gnu.prelink_undo", |
| 561 | NULL) != NULL); | 578 | NULL) != NULL); |
| 579 | } else self->adjust_symbols = 0; | ||
| 580 | |||
| 562 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { | 581 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { |
| 563 | struct symbol *f; | 582 | struct symbol *f; |
| 583 | const char *name; | ||
| 584 | char *demangled; | ||
| 564 | u64 obj_start; | 585 | u64 obj_start; |
| 565 | struct section *section = NULL; | 586 | struct section *section = NULL; |
| 566 | int is_label = elf_sym__is_label(&sym); | 587 | int is_label = elf_sym__is_label(&sym); |
| @@ -599,10 +620,19 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 599 | goto out_elf_end; | 620 | goto out_elf_end; |
| 600 | } | 621 | } |
| 601 | } | 622 | } |
| 623 | /* | ||
| 624 | * We need to figure out if the object was created from C++ sources | ||
| 625 | * DWARF DW_compile_unit has this, but we don't always have access | ||
| 626 | * to it... | ||
| 627 | */ | ||
| 628 | name = elf_sym__name(&sym, symstrs); | ||
| 629 | demangled = bfd_demangle(NULL, name, DMGL_PARAMS | DMGL_ANSI); | ||
| 630 | if (demangled != NULL) | ||
| 631 | name = demangled; | ||
| 602 | 632 | ||
| 603 | f = symbol__new(sym.st_value, sym.st_size, | 633 | f = symbol__new(sym.st_value, sym.st_size, name, |
| 604 | elf_sym__name(&sym, symstrs), | ||
| 605 | self->sym_priv_size, obj_start, verbose); | 634 | self->sym_priv_size, obj_start, verbose); |
| 635 | free(demangled); | ||
| 606 | if (!f) | 636 | if (!f) |
| 607 | goto out_elf_end; | 637 | goto out_elf_end; |
| 608 | 638 | ||
| @@ -668,6 +698,11 @@ more: | |||
| 668 | if (!ret) | 698 | if (!ret) |
| 669 | goto more; | 699 | goto more; |
| 670 | 700 | ||
| 701 | if (ret > 0) { | ||
| 702 | int nr_plt = dso__synthesize_plt_symbols(self, verbose); | ||
| 703 | if (nr_plt > 0) | ||
| 704 | ret += nr_plt; | ||
| 705 | } | ||
| 671 | out: | 706 | out: |
| 672 | free(name); | 707 | free(name); |
| 673 | return ret; | 708 | return ret; |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 7918cffb23cd..2f92b21c712d 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -25,6 +25,7 @@ struct dso { | |||
| 25 | struct symbol *(*find_symbol)(struct dso *, u64 ip); | 25 | struct symbol *(*find_symbol)(struct dso *, u64 ip); |
| 26 | unsigned int sym_priv_size; | 26 | unsigned int sym_priv_size; |
| 27 | unsigned char adjust_symbols; | 27 | unsigned char adjust_symbols; |
| 28 | unsigned char slen_calculated; | ||
| 28 | char name[0]; | 29 | char name[0]; |
| 29 | }; | 30 | }; |
| 30 | 31 | ||
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> |
