diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2009-05-28 13:55:04 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-05-28 17:25:42 -0400 |
commit | a2928c42a5d69328c3578b41bd4d72f6658cf7dc (patch) | |
tree | 09cf963c18e03482183cdf0b82a23a74b7722f6a /Documentation/perf_counter/util/symbol.c | |
parent | c93f7669098eb97c5376e5396e3dfb734c17df4f (diff) |
perf_counter tools: Move symbol resolution classes from report to libperf
Will be used by perf top as well.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <20090528175504.GC4747@ghostprotocols.net>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'Documentation/perf_counter/util/symbol.c')
-rw-r--r-- | Documentation/perf_counter/util/symbol.c | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/Documentation/perf_counter/util/symbol.c b/Documentation/perf_counter/util/symbol.c new file mode 100644 index 000000000000..d06de2cfcdc6 --- /dev/null +++ b/Documentation/perf_counter/util/symbol.c | |||
@@ -0,0 +1,421 @@ | |||
1 | #include "util.h" | ||
2 | #include "../perf.h" | ||
3 | #include "symbol.h" | ||
4 | |||
5 | #include <libelf.h> | ||
6 | #include <gelf.h> | ||
7 | #include <elf.h> | ||
8 | |||
9 | static struct symbol *symbol__new(uint64_t start, uint64_t len, | ||
10 | const char *name) | ||
11 | { | ||
12 | struct symbol *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
13 | |||
14 | if (self != NULL) { | ||
15 | self->start = start; | ||
16 | self->end = start + len; | ||
17 | strcpy(self->name, name); | ||
18 | } | ||
19 | |||
20 | return self; | ||
21 | } | ||
22 | |||
23 | static void symbol__delete(struct symbol *self) | ||
24 | { | ||
25 | free(self); | ||
26 | } | ||
27 | |||
28 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | ||
29 | { | ||
30 | return fprintf(fp, " %llx-%llx %s\n", | ||
31 | self->start, self->end, self->name); | ||
32 | } | ||
33 | |||
34 | struct dso *dso__new(const char *name) | ||
35 | { | ||
36 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
37 | |||
38 | if (self != NULL) { | ||
39 | strcpy(self->name, name); | ||
40 | self->syms = RB_ROOT; | ||
41 | } | ||
42 | |||
43 | return self; | ||
44 | } | ||
45 | |||
46 | static void dso__delete_symbols(struct dso *self) | ||
47 | { | ||
48 | struct symbol *pos; | ||
49 | struct rb_node *next = rb_first(&self->syms); | ||
50 | |||
51 | while (next) { | ||
52 | pos = rb_entry(next, struct symbol, rb_node); | ||
53 | next = rb_next(&pos->rb_node); | ||
54 | symbol__delete(pos); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | void dso__delete(struct dso *self) | ||
59 | { | ||
60 | dso__delete_symbols(self); | ||
61 | free(self); | ||
62 | } | ||
63 | |||
64 | static void dso__insert_symbol(struct dso *self, struct symbol *sym) | ||
65 | { | ||
66 | struct rb_node **p = &self->syms.rb_node; | ||
67 | struct rb_node *parent = NULL; | ||
68 | const uint64_t ip = sym->start; | ||
69 | struct symbol *s; | ||
70 | |||
71 | while (*p != NULL) { | ||
72 | parent = *p; | ||
73 | s = rb_entry(parent, struct symbol, rb_node); | ||
74 | if (ip < s->start) | ||
75 | p = &(*p)->rb_left; | ||
76 | else | ||
77 | p = &(*p)->rb_right; | ||
78 | } | ||
79 | rb_link_node(&sym->rb_node, parent, p); | ||
80 | rb_insert_color(&sym->rb_node, &self->syms); | ||
81 | } | ||
82 | |||
83 | struct symbol *dso__find_symbol(struct dso *self, uint64_t ip) | ||
84 | { | ||
85 | struct rb_node *n; | ||
86 | |||
87 | if (self == NULL) | ||
88 | return NULL; | ||
89 | |||
90 | n = self->syms.rb_node; | ||
91 | |||
92 | while (n) { | ||
93 | struct symbol *s = rb_entry(n, struct symbol, rb_node); | ||
94 | |||
95 | if (ip < s->start) | ||
96 | n = n->rb_left; | ||
97 | else if (ip > s->end) | ||
98 | n = n->rb_right; | ||
99 | else | ||
100 | return s; | ||
101 | } | ||
102 | |||
103 | return NULL; | ||
104 | } | ||
105 | |||
106 | size_t dso__fprintf(struct dso *self, FILE *fp) | ||
107 | { | ||
108 | size_t ret = fprintf(fp, "dso: %s\n", self->name); | ||
109 | |||
110 | struct rb_node *nd; | ||
111 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { | ||
112 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
113 | ret += symbol__fprintf(pos, fp); | ||
114 | } | ||
115 | |||
116 | return ret; | ||
117 | } | ||
118 | |||
119 | static int hex(char ch) | ||
120 | { | ||
121 | if ((ch >= '0') && (ch <= '9')) | ||
122 | return ch - '0'; | ||
123 | if ((ch >= 'a') && (ch <= 'f')) | ||
124 | return ch - 'a' + 10; | ||
125 | if ((ch >= 'A') && (ch <= 'F')) | ||
126 | return ch - 'A' + 10; | ||
127 | return -1; | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * While we find nice hex chars, build a long_val. | ||
132 | * Return number of chars processed. | ||
133 | */ | ||
134 | static int hex2long(char *ptr, unsigned long *long_val) | ||
135 | { | ||
136 | const char *p = ptr; | ||
137 | *long_val = 0; | ||
138 | |||
139 | while (*p) { | ||
140 | const int hex_val = hex(*p); | ||
141 | |||
142 | if (hex_val < 0) | ||
143 | break; | ||
144 | |||
145 | *long_val = (*long_val << 4) | hex_val; | ||
146 | p++; | ||
147 | } | ||
148 | |||
149 | return p - ptr; | ||
150 | } | ||
151 | |||
152 | int dso__load_kallsyms(struct dso *self) | ||
153 | { | ||
154 | struct rb_node *nd, *prevnd; | ||
155 | char *line = NULL; | ||
156 | size_t n; | ||
157 | FILE *file = fopen("/proc/kallsyms", "r"); | ||
158 | |||
159 | if (file == NULL) | ||
160 | goto out_failure; | ||
161 | |||
162 | while (!feof(file)) { | ||
163 | unsigned long start; | ||
164 | struct symbol *sym; | ||
165 | int line_len, len; | ||
166 | char symbol_type; | ||
167 | |||
168 | line_len = getline(&line, &n, file); | ||
169 | if (line_len < 0) | ||
170 | break; | ||
171 | |||
172 | if (!line) | ||
173 | goto out_failure; | ||
174 | |||
175 | line[--line_len] = '\0'; /* \n */ | ||
176 | |||
177 | len = hex2long(line, &start); | ||
178 | |||
179 | len++; | ||
180 | if (len + 2 >= line_len) | ||
181 | continue; | ||
182 | |||
183 | symbol_type = toupper(line[len]); | ||
184 | /* | ||
185 | * We're interested only in code ('T'ext) | ||
186 | */ | ||
187 | if (symbol_type != 'T' && symbol_type != 'W') | ||
188 | continue; | ||
189 | /* | ||
190 | * Well fix up the end later, when we have all sorted. | ||
191 | */ | ||
192 | sym = symbol__new(start, 0xdead, line + len + 2); | ||
193 | |||
194 | if (sym == NULL) | ||
195 | goto out_delete_line; | ||
196 | |||
197 | dso__insert_symbol(self, sym); | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * Now that we have all sorted out, just set the ->end of all | ||
202 | * symbols | ||
203 | */ | ||
204 | prevnd = rb_first(&self->syms); | ||
205 | |||
206 | if (prevnd == NULL) | ||
207 | goto out_delete_line; | ||
208 | |||
209 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | ||
210 | struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node), | ||
211 | *curr = rb_entry(nd, struct symbol, rb_node); | ||
212 | |||
213 | prev->end = curr->start - 1; | ||
214 | prevnd = nd; | ||
215 | } | ||
216 | |||
217 | free(line); | ||
218 | fclose(file); | ||
219 | |||
220 | return 0; | ||
221 | |||
222 | out_delete_line: | ||
223 | free(line); | ||
224 | out_failure: | ||
225 | return -1; | ||
226 | } | ||
227 | |||
228 | /** | ||
229 | * elf_symtab__for_each_symbol - iterate thru all the symbols | ||
230 | * | ||
231 | * @self: struct elf_symtab instance to iterate | ||
232 | * @index: uint32_t index | ||
233 | * @sym: GElf_Sym iterator | ||
234 | */ | ||
235 | #define elf_symtab__for_each_symbol(syms, nr_syms, index, sym) \ | ||
236 | for (index = 0, gelf_getsym(syms, index, &sym);\ | ||
237 | index < nr_syms; \ | ||
238 | index++, gelf_getsym(syms, index, &sym)) | ||
239 | |||
240 | static inline uint8_t elf_sym__type(const GElf_Sym *sym) | ||
241 | { | ||
242 | return GELF_ST_TYPE(sym->st_info); | ||
243 | } | ||
244 | |||
245 | static inline int elf_sym__is_function(const GElf_Sym *sym) | ||
246 | { | ||
247 | return elf_sym__type(sym) == STT_FUNC && | ||
248 | sym->st_name != 0 && | ||
249 | sym->st_shndx != SHN_UNDEF && | ||
250 | sym->st_size != 0; | ||
251 | } | ||
252 | |||
253 | static inline const char *elf_sym__name(const GElf_Sym *sym, | ||
254 | const Elf_Data *symstrs) | ||
255 | { | ||
256 | return symstrs->d_buf + sym->st_name; | ||
257 | } | ||
258 | |||
259 | static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | ||
260 | GElf_Shdr *shp, const char *name, | ||
261 | size_t *index) | ||
262 | { | ||
263 | Elf_Scn *sec = NULL; | ||
264 | size_t cnt = 1; | ||
265 | |||
266 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
267 | char *str; | ||
268 | |||
269 | gelf_getshdr(sec, shp); | ||
270 | str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); | ||
271 | if (!strcmp(name, str)) { | ||
272 | if (index) | ||
273 | *index = cnt; | ||
274 | break; | ||
275 | } | ||
276 | ++cnt; | ||
277 | } | ||
278 | |||
279 | return sec; | ||
280 | } | ||
281 | |||
282 | static int dso__load_sym(struct dso *self, int fd, const char *name) | ||
283 | { | ||
284 | Elf_Data *symstrs; | ||
285 | uint32_t nr_syms; | ||
286 | int err = -1; | ||
287 | uint32_t index; | ||
288 | GElf_Ehdr ehdr; | ||
289 | GElf_Shdr shdr; | ||
290 | Elf_Data *syms; | ||
291 | GElf_Sym sym; | ||
292 | Elf_Scn *sec; | ||
293 | Elf *elf; | ||
294 | int nr = 0; | ||
295 | |||
296 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | ||
297 | if (elf == NULL) { | ||
298 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
299 | __func__, name); | ||
300 | goto out_close; | ||
301 | } | ||
302 | |||
303 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
304 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
305 | goto out_elf_end; | ||
306 | } | ||
307 | |||
308 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | ||
309 | if (sec == NULL) | ||
310 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); | ||
311 | |||
312 | if (sec == NULL) | ||
313 | goto out_elf_end; | ||
314 | |||
315 | syms = elf_getdata(sec, NULL); | ||
316 | if (syms == NULL) | ||
317 | goto out_elf_end; | ||
318 | |||
319 | sec = elf_getscn(elf, shdr.sh_link); | ||
320 | if (sec == NULL) | ||
321 | goto out_elf_end; | ||
322 | |||
323 | symstrs = elf_getdata(sec, NULL); | ||
324 | if (symstrs == NULL) | ||
325 | goto out_elf_end; | ||
326 | |||
327 | nr_syms = shdr.sh_size / shdr.sh_entsize; | ||
328 | |||
329 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { | ||
330 | struct symbol *f; | ||
331 | |||
332 | if (!elf_sym__is_function(&sym)) | ||
333 | continue; | ||
334 | |||
335 | sec = elf_getscn(elf, sym.st_shndx); | ||
336 | if (!sec) | ||
337 | goto out_elf_end; | ||
338 | |||
339 | gelf_getshdr(sec, &shdr); | ||
340 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | ||
341 | |||
342 | f = symbol__new(sym.st_value, sym.st_size, | ||
343 | elf_sym__name(&sym, symstrs)); | ||
344 | if (!f) | ||
345 | goto out_elf_end; | ||
346 | |||
347 | dso__insert_symbol(self, f); | ||
348 | |||
349 | nr++; | ||
350 | } | ||
351 | |||
352 | err = nr; | ||
353 | out_elf_end: | ||
354 | elf_end(elf); | ||
355 | out_close: | ||
356 | return err; | ||
357 | } | ||
358 | |||
359 | int dso__load(struct dso *self) | ||
360 | { | ||
361 | int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug"); | ||
362 | char *name = malloc(size); | ||
363 | int variant = 0; | ||
364 | int ret = -1; | ||
365 | int fd; | ||
366 | |||
367 | if (!name) | ||
368 | return -1; | ||
369 | |||
370 | more: | ||
371 | do { | ||
372 | switch (variant) { | ||
373 | case 0: /* Fedora */ | ||
374 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); | ||
375 | break; | ||
376 | case 1: /* Ubuntu */ | ||
377 | snprintf(name, size, "/usr/lib/debug%s", self->name); | ||
378 | break; | ||
379 | case 2: /* Sane people */ | ||
380 | snprintf(name, size, "%s", self->name); | ||
381 | break; | ||
382 | |||
383 | default: | ||
384 | goto out; | ||
385 | } | ||
386 | variant++; | ||
387 | |||
388 | fd = open(name, O_RDONLY); | ||
389 | } while (fd < 0); | ||
390 | |||
391 | ret = dso__load_sym(self, fd, name); | ||
392 | close(fd); | ||
393 | |||
394 | /* | ||
395 | * Some people seem to have debuginfo files _WITHOUT_ debug info!?!? | ||
396 | */ | ||
397 | if (!ret) | ||
398 | goto more; | ||
399 | |||
400 | out: | ||
401 | free(name); | ||
402 | return ret; | ||
403 | } | ||
404 | |||
405 | int dso__load_vmlinux(struct dso *self, const char *vmlinux) | ||
406 | { | ||
407 | int err, fd = open(vmlinux, O_RDONLY); | ||
408 | |||
409 | if (fd < 0) | ||
410 | return -1; | ||
411 | |||
412 | err = dso__load_sym(self, fd, vmlinux); | ||
413 | close(fd); | ||
414 | |||
415 | return err; | ||
416 | } | ||
417 | |||
418 | void symbol__init(void) | ||
419 | { | ||
420 | elf_version(EV_CURRENT); | ||
421 | } | ||