]>
Commit | Line | Data |
---|---|---|
f048d548 NK |
1 | #include <stdio.h> |
2 | #include <stdlib.h> | |
3 | #include <string.h> | |
4 | ||
5 | #include <linux/kernel.h> | |
6 | ||
86c98cab | 7 | #include "util/dso.h" |
f048d548 NK |
8 | #include "util/util.h" |
9 | #include "util/debug.h" | |
10 | ||
85c116a6 AK |
11 | #include "symbol.h" |
12 | ||
a9710ba0 AK |
13 | bool srcline_full_filename; |
14 | ||
2f48fcd8 RV |
15 | #ifdef HAVE_LIBBFD_SUPPORT |
16 | ||
17 | /* | |
18 | * Implement addr2line using libbfd. | |
19 | */ | |
20 | #define PACKAGE "perf" | |
21 | #include <bfd.h> | |
22 | ||
23 | struct a2l_data { | |
24 | const char *input; | |
ac931f87 | 25 | u64 addr; |
2f48fcd8 RV |
26 | |
27 | bool found; | |
28 | const char *filename; | |
29 | const char *funcname; | |
30 | unsigned line; | |
31 | ||
32 | bfd *abfd; | |
33 | asymbol **syms; | |
34 | }; | |
35 | ||
36 | static int bfd_error(const char *string) | |
37 | { | |
38 | const char *errmsg; | |
39 | ||
40 | errmsg = bfd_errmsg(bfd_get_error()); | |
41 | fflush(stdout); | |
42 | ||
43 | if (string) | |
44 | pr_debug("%s: %s\n", string, errmsg); | |
45 | else | |
46 | pr_debug("%s\n", errmsg); | |
47 | ||
48 | return -1; | |
49 | } | |
50 | ||
51 | static int slurp_symtab(bfd *abfd, struct a2l_data *a2l) | |
52 | { | |
53 | long storage; | |
54 | long symcount; | |
55 | asymbol **syms; | |
56 | bfd_boolean dynamic = FALSE; | |
57 | ||
58 | if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) | |
59 | return bfd_error(bfd_get_filename(abfd)); | |
60 | ||
61 | storage = bfd_get_symtab_upper_bound(abfd); | |
62 | if (storage == 0L) { | |
63 | storage = bfd_get_dynamic_symtab_upper_bound(abfd); | |
64 | dynamic = TRUE; | |
65 | } | |
66 | if (storage < 0L) | |
67 | return bfd_error(bfd_get_filename(abfd)); | |
68 | ||
69 | syms = malloc(storage); | |
70 | if (dynamic) | |
71 | symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); | |
72 | else | |
73 | symcount = bfd_canonicalize_symtab(abfd, syms); | |
74 | ||
75 | if (symcount < 0) { | |
76 | free(syms); | |
77 | return bfd_error(bfd_get_filename(abfd)); | |
78 | } | |
79 | ||
80 | a2l->syms = syms; | |
81 | return 0; | |
82 | } | |
83 | ||
84 | static void find_address_in_section(bfd *abfd, asection *section, void *data) | |
85 | { | |
86 | bfd_vma pc, vma; | |
87 | bfd_size_type size; | |
88 | struct a2l_data *a2l = data; | |
89 | ||
90 | if (a2l->found) | |
91 | return; | |
92 | ||
93 | if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) | |
94 | return; | |
95 | ||
96 | pc = a2l->addr; | |
97 | vma = bfd_get_section_vma(abfd, section); | |
98 | size = bfd_get_section_size(section); | |
99 | ||
100 | if (pc < vma || pc >= vma + size) | |
101 | return; | |
102 | ||
103 | a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma, | |
104 | &a2l->filename, &a2l->funcname, | |
105 | &a2l->line); | |
106 | } | |
107 | ||
108 | static struct a2l_data *addr2line_init(const char *path) | |
109 | { | |
110 | bfd *abfd; | |
111 | struct a2l_data *a2l = NULL; | |
112 | ||
113 | abfd = bfd_openr(path, NULL); | |
114 | if (abfd == NULL) | |
115 | return NULL; | |
116 | ||
117 | if (!bfd_check_format(abfd, bfd_object)) | |
118 | goto out; | |
119 | ||
120 | a2l = zalloc(sizeof(*a2l)); | |
121 | if (a2l == NULL) | |
122 | goto out; | |
123 | ||
124 | a2l->abfd = abfd; | |
125 | a2l->input = strdup(path); | |
126 | if (a2l->input == NULL) | |
127 | goto out; | |
128 | ||
129 | if (slurp_symtab(abfd, a2l)) | |
130 | goto out; | |
131 | ||
132 | return a2l; | |
133 | ||
134 | out: | |
135 | if (a2l) { | |
7d16c634 | 136 | zfree((char **)&a2l->input); |
2f48fcd8 RV |
137 | free(a2l); |
138 | } | |
139 | bfd_close(abfd); | |
140 | return NULL; | |
141 | } | |
142 | ||
143 | static void addr2line_cleanup(struct a2l_data *a2l) | |
144 | { | |
145 | if (a2l->abfd) | |
146 | bfd_close(a2l->abfd); | |
7d16c634 | 147 | zfree((char **)&a2l->input); |
74cf249d | 148 | zfree(&a2l->syms); |
2f48fcd8 RV |
149 | free(a2l); |
150 | } | |
151 | ||
2f84b42b AK |
152 | #define MAX_INLINE_NEST 1024 |
153 | ||
ac931f87 | 154 | static int addr2line(const char *dso_name, u64 addr, |
2f84b42b AK |
155 | char **file, unsigned int *line, struct dso *dso, |
156 | bool unwind_inlines) | |
2f48fcd8 RV |
157 | { |
158 | int ret = 0; | |
454ff00f AH |
159 | struct a2l_data *a2l = dso->a2l; |
160 | ||
161 | if (!a2l) { | |
162 | dso->a2l = addr2line_init(dso_name); | |
163 | a2l = dso->a2l; | |
164 | } | |
2f48fcd8 | 165 | |
2f48fcd8 RV |
166 | if (a2l == NULL) { |
167 | pr_warning("addr2line_init failed for %s\n", dso_name); | |
168 | return 0; | |
169 | } | |
170 | ||
171 | a2l->addr = addr; | |
454ff00f AH |
172 | a2l->found = false; |
173 | ||
2f48fcd8 RV |
174 | bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); |
175 | ||
2f84b42b AK |
176 | if (a2l->found && unwind_inlines) { |
177 | int cnt = 0; | |
178 | ||
179 | while (bfd_find_inliner_info(a2l->abfd, &a2l->filename, | |
180 | &a2l->funcname, &a2l->line) && | |
181 | cnt++ < MAX_INLINE_NEST) | |
182 | ; | |
183 | } | |
184 | ||
2f48fcd8 RV |
185 | if (a2l->found && a2l->filename) { |
186 | *file = strdup(a2l->filename); | |
187 | *line = a2l->line; | |
188 | ||
189 | if (*file) | |
190 | ret = 1; | |
191 | } | |
192 | ||
2f48fcd8 RV |
193 | return ret; |
194 | } | |
195 | ||
454ff00f AH |
196 | void dso__free_a2l(struct dso *dso) |
197 | { | |
198 | struct a2l_data *a2l = dso->a2l; | |
199 | ||
200 | if (!a2l) | |
201 | return; | |
202 | ||
203 | addr2line_cleanup(a2l); | |
204 | ||
205 | dso->a2l = NULL; | |
206 | } | |
207 | ||
2f48fcd8 RV |
208 | #else /* HAVE_LIBBFD_SUPPORT */ |
209 | ||
ac931f87 | 210 | static int addr2line(const char *dso_name, u64 addr, |
454ff00f | 211 | char **file, unsigned int *line_nr, |
2f84b42b AK |
212 | struct dso *dso __maybe_unused, |
213 | bool unwind_inlines __maybe_unused) | |
f048d548 NK |
214 | { |
215 | FILE *fp; | |
216 | char cmd[PATH_MAX]; | |
217 | char *filename = NULL; | |
218 | size_t len; | |
219 | char *sep; | |
220 | int ret = 0; | |
221 | ||
222 | scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64, | |
223 | dso_name, addr); | |
224 | ||
225 | fp = popen(cmd, "r"); | |
226 | if (fp == NULL) { | |
227 | pr_warning("popen failed for %s\n", dso_name); | |
228 | return 0; | |
229 | } | |
230 | ||
231 | if (getline(&filename, &len, fp) < 0 || !len) { | |
232 | pr_warning("addr2line has no output for %s\n", dso_name); | |
233 | goto out; | |
234 | } | |
235 | ||
236 | sep = strchr(filename, '\n'); | |
237 | if (sep) | |
238 | *sep = '\0'; | |
239 | ||
240 | if (!strcmp(filename, "??:0")) { | |
241 | pr_debug("no debugging info in %s\n", dso_name); | |
242 | free(filename); | |
243 | goto out; | |
244 | } | |
245 | ||
246 | sep = strchr(filename, ':'); | |
247 | if (sep) { | |
248 | *sep++ = '\0'; | |
249 | *file = filename; | |
250 | *line_nr = strtoul(sep, NULL, 0); | |
251 | ret = 1; | |
252 | } | |
253 | out: | |
254 | pclose(fp); | |
255 | return ret; | |
256 | } | |
454ff00f AH |
257 | |
258 | void dso__free_a2l(struct dso *dso __maybe_unused) | |
259 | { | |
260 | } | |
261 | ||
2f48fcd8 | 262 | #endif /* HAVE_LIBBFD_SUPPORT */ |
f048d548 | 263 | |
906049c8 AH |
264 | /* |
265 | * Number of addr2line failures (without success) before disabling it for that | |
266 | * dso. | |
267 | */ | |
268 | #define A2L_FAIL_LIMIT 123 | |
269 | ||
2f84b42b AK |
270 | char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, |
271 | bool show_sym, bool unwind_inlines) | |
f048d548 | 272 | { |
a949fffb DA |
273 | char *file = NULL; |
274 | unsigned line = 0; | |
2cc9d0ef | 275 | char *srcline; |
bf4414ae | 276 | const char *dso_name; |
f048d548 | 277 | |
2cc9d0ef | 278 | if (!dso->has_srcline) |
23f0981b | 279 | goto out; |
2cc9d0ef | 280 | |
0058aef6 AH |
281 | if (dso->symsrc_filename) |
282 | dso_name = dso->symsrc_filename; | |
283 | else | |
284 | dso_name = dso->long_name; | |
285 | ||
58d91a00 NK |
286 | if (dso_name[0] == '[') |
287 | goto out; | |
288 | ||
289 | if (!strncmp(dso_name, "/tmp/perf-", 10)) | |
290 | goto out; | |
291 | ||
2f84b42b | 292 | if (!addr2line(dso_name, addr, &file, &line, dso, unwind_inlines)) |
58d91a00 | 293 | goto out; |
f048d548 | 294 | |
a9710ba0 AK |
295 | if (asprintf(&srcline, "%s:%u", |
296 | srcline_full_filename ? file : basename(file), | |
297 | line) < 0) { | |
906049c8 AH |
298 | free(file); |
299 | goto out; | |
300 | } | |
301 | ||
302 | dso->a2l_fails = 0; | |
f048d548 NK |
303 | |
304 | free(file); | |
305 | return srcline; | |
2cc9d0ef NK |
306 | |
307 | out: | |
906049c8 AH |
308 | if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) { |
309 | dso->has_srcline = 0; | |
310 | dso__free_a2l(dso); | |
311 | } | |
85c116a6 | 312 | if (sym) { |
ac931f87 | 313 | if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "", |
85c116a6 AK |
314 | addr - sym->start) < 0) |
315 | return SRCLINE_UNKNOWN; | |
ac931f87 | 316 | } else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso->short_name, addr) < 0) |
23f0981b AK |
317 | return SRCLINE_UNKNOWN; |
318 | return srcline; | |
f048d548 NK |
319 | } |
320 | ||
321 | void free_srcline(char *srcline) | |
322 | { | |
323 | if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0) | |
324 | free(srcline); | |
325 | } | |
2f84b42b AK |
326 | |
327 | char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, | |
328 | bool show_sym) | |
329 | { | |
330 | return __get_srcline(dso, addr, sym, show_sym, false); | |
331 | } |