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;
26 static int full_paths;
28 static unsigned long page_size;
29 static unsigned long mmap_window = 32;
31 const char *perf_event_names[] = {
32 [PERF_EVENT_MMAP] = " PERF_EVENT_MMAP",
33 [PERF_EVENT_MUNMAP] = " PERF_EVENT_MUNMAP",
34 [PERF_EVENT_COMM] = " PERF_EVENT_COMM",
38 struct perf_event_header header;
43 struct perf_event_header header;
48 char filename[PATH_MAX];
51 struct perf_event_header header;
56 typedef union event_union {
57 struct perf_event_header header;
59 struct mmap_event mmap;
60 struct comm_event comm;
63 static LIST_HEAD(dsos);
64 static struct dso *kernel_dso;
66 static void dsos__add(struct dso *dso)
68 list_add_tail(&dso->node, &dsos);
71 static struct dso *dsos__find(const char *name)
75 list_for_each_entry(pos, &dsos, node)
76 if (strcmp(pos->name, name) == 0)
81 static struct dso *dsos__findnew(const char *name)
83 struct dso *dso = dsos__find(name);
87 dso = dso__new(name, 0);
91 nr = dso__load(dso, NULL);
93 fprintf(stderr, "Failed to open: %s\n", name);
98 "Failed to find debug symbols for: %s, maybe install a debug package?\n",
112 static void dsos__fprintf(FILE *fp)
116 list_for_each_entry(pos, &dsos, node)
117 dso__fprintf(pos, fp);
120 static int load_kernel(void)
124 kernel_dso = dso__new("[kernel]", 0);
128 err = dso__load_kernel(kernel_dso, vmlinux, NULL);
130 dso__delete(kernel_dso);
133 dsos__add(kernel_dso);
138 static int strcommon(const char *pathname, const char *cwd, int cwdlen)
142 while (pathname[n] == cwd[n] && n < cwdlen)
149 struct list_head node;
156 static struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen)
158 struct map *self = malloc(sizeof(*self));
161 const char *filename = event->filename;
162 char newfilename[PATH_MAX];
165 int n = strcommon(filename, cwd, cwdlen);
167 snprintf(newfilename, sizeof(newfilename),
168 ".%s", filename + n);
169 filename = newfilename;
173 self->start = event->start;
174 self->end = event->start + event->len;
175 self->pgoff = event->pgoff;
177 self->dso = dsos__findnew(filename);
178 if (self->dso == NULL)
190 struct rb_node rb_node;
191 struct list_head maps;
196 static struct thread *thread__new(pid_t pid)
198 struct thread *self = malloc(sizeof(*self));
203 INIT_LIST_HEAD(&self->maps);
209 static int thread__set_comm(struct thread *self, const char *comm)
211 self->comm = strdup(comm);
212 return self->comm ? 0 : -ENOMEM;
215 static struct rb_root threads;
217 static struct thread *threads__findnew(pid_t pid)
219 struct rb_node **p = &threads.rb_node;
220 struct rb_node *parent = NULL;
225 th = rb_entry(parent, struct thread, rb_node);
236 th = thread__new(pid);
238 rb_link_node(&th->rb_node, parent, p);
239 rb_insert_color(&th->rb_node, &threads);
244 static void thread__insert_map(struct thread *self, struct map *map)
246 list_add_tail(&map->node, &self->maps);
249 static struct map *thread__find_map(struct thread *self, uint64_t ip)
256 list_for_each_entry(pos, &self->maps, node)
257 if (ip >= pos->start && ip <= pos->end)
264 * histogram, sorted on item, collects counts
267 static struct rb_root hist;
270 struct rb_node rb_node;
272 struct thread *thread;
283 * configurable sorting bits
287 struct list_head list;
291 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
292 size_t (*print)(FILE *fp, struct hist_entry *);
296 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
298 return right->thread->pid - left->thread->pid;
302 sort__thread_print(FILE *fp, struct hist_entry *self)
304 return fprintf(fp, " %16s:%5d", self->thread->comm ?: "", self->thread->pid);
307 static struct sort_entry sort_thread = {
308 .header = " Command: Pid ",
309 .cmp = sort__thread_cmp,
310 .print = sort__thread_print,
314 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
316 char *comm_l = left->thread->comm;
317 char *comm_r = right->thread->comm;
319 if (!comm_l || !comm_r) {
320 if (!comm_l && !comm_r)
328 return strcmp(comm_l, comm_r);
332 sort__comm_print(FILE *fp, struct hist_entry *self)
334 return fprintf(fp, " %16s", self->thread->comm ?: "<unknown>");
337 static struct sort_entry sort_comm = {
338 .header = " Command",
339 .cmp = sort__comm_cmp,
340 .print = sort__comm_print,
344 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
346 struct dso *dso_l = left->dso;
347 struct dso *dso_r = right->dso;
349 if (!dso_l || !dso_r) {
350 if (!dso_l && !dso_r)
358 return strcmp(dso_l->name, dso_r->name);
362 sort__dso_print(FILE *fp, struct hist_entry *self)
364 return fprintf(fp, " %64s", self->dso ? self->dso->name : "<unknown>");
367 static struct sort_entry sort_dso = {
368 .header = " Shared Object",
369 .cmp = sort__dso_cmp,
370 .print = sort__dso_print,
374 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
378 if (left->sym == right->sym)
381 ip_l = left->sym ? left->sym->start : left->ip;
382 ip_r = right->sym ? right->sym->start : right->ip;
384 return (int64_t)(ip_r - ip_l);
388 sort__sym_print(FILE *fp, struct hist_entry *self)
393 ret += fprintf(fp, " %#018llx", (unsigned long long)self->ip);
395 ret += fprintf(fp, " %s: %s",
396 self->dso ? self->dso->name : "<unknown>",
397 self->sym ? self->sym->name : "<unknown>");
402 static struct sort_entry sort_sym = {
403 .header = "Shared Object: Symbol",
404 .cmp = sort__sym_cmp,
405 .print = sort__sym_print,
408 struct sort_dimension {
410 struct sort_entry *entry;
414 static struct sort_dimension sort_dimensions[] = {
415 { .name = "pid", .entry = &sort_thread, },
416 { .name = "comm", .entry = &sort_comm, },
417 { .name = "dso", .entry = &sort_dso, },
418 { .name = "symbol", .entry = &sort_sym, },
421 static LIST_HEAD(hist_entry__sort_list);
423 static int sort_dimension__add(char *tok)
427 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
428 struct sort_dimension *sd = &sort_dimensions[i];
433 if (strcmp(tok, sd->name))
436 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
444 static void setup_sorting(void)
446 char *tmp, *tok, *str = strdup(sort_order);
448 for (tok = strtok_r(str, ", ", &tmp);
449 tok; tok = strtok_r(NULL, ", ", &tmp))
450 sort_dimension__add(tok);
456 hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
458 struct sort_entry *se;
461 list_for_each_entry(se, &hist_entry__sort_list, list) {
462 cmp = se->cmp(left, right);
471 hist_entry__fprintf(FILE *fp, struct hist_entry *self, uint64_t total_samples)
473 struct sort_entry *se;
477 ret = fprintf(fp, " %5.2f%%",
478 (self->count * 100.0) / total_samples);
480 ret = fprintf(fp, "%12d ", self->count);
482 list_for_each_entry(se, &hist_entry__sort_list, list)
483 ret += se->print(fp, self);
485 ret += fprintf(fp, "\n");
491 * collect histogram counts
495 hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
496 struct symbol *sym, uint64_t ip, char level)
498 struct rb_node **p = &hist.rb_node;
499 struct rb_node *parent = NULL;
500 struct hist_entry *he;
501 struct hist_entry entry = {
514 he = rb_entry(parent, struct hist_entry, rb_node);
516 cmp = hist_entry__cmp(&entry, he);
529 he = malloc(sizeof(*he));
533 rb_link_node(&he->rb_node, parent, p);
534 rb_insert_color(&he->rb_node, &hist);
540 * reverse the map, sort on count.
543 static struct rb_root output_hists;
545 static void output__insert_entry(struct hist_entry *he)
547 struct rb_node **p = &output_hists.rb_node;
548 struct rb_node *parent = NULL;
549 struct hist_entry *iter;
553 iter = rb_entry(parent, struct hist_entry, rb_node);
555 if (he->count > iter->count)
561 rb_link_node(&he->rb_node, parent, p);
562 rb_insert_color(&he->rb_node, &output_hists);
565 static void output__resort(void)
567 struct rb_node *next = rb_first(&hist);
568 struct hist_entry *n;
571 n = rb_entry(next, struct hist_entry, rb_node);
572 next = rb_next(&n->rb_node);
574 rb_erase(&n->rb_node, &hist);
575 output__insert_entry(n);
579 static size_t output__fprintf(FILE *fp, uint64_t total_samples)
581 struct hist_entry *pos;
582 struct sort_entry *se;
588 fprintf(fp, "# Overhead");
589 list_for_each_entry(se, &hist_entry__sort_list, list)
590 fprintf(fp, " %s", se->header);
593 fprintf(fp, "# ........");
594 list_for_each_entry(se, &hist_entry__sort_list, list) {
598 for (i = 0; i < strlen(se->header); i++)
605 for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
606 pos = rb_entry(nd, struct hist_entry, rb_node);
607 ret += hist_entry__fprintf(fp, pos, total_samples);
614 static int __cmd_report(void)
616 unsigned long offset = 0;
617 unsigned long head = 0;
621 int ret, rc = EXIT_FAILURE;
623 unsigned long total = 0, total_mmap = 0, total_comm = 0, total_unknown = 0;
624 char cwd[PATH_MAX], *cwdp = cwd;
627 input = open(input_name, O_RDONLY);
629 perror("failed to open file");
633 ret = fstat(input, &stat);
635 perror("failed to stat file");
640 fprintf(stderr, "zero-sized file, nothing to do!\n");
644 if (load_kernel() < 0) {
645 perror("failed to load kernel symbols");
650 if (getcwd(cwd, sizeof(cwd)) == NULL) {
651 perror("failed to get the current directory");
654 cwdlen = strlen(cwd);
658 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
659 MAP_SHARED, input, offset);
660 if (buf == MAP_FAILED) {
661 perror("failed to mmap file");
666 event = (event_t *)(buf + head);
668 size = event->header.size;
672 if (head + event->header.size >= page_size * mmap_window) {
673 unsigned long shift = page_size * (head / page_size);
676 ret = munmap(buf, page_size * mmap_window);
684 size = event->header.size;
688 if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) {
691 struct dso *dso = NULL;
692 struct thread *thread = threads__findnew(event->ip.pid);
693 uint64_t ip = event->ip.ip;
694 struct map *map = NULL;
697 fprintf(stderr, "%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
698 (void *)(offset + head),
699 (void *)(long)(event->header.size),
705 if (thread == NULL) {
706 fprintf(stderr, "problem processing %d event, skipping it.\n",
711 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
717 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
722 map = thread__find_map(thread, ip);
725 ip -= map->start + map->pgoff;
733 if (show & show_mask) {
734 struct symbol *sym = dso__find_symbol(dso, ip);
736 if (hist_entry__add(thread, map, dso, sym, ip, level)) {
738 "problem incrementing symbol count, skipping event\n");
743 } else switch (event->header.type) {
744 case PERF_EVENT_MMAP: {
745 struct thread *thread = threads__findnew(event->mmap.pid);
746 struct map *map = map__new(&event->mmap, cwdp, cwdlen);
749 fprintf(stderr, "%p [%p]: PERF_EVENT_MMAP: [%p(%p) @ %p]: %s\n",
750 (void *)(offset + head),
751 (void *)(long)(event->header.size),
752 (void *)(long)event->mmap.start,
753 (void *)(long)event->mmap.len,
754 (void *)(long)event->mmap.pgoff,
755 event->mmap.filename);
757 if (thread == NULL || map == NULL) {
758 fprintf(stderr, "problem processing PERF_EVENT_MMAP, skipping event.\n");
761 thread__insert_map(thread, map);
765 case PERF_EVENT_COMM: {
766 struct thread *thread = threads__findnew(event->comm.pid);
769 fprintf(stderr, "%p [%p]: PERF_EVENT_COMM: %s:%d\n",
770 (void *)(offset + head),
771 (void *)(long)(event->header.size),
772 event->comm.comm, event->comm.pid);
774 if (thread == NULL ||
775 thread__set_comm(thread, event->comm.comm)) {
776 fprintf(stderr, "problem processing PERF_EVENT_COMM, skipping event.\n");
785 fprintf(stderr, "%p [%p]: skipping unknown header type: %d\n",
786 (void *)(offset + head),
787 (void *)(long)(event->header.size),
793 * assume we lost track of the stream, check alignment, and
794 * increment a single u64 in the hope to catch on again 'soon'.
797 if (unlikely(head & 7))
806 if (offset + head < stat.st_size)
813 fprintf(stderr, " IP events: %10ld\n", total);
814 fprintf(stderr, " mmap events: %10ld\n", total_mmap);
815 fprintf(stderr, " comm events: %10ld\n", total_comm);
816 fprintf(stderr, " unknown events: %10ld\n", total_unknown);
822 dsos__fprintf(stdout);
825 output__fprintf(stdout, total);
830 static const char * const report_usage[] = {
831 "perf report [<options>] <command>",
835 static const struct option options[] = {
836 OPT_STRING('i', "input", &input_name, "file",
838 OPT_BOOLEAN('v', "verbose", &verbose,
839 "be more verbose (show symbol address, etc)"),
840 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
841 "dump raw trace in ASCII"),
842 OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
843 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
844 "sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"),
845 OPT_BOOLEAN('P', "full-paths", &full_paths,
846 "Don't shorten the pathnames taking into account the cwd"),
850 int cmd_report(int argc, const char **argv, const char *prefix)
854 page_size = getpagesize();
856 parse_options(argc, argv, options, report_usage, 0);
862 return __cmd_report();