perf_counter tools: Optionally pass a symbol filter to the dso load routines
[deliverable/linux.git] / Documentation / perf_counter / builtin-report.c
1 #include "util/util.h"
2 #include "builtin.h"
3
4 #include "util/list.h"
5 #include "util/cache.h"
6 #include "util/rbtree.h"
7 #include "util/symbol.h"
8
9 #include "perf.h"
10
11 #include "util/parse-options.h"
12 #include "util/parse-events.h"
13
14 #define SHOW_KERNEL 1
15 #define SHOW_USER 2
16 #define SHOW_HV 4
17
18 static char const *input_name = "perf.data";
19 static char *vmlinux = NULL;
20 static char *sort_order = "pid,symbol";
21 static int input;
22 static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
23
24 static int dump_trace = 0;
25 static int verbose;
26
27 static unsigned long page_size;
28 static unsigned long mmap_window = 32;
29
30 const char *perf_event_names[] = {
31 [PERF_EVENT_MMAP] = " PERF_EVENT_MMAP",
32 [PERF_EVENT_MUNMAP] = " PERF_EVENT_MUNMAP",
33 [PERF_EVENT_COMM] = " PERF_EVENT_COMM",
34 };
35
36 struct ip_event {
37 struct perf_event_header header;
38 __u64 ip;
39 __u32 pid, tid;
40 };
41 struct 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 };
49 struct comm_event {
50 struct perf_event_header header;
51 __u32 pid,tid;
52 char comm[16];
53 };
54
55 typedef 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
62 static LIST_HEAD(dsos);
63 static struct dso *kernel_dso;
64
65 static void dsos__add(struct dso *dso)
66 {
67 list_add_tail(&dso->node, &dsos);
68 }
69
70 static struct dso *dsos__find(const char *name)
71 {
72 struct dso *pos;
73
74 list_for_each_entry(pos, &dsos, node)
75 if (strcmp(pos->name, name) == 0)
76 return pos;
77 return NULL;
78 }
79
80 static struct dso *dsos__findnew(const char *name)
81 {
82 struct dso *dso = dsos__find(name);
83 int nr;
84
85 if (dso == NULL) {
86 dso = dso__new(name, 0);
87 if (!dso)
88 goto out_delete_dso;
89
90 nr = dso__load(dso, NULL);
91 if (nr < 0) {
92 fprintf(stderr, "Failed to open: %s\n", name);
93 goto out_delete_dso;
94 }
95 if (!nr) {
96 fprintf(stderr,
97 "Failed to find debug symbols for: %s, maybe install a debug package?\n",
98 name);
99 }
100
101 dsos__add(dso);
102 }
103
104 return dso;
105
106 out_delete_dso:
107 dso__delete(dso);
108 return NULL;
109 }
110
111 static void dsos__fprintf(FILE *fp)
112 {
113 struct dso *pos;
114
115 list_for_each_entry(pos, &dsos, node)
116 dso__fprintf(pos, fp);
117 }
118
119 static int load_kernel(void)
120 {
121 int err;
122
123 kernel_dso = dso__new("[kernel]", 0);
124 if (!kernel_dso)
125 return -1;
126
127 err = dso__load_kernel(kernel_dso, vmlinux, NULL);
128 if (err) {
129 dso__delete(kernel_dso);
130 kernel_dso = NULL;
131 } else
132 dsos__add(kernel_dso);
133
134 return err;
135 }
136
137 struct map {
138 struct list_head node;
139 uint64_t start;
140 uint64_t end;
141 uint64_t pgoff;
142 struct dso *dso;
143 };
144
145 static struct map *map__new(struct mmap_event *event)
146 {
147 struct map *self = malloc(sizeof(*self));
148
149 if (self != NULL) {
150 self->start = event->start;
151 self->end = event->start + event->len;
152 self->pgoff = event->pgoff;
153
154 self->dso = dsos__findnew(event->filename);
155 if (self->dso == NULL)
156 goto out_delete;
157 }
158 return self;
159 out_delete:
160 free(self);
161 return NULL;
162 }
163
164 struct thread;
165
166 struct thread {
167 struct rb_node rb_node;
168 struct list_head maps;
169 pid_t pid;
170 char *comm;
171 };
172
173 static struct thread *thread__new(pid_t pid)
174 {
175 struct thread *self = malloc(sizeof(*self));
176
177 if (self != NULL) {
178 self->pid = pid;
179 self->comm = NULL;
180 INIT_LIST_HEAD(&self->maps);
181 }
182
183 return self;
184 }
185
186 static int thread__set_comm(struct thread *self, const char *comm)
187 {
188 self->comm = strdup(comm);
189 return self->comm ? 0 : -ENOMEM;
190 }
191
192 static struct rb_root threads;
193
194 static struct thread *threads__findnew(pid_t pid)
195 {
196 struct rb_node **p = &threads.rb_node;
197 struct rb_node *parent = NULL;
198 struct thread *th;
199
200 while (*p != NULL) {
201 parent = *p;
202 th = rb_entry(parent, struct thread, rb_node);
203
204 if (th->pid == pid)
205 return th;
206
207 if (pid < th->pid)
208 p = &(*p)->rb_left;
209 else
210 p = &(*p)->rb_right;
211 }
212
213 th = thread__new(pid);
214 if (th != NULL) {
215 rb_link_node(&th->rb_node, parent, p);
216 rb_insert_color(&th->rb_node, &threads);
217 }
218 return th;
219 }
220
221 static void thread__insert_map(struct thread *self, struct map *map)
222 {
223 list_add_tail(&map->node, &self->maps);
224 }
225
226 static struct map *thread__find_map(struct thread *self, uint64_t ip)
227 {
228 struct map *pos;
229
230 if (self == NULL)
231 return NULL;
232
233 list_for_each_entry(pos, &self->maps, node)
234 if (ip >= pos->start && ip <= pos->end)
235 return pos;
236
237 return NULL;
238 }
239
240 /*
241 * histogram, sorted on item, collects counts
242 */
243
244 static struct rb_root hist;
245
246 struct hist_entry {
247 struct rb_node rb_node;
248
249 struct thread *thread;
250 struct map *map;
251 struct dso *dso;
252 struct symbol *sym;
253 uint64_t ip;
254 char level;
255
256 uint32_t count;
257 };
258
259 /*
260 * configurable sorting bits
261 */
262
263 struct sort_entry {
264 struct list_head list;
265
266 char *header;
267
268 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
269 size_t (*print)(FILE *fp, struct hist_entry *);
270 };
271
272 static int64_t
273 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
274 {
275 return right->thread->pid - left->thread->pid;
276 }
277
278 static size_t
279 sort__thread_print(FILE *fp, struct hist_entry *self)
280 {
281 return fprintf(fp, " %16s:%5d", self->thread->comm ?: "", self->thread->pid);
282 }
283
284 static struct sort_entry sort_thread = {
285 .header = " Command: Pid ",
286 .cmp = sort__thread_cmp,
287 .print = sort__thread_print,
288 };
289
290 static int64_t
291 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
292 {
293 char *comm_l = left->thread->comm;
294 char *comm_r = right->thread->comm;
295
296 if (!comm_l || !comm_r) {
297 if (!comm_l && !comm_r)
298 return 0;
299 else if (!comm_l)
300 return -1;
301 else
302 return 1;
303 }
304
305 return strcmp(comm_l, comm_r);
306 }
307
308 static size_t
309 sort__comm_print(FILE *fp, struct hist_entry *self)
310 {
311 return fprintf(fp, " %16s", self->thread->comm ?: "<unknown>");
312 }
313
314 static struct sort_entry sort_comm = {
315 .header = " Command",
316 .cmp = sort__comm_cmp,
317 .print = sort__comm_print,
318 };
319
320 static int64_t
321 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
322 {
323 struct dso *dso_l = left->dso;
324 struct dso *dso_r = right->dso;
325
326 if (!dso_l || !dso_r) {
327 if (!dso_l && !dso_r)
328 return 0;
329 else if (!dso_l)
330 return -1;
331 else
332 return 1;
333 }
334
335 return strcmp(dso_l->name, dso_r->name);
336 }
337
338 static size_t
339 sort__dso_print(FILE *fp, struct hist_entry *self)
340 {
341 return fprintf(fp, " %64s", self->dso ? self->dso->name : "<unknown>");
342 }
343
344 static struct sort_entry sort_dso = {
345 .header = " Shared Object",
346 .cmp = sort__dso_cmp,
347 .print = sort__dso_print,
348 };
349
350 static int64_t
351 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
352 {
353 uint64_t ip_l, ip_r;
354
355 if (left->sym == right->sym)
356 return 0;
357
358 ip_l = left->sym ? left->sym->start : left->ip;
359 ip_r = right->sym ? right->sym->start : right->ip;
360
361 return (int64_t)(ip_r - ip_l);
362 }
363
364 static size_t
365 sort__sym_print(FILE *fp, struct hist_entry *self)
366 {
367 size_t ret = 0;
368
369 if (verbose)
370 ret += fprintf(fp, " %#018llx", (unsigned long long)self->ip);
371
372 ret += fprintf(fp, " %s: %s",
373 self->dso ? self->dso->name : "<unknown>",
374 self->sym ? self->sym->name : "<unknown>");
375
376 return ret;
377 }
378
379 static struct sort_entry sort_sym = {
380 .header = "Shared Object: Symbol",
381 .cmp = sort__sym_cmp,
382 .print = sort__sym_print,
383 };
384
385 struct sort_dimension {
386 char *name;
387 struct sort_entry *entry;
388 int taken;
389 };
390
391 static struct sort_dimension sort_dimensions[] = {
392 { .name = "pid", .entry = &sort_thread, },
393 { .name = "comm", .entry = &sort_comm, },
394 { .name = "dso", .entry = &sort_dso, },
395 { .name = "symbol", .entry = &sort_sym, },
396 };
397
398 static LIST_HEAD(hist_entry__sort_list);
399
400 static int sort_dimension__add(char *tok)
401 {
402 int i;
403
404 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
405 struct sort_dimension *sd = &sort_dimensions[i];
406
407 if (sd->taken)
408 continue;
409
410 if (strcmp(tok, sd->name))
411 continue;
412
413 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
414 sd->taken = 1;
415 return 0;
416 }
417
418 return -ESRCH;
419 }
420
421 static void setup_sorting(void)
422 {
423 char *tmp, *tok, *str = strdup(sort_order);
424
425 for (tok = strtok_r(str, ", ", &tmp);
426 tok; tok = strtok_r(NULL, ", ", &tmp))
427 sort_dimension__add(tok);
428
429 free(str);
430 }
431
432 static int64_t
433 hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
434 {
435 struct sort_entry *se;
436 int64_t cmp = 0;
437
438 list_for_each_entry(se, &hist_entry__sort_list, list) {
439 cmp = se->cmp(left, right);
440 if (cmp)
441 break;
442 }
443
444 return cmp;
445 }
446
447 static size_t
448 hist_entry__fprintf(FILE *fp, struct hist_entry *self, uint64_t total_samples)
449 {
450 struct sort_entry *se;
451 size_t ret;
452
453 if (total_samples) {
454 ret = fprintf(fp, " %5.2f%%",
455 (self->count * 100.0) / total_samples);
456 } else
457 ret = fprintf(fp, "%12d ", self->count);
458
459 list_for_each_entry(se, &hist_entry__sort_list, list)
460 ret += se->print(fp, self);
461
462 ret += fprintf(fp, "\n");
463
464 return ret;
465 }
466
467 /*
468 * collect histogram counts
469 */
470
471 static int
472 hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
473 struct symbol *sym, uint64_t ip, char level)
474 {
475 struct rb_node **p = &hist.rb_node;
476 struct rb_node *parent = NULL;
477 struct hist_entry *he;
478 struct hist_entry entry = {
479 .thread = thread,
480 .map = map,
481 .dso = dso,
482 .sym = sym,
483 .ip = ip,
484 .level = level,
485 .count = 1,
486 };
487 int cmp;
488
489 while (*p != NULL) {
490 parent = *p;
491 he = rb_entry(parent, struct hist_entry, rb_node);
492
493 cmp = hist_entry__cmp(&entry, he);
494
495 if (!cmp) {
496 he->count++;
497 return 0;
498 }
499
500 if (cmp < 0)
501 p = &(*p)->rb_left;
502 else
503 p = &(*p)->rb_right;
504 }
505
506 he = malloc(sizeof(*he));
507 if (!he)
508 return -ENOMEM;
509 *he = entry;
510 rb_link_node(&he->rb_node, parent, p);
511 rb_insert_color(&he->rb_node, &hist);
512
513 return 0;
514 }
515
516 /*
517 * reverse the map, sort on count.
518 */
519
520 static struct rb_root output_hists;
521
522 static void output__insert_entry(struct hist_entry *he)
523 {
524 struct rb_node **p = &output_hists.rb_node;
525 struct rb_node *parent = NULL;
526 struct hist_entry *iter;
527
528 while (*p != NULL) {
529 parent = *p;
530 iter = rb_entry(parent, struct hist_entry, rb_node);
531
532 if (he->count > iter->count)
533 p = &(*p)->rb_left;
534 else
535 p = &(*p)->rb_right;
536 }
537
538 rb_link_node(&he->rb_node, parent, p);
539 rb_insert_color(&he->rb_node, &output_hists);
540 }
541
542 static void output__resort(void)
543 {
544 struct rb_node *next = rb_first(&hist);
545 struct hist_entry *n;
546
547 while (next) {
548 n = rb_entry(next, struct hist_entry, rb_node);
549 next = rb_next(&n->rb_node);
550
551 rb_erase(&n->rb_node, &hist);
552 output__insert_entry(n);
553 }
554 }
555
556 static size_t output__fprintf(FILE *fp, uint64_t total_samples)
557 {
558 struct hist_entry *pos;
559 struct sort_entry *se;
560 struct rb_node *nd;
561 size_t ret = 0;
562
563 fprintf(fp, "#\n");
564
565 fprintf(fp, "# Overhead");
566 list_for_each_entry(se, &hist_entry__sort_list, list)
567 fprintf(fp, " %s", se->header);
568 fprintf(fp, "\n");
569
570 fprintf(fp, "# ........");
571 list_for_each_entry(se, &hist_entry__sort_list, list) {
572 int i;
573
574 fprintf(fp, " ");
575 for (i = 0; i < strlen(se->header); i++)
576 fprintf(fp, ".");
577 }
578 fprintf(fp, "\n");
579
580 fprintf(fp, "#\n");
581
582 for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
583 pos = rb_entry(nd, struct hist_entry, rb_node);
584 ret += hist_entry__fprintf(fp, pos, total_samples);
585 }
586
587 return ret;
588 }
589
590
591 static int __cmd_report(void)
592 {
593 unsigned long offset = 0;
594 unsigned long head = 0;
595 struct stat stat;
596 char *buf;
597 event_t *event;
598 int ret, rc = EXIT_FAILURE;
599 uint32_t size;
600 unsigned long total = 0, total_mmap = 0, total_comm = 0, total_unknown = 0;
601
602 input = open(input_name, O_RDONLY);
603 if (input < 0) {
604 perror("failed to open file");
605 exit(-1);
606 }
607
608 ret = fstat(input, &stat);
609 if (ret < 0) {
610 perror("failed to stat file");
611 exit(-1);
612 }
613
614 if (!stat.st_size) {
615 fprintf(stderr, "zero-sized file, nothing to do!\n");
616 exit(0);
617 }
618
619 if (load_kernel() < 0) {
620 perror("failed to load kernel symbols");
621 return EXIT_FAILURE;
622 }
623
624 remap:
625 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
626 MAP_SHARED, input, offset);
627 if (buf == MAP_FAILED) {
628 perror("failed to mmap file");
629 exit(-1);
630 }
631
632 more:
633 event = (event_t *)(buf + head);
634
635 size = event->header.size;
636 if (!size)
637 size = 8;
638
639 if (head + event->header.size >= page_size * mmap_window) {
640 unsigned long shift = page_size * (head / page_size);
641 int ret;
642
643 ret = munmap(buf, page_size * mmap_window);
644 assert(ret == 0);
645
646 offset += shift;
647 head -= shift;
648 goto remap;
649 }
650
651 size = event->header.size;
652 if (!size)
653 goto broken_event;
654
655 if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
656 char level;
657 int show = 0;
658 struct dso *dso = NULL;
659 struct thread *thread = threads__findnew(event->ip.pid);
660 uint64_t ip = event->ip.ip;
661 struct map *map = NULL;
662
663 if (dump_trace) {
664 fprintf(stderr, "%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
665 (void *)(offset + head),
666 (void *)(long)(event->header.size),
667 event->header.misc,
668 event->ip.pid,
669 (void *)(long)ip);
670 }
671
672 if (thread == NULL) {
673 fprintf(stderr, "problem processing %d event, skipping it.\n",
674 event->header.type);
675 goto broken_event;
676 }
677
678 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
679 show = SHOW_KERNEL;
680 level = 'k';
681
682 dso = kernel_dso;
683
684 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
685
686 show = SHOW_USER;
687 level = '.';
688
689 map = thread__find_map(thread, ip);
690 if (map != NULL) {
691 dso = map->dso;
692 ip -= map->start + map->pgoff;
693 }
694
695 } else {
696 show = SHOW_HV;
697 level = 'H';
698 }
699
700 if (show & show_mask) {
701 struct symbol *sym = dso__find_symbol(dso, ip);
702
703 if (hist_entry__add(thread, map, dso, sym, ip, level)) {
704 fprintf(stderr,
705 "problem incrementing symbol count, skipping event\n");
706 goto broken_event;
707 }
708 }
709 total++;
710 } else switch (event->header.type) {
711 case PERF_EVENT_MMAP: {
712 struct thread *thread = threads__findnew(event->mmap.pid);
713 struct map *map = map__new(&event->mmap);
714
715 if (dump_trace) {
716 fprintf(stderr, "%p [%p]: PERF_EVENT_MMAP: [%p(%p) @ %p]: %s\n",
717 (void *)(offset + head),
718 (void *)(long)(event->header.size),
719 (void *)(long)event->mmap.start,
720 (void *)(long)event->mmap.len,
721 (void *)(long)event->mmap.pgoff,
722 event->mmap.filename);
723 }
724 if (thread == NULL || map == NULL) {
725 fprintf(stderr, "problem processing PERF_EVENT_MMAP, skipping event.\n");
726 goto broken_event;
727 }
728 thread__insert_map(thread, map);
729 total_mmap++;
730 break;
731 }
732 case PERF_EVENT_COMM: {
733 struct thread *thread = threads__findnew(event->comm.pid);
734
735 if (dump_trace) {
736 fprintf(stderr, "%p [%p]: PERF_EVENT_COMM: %s:%d\n",
737 (void *)(offset + head),
738 (void *)(long)(event->header.size),
739 event->comm.comm, event->comm.pid);
740 }
741 if (thread == NULL ||
742 thread__set_comm(thread, event->comm.comm)) {
743 fprintf(stderr, "problem processing PERF_EVENT_COMM, skipping event.\n");
744 goto broken_event;
745 }
746 total_comm++;
747 break;
748 }
749 default: {
750 broken_event:
751 if (dump_trace)
752 fprintf(stderr, "%p [%p]: skipping unknown header type: %d\n",
753 (void *)(offset + head),
754 (void *)(long)(event->header.size),
755 event->header.type);
756
757 total_unknown++;
758
759 /*
760 * assume we lost track of the stream, check alignment, and
761 * increment a single u64 in the hope to catch on again 'soon'.
762 */
763
764 if (unlikely(head & 7))
765 head &= ~7ULL;
766
767 size = 8;
768 }
769 }
770
771 head += size;
772
773 if (offset + head < stat.st_size)
774 goto more;
775
776 rc = EXIT_SUCCESS;
777 close(input);
778
779 if (dump_trace) {
780 fprintf(stderr, " IP events: %10ld\n", total);
781 fprintf(stderr, " mmap events: %10ld\n", total_mmap);
782 fprintf(stderr, " comm events: %10ld\n", total_comm);
783 fprintf(stderr, " unknown events: %10ld\n", total_unknown);
784
785 return 0;
786 }
787
788 if (verbose >= 2)
789 dsos__fprintf(stdout);
790
791 output__resort();
792 output__fprintf(stdout, total);
793
794 return rc;
795 }
796
797 static const char * const report_usage[] = {
798 "perf report [<options>] <command>",
799 NULL
800 };
801
802 static const struct option options[] = {
803 OPT_STRING('i', "input", &input_name, "file",
804 "input file name"),
805 OPT_BOOLEAN('v', "verbose", &verbose,
806 "be more verbose (show symbol address, etc)"),
807 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
808 "dump raw trace in ASCII"),
809 OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
810 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
811 "sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"),
812 OPT_END()
813 };
814
815 int cmd_report(int argc, const char **argv, const char *prefix)
816 {
817 symbol__init();
818
819 page_size = getpagesize();
820
821 parse_options(argc, argv, options, report_usage, 0);
822
823 setup_sorting();
824
825 setup_pager();
826
827 return __cmd_report();
828 }
This page took 0.058472 seconds and 6 git commands to generate.