diff options
Diffstat (limited to 'tools/perf/util/srcline.c')
-rw-r--r-- | tools/perf/util/srcline.c | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c new file mode 100644 index 000000000000..d11aefbc4b8d --- /dev/null +++ b/tools/perf/util/srcline.c | |||
@@ -0,0 +1,265 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | |||
5 | #include <linux/kernel.h> | ||
6 | |||
7 | #include "util/dso.h" | ||
8 | #include "util/util.h" | ||
9 | #include "util/debug.h" | ||
10 | |||
11 | #ifdef HAVE_LIBBFD_SUPPORT | ||
12 | |||
13 | /* | ||
14 | * Implement addr2line using libbfd. | ||
15 | */ | ||
16 | #define PACKAGE "perf" | ||
17 | #include <bfd.h> | ||
18 | |||
19 | struct a2l_data { | ||
20 | const char *input; | ||
21 | unsigned long addr; | ||
22 | |||
23 | bool found; | ||
24 | const char *filename; | ||
25 | const char *funcname; | ||
26 | unsigned line; | ||
27 | |||
28 | bfd *abfd; | ||
29 | asymbol **syms; | ||
30 | }; | ||
31 | |||
32 | static int bfd_error(const char *string) | ||
33 | { | ||
34 | const char *errmsg; | ||
35 | |||
36 | errmsg = bfd_errmsg(bfd_get_error()); | ||
37 | fflush(stdout); | ||
38 | |||
39 | if (string) | ||
40 | pr_debug("%s: %s\n", string, errmsg); | ||
41 | else | ||
42 | pr_debug("%s\n", errmsg); | ||
43 | |||
44 | return -1; | ||
45 | } | ||
46 | |||
47 | static int slurp_symtab(bfd *abfd, struct a2l_data *a2l) | ||
48 | { | ||
49 | long storage; | ||
50 | long symcount; | ||
51 | asymbol **syms; | ||
52 | bfd_boolean dynamic = FALSE; | ||
53 | |||
54 | if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) | ||
55 | return bfd_error(bfd_get_filename(abfd)); | ||
56 | |||
57 | storage = bfd_get_symtab_upper_bound(abfd); | ||
58 | if (storage == 0L) { | ||
59 | storage = bfd_get_dynamic_symtab_upper_bound(abfd); | ||
60 | dynamic = TRUE; | ||
61 | } | ||
62 | if (storage < 0L) | ||
63 | return bfd_error(bfd_get_filename(abfd)); | ||
64 | |||
65 | syms = malloc(storage); | ||
66 | if (dynamic) | ||
67 | symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); | ||
68 | else | ||
69 | symcount = bfd_canonicalize_symtab(abfd, syms); | ||
70 | |||
71 | if (symcount < 0) { | ||
72 | free(syms); | ||
73 | return bfd_error(bfd_get_filename(abfd)); | ||
74 | } | ||
75 | |||
76 | a2l->syms = syms; | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static void find_address_in_section(bfd *abfd, asection *section, void *data) | ||
81 | { | ||
82 | bfd_vma pc, vma; | ||
83 | bfd_size_type size; | ||
84 | struct a2l_data *a2l = data; | ||
85 | |||
86 | if (a2l->found) | ||
87 | return; | ||
88 | |||
89 | if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) | ||
90 | return; | ||
91 | |||
92 | pc = a2l->addr; | ||
93 | vma = bfd_get_section_vma(abfd, section); | ||
94 | size = bfd_get_section_size(section); | ||
95 | |||
96 | if (pc < vma || pc >= vma + size) | ||
97 | return; | ||
98 | |||
99 | a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma, | ||
100 | &a2l->filename, &a2l->funcname, | ||
101 | &a2l->line); | ||
102 | } | ||
103 | |||
104 | static struct a2l_data *addr2line_init(const char *path) | ||
105 | { | ||
106 | bfd *abfd; | ||
107 | struct a2l_data *a2l = NULL; | ||
108 | |||
109 | abfd = bfd_openr(path, NULL); | ||
110 | if (abfd == NULL) | ||
111 | return NULL; | ||
112 | |||
113 | if (!bfd_check_format(abfd, bfd_object)) | ||
114 | goto out; | ||
115 | |||
116 | a2l = zalloc(sizeof(*a2l)); | ||
117 | if (a2l == NULL) | ||
118 | goto out; | ||
119 | |||
120 | a2l->abfd = abfd; | ||
121 | a2l->input = strdup(path); | ||
122 | if (a2l->input == NULL) | ||
123 | goto out; | ||
124 | |||
125 | if (slurp_symtab(abfd, a2l)) | ||
126 | goto out; | ||
127 | |||
128 | return a2l; | ||
129 | |||
130 | out: | ||
131 | if (a2l) { | ||
132 | free((void *)a2l->input); | ||
133 | free(a2l); | ||
134 | } | ||
135 | bfd_close(abfd); | ||
136 | return NULL; | ||
137 | } | ||
138 | |||
139 | static void addr2line_cleanup(struct a2l_data *a2l) | ||
140 | { | ||
141 | if (a2l->abfd) | ||
142 | bfd_close(a2l->abfd); | ||
143 | free((void *)a2l->input); | ||
144 | free(a2l->syms); | ||
145 | free(a2l); | ||
146 | } | ||
147 | |||
148 | static int addr2line(const char *dso_name, unsigned long addr, | ||
149 | char **file, unsigned int *line) | ||
150 | { | ||
151 | int ret = 0; | ||
152 | struct a2l_data *a2l; | ||
153 | |||
154 | a2l = addr2line_init(dso_name); | ||
155 | if (a2l == NULL) { | ||
156 | pr_warning("addr2line_init failed for %s\n", dso_name); | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | a2l->addr = addr; | ||
161 | bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); | ||
162 | |||
163 | if (a2l->found && a2l->filename) { | ||
164 | *file = strdup(a2l->filename); | ||
165 | *line = a2l->line; | ||
166 | |||
167 | if (*file) | ||
168 | ret = 1; | ||
169 | } | ||
170 | |||
171 | addr2line_cleanup(a2l); | ||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | #else /* HAVE_LIBBFD_SUPPORT */ | ||
176 | |||
177 | static int addr2line(const char *dso_name, unsigned long addr, | ||
178 | char **file, unsigned int *line_nr) | ||
179 | { | ||
180 | FILE *fp; | ||
181 | char cmd[PATH_MAX]; | ||
182 | char *filename = NULL; | ||
183 | size_t len; | ||
184 | char *sep; | ||
185 | int ret = 0; | ||
186 | |||
187 | scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64, | ||
188 | dso_name, addr); | ||
189 | |||
190 | fp = popen(cmd, "r"); | ||
191 | if (fp == NULL) { | ||
192 | pr_warning("popen failed for %s\n", dso_name); | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | if (getline(&filename, &len, fp) < 0 || !len) { | ||
197 | pr_warning("addr2line has no output for %s\n", dso_name); | ||
198 | goto out; | ||
199 | } | ||
200 | |||
201 | sep = strchr(filename, '\n'); | ||
202 | if (sep) | ||
203 | *sep = '\0'; | ||
204 | |||
205 | if (!strcmp(filename, "??:0")) { | ||
206 | pr_debug("no debugging info in %s\n", dso_name); | ||
207 | free(filename); | ||
208 | goto out; | ||
209 | } | ||
210 | |||
211 | sep = strchr(filename, ':'); | ||
212 | if (sep) { | ||
213 | *sep++ = '\0'; | ||
214 | *file = filename; | ||
215 | *line_nr = strtoul(sep, NULL, 0); | ||
216 | ret = 1; | ||
217 | } | ||
218 | out: | ||
219 | pclose(fp); | ||
220 | return ret; | ||
221 | } | ||
222 | #endif /* HAVE_LIBBFD_SUPPORT */ | ||
223 | |||
224 | char *get_srcline(struct dso *dso, unsigned long addr) | ||
225 | { | ||
226 | char *file = NULL; | ||
227 | unsigned line = 0; | ||
228 | char *srcline; | ||
229 | char *dso_name = dso->long_name; | ||
230 | size_t size; | ||
231 | |||
232 | if (!dso->has_srcline) | ||
233 | return SRCLINE_UNKNOWN; | ||
234 | |||
235 | if (dso_name[0] == '[') | ||
236 | goto out; | ||
237 | |||
238 | if (!strncmp(dso_name, "/tmp/perf-", 10)) | ||
239 | goto out; | ||
240 | |||
241 | if (!addr2line(dso_name, addr, &file, &line)) | ||
242 | goto out; | ||
243 | |||
244 | /* just calculate actual length */ | ||
245 | size = snprintf(NULL, 0, "%s:%u", file, line) + 1; | ||
246 | |||
247 | srcline = malloc(size); | ||
248 | if (srcline) | ||
249 | snprintf(srcline, size, "%s:%u", file, line); | ||
250 | else | ||
251 | srcline = SRCLINE_UNKNOWN; | ||
252 | |||
253 | free(file); | ||
254 | return srcline; | ||
255 | |||
256 | out: | ||
257 | dso->has_srcline = 0; | ||
258 | return SRCLINE_UNKNOWN; | ||
259 | } | ||
260 | |||
261 | void free_srcline(char *srcline) | ||
262 | { | ||
263 | if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0) | ||
264 | free(srcline); | ||
265 | } | ||