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/builtin-report.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/builtin-report.c')
-rw-r--r-- | Documentation/perf_counter/builtin-report.c | 458 |
1 files changed, 15 insertions, 443 deletions
diff --git a/Documentation/perf_counter/builtin-report.c b/Documentation/perf_counter/builtin-report.c index 9fdf8224ee61..04fc7ec0c359 100644 --- a/Documentation/perf_counter/builtin-report.c +++ b/Documentation/perf_counter/builtin-report.c | |||
@@ -1,13 +1,10 @@ | |||
1 | #include "util/util.h" | 1 | #include "util/util.h" |
2 | #include "builtin.h" | 2 | #include "builtin.h" |
3 | 3 | ||
4 | #include <libelf.h> | ||
5 | #include <gelf.h> | ||
6 | #include <elf.h> | ||
7 | |||
8 | #include "util/list.h" | 4 | #include "util/list.h" |
9 | #include "util/cache.h" | 5 | #include "util/cache.h" |
10 | #include "util/rbtree.h" | 6 | #include "util/rbtree.h" |
7 | #include "util/symbol.h" | ||
11 | 8 | ||
12 | #include "perf.h" | 9 | #include "perf.h" |
13 | 10 | ||
@@ -62,305 +59,6 @@ typedef union event_union { | |||
62 | struct comm_event comm; | 59 | struct comm_event comm; |
63 | } event_t; | 60 | } event_t; |
64 | 61 | ||
65 | struct symbol { | ||
66 | struct rb_node rb_node; | ||
67 | __u64 start; | ||
68 | __u64 end; | ||
69 | char name[0]; | ||
70 | }; | ||
71 | |||
72 | static struct symbol *symbol__new(uint64_t start, uint64_t len, const char *name) | ||
73 | { | ||
74 | struct symbol *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
75 | |||
76 | if (self != NULL) { | ||
77 | self->start = start; | ||
78 | self->end = start + len; | ||
79 | strcpy(self->name, name); | ||
80 | } | ||
81 | |||
82 | return self; | ||
83 | } | ||
84 | |||
85 | static void symbol__delete(struct symbol *self) | ||
86 | { | ||
87 | free(self); | ||
88 | } | ||
89 | |||
90 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | ||
91 | { | ||
92 | return fprintf(fp, " %llx-%llx %s\n", | ||
93 | self->start, self->end, self->name); | ||
94 | } | ||
95 | |||
96 | struct dso { | ||
97 | struct list_head node; | ||
98 | struct rb_root syms; | ||
99 | char name[0]; | ||
100 | }; | ||
101 | |||
102 | static struct dso *dso__new(const char *name) | ||
103 | { | ||
104 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
105 | |||
106 | if (self != NULL) { | ||
107 | strcpy(self->name, name); | ||
108 | self->syms = RB_ROOT; | ||
109 | } | ||
110 | |||
111 | return self; | ||
112 | } | ||
113 | |||
114 | static void dso__delete_symbols(struct dso *self) | ||
115 | { | ||
116 | struct symbol *pos; | ||
117 | struct rb_node *next = rb_first(&self->syms); | ||
118 | |||
119 | while (next) { | ||
120 | pos = rb_entry(next, struct symbol, rb_node); | ||
121 | next = rb_next(&pos->rb_node); | ||
122 | symbol__delete(pos); | ||
123 | } | ||
124 | } | ||
125 | |||
126 | static void dso__delete(struct dso *self) | ||
127 | { | ||
128 | dso__delete_symbols(self); | ||
129 | free(self); | ||
130 | } | ||
131 | |||
132 | static void dso__insert_symbol(struct dso *self, struct symbol *sym) | ||
133 | { | ||
134 | struct rb_node **p = &self->syms.rb_node; | ||
135 | struct rb_node *parent = NULL; | ||
136 | const uint64_t ip = sym->start; | ||
137 | struct symbol *s; | ||
138 | |||
139 | while (*p != NULL) { | ||
140 | parent = *p; | ||
141 | s = rb_entry(parent, struct symbol, rb_node); | ||
142 | if (ip < s->start) | ||
143 | p = &(*p)->rb_left; | ||
144 | else | ||
145 | p = &(*p)->rb_right; | ||
146 | } | ||
147 | rb_link_node(&sym->rb_node, parent, p); | ||
148 | rb_insert_color(&sym->rb_node, &self->syms); | ||
149 | } | ||
150 | |||
151 | static struct symbol *dso__find_symbol(struct dso *self, uint64_t ip) | ||
152 | { | ||
153 | struct rb_node *n; | ||
154 | |||
155 | if (self == NULL) | ||
156 | return NULL; | ||
157 | |||
158 | n = self->syms.rb_node; | ||
159 | |||
160 | while (n) { | ||
161 | struct symbol *s = rb_entry(n, struct symbol, rb_node); | ||
162 | |||
163 | if (ip < s->start) | ||
164 | n = n->rb_left; | ||
165 | else if (ip > s->end) | ||
166 | n = n->rb_right; | ||
167 | else | ||
168 | return s; | ||
169 | } | ||
170 | |||
171 | return NULL; | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * elf_symtab__for_each_symbol - iterate thru all the symbols | ||
176 | * | ||
177 | * @self: struct elf_symtab instance to iterate | ||
178 | * @index: uint32_t index | ||
179 | * @sym: GElf_Sym iterator | ||
180 | */ | ||
181 | #define elf_symtab__for_each_symbol(syms, nr_syms, index, sym) \ | ||
182 | for (index = 0, gelf_getsym(syms, index, &sym);\ | ||
183 | index < nr_syms; \ | ||
184 | index++, gelf_getsym(syms, index, &sym)) | ||
185 | |||
186 | static inline uint8_t elf_sym__type(const GElf_Sym *sym) | ||
187 | { | ||
188 | return GELF_ST_TYPE(sym->st_info); | ||
189 | } | ||
190 | |||
191 | static inline int elf_sym__is_function(const GElf_Sym *sym) | ||
192 | { | ||
193 | return elf_sym__type(sym) == STT_FUNC && | ||
194 | sym->st_name != 0 && | ||
195 | sym->st_shndx != SHN_UNDEF && | ||
196 | sym->st_size != 0; | ||
197 | } | ||
198 | |||
199 | static inline const char *elf_sym__name(const GElf_Sym *sym, | ||
200 | const Elf_Data *symstrs) | ||
201 | { | ||
202 | return symstrs->d_buf + sym->st_name; | ||
203 | } | ||
204 | |||
205 | static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | ||
206 | GElf_Shdr *shp, const char *name, | ||
207 | size_t *index) | ||
208 | { | ||
209 | Elf_Scn *sec = NULL; | ||
210 | size_t cnt = 1; | ||
211 | |||
212 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
213 | char *str; | ||
214 | |||
215 | gelf_getshdr(sec, shp); | ||
216 | str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); | ||
217 | if (!strcmp(name, str)) { | ||
218 | if (index) | ||
219 | *index = cnt; | ||
220 | break; | ||
221 | } | ||
222 | ++cnt; | ||
223 | } | ||
224 | |||
225 | return sec; | ||
226 | } | ||
227 | |||
228 | static int dso__load_sym(struct dso *self, int fd, char *name) | ||
229 | { | ||
230 | Elf_Data *symstrs; | ||
231 | uint32_t nr_syms; | ||
232 | int err = -1; | ||
233 | uint32_t index; | ||
234 | GElf_Ehdr ehdr; | ||
235 | GElf_Shdr shdr; | ||
236 | Elf_Data *syms; | ||
237 | GElf_Sym sym; | ||
238 | Elf_Scn *sec; | ||
239 | Elf *elf; | ||
240 | int nr = 0; | ||
241 | |||
242 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | ||
243 | if (elf == NULL) { | ||
244 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
245 | __func__, name); | ||
246 | goto out_close; | ||
247 | } | ||
248 | |||
249 | if (gelf_getehdr(elf, &ehdr) == NULL) { | ||
250 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
251 | goto out_elf_end; | ||
252 | } | ||
253 | |||
254 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | ||
255 | if (sec == NULL) | ||
256 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); | ||
257 | |||
258 | if (sec == NULL) | ||
259 | goto out_elf_end; | ||
260 | |||
261 | syms = elf_getdata(sec, NULL); | ||
262 | if (syms == NULL) | ||
263 | goto out_elf_end; | ||
264 | |||
265 | sec = elf_getscn(elf, shdr.sh_link); | ||
266 | if (sec == NULL) | ||
267 | goto out_elf_end; | ||
268 | |||
269 | symstrs = elf_getdata(sec, NULL); | ||
270 | if (symstrs == NULL) | ||
271 | goto out_elf_end; | ||
272 | |||
273 | nr_syms = shdr.sh_size / shdr.sh_entsize; | ||
274 | |||
275 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { | ||
276 | struct symbol *f; | ||
277 | |||
278 | if (!elf_sym__is_function(&sym)) | ||
279 | continue; | ||
280 | |||
281 | sec = elf_getscn(elf, sym.st_shndx); | ||
282 | if (!sec) | ||
283 | goto out_elf_end; | ||
284 | |||
285 | gelf_getshdr(sec, &shdr); | ||
286 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | ||
287 | |||
288 | f = symbol__new(sym.st_value, sym.st_size, | ||
289 | elf_sym__name(&sym, symstrs)); | ||
290 | if (!f) | ||
291 | goto out_elf_end; | ||
292 | |||
293 | dso__insert_symbol(self, f); | ||
294 | |||
295 | nr++; | ||
296 | } | ||
297 | |||
298 | err = nr; | ||
299 | out_elf_end: | ||
300 | elf_end(elf); | ||
301 | out_close: | ||
302 | return err; | ||
303 | } | ||
304 | |||
305 | static int dso__load(struct dso *self) | ||
306 | { | ||
307 | int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug"); | ||
308 | char *name = malloc(size); | ||
309 | int variant = 0; | ||
310 | int ret = -1; | ||
311 | int fd; | ||
312 | |||
313 | if (!name) | ||
314 | return -1; | ||
315 | |||
316 | more: | ||
317 | do { | ||
318 | switch (variant) { | ||
319 | case 0: /* Fedora */ | ||
320 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); | ||
321 | break; | ||
322 | case 1: /* Ubuntu */ | ||
323 | snprintf(name, size, "/usr/lib/debug%s", self->name); | ||
324 | break; | ||
325 | case 2: /* Sane people */ | ||
326 | snprintf(name, size, "%s", self->name); | ||
327 | break; | ||
328 | |||
329 | default: | ||
330 | goto out; | ||
331 | } | ||
332 | variant++; | ||
333 | |||
334 | fd = open(name, O_RDONLY); | ||
335 | } while (fd < 0); | ||
336 | |||
337 | ret = dso__load_sym(self, fd, name); | ||
338 | close(fd); | ||
339 | |||
340 | /* | ||
341 | * Some people seem to have debuginfo files _WITHOUT_ debug info!?!? | ||
342 | */ | ||
343 | if (!ret) | ||
344 | goto more; | ||
345 | |||
346 | out: | ||
347 | free(name); | ||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | static size_t dso__fprintf(struct dso *self, FILE *fp) | ||
352 | { | ||
353 | size_t ret = fprintf(fp, "dso: %s\n", self->name); | ||
354 | |||
355 | struct rb_node *nd; | ||
356 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { | ||
357 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
358 | ret += symbol__fprintf(pos, fp); | ||
359 | } | ||
360 | |||
361 | return ret; | ||
362 | } | ||
363 | |||
364 | static LIST_HEAD(dsos); | 62 | static LIST_HEAD(dsos); |
365 | static struct dso *kernel_dso; | 63 | static struct dso *kernel_dso; |
366 | 64 | ||
@@ -418,153 +116,27 @@ static void dsos__fprintf(FILE *fp) | |||
418 | dso__fprintf(pos, fp); | 116 | dso__fprintf(pos, fp); |
419 | } | 117 | } |
420 | 118 | ||
421 | static int hex(char ch) | ||
422 | { | ||
423 | if ((ch >= '0') && (ch <= '9')) | ||
424 | return ch - '0'; | ||
425 | if ((ch >= 'a') && (ch <= 'f')) | ||
426 | return ch - 'a' + 10; | ||
427 | if ((ch >= 'A') && (ch <= 'F')) | ||
428 | return ch - 'A' + 10; | ||
429 | return -1; | ||
430 | } | ||
431 | |||
432 | /* | ||
433 | * While we find nice hex chars, build a long_val. | ||
434 | * Return number of chars processed. | ||
435 | */ | ||
436 | static int hex2long(char *ptr, unsigned long *long_val) | ||
437 | { | ||
438 | const char *p = ptr; | ||
439 | *long_val = 0; | ||
440 | |||
441 | while (*p) { | ||
442 | const int hex_val = hex(*p); | ||
443 | |||
444 | if (hex_val < 0) | ||
445 | break; | ||
446 | |||
447 | *long_val = (*long_val << 4) | hex_val; | ||
448 | p++; | ||
449 | } | ||
450 | |||
451 | return p - ptr; | ||
452 | } | ||
453 | |||
454 | static int load_kallsyms(void) | ||
455 | { | ||
456 | struct rb_node *nd, *prevnd; | ||
457 | char *line = NULL; | ||
458 | FILE *file; | ||
459 | size_t n; | ||
460 | |||
461 | kernel_dso = dso__new("[kernel]"); | ||
462 | if (kernel_dso == NULL) | ||
463 | return -1; | ||
464 | |||
465 | file = fopen("/proc/kallsyms", "r"); | ||
466 | if (file == NULL) | ||
467 | goto out_delete_dso; | ||
468 | |||
469 | while (!feof(file)) { | ||
470 | unsigned long start; | ||
471 | struct symbol *sym; | ||
472 | int line_len, len; | ||
473 | char symbol_type; | ||
474 | |||
475 | line_len = getline(&line, &n, file); | ||
476 | if (line_len < 0) | ||
477 | break; | ||
478 | |||
479 | if (!line) | ||
480 | goto out_delete_dso; | ||
481 | |||
482 | line[--line_len] = '\0'; /* \n */ | ||
483 | |||
484 | len = hex2long(line, &start); | ||
485 | |||
486 | len++; | ||
487 | if (len + 2 >= line_len) | ||
488 | continue; | ||
489 | |||
490 | symbol_type = toupper(line[len]); | ||
491 | /* | ||
492 | * We're interested only in code ('T'ext) | ||
493 | */ | ||
494 | if (symbol_type != 'T' && symbol_type != 'W') | ||
495 | continue; | ||
496 | /* | ||
497 | * Well fix up the end later, when we have all sorted. | ||
498 | */ | ||
499 | sym = symbol__new(start, 0xdead, line + len + 2); | ||
500 | |||
501 | if (sym == NULL) | ||
502 | goto out_delete_dso; | ||
503 | |||
504 | dso__insert_symbol(kernel_dso, sym); | ||
505 | } | ||
506 | |||
507 | /* | ||
508 | * Now that we have all sorted out, just set the ->end of all | ||
509 | * symbols | ||
510 | */ | ||
511 | prevnd = rb_first(&kernel_dso->syms); | ||
512 | |||
513 | if (prevnd == NULL) | ||
514 | goto out_delete_line; | ||
515 | |||
516 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | ||
517 | struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node), | ||
518 | *curr = rb_entry(nd, struct symbol, rb_node); | ||
519 | |||
520 | prev->end = curr->start - 1; | ||
521 | prevnd = nd; | ||
522 | } | ||
523 | |||
524 | dsos__add(kernel_dso); | ||
525 | free(line); | ||
526 | fclose(file); | ||
527 | |||
528 | return 0; | ||
529 | |||
530 | out_delete_line: | ||
531 | free(line); | ||
532 | out_delete_dso: | ||
533 | dso__delete(kernel_dso); | ||
534 | return -1; | ||
535 | } | ||
536 | |||
537 | static int load_kernel(void) | 119 | static int load_kernel(void) |
538 | { | 120 | { |
539 | int fd, nr; | 121 | int err = -1; |
540 | |||
541 | if (!vmlinux) | ||
542 | goto kallsyms; | ||
543 | |||
544 | fd = open(vmlinux, O_RDONLY); | ||
545 | if (fd < 0) | ||
546 | goto kallsyms; | ||
547 | 122 | ||
548 | kernel_dso = dso__new("[kernel]"); | 123 | kernel_dso = dso__new("[kernel]"); |
549 | if (!kernel_dso) | 124 | if (!kernel_dso) |
550 | goto fail_open; | 125 | return -1; |
551 | |||
552 | nr = dso__load_sym(kernel_dso, fd, vmlinux); | ||
553 | 126 | ||
554 | if (nr <= 0) | 127 | if (vmlinux) |
555 | goto fail_load; | 128 | err = dso__load_vmlinux(kernel_dso, vmlinux); |
556 | 129 | ||
557 | dsos__add(kernel_dso); | 130 | if (err) |
558 | close(fd); | 131 | err = dso__load_kallsyms(kernel_dso); |
559 | 132 | ||
560 | return 0; | 133 | if (err) { |
134 | dso__delete(kernel_dso); | ||
135 | kernel_dso = NULL; | ||
136 | } else | ||
137 | dsos__add(kernel_dso); | ||
561 | 138 | ||
562 | fail_load: | 139 | return err; |
563 | dso__delete(kernel_dso); | ||
564 | fail_open: | ||
565 | close(fd); | ||
566 | kallsyms: | ||
567 | return load_kallsyms(); | ||
568 | } | 140 | } |
569 | 141 | ||
570 | struct map { | 142 | struct map { |
@@ -1050,7 +622,7 @@ static int __cmd_report(void) | |||
1050 | } | 622 | } |
1051 | 623 | ||
1052 | if (load_kernel() < 0) { | 624 | if (load_kernel() < 0) { |
1053 | perror("failed to open kallsyms"); | 625 | perror("failed to load kernel symbols"); |
1054 | return EXIT_FAILURE; | 626 | return EXIT_FAILURE; |
1055 | } | 627 | } |
1056 | 628 | ||
@@ -1247,7 +819,7 @@ static const struct option options[] = { | |||
1247 | 819 | ||
1248 | int cmd_report(int argc, const char **argv, const char *prefix) | 820 | int cmd_report(int argc, const char **argv, const char *prefix) |
1249 | { | 821 | { |
1250 | elf_version(EV_CURRENT); | 822 | symbol__init(); |
1251 | 823 | ||
1252 | page_size = getpagesize(); | 824 | page_size = getpagesize(); |
1253 | 825 | ||