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 | ||
ac931f87 | 152 | static int addr2line(const char *dso_name, u64 addr, |
454ff00f | 153 | char **file, unsigned int *line, struct dso *dso) |
2f48fcd8 RV |
154 | { |
155 | int ret = 0; | |
454ff00f AH |
156 | struct a2l_data *a2l = dso->a2l; |
157 | ||
158 | if (!a2l) { | |
159 | dso->a2l = addr2line_init(dso_name); | |
160 | a2l = dso->a2l; | |
161 | } | |
2f48fcd8 | 162 | |
2f48fcd8 RV |
163 | if (a2l == NULL) { |
164 | pr_warning("addr2line_init failed for %s\n", dso_name); | |
165 | return 0; | |
166 | } | |
167 | ||
168 | a2l->addr = addr; | |
454ff00f AH |
169 | a2l->found = false; |
170 | ||
2f48fcd8 RV |
171 | bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); |
172 | ||
173 | if (a2l->found && a2l->filename) { | |
174 | *file = strdup(a2l->filename); | |
175 | *line = a2l->line; | |
176 | ||
177 | if (*file) | |
178 | ret = 1; | |
179 | } | |
180 | ||
2f48fcd8 RV |
181 | return ret; |
182 | } | |
183 | ||
454ff00f AH |
184 | void dso__free_a2l(struct dso *dso) |
185 | { | |
186 | struct a2l_data *a2l = dso->a2l; | |
187 | ||
188 | if (!a2l) | |
189 | return; | |
190 | ||
191 | addr2line_cleanup(a2l); | |
192 | ||
193 | dso->a2l = NULL; | |
194 | } | |
195 | ||
2f48fcd8 RV |
196 | #else /* HAVE_LIBBFD_SUPPORT */ |
197 | ||
ac931f87 | 198 | static int addr2line(const char *dso_name, u64 addr, |
454ff00f AH |
199 | char **file, unsigned int *line_nr, |
200 | struct dso *dso __maybe_unused) | |
f048d548 NK |
201 | { |
202 | FILE *fp; | |
203 | char cmd[PATH_MAX]; | |
204 | char *filename = NULL; | |
205 | size_t len; | |
206 | char *sep; | |
207 | int ret = 0; | |
208 | ||
209 | scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64, | |
210 | dso_name, addr); | |
211 | ||
212 | fp = popen(cmd, "r"); | |
213 | if (fp == NULL) { | |
214 | pr_warning("popen failed for %s\n", dso_name); | |
215 | return 0; | |
216 | } | |
217 | ||
218 | if (getline(&filename, &len, fp) < 0 || !len) { | |
219 | pr_warning("addr2line has no output for %s\n", dso_name); | |
220 | goto out; | |
221 | } | |
222 | ||
223 | sep = strchr(filename, '\n'); | |
224 | if (sep) | |
225 | *sep = '\0'; | |
226 | ||
227 | if (!strcmp(filename, "??:0")) { | |
228 | pr_debug("no debugging info in %s\n", dso_name); | |
229 | free(filename); | |
230 | goto out; | |
231 | } | |
232 | ||
233 | sep = strchr(filename, ':'); | |
234 | if (sep) { | |
235 | *sep++ = '\0'; | |
236 | *file = filename; | |
237 | *line_nr = strtoul(sep, NULL, 0); | |
238 | ret = 1; | |
239 | } | |
240 | out: | |
241 | pclose(fp); | |
242 | return ret; | |
243 | } | |
454ff00f AH |
244 | |
245 | void dso__free_a2l(struct dso *dso __maybe_unused) | |
246 | { | |
247 | } | |
248 | ||
2f48fcd8 | 249 | #endif /* HAVE_LIBBFD_SUPPORT */ |
f048d548 | 250 | |
906049c8 AH |
251 | /* |
252 | * Number of addr2line failures (without success) before disabling it for that | |
253 | * dso. | |
254 | */ | |
255 | #define A2L_FAIL_LIMIT 123 | |
256 | ||
ac931f87 | 257 | char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, |
85c116a6 | 258 | bool show_sym) |
f048d548 | 259 | { |
a949fffb DA |
260 | char *file = NULL; |
261 | unsigned line = 0; | |
2cc9d0ef | 262 | char *srcline; |
bf4414ae | 263 | const char *dso_name; |
f048d548 | 264 | |
2cc9d0ef | 265 | if (!dso->has_srcline) |
23f0981b | 266 | goto out; |
2cc9d0ef | 267 | |
0058aef6 AH |
268 | if (dso->symsrc_filename) |
269 | dso_name = dso->symsrc_filename; | |
270 | else | |
271 | dso_name = dso->long_name; | |
272 | ||
58d91a00 NK |
273 | if (dso_name[0] == '[') |
274 | goto out; | |
275 | ||
276 | if (!strncmp(dso_name, "/tmp/perf-", 10)) | |
277 | goto out; | |
278 | ||
454ff00f | 279 | if (!addr2line(dso_name, addr, &file, &line, dso)) |
58d91a00 | 280 | goto out; |
f048d548 | 281 | |
a9710ba0 AK |
282 | if (asprintf(&srcline, "%s:%u", |
283 | srcline_full_filename ? file : basename(file), | |
284 | line) < 0) { | |
906049c8 AH |
285 | free(file); |
286 | goto out; | |
287 | } | |
288 | ||
289 | dso->a2l_fails = 0; | |
f048d548 NK |
290 | |
291 | free(file); | |
292 | return srcline; | |
2cc9d0ef NK |
293 | |
294 | out: | |
906049c8 AH |
295 | if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) { |
296 | dso->has_srcline = 0; | |
297 | dso__free_a2l(dso); | |
298 | } | |
85c116a6 | 299 | if (sym) { |
ac931f87 | 300 | if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "", |
85c116a6 AK |
301 | addr - sym->start) < 0) |
302 | return SRCLINE_UNKNOWN; | |
ac931f87 | 303 | } else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso->short_name, addr) < 0) |
23f0981b AK |
304 | return SRCLINE_UNKNOWN; |
305 | return srcline; | |
f048d548 NK |
306 | } |
307 | ||
308 | void free_srcline(char *srcline) | |
309 | { | |
310 | if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0) | |
311 | free(srcline); | |
312 | } |