]> bbs.cooldavid.org Git - net-next-2.6.git/blame - tools/perf/util/symbol.c
perf stat: Handle pipe read failures in perf stat
[net-next-2.6.git] / tools / perf / util / symbol.c
CommitLineData
a2928c42
ACM
1#include "util.h"
2#include "../perf.h"
a0055ae2 3#include "string.h"
a2928c42
ACM
4#include "symbol.h"
5
6#include <libelf.h>
7#include <gelf.h>
8#include <elf.h>
9
0b73da3f
IM
10const char *sym_hist_filter;
11
9cffa8d5 12static struct symbol *symbol__new(u64 start, u64 len,
0b73da3f 13 const char *name, unsigned int priv_size,
9cffa8d5 14 u64 obj_start, int verbose)
a2928c42 15{
0085c954 16 size_t namelen = strlen(name) + 1;
0b73da3f 17 struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen);
a2928c42 18
0b73da3f
IM
19 if (!self)
20 return NULL;
21
22 if (verbose >= 2)
23 printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n",
9cffa8d5 24 (u64)start, (unsigned long)len, name, self->hist, (void *)(unsigned long)obj_start);
0b73da3f
IM
25
26 self->obj_start= obj_start;
27 self->hist = NULL;
28 self->hist_sum = 0;
29
30 if (sym_hist_filter && !strcmp(name, sym_hist_filter))
9cffa8d5 31 self->hist = calloc(sizeof(u64), len);
0b73da3f
IM
32
33 if (priv_size) {
34 memset(self, 0, priv_size);
35 self = ((void *)self) + priv_size;
a2928c42 36 }
0b73da3f
IM
37 self->start = start;
38 self->end = start + len - 1;
39 memcpy(self->name, name, namelen);
a2928c42
ACM
40
41 return self;
42}
43
0085c954 44static void symbol__delete(struct symbol *self, unsigned int priv_size)
a2928c42 45{
0085c954 46 free(((void *)self) - priv_size);
a2928c42
ACM
47}
48
49static size_t symbol__fprintf(struct symbol *self, FILE *fp)
50{
51 return fprintf(fp, " %llx-%llx %s\n",
52 self->start, self->end, self->name);
53}
54
0085c954 55struct dso *dso__new(const char *name, unsigned int sym_priv_size)
a2928c42
ACM
56{
57 struct dso *self = malloc(sizeof(*self) + strlen(name) + 1);
58
59 if (self != NULL) {
60 strcpy(self->name, name);
61 self->syms = RB_ROOT;
0085c954 62 self->sym_priv_size = sym_priv_size;
fc54db51 63 self->find_symbol = dso__find_symbol;
a2928c42
ACM
64 }
65
66 return self;
67}
68
69static void dso__delete_symbols(struct dso *self)
70{
71 struct symbol *pos;
72 struct rb_node *next = rb_first(&self->syms);
73
74 while (next) {
75 pos = rb_entry(next, struct symbol, rb_node);
76 next = rb_next(&pos->rb_node);
c8c96525 77 rb_erase(&pos->rb_node, &self->syms);
0085c954 78 symbol__delete(pos, self->sym_priv_size);
a2928c42
ACM
79 }
80}
81
82void dso__delete(struct dso *self)
83{
84 dso__delete_symbols(self);
85 free(self);
86}
87
88static void dso__insert_symbol(struct dso *self, struct symbol *sym)
89{
90 struct rb_node **p = &self->syms.rb_node;
91 struct rb_node *parent = NULL;
9cffa8d5 92 const u64 ip = sym->start;
a2928c42
ACM
93 struct symbol *s;
94
95 while (*p != NULL) {
96 parent = *p;
97 s = rb_entry(parent, struct symbol, rb_node);
98 if (ip < s->start)
99 p = &(*p)->rb_left;
100 else
101 p = &(*p)->rb_right;
102 }
103 rb_link_node(&sym->rb_node, parent, p);
104 rb_insert_color(&sym->rb_node, &self->syms);
105}
106
9cffa8d5 107struct symbol *dso__find_symbol(struct dso *self, u64 ip)
a2928c42
ACM
108{
109 struct rb_node *n;
110
111 if (self == NULL)
112 return NULL;
113
114 n = self->syms.rb_node;
115
116 while (n) {
117 struct symbol *s = rb_entry(n, struct symbol, rb_node);
118
119 if (ip < s->start)
120 n = n->rb_left;
121 else if (ip > s->end)
122 n = n->rb_right;
123 else
124 return s;
125 }
126
127 return NULL;
128}
129
130size_t dso__fprintf(struct dso *self, FILE *fp)
131{
132 size_t ret = fprintf(fp, "dso: %s\n", self->name);
133
134 struct rb_node *nd;
135 for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) {
136 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
137 ret += symbol__fprintf(pos, fp);
138 }
139
140 return ret;
141}
142
bd74137e 143static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int verbose)
a2928c42
ACM
144{
145 struct rb_node *nd, *prevnd;
146 char *line = NULL;
147 size_t n;
148 FILE *file = fopen("/proc/kallsyms", "r");
149
150 if (file == NULL)
151 goto out_failure;
152
153 while (!feof(file)) {
9cffa8d5 154 u64 start;
a2928c42
ACM
155 struct symbol *sym;
156 int line_len, len;
157 char symbol_type;
158
159 line_len = getline(&line, &n, file);
160 if (line_len < 0)
161 break;
162
163 if (!line)
164 goto out_failure;
165
166 line[--line_len] = '\0'; /* \n */
167
a0055ae2 168 len = hex2u64(line, &start);
a2928c42
ACM
169
170 len++;
171 if (len + 2 >= line_len)
172 continue;
173
174 symbol_type = toupper(line[len]);
175 /*
176 * We're interested only in code ('T'ext)
177 */
178 if (symbol_type != 'T' && symbol_type != 'W')
179 continue;
180 /*
181 * Well fix up the end later, when we have all sorted.
182 */
0085c954 183 sym = symbol__new(start, 0xdead, line + len + 2,
0b73da3f 184 self->sym_priv_size, 0, verbose);
a2928c42
ACM
185
186 if (sym == NULL)
187 goto out_delete_line;
188
69ee69f6
ACM
189 if (filter && filter(self, sym))
190 symbol__delete(sym, self->sym_priv_size);
191 else
192 dso__insert_symbol(self, sym);
a2928c42
ACM
193 }
194
195 /*
196 * Now that we have all sorted out, just set the ->end of all
197 * symbols
198 */
199 prevnd = rb_first(&self->syms);
200
201 if (prevnd == NULL)
202 goto out_delete_line;
203
204 for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
205 struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node),
206 *curr = rb_entry(nd, struct symbol, rb_node);
207
208 prev->end = curr->start - 1;
209 prevnd = nd;
210 }
211
212 free(line);
213 fclose(file);
214
215 return 0;
216
217out_delete_line:
218 free(line);
219out_failure:
220 return -1;
221}
222
80d496be
PE
223static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int verbose)
224{
225 char *line = NULL;
226 size_t n;
227 FILE *file;
228 int nr_syms = 0;
229
230 file = fopen(self->name, "r");
231 if (file == NULL)
232 goto out_failure;
233
234 while (!feof(file)) {
9cffa8d5 235 u64 start, size;
80d496be
PE
236 struct symbol *sym;
237 int line_len, len;
238
239 line_len = getline(&line, &n, file);
240 if (line_len < 0)
241 break;
242
243 if (!line)
244 goto out_failure;
245
246 line[--line_len] = '\0'; /* \n */
247
248 len = hex2u64(line, &start);
249
250 len++;
251 if (len + 2 >= line_len)
252 continue;
253
254 len += hex2u64(line + len, &size);
255
256 len++;
257 if (len + 2 >= line_len)
258 continue;
259
260 sym = symbol__new(start, size, line + len,
261 self->sym_priv_size, start, verbose);
262
263 if (sym == NULL)
264 goto out_delete_line;
265
266 if (filter && filter(self, sym))
267 symbol__delete(sym, self->sym_priv_size);
268 else {
269 dso__insert_symbol(self, sym);
270 nr_syms++;
271 }
272 }
273
274 free(line);
275 fclose(file);
276
277 return nr_syms;
278
279out_delete_line:
280 free(line);
281out_failure:
282 return -1;
283}
284
a2928c42
ACM
285/**
286 * elf_symtab__for_each_symbol - iterate thru all the symbols
287 *
288 * @self: struct elf_symtab instance to iterate
289 * @index: uint32_t index
290 * @sym: GElf_Sym iterator
291 */
292#define elf_symtab__for_each_symbol(syms, nr_syms, index, sym) \
293 for (index = 0, gelf_getsym(syms, index, &sym);\
294 index < nr_syms; \
295 index++, gelf_getsym(syms, index, &sym))
296
297static inline uint8_t elf_sym__type(const GElf_Sym *sym)
298{
299 return GELF_ST_TYPE(sym->st_info);
300}
301
302static inline int elf_sym__is_function(const GElf_Sym *sym)
303{
304 return elf_sym__type(sym) == STT_FUNC &&
305 sym->st_name != 0 &&
306 sym->st_shndx != SHN_UNDEF &&
307 sym->st_size != 0;
308}
309
310static inline const char *elf_sym__name(const GElf_Sym *sym,
311 const Elf_Data *symstrs)
312{
313 return symstrs->d_buf + sym->st_name;
314}
315
316static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
317 GElf_Shdr *shp, const char *name,
318 size_t *index)
319{
320 Elf_Scn *sec = NULL;
321 size_t cnt = 1;
322
323 while ((sec = elf_nextscn(elf, sec)) != NULL) {
324 char *str;
325
326 gelf_getshdr(sec, shp);
327 str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
328 if (!strcmp(name, str)) {
329 if (index)
330 *index = cnt;
331 break;
332 }
333 ++cnt;
334 }
335
336 return sec;
337}
338
8ce998d6
ACM
339#define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \
340 for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \
341 idx < nr_entries; \
342 ++idx, pos = gelf_getrel(reldata, idx, &pos_mem))
343
344#define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \
345 for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \
346 idx < nr_entries; \
347 ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
348
349static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf,
350 GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym,
351 GElf_Shdr *shdr_dynsym,
0b73da3f 352 size_t dynsym_idx, int verbose)
8ce998d6
ACM
353{
354 uint32_t nr_rel_entries, idx;
355 GElf_Sym sym;
9cffa8d5 356 u64 plt_offset;
8ce998d6
ACM
357 GElf_Shdr shdr_plt;
358 struct symbol *f;
359 GElf_Shdr shdr_rel_plt;
360 Elf_Data *reldata, *syms, *symstrs;
361 Elf_Scn *scn_plt_rel, *scn_symstrs;
362 char sympltname[1024];
363 int nr = 0, symidx;
364
365 scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt,
366 ".rela.plt", NULL);
367 if (scn_plt_rel == NULL) {
368 scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt,
369 ".rel.plt", NULL);
370 if (scn_plt_rel == NULL)
371 return 0;
372 }
373
374 if (shdr_rel_plt.sh_link != dynsym_idx)
375 return 0;
376
377 if (elf_section_by_name(elf, ehdr, &shdr_plt, ".plt", NULL) == NULL)
378 return 0;
379
380 /*
381 * Fetch the relocation section to find the indexes to the GOT
382 * and the symbols in the .dynsym they refer to.
383 */
384 reldata = elf_getdata(scn_plt_rel, NULL);
385 if (reldata == NULL)
386 return -1;
387
388 syms = elf_getdata(scn_dynsym, NULL);
389 if (syms == NULL)
390 return -1;
391
392 scn_symstrs = elf_getscn(elf, shdr_dynsym->sh_link);
393 if (scn_symstrs == NULL)
394 return -1;
395
396 symstrs = elf_getdata(scn_symstrs, NULL);
397 if (symstrs == NULL)
398 return -1;
399
400 nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
401 plt_offset = shdr_plt.sh_offset;
402
403 if (shdr_rel_plt.sh_type == SHT_RELA) {
404 GElf_Rela pos_mem, *pos;
405
406 elf_section__for_each_rela(reldata, pos, pos_mem, idx,
407 nr_rel_entries) {
408 symidx = GELF_R_SYM(pos->r_info);
409 plt_offset += shdr_plt.sh_entsize;
410 gelf_getsym(syms, symidx, &sym);
411 snprintf(sympltname, sizeof(sympltname),
412 "%s@plt", elf_sym__name(&sym, symstrs));
413
414 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
0b73da3f 415 sympltname, self->sym_priv_size, 0, verbose);
8ce998d6
ACM
416 if (!f)
417 return -1;
418
419 dso__insert_symbol(self, f);
420 ++nr;
421 }
422 } else if (shdr_rel_plt.sh_type == SHT_REL) {
423 GElf_Rel pos_mem, *pos;
424 elf_section__for_each_rel(reldata, pos, pos_mem, idx,
425 nr_rel_entries) {
426 symidx = GELF_R_SYM(pos->r_info);
427 plt_offset += shdr_plt.sh_entsize;
428 gelf_getsym(syms, symidx, &sym);
429 snprintf(sympltname, sizeof(sympltname),
430 "%s@plt", elf_sym__name(&sym, symstrs));
431
432 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
0b73da3f 433 sympltname, self->sym_priv_size, 0, verbose);
8ce998d6
ACM
434 if (!f)
435 return -1;
436
437 dso__insert_symbol(self, f);
438 ++nr;
439 }
440 } else {
441 /*
442 * TODO: There are still one more shdr_rel_plt.sh_type
443 * I have to investigate, but probably should be ignored.
444 */
445 }
446
447 return nr;
448}
449
69ee69f6 450static int dso__load_sym(struct dso *self, int fd, const char *name,
bd74137e 451 symbol_filter_t filter, int verbose)
a2928c42
ACM
452{
453 Elf_Data *symstrs;
454 uint32_t nr_syms;
455 int err = -1;
456 uint32_t index;
457 GElf_Ehdr ehdr;
458 GElf_Shdr shdr;
459 Elf_Data *syms;
460 GElf_Sym sym;
8ce998d6 461 Elf_Scn *sec, *sec_dynsym;
a2928c42 462 Elf *elf;
8ce998d6 463 size_t dynsym_idx;
a2928c42
ACM
464 int nr = 0;
465
466 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
467 if (elf == NULL) {
bd74137e
IM
468 if (verbose)
469 fprintf(stderr, "%s: cannot read %s ELF file.\n",
470 __func__, name);
a2928c42
ACM
471 goto out_close;
472 }
473
474 if (gelf_getehdr(elf, &ehdr) == NULL) {
bd74137e
IM
475 if (verbose)
476 fprintf(stderr, "%s: cannot get elf header.\n", __func__);
a2928c42
ACM
477 goto out_elf_end;
478 }
479
8ce998d6
ACM
480 /*
481 * We need to check if we have a .dynsym, so that we can handle the
482 * .plt, synthesizing its symbols, that aren't on the symtabs (be it
483 * .dynsym or .symtab)
484 */
485 sec_dynsym = elf_section_by_name(elf, &ehdr, &shdr,
486 ".dynsym", &dynsym_idx);
487 if (sec_dynsym != NULL) {
488 nr = dso__synthesize_plt_symbols(self, elf, &ehdr,
489 sec_dynsym, &shdr,
0b73da3f 490 dynsym_idx, verbose);
8ce998d6
ACM
491 if (nr < 0)
492 goto out_elf_end;
493 }
494
495 /*
496 * But if we have a full .symtab (that is a superset of .dynsym) we
497 * should add the symbols not in the .dynsyn
498 */
a2928c42 499 sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
8ce998d6
ACM
500 if (sec == NULL) {
501 if (sec_dynsym == NULL)
502 goto out_elf_end;
a2928c42 503
8ce998d6
ACM
504 sec = sec_dynsym;
505 gelf_getshdr(sec, &shdr);
506 }
a2928c42
ACM
507
508 syms = elf_getdata(sec, NULL);
509 if (syms == NULL)
510 goto out_elf_end;
511
512 sec = elf_getscn(elf, shdr.sh_link);
513 if (sec == NULL)
514 goto out_elf_end;
515
516 symstrs = elf_getdata(sec, NULL);
517 if (symstrs == NULL)
518 goto out_elf_end;
519
520 nr_syms = shdr.sh_size / shdr.sh_entsize;
521
e9fbc9dc 522 memset(&sym, 0, sizeof(sym));
f5812a7a
ACM
523 self->prelinked = elf_section_by_name(elf, &ehdr, &shdr,
524 ".gnu.prelink_undo",
525 NULL) != NULL;
a2928c42
ACM
526 elf_symtab__for_each_symbol(syms, nr_syms, index, sym) {
527 struct symbol *f;
9cffa8d5 528 u64 obj_start;
a2928c42
ACM
529
530 if (!elf_sym__is_function(&sym))
531 continue;
532
533 sec = elf_getscn(elf, sym.st_shndx);
534 if (!sec)
535 goto out_elf_end;
536
537 gelf_getshdr(sec, &shdr);
0b73da3f
IM
538 obj_start = sym.st_value;
539
f5812a7a
ACM
540 if (self->prelinked) {
541 if (verbose >= 2)
542 printf("adjusting symbol: st_value: %Lx sh_addr: %Lx sh_offset: %Lx\n",
543 (u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset);
520f2c34 544
f5812a7a
ACM
545 sym.st_value -= shdr.sh_addr - shdr.sh_offset;
546 }
a2928c42
ACM
547
548 f = symbol__new(sym.st_value, sym.st_size,
0085c954 549 elf_sym__name(&sym, symstrs),
0b73da3f 550 self->sym_priv_size, obj_start, verbose);
a2928c42
ACM
551 if (!f)
552 goto out_elf_end;
553
69ee69f6
ACM
554 if (filter && filter(self, f))
555 symbol__delete(f, self->sym_priv_size);
556 else {
557 dso__insert_symbol(self, f);
558 nr++;
559 }
a2928c42
ACM
560 }
561
562 err = nr;
563out_elf_end:
564 elf_end(elf);
565out_close:
566 return err;
567}
568
bd74137e 569int dso__load(struct dso *self, symbol_filter_t filter, int verbose)
a2928c42
ACM
570{
571 int size = strlen(self->name) + sizeof("/usr/lib/debug%s.debug");
572 char *name = malloc(size);
573 int variant = 0;
574 int ret = -1;
575 int fd;
576
577 if (!name)
578 return -1;
579
f5812a7a
ACM
580 self->prelinked = 0;
581
80d496be
PE
582 if (strncmp(self->name, "/tmp/perf-", 10) == 0)
583 return dso__load_perf_map(self, filter, verbose);
584
a2928c42
ACM
585more:
586 do {
587 switch (variant) {
588 case 0: /* Fedora */
589 snprintf(name, size, "/usr/lib/debug%s.debug", self->name);
590 break;
591 case 1: /* Ubuntu */
592 snprintf(name, size, "/usr/lib/debug%s", self->name);
593 break;
594 case 2: /* Sane people */
595 snprintf(name, size, "%s", self->name);
596 break;
597
598 default:
599 goto out;
600 }
601 variant++;
602
603 fd = open(name, O_RDONLY);
604 } while (fd < 0);
605
bd74137e 606 ret = dso__load_sym(self, fd, name, filter, verbose);
a2928c42
ACM
607 close(fd);
608
609 /*
610 * Some people seem to have debuginfo files _WITHOUT_ debug info!?!?
611 */
612 if (!ret)
613 goto more;
614
615out:
616 free(name);
617 return ret;
618}
619
69ee69f6 620static int dso__load_vmlinux(struct dso *self, const char *vmlinux,
bd74137e 621 symbol_filter_t filter, int verbose)
a2928c42
ACM
622{
623 int err, fd = open(vmlinux, O_RDONLY);
624
625 if (fd < 0)
626 return -1;
627
bd74137e 628 err = dso__load_sym(self, fd, vmlinux, filter, verbose);
a2928c42
ACM
629 close(fd);
630
631 return err;
632}
633
bd74137e
IM
634int dso__load_kernel(struct dso *self, const char *vmlinux,
635 symbol_filter_t filter, int verbose)
a827c875
ACM
636{
637 int err = -1;
638
639 if (vmlinux)
bd74137e 640 err = dso__load_vmlinux(self, vmlinux, filter, verbose);
a827c875 641
c1f47b45 642 if (err < 0)
bd74137e 643 err = dso__load_kallsyms(self, filter, verbose);
a827c875
ACM
644
645 return err;
646}
647
a2928c42
ACM
648void symbol__init(void)
649{
650 elf_version(EV_CURRENT);
651}