]>
Commit | Line | Data |
---|---|---|
53cb8bc2 IM |
1 | #include "util/util.h" |
2 | ||
3 | #include <libelf.h> | |
62eb9390 ACM |
4 | #include <gelf.h> |
5 | #include <elf.h> | |
53cb8bc2 | 6 | |
35a50c8a ACM |
7 | #include "util/list.h" |
8 | #include "util/rbtree.h" | |
8fa66bdc | 9 | |
53cb8bc2 IM |
10 | #include "perf.h" |
11 | ||
12 | #include "util/parse-options.h" | |
13 | #include "util/parse-events.h" | |
14 | ||
8fa66bdc ACM |
15 | #define SHOW_KERNEL 1 |
16 | #define SHOW_USER 2 | |
17 | #define SHOW_HV 4 | |
18 | ||
53cb8bc2 | 19 | static char const *input_name = "output.perf"; |
8fa66bdc ACM |
20 | static int input; |
21 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | |
22 | ||
97b07b69 IM |
23 | static int dump_trace = 0; |
24 | ||
8fa66bdc ACM |
25 | static unsigned long page_size; |
26 | static unsigned long mmap_window = 32; | |
27 | ||
53cb8bc2 | 28 | const char *perf_event_names[] = { |
8fa66bdc ACM |
29 | [PERF_EVENT_MMAP] = " PERF_EVENT_MMAP", |
30 | [PERF_EVENT_MUNMAP] = " PERF_EVENT_MUNMAP", | |
31 | [PERF_EVENT_COMM] = " PERF_EVENT_COMM", | |
32 | }; | |
33 | ||
34 | struct ip_event { | |
35 | struct perf_event_header header; | |
36 | __u64 ip; | |
37 | __u32 pid, tid; | |
38 | }; | |
39 | struct mmap_event { | |
40 | struct perf_event_header header; | |
41 | __u32 pid, tid; | |
42 | __u64 start; | |
43 | __u64 len; | |
44 | __u64 pgoff; | |
45 | char filename[PATH_MAX]; | |
46 | }; | |
47 | struct comm_event { | |
48 | struct perf_event_header header; | |
49 | __u32 pid,tid; | |
50 | char comm[16]; | |
51 | }; | |
52 | ||
53 | typedef union event_union { | |
54 | struct perf_event_header header; | |
55 | struct ip_event ip; | |
56 | struct mmap_event mmap; | |
57 | struct comm_event comm; | |
58 | } event_t; | |
59 | ||
8fa66bdc | 60 | struct symbol { |
35a50c8a ACM |
61 | struct rb_node rb_node; |
62 | uint64_t start; | |
63 | uint64_t end; | |
64 | char name[0]; | |
8fa66bdc ACM |
65 | }; |
66 | ||
67 | static struct symbol *symbol__new(uint64_t start, uint64_t len, const char *name) | |
68 | { | |
69 | struct symbol *self = malloc(sizeof(*self) + strlen(name) + 1); | |
70 | ||
71 | if (self != NULL) { | |
72 | self->start = start; | |
73 | self->end = start + len; | |
74 | strcpy(self->name, name); | |
75 | } | |
76 | ||
77 | return self; | |
78 | } | |
79 | ||
80 | static void symbol__delete(struct symbol *self) | |
81 | { | |
82 | free(self); | |
83 | } | |
84 | ||
85 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | |
86 | { | |
87 | return fprintf(fp, " %lx-%lx %s\n", | |
88 | self->start, self->end, self->name); | |
89 | } | |
90 | ||
91 | struct dso { | |
92 | struct list_head node; | |
35a50c8a | 93 | struct rb_root syms; |
8fa66bdc ACM |
94 | char name[0]; |
95 | }; | |
96 | ||
97 | static struct dso *dso__new(const char *name) | |
98 | { | |
99 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); | |
100 | ||
101 | if (self != NULL) { | |
102 | strcpy(self->name, name); | |
35a50c8a | 103 | self->syms = RB_ROOT; |
8fa66bdc ACM |
104 | } |
105 | ||
106 | return self; | |
107 | } | |
108 | ||
8fa66bdc ACM |
109 | static void dso__delete_symbols(struct dso *self) |
110 | { | |
35a50c8a ACM |
111 | struct symbol *pos; |
112 | struct rb_node *next = rb_first(&self->syms); | |
8fa66bdc | 113 | |
35a50c8a ACM |
114 | while (next) { |
115 | pos = rb_entry(next, struct symbol, rb_node); | |
116 | next = rb_next(&pos->rb_node); | |
8fa66bdc | 117 | symbol__delete(pos); |
35a50c8a | 118 | } |
8fa66bdc ACM |
119 | } |
120 | ||
121 | static void dso__delete(struct dso *self) | |
122 | { | |
8fa66bdc ACM |
123 | dso__delete_symbols(self); |
124 | free(self); | |
125 | } | |
126 | ||
127 | static void dso__insert_symbol(struct dso *self, struct symbol *sym) | |
128 | { | |
35a50c8a ACM |
129 | struct rb_node **p = &self->syms.rb_node; |
130 | struct rb_node *parent = NULL; | |
131 | const uint64_t ip = sym->start; | |
132 | struct symbol *s; | |
133 | ||
134 | while (*p != NULL) { | |
135 | parent = *p; | |
136 | s = rb_entry(parent, struct symbol, rb_node); | |
137 | if (ip < s->start) | |
138 | p = &(*p)->rb_left; | |
139 | else | |
140 | p = &(*p)->rb_right; | |
141 | } | |
142 | rb_link_node(&sym->rb_node, parent, p); | |
143 | rb_insert_color(&sym->rb_node, &self->syms); | |
8fa66bdc ACM |
144 | } |
145 | ||
146 | static struct symbol *dso__find_symbol(struct dso *self, uint64_t ip) | |
147 | { | |
148 | if (self == NULL) | |
149 | return NULL; | |
150 | ||
35a50c8a | 151 | struct rb_node *n = self->syms.rb_node; |
8fa66bdc | 152 | |
35a50c8a ACM |
153 | while (n) { |
154 | struct symbol *s = rb_entry(n, struct symbol, rb_node); | |
155 | ||
156 | if (ip < s->start) | |
157 | n = n->rb_left; | |
158 | else if (ip > s->end) | |
159 | n = n->rb_right; | |
160 | else | |
161 | return s; | |
162 | } | |
8fa66bdc ACM |
163 | |
164 | return NULL; | |
165 | } | |
166 | ||
62eb9390 ACM |
167 | /** |
168 | * elf_symtab__for_each_symbol - iterate thru all the symbols | |
169 | * | |
170 | * @self: struct elf_symtab instance to iterate | |
171 | * @index: uint32_t index | |
172 | * @sym: GElf_Sym iterator | |
173 | */ | |
174 | #define elf_symtab__for_each_symbol(syms, nr_syms, index, sym) \ | |
175 | for (index = 0, gelf_getsym(syms, index, &sym);\ | |
176 | index < nr_syms; \ | |
177 | index++, gelf_getsym(syms, index, &sym)) | |
178 | ||
179 | static inline uint8_t elf_sym__type(const GElf_Sym *sym) | |
180 | { | |
181 | return GELF_ST_TYPE(sym->st_info); | |
182 | } | |
183 | ||
53cb8bc2 | 184 | static inline int elf_sym__is_function(const GElf_Sym *sym) |
62eb9390 ACM |
185 | { |
186 | return elf_sym__type(sym) == STT_FUNC && | |
187 | sym->st_name != 0 && | |
188 | sym->st_shndx != SHN_UNDEF; | |
189 | } | |
190 | ||
191 | static inline const char *elf_sym__name(const GElf_Sym *sym, | |
192 | const Elf_Data *symstrs) | |
193 | { | |
194 | return symstrs->d_buf + sym->st_name; | |
195 | } | |
196 | ||
197 | static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |
198 | GElf_Shdr *shp, const char *name, | |
199 | size_t *index) | |
200 | { | |
201 | Elf_Scn *sec = NULL; | |
202 | size_t cnt = 1; | |
203 | ||
204 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | |
205 | char *str; | |
206 | ||
207 | gelf_getshdr(sec, shp); | |
208 | str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); | |
209 | if (!strcmp(name, str)) { | |
210 | if (index) | |
211 | *index = cnt; | |
212 | break; | |
213 | } | |
214 | ++cnt; | |
215 | } | |
216 | ||
217 | return sec; | |
218 | } | |
219 | ||
8fa66bdc ACM |
220 | static int dso__load(struct dso *self) |
221 | { | |
62eb9390 ACM |
222 | int fd = open(self->name, O_RDONLY), err = -1; |
223 | ||
224 | if (fd == -1) | |
225 | return -1; | |
226 | ||
227 | Elf *elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | |
228 | if (elf == NULL) { | |
229 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | |
230 | __func__, self->name); | |
231 | goto out_close; | |
232 | } | |
233 | ||
234 | GElf_Ehdr ehdr; | |
235 | if (gelf_getehdr(elf, &ehdr) == NULL) { | |
236 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | |
237 | goto out_elf_end; | |
238 | } | |
239 | ||
240 | GElf_Shdr shdr; | |
241 | Elf_Scn *sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | |
242 | if (sec == NULL) | |
243 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); | |
244 | ||
245 | if (sec == NULL) | |
246 | goto out_elf_end; | |
247 | ||
62eb9390 ACM |
248 | Elf_Data *syms = elf_getdata(sec, NULL); |
249 | if (syms == NULL) | |
250 | goto out_elf_end; | |
251 | ||
252 | sec = elf_getscn(elf, shdr.sh_link); | |
253 | if (sec == NULL) | |
254 | goto out_elf_end; | |
255 | ||
256 | Elf_Data *symstrs = elf_getdata(sec, NULL); | |
257 | if (symstrs == NULL) | |
258 | goto out_elf_end; | |
259 | ||
260 | const uint32_t nr_syms = shdr.sh_size / shdr.sh_entsize; | |
261 | ||
262 | GElf_Sym sym; | |
263 | uint32_t index; | |
264 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { | |
f17e04af PZ |
265 | struct symbol *f; |
266 | ||
62eb9390 ACM |
267 | if (!elf_sym__is_function(&sym)) |
268 | continue; | |
f17e04af PZ |
269 | |
270 | sec = elf_getscn(elf, sym.st_shndx); | |
271 | if (!sec) | |
272 | goto out_elf_end; | |
273 | ||
274 | gelf_getshdr(sec, &shdr); | |
275 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | |
276 | ||
277 | f = symbol__new(sym.st_value, sym.st_size, | |
278 | elf_sym__name(&sym, symstrs)); | |
279 | if (!f) | |
62eb9390 ACM |
280 | goto out_elf_end; |
281 | ||
282 | dso__insert_symbol(self, f); | |
283 | } | |
284 | ||
285 | err = 0; | |
286 | out_elf_end: | |
287 | elf_end(elf); | |
288 | out_close: | |
289 | close(fd); | |
290 | return err; | |
8fa66bdc ACM |
291 | } |
292 | ||
293 | static size_t dso__fprintf(struct dso *self, FILE *fp) | |
294 | { | |
8fa66bdc ACM |
295 | size_t ret = fprintf(fp, "dso: %s\n", self->name); |
296 | ||
35a50c8a ACM |
297 | struct rb_node *nd; |
298 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { | |
299 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | |
8fa66bdc | 300 | ret += symbol__fprintf(pos, fp); |
35a50c8a | 301 | } |
8fa66bdc ACM |
302 | |
303 | return ret; | |
304 | } | |
305 | ||
306 | static LIST_HEAD(dsos); | |
307 | static struct dso *kernel_dso; | |
308 | ||
309 | static void dsos__add(struct dso *dso) | |
310 | { | |
311 | list_add_tail(&dso->node, &dsos); | |
312 | } | |
313 | ||
314 | static struct dso *dsos__find(const char *name) | |
315 | { | |
316 | struct dso *pos; | |
317 | ||
318 | list_for_each_entry(pos, &dsos, node) | |
319 | if (strcmp(pos->name, name) == 0) | |
320 | return pos; | |
321 | return NULL; | |
322 | } | |
323 | ||
324 | static struct dso *dsos__findnew(const char *name) | |
325 | { | |
326 | struct dso *dso = dsos__find(name); | |
327 | ||
328 | if (dso == NULL) { | |
329 | dso = dso__new(name); | |
330 | if (dso != NULL && dso__load(dso) < 0) | |
331 | goto out_delete_dso; | |
332 | ||
333 | dsos__add(dso); | |
334 | } | |
335 | ||
336 | return dso; | |
337 | ||
338 | out_delete_dso: | |
339 | dso__delete(dso); | |
340 | return NULL; | |
341 | } | |
342 | ||
53cb8bc2 | 343 | void dsos__fprintf(FILE *fp) |
8fa66bdc ACM |
344 | { |
345 | struct dso *pos; | |
346 | ||
347 | list_for_each_entry(pos, &dsos, node) | |
348 | dso__fprintf(pos, fp); | |
349 | } | |
350 | ||
351 | static int load_kallsyms(void) | |
352 | { | |
353 | kernel_dso = dso__new("[kernel]"); | |
354 | if (kernel_dso == NULL) | |
355 | return -1; | |
356 | ||
357 | FILE *file = fopen("/proc/kallsyms", "r"); | |
358 | ||
359 | if (file == NULL) | |
360 | goto out_delete_dso; | |
361 | ||
362 | char *line = NULL; | |
363 | size_t n; | |
364 | ||
365 | while (!feof(file)) { | |
366 | unsigned long long start; | |
abd54f68 | 367 | char c, symbf[4096]; |
8fa66bdc ACM |
368 | |
369 | if (getline(&line, &n, file) < 0) | |
370 | break; | |
371 | ||
372 | if (!line) | |
373 | goto out_delete_dso; | |
374 | ||
375 | if (sscanf(line, "%llx %c %s", &start, &c, symbf) == 3) { | |
abd54f68 ACM |
376 | /* |
377 | * Well fix up the end later, when we have all sorted. | |
378 | */ | |
379 | struct symbol *sym = symbol__new(start, 0xdead, symbf); | |
380 | ||
381 | if (sym == NULL) | |
382 | goto out_delete_dso; | |
383 | ||
384 | dso__insert_symbol(kernel_dso, sym); | |
8fa66bdc ACM |
385 | } |
386 | } | |
387 | ||
abd54f68 ACM |
388 | /* |
389 | * Now that we have all sorted out, just set the ->end of all | |
390 | * symbols | |
391 | */ | |
392 | struct rb_node *nd, *prevnd = rb_first(&kernel_dso->syms); | |
393 | ||
394 | if (prevnd == NULL) | |
395 | goto out_delete_line; | |
396 | ||
397 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | |
398 | struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node), | |
399 | *curr = rb_entry(nd, struct symbol, rb_node); | |
400 | ||
401 | prev->end = curr->start - 1; | |
402 | prevnd = nd; | |
403 | } | |
404 | ||
8fa66bdc ACM |
405 | dsos__add(kernel_dso); |
406 | free(line); | |
407 | fclose(file); | |
408 | return 0; | |
409 | ||
59d81029 ACM |
410 | out_delete_line: |
411 | free(line); | |
8fa66bdc ACM |
412 | out_delete_dso: |
413 | dso__delete(kernel_dso); | |
414 | return -1; | |
415 | } | |
416 | ||
417 | struct map { | |
418 | struct list_head node; | |
419 | uint64_t start; | |
420 | uint64_t end; | |
421 | uint64_t pgoff; | |
422 | struct dso *dso; | |
423 | }; | |
424 | ||
425 | static struct map *map__new(struct mmap_event *event) | |
426 | { | |
427 | struct map *self = malloc(sizeof(*self)); | |
428 | ||
429 | if (self != NULL) { | |
430 | self->start = event->start; | |
431 | self->end = event->start + event->len; | |
432 | self->pgoff = event->pgoff; | |
433 | ||
434 | self->dso = dsos__findnew(event->filename); | |
435 | if (self->dso == NULL) | |
436 | goto out_delete; | |
437 | } | |
438 | return self; | |
439 | out_delete: | |
440 | free(self); | |
441 | return NULL; | |
442 | } | |
443 | ||
444 | static size_t map__fprintf(struct map *self, FILE *fp) | |
445 | { | |
446 | return fprintf(fp, " %lx-%lx %lx %s\n", | |
447 | self->start, self->end, self->pgoff, self->dso->name); | |
448 | } | |
449 | ||
450 | struct symhist { | |
ce7e4365 | 451 | struct rb_node rb_node; |
8fa66bdc ACM |
452 | struct dso *dso; |
453 | struct symbol *sym; | |
ce7e4365 | 454 | uint64_t ip; |
8fa66bdc ACM |
455 | uint32_t count; |
456 | char level; | |
457 | }; | |
458 | ||
ce7e4365 ACM |
459 | static struct symhist *symhist__new(struct symbol *sym, uint64_t ip, |
460 | struct dso *dso, char level) | |
8fa66bdc ACM |
461 | { |
462 | struct symhist *self = malloc(sizeof(*self)); | |
463 | ||
464 | if (self != NULL) { | |
465 | self->sym = sym; | |
ce7e4365 | 466 | self->ip = ip; |
8fa66bdc ACM |
467 | self->dso = dso; |
468 | self->level = level; | |
ce7e4365 | 469 | self->count = 1; |
8fa66bdc ACM |
470 | } |
471 | ||
472 | return self; | |
473 | } | |
474 | ||
53cb8bc2 | 475 | void symhist__delete(struct symhist *self) |
8fa66bdc ACM |
476 | { |
477 | free(self); | |
478 | } | |
479 | ||
8fa66bdc ACM |
480 | static void symhist__inc(struct symhist *self) |
481 | { | |
482 | ++self->count; | |
483 | } | |
484 | ||
485 | static size_t symhist__fprintf(struct symhist *self, FILE *fp) | |
486 | { | |
ce7e4365 | 487 | size_t ret = fprintf(fp, "%#llx [%c] ", (unsigned long long)self->ip, self->level); |
8fa66bdc ACM |
488 | |
489 | if (self->level != '.') | |
f3e08c53 | 490 | ret += fprintf(fp, "%s", self->sym ? self->sym->name: "<unknown>"); |
8fa66bdc ACM |
491 | else |
492 | ret += fprintf(fp, "%s: %s", | |
f17e04af | 493 | self->dso ? self->dso->name : "<unknown>", |
8fa66bdc ACM |
494 | self->sym ? self->sym->name : "<unknown>"); |
495 | return ret + fprintf(fp, ": %u\n", self->count); | |
496 | } | |
497 | ||
498 | struct thread { | |
ce7e4365 | 499 | struct rb_node rb_node; |
8fa66bdc | 500 | struct list_head maps; |
ce7e4365 | 501 | struct rb_root symhists; |
8fa66bdc ACM |
502 | pid_t pid; |
503 | char *comm; | |
504 | }; | |
505 | ||
506 | static struct thread *thread__new(pid_t pid) | |
507 | { | |
508 | struct thread *self = malloc(sizeof(*self)); | |
509 | ||
510 | if (self != NULL) { | |
511 | self->pid = pid; | |
512 | self->comm = NULL; | |
513 | INIT_LIST_HEAD(&self->maps); | |
ce7e4365 | 514 | self->symhists = RB_ROOT; |
8fa66bdc ACM |
515 | } |
516 | ||
517 | return self; | |
518 | } | |
519 | ||
ce7e4365 ACM |
520 | static int thread__symbol_incnew(struct thread *self, struct symbol *sym, |
521 | uint64_t ip, struct dso *dso, char level) | |
8fa66bdc | 522 | { |
ce7e4365 ACM |
523 | struct rb_node **p = &self->symhists.rb_node; |
524 | struct rb_node *parent = NULL; | |
525 | struct symhist *sh; | |
8fa66bdc | 526 | |
ce7e4365 ACM |
527 | while (*p != NULL) { |
528 | parent = *p; | |
529 | sh = rb_entry(parent, struct symhist, rb_node); | |
8fa66bdc | 530 | |
ce7e4365 ACM |
531 | if (sh->sym == sym || ip == sh->ip) { |
532 | symhist__inc(sh); | |
533 | return 0; | |
534 | } | |
8fa66bdc | 535 | |
ce7e4365 ACM |
536 | /* Handle unresolved symbols too */ |
537 | const uint64_t start = !sh->sym ? sh->ip : sh->sym->start; | |
8fa66bdc | 538 | |
ce7e4365 ACM |
539 | if (ip < start) |
540 | p = &(*p)->rb_left; | |
541 | else | |
542 | p = &(*p)->rb_right; | |
8fa66bdc ACM |
543 | } |
544 | ||
ce7e4365 ACM |
545 | sh = symhist__new(sym, ip, dso, level); |
546 | if (sh == NULL) | |
547 | return -ENOMEM; | |
548 | rb_link_node(&sh->rb_node, parent, p); | |
549 | rb_insert_color(&sh->rb_node, &self->symhists); | |
8fa66bdc | 550 | return 0; |
8fa66bdc ACM |
551 | } |
552 | ||
553 | static int thread__set_comm(struct thread *self, const char *comm) | |
554 | { | |
555 | self->comm = strdup(comm); | |
556 | return self->comm ? 0 : -ENOMEM; | |
557 | } | |
558 | ||
53cb8bc2 | 559 | size_t thread__maps_fprintf(struct thread *self, FILE *fp) |
8fa66bdc ACM |
560 | { |
561 | struct map *pos; | |
562 | size_t ret = 0; | |
563 | ||
564 | list_for_each_entry(pos, &self->maps, node) | |
565 | ret += map__fprintf(pos, fp); | |
566 | ||
567 | return ret; | |
568 | } | |
569 | ||
570 | static size_t thread__fprintf(struct thread *self, FILE *fp) | |
571 | { | |
8fa66bdc | 572 | int ret = fprintf(fp, "thread: %d %s\n", self->pid, self->comm); |
ce7e4365 | 573 | struct rb_node *nd; |
8fa66bdc | 574 | |
ce7e4365 ACM |
575 | for (nd = rb_first(&self->symhists); nd; nd = rb_next(nd)) { |
576 | struct symhist *pos = rb_entry(nd, struct symhist, rb_node); | |
8fa66bdc | 577 | ret += symhist__fprintf(pos, fp); |
ce7e4365 | 578 | } |
8fa66bdc ACM |
579 | |
580 | return ret; | |
581 | } | |
582 | ||
ce7e4365 | 583 | static struct rb_root threads = RB_ROOT; |
8fa66bdc | 584 | |
ce7e4365 | 585 | static struct thread *threads__findnew(pid_t pid) |
8fa66bdc | 586 | { |
ce7e4365 ACM |
587 | struct rb_node **p = &threads.rb_node; |
588 | struct rb_node *parent = NULL; | |
589 | struct thread *th; | |
8fa66bdc | 590 | |
ce7e4365 ACM |
591 | while (*p != NULL) { |
592 | parent = *p; | |
593 | th = rb_entry(parent, struct thread, rb_node); | |
8fa66bdc | 594 | |
ce7e4365 ACM |
595 | if (th->pid == pid) |
596 | return th; | |
8fa66bdc | 597 | |
ce7e4365 ACM |
598 | if (pid < th->pid) |
599 | p = &(*p)->rb_left; | |
600 | else | |
601 | p = &(*p)->rb_right; | |
8fa66bdc ACM |
602 | } |
603 | ||
ce7e4365 ACM |
604 | th = thread__new(pid); |
605 | if (th != NULL) { | |
606 | rb_link_node(&th->rb_node, parent, p); | |
607 | rb_insert_color(&th->rb_node, &threads); | |
608 | } | |
609 | return th; | |
8fa66bdc ACM |
610 | } |
611 | ||
612 | static void thread__insert_map(struct thread *self, struct map *map) | |
613 | { | |
614 | list_add_tail(&map->node, &self->maps); | |
615 | } | |
616 | ||
617 | static struct map *thread__find_map(struct thread *self, uint64_t ip) | |
618 | { | |
619 | if (self == NULL) | |
620 | return NULL; | |
621 | ||
622 | struct map *pos; | |
623 | ||
624 | list_for_each_entry(pos, &self->maps, node) | |
625 | if (ip >= pos->start && ip <= pos->end) | |
626 | return pos; | |
627 | ||
628 | return NULL; | |
629 | } | |
630 | ||
631 | static void threads__fprintf(FILE *fp) | |
632 | { | |
ce7e4365 ACM |
633 | struct rb_node *nd; |
634 | for (nd = rb_first(&threads); nd; nd = rb_next(nd)) { | |
635 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | |
8fa66bdc | 636 | thread__fprintf(pos, fp); |
ce7e4365 | 637 | } |
8fa66bdc ACM |
638 | } |
639 | ||
53cb8bc2 | 640 | static int __cmd_report(void) |
8fa66bdc ACM |
641 | { |
642 | unsigned long offset = 0; | |
643 | unsigned long head = 0; | |
644 | struct stat stat; | |
645 | char *buf; | |
646 | event_t *event; | |
647 | int ret, rc = EXIT_FAILURE; | |
f49515b1 | 648 | unsigned long total = 0, total_mmap = 0, total_comm = 0, total_unknown = 0; |
8fa66bdc | 649 | |
8fa66bdc ACM |
650 | input = open(input_name, O_RDONLY); |
651 | if (input < 0) { | |
652 | perror("failed to open file"); | |
653 | exit(-1); | |
654 | } | |
655 | ||
656 | ret = fstat(input, &stat); | |
657 | if (ret < 0) { | |
658 | perror("failed to stat file"); | |
659 | exit(-1); | |
660 | } | |
661 | ||
662 | if (!stat.st_size) { | |
663 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | |
664 | exit(0); | |
665 | } | |
666 | ||
667 | if (load_kallsyms() < 0) { | |
668 | perror("failed to open kallsyms"); | |
669 | return EXIT_FAILURE; | |
670 | } | |
671 | ||
672 | remap: | |
673 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, | |
674 | MAP_SHARED, input, offset); | |
675 | if (buf == MAP_FAILED) { | |
676 | perror("failed to mmap file"); | |
677 | exit(-1); | |
678 | } | |
679 | ||
680 | more: | |
681 | event = (event_t *)(buf + head); | |
682 | ||
683 | if (head + event->header.size >= page_size * mmap_window) { | |
684 | unsigned long shift = page_size * (head / page_size); | |
685 | int ret; | |
686 | ||
687 | ret = munmap(buf, page_size * mmap_window); | |
688 | assert(ret == 0); | |
689 | ||
690 | offset += shift; | |
691 | head -= shift; | |
692 | goto remap; | |
693 | } | |
694 | ||
695 | ||
696 | if (!event->header.size) { | |
697 | fprintf(stderr, "zero-sized event at file offset %ld\n", offset + head); | |
698 | fprintf(stderr, "skipping %ld bytes of events.\n", stat.st_size - offset - head); | |
699 | goto done; | |
700 | } | |
701 | ||
8fa66bdc ACM |
702 | if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) { |
703 | char level; | |
704 | int show = 0; | |
705 | struct dso *dso = NULL; | |
706 | struct thread *thread = threads__findnew(event->ip.pid); | |
f17e04af | 707 | uint64_t ip = event->ip.ip; |
8fa66bdc | 708 | |
97b07b69 | 709 | if (dump_trace) { |
f49515b1 IM |
710 | fprintf(stderr, "%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", |
711 | (void *)(offset + head), | |
712 | (void *)(long)(event->header.size), | |
97b07b69 IM |
713 | event->header.misc, |
714 | event->ip.pid, | |
715 | (void *)event->ip.ip); | |
716 | } | |
717 | ||
ce7e4365 ACM |
718 | if (thread == NULL) { |
719 | fprintf(stderr, "problem processing %d event, bailing out\n", | |
720 | event->header.type); | |
8fa66bdc | 721 | goto done; |
ce7e4365 | 722 | } |
8fa66bdc ACM |
723 | |
724 | if (event->header.misc & PERF_EVENT_MISC_KERNEL) { | |
725 | show = SHOW_KERNEL; | |
726 | level = 'k'; | |
727 | dso = kernel_dso; | |
728 | } else if (event->header.misc & PERF_EVENT_MISC_USER) { | |
729 | show = SHOW_USER; | |
730 | level = '.'; | |
f17e04af PZ |
731 | struct map *map = thread__find_map(thread, ip); |
732 | if (map != NULL) { | |
8fa66bdc | 733 | dso = map->dso; |
f17e04af PZ |
734 | ip -= map->start + map->pgoff; |
735 | } | |
8fa66bdc ACM |
736 | } else { |
737 | show = SHOW_HV; | |
738 | level = 'H'; | |
739 | } | |
740 | ||
741 | if (show & show_mask) { | |
f17e04af | 742 | struct symbol *sym = dso__find_symbol(dso, ip); |
8fa66bdc | 743 | |
f17e04af | 744 | if (thread__symbol_incnew(thread, sym, ip, dso, level)) { |
ce7e4365 | 745 | fprintf(stderr, "problem incrementing symbol count, bailing out\n"); |
8fa66bdc | 746 | goto done; |
ce7e4365 | 747 | } |
8fa66bdc ACM |
748 | } |
749 | total++; | |
750 | } else switch (event->header.type) { | |
751 | case PERF_EVENT_MMAP: { | |
752 | struct thread *thread = threads__findnew(event->mmap.pid); | |
753 | struct map *map = map__new(&event->mmap); | |
754 | ||
97b07b69 | 755 | if (dump_trace) { |
f49515b1 IM |
756 | fprintf(stderr, "%p [%p]: PERF_EVENT_MMAP: [%p(%p) @ %p]: %s\n", |
757 | (void *)(offset + head), | |
758 | (void *)(long)(event->header.size), | |
97b07b69 IM |
759 | (void *)event->mmap.start, |
760 | (void *)event->mmap.len, | |
761 | (void *)event->mmap.pgoff, | |
762 | event->mmap.filename); | |
763 | } | |
ce7e4365 ACM |
764 | if (thread == NULL || map == NULL) { |
765 | fprintf(stderr, "problem processing PERF_EVENT_MMAP, bailing out\n"); | |
8fa66bdc | 766 | goto done; |
ce7e4365 | 767 | } |
8fa66bdc | 768 | thread__insert_map(thread, map); |
97b07b69 | 769 | total_mmap++; |
8fa66bdc ACM |
770 | break; |
771 | } | |
772 | case PERF_EVENT_COMM: { | |
773 | struct thread *thread = threads__findnew(event->comm.pid); | |
774 | ||
97b07b69 | 775 | if (dump_trace) { |
f49515b1 IM |
776 | fprintf(stderr, "%p [%p]: PERF_EVENT_COMM: %s:%d\n", |
777 | (void *)(offset + head), | |
778 | (void *)(long)(event->header.size), | |
97b07b69 IM |
779 | event->comm.comm, event->comm.pid); |
780 | } | |
8fa66bdc | 781 | if (thread == NULL || |
ce7e4365 ACM |
782 | thread__set_comm(thread, event->comm.comm)) { |
783 | fprintf(stderr, "problem processing PERF_EVENT_COMM, bailing out\n"); | |
8fa66bdc | 784 | goto done; |
ce7e4365 | 785 | } |
97b07b69 | 786 | total_comm++; |
8fa66bdc ACM |
787 | break; |
788 | } | |
97b07b69 | 789 | default: { |
f49515b1 IM |
790 | fprintf(stderr, "%p [%p]: skipping unknown header type: %d\n", |
791 | (void *)(offset + head), | |
792 | (void *)(long)(event->header.size), | |
97b07b69 | 793 | event->header.type); |
3e706114 | 794 | total_unknown++; |
97b07b69 | 795 | } |
8fa66bdc ACM |
796 | } |
797 | ||
f49515b1 IM |
798 | head += event->header.size; |
799 | ||
8fa66bdc ACM |
800 | if (offset + head < stat.st_size) |
801 | goto more; | |
802 | ||
803 | rc = EXIT_SUCCESS; | |
804 | done: | |
805 | close(input); | |
97b07b69 IM |
806 | |
807 | if (dump_trace) { | |
3e706114 IM |
808 | fprintf(stderr, " IP events: %10ld\n", total); |
809 | fprintf(stderr, " mmap events: %10ld\n", total_mmap); | |
810 | fprintf(stderr, " comm events: %10ld\n", total_comm); | |
811 | fprintf(stderr, " unknown events: %10ld\n", total_unknown); | |
97b07b69 IM |
812 | |
813 | return 0; | |
814 | } | |
815 | ||
8fa66bdc ACM |
816 | //dsos__fprintf(stdout); |
817 | threads__fprintf(stdout); | |
818 | #if 0 | |
819 | std::map<std::string, int>::iterator hi = hist.begin(); | |
820 | ||
821 | while (hi != hist.end()) { | |
822 | rev_hist.insert(std::pair<int, std::string>(hi->second, hi->first)); | |
823 | hist.erase(hi++); | |
824 | } | |
825 | ||
826 | std::multimap<int, std::string>::const_iterator ri = rev_hist.begin(); | |
827 | ||
828 | while (ri != rev_hist.end()) { | |
829 | printf(" %5.2f %s\n", (100.0 * ri->first)/total, ri->second.c_str()); | |
830 | ri++; | |
831 | } | |
832 | #endif | |
833 | return rc; | |
834 | } | |
835 | ||
53cb8bc2 IM |
836 | static const char * const report_usage[] = { |
837 | "perf report [<options>] <command>", | |
838 | NULL | |
839 | }; | |
840 | ||
841 | static const struct option options[] = { | |
842 | OPT_STRING('i', "input", &input_name, "file", | |
843 | "input file name"), | |
97b07b69 IM |
844 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
845 | "dump raw trace in ASCII"), | |
53cb8bc2 IM |
846 | OPT_END() |
847 | }; | |
848 | ||
849 | int cmd_report(int argc, const char **argv, const char *prefix) | |
850 | { | |
851 | elf_version(EV_CURRENT); | |
852 | ||
853 | page_size = getpagesize(); | |
854 | ||
855 | parse_options(argc, argv, options, report_usage, 0); | |
856 | ||
857 | return __cmd_report(); | |
858 | } |