blob: 9aef7c54483ec27896a7c1a51b592ae32f3a69c4 [file] [log] [blame]
Ingo Molnar53cb8bc2009-05-26 09:17:18 +02001#include "util/util.h"
Ingo Molnar16f762a2009-05-27 09:10:38 +02002#include "builtin.h"
Ingo Molnar53cb8bc2009-05-26 09:17:18 +02003
4#include <libelf.h>
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -03005#include <gelf.h>
6#include <elf.h>
Arnaldo Carvalho de Melo03f63162009-05-26 19:21:55 -03007#include <ctype.h>
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -03008
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -03009#include "util/list.h"
Ingo Molnara930d2c2009-05-27 09:50:13 +020010#include "util/cache.h"
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -030011#include "util/rbtree.h"
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030012
Ingo Molnar53cb8bc2009-05-26 09:17:18 +020013#include "perf.h"
14
15#include "util/parse-options.h"
16#include "util/parse-events.h"
17
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030018#define SHOW_KERNEL 1
19#define SHOW_USER 2
20#define SHOW_HV 4
21
Ingo Molnar23ac9cb2009-05-27 09:33:18 +020022static char const *input_name = "perf.data";
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030023static int input;
24static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
25
Ingo Molnar97b07b62009-05-26 18:48:58 +020026static int dump_trace = 0;
Ingo Molnar16f762a2009-05-27 09:10:38 +020027static int verbose;
Ingo Molnar97b07b62009-05-26 18:48:58 +020028
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030029static unsigned long page_size;
30static unsigned long mmap_window = 32;
31
Ingo Molnar53cb8bc2009-05-26 09:17:18 +020032const char *perf_event_names[] = {
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030033 [PERF_EVENT_MMAP] = " PERF_EVENT_MMAP",
34 [PERF_EVENT_MUNMAP] = " PERF_EVENT_MUNMAP",
35 [PERF_EVENT_COMM] = " PERF_EVENT_COMM",
36};
37
38struct ip_event {
39 struct perf_event_header header;
40 __u64 ip;
41 __u32 pid, tid;
42};
43struct mmap_event {
44 struct perf_event_header header;
45 __u32 pid, tid;
46 __u64 start;
47 __u64 len;
48 __u64 pgoff;
49 char filename[PATH_MAX];
50};
51struct comm_event {
52 struct perf_event_header header;
53 __u32 pid,tid;
54 char comm[16];
55};
56
57typedef union event_union {
58 struct perf_event_header header;
59 struct ip_event ip;
60 struct mmap_event mmap;
61 struct comm_event comm;
62} event_t;
63
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030064struct symbol {
Ingo Molnar16f762a2009-05-27 09:10:38 +020065 struct rb_node rb_node;
66 __u64 start;
67 __u64 end;
68 char name[0];
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030069};
70
71static struct symbol *symbol__new(uint64_t start, uint64_t len, const char *name)
72{
73 struct symbol *self = malloc(sizeof(*self) + strlen(name) + 1);
74
75 if (self != NULL) {
76 self->start = start;
77 self->end = start + len;
78 strcpy(self->name, name);
79 }
80
81 return self;
82}
83
84static void symbol__delete(struct symbol *self)
85{
86 free(self);
87}
88
89static size_t symbol__fprintf(struct symbol *self, FILE *fp)
90{
Ingo Molnar16f762a2009-05-27 09:10:38 +020091 return fprintf(fp, " %llx-%llx %s\n",
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030092 self->start, self->end, self->name);
93}
94
95struct dso {
96 struct list_head node;
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -030097 struct rb_root syms;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -030098 char name[0];
99};
100
101static struct dso *dso__new(const char *name)
102{
103 struct dso *self = malloc(sizeof(*self) + strlen(name) + 1);
104
105 if (self != NULL) {
106 strcpy(self->name, name);
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300107 self->syms = RB_ROOT;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300108 }
109
110 return self;
111}
112
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300113static void dso__delete_symbols(struct dso *self)
114{
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300115 struct symbol *pos;
116 struct rb_node *next = rb_first(&self->syms);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300117
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300118 while (next) {
119 pos = rb_entry(next, struct symbol, rb_node);
120 next = rb_next(&pos->rb_node);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300121 symbol__delete(pos);
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300122 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300123}
124
125static void dso__delete(struct dso *self)
126{
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300127 dso__delete_symbols(self);
128 free(self);
129}
130
131static void dso__insert_symbol(struct dso *self, struct symbol *sym)
132{
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300133 struct rb_node **p = &self->syms.rb_node;
134 struct rb_node *parent = NULL;
135 const uint64_t ip = sym->start;
136 struct symbol *s;
137
138 while (*p != NULL) {
139 parent = *p;
140 s = rb_entry(parent, struct symbol, rb_node);
141 if (ip < s->start)
142 p = &(*p)->rb_left;
143 else
144 p = &(*p)->rb_right;
145 }
146 rb_link_node(&sym->rb_node, parent, p);
147 rb_insert_color(&sym->rb_node, &self->syms);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300148}
149
150static struct symbol *dso__find_symbol(struct dso *self, uint64_t ip)
151{
Ingo Molnar16f762a2009-05-27 09:10:38 +0200152 struct rb_node *n;
153
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300154 if (self == NULL)
155 return NULL;
156
Ingo Molnar16f762a2009-05-27 09:10:38 +0200157 n = self->syms.rb_node;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300158
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300159 while (n) {
160 struct symbol *s = rb_entry(n, struct symbol, rb_node);
161
162 if (ip < s->start)
163 n = n->rb_left;
164 else if (ip > s->end)
165 n = n->rb_right;
166 else
167 return s;
168 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300169
170 return NULL;
171}
172
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300173/**
174 * elf_symtab__for_each_symbol - iterate thru all the symbols
175 *
176 * @self: struct elf_symtab instance to iterate
177 * @index: uint32_t index
178 * @sym: GElf_Sym iterator
179 */
180#define elf_symtab__for_each_symbol(syms, nr_syms, index, sym) \
181 for (index = 0, gelf_getsym(syms, index, &sym);\
182 index < nr_syms; \
183 index++, gelf_getsym(syms, index, &sym))
184
185static inline uint8_t elf_sym__type(const GElf_Sym *sym)
186{
187 return GELF_ST_TYPE(sym->st_info);
188}
189
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200190static inline int elf_sym__is_function(const GElf_Sym *sym)
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300191{
192 return elf_sym__type(sym) == STT_FUNC &&
193 sym->st_name != 0 &&
194 sym->st_shndx != SHN_UNDEF;
195}
196
197static inline const char *elf_sym__name(const GElf_Sym *sym,
198 const Elf_Data *symstrs)
199{
200 return symstrs->d_buf + sym->st_name;
201}
202
203static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
204 GElf_Shdr *shp, const char *name,
205 size_t *index)
206{
207 Elf_Scn *sec = NULL;
208 size_t cnt = 1;
209
210 while ((sec = elf_nextscn(elf, sec)) != NULL) {
211 char *str;
212
213 gelf_getshdr(sec, shp);
214 str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
215 if (!strcmp(name, str)) {
216 if (index)
217 *index = cnt;
218 break;
219 }
220 ++cnt;
221 }
222
223 return sec;
224}
225
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300226static int dso__load(struct dso *self)
227{
Ingo Molnar16f762a2009-05-27 09:10:38 +0200228 Elf_Data *symstrs;
229 uint32_t nr_syms;
230 int fd, err = -1;
231 uint32_t index;
232 GElf_Ehdr ehdr;
233 GElf_Shdr shdr;
234 Elf_Data *syms;
235 GElf_Sym sym;
236 Elf_Scn *sec;
237 Elf *elf;
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300238
Ingo Molnar16f762a2009-05-27 09:10:38 +0200239
240 fd = open(self->name, O_RDONLY);
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300241 if (fd == -1)
242 return -1;
243
Ingo Molnar16f762a2009-05-27 09:10:38 +0200244 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300245 if (elf == NULL) {
246 fprintf(stderr, "%s: cannot read %s ELF file.\n",
247 __func__, self->name);
248 goto out_close;
249 }
250
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300251 if (gelf_getehdr(elf, &ehdr) == NULL) {
252 fprintf(stderr, "%s: cannot get elf header.\n", __func__);
253 goto out_elf_end;
254 }
255
Ingo Molnar16f762a2009-05-27 09:10:38 +0200256 sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300257 if (sec == NULL)
258 sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
259
260 if (sec == NULL)
261 goto out_elf_end;
262
Ingo Molnar16f762a2009-05-27 09:10:38 +0200263 syms = elf_getdata(sec, NULL);
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300264 if (syms == NULL)
265 goto out_elf_end;
266
267 sec = elf_getscn(elf, shdr.sh_link);
268 if (sec == NULL)
269 goto out_elf_end;
270
Ingo Molnar16f762a2009-05-27 09:10:38 +0200271 symstrs = elf_getdata(sec, NULL);
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300272 if (symstrs == NULL)
273 goto out_elf_end;
274
Ingo Molnar16f762a2009-05-27 09:10:38 +0200275 nr_syms = shdr.sh_size / shdr.sh_entsize;
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300276
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300277 elf_symtab__for_each_symbol(syms, nr_syms, index, sym) {
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200278 struct symbol *f;
279
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300280 if (!elf_sym__is_function(&sym))
281 continue;
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200282
283 sec = elf_getscn(elf, sym.st_shndx);
284 if (!sec)
285 goto out_elf_end;
286
287 gelf_getshdr(sec, &shdr);
288 sym.st_value -= shdr.sh_addr - shdr.sh_offset;
289
290 f = symbol__new(sym.st_value, sym.st_size,
291 elf_sym__name(&sym, symstrs));
292 if (!f)
Arnaldo Carvalho de Melo62eb9392009-05-18 14:28:47 -0300293 goto out_elf_end;
294
295 dso__insert_symbol(self, f);
296 }
297
298 err = 0;
299out_elf_end:
300 elf_end(elf);
301out_close:
302 close(fd);
303 return err;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300304}
305
306static size_t dso__fprintf(struct dso *self, FILE *fp)
307{
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300308 size_t ret = fprintf(fp, "dso: %s\n", self->name);
309
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300310 struct rb_node *nd;
311 for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) {
312 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300313 ret += symbol__fprintf(pos, fp);
Arnaldo Carvalho de Melo35a50c82009-05-18 16:24:49 -0300314 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300315
316 return ret;
317}
318
319static LIST_HEAD(dsos);
320static struct dso *kernel_dso;
321
322static void dsos__add(struct dso *dso)
323{
324 list_add_tail(&dso->node, &dsos);
325}
326
327static struct dso *dsos__find(const char *name)
328{
329 struct dso *pos;
330
331 list_for_each_entry(pos, &dsos, node)
332 if (strcmp(pos->name, name) == 0)
333 return pos;
334 return NULL;
335}
336
337static struct dso *dsos__findnew(const char *name)
338{
339 struct dso *dso = dsos__find(name);
340
341 if (dso == NULL) {
342 dso = dso__new(name);
343 if (dso != NULL && dso__load(dso) < 0)
344 goto out_delete_dso;
345
346 dsos__add(dso);
347 }
348
349 return dso;
350
351out_delete_dso:
352 dso__delete(dso);
353 return NULL;
354}
355
Ingo Molnar16f762a2009-05-27 09:10:38 +0200356static void dsos__fprintf(FILE *fp)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300357{
358 struct dso *pos;
359
360 list_for_each_entry(pos, &dsos, node)
361 dso__fprintf(pos, fp);
362}
363
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300364static int hex(char ch)
365{
366 if ((ch >= '0') && (ch <= '9'))
367 return ch - '0';
368 if ((ch >= 'a') && (ch <= 'f'))
369 return ch - 'a' + 10;
370 if ((ch >= 'A') && (ch <= 'F'))
371 return ch - 'A' + 10;
372 return -1;
373}
374
375/*
376 * While we find nice hex chars, build a long_val.
377 * Return number of chars processed.
378 */
Ingo Molnar16f762a2009-05-27 09:10:38 +0200379static int hex2long(char *ptr, unsigned long *long_val)
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300380{
381 const char *p = ptr;
382 *long_val = 0;
383
384 while (*p) {
385 const int hex_val = hex(*p);
386
387 if (hex_val < 0)
388 break;
389
390 *long_val = (*long_val << 4) | hex_val;
391 p++;
392 }
393
394 return p - ptr;
395}
396
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300397static int load_kallsyms(void)
398{
Ingo Molnaraf836322009-05-27 08:38:48 +0200399 struct rb_node *nd, *prevnd;
400 char *line = NULL;
401 FILE *file;
402 size_t n;
403
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300404 kernel_dso = dso__new("[kernel]");
405 if (kernel_dso == NULL)
406 return -1;
407
Ingo Molnaraf836322009-05-27 08:38:48 +0200408 file = fopen("/proc/kallsyms", "r");
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300409 if (file == NULL)
410 goto out_delete_dso;
411
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300412 while (!feof(file)) {
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300413 unsigned long start;
Ingo Molnaraf836322009-05-27 08:38:48 +0200414 struct symbol *sym;
415 int line_len, len;
416 char symbol_type;
417
418 line_len = getline(&line, &n, file);
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300419 if (line_len < 0)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300420 break;
421
422 if (!line)
423 goto out_delete_dso;
424
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300425 line[--line_len] = '\0'; /* \n */
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300426
Ingo Molnaraf836322009-05-27 08:38:48 +0200427 len = hex2long(line, &start);
428
Arnaldo Carvalho de Melo03f63162009-05-26 19:21:55 -0300429 len++;
430 if (len + 2 >= line_len)
431 continue;
432
Ingo Molnaraf836322009-05-27 08:38:48 +0200433 symbol_type = toupper(line[len]);
Arnaldo Carvalho de Melo03f63162009-05-26 19:21:55 -0300434 /*
435 * We're interested only in code ('T'ext)
436 */
Ingo Molnaraf836322009-05-27 08:38:48 +0200437 if (symbol_type != 'T' && symbol_type != 'W')
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300438 continue;
439 /*
440 * Well fix up the end later, when we have all sorted.
441 */
Ingo Molnaraf836322009-05-27 08:38:48 +0200442 sym = symbol__new(start, 0xdead, line + len + 2);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300443
Arnaldo Carvalho de Melod8d16562009-05-26 19:20:57 -0300444 if (sym == NULL)
445 goto out_delete_dso;
446
447 dso__insert_symbol(kernel_dso, sym);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300448 }
449
Arnaldo Carvalho de Meloabd54f62009-05-26 12:21:34 -0300450 /*
451 * Now that we have all sorted out, just set the ->end of all
452 * symbols
453 */
Ingo Molnaraf836322009-05-27 08:38:48 +0200454 prevnd = rb_first(&kernel_dso->syms);
Arnaldo Carvalho de Meloabd54f62009-05-26 12:21:34 -0300455
456 if (prevnd == NULL)
457 goto out_delete_line;
458
459 for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
460 struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node),
461 *curr = rb_entry(nd, struct symbol, rb_node);
462
463 prev->end = curr->start - 1;
464 prevnd = nd;
465 }
466
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300467 dsos__add(kernel_dso);
468 free(line);
469 fclose(file);
Ingo Molnaraf836322009-05-27 08:38:48 +0200470
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300471 return 0;
472
Arnaldo Carvalho de Melo59d81022009-05-26 11:14:27 -0300473out_delete_line:
474 free(line);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300475out_delete_dso:
476 dso__delete(kernel_dso);
477 return -1;
478}
479
480struct map {
481 struct list_head node;
482 uint64_t start;
483 uint64_t end;
484 uint64_t pgoff;
485 struct dso *dso;
486};
487
488static struct map *map__new(struct mmap_event *event)
489{
490 struct map *self = malloc(sizeof(*self));
491
492 if (self != NULL) {
493 self->start = event->start;
494 self->end = event->start + event->len;
495 self->pgoff = event->pgoff;
496
497 self->dso = dsos__findnew(event->filename);
498 if (self->dso == NULL)
499 goto out_delete;
500 }
501 return self;
502out_delete:
503 free(self);
504 return NULL;
505}
506
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300507struct thread;
508
509static const char *thread__name(struct thread *self, char *bf, size_t size);
510
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300511struct symhist {
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300512 struct rb_node rb_node;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300513 struct dso *dso;
514 struct symbol *sym;
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300515 struct thread *thread;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300516 uint64_t ip;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300517 uint32_t count;
518 char level;
519};
520
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300521static struct symhist *symhist__new(struct symbol *sym, uint64_t ip,
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300522 struct thread *thread, struct dso *dso,
523 char level)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300524{
525 struct symhist *self = malloc(sizeof(*self));
526
527 if (self != NULL) {
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300528 self->sym = sym;
529 self->thread = thread;
530 self->ip = ip;
531 self->dso = dso;
532 self->level = level;
533 self->count = 1;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300534 }
535
536 return 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) {
Ingo Molnar16f762a2009-05-27 09:10:38 +0200611 uint64_t start;
612
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300613 parent = *p;
614 sh = rb_entry(parent, struct symhist, rb_node);
615
616 if (sh->sym == sym || ip == sh->ip) {
617 symhist__inc(sh);
618 return 0;
619 }
620
621 /* Handle unresolved symbols too */
Ingo Molnar16f762a2009-05-27 09:10:38 +0200622 start = !sh->sym ? sh->ip : sh->sym->start;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300623
624 if (ip < start)
625 p = &(*p)->rb_left;
626 else
627 p = &(*p)->rb_right;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300628 }
629
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300630 sh = symhist__new(sym, ip, self, dso, level);
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300631 if (sh == NULL)
632 return -ENOMEM;
633 rb_link_node(&sh->rb_node, parent, p);
634 rb_insert_color(&sh->rb_node, &self->symhists);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300635 return 0;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300636}
637
638static int thread__set_comm(struct thread *self, const char *comm)
639{
640 self->comm = strdup(comm);
641 return self->comm ? 0 : -ENOMEM;
642}
643
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300644static size_t thread__fprintf(struct thread *self, FILE *fp)
645{
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300646 int ret = fprintf(fp, "thread: %d %s\n", self->pid, self->comm);
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300647 struct rb_node *nd;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300648
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300649 for (nd = rb_first(&self->symhists); nd; nd = rb_next(nd)) {
650 struct symhist *pos = rb_entry(nd, struct symhist, rb_node);
Ingo Molnar16f762a2009-05-27 09:10:38 +0200651
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300652 ret += symhist__fprintf(pos, 0, fp);
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300653 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300654
655 return ret;
656}
657
Ingo Molnar16f762a2009-05-27 09:10:38 +0200658static struct rb_root threads;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300659
660static struct thread *threads__findnew(pid_t pid)
661{
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300662 struct rb_node **p = &threads.rb_node;
663 struct rb_node *parent = NULL;
664 struct thread *th;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300665
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300666 while (*p != NULL) {
667 parent = *p;
668 th = rb_entry(parent, struct thread, rb_node);
669
670 if (th->pid == pid)
671 return th;
672
673 if (pid < th->pid)
674 p = &(*p)->rb_left;
675 else
676 p = &(*p)->rb_right;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300677 }
678
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300679 th = thread__new(pid);
680 if (th != NULL) {
681 rb_link_node(&th->rb_node, parent, p);
682 rb_insert_color(&th->rb_node, &threads);
683 }
684 return th;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300685}
686
687static void thread__insert_map(struct thread *self, struct map *map)
688{
689 list_add_tail(&map->node, &self->maps);
690}
691
692static struct map *thread__find_map(struct thread *self, uint64_t ip)
693{
Ingo Molnar16f762a2009-05-27 09:10:38 +0200694 struct map *pos;
695
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300696 if (self == NULL)
697 return NULL;
698
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300699 list_for_each_entry(pos, &self->maps, node)
700 if (ip >= pos->start && ip <= pos->end)
701 return pos;
702
703 return NULL;
704}
705
Ingo Molnar16f762a2009-05-27 09:10:38 +0200706static void threads__fprintf(FILE *fp)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300707{
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300708 struct rb_node *nd;
709 for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
710 struct thread *pos = rb_entry(nd, struct thread, rb_node);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300711 thread__fprintf(pos, fp);
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300712 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300713}
714
Ingo Molnar16f762a2009-05-27 09:10:38 +0200715static struct rb_root global_symhists;
Arnaldo Carvalho de Melo3a4b8cc2009-05-26 16:19:04 -0300716
717static void threads__insert_symhist(struct symhist *sh)
718{
719 struct rb_node **p = &global_symhists.rb_node;
720 struct rb_node *parent = NULL;
721 struct symhist *iter;
722
723 while (*p != NULL) {
724 parent = *p;
725 iter = rb_entry(parent, struct symhist, rb_node);
726
727 /* Reverse order */
728 if (sh->count > iter->count)
729 p = &(*p)->rb_left;
730 else
731 p = &(*p)->rb_right;
732 }
733
734 rb_link_node(&sh->rb_node, parent, p);
735 rb_insert_color(&sh->rb_node, &global_symhists);
736}
737
738static void threads__sort_symhists(void)
739{
740 struct rb_node *nd;
741
742 for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
743 struct thread *thread = rb_entry(nd, struct thread, rb_node);
744 struct rb_node *next = rb_first(&thread->symhists);
745
746 while (next) {
747 struct symhist *n = rb_entry(next, struct symhist,
748 rb_node);
749 next = rb_next(&n->rb_node);
750 rb_erase(&n->rb_node, &thread->symhists);
751 threads__insert_symhist(n);
752 }
753
754 }
755}
756
757static size_t threads__symhists_fprintf(uint64_t total_samples, FILE *fp)
758{
759 struct rb_node *nd;
760 size_t ret = 0;
761
762 for (nd = rb_first(&global_symhists); nd; nd = rb_next(nd)) {
763 struct symhist *pos = rb_entry(nd, struct symhist, rb_node);
764 ret += symhist__fprintf(pos, total_samples, fp);
765 }
766
767 return ret;
768}
769
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200770static int __cmd_report(void)
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300771{
772 unsigned long offset = 0;
773 unsigned long head = 0;
774 struct stat stat;
775 char *buf;
776 event_t *event;
777 int ret, rc = EXIT_FAILURE;
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200778 uint32_t size;
Ingo Molnarf49515b2009-05-26 19:03:36 +0200779 unsigned long total = 0, total_mmap = 0, total_comm = 0, total_unknown = 0;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300780
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300781 input = open(input_name, O_RDONLY);
782 if (input < 0) {
783 perror("failed to open file");
784 exit(-1);
785 }
786
787 ret = fstat(input, &stat);
788 if (ret < 0) {
789 perror("failed to stat file");
790 exit(-1);
791 }
792
793 if (!stat.st_size) {
794 fprintf(stderr, "zero-sized file, nothing to do!\n");
795 exit(0);
796 }
797
798 if (load_kallsyms() < 0) {
799 perror("failed to open kallsyms");
800 return EXIT_FAILURE;
801 }
802
803remap:
804 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
805 MAP_SHARED, input, offset);
806 if (buf == MAP_FAILED) {
807 perror("failed to mmap file");
808 exit(-1);
809 }
810
811more:
812 event = (event_t *)(buf + head);
813
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200814 size = event->header.size;
815 if (!size)
816 size = 8;
817
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300818 if (head + event->header.size >= page_size * mmap_window) {
819 unsigned long shift = page_size * (head / page_size);
820 int ret;
821
822 ret = munmap(buf, page_size * mmap_window);
823 assert(ret == 0);
824
825 offset += shift;
826 head -= shift;
827 goto remap;
828 }
829
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200830 size = event->header.size;
831 if (!size)
832 goto broken_event;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300833
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300834 if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
835 char level;
836 int show = 0;
837 struct dso *dso = NULL;
838 struct thread *thread = threads__findnew(event->ip.pid);
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200839 uint64_t ip = event->ip.ip;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300840
Ingo Molnar97b07b62009-05-26 18:48:58 +0200841 if (dump_trace) {
Ingo Molnarf49515b2009-05-26 19:03:36 +0200842 fprintf(stderr, "%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
843 (void *)(offset + head),
844 (void *)(long)(event->header.size),
Ingo Molnar97b07b62009-05-26 18:48:58 +0200845 event->header.misc,
846 event->ip.pid,
Ingo Molnar16f762a2009-05-27 09:10:38 +0200847 (void *)(long)ip);
Ingo Molnar97b07b62009-05-26 18:48:58 +0200848 }
849
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300850 if (thread == NULL) {
851 fprintf(stderr, "problem processing %d event, bailing out\n",
852 event->header.type);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300853 goto done;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300854 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300855
856 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
857 show = SHOW_KERNEL;
858 level = 'k';
859 dso = kernel_dso;
860 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
Ingo Molnar16f762a2009-05-27 09:10:38 +0200861 struct map *map;
862
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300863 show = SHOW_USER;
864 level = '.';
Ingo Molnar16f762a2009-05-27 09:10:38 +0200865
866 map = thread__find_map(thread, ip);
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200867 if (map != NULL) {
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300868 dso = map->dso;
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200869 ip -= map->start + map->pgoff;
870 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300871 } else {
872 show = SHOW_HV;
873 level = 'H';
874 }
875
876 if (show & show_mask) {
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200877 struct symbol *sym = dso__find_symbol(dso, ip);
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300878
Peter Zijlstraf17e04a2009-05-26 15:30:22 +0200879 if (thread__symbol_incnew(thread, sym, ip, dso, level)) {
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300880 fprintf(stderr, "problem incrementing symbol count, bailing out\n");
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300881 goto done;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300882 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300883 }
884 total++;
885 } else switch (event->header.type) {
886 case PERF_EVENT_MMAP: {
887 struct thread *thread = threads__findnew(event->mmap.pid);
888 struct map *map = map__new(&event->mmap);
889
Ingo Molnar97b07b62009-05-26 18:48:58 +0200890 if (dump_trace) {
Ingo Molnarf49515b2009-05-26 19:03:36 +0200891 fprintf(stderr, "%p [%p]: PERF_EVENT_MMAP: [%p(%p) @ %p]: %s\n",
892 (void *)(offset + head),
893 (void *)(long)(event->header.size),
Ingo Molnar16f762a2009-05-27 09:10:38 +0200894 (void *)(long)event->mmap.start,
895 (void *)(long)event->mmap.len,
896 (void *)(long)event->mmap.pgoff,
Ingo Molnar97b07b62009-05-26 18:48:58 +0200897 event->mmap.filename);
898 }
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300899 if (thread == NULL || map == NULL) {
900 fprintf(stderr, "problem processing PERF_EVENT_MMAP, bailing out\n");
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300901 goto done;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300902 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300903 thread__insert_map(thread, map);
Ingo Molnar97b07b62009-05-26 18:48:58 +0200904 total_mmap++;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300905 break;
906 }
907 case PERF_EVENT_COMM: {
908 struct thread *thread = threads__findnew(event->comm.pid);
909
Ingo Molnar97b07b62009-05-26 18:48:58 +0200910 if (dump_trace) {
Ingo Molnarf49515b2009-05-26 19:03:36 +0200911 fprintf(stderr, "%p [%p]: PERF_EVENT_COMM: %s:%d\n",
912 (void *)(offset + head),
913 (void *)(long)(event->header.size),
Ingo Molnar97b07b62009-05-26 18:48:58 +0200914 event->comm.comm, event->comm.pid);
915 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300916 if (thread == NULL ||
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300917 thread__set_comm(thread, event->comm.comm)) {
918 fprintf(stderr, "problem processing PERF_EVENT_COMM, bailing out\n");
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300919 goto done;
Arnaldo Carvalho de Meloce7e4362009-05-19 09:30:23 -0300920 }
Ingo Molnar97b07b62009-05-26 18:48:58 +0200921 total_comm++;
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300922 break;
923 }
Ingo Molnar97b07b62009-05-26 18:48:58 +0200924 default: {
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200925broken_event:
Ingo Molnarf49515b2009-05-26 19:03:36 +0200926 fprintf(stderr, "%p [%p]: skipping unknown header type: %d\n",
927 (void *)(offset + head),
928 (void *)(long)(event->header.size),
Ingo Molnar97b07b62009-05-26 18:48:58 +0200929 event->header.type);
Ingo Molnar3e706112009-05-26 18:53:17 +0200930 total_unknown++;
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200931
932 /*
933 * assume we lost track of the stream, check alignment, and
934 * increment a single u64 in the hope to catch on again 'soon'.
935 */
936
937 if (unlikely(head & 7))
938 head &= ~7ULL;
939
940 size = 8;
Ingo Molnar97b07b62009-05-26 18:48:58 +0200941 }
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300942 }
943
Peter Zijlstra6142f9e2009-05-26 20:51:47 +0200944 head += size;
Ingo Molnarf49515b2009-05-26 19:03:36 +0200945
Arnaldo Carvalho de Melo8fa66bd2009-05-18 12:45:42 -0300946 if (offset + head < stat.st_size)
947 goto more;
948
949 rc = EXIT_SUCCESS;
950done:
951 close(input);
Ingo Molnar97b07b62009-05-26 18:48:58 +0200952
953 if (dump_trace) {
Ingo Molnar3e706112009-05-26 18:53:17 +0200954 fprintf(stderr, " IP events: %10ld\n", total);
955 fprintf(stderr, " mmap events: %10ld\n", total_mmap);
956 fprintf(stderr, " comm events: %10ld\n", total_comm);
957 fprintf(stderr, " unknown events: %10ld\n", total_unknown);
Ingo Molnar97b07b62009-05-26 18:48:58 +0200958
959 return 0;
960 }
961
Ingo Molnar16f762a2009-05-27 09:10:38 +0200962 if (verbose >= 2) {
963 dsos__fprintf(stdout);
964 threads__fprintf(stdout);
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
Ingo Molnara930d2c2009-05-27 09:50:13 +0200996 setup_pager();
997
Ingo Molnar53cb8bc2009-05-26 09:17:18 +0200998 return __cmd_report();
999}