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