]>
Commit | Line | Data |
---|---|---|
fd20e811 | 1 | #include <inttypes.h> |
f048d548 NK |
2 | #include <stdio.h> |
3 | #include <stdlib.h> | |
4 | #include <string.h> | |
5 | ||
6 | #include <linux/kernel.h> | |
7 | ||
86c98cab | 8 | #include "util/dso.h" |
f048d548 NK |
9 | #include "util/util.h" |
10 | #include "util/debug.h" | |
a64489c5 | 11 | #include "util/callchain.h" |
632a5cab | 12 | #include "srcline.h" |
f048d548 | 13 | |
85c116a6 AK |
14 | #include "symbol.h" |
15 | ||
a9710ba0 AK |
16 | bool srcline_full_filename; |
17 | ||
5580338d JY |
18 | static const char *dso__name(struct dso *dso) |
19 | { | |
20 | const char *dso_name; | |
21 | ||
22 | if (dso->symsrc_filename) | |
23 | dso_name = dso->symsrc_filename; | |
24 | else | |
25 | dso_name = dso->long_name; | |
26 | ||
27 | if (dso_name[0] == '[') | |
28 | return NULL; | |
29 | ||
30 | if (!strncmp(dso_name, "/tmp/perf-", 10)) | |
31 | return NULL; | |
32 | ||
33 | return dso_name; | |
34 | } | |
35 | ||
a64489c5 JY |
36 | static int inline_list__append(char *filename, char *funcname, int line_nr, |
37 | struct inline_node *node, struct dso *dso) | |
38 | { | |
39 | struct inline_list *ilist; | |
40 | char *demangled; | |
41 | ||
42 | ilist = zalloc(sizeof(*ilist)); | |
43 | if (ilist == NULL) | |
44 | return -1; | |
45 | ||
46 | ilist->filename = filename; | |
47 | ilist->line_nr = line_nr; | |
48 | ||
49 | if (dso != NULL) { | |
50 | demangled = dso__demangle_sym(dso, 0, funcname); | |
51 | if (demangled == NULL) { | |
52 | ilist->funcname = funcname; | |
53 | } else { | |
54 | ilist->funcname = demangled; | |
55 | free(funcname); | |
56 | } | |
57 | } | |
58 | ||
28071f51 MW |
59 | if (callchain_param.order == ORDER_CALLEE) |
60 | list_add_tail(&ilist->list, &node->val); | |
61 | else | |
62 | list_add(&ilist->list, &node->val); | |
a64489c5 JY |
63 | |
64 | return 0; | |
65 | } | |
66 | ||
2f48fcd8 RV |
67 | #ifdef HAVE_LIBBFD_SUPPORT |
68 | ||
69 | /* | |
70 | * Implement addr2line using libbfd. | |
71 | */ | |
72 | #define PACKAGE "perf" | |
73 | #include <bfd.h> | |
74 | ||
75 | struct a2l_data { | |
76 | const char *input; | |
ac931f87 | 77 | u64 addr; |
2f48fcd8 RV |
78 | |
79 | bool found; | |
80 | const char *filename; | |
81 | const char *funcname; | |
82 | unsigned line; | |
83 | ||
84 | bfd *abfd; | |
85 | asymbol **syms; | |
86 | }; | |
87 | ||
88 | static int bfd_error(const char *string) | |
89 | { | |
90 | const char *errmsg; | |
91 | ||
92 | errmsg = bfd_errmsg(bfd_get_error()); | |
93 | fflush(stdout); | |
94 | ||
95 | if (string) | |
96 | pr_debug("%s: %s\n", string, errmsg); | |
97 | else | |
98 | pr_debug("%s\n", errmsg); | |
99 | ||
100 | return -1; | |
101 | } | |
102 | ||
103 | static int slurp_symtab(bfd *abfd, struct a2l_data *a2l) | |
104 | { | |
105 | long storage; | |
106 | long symcount; | |
107 | asymbol **syms; | |
108 | bfd_boolean dynamic = FALSE; | |
109 | ||
110 | if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) | |
111 | return bfd_error(bfd_get_filename(abfd)); | |
112 | ||
113 | storage = bfd_get_symtab_upper_bound(abfd); | |
114 | if (storage == 0L) { | |
115 | storage = bfd_get_dynamic_symtab_upper_bound(abfd); | |
116 | dynamic = TRUE; | |
117 | } | |
118 | if (storage < 0L) | |
119 | return bfd_error(bfd_get_filename(abfd)); | |
120 | ||
121 | syms = malloc(storage); | |
122 | if (dynamic) | |
123 | symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); | |
124 | else | |
125 | symcount = bfd_canonicalize_symtab(abfd, syms); | |
126 | ||
127 | if (symcount < 0) { | |
128 | free(syms); | |
129 | return bfd_error(bfd_get_filename(abfd)); | |
130 | } | |
131 | ||
132 | a2l->syms = syms; | |
133 | return 0; | |
134 | } | |
135 | ||
136 | static void find_address_in_section(bfd *abfd, asection *section, void *data) | |
137 | { | |
138 | bfd_vma pc, vma; | |
139 | bfd_size_type size; | |
140 | struct a2l_data *a2l = data; | |
141 | ||
142 | if (a2l->found) | |
143 | return; | |
144 | ||
145 | if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) | |
146 | return; | |
147 | ||
148 | pc = a2l->addr; | |
149 | vma = bfd_get_section_vma(abfd, section); | |
150 | size = bfd_get_section_size(section); | |
151 | ||
152 | if (pc < vma || pc >= vma + size) | |
153 | return; | |
154 | ||
155 | a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma, | |
156 | &a2l->filename, &a2l->funcname, | |
157 | &a2l->line); | |
158 | } | |
159 | ||
160 | static struct a2l_data *addr2line_init(const char *path) | |
161 | { | |
162 | bfd *abfd; | |
163 | struct a2l_data *a2l = NULL; | |
164 | ||
165 | abfd = bfd_openr(path, NULL); | |
166 | if (abfd == NULL) | |
167 | return NULL; | |
168 | ||
169 | if (!bfd_check_format(abfd, bfd_object)) | |
170 | goto out; | |
171 | ||
172 | a2l = zalloc(sizeof(*a2l)); | |
173 | if (a2l == NULL) | |
174 | goto out; | |
175 | ||
176 | a2l->abfd = abfd; | |
177 | a2l->input = strdup(path); | |
178 | if (a2l->input == NULL) | |
179 | goto out; | |
180 | ||
181 | if (slurp_symtab(abfd, a2l)) | |
182 | goto out; | |
183 | ||
184 | return a2l; | |
185 | ||
186 | out: | |
187 | if (a2l) { | |
7d16c634 | 188 | zfree((char **)&a2l->input); |
2f48fcd8 RV |
189 | free(a2l); |
190 | } | |
191 | bfd_close(abfd); | |
192 | return NULL; | |
193 | } | |
194 | ||
195 | static void addr2line_cleanup(struct a2l_data *a2l) | |
196 | { | |
197 | if (a2l->abfd) | |
198 | bfd_close(a2l->abfd); | |
7d16c634 | 199 | zfree((char **)&a2l->input); |
74cf249d | 200 | zfree(&a2l->syms); |
2f48fcd8 RV |
201 | free(a2l); |
202 | } | |
203 | ||
2f84b42b AK |
204 | #define MAX_INLINE_NEST 1024 |
205 | ||
4d53b9d5 MW |
206 | static int inline_list__append_dso_a2l(struct dso *dso, |
207 | struct inline_node *node) | |
208 | { | |
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; | |
212 | ||
213 | return inline_list__append(filename, funcname, a2l->line, node, dso); | |
214 | } | |
215 | ||
ac931f87 | 216 | static int addr2line(const char *dso_name, u64 addr, |
2f84b42b | 217 | char **file, unsigned int *line, struct dso *dso, |
a64489c5 | 218 | bool unwind_inlines, struct inline_node *node) |
2f48fcd8 RV |
219 | { |
220 | int ret = 0; | |
454ff00f AH |
221 | struct a2l_data *a2l = dso->a2l; |
222 | ||
223 | if (!a2l) { | |
224 | dso->a2l = addr2line_init(dso_name); | |
225 | a2l = dso->a2l; | |
226 | } | |
2f48fcd8 | 227 | |
2f48fcd8 RV |
228 | if (a2l == NULL) { |
229 | pr_warning("addr2line_init failed for %s\n", dso_name); | |
230 | return 0; | |
231 | } | |
232 | ||
233 | a2l->addr = addr; | |
454ff00f AH |
234 | a2l->found = false; |
235 | ||
2f48fcd8 RV |
236 | bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); |
237 | ||
b21cc978 MW |
238 | if (!a2l->found) |
239 | return 0; | |
240 | ||
241 | if (unwind_inlines) { | |
2f84b42b AK |
242 | int cnt = 0; |
243 | ||
4d53b9d5 MW |
244 | if (node && inline_list__append_dso_a2l(dso, node)) |
245 | return 0; | |
246 | ||
2f84b42b AK |
247 | while (bfd_find_inliner_info(a2l->abfd, &a2l->filename, |
248 | &a2l->funcname, &a2l->line) && | |
a64489c5 JY |
249 | cnt++ < MAX_INLINE_NEST) { |
250 | ||
251 | if (node != NULL) { | |
4d53b9d5 | 252 | if (inline_list__append_dso_a2l(dso, node)) |
a64489c5 | 253 | return 0; |
b21cc978 MW |
254 | // found at least one inline frame |
255 | ret = 1; | |
a64489c5 JY |
256 | } |
257 | } | |
2f84b42b AK |
258 | } |
259 | ||
b21cc978 MW |
260 | if (file) { |
261 | *file = a2l->filename ? strdup(a2l->filename) : NULL; | |
262 | ret = *file ? 1 : 0; | |
2f48fcd8 RV |
263 | } |
264 | ||
b21cc978 MW |
265 | if (line) |
266 | *line = a2l->line; | |
267 | ||
2f48fcd8 RV |
268 | return ret; |
269 | } | |
270 | ||
454ff00f AH |
271 | void dso__free_a2l(struct dso *dso) |
272 | { | |
273 | struct a2l_data *a2l = dso->a2l; | |
274 | ||
275 | if (!a2l) | |
276 | return; | |
277 | ||
278 | addr2line_cleanup(a2l); | |
279 | ||
280 | dso->a2l = NULL; | |
281 | } | |
282 | ||
a64489c5 JY |
283 | static struct inline_node *addr2inlines(const char *dso_name, u64 addr, |
284 | struct dso *dso) | |
285 | { | |
a64489c5 JY |
286 | struct inline_node *node; |
287 | ||
288 | node = zalloc(sizeof(*node)); | |
289 | if (node == NULL) { | |
290 | perror("not enough memory for the inline node"); | |
291 | return NULL; | |
292 | } | |
293 | ||
294 | INIT_LIST_HEAD(&node->val); | |
295 | node->addr = addr; | |
296 | ||
b21cc978 | 297 | if (!addr2line(dso_name, addr, NULL, NULL, dso, TRUE, node)) |
a64489c5 JY |
298 | goto out_free_inline_node; |
299 | ||
300 | if (list_empty(&node->val)) | |
301 | goto out_free_inline_node; | |
302 | ||
303 | return node; | |
304 | ||
305 | out_free_inline_node: | |
306 | inline_node__delete(node); | |
307 | return NULL; | |
308 | } | |
309 | ||
2f48fcd8 RV |
310 | #else /* HAVE_LIBBFD_SUPPORT */ |
311 | ||
5580338d JY |
312 | static int filename_split(char *filename, unsigned int *line_nr) |
313 | { | |
314 | char *sep; | |
315 | ||
316 | sep = strchr(filename, '\n'); | |
317 | if (sep) | |
318 | *sep = '\0'; | |
319 | ||
320 | if (!strcmp(filename, "??:0")) | |
321 | return 0; | |
322 | ||
323 | sep = strchr(filename, ':'); | |
324 | if (sep) { | |
325 | *sep++ = '\0'; | |
326 | *line_nr = strtoul(sep, NULL, 0); | |
327 | return 1; | |
328 | } | |
329 | ||
330 | return 0; | |
331 | } | |
332 | ||
ac931f87 | 333 | static int addr2line(const char *dso_name, u64 addr, |
454ff00f | 334 | char **file, unsigned int *line_nr, |
2f84b42b | 335 | struct dso *dso __maybe_unused, |
a64489c5 JY |
336 | bool unwind_inlines __maybe_unused, |
337 | struct inline_node *node __maybe_unused) | |
f048d548 NK |
338 | { |
339 | FILE *fp; | |
340 | char cmd[PATH_MAX]; | |
341 | char *filename = NULL; | |
342 | size_t len; | |
f048d548 NK |
343 | int ret = 0; |
344 | ||
345 | scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64, | |
346 | dso_name, addr); | |
347 | ||
348 | fp = popen(cmd, "r"); | |
349 | if (fp == NULL) { | |
350 | pr_warning("popen failed for %s\n", dso_name); | |
351 | return 0; | |
352 | } | |
353 | ||
354 | if (getline(&filename, &len, fp) < 0 || !len) { | |
355 | pr_warning("addr2line has no output for %s\n", dso_name); | |
356 | goto out; | |
357 | } | |
358 | ||
5580338d JY |
359 | ret = filename_split(filename, line_nr); |
360 | if (ret != 1) { | |
f048d548 NK |
361 | free(filename); |
362 | goto out; | |
363 | } | |
364 | ||
5580338d JY |
365 | *file = filename; |
366 | ||
f048d548 NK |
367 | out: |
368 | pclose(fp); | |
369 | return ret; | |
370 | } | |
454ff00f AH |
371 | |
372 | void dso__free_a2l(struct dso *dso __maybe_unused) | |
373 | { | |
374 | } | |
375 | ||
a64489c5 JY |
376 | static struct inline_node *addr2inlines(const char *dso_name, u64 addr, |
377 | struct dso *dso __maybe_unused) | |
378 | { | |
379 | FILE *fp; | |
380 | char cmd[PATH_MAX]; | |
381 | struct inline_node *node; | |
382 | char *filename = NULL; | |
383 | size_t len; | |
384 | unsigned int line_nr = 0; | |
385 | ||
386 | scnprintf(cmd, sizeof(cmd), "addr2line -e %s -i %016"PRIx64, | |
387 | dso_name, addr); | |
388 | ||
389 | fp = popen(cmd, "r"); | |
390 | if (fp == NULL) { | |
391 | pr_err("popen failed for %s\n", dso_name); | |
392 | return NULL; | |
393 | } | |
394 | ||
395 | node = zalloc(sizeof(*node)); | |
396 | if (node == NULL) { | |
397 | perror("not enough memory for the inline node"); | |
398 | goto out; | |
399 | } | |
400 | ||
401 | INIT_LIST_HEAD(&node->val); | |
402 | node->addr = addr; | |
403 | ||
404 | while (getline(&filename, &len, fp) != -1) { | |
405 | if (filename_split(filename, &line_nr) != 1) { | |
406 | free(filename); | |
407 | goto out; | |
408 | } | |
409 | ||
410 | if (inline_list__append(filename, NULL, line_nr, node, | |
411 | NULL) != 0) | |
412 | goto out; | |
413 | ||
414 | filename = NULL; | |
415 | } | |
416 | ||
417 | out: | |
418 | pclose(fp); | |
419 | ||
420 | if (list_empty(&node->val)) { | |
421 | inline_node__delete(node); | |
422 | return NULL; | |
423 | } | |
424 | ||
425 | return node; | |
426 | } | |
427 | ||
2f48fcd8 | 428 | #endif /* HAVE_LIBBFD_SUPPORT */ |
f048d548 | 429 | |
906049c8 AH |
430 | /* |
431 | * Number of addr2line failures (without success) before disabling it for that | |
432 | * dso. | |
433 | */ | |
434 | #define A2L_FAIL_LIMIT 123 | |
435 | ||
2f84b42b | 436 | char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, |
5dfa210e | 437 | bool show_sym, bool show_addr, bool unwind_inlines) |
f048d548 | 438 | { |
a949fffb DA |
439 | char *file = NULL; |
440 | unsigned line = 0; | |
2cc9d0ef | 441 | char *srcline; |
bf4414ae | 442 | const char *dso_name; |
f048d548 | 443 | |
2cc9d0ef | 444 | if (!dso->has_srcline) |
23f0981b | 445 | goto out; |
2cc9d0ef | 446 | |
5580338d JY |
447 | dso_name = dso__name(dso); |
448 | if (dso_name == NULL) | |
58d91a00 NK |
449 | goto out; |
450 | ||
a64489c5 | 451 | if (!addr2line(dso_name, addr, &file, &line, dso, unwind_inlines, NULL)) |
58d91a00 | 452 | goto out; |
f048d548 | 453 | |
a9710ba0 AK |
454 | if (asprintf(&srcline, "%s:%u", |
455 | srcline_full_filename ? file : basename(file), | |
456 | line) < 0) { | |
906049c8 AH |
457 | free(file); |
458 | goto out; | |
459 | } | |
460 | ||
461 | dso->a2l_fails = 0; | |
f048d548 NK |
462 | |
463 | free(file); | |
464 | return srcline; | |
2cc9d0ef NK |
465 | |
466 | out: | |
906049c8 AH |
467 | if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) { |
468 | dso->has_srcline = 0; | |
469 | dso__free_a2l(dso); | |
470 | } | |
5dfa210e MW |
471 | |
472 | if (!show_addr) | |
473 | return (show_sym && sym) ? | |
474 | strndup(sym->name, sym->namelen) : NULL; | |
475 | ||
85c116a6 | 476 | if (sym) { |
ac931f87 | 477 | if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "", |
85c116a6 AK |
478 | addr - sym->start) < 0) |
479 | return SRCLINE_UNKNOWN; | |
ac931f87 | 480 | } else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso->short_name, addr) < 0) |
23f0981b AK |
481 | return SRCLINE_UNKNOWN; |
482 | return srcline; | |
f048d548 NK |
483 | } |
484 | ||
485 | void free_srcline(char *srcline) | |
486 | { | |
487 | if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0) | |
488 | free(srcline); | |
489 | } | |
2f84b42b AK |
490 | |
491 | char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, | |
5dfa210e | 492 | bool show_sym, bool show_addr) |
2f84b42b | 493 | { |
5dfa210e | 494 | return __get_srcline(dso, addr, sym, show_sym, show_addr, false); |
2f84b42b | 495 | } |
a64489c5 JY |
496 | |
497 | struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr) | |
498 | { | |
499 | const char *dso_name; | |
500 | ||
501 | dso_name = dso__name(dso); | |
502 | if (dso_name == NULL) | |
503 | return NULL; | |
504 | ||
505 | return addr2inlines(dso_name, addr, dso); | |
506 | } | |
507 | ||
508 | void inline_node__delete(struct inline_node *node) | |
509 | { | |
510 | struct inline_list *ilist, *tmp; | |
511 | ||
512 | list_for_each_entry_safe(ilist, tmp, &node->val, list) { | |
513 | list_del_init(&ilist->list); | |
514 | zfree(&ilist->filename); | |
515 | zfree(&ilist->funcname); | |
516 | free(ilist); | |
517 | } | |
518 | ||
519 | free(node); | |
520 | } |