diff options
author | Frederic Weisbecker <fweisbec@gmail.com> | 2009-06-30 23:35:14 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-07-01 03:58:26 -0400 |
commit | 4424961ad6621a02c6b4c9093e801002c1bb9f65 (patch) | |
tree | 9851ec33de4b89fee395941af8fbbaab60ab2028 | |
parent | 9198aa77b69647d1d91207f8075763abe7dc0bf4 (diff) |
perf_counter tools: Resolve symbols in callchains
This patch resolves the names, when possible, of each ip
present in the callchains while using the -c option with perf
report.
Example:
5.40% [k] __d_lookup
5.37%
perf_callchain
perf_counter_overflow
intel_pmu_handle_irq
perf_counter_nmi_handler
notifier_call_chain
atomic_notifier_call_chain
notify_die
do_nmi
nmi
do_lookup
__link_path_walk
path_walk
do_path_lookup
user_path_at
sys_faccessat
sys_access
system_call_fastpath
0x7fb609846f77
0.01%
perf_callchain
perf_counter_overflow
intel_pmu_handle_irq
perf_counter_nmi_handler
notifier_call_chain
atomic_notifier_call_chain
notify_die
do_nmi
nmi
do_lookup
__link_path_walk
path_walk
do_path_lookup
user_path_at
sys_faccessat
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Anton Blanchard <anton@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
LKML-Reference: <1246419315-9968-3-git-send-email-fweisbec@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | tools/perf/builtin-report.c | 102 | ||||
-rw-r--r-- | tools/perf/util/callchain.c | 33 | ||||
-rw-r--r-- | tools/perf/util/callchain.h | 5 |
3 files changed, 90 insertions, 50 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3f5d8ea05ff0..197793051fa5 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -794,8 +794,15 @@ callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples) | |||
794 | ret += callchain__fprintf(fp, self->parent, total_samples); | 794 | ret += callchain__fprintf(fp, self->parent, total_samples); |
795 | 795 | ||
796 | 796 | ||
797 | list_for_each_entry(chain, &self->val, list) | 797 | list_for_each_entry(chain, &self->val, list) { |
798 | ret += fprintf(fp, " %p\n", (void *)chain->ip); | 798 | if (chain->ip >= PERF_CONTEXT_MAX) |
799 | continue; | ||
800 | if (chain->sym) | ||
801 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
802 | else | ||
803 | ret += fprintf(fp, " %p\n", | ||
804 | (void *)chain->ip); | ||
805 | } | ||
799 | 806 | ||
800 | return ret; | 807 | return ret; |
801 | } | 808 | } |
@@ -930,6 +937,55 @@ static int call__match(struct symbol *sym) | |||
930 | return 0; | 937 | return 0; |
931 | } | 938 | } |
932 | 939 | ||
940 | static struct symbol ** | ||
941 | resolve_callchain(struct thread *thread, struct map *map, | ||
942 | struct ip_callchain *chain, struct hist_entry *entry) | ||
943 | { | ||
944 | int i; | ||
945 | struct symbol **syms; | ||
946 | u64 context = PERF_CONTEXT_MAX; | ||
947 | |||
948 | if (callchain) { | ||
949 | syms = calloc(chain->nr, sizeof(*syms)); | ||
950 | if (!syms) { | ||
951 | fprintf(stderr, "Can't allocate memory for symbols\n"); | ||
952 | exit(-1); | ||
953 | } | ||
954 | } | ||
955 | |||
956 | for (i = 0; i < chain->nr; i++) { | ||
957 | u64 ip = chain->ips[i]; | ||
958 | struct dso *dso = NULL; | ||
959 | struct symbol *sym; | ||
960 | |||
961 | if (ip >= PERF_CONTEXT_MAX) { | ||
962 | context = ip; | ||
963 | continue; | ||
964 | } | ||
965 | |||
966 | switch (context) { | ||
967 | case PERF_CONTEXT_KERNEL: | ||
968 | dso = kernel_dso; | ||
969 | break; | ||
970 | default: | ||
971 | break; | ||
972 | } | ||
973 | |||
974 | sym = resolve_symbol(thread, NULL, &dso, &ip); | ||
975 | |||
976 | if (sym) { | ||
977 | if (sort__has_parent && call__match(sym) && | ||
978 | !entry->parent) | ||
979 | entry->parent = sym; | ||
980 | if (!callchain) | ||
981 | break; | ||
982 | syms[i] = sym; | ||
983 | } | ||
984 | } | ||
985 | |||
986 | return syms; | ||
987 | } | ||
988 | |||
933 | /* | 989 | /* |
934 | * collect histogram counts | 990 | * collect histogram counts |
935 | */ | 991 | */ |
@@ -942,6 +998,7 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
942 | struct rb_node **p = &hist.rb_node; | 998 | struct rb_node **p = &hist.rb_node; |
943 | struct rb_node *parent = NULL; | 999 | struct rb_node *parent = NULL; |
944 | struct hist_entry *he; | 1000 | struct hist_entry *he; |
1001 | struct symbol **syms = NULL; | ||
945 | struct hist_entry entry = { | 1002 | struct hist_entry entry = { |
946 | .thread = thread, | 1003 | .thread = thread, |
947 | .map = map, | 1004 | .map = map, |
@@ -955,39 +1012,11 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
955 | }; | 1012 | }; |
956 | int cmp; | 1013 | int cmp; |
957 | 1014 | ||
958 | if (sort__has_parent && chain) { | ||
959 | u64 context = PERF_CONTEXT_MAX; | ||
960 | int i; | ||
961 | |||
962 | for (i = 0; i < chain->nr; i++) { | ||
963 | u64 ip = chain->ips[i]; | ||
964 | struct dso *dso = NULL; | ||
965 | struct symbol *sym; | ||
966 | |||
967 | if (ip >= PERF_CONTEXT_MAX) { | ||
968 | context = ip; | ||
969 | continue; | ||
970 | } | ||
971 | |||
972 | switch (context) { | ||
973 | case PERF_CONTEXT_HV: | 1015 | case PERF_CONTEXT_HV: |
974 | dso = hypervisor_dso; | 1016 | dso = hypervisor_dso; |
975 | break; | 1017 | break; |
976 | case PERF_CONTEXT_KERNEL: | 1018 | if ((sort__has_parent || callchain) && chain) |
977 | dso = kernel_dso; | 1019 | syms = resolve_callchain(thread, map, chain, &entry); |
978 | break; | ||
979 | default: | ||
980 | break; | ||
981 | } | ||
982 | |||
983 | sym = resolve_symbol(thread, NULL, &dso, &ip); | ||
984 | |||
985 | if (sym && call__match(sym)) { | ||
986 | entry.parent = sym; | ||
987 | break; | ||
988 | } | ||
989 | } | ||
990 | } | ||
991 | 1020 | ||
992 | while (*p != NULL) { | 1021 | while (*p != NULL) { |
993 | parent = *p; | 1022 | parent = *p; |
@@ -997,8 +1026,10 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
997 | 1026 | ||
998 | if (!cmp) { | 1027 | if (!cmp) { |
999 | he->count += count; | 1028 | he->count += count; |
1000 | if (callchain) | 1029 | if (callchain) { |
1001 | append_chain(&he->callchain, chain); | 1030 | append_chain(&he->callchain, chain, syms); |
1031 | free(syms); | ||
1032 | } | ||
1002 | return 0; | 1033 | return 0; |
1003 | } | 1034 | } |
1004 | 1035 | ||
@@ -1014,7 +1045,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
1014 | *he = entry; | 1045 | *he = entry; |
1015 | if (callchain) { | 1046 | if (callchain) { |
1016 | callchain_init(&he->callchain); | 1047 | callchain_init(&he->callchain); |
1017 | append_chain(&he->callchain, chain); | 1048 | append_chain(&he->callchain, chain, syms); |
1049 | free(syms); | ||
1018 | } | 1050 | } |
1019 | rb_link_node(&he->rb_node, parent, p); | 1051 | rb_link_node(&he->rb_node, parent, p); |
1020 | rb_insert_color(&he->rb_node, &hist); | 1052 | rb_insert_color(&he->rb_node, &hist); |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index bbf7813fefe0..6568cb198ba6 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -67,7 +67,8 @@ static struct callchain_node *create_child(struct callchain_node *parent) | |||
67 | } | 67 | } |
68 | 68 | ||
69 | static void | 69 | static void |
70 | fill_node(struct callchain_node *node, struct ip_callchain *chain, int start) | 70 | fill_node(struct callchain_node *node, struct ip_callchain *chain, int start, |
71 | struct symbol **syms) | ||
71 | { | 72 | { |
72 | int i; | 73 | int i; |
73 | 74 | ||
@@ -80,24 +81,26 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain, int start) | |||
80 | return; | 81 | return; |
81 | } | 82 | } |
82 | call->ip = chain->ips[i]; | 83 | call->ip = chain->ips[i]; |
84 | call->sym = syms[i]; | ||
83 | list_add_tail(&call->list, &node->val); | 85 | list_add_tail(&call->list, &node->val); |
84 | } | 86 | } |
85 | node->val_nr = i - start; | 87 | node->val_nr = i - start; |
86 | } | 88 | } |
87 | 89 | ||
88 | static void add_child(struct callchain_node *parent, struct ip_callchain *chain) | 90 | static void add_child(struct callchain_node *parent, struct ip_callchain *chain, |
91 | struct symbol **syms) | ||
89 | { | 92 | { |
90 | struct callchain_node *new; | 93 | struct callchain_node *new; |
91 | 94 | ||
92 | new = create_child(parent); | 95 | new = create_child(parent); |
93 | fill_node(new, chain, parent->val_nr); | 96 | fill_node(new, chain, parent->val_nr, syms); |
94 | 97 | ||
95 | new->hit = 1; | 98 | new->hit = 1; |
96 | } | 99 | } |
97 | 100 | ||
98 | static void | 101 | static void |
99 | split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | 102 | split_add_child(struct callchain_node *parent, struct ip_callchain *chain, |
100 | struct callchain_list *to_split, int idx) | 103 | struct callchain_list *to_split, int idx, struct symbol **syms) |
101 | { | 104 | { |
102 | struct callchain_node *new; | 105 | struct callchain_node *new; |
103 | 106 | ||
@@ -109,21 +112,22 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
109 | parent->val_nr = idx; | 112 | parent->val_nr = idx; |
110 | 113 | ||
111 | /* create the new one */ | 114 | /* create the new one */ |
112 | add_child(parent, chain); | 115 | add_child(parent, chain, syms); |
113 | } | 116 | } |
114 | 117 | ||
115 | static int | 118 | static int |
116 | __append_chain(struct callchain_node *root, struct ip_callchain *chain, | 119 | __append_chain(struct callchain_node *root, struct ip_callchain *chain, |
117 | int start); | 120 | int start, struct symbol **syms); |
118 | 121 | ||
119 | static int | 122 | static int |
120 | __append_chain_children(struct callchain_node *root, struct ip_callchain *chain) | 123 | __append_chain_children(struct callchain_node *root, struct ip_callchain *chain, |
124 | struct symbol **syms) | ||
121 | { | 125 | { |
122 | struct callchain_node *rnode; | 126 | struct callchain_node *rnode; |
123 | 127 | ||
124 | /* lookup in childrens */ | 128 | /* lookup in childrens */ |
125 | list_for_each_entry(rnode, &root->children, brothers) { | 129 | list_for_each_entry(rnode, &root->children, brothers) { |
126 | int ret = __append_chain(rnode, chain, root->val_nr); | 130 | int ret = __append_chain(rnode, chain, root->val_nr, syms); |
127 | if (!ret) | 131 | if (!ret) |
128 | return 0; | 132 | return 0; |
129 | } | 133 | } |
@@ -132,7 +136,7 @@ __append_chain_children(struct callchain_node *root, struct ip_callchain *chain) | |||
132 | 136 | ||
133 | static int | 137 | static int |
134 | __append_chain(struct callchain_node *root, struct ip_callchain *chain, | 138 | __append_chain(struct callchain_node *root, struct ip_callchain *chain, |
135 | int start) | 139 | int start, struct symbol **syms) |
136 | { | 140 | { |
137 | struct callchain_list *cnode; | 141 | struct callchain_list *cnode; |
138 | int i = start; | 142 | int i = start; |
@@ -154,7 +158,7 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, | |||
154 | 158 | ||
155 | /* we match only a part of the node. Split it and add the new chain */ | 159 | /* we match only a part of the node. Split it and add the new chain */ |
156 | if (i < root->val_nr) { | 160 | if (i < root->val_nr) { |
157 | split_add_child(root, chain, cnode, i); | 161 | split_add_child(root, chain, cnode, i, syms); |
158 | return 0; | 162 | return 0; |
159 | } | 163 | } |
160 | 164 | ||
@@ -164,11 +168,12 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, | |||
164 | return 0; | 168 | return 0; |
165 | } | 169 | } |
166 | 170 | ||
167 | return __append_chain_children(root, chain); | 171 | return __append_chain_children(root, chain, syms); |
168 | } | 172 | } |
169 | 173 | ||
170 | void append_chain(struct callchain_node *root, struct ip_callchain *chain) | 174 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, |
175 | struct symbol **syms) | ||
171 | { | 176 | { |
172 | if (__append_chain_children(root, chain) == -1) | 177 | if (__append_chain_children(root, chain, syms) == -1) |
173 | add_child(root, chain); | 178 | add_child(root, chain, syms); |
174 | } | 179 | } |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index fa1cd2f71fd3..c942daa712e6 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "../perf.h" | 4 | #include "../perf.h" |
5 | #include "list.h" | 5 | #include "list.h" |
6 | #include "rbtree.h" | 6 | #include "rbtree.h" |
7 | #include "symbol.h" | ||
7 | 8 | ||
8 | 9 | ||
9 | struct callchain_node { | 10 | struct callchain_node { |
@@ -18,6 +19,7 @@ struct callchain_node { | |||
18 | 19 | ||
19 | struct callchain_list { | 20 | struct callchain_list { |
20 | unsigned long ip; | 21 | unsigned long ip; |
22 | struct symbol *sym; | ||
21 | struct list_head list; | 23 | struct list_head list; |
22 | }; | 24 | }; |
23 | 25 | ||
@@ -28,6 +30,7 @@ static inline void callchain_init(struct callchain_node *node) | |||
28 | INIT_LIST_HEAD(&node->val); | 30 | INIT_LIST_HEAD(&node->val); |
29 | } | 31 | } |
30 | 32 | ||
31 | void append_chain(struct callchain_node *root, struct ip_callchain *chain); | 33 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, |
34 | struct symbol **syms); | ||
32 | void sort_chain_to_rbtree(struct rb_root *rb_root, struct callchain_node *node); | 35 | void sort_chain_to_rbtree(struct rb_root *rb_root, struct callchain_node *node); |
33 | #endif | 36 | #endif |