blob: 2d65d9c12aad5a49bb544f0c96ab947e574e3a7f [file] [log] [blame]
Ingo Molnar53cb8bc2009-05-26 09:17:18 +02001#include "util/util.h"
2
3#include <libelf.h>
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -03004#include <gelf.h>
5#include <elf.h>
Arnaldo Carvalho de Melo03f63162009-05-26 19:21:55 -03006#include <ctype.h>
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -03007
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -03008#include "util/list.h"
9#include "util/rbtree.h"
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030010
Ingo Molnar53cb8bc2009-05-26 09:17:18 +020011#include "perf.h"
12
13#include "util/parse-options.h"
14#include "util/parse-events.h"
15
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030016#define SHOW_KERNEL 1
17#define SHOW_USER 2
18#define SHOW_HV 4
19
Ingo Molnar53cb8bc2009-05-26 09:17:18 +020020static char const *input_name = "output.perf";
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030021static int input;
22static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
23
Ingo Molnar97b07b62009-05-26 18:48:58 +020024static int dump_trace = 0;
Arnaldo Carvalho de Melo815e7772009-05-26 19:46:14 -030025static int verbose;
Ingo Molnar97b07b62009-05-26 18:48:58 +020026
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030027static unsigned long page_size;
28static unsigned long mmap_window = 32;
29
Ingo Molnar53cb8bc2009-05-26 09:17:18 +020030const char *perf_event_names[] = {
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030031 [PERF_EVENT_MMAP] = " PERF_EVENT_MMAP",
32 [PERF_EVENT_MUNMAP] = " PERF_EVENT_MUNMAP",
33 [PERF_EVENT_COMM] = " PERF_EVENT_COMM",
34};
35
36struct ip_event {
37 struct perf_event_header header;
38 __u64 ip;
39 __u32 pid, tid;
40};
41struct mmap_event {
42 struct perf_event_header header;
43 __u32 pid, tid;
44 __u64 start;
45 __u64 len;
46 __u64 pgoff;
47 char filename[PATH_MAX];
48};
49struct comm_event {
50 struct perf_event_header header;
51 __u32 pid,tid;
52 char comm[16];
53};
54
55typedef union event_union {
56 struct perf_event_header header;
57 struct ip_event ip;
58 struct mmap_event mmap;
59 struct comm_event comm;
60} event_t;
61
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030062struct symbol {
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -030063 struct rb_node rb_node;
64 uint64_t start;
65 uint64_t end;
66 char name[0];
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030067};
68
69static struct symbol *symbol__new(uint64_t start, uint64_t len, const char *name)
70{
71 struct symbol *self = malloc(sizeof(*self) + strlen(name) + 1);
72
73 if (self != NULL) {
74 self->start = start;
75 self->end = start + len;
76 strcpy(self->name, name);
77 }
78
79 return self;
80}
81
82static void symbol__delete(struct symbol *self)
83{
84 free(self);
85}
86
87static size_t symbol__fprintf(struct symbol *self, FILE *fp)
88{
89 return fprintf(fp, " %lx-%lx %s\n",
90 self->start, self->end, self->name);
91}
92
93struct dso {
94 struct list_head node;
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -030095 struct rb_root syms;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030096 char name[0];
97};
98
99static struct dso *dso__new(const char *name)
100{
101 struct dso *self = malloc(sizeof(*self) + strlen(name) + 1);
102
103 if (self != NULL) {
104 strcpy(self->name, name);
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300105 self->syms = RB_ROOT;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300106 }
107
108 return self;
109}
110
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300111static void dso__delete_symbols(struct dso *self)
112{
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300113 struct symbol *pos;
114 struct rb_node *next = rb_first(&self->syms);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300115
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300116 while (next) {
117 pos = rb_entry(next, struct symbol, rb_node);
118 next = rb_next(&pos->rb_node);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300119 symbol__delete(pos);
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300120 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300121}
122
123static void dso__delete(struct dso *self)
124{
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300125 dso__delete_symbols(self);
126 free(self);
127}
128
129static void dso__insert_symbol(struct dso *self, struct symbol *sym)
130{
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300131 struct rb_node **p = &self->syms.rb_node;
132 struct rb_node *parent = NULL;
133 const uint64_t ip = sym->start;
134 struct symbol *s;
135
136 while (*p != NULL) {
137 parent = *p;
138 s = rb_entry(parent, struct symbol, rb_node);
139 if (ip < s->start)
140 p = &(*p)->rb_left;
141 else
142 p = &(*p)->rb_right;
143 }
144 rb_link_node(&sym->rb_node, parent, p);
145 rb_insert_color(&sym->rb_node, &self->syms);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300146}
147
148static struct symbol *dso__find_symbol(struct dso *self, uint64_t ip)
149{
150 if (self == NULL)
151 return NULL;
152
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300153 struct rb_node *n = self->syms.rb_node;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300154
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300155 while (n) {
156 struct symbol *s = rb_entry(n, struct symbol, rb_node);
157
158 if (ip < s->start)
159 n = n->rb_left;
160 else if (ip > s->end)
161 n = n->rb_right;
162 else
163 return s;
164 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300165
166 return NULL;
167}
168
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300169/**
170 * elf_symtab__for_each_symbol - iterate thru all the symbols
171 *
172 * @self: struct elf_symtab instance to iterate
173 * @index: uint32_t index
174 * @sym: GElf_Sym iterator
175 */
176#define elf_symtab__for_each_symbol(syms, nr_syms, index, sym) \
177 for (index = 0, gelf_getsym(syms, index, &sym);\
178 index < nr_syms; \
179 index++, gelf_getsym(syms, index, &sym))
180
181static inline uint8_t elf_sym__type(const GElf_Sym *sym)
182{
183 return GELF_ST_TYPE(sym->st_info);
184}
185
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200186static inline int elf_sym__is_function(const GElf_Sym *sym)
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300187{
188 return elf_sym__type(sym) == STT_FUNC &&
189 sym->st_name != 0 &&
190 sym->st_shndx != SHN_UNDEF;
191}
192
193static inline const char *elf_sym__name(const GElf_Sym *sym,
194 const Elf_Data *symstrs)
195{
196 return symstrs->d_buf + sym->st_name;
197}
198
199static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
200 GElf_Shdr *shp, const char *name,
201 size_t *index)
202{
203 Elf_Scn *sec = NULL;
204 size_t cnt = 1;
205
206 while ((sec = elf_nextscn(elf, sec)) != NULL) {
207 char *str;
208
209 gelf_getshdr(sec, shp);
210 str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
211 if (!strcmp(name, str)) {
212 if (index)
213 *index = cnt;
214 break;
215 }
216 ++cnt;
217 }
218
219 return sec;
220}
221
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300222static int dso__load(struct dso *self)
223{
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300224 int fd = open(self->name, O_RDONLY), err = -1;
225
226 if (fd == -1)
227 return -1;
228
229 Elf *elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
230 if (elf == NULL) {
231 fprintf(stderr, "%s: cannot read %s ELF file.\n",
232 __func__, self->name);
233 goto out_close;
234 }
235
236 GElf_Ehdr ehdr;
237 if (gelf_getehdr(elf, &ehdr) == NULL) {
238 fprintf(stderr, "%s: cannot get elf header.\n", __func__);
239 goto out_elf_end;
240 }
241
242 GElf_Shdr shdr;
243 Elf_Scn *sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
244 if (sec == NULL)
245 sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
246
247 if (sec == NULL)
248 goto out_elf_end;
249
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300250 Elf_Data *syms = elf_getdata(sec, NULL);
251 if (syms == NULL)
252 goto out_elf_end;
253
254 sec = elf_getscn(elf, shdr.sh_link);
255 if (sec == NULL)
256 goto out_elf_end;
257
258 Elf_Data *symstrs = elf_getdata(sec, NULL);
259 if (symstrs == NULL)
260 goto out_elf_end;
261
262 const uint32_t nr_syms = shdr.sh_size / shdr.sh_entsize;
263
264 GElf_Sym sym;
265 uint32_t index;
266 elf_symtab__for_each_symbol(syms, nr_syms, index, sym) {
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200267 struct symbol *f;
268
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300269 if (!elf_sym__is_function(&sym))
270 continue;
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200271
272 sec = elf_getscn(elf, sym.st_shndx);
273 if (!sec)
274 goto out_elf_end;
275
276 gelf_getshdr(sec, &shdr);
277 sym.st_value -= shdr.sh_addr - shdr.sh_offset;
278
279 f = symbol__new(sym.st_value, sym.st_size,
280 elf_sym__name(&sym, symstrs));
281 if (!f)
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300282 goto out_elf_end;
283
284 dso__insert_symbol(self, f);
285 }
286
287 err = 0;
288out_elf_end:
289 elf_end(elf);
290out_close:
291 close(fd);
292 return err;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300293}
294
295static size_t dso__fprintf(struct dso *self, FILE *fp)
296{
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300297 size_t ret = fprintf(fp, "dso: %s\n", self->name);
298
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300299 struct rb_node *nd;
300 for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) {
301 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300302 ret += symbol__fprintf(pos, fp);
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300303 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300304
305 return ret;
306}
307
308static LIST_HEAD(dsos);
309static struct dso *kernel_dso;
310
311static void dsos__add(struct dso *dso)
312{
313 list_add_tail(&dso->node, &dsos);
314}
315
316static struct dso *dsos__find(const char *name)
317{
318 struct dso *pos;
319
320 list_for_each_entry(pos, &dsos, node)
321 if (strcmp(pos->name, name) == 0)
322 return pos;
323 return NULL;
324}
325
326static struct dso *dsos__findnew(const char *name)
327{
328 struct dso *dso = dsos__find(name);
329
330 if (dso == NULL) {
331 dso = dso__new(name);
332 if (dso != NULL && dso__load(dso) < 0)
333 goto out_delete_dso;
334
335 dsos__add(dso);
336 }
337
338 return dso;
339
340out_delete_dso:
341 dso__delete(dso);
342 return NULL;
343}
344
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200345void dsos__fprintf(FILE *fp)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300346{
347 struct dso *pos;
348
349 list_for_each_entry(pos, &dsos, node)
350 dso__fprintf(pos, fp);
351}
352
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300353static int hex(char ch)
354{
355 if ((ch >= '0') && (ch <= '9'))
356 return ch - '0';
357 if ((ch >= 'a') && (ch <= 'f'))
358 return ch - 'a' + 10;
359 if ((ch >= 'A') && (ch <= 'F'))
360 return ch - 'A' + 10;
361 return -1;
362}
363
364/*
365 * While we find nice hex chars, build a long_val.
366 * Return number of chars processed.
367 */
368int hex2long(char *ptr, unsigned long *long_val)
369{
370 const char *p = ptr;
371 *long_val = 0;
372
373 while (*p) {
374 const int hex_val = hex(*p);
375
376 if (hex_val < 0)
377 break;
378
379 *long_val = (*long_val << 4) | hex_val;
380 p++;
381 }
382
383 return p - ptr;
384}
385
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300386static int load_kallsyms(void)
387{
Ingo Molnaraf836322009-05-27 08:38:48 +0200388 struct rb_node *nd, *prevnd;
389 char *line = NULL;
390 FILE *file;
391 size_t n;
392
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300393 kernel_dso = dso__new("[kernel]");
394 if (kernel_dso == NULL)
395 return -1;
396
Ingo Molnaraf836322009-05-27 08:38:48 +0200397 file = fopen("/proc/kallsyms", "r");
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300398 if (file == NULL)
399 goto out_delete_dso;
400
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300401 while (!feof(file)) {
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300402 unsigned long start;
Ingo Molnaraf836322009-05-27 08:38:48 +0200403 struct symbol *sym;
404 int line_len, len;
405 char symbol_type;
406
407 line_len = getline(&line, &n, file);
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300408 if (line_len < 0)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300409 break;
410
411 if (!line)
412 goto out_delete_dso;
413
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300414 line[--line_len] = '\0'; /* \n */
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300415
Ingo Molnaraf836322009-05-27 08:38:48 +0200416 len = hex2long(line, &start);
417
Arnaldo Carvalho de Melo03f63162009-05-26 19:21:55 -0300418 len++;
419 if (len + 2 >= line_len)
420 continue;
421
Ingo Molnaraf836322009-05-27 08:38:48 +0200422 symbol_type = toupper(line[len]);
Arnaldo Carvalho de Melo03f63162009-05-26 19:21:55 -0300423 /*
424 * We're interested only in code ('T'ext)
425 */
Ingo Molnaraf836322009-05-27 08:38:48 +0200426 if (symbol_type != 'T' && symbol_type != 'W')
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300427 continue;
428 /*
429 * Well fix up the end later, when we have all sorted.
430 */
Ingo Molnaraf836322009-05-27 08:38:48 +0200431 sym = symbol__new(start, 0xdead, line + len + 2);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300432
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300433 if (sym == NULL)
434 goto out_delete_dso;
435
436 dso__insert_symbol(kernel_dso, sym);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300437 }
438
Arnaldo Carvalho de Meloabd54f62009-05-26 12:21:34 -0300439 /*
440 * Now that we have all sorted out, just set the ->end of all
441 * symbols
442 */
Ingo Molnaraf836322009-05-27 08:38:48 +0200443 prevnd = rb_first(&kernel_dso->syms);
Arnaldo Carvalho de Meloabd54f62009-05-26 12:21:34 -0300444
445 if (prevnd == NULL)
446 goto out_delete_line;
447
448 for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
449 struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node),
450 *curr = rb_entry(nd, struct symbol, rb_node);
451
452 prev->end = curr->start - 1;
453 prevnd = nd;
454 }
455
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300456 dsos__add(kernel_dso);
457 free(line);
458 fclose(file);
Ingo Molnaraf836322009-05-27 08:38:48 +0200459
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300460 return 0;
461
Arnaldo Carvalho de Melo59d81022009-05-26 11:14:27 -0300462out_delete_line:
463 free(line);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300464out_delete_dso:
465 dso__delete(kernel_dso);
466 return -1;
467}
468
469struct map {
470 struct list_head node;
471 uint64_t start;
472 uint64_t end;
473 uint64_t pgoff;
474 struct dso *dso;
475};
476
477static struct map *map__new(struct mmap_event *event)
478{
479 struct map *self = malloc(sizeof(*self));
480
481 if (self != NULL) {
482 self->start = event->start;
483 self->end = event->start + event->len;
484 self->pgoff = event->pgoff;
485
486 self->dso = dsos__findnew(event->filename);
487 if (self->dso == NULL)
488 goto out_delete;
489 }
490 return self;
491out_delete:
492 free(self);
493 return NULL;
494}
495
496static size_t map__fprintf(struct map *self, FILE *fp)
497{
498 return fprintf(fp, " %lx-%lx %lx %s\n",
499 self->start, self->end, self->pgoff, self->dso->name);
500}
501
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300502struct thread;
503
504static const char *thread__name(struct thread *self, char *bf, size_t size);
505
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300506struct symhist {
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300507 struct rb_node rb_node;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300508 struct dso *dso;
509 struct symbol *sym;
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300510 struct thread *thread;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300511 uint64_t ip;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300512 uint32_t count;
513 char level;
514};
515
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300516static struct symhist *symhist__new(struct symbol *sym, uint64_t ip,
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300517 struct thread *thread, struct dso *dso,
518 char level)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300519{
520 struct symhist *self = malloc(sizeof(*self));
521
522 if (self != NULL) {
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300523 self->sym = sym;
524 self->thread = thread;
525 self->ip = ip;
526 self->dso = dso;
527 self->level = level;
528 self->count = 1;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300529 }
530
531 return self;
532}
533
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200534void symhist__delete(struct symhist *self)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300535{
536 free(self);
537}
538
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300539static void symhist__inc(struct symhist *self)
540{
541 ++self->count;
542}
543
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300544static size_t
545symhist__fprintf(struct symhist *self, uint64_t total_samples, FILE *fp)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300546{
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300547 char bf[32];
548 size_t ret;
549
550 if (total_samples)
551 ret = fprintf(fp, "%5.2f", (self->count * 100.0) / total_samples);
552 else
553 ret = fprintf(fp, "%12d", self->count);
554
Arnaldo Carvalho de Melo815e7772009-05-26 19:46:14 -0300555 ret += fprintf(fp, "%14s [%c] ",
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300556 thread__name(self->thread, bf, sizeof(bf)),
Arnaldo Carvalho de Melo815e7772009-05-26 19:46:14 -0300557 self->level);
558
559 if (verbose)
560 ret += fprintf(fp, "%#018llx ", (unsigned long long)self->ip);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300561
562 if (self->level != '.')
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300563 ret += fprintf(fp, "%s\n",
564 self->sym ? self->sym->name : "<unknown>");
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300565 else
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300566 ret += fprintf(fp, "%s: %s\n",
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200567 self->dso ? self->dso->name : "<unknown>",
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300568 self->sym ? self->sym->name : "<unknown>");
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300569 return ret;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300570}
571
572struct thread {
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300573 struct rb_node rb_node;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300574 struct list_head maps;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300575 struct rb_root symhists;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300576 pid_t pid;
577 char *comm;
578};
579
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300580static const char *thread__name(struct thread *self, char *bf, size_t size)
581{
582 if (self->comm)
583 return self->comm;
584
585 snprintf(bf, sizeof(bf), ":%u", self->pid);
586 return bf;
587}
588
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300589static struct thread *thread__new(pid_t pid)
590{
591 struct thread *self = malloc(sizeof(*self));
592
593 if (self != NULL) {
594 self->pid = pid;
595 self->comm = NULL;
596 INIT_LIST_HEAD(&self->maps);
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300597 self->symhists = RB_ROOT;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300598 }
599
600 return self;
601}
602
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300603static int thread__symbol_incnew(struct thread *self, struct symbol *sym,
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300604 uint64_t ip, struct dso *dso, char level)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300605{
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300606 struct rb_node **p = &self->symhists.rb_node;
607 struct rb_node *parent = NULL;
608 struct symhist *sh;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300609
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300610 while (*p != NULL) {
611 parent = *p;
612 sh = rb_entry(parent, struct symhist, rb_node);
613
614 if (sh->sym == sym || ip == sh->ip) {
615 symhist__inc(sh);
616 return 0;
617 }
618
619 /* Handle unresolved symbols too */
620 const uint64_t start = !sh->sym ? sh->ip : sh->sym->start;
621
622 if (ip < start)
623 p = &(*p)->rb_left;
624 else
625 p = &(*p)->rb_right;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300626 }
627
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300628 sh = symhist__new(sym, ip, self, dso, level);
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300629 if (sh == NULL)
630 return -ENOMEM;
631 rb_link_node(&sh->rb_node, parent, p);
632 rb_insert_color(&sh->rb_node, &self->symhists);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300633 return 0;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300634}
635
636static int thread__set_comm(struct thread *self, const char *comm)
637{
638 self->comm = strdup(comm);
639 return self->comm ? 0 : -ENOMEM;
640}
641
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200642size_t thread__maps_fprintf(struct thread *self, FILE *fp)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300643{
644 struct map *pos;
645 size_t ret = 0;
646
647 list_for_each_entry(pos, &self->maps, node)
648 ret += map__fprintf(pos, fp);
649
650 return ret;
651}
652
653static size_t thread__fprintf(struct thread *self, FILE *fp)
654{
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300655 int ret = fprintf(fp, "thread: %d %s\n", self->pid, self->comm);
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300656 struct rb_node *nd;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300657
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300658 for (nd = rb_first(&self->symhists); nd; nd = rb_next(nd)) {
659 struct symhist *pos = rb_entry(nd, struct symhist, rb_node);
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300660 ret += symhist__fprintf(pos, 0, fp);
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300661 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300662
663 return ret;
664}
665
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300666static struct rb_root threads = RB_ROOT;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300667
668static struct thread *threads__findnew(pid_t pid)
669{
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300670 struct rb_node **p = &threads.rb_node;
671 struct rb_node *parent = NULL;
672 struct thread *th;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300673
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300674 while (*p != NULL) {
675 parent = *p;
676 th = rb_entry(parent, struct thread, rb_node);
677
678 if (th->pid == pid)
679 return th;
680
681 if (pid < th->pid)
682 p = &(*p)->rb_left;
683 else
684 p = &(*p)->rb_right;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300685 }
686
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300687 th = thread__new(pid);
688 if (th != NULL) {
689 rb_link_node(&th->rb_node, parent, p);
690 rb_insert_color(&th->rb_node, &threads);
691 }
692 return th;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300693}
694
695static void thread__insert_map(struct thread *self, struct map *map)
696{
697 list_add_tail(&map->node, &self->maps);
698}
699
700static struct map *thread__find_map(struct thread *self, uint64_t ip)
701{
702 if (self == NULL)
703 return NULL;
704
705 struct map *pos;
706
707 list_for_each_entry(pos, &self->maps, node)
708 if (ip >= pos->start && ip <= pos->end)
709 return pos;
710
711 return NULL;
712}
713
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300714void threads__fprintf(FILE *fp)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300715{
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300716 struct rb_node *nd;
717 for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
718 struct thread *pos = rb_entry(nd, struct thread, rb_node);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300719 thread__fprintf(pos, fp);
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300720 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300721}
722
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300723static struct rb_root global_symhists = RB_ROOT;
724
725static void threads__insert_symhist(struct symhist *sh)
726{
727 struct rb_node **p = &global_symhists.rb_node;
728 struct rb_node *parent = NULL;
729 struct symhist *iter;
730
731 while (*p != NULL) {
732 parent = *p;
733 iter = rb_entry(parent, struct symhist, rb_node);
734
735 /* Reverse order */
736 if (sh->count > iter->count)
737 p = &(*p)->rb_left;
738 else
739 p = &(*p)->rb_right;
740 }
741
742 rb_link_node(&sh->rb_node, parent, p);
743 rb_insert_color(&sh->rb_node, &global_symhists);
744}
745
746static void threads__sort_symhists(void)
747{
748 struct rb_node *nd;
749
750 for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
751 struct thread *thread = rb_entry(nd, struct thread, rb_node);
752 struct rb_node *next = rb_first(&thread->symhists);
753
754 while (next) {
755 struct symhist *n = rb_entry(next, struct symhist,
756 rb_node);
757 next = rb_next(&n->rb_node);
758 rb_erase(&n->rb_node, &thread->symhists);
759 threads__insert_symhist(n);
760 }
761
762 }
763}
764
765static size_t threads__symhists_fprintf(uint64_t total_samples, FILE *fp)
766{
767 struct rb_node *nd;
768 size_t ret = 0;
769
770 for (nd = rb_first(&global_symhists); nd; nd = rb_next(nd)) {
771 struct symhist *pos = rb_entry(nd, struct symhist, rb_node);
772 ret += symhist__fprintf(pos, total_samples, fp);
773 }
774
775 return ret;
776}
777
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200778static int __cmd_report(void)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300779{
780 unsigned long offset = 0;
781 unsigned long head = 0;
782 struct stat stat;
783 char *buf;
784 event_t *event;
785 int ret, rc = EXIT_FAILURE;
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200786 uint32_t size;
Ingo Molnarf49515b2009-05-26 19:03:36 +0200787 unsigned long total = 0, total_mmap = 0, total_comm = 0, total_unknown = 0;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300788
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300789 input = open(input_name, O_RDONLY);
790 if (input < 0) {
791 perror("failed to open file");
792 exit(-1);
793 }
794
795 ret = fstat(input, &stat);
796 if (ret < 0) {
797 perror("failed to stat file");
798 exit(-1);
799 }
800
801 if (!stat.st_size) {
802 fprintf(stderr, "zero-sized file, nothing to do!\n");
803 exit(0);
804 }
805
806 if (load_kallsyms() < 0) {
807 perror("failed to open kallsyms");
808 return EXIT_FAILURE;
809 }
810
811remap:
812 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
813 MAP_SHARED, input, offset);
814 if (buf == MAP_FAILED) {
815 perror("failed to mmap file");
816 exit(-1);
817 }
818
819more:
820 event = (event_t *)(buf + head);
821
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200822 size = event->header.size;
823 if (!size)
824 size = 8;
825
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300826 if (head + event->header.size >= page_size * mmap_window) {
827 unsigned long shift = page_size * (head / page_size);
828 int ret;
829
830 ret = munmap(buf, page_size * mmap_window);
831 assert(ret == 0);
832
833 offset += shift;
834 head -= shift;
835 goto remap;
836 }
837
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200838 size = event->header.size;
839 if (!size)
840 goto broken_event;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300841
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300842 if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
843 char level;
844 int show = 0;
845 struct dso *dso = NULL;
846 struct thread *thread = threads__findnew(event->ip.pid);
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200847 uint64_t ip = event->ip.ip;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300848
Ingo Molnar97b07b62009-05-26 18:48:58 +0200849 if (dump_trace) {
Ingo Molnarf49515b2009-05-26 19:03:36 +0200850 fprintf(stderr, "%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
851 (void *)(offset + head),
852 (void *)(long)(event->header.size),
Ingo Molnar97b07b62009-05-26 18:48:58 +0200853 event->header.misc,
854 event->ip.pid,
855 (void *)event->ip.ip);
856 }
857
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300858 if (thread == NULL) {
859 fprintf(stderr, "problem processing %d event, bailing out\n",
860 event->header.type);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300861 goto done;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300862 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300863
864 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
865 show = SHOW_KERNEL;
866 level = 'k';
867 dso = kernel_dso;
868 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
869 show = SHOW_USER;
870 level = '.';
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200871 struct map *map = thread__find_map(thread, ip);
872 if (map != NULL) {
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300873 dso = map->dso;
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200874 ip -= map->start + map->pgoff;
875 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300876 } else {
877 show = SHOW_HV;
878 level = 'H';
879 }
880
881 if (show & show_mask) {
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200882 struct symbol *sym = dso__find_symbol(dso, ip);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300883
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200884 if (thread__symbol_incnew(thread, sym, ip, dso, level)) {
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300885 fprintf(stderr, "problem incrementing symbol count, bailing out\n");
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300886 goto done;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300887 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300888 }
889 total++;
890 } else switch (event->header.type) {
891 case PERF_EVENT_MMAP: {
892 struct thread *thread = threads__findnew(event->mmap.pid);
893 struct map *map = map__new(&event->mmap);
894
Ingo Molnar97b07b62009-05-26 18:48:58 +0200895 if (dump_trace) {
Ingo Molnarf49515b2009-05-26 19:03:36 +0200896 fprintf(stderr, "%p [%p]: PERF_EVENT_MMAP: [%p(%p) @ %p]: %s\n",
897 (void *)(offset + head),
898 (void *)(long)(event->header.size),
Ingo Molnar97b07b62009-05-26 18:48:58 +0200899 (void *)event->mmap.start,
900 (void *)event->mmap.len,
901 (void *)event->mmap.pgoff,
902 event->mmap.filename);
903 }
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300904 if (thread == NULL || map == NULL) {
905 fprintf(stderr, "problem processing PERF_EVENT_MMAP, bailing out\n");
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300906 goto done;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300907 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300908 thread__insert_map(thread, map);
Ingo Molnar97b07b62009-05-26 18:48:58 +0200909 total_mmap++;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300910 break;
911 }
912 case PERF_EVENT_COMM: {
913 struct thread *thread = threads__findnew(event->comm.pid);
914
Ingo Molnar97b07b62009-05-26 18:48:58 +0200915 if (dump_trace) {
Ingo Molnarf49515b2009-05-26 19:03:36 +0200916 fprintf(stderr, "%p [%p]: PERF_EVENT_COMM: %s:%d\n",
917 (void *)(offset + head),
918 (void *)(long)(event->header.size),
Ingo Molnar97b07b62009-05-26 18:48:58 +0200919 event->comm.comm, event->comm.pid);
920 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300921 if (thread == NULL ||
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300922 thread__set_comm(thread, event->comm.comm)) {
923 fprintf(stderr, "problem processing PERF_EVENT_COMM, bailing out\n");
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300924 goto done;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300925 }
Ingo Molnar97b07b62009-05-26 18:48:58 +0200926 total_comm++;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300927 break;
928 }
Ingo Molnar97b07b62009-05-26 18:48:58 +0200929 default: {
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200930broken_event:
Ingo Molnarf49515b2009-05-26 19:03:36 +0200931 fprintf(stderr, "%p [%p]: skipping unknown header type: %d\n",
932 (void *)(offset + head),
933 (void *)(long)(event->header.size),
Ingo Molnar97b07b62009-05-26 18:48:58 +0200934 event->header.type);
Ingo Molnar3e706112009-05-26 18:53:17 +0200935 total_unknown++;
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200936
937 /*
938 * assume we lost track of the stream, check alignment, and
939 * increment a single u64 in the hope to catch on again 'soon'.
940 */
941
942 if (unlikely(head & 7))
943 head &= ~7ULL;
944
945 size = 8;
Ingo Molnar97b07b62009-05-26 18:48:58 +0200946 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300947 }
948
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200949 head += size;
Ingo Molnarf49515b2009-05-26 19:03:36 +0200950
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300951 if (offset + head < stat.st_size)
952 goto more;
953
954 rc = EXIT_SUCCESS;
955done:
956 close(input);
Ingo Molnar97b07b62009-05-26 18:48:58 +0200957
958 if (dump_trace) {
Ingo Molnar3e706112009-05-26 18:53:17 +0200959 fprintf(stderr, " IP events: %10ld\n", total);
960 fprintf(stderr, " mmap events: %10ld\n", total_mmap);
961 fprintf(stderr, " comm events: %10ld\n", total_comm);
962 fprintf(stderr, " unknown events: %10ld\n", total_unknown);
Ingo Molnar97b07b62009-05-26 18:48:58 +0200963
964 return 0;
965 }
966
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300967 threads__sort_symhists();
968 threads__symhists_fprintf(total, stdout);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300969
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300970 return rc;
971}
972
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200973static const char * const report_usage[] = {
974 "perf report [<options>] <command>",
975 NULL
976};
977
978static const struct option options[] = {
979 OPT_STRING('i', "input", &input_name, "file",
980 "input file name"),
Arnaldo Carvalho de Melo815e7772009-05-26 19:46:14 -0300981 OPT_BOOLEAN('v', "verbose", &verbose,
982 "be more verbose (show symbol address, etc)"),
Ingo Molnar97b07b62009-05-26 18:48:58 +0200983 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
984 "dump raw trace in ASCII"),
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200985 OPT_END()
986};
987
988int cmd_report(int argc, const char **argv, const char *prefix)
989{
990 elf_version(EV_CURRENT);
991
992 page_size = getpagesize();
993
994 parse_options(argc, argv, options, report_usage, 0);
995
996 return __cmd_report();
997}