diff options
| author | Ingo Molnar <mingo@elte.hu> | 2009-08-11 08:19:09 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-08-11 08:19:09 -0400 |
| commit | 89034bc2c7b839702c00a704e79d112737f98be0 (patch) | |
| tree | e65b1f3d4c751baa840efc81bc4734f089379eb3 /tools/perf/util | |
| parent | fb82ad719831db58e9baa4c67015aae3fe27e7e3 (diff) | |
| parent | 85dfd81dc57e8183a277ddd7a56aa65c96f3f487 (diff) | |
Merge branch 'linus' into tracing/core
Conflicts:
kernel/trace/trace_events_filter.c
We use the tracing/core version.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/util')
| -rw-r--r-- | tools/perf/util/cache.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/callchain.c | 32 | ||||
| -rw-r--r-- | tools/perf/util/callchain.h | 8 | ||||
| -rw-r--r-- | tools/perf/util/header.c | 5 | ||||
| -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 | 206 | ||||
| -rw-r--r-- | tools/perf/util/parse-events.h | 6 | ||||
| -rw-r--r-- | tools/perf/util/quote.c | 2 | ||||
| -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 | 281 | ||||
| -rw-r--r-- | tools/perf/util/symbol.h | 3 | ||||
| -rw-r--r-- | tools/perf/util/util.h | 2 |
15 files changed, 499 insertions, 91 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/callchain.c b/tools/perf/util/callchain.c index 9d3c8141b8c1..011473411642 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <stdio.h> | 13 | #include <stdio.h> |
| 14 | #include <stdbool.h> | 14 | #include <stdbool.h> |
| 15 | #include <errno.h> | 15 | #include <errno.h> |
| 16 | #include <math.h> | ||
| 16 | 17 | ||
| 17 | #include "callchain.h" | 18 | #include "callchain.h" |
| 18 | 19 | ||
| @@ -26,10 +27,14 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | |||
| 26 | struct rb_node **p = &root->rb_node; | 27 | struct rb_node **p = &root->rb_node; |
| 27 | struct rb_node *parent = NULL; | 28 | struct rb_node *parent = NULL; |
| 28 | struct callchain_node *rnode; | 29 | struct callchain_node *rnode; |
| 30 | u64 chain_cumul = cumul_hits(chain); | ||
| 29 | 31 | ||
| 30 | while (*p) { | 32 | while (*p) { |
| 33 | u64 rnode_cumul; | ||
| 34 | |||
| 31 | parent = *p; | 35 | parent = *p; |
| 32 | rnode = rb_entry(parent, struct callchain_node, rb_node); | 36 | rnode = rb_entry(parent, struct callchain_node, rb_node); |
| 37 | rnode_cumul = cumul_hits(rnode); | ||
| 33 | 38 | ||
| 34 | switch (mode) { | 39 | switch (mode) { |
| 35 | case CHAIN_FLAT: | 40 | case CHAIN_FLAT: |
| @@ -40,7 +45,7 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | |||
| 40 | break; | 45 | break; |
| 41 | case CHAIN_GRAPH_ABS: /* Falldown */ | 46 | case CHAIN_GRAPH_ABS: /* Falldown */ |
| 42 | case CHAIN_GRAPH_REL: | 47 | case CHAIN_GRAPH_REL: |
| 43 | if (rnode->cumul_hit < chain->cumul_hit) | 48 | if (rnode_cumul < chain_cumul) |
| 44 | p = &(*p)->rb_left; | 49 | p = &(*p)->rb_left; |
| 45 | else | 50 | else |
| 46 | p = &(*p)->rb_right; | 51 | p = &(*p)->rb_right; |
| @@ -87,7 +92,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node, | |||
| 87 | 92 | ||
| 88 | chain_for_each_child(child, node) { | 93 | chain_for_each_child(child, node) { |
| 89 | __sort_chain_graph_abs(child, min_hit); | 94 | __sort_chain_graph_abs(child, min_hit); |
| 90 | if (child->cumul_hit >= min_hit) | 95 | if (cumul_hits(child) >= min_hit) |
| 91 | rb_insert_callchain(&node->rb_root, child, | 96 | rb_insert_callchain(&node->rb_root, child, |
| 92 | CHAIN_GRAPH_ABS); | 97 | CHAIN_GRAPH_ABS); |
| 93 | } | 98 | } |
| @@ -108,11 +113,11 @@ static void __sort_chain_graph_rel(struct callchain_node *node, | |||
| 108 | u64 min_hit; | 113 | u64 min_hit; |
| 109 | 114 | ||
| 110 | node->rb_root = RB_ROOT; | 115 | node->rb_root = RB_ROOT; |
| 111 | min_hit = node->cumul_hit * min_percent / 100.0; | 116 | min_hit = ceil(node->children_hit * min_percent); |
| 112 | 117 | ||
| 113 | chain_for_each_child(child, node) { | 118 | chain_for_each_child(child, node) { |
| 114 | __sort_chain_graph_rel(child, min_percent); | 119 | __sort_chain_graph_rel(child, min_percent); |
| 115 | if (child->cumul_hit >= min_hit) | 120 | if (cumul_hits(child) >= min_hit) |
| 116 | rb_insert_callchain(&node->rb_root, child, | 121 | rb_insert_callchain(&node->rb_root, child, |
| 117 | CHAIN_GRAPH_REL); | 122 | CHAIN_GRAPH_REL); |
| 118 | } | 123 | } |
| @@ -122,7 +127,7 @@ static void | |||
| 122 | sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root, | 127 | sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root, |
| 123 | u64 min_hit __used, struct callchain_param *param) | 128 | u64 min_hit __used, struct callchain_param *param) |
| 124 | { | 129 | { |
| 125 | __sort_chain_graph_rel(chain_root, param->min_percent); | 130 | __sort_chain_graph_rel(chain_root, param->min_percent / 100.0); |
| 126 | rb_root->rb_node = chain_root->rb_root.rb_node; | 131 | rb_root->rb_node = chain_root->rb_root.rb_node; |
| 127 | } | 132 | } |
| 128 | 133 | ||
| @@ -211,7 +216,8 @@ add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
| 211 | new = create_child(parent, false); | 216 | new = create_child(parent, false); |
| 212 | fill_node(new, chain, start, syms); | 217 | fill_node(new, chain, start, syms); |
| 213 | 218 | ||
| 214 | new->cumul_hit = new->hit = 1; | 219 | new->children_hit = 0; |
| 220 | new->hit = 1; | ||
| 215 | } | 221 | } |
| 216 | 222 | ||
| 217 | /* | 223 | /* |
| @@ -241,7 +247,8 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
| 241 | 247 | ||
| 242 | /* split the hits */ | 248 | /* split the hits */ |
| 243 | new->hit = parent->hit; | 249 | new->hit = parent->hit; |
| 244 | new->cumul_hit = parent->cumul_hit; | 250 | new->children_hit = parent->children_hit; |
| 251 | parent->children_hit = cumul_hits(new); | ||
| 245 | new->val_nr = parent->val_nr - idx_local; | 252 | new->val_nr = parent->val_nr - idx_local; |
| 246 | parent->val_nr = idx_local; | 253 | parent->val_nr = idx_local; |
| 247 | 254 | ||
| @@ -249,6 +256,7 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
| 249 | if (idx_total < chain->nr) { | 256 | if (idx_total < chain->nr) { |
| 250 | parent->hit = 0; | 257 | parent->hit = 0; |
| 251 | add_child(parent, chain, idx_total, syms); | 258 | add_child(parent, chain, idx_total, syms); |
| 259 | parent->children_hit++; | ||
| 252 | } else { | 260 | } else { |
| 253 | parent->hit = 1; | 261 | parent->hit = 1; |
| 254 | } | 262 | } |
| @@ -269,13 +277,13 @@ __append_chain_children(struct callchain_node *root, struct ip_callchain *chain, | |||
| 269 | unsigned int ret = __append_chain(rnode, chain, start, syms); | 277 | unsigned int ret = __append_chain(rnode, chain, start, syms); |
| 270 | 278 | ||
| 271 | if (!ret) | 279 | if (!ret) |
| 272 | goto cumul; | 280 | goto inc_children_hit; |
| 273 | } | 281 | } |
| 274 | /* nothing in children, add to the current node */ | 282 | /* nothing in children, add to the current node */ |
| 275 | add_child(root, chain, start, syms); | 283 | add_child(root, chain, start, syms); |
| 276 | 284 | ||
| 277 | cumul: | 285 | inc_children_hit: |
| 278 | root->cumul_hit++; | 286 | root->children_hit++; |
| 279 | } | 287 | } |
| 280 | 288 | ||
| 281 | static int | 289 | static int |
| @@ -317,8 +325,6 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, | |||
| 317 | /* we match 100% of the path, increment the hit */ | 325 | /* we match 100% of the path, increment the hit */ |
| 318 | if (i - start == root->val_nr && i == chain->nr) { | 326 | if (i - start == root->val_nr && i == chain->nr) { |
| 319 | root->hit++; | 327 | root->hit++; |
| 320 | root->cumul_hit++; | ||
| 321 | |||
| 322 | return 0; | 328 | return 0; |
| 323 | } | 329 | } |
| 324 | 330 | ||
| @@ -331,5 +337,7 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, | |||
| 331 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | 337 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, |
| 332 | struct symbol **syms) | 338 | struct symbol **syms) |
| 333 | { | 339 | { |
| 340 | if (!chain->nr) | ||
| 341 | return; | ||
| 334 | __append_chain_children(root, chain, syms, 0); | 342 | __append_chain_children(root, chain, syms, 0); |
| 335 | } | 343 | } |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 7812122bea1d..a926ae4f5a16 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "symbol.h" | 7 | #include "symbol.h" |
| 8 | 8 | ||
| 9 | enum chain_mode { | 9 | enum chain_mode { |
| 10 | CHAIN_NONE, | ||
| 10 | CHAIN_FLAT, | 11 | CHAIN_FLAT, |
| 11 | CHAIN_GRAPH_ABS, | 12 | CHAIN_GRAPH_ABS, |
| 12 | CHAIN_GRAPH_REL | 13 | CHAIN_GRAPH_REL |
| @@ -21,7 +22,7 @@ struct callchain_node { | |||
| 21 | struct rb_root rb_root; /* sorted tree of children */ | 22 | struct rb_root rb_root; /* sorted tree of children */ |
| 22 | unsigned int val_nr; | 23 | unsigned int val_nr; |
| 23 | u64 hit; | 24 | u64 hit; |
| 24 | u64 cumul_hit; /* hit + hits of children */ | 25 | u64 children_hit; |
| 25 | }; | 26 | }; |
| 26 | 27 | ||
| 27 | struct callchain_param; | 28 | struct callchain_param; |
| @@ -48,6 +49,11 @@ static inline void callchain_init(struct callchain_node *node) | |||
| 48 | INIT_LIST_HEAD(&node->val); | 49 | INIT_LIST_HEAD(&node->val); |
| 49 | } | 50 | } |
| 50 | 51 | ||
| 52 | static inline u64 cumul_hits(struct callchain_node *node) | ||
| 53 | { | ||
| 54 | return node->hit + node->children_hit; | ||
| 55 | } | ||
| 56 | |||
| 51 | int register_callchain_param(struct callchain_param *param); | 57 | int register_callchain_param(struct callchain_param *param); |
| 52 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | 58 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, |
| 53 | struct symbol **syms); | 59 | struct symbol **syms); |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 450384b3bbe5..b92a457ca32e 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -185,6 +185,8 @@ static void do_read(int fd, void *buf, size_t size) | |||
| 185 | 185 | ||
| 186 | if (ret < 0) | 186 | if (ret < 0) |
| 187 | die("failed to read"); | 187 | die("failed to read"); |
| 188 | if (ret == 0) | ||
| 189 | die("failed to read: missing data"); | ||
| 188 | 190 | ||
| 189 | size -= ret; | 191 | size -= ret; |
| 190 | buf += ret; | 192 | buf += ret; |
| @@ -213,9 +215,10 @@ struct perf_header *perf_header__read(int fd) | |||
| 213 | 215 | ||
| 214 | for (i = 0; i < nr_attrs; i++) { | 216 | for (i = 0; i < nr_attrs; i++) { |
| 215 | struct perf_header_attr *attr; | 217 | struct perf_header_attr *attr; |
| 216 | off_t tmp = lseek(fd, 0, SEEK_CUR); | 218 | off_t tmp; |
| 217 | 219 | ||
| 218 | do_read(fd, &f_attr, sizeof(f_attr)); | 220 | do_read(fd, &f_attr, sizeof(f_attr)); |
| 221 | tmp = lseek(fd, 0, SEEK_CUR); | ||
| 219 | 222 | ||
| 220 | attr = perf_header_attr__new(&f_attr.attr); | 223 | attr = perf_header_attr__new(&f_attr.attr); |
| 221 | 224 | ||
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..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 | } |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index e3d552908e60..192a962e3a0f 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
| @@ -3,11 +3,14 @@ | |||
| 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]; |
| 9 | 11 | ||
| 10 | extern char *event_name(int ctr); | 12 | extern char *event_name(int ctr); |
| 13 | extern char *__event_name(int type, u64 config); | ||
| 11 | 14 | ||
| 12 | extern int parse_events(const struct option *opt, const char *str, int unset); | 15 | extern int parse_events(const struct option *opt, const char *str, int unset); |
| 13 | 16 | ||
| @@ -15,3 +18,6 @@ extern int parse_events(const struct option *opt, const char *str, int unset); | |||
| 15 | 18 | ||
| 16 | extern void print_events(void); | 19 | extern void print_events(void); |
| 17 | 20 | ||
| 21 | extern char debugfs_path[]; | ||
| 22 | extern int valid_debugfs_mount(const char *debugfs); | ||
| 23 | |||
diff --git a/tools/perf/util/quote.c b/tools/perf/util/quote.c index c6e5dc0dc82f..2726fe40eb5d 100644 --- a/tools/perf/util/quote.c +++ b/tools/perf/util/quote.c | |||
| @@ -318,7 +318,7 @@ char *quote_path_relative(const char *in, int len, | |||
| 318 | strbuf_addch(out, '"'); | 318 | strbuf_addch(out, '"'); |
| 319 | if (prefix) { | 319 | if (prefix) { |
| 320 | int off = 0; | 320 | int off = 0; |
| 321 | while (prefix[off] && off < len && prefix[off] == in[off]) | 321 | while (off < len && prefix[off] && prefix[off] == in[off]) |
| 322 | if (prefix[off] == '/') { | 322 | if (prefix[off] == '/') { |
| 323 | prefix += off + 1; | 323 | prefix += off + 1; |
| 324 | in += off + 1; | 324 | in += off + 1; |
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..f1dcede14307 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -7,8 +7,33 @@ | |||
| 7 | #include <gelf.h> | 7 | #include <gelf.h> |
| 8 | #include <elf.h> | 8 | #include <elf.h> |
| 9 | 9 | ||
| 10 | #ifndef NO_DEMANGLE | ||
| 11 | #include <bfd.h> | ||
| 12 | #else | ||
| 13 | static inline | ||
| 14 | char *bfd_demangle(void __used *v, const char __used *c, int __used i) | ||
| 15 | { | ||
| 16 | return NULL; | ||
| 17 | } | ||
| 18 | #endif | ||
| 19 | |||
| 10 | const char *sym_hist_filter; | 20 | const char *sym_hist_filter; |
| 11 | 21 | ||
| 22 | #ifndef DMGL_PARAMS | ||
| 23 | #define DMGL_PARAMS (1 << 0) /* Include function args */ | ||
| 24 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | ||
| 25 | #endif | ||
| 26 | |||
| 27 | enum dso_origin { | ||
| 28 | DSO__ORIG_KERNEL = 0, | ||
| 29 | DSO__ORIG_JAVA_JIT, | ||
| 30 | DSO__ORIG_FEDORA, | ||
| 31 | DSO__ORIG_UBUNTU, | ||
| 32 | DSO__ORIG_BUILDID, | ||
| 33 | DSO__ORIG_DSO, | ||
| 34 | DSO__ORIG_NOT_FOUND, | ||
| 35 | }; | ||
| 36 | |||
| 12 | static struct symbol *symbol__new(u64 start, u64 len, | 37 | static struct symbol *symbol__new(u64 start, u64 len, |
| 13 | const char *name, unsigned int priv_size, | 38 | const char *name, unsigned int priv_size, |
| 14 | u64 obj_start, int verbose) | 39 | u64 obj_start, int verbose) |
| @@ -65,6 +90,8 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size) | |||
| 65 | self->syms = RB_ROOT; | 90 | self->syms = RB_ROOT; |
| 66 | self->sym_priv_size = sym_priv_size; | 91 | self->sym_priv_size = sym_priv_size; |
| 67 | self->find_symbol = dso__find_symbol; | 92 | self->find_symbol = dso__find_symbol; |
| 93 | self->slen_calculated = 0; | ||
| 94 | self->origin = DSO__ORIG_NOT_FOUND; | ||
| 68 | } | 95 | } |
| 69 | 96 | ||
| 70 | return self; | 97 | return self; |
| @@ -373,36 +400,61 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
| 373 | idx < nr_entries; \ | 400 | idx < nr_entries; \ |
| 374 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | 401 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) |
| 375 | 402 | ||
| 376 | static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | 403 | /* |
| 377 | GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym, | 404 | * We need to check if we have a .dynsym, so that we can handle the |
| 378 | GElf_Shdr *shdr_dynsym, | 405 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it |
| 379 | size_t dynsym_idx, int verbose) | 406 | * .dynsym or .symtab). |
| 407 | * And always look at the original dso, not at debuginfo packages, that | ||
| 408 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | ||
| 409 | */ | ||
| 410 | static int dso__synthesize_plt_symbols(struct dso *self, int verbose) | ||
| 380 | { | 411 | { |
| 381 | uint32_t nr_rel_entries, idx; | 412 | uint32_t nr_rel_entries, idx; |
| 382 | GElf_Sym sym; | 413 | GElf_Sym sym; |
| 383 | u64 plt_offset; | 414 | u64 plt_offset; |
| 384 | GElf_Shdr shdr_plt; | 415 | GElf_Shdr shdr_plt; |
| 385 | struct symbol *f; | 416 | struct symbol *f; |
| 386 | GElf_Shdr shdr_rel_plt; | 417 | GElf_Shdr shdr_rel_plt, shdr_dynsym; |
| 387 | Elf_Data *reldata, *syms, *symstrs; | 418 | Elf_Data *reldata, *syms, *symstrs; |
| 388 | Elf_Scn *scn_plt_rel, *scn_symstrs; | 419 | Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; |
| 420 | size_t dynsym_idx; | ||
| 421 | GElf_Ehdr ehdr; | ||
| 389 | char sympltname[1024]; | 422 | char sympltname[1024]; |
| 390 | int nr = 0, symidx; | 423 | Elf *elf; |
| 424 | int nr = 0, symidx, fd, err = 0; | ||
| 425 | |||
| 426 | fd = open(self->name, O_RDONLY); | ||
| 427 | if (fd < 0) | ||
| 428 | goto out; | ||
| 429 | |||
| 430 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | ||
| 431 | if (elf == NULL) | ||
| 432 | goto out_close; | ||
| 433 | |||
| 434 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
| 435 | goto out_elf_end; | ||
| 391 | 436 | ||
| 392 | scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, | 437 | scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, |
| 438 | ".dynsym", &dynsym_idx); | ||
| 439 | if (scn_dynsym == NULL) | ||
| 440 | goto out_elf_end; | ||
| 441 | |||
| 442 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | ||
| 393 | ".rela.plt", NULL); | 443 | ".rela.plt", NULL); |
| 394 | if (scn_plt_rel == NULL) { | 444 | if (scn_plt_rel == NULL) { |
| 395 | scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, | 445 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, |
| 396 | ".rel.plt", NULL); | 446 | ".rel.plt", NULL); |
| 397 | if (scn_plt_rel == NULL) | 447 | if (scn_plt_rel == NULL) |
| 398 | return 0; | 448 | goto out_elf_end; |
| 399 | } | 449 | } |
| 400 | 450 | ||
| 451 | err = -1; | ||
| 452 | |||
| 401 | if (shdr_rel_plt.sh_link != dynsym_idx) | 453 | if (shdr_rel_plt.sh_link != dynsym_idx) |
| 402 | return 0; | 454 | goto out_elf_end; |
| 403 | 455 | ||
| 404 | if (elf_section_by_name(elf, ehdr, &shdr_plt, ".plt", NULL) == NULL) | 456 | if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) |
| 405 | return 0; | 457 | goto out_elf_end; |
| 406 | 458 | ||
| 407 | /* | 459 | /* |
| 408 | * Fetch the relocation section to find the indexes to the GOT | 460 | * Fetch the relocation section to find the indexes to the GOT |
| @@ -410,19 +462,19 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 410 | */ | 462 | */ |
| 411 | reldata = elf_getdata(scn_plt_rel, NULL); | 463 | reldata = elf_getdata(scn_plt_rel, NULL); |
| 412 | if (reldata == NULL) | 464 | if (reldata == NULL) |
| 413 | return -1; | 465 | goto out_elf_end; |
| 414 | 466 | ||
| 415 | syms = elf_getdata(scn_dynsym, NULL); | 467 | syms = elf_getdata(scn_dynsym, NULL); |
| 416 | if (syms == NULL) | 468 | if (syms == NULL) |
| 417 | return -1; | 469 | goto out_elf_end; |
| 418 | 470 | ||
| 419 | scn_symstrs = elf_getscn(elf, shdr_dynsym->sh_link); | 471 | scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link); |
| 420 | if (scn_symstrs == NULL) | 472 | if (scn_symstrs == NULL) |
| 421 | return -1; | 473 | goto out_elf_end; |
| 422 | 474 | ||
| 423 | symstrs = elf_getdata(scn_symstrs, NULL); | 475 | symstrs = elf_getdata(scn_symstrs, NULL); |
| 424 | if (symstrs == NULL) | 476 | if (symstrs == NULL) |
| 425 | return -1; | 477 | goto out_elf_end; |
| 426 | 478 | ||
| 427 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; | 479 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; |
| 428 | plt_offset = shdr_plt.sh_offset; | 480 | plt_offset = shdr_plt.sh_offset; |
| @@ -441,7 +493,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 441 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 493 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
| 442 | sympltname, self->sym_priv_size, 0, verbose); | 494 | sympltname, self->sym_priv_size, 0, verbose); |
| 443 | if (!f) | 495 | if (!f) |
| 444 | return -1; | 496 | goto out_elf_end; |
| 445 | 497 | ||
| 446 | dso__insert_symbol(self, f); | 498 | dso__insert_symbol(self, f); |
| 447 | ++nr; | 499 | ++nr; |
| @@ -459,19 +511,25 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 459 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 511 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
| 460 | sympltname, self->sym_priv_size, 0, verbose); | 512 | sympltname, self->sym_priv_size, 0, verbose); |
| 461 | if (!f) | 513 | if (!f) |
| 462 | return -1; | 514 | goto out_elf_end; |
| 463 | 515 | ||
| 464 | dso__insert_symbol(self, f); | 516 | dso__insert_symbol(self, f); |
| 465 | ++nr; | 517 | ++nr; |
| 466 | } | 518 | } |
| 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 | } | 519 | } |
| 473 | 520 | ||
| 474 | return nr; | 521 | err = 0; |
| 522 | out_elf_end: | ||
| 523 | elf_end(elf); | ||
| 524 | out_close: | ||
| 525 | close(fd); | ||
| 526 | |||
| 527 | if (err == 0) | ||
| 528 | return nr; | ||
| 529 | out: | ||
| 530 | fprintf(stderr, "%s: problems reading %s PLT info.\n", | ||
| 531 | __func__, self->name); | ||
| 532 | return 0; | ||
| 475 | } | 533 | } |
| 476 | 534 | ||
| 477 | static int dso__load_sym(struct dso *self, int fd, const char *name, | 535 | static int dso__load_sym(struct dso *self, int fd, const char *name, |
| @@ -485,10 +543,9 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 485 | GElf_Shdr shdr; | 543 | GElf_Shdr shdr; |
| 486 | Elf_Data *syms; | 544 | Elf_Data *syms; |
| 487 | GElf_Sym sym; | 545 | GElf_Sym sym; |
| 488 | Elf_Scn *sec, *sec_dynsym, *sec_strndx; | 546 | Elf_Scn *sec, *sec_strndx; |
| 489 | Elf *elf; | 547 | Elf *elf; |
| 490 | size_t dynsym_idx; | 548 | int nr = 0, kernel = !strcmp("[kernel]", self->name); |
| 491 | int nr = 0; | ||
| 492 | 549 | ||
| 493 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | 550 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); |
| 494 | if (elf == NULL) { | 551 | if (elf == NULL) { |
| @@ -504,32 +561,11 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 504 | goto out_elf_end; | 561 | goto out_elf_end; |
| 505 | } | 562 | } |
| 506 | 563 | ||
| 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); | 564 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); |
| 527 | if (sec == NULL) { | 565 | if (sec == NULL) { |
| 528 | if (sec_dynsym == NULL) | 566 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); |
| 567 | if (sec == NULL) | ||
| 529 | goto out_elf_end; | 568 | goto out_elf_end; |
| 530 | |||
| 531 | sec = sec_dynsym; | ||
| 532 | gelf_getshdr(sec, &shdr); | ||
| 533 | } | 569 | } |
| 534 | 570 | ||
| 535 | syms = elf_getdata(sec, NULL); | 571 | syms = elf_getdata(sec, NULL); |
| @@ -549,18 +585,23 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 549 | goto out_elf_end; | 585 | goto out_elf_end; |
| 550 | 586 | ||
| 551 | secstrs = elf_getdata(sec_strndx, NULL); | 587 | secstrs = elf_getdata(sec_strndx, NULL); |
| 552 | if (symstrs == NULL) | 588 | if (secstrs == NULL) |
| 553 | goto out_elf_end; | 589 | goto out_elf_end; |
| 554 | 590 | ||
| 555 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 591 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
| 556 | 592 | ||
| 557 | memset(&sym, 0, sizeof(sym)); | 593 | memset(&sym, 0, sizeof(sym)); |
| 558 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | 594 | if (!kernel) { |
| 595 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | ||
| 559 | elf_section_by_name(elf, &ehdr, &shdr, | 596 | elf_section_by_name(elf, &ehdr, &shdr, |
| 560 | ".gnu.prelink_undo", | 597 | ".gnu.prelink_undo", |
| 561 | NULL) != NULL); | 598 | NULL) != NULL); |
| 599 | } else self->adjust_symbols = 0; | ||
| 600 | |||
| 562 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { | 601 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { |
| 563 | struct symbol *f; | 602 | struct symbol *f; |
| 603 | const char *name; | ||
| 604 | char *demangled; | ||
| 564 | u64 obj_start; | 605 | u64 obj_start; |
| 565 | struct section *section = NULL; | 606 | struct section *section = NULL; |
| 566 | int is_label = elf_sym__is_label(&sym); | 607 | int is_label = elf_sym__is_label(&sym); |
| @@ -599,10 +640,19 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 599 | goto out_elf_end; | 640 | goto out_elf_end; |
| 600 | } | 641 | } |
| 601 | } | 642 | } |
| 643 | /* | ||
| 644 | * We need to figure out if the object was created from C++ sources | ||
| 645 | * DWARF DW_compile_unit has this, but we don't always have access | ||
| 646 | * to it... | ||
| 647 | */ | ||
| 648 | name = elf_sym__name(&sym, symstrs); | ||
| 649 | demangled = bfd_demangle(NULL, name, DMGL_PARAMS | DMGL_ANSI); | ||
| 650 | if (demangled != NULL) | ||
| 651 | name = demangled; | ||
| 602 | 652 | ||
| 603 | f = symbol__new(sym.st_value, sym.st_size, | 653 | f = symbol__new(sym.st_value, sym.st_size, name, |
| 604 | elf_sym__name(&sym, symstrs), | ||
| 605 | self->sym_priv_size, obj_start, verbose); | 654 | self->sym_priv_size, obj_start, verbose); |
| 655 | free(demangled); | ||
| 606 | if (!f) | 656 | if (!f) |
| 607 | goto out_elf_end; | 657 | goto out_elf_end; |
| 608 | 658 | ||
| @@ -622,11 +672,85 @@ out_close: | |||
| 622 | return err; | 672 | return err; |
| 623 | } | 673 | } |
| 624 | 674 | ||
| 675 | #define BUILD_ID_SIZE 128 | ||
| 676 | |||
| 677 | static char *dso__read_build_id(struct dso *self, int verbose) | ||
| 678 | { | ||
| 679 | int i; | ||
| 680 | GElf_Ehdr ehdr; | ||
| 681 | GElf_Shdr shdr; | ||
| 682 | Elf_Data *build_id_data; | ||
| 683 | Elf_Scn *sec; | ||
| 684 | char *build_id = NULL, *bid; | ||
| 685 | unsigned char *raw; | ||
| 686 | Elf *elf; | ||
| 687 | int fd = open(self->name, O_RDONLY); | ||
| 688 | |||
| 689 | if (fd < 0) | ||
| 690 | goto out; | ||
| 691 | |||
| 692 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | ||
| 693 | if (elf == NULL) { | ||
| 694 | if (verbose) | ||
| 695 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
| 696 | __func__, self->name); | ||
| 697 | goto out_close; | ||
| 698 | } | ||
| 699 | |||
| 700 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
| 701 | if (verbose) | ||
| 702 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
| 703 | goto out_elf_end; | ||
| 704 | } | ||
| 705 | |||
| 706 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); | ||
| 707 | if (sec == NULL) | ||
| 708 | goto out_elf_end; | ||
| 709 | |||
| 710 | build_id_data = elf_getdata(sec, NULL); | ||
| 711 | if (build_id_data == NULL) | ||
| 712 | goto out_elf_end; | ||
| 713 | build_id = malloc(BUILD_ID_SIZE); | ||
| 714 | if (build_id == NULL) | ||
| 715 | goto out_elf_end; | ||
| 716 | raw = build_id_data->d_buf + 16; | ||
| 717 | bid = build_id; | ||
| 718 | |||
| 719 | for (i = 0; i < 20; ++i) { | ||
| 720 | sprintf(bid, "%02x", *raw); | ||
| 721 | ++raw; | ||
| 722 | bid += 2; | ||
| 723 | } | ||
| 724 | if (verbose >= 2) | ||
| 725 | printf("%s(%s): %s\n", __func__, self->name, build_id); | ||
| 726 | out_elf_end: | ||
| 727 | elf_end(elf); | ||
| 728 | out_close: | ||
| 729 | close(fd); | ||
| 730 | out: | ||
| 731 | return build_id; | ||
| 732 | } | ||
| 733 | |||
| 734 | char dso__symtab_origin(const struct dso *self) | ||
| 735 | { | ||
| 736 | static const char origin[] = { | ||
| 737 | [DSO__ORIG_KERNEL] = 'k', | ||
| 738 | [DSO__ORIG_JAVA_JIT] = 'j', | ||
| 739 | [DSO__ORIG_FEDORA] = 'f', | ||
| 740 | [DSO__ORIG_UBUNTU] = 'u', | ||
| 741 | [DSO__ORIG_BUILDID] = 'b', | ||
| 742 | [DSO__ORIG_DSO] = 'd', | ||
| 743 | }; | ||
| 744 | |||
| 745 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | ||
| 746 | return '!'; | ||
| 747 | return origin[self->origin]; | ||
| 748 | } | ||
| 749 | |||
| 625 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose) | 750 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose) |
| 626 | { | 751 | { |
| 627 | int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug"); | 752 | int size = PATH_MAX; |
| 628 | char *name = malloc(size); | 753 | char *name = malloc(size), *build_id = NULL; |
| 629 | int variant = 0; | ||
| 630 | int ret = -1; | 754 | int ret = -1; |
| 631 | int fd; | 755 | int fd; |
| 632 | 756 | ||
| @@ -635,26 +759,43 @@ int dso__load(struct dso *self, symbol_filter_t filter, int verbose) | |||
| 635 | 759 | ||
| 636 | self->adjust_symbols = 0; | 760 | self->adjust_symbols = 0; |
| 637 | 761 | ||
| 638 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) | 762 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { |
| 639 | return dso__load_perf_map(self, filter, verbose); | 763 | ret = dso__load_perf_map(self, filter, verbose); |
| 764 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : | ||
| 765 | DSO__ORIG_NOT_FOUND; | ||
| 766 | return ret; | ||
| 767 | } | ||
| 768 | |||
| 769 | self->origin = DSO__ORIG_FEDORA - 1; | ||
| 640 | 770 | ||
| 641 | more: | 771 | more: |
| 642 | do { | 772 | do { |
| 643 | switch (variant) { | 773 | self->origin++; |
| 644 | case 0: /* Fedora */ | 774 | switch (self->origin) { |
| 775 | case DSO__ORIG_FEDORA: | ||
| 645 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); | 776 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); |
| 646 | break; | 777 | break; |
| 647 | case 1: /* Ubuntu */ | 778 | case DSO__ORIG_UBUNTU: |
| 648 | snprintf(name, size, "/usr/lib/debug%s", self->name); | 779 | snprintf(name, size, "/usr/lib/debug%s", self->name); |
| 649 | break; | 780 | break; |
| 650 | case 2: /* Sane people */ | 781 | case DSO__ORIG_BUILDID: |
| 782 | build_id = dso__read_build_id(self, verbose); | ||
| 783 | if (build_id != NULL) { | ||
| 784 | snprintf(name, size, | ||
| 785 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | ||
| 786 | build_id, build_id + 2); | ||
| 787 | free(build_id); | ||
| 788 | break; | ||
| 789 | } | ||
| 790 | self->origin++; | ||
| 791 | /* Fall thru */ | ||
| 792 | case DSO__ORIG_DSO: | ||
| 651 | snprintf(name, size, "%s", self->name); | 793 | snprintf(name, size, "%s", self->name); |
| 652 | break; | 794 | break; |
| 653 | 795 | ||
| 654 | default: | 796 | default: |
| 655 | goto out; | 797 | goto out; |
| 656 | } | 798 | } |
| 657 | variant++; | ||
| 658 | 799 | ||
| 659 | fd = open(name, O_RDONLY); | 800 | fd = open(name, O_RDONLY); |
| 660 | } while (fd < 0); | 801 | } while (fd < 0); |
| @@ -668,6 +809,11 @@ more: | |||
| 668 | if (!ret) | 809 | if (!ret) |
| 669 | goto more; | 810 | goto more; |
| 670 | 811 | ||
| 812 | if (ret > 0) { | ||
| 813 | int nr_plt = dso__synthesize_plt_symbols(self, verbose); | ||
| 814 | if (nr_plt > 0) | ||
| 815 | ret += nr_plt; | ||
| 816 | } | ||
| 671 | out: | 817 | out: |
| 672 | free(name); | 818 | free(name); |
| 673 | return ret; | 819 | return ret; |
| @@ -785,6 +931,9 @@ int dso__load_kernel(struct dso *self, const char *vmlinux, | |||
| 785 | if (err <= 0) | 931 | if (err <= 0) |
| 786 | err = dso__load_kallsyms(self, filter, verbose); | 932 | err = dso__load_kallsyms(self, filter, verbose); |
| 787 | 933 | ||
| 934 | if (err > 0) | ||
| 935 | self->origin = DSO__ORIG_KERNEL; | ||
| 936 | |||
| 788 | return err; | 937 | return err; |
| 789 | } | 938 | } |
| 790 | 939 | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 7918cffb23cd..1e003ec2f4b1 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -25,6 +25,8 @@ 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; | ||
| 29 | unsigned char origin; | ||
| 28 | char name[0]; | 30 | char name[0]; |
| 29 | }; | 31 | }; |
| 30 | 32 | ||
| @@ -48,6 +50,7 @@ int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose); | |||
| 48 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose); | 50 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose); |
| 49 | 51 | ||
| 50 | size_t dso__fprintf(struct dso *self, FILE *fp); | 52 | size_t dso__fprintf(struct dso *self, FILE *fp); |
| 53 | char dso__symtab_origin(const struct dso *self); | ||
| 51 | 54 | ||
| 52 | void symbol__init(void); | 55 | void symbol__init(void); |
| 53 | #endif /* _PERF_SYMBOL_ */ | 56 | #endif /* _PERF_SYMBOL_ */ |
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> |
