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