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