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