aboutsummaryrefslogtreecommitdiffstats
path: root/Documentation/perf_counter/builtin-report.c
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2009-05-28 13:55:04 -0400
committerIngo Molnar <mingo@elte.hu>2009-05-28 17:25:42 -0400
commita2928c42a5d69328c3578b41bd4d72f6658cf7dc (patch)
tree09cf963c18e03482183cdf0b82a23a74b7722f6a /Documentation/perf_counter/builtin-report.c
parentc93f7669098eb97c5376e5396e3dfb734c17df4f (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.c458
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
65struct symbol {
66 struct rb_node rb_node;
67 __u64 start;
68 __u64 end;
69 char name[0];
70};
71
72static 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
85static void symbol__delete(struct symbol *self)
86{
87 free(self);
88}
89
90static 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
96struct dso {
97 struct list_head node;
98 struct rb_root syms;
99 char name[0];
100};
101
102static 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
114static 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
126static void dso__delete(struct dso *self)
127{
128 dso__delete_symbols(self);
129 free(self);
130}
131
132static 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
151static 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
186static inline uint8_t elf_sym__type(const GElf_Sym *sym)
187{
188 return GELF_ST_TYPE(sym->st_info);
189}
190
191static 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
199static 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
205static 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
228static 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;
299out_elf_end:
300 elf_end(elf);
301out_close:
302 return err;
303}
304
305static 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
316more:
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
346out:
347 free(name);
348 return ret;
349}
350
351static 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
364static LIST_HEAD(dsos); 62static LIST_HEAD(dsos);
365static struct dso *kernel_dso; 63static 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
421static 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 */
436static 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
454static 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
530out_delete_line:
531 free(line);
532out_delete_dso:
533 dso__delete(kernel_dso);
534 return -1;
535}
536
537static int load_kernel(void) 119static 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
562fail_load: 139 return err;
563 dso__delete(kernel_dso);
564fail_open:
565 close(fd);
566kallsyms:
567 return load_kallsyms();
568} 140}
569 141
570struct map { 142struct 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
1248int cmd_report(int argc, const char **argv, const char *prefix) 820int 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