5 #include "util/cache.h"
6 #include "util/rbtree.h"
7 #include "util/symbol.h"
11 #include "util/parse-options.h"
12 #include "util/parse-events.h"
18 static char const *input_name = "perf.data";
19 static char *vmlinux = NULL;
20 static char *sort_order = "pid,symbol";
22 static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
24 static int dump_trace = 0;
27 static unsigned long page_size;
28 static unsigned long mmap_window = 32;
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",
37 struct perf_event_header header;
42 struct perf_event_header header;
47 char filename[PATH_MAX];
50 struct perf_event_header header;
55 typedef union event_union {
56 struct perf_event_header header;
58 struct mmap_event mmap;
59 struct comm_event comm;
62 static LIST_HEAD(dsos);
63 static struct dso *kernel_dso;
65 static void dsos__add(struct dso *dso)
67 list_add_tail(&dso->node, &dsos);
70 static struct dso *dsos__find(const char *name)
74 list_for_each_entry(pos, &dsos, node)
75 if (strcmp(pos->name, name) == 0)
80 static struct dso *dsos__findnew(const char *name)
82 struct dso *dso = dsos__find(name);
86 dso = dso__new(name, 0);
92 fprintf(stderr, "Failed to open: %s\n", name);
97 "Failed to find debug symbols for: %s, maybe install a debug package?\n",
111 static void dsos__fprintf(FILE *fp)
115 list_for_each_entry(pos, &dsos, node)
116 dso__fprintf(pos, fp);
119 static int load_kernel(void)
123 kernel_dso = dso__new("[kernel]", 0);
128 err = dso__load_vmlinux(kernel_dso, vmlinux);
131 err = dso__load_kallsyms(kernel_dso);
134 dso__delete(kernel_dso);
137 dsos__add(kernel_dso);
143 struct list_head node;
150 static struct map *map__new(struct mmap_event *event)
152 struct map *self = malloc(sizeof(*self));
155 self->start = event->start;
156 self->end = event->start + event->len;
157 self->pgoff = event->pgoff;
159 self->dso = dsos__findnew(event->filename);
160 if (self->dso == NULL)
172 struct rb_node rb_node;
173 struct list_head maps;
178 static struct thread *thread__new(pid_t pid)
180 struct thread *self = malloc(sizeof(*self));
185 INIT_LIST_HEAD(&self->maps);
191 static int thread__set_comm(struct thread *self, const char *comm)
193 self->comm = strdup(comm);
194 return self->comm ? 0 : -ENOMEM;
197 static struct rb_root threads;
199 static struct thread *threads__findnew(pid_t pid)
201 struct rb_node **p = &threads.rb_node;
202 struct rb_node *parent = NULL;
207 th = rb_entry(parent, struct thread, rb_node);
218 th = thread__new(pid);
220 rb_link_node(&th->rb_node, parent, p);
221 rb_insert_color(&th->rb_node, &threads);
226 static void thread__insert_map(struct thread *self, struct map *map)
228 list_add_tail(&map->node, &self->maps);
231 static struct map *thread__find_map(struct thread *self, uint64_t ip)
238 list_for_each_entry(pos, &self->maps, node)
239 if (ip >= pos->start && ip <= pos->end)
246 * histogram, sorted on item, collects counts
249 static struct rb_root hist;
252 struct rb_node rb_node;
254 struct thread *thread;
265 * configurable sorting bits
269 struct list_head list;
273 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
274 size_t (*print)(FILE *fp, struct hist_entry *);
278 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
280 return right->thread->pid - left->thread->pid;
284 sort__thread_print(FILE *fp, struct hist_entry *self)
286 return fprintf(fp, " %16s:%5d", self->thread->comm ?: "", self->thread->pid);
289 static struct sort_entry sort_thread = {
290 .header = " Command: Pid ",
291 .cmp = sort__thread_cmp,
292 .print = sort__thread_print,
296 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
298 char *comm_l = left->thread->comm;
299 char *comm_r = right->thread->comm;
301 if (!comm_l || !comm_r) {
302 if (!comm_l && !comm_r)
310 return strcmp(comm_l, comm_r);
314 sort__comm_print(FILE *fp, struct hist_entry *self)
316 return fprintf(fp, " %16s", self->thread->comm ?: "<unknown>");
319 static struct sort_entry sort_comm = {
320 .header = " Command",
321 .cmp = sort__comm_cmp,
322 .print = sort__comm_print,
326 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
328 struct dso *dso_l = left->dso;
329 struct dso *dso_r = right->dso;
331 if (!dso_l || !dso_r) {
332 if (!dso_l && !dso_r)
340 return strcmp(dso_l->name, dso_r->name);
344 sort__dso_print(FILE *fp, struct hist_entry *self)
346 return fprintf(fp, " %64s", self->dso ? self->dso->name : "<unknown>");
349 static struct sort_entry sort_dso = {
350 .header = " Shared Object",
351 .cmp = sort__dso_cmp,
352 .print = sort__dso_print,
356 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
360 if (left->sym == right->sym)
363 ip_l = left->sym ? left->sym->start : left->ip;
364 ip_r = right->sym ? right->sym->start : right->ip;
366 return (int64_t)(ip_r - ip_l);
370 sort__sym_print(FILE *fp, struct hist_entry *self)
375 ret += fprintf(fp, " %#018llx", (unsigned long long)self->ip);
377 ret += fprintf(fp, " %s: %s",
378 self->dso ? self->dso->name : "<unknown>",
379 self->sym ? self->sym->name : "<unknown>");
384 static struct sort_entry sort_sym = {
385 .header = "Shared Object: Symbol",
386 .cmp = sort__sym_cmp,
387 .print = sort__sym_print,
390 struct sort_dimension {
392 struct sort_entry *entry;
396 static struct sort_dimension sort_dimensions[] = {
397 { .name = "pid", .entry = &sort_thread, },
398 { .name = "comm", .entry = &sort_comm, },
399 { .name = "dso", .entry = &sort_dso, },
400 { .name = "symbol", .entry = &sort_sym, },
403 static LIST_HEAD(hist_entry__sort_list);
405 static int sort_dimension__add(char *tok)
409 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
410 struct sort_dimension *sd = &sort_dimensions[i];
415 if (strcmp(tok, sd->name))
418 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
426 static void setup_sorting(void)
428 char *tmp, *tok, *str = strdup(sort_order);
430 for (tok = strtok_r(str, ", ", &tmp);
431 tok; tok = strtok_r(NULL, ", ", &tmp))
432 sort_dimension__add(tok);
438 hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
440 struct sort_entry *se;
443 list_for_each_entry(se, &hist_entry__sort_list, list) {
444 cmp = se->cmp(left, right);
453 hist_entry__fprintf(FILE *fp, struct hist_entry *self, uint64_t total_samples)
455 struct sort_entry *se;
459 ret = fprintf(fp, " %5.2f%%",
460 (self->count * 100.0) / total_samples);
462 ret = fprintf(fp, "%12d ", self->count);
464 list_for_each_entry(se, &hist_entry__sort_list, list)
465 ret += se->print(fp, self);
467 ret += fprintf(fp, "\n");
473 * collect histogram counts
477 hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
478 struct symbol *sym, uint64_t ip, char level)
480 struct rb_node **p = &hist.rb_node;
481 struct rb_node *parent = NULL;
482 struct hist_entry *he;
483 struct hist_entry entry = {
496 he = rb_entry(parent, struct hist_entry, rb_node);
498 cmp = hist_entry__cmp(&entry, he);
511 he = malloc(sizeof(*he));
515 rb_link_node(&he->rb_node, parent, p);
516 rb_insert_color(&he->rb_node, &hist);
522 * reverse the map, sort on count.
525 static struct rb_root output_hists;
527 static void output__insert_entry(struct hist_entry *he)
529 struct rb_node **p = &output_hists.rb_node;
530 struct rb_node *parent = NULL;
531 struct hist_entry *iter;
535 iter = rb_entry(parent, struct hist_entry, rb_node);
537 if (he->count > iter->count)
543 rb_link_node(&he->rb_node, parent, p);
544 rb_insert_color(&he->rb_node, &output_hists);
547 static void output__resort(void)
549 struct rb_node *next = rb_first(&hist);
550 struct hist_entry *n;
553 n = rb_entry(next, struct hist_entry, rb_node);
554 next = rb_next(&n->rb_node);
556 rb_erase(&n->rb_node, &hist);
557 output__insert_entry(n);
561 static size_t output__fprintf(FILE *fp, uint64_t total_samples)
563 struct hist_entry *pos;
564 struct sort_entry *se;
570 fprintf(fp, "# Overhead");
571 list_for_each_entry(se, &hist_entry__sort_list, list)
572 fprintf(fp, " %s", se->header);
575 fprintf(fp, "# ........");
576 list_for_each_entry(se, &hist_entry__sort_list, list) {
580 for (i = 0; i < strlen(se->header); i++)
587 for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
588 pos = rb_entry(nd, struct hist_entry, rb_node);
589 ret += hist_entry__fprintf(fp, pos, total_samples);
596 static int __cmd_report(void)
598 unsigned long offset = 0;
599 unsigned long head = 0;
603 int ret, rc = EXIT_FAILURE;
605 unsigned long total = 0, total_mmap = 0, total_comm = 0, total_unknown = 0;
607 input = open(input_name, O_RDONLY);
609 perror("failed to open file");
613 ret = fstat(input, &stat);
615 perror("failed to stat file");
620 fprintf(stderr, "zero-sized file, nothing to do!\n");
624 if (load_kernel() < 0) {
625 perror("failed to load kernel symbols");
630 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
631 MAP_SHARED, input, offset);
632 if (buf == MAP_FAILED) {
633 perror("failed to mmap file");
638 event = (event_t *)(buf + head);
640 size = event->header.size;
644 if (head + event->header.size >= page_size * mmap_window) {
645 unsigned long shift = page_size * (head / page_size);
648 ret = munmap(buf, page_size * mmap_window);
656 size = event->header.size;
660 if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
663 struct dso *dso = NULL;
664 struct thread *thread = threads__findnew(event->ip.pid);
665 uint64_t ip = event->ip.ip;
666 struct map *map = NULL;
669 fprintf(stderr, "%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
670 (void *)(offset + head),
671 (void *)(long)(event->header.size),
677 if (thread == NULL) {
678 fprintf(stderr, "problem processing %d event, skipping it.\n",
683 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
689 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
694 map = thread__find_map(thread, ip);
697 ip -= map->start + map->pgoff;
705 if (show & show_mask) {
706 struct symbol *sym = dso__find_symbol(dso, ip);
708 if (hist_entry__add(thread, map, dso, sym, ip, level)) {
710 "problem incrementing symbol count, skipping event\n");
715 } else switch (event->header.type) {
716 case PERF_EVENT_MMAP: {
717 struct thread *thread = threads__findnew(event->mmap.pid);
718 struct map *map = map__new(&event->mmap);
721 fprintf(stderr, "%p [%p]: PERF_EVENT_MMAP: [%p(%p) @ %p]: %s\n",
722 (void *)(offset + head),
723 (void *)(long)(event->header.size),
724 (void *)(long)event->mmap.start,
725 (void *)(long)event->mmap.len,
726 (void *)(long)event->mmap.pgoff,
727 event->mmap.filename);
729 if (thread == NULL || map == NULL) {
730 fprintf(stderr, "problem processing PERF_EVENT_MMAP, skipping event.\n");
733 thread__insert_map(thread, map);
737 case PERF_EVENT_COMM: {
738 struct thread *thread = threads__findnew(event->comm.pid);
741 fprintf(stderr, "%p [%p]: PERF_EVENT_COMM: %s:%d\n",
742 (void *)(offset + head),
743 (void *)(long)(event->header.size),
744 event->comm.comm, event->comm.pid);
746 if (thread == NULL ||
747 thread__set_comm(thread, event->comm.comm)) {
748 fprintf(stderr, "problem processing PERF_EVENT_COMM, skipping event.\n");
757 fprintf(stderr, "%p [%p]: skipping unknown header type: %d\n",
758 (void *)(offset + head),
759 (void *)(long)(event->header.size),
765 * assume we lost track of the stream, check alignment, and
766 * increment a single u64 in the hope to catch on again 'soon'.
769 if (unlikely(head & 7))
778 if (offset + head < stat.st_size)
785 fprintf(stderr, " IP events: %10ld\n", total);
786 fprintf(stderr, " mmap events: %10ld\n", total_mmap);
787 fprintf(stderr, " comm events: %10ld\n", total_comm);
788 fprintf(stderr, " unknown events: %10ld\n", total_unknown);
794 dsos__fprintf(stdout);
797 output__fprintf(stdout, total);
802 static const char * const report_usage[] = {
803 "perf report [<options>] <command>",
807 static const struct option options[] = {
808 OPT_STRING('i', "input", &input_name, "file",
810 OPT_BOOLEAN('v', "verbose", &verbose,
811 "be more verbose (show symbol address, etc)"),
812 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
813 "dump raw trace in ASCII"),
814 OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
815 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
816 "sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"),
820 int cmd_report(int argc, const char **argv, const char *prefix)
824 page_size = getpagesize();
826 parse_options(argc, argv, options, report_usage, 0);
832 return __cmd_report();