diff options
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/Documentation/perf-script.txt | 4 | ||||
-rw-r--r-- | tools/perf/builtin-script.c | 2 | ||||
-rw-r--r-- | tools/perf/ui/hist.c | 2 | ||||
-rw-r--r-- | tools/perf/util/callchain.c | 13 | ||||
-rw-r--r-- | tools/perf/util/evsel_fprintf.c | 33 | ||||
-rw-r--r-- | tools/perf/util/srcline.c | 49 | ||||
-rw-r--r-- | tools/perf/util/unwind-libdw.c | 6 | ||||
-rw-r--r-- | tools/perf/util/unwind-libunwind-local.c | 11 |
8 files changed, 92 insertions, 28 deletions
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index cb0eda3925e6..3517e204a2b3 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -311,6 +311,10 @@ include::itrace.txt[] | |||
311 | Set the maximum number of program blocks to print with brstackasm for | 311 | Set the maximum number of program blocks to print with brstackasm for |
312 | each sample. | 312 | each sample. |
313 | 313 | ||
314 | --inline:: | ||
315 | If a callgraph address belongs to an inlined function, the inline stack | ||
316 | will be printed. Each entry has function name and file/line. | ||
317 | |||
314 | SEE ALSO | 318 | SEE ALSO |
315 | -------- | 319 | -------- |
316 | linkperf:perf-record[1], linkperf:perf-script-perl[1], | 320 | linkperf:perf-record[1], linkperf:perf-script-perl[1], |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index d05aec491cff..4761b0d7fcb5 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -2494,6 +2494,8 @@ int cmd_script(int argc, const char **argv) | |||
2494 | "Enable kernel symbol demangling"), | 2494 | "Enable kernel symbol demangling"), |
2495 | OPT_STRING(0, "time", &script.time_str, "str", | 2495 | OPT_STRING(0, "time", &script.time_str, "str", |
2496 | "Time span of interest (start,stop)"), | 2496 | "Time span of interest (start,stop)"), |
2497 | OPT_BOOLEAN(0, "inline", &symbol_conf.inline_name, | ||
2498 | "Show inline function"), | ||
2497 | OPT_END() | 2499 | OPT_END() |
2498 | }; | 2500 | }; |
2499 | const char * const script_subcommands[] = { "record", "report", NULL }; | 2501 | const char * const script_subcommands[] = { "record", "report", NULL }; |
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 59addd52d9cd..ddb2c6fbdf91 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
@@ -210,6 +210,8 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, | |||
210 | return 0; | 210 | return 0; |
211 | 211 | ||
212 | ret = b->callchain->max_depth - a->callchain->max_depth; | 212 | ret = b->callchain->max_depth - a->callchain->max_depth; |
213 | if (callchain_param.order == ORDER_CALLER) | ||
214 | ret = -ret; | ||
213 | } | 215 | } |
214 | return ret; | 216 | return ret; |
215 | } | 217 | } |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 81fc29ac798f..b4204b43ed58 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -621,14 +621,19 @@ enum match_result { | |||
621 | static enum match_result match_chain_srcline(struct callchain_cursor_node *node, | 621 | static enum match_result match_chain_srcline(struct callchain_cursor_node *node, |
622 | struct callchain_list *cnode) | 622 | struct callchain_list *cnode) |
623 | { | 623 | { |
624 | char *left = get_srcline(cnode->ms.map->dso, | 624 | char *left = NULL; |
625 | char *right = NULL; | ||
626 | enum match_result ret = MATCH_EQ; | ||
627 | int cmp; | ||
628 | |||
629 | if (cnode->ms.map) | ||
630 | left = get_srcline(cnode->ms.map->dso, | ||
625 | map__rip_2objdump(cnode->ms.map, cnode->ip), | 631 | map__rip_2objdump(cnode->ms.map, cnode->ip), |
626 | cnode->ms.sym, true, false); | 632 | cnode->ms.sym, true, false); |
627 | char *right = get_srcline(node->map->dso, | 633 | if (node->map) |
634 | right = get_srcline(node->map->dso, | ||
628 | map__rip_2objdump(node->map, node->ip), | 635 | map__rip_2objdump(node->map, node->ip), |
629 | node->sym, true, false); | 636 | node->sym, true, false); |
630 | enum match_result ret = MATCH_EQ; | ||
631 | int cmp; | ||
632 | 637 | ||
633 | if (left && right) | 638 | if (left && right) |
634 | cmp = strcmp(left, right); | 639 | cmp = strcmp(left, right); |
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index e415aee6a245..583f3a602506 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "map.h" | 7 | #include "map.h" |
8 | #include "strlist.h" | 8 | #include "strlist.h" |
9 | #include "symbol.h" | 9 | #include "symbol.h" |
10 | #include "srcline.h" | ||
10 | 11 | ||
11 | static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...) | 12 | static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...) |
12 | { | 13 | { |
@@ -168,6 +169,38 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, | |||
168 | if (!print_oneline) | 169 | if (!print_oneline) |
169 | printed += fprintf(fp, "\n"); | 170 | printed += fprintf(fp, "\n"); |
170 | 171 | ||
172 | if (symbol_conf.inline_name && node->map) { | ||
173 | struct inline_node *inode; | ||
174 | |||
175 | addr = map__rip_2objdump(node->map, node->ip), | ||
176 | inode = dso__parse_addr_inlines(node->map->dso, addr); | ||
177 | |||
178 | if (inode) { | ||
179 | struct inline_list *ilist; | ||
180 | |||
181 | list_for_each_entry(ilist, &inode->val, list) { | ||
182 | if (print_arrow) | ||
183 | printed += fprintf(fp, " <-"); | ||
184 | |||
185 | /* IP is same, just skip it */ | ||
186 | if (print_ip) | ||
187 | printed += fprintf(fp, "%c%16s", | ||
188 | s, ""); | ||
189 | if (print_sym) | ||
190 | printed += fprintf(fp, " %s", | ||
191 | ilist->funcname); | ||
192 | if (print_srcline) | ||
193 | printed += fprintf(fp, "\n %s:%d", | ||
194 | ilist->filename, | ||
195 | ilist->line_nr); | ||
196 | if (!print_oneline) | ||
197 | printed += fprintf(fp, "\n"); | ||
198 | } | ||
199 | |||
200 | inline_node__delete(inode); | ||
201 | } | ||
202 | } | ||
203 | |||
171 | if (symbol_conf.bt_stop_list && | 204 | if (symbol_conf.bt_stop_list && |
172 | node->sym && | 205 | node->sym && |
173 | strlist__has_entry(symbol_conf.bt_stop_list, | 206 | strlist__has_entry(symbol_conf.bt_stop_list, |
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index df051a52393c..ebc88a74e67b 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c | |||
@@ -56,7 +56,10 @@ static int inline_list__append(char *filename, char *funcname, int line_nr, | |||
56 | } | 56 | } |
57 | } | 57 | } |
58 | 58 | ||
59 | list_add_tail(&ilist->list, &node->val); | 59 | if (callchain_param.order == ORDER_CALLEE) |
60 | list_add_tail(&ilist->list, &node->val); | ||
61 | else | ||
62 | list_add(&ilist->list, &node->val); | ||
60 | 63 | ||
61 | return 0; | 64 | return 0; |
62 | } | 65 | } |
@@ -200,12 +203,14 @@ static void addr2line_cleanup(struct a2l_data *a2l) | |||
200 | 203 | ||
201 | #define MAX_INLINE_NEST 1024 | 204 | #define MAX_INLINE_NEST 1024 |
202 | 205 | ||
203 | static void inline_list__reverse(struct inline_node *node) | 206 | static int inline_list__append_dso_a2l(struct dso *dso, |
207 | struct inline_node *node) | ||
204 | { | 208 | { |
205 | struct inline_list *ilist, *n; | 209 | struct a2l_data *a2l = dso->a2l; |
210 | char *funcname = a2l->funcname ? strdup(a2l->funcname) : NULL; | ||
211 | char *filename = a2l->filename ? strdup(a2l->filename) : NULL; | ||
206 | 212 | ||
207 | list_for_each_entry_safe_reverse(ilist, n, &node->val, list) | 213 | return inline_list__append(filename, funcname, a2l->line, node, dso); |
208 | list_move_tail(&ilist->list, &node->val); | ||
209 | } | 214 | } |
210 | 215 | ||
211 | static int addr2line(const char *dso_name, u64 addr, | 216 | static int addr2line(const char *dso_name, u64 addr, |
@@ -230,36 +235,36 @@ static int addr2line(const char *dso_name, u64 addr, | |||
230 | 235 | ||
231 | bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); | 236 | bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); |
232 | 237 | ||
233 | if (a2l->found && unwind_inlines) { | 238 | if (!a2l->found) |
239 | return 0; | ||
240 | |||
241 | if (unwind_inlines) { | ||
234 | int cnt = 0; | 242 | int cnt = 0; |
235 | 243 | ||
244 | if (node && inline_list__append_dso_a2l(dso, node)) | ||
245 | return 0; | ||
246 | |||
236 | while (bfd_find_inliner_info(a2l->abfd, &a2l->filename, | 247 | while (bfd_find_inliner_info(a2l->abfd, &a2l->filename, |
237 | &a2l->funcname, &a2l->line) && | 248 | &a2l->funcname, &a2l->line) && |
238 | cnt++ < MAX_INLINE_NEST) { | 249 | cnt++ < MAX_INLINE_NEST) { |
239 | 250 | ||
240 | if (node != NULL) { | 251 | if (node != NULL) { |
241 | if (inline_list__append(strdup(a2l->filename), | 252 | if (inline_list__append_dso_a2l(dso, node)) |
242 | strdup(a2l->funcname), | ||
243 | a2l->line, node, | ||
244 | dso) != 0) | ||
245 | return 0; | 253 | return 0; |
254 | // found at least one inline frame | ||
255 | ret = 1; | ||
246 | } | 256 | } |
247 | } | 257 | } |
258 | } | ||
248 | 259 | ||
249 | if ((node != NULL) && | 260 | if (file) { |
250 | (callchain_param.order != ORDER_CALLEE)) { | 261 | *file = a2l->filename ? strdup(a2l->filename) : NULL; |
251 | inline_list__reverse(node); | 262 | ret = *file ? 1 : 0; |
252 | } | ||
253 | } | 263 | } |
254 | 264 | ||
255 | if (a2l->found && a2l->filename) { | 265 | if (line) |
256 | *file = strdup(a2l->filename); | ||
257 | *line = a2l->line; | 266 | *line = a2l->line; |
258 | 267 | ||
259 | if (*file) | ||
260 | ret = 1; | ||
261 | } | ||
262 | |||
263 | return ret; | 268 | return ret; |
264 | } | 269 | } |
265 | 270 | ||
@@ -278,8 +283,6 @@ void dso__free_a2l(struct dso *dso) | |||
278 | static struct inline_node *addr2inlines(const char *dso_name, u64 addr, | 283 | static struct inline_node *addr2inlines(const char *dso_name, u64 addr, |
279 | struct dso *dso) | 284 | struct dso *dso) |
280 | { | 285 | { |
281 | char *file = NULL; | ||
282 | unsigned int line = 0; | ||
283 | struct inline_node *node; | 286 | struct inline_node *node; |
284 | 287 | ||
285 | node = zalloc(sizeof(*node)); | 288 | node = zalloc(sizeof(*node)); |
@@ -291,7 +294,7 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr, | |||
291 | INIT_LIST_HEAD(&node->val); | 294 | INIT_LIST_HEAD(&node->val); |
292 | node->addr = addr; | 295 | node->addr = addr; |
293 | 296 | ||
294 | if (!addr2line(dso_name, addr, &file, &line, dso, TRUE, node)) | 297 | if (!addr2line(dso_name, addr, NULL, NULL, dso, TRUE, node)) |
295 | goto out_free_inline_node; | 298 | goto out_free_inline_node; |
296 | 299 | ||
297 | if (list_empty(&node->val)) | 300 | if (list_empty(&node->val)) |
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index f90e11a555b2..943a06291587 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c | |||
@@ -168,12 +168,16 @@ frame_callback(Dwfl_Frame *state, void *arg) | |||
168 | { | 168 | { |
169 | struct unwind_info *ui = arg; | 169 | struct unwind_info *ui = arg; |
170 | Dwarf_Addr pc; | 170 | Dwarf_Addr pc; |
171 | bool isactivation; | ||
171 | 172 | ||
172 | if (!dwfl_frame_pc(state, &pc, NULL)) { | 173 | if (!dwfl_frame_pc(state, &pc, &isactivation)) { |
173 | pr_err("%s", dwfl_errmsg(-1)); | 174 | pr_err("%s", dwfl_errmsg(-1)); |
174 | return DWARF_CB_ABORT; | 175 | return DWARF_CB_ABORT; |
175 | } | 176 | } |
176 | 177 | ||
178 | if (!isactivation) | ||
179 | --pc; | ||
180 | |||
177 | return entry(pc, ui) || !(--ui->max_stack) ? | 181 | return entry(pc, ui) || !(--ui->max_stack) ? |
178 | DWARF_CB_ABORT : DWARF_CB_OK; | 182 | DWARF_CB_ABORT : DWARF_CB_OK; |
179 | } | 183 | } |
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index f8455bed6e65..672c2ada9357 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c | |||
@@ -692,6 +692,17 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | |||
692 | 692 | ||
693 | while (!ret && (unw_step(&c) > 0) && i < max_stack) { | 693 | while (!ret && (unw_step(&c) > 0) && i < max_stack) { |
694 | unw_get_reg(&c, UNW_REG_IP, &ips[i]); | 694 | unw_get_reg(&c, UNW_REG_IP, &ips[i]); |
695 | |||
696 | /* | ||
697 | * Decrement the IP for any non-activation frames. | ||
698 | * this is required to properly find the srcline | ||
699 | * for caller frames. | ||
700 | * See also the documentation for dwfl_frame_pc(), | ||
701 | * which this code tries to replicate. | ||
702 | */ | ||
703 | if (unw_is_signal_frame(&c) <= 0) | ||
704 | --ips[i]; | ||
705 | |||
695 | ++i; | 706 | ++i; |
696 | } | 707 | } |
697 | 708 | ||