diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 399 |
1 files changed, 305 insertions, 94 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 4e5cc266311e..8b2ec882e6e0 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -31,11 +31,14 @@ | |||
31 | static char const *input_name = "perf.data"; | 31 | static char const *input_name = "perf.data"; |
32 | static char *vmlinux = NULL; | 32 | static char *vmlinux = NULL; |
33 | 33 | ||
34 | static char default_sort_order[] = "comm,dso"; | 34 | static char default_sort_order[] = "comm,dso,symbol"; |
35 | static char *sort_order = default_sort_order; | 35 | static char *sort_order = default_sort_order; |
36 | static char *dso_list_str, *comm_list_str, *sym_list_str; | 36 | static char *dso_list_str, *comm_list_str, *sym_list_str, |
37 | *col_width_list_str; | ||
37 | static struct strlist *dso_list, *comm_list, *sym_list; | 38 | static struct strlist *dso_list, *comm_list, *sym_list; |
39 | static char *field_sep; | ||
38 | 40 | ||
41 | static int force; | ||
39 | static int input; | 42 | static int input; |
40 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | 43 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; |
41 | 44 | ||
@@ -49,6 +52,7 @@ static int verbose; | |||
49 | static int modules; | 52 | static int modules; |
50 | 53 | ||
51 | static int full_paths; | 54 | static int full_paths; |
55 | static int show_nr_samples; | ||
52 | 56 | ||
53 | static unsigned long page_size; | 57 | static unsigned long page_size; |
54 | static unsigned long mmap_window = 32; | 58 | static unsigned long mmap_window = 32; |
@@ -65,7 +69,7 @@ static int callchain; | |||
65 | 69 | ||
66 | static | 70 | static |
67 | struct callchain_param callchain_param = { | 71 | struct callchain_param callchain_param = { |
68 | .mode = CHAIN_GRAPH_ABS, | 72 | .mode = CHAIN_GRAPH_REL, |
69 | .min_percent = 0.5 | 73 | .min_percent = 0.5 |
70 | }; | 74 | }; |
71 | 75 | ||
@@ -96,13 +100,7 @@ struct comm_event { | |||
96 | struct fork_event { | 100 | struct fork_event { |
97 | struct perf_event_header header; | 101 | struct perf_event_header header; |
98 | u32 pid, ppid; | 102 | u32 pid, ppid; |
99 | }; | 103 | u32 tid, ptid; |
100 | |||
101 | struct period_event { | ||
102 | struct perf_event_header header; | ||
103 | u64 time; | ||
104 | u64 id; | ||
105 | u64 sample_period; | ||
106 | }; | 104 | }; |
107 | 105 | ||
108 | struct lost_event { | 106 | struct lost_event { |
@@ -115,7 +113,9 @@ struct read_event { | |||
115 | struct perf_event_header header; | 113 | struct perf_event_header header; |
116 | u32 pid,tid; | 114 | u32 pid,tid; |
117 | u64 value; | 115 | u64 value; |
118 | u64 format[3]; | 116 | u64 time_enabled; |
117 | u64 time_running; | ||
118 | u64 id; | ||
119 | }; | 119 | }; |
120 | 120 | ||
121 | typedef union event_union { | 121 | typedef union event_union { |
@@ -124,11 +124,37 @@ typedef union event_union { | |||
124 | struct mmap_event mmap; | 124 | struct mmap_event mmap; |
125 | struct comm_event comm; | 125 | struct comm_event comm; |
126 | struct fork_event fork; | 126 | struct fork_event fork; |
127 | struct period_event period; | ||
128 | struct lost_event lost; | 127 | struct lost_event lost; |
129 | struct read_event read; | 128 | struct read_event read; |
130 | } event_t; | 129 | } event_t; |
131 | 130 | ||
131 | static int repsep_fprintf(FILE *fp, const char *fmt, ...) | ||
132 | { | ||
133 | int n; | ||
134 | va_list ap; | ||
135 | |||
136 | va_start(ap, fmt); | ||
137 | if (!field_sep) | ||
138 | n = vfprintf(fp, fmt, ap); | ||
139 | else { | ||
140 | char *bf = NULL; | ||
141 | n = vasprintf(&bf, fmt, ap); | ||
142 | if (n > 0) { | ||
143 | char *sep = bf; | ||
144 | while (1) { | ||
145 | sep = strchr(sep, *field_sep); | ||
146 | if (sep == NULL) | ||
147 | break; | ||
148 | *sep = '.'; | ||
149 | } | ||
150 | } | ||
151 | fputs(bf, fp); | ||
152 | free(bf); | ||
153 | } | ||
154 | va_end(ap); | ||
155 | return n; | ||
156 | } | ||
157 | |||
132 | static LIST_HEAD(dsos); | 158 | static LIST_HEAD(dsos); |
133 | static struct dso *kernel_dso; | 159 | static struct dso *kernel_dso; |
134 | static struct dso *vdso; | 160 | static struct dso *vdso; |
@@ -230,7 +256,7 @@ static int strcommon(const char *pathname) | |||
230 | { | 256 | { |
231 | int n = 0; | 257 | int n = 0; |
232 | 258 | ||
233 | while (pathname[n] == cwd[n] && n < cwdlen) | 259 | while (n < cwdlen && pathname[n] == cwd[n]) |
234 | ++n; | 260 | ++n; |
235 | 261 | ||
236 | return n; | 262 | return n; |
@@ -360,12 +386,28 @@ static struct thread *thread__new(pid_t pid) | |||
360 | return self; | 386 | return self; |
361 | } | 387 | } |
362 | 388 | ||
389 | static unsigned int dsos__col_width, | ||
390 | comms__col_width, | ||
391 | threads__col_width; | ||
392 | |||
363 | static int thread__set_comm(struct thread *self, const char *comm) | 393 | static int thread__set_comm(struct thread *self, const char *comm) |
364 | { | 394 | { |
365 | if (self->comm) | 395 | if (self->comm) |
366 | free(self->comm); | 396 | free(self->comm); |
367 | self->comm = strdup(comm); | 397 | self->comm = strdup(comm); |
368 | return self->comm ? 0 : -ENOMEM; | 398 | if (!self->comm) |
399 | return -ENOMEM; | ||
400 | |||
401 | if (!col_width_list_str && !field_sep && | ||
402 | (!comm_list || strlist__has_entry(comm_list, comm))) { | ||
403 | unsigned int slen = strlen(comm); | ||
404 | if (slen > comms__col_width) { | ||
405 | comms__col_width = slen; | ||
406 | threads__col_width = slen + 6; | ||
407 | } | ||
408 | } | ||
409 | |||
410 | return 0; | ||
369 | } | 411 | } |
370 | 412 | ||
371 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 413 | static size_t thread__fprintf(struct thread *self, FILE *fp) |
@@ -536,7 +578,9 @@ struct sort_entry { | |||
536 | 578 | ||
537 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); | 579 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); |
538 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); | 580 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); |
539 | size_t (*print)(FILE *fp, struct hist_entry *); | 581 | size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); |
582 | unsigned int *width; | ||
583 | bool elide; | ||
540 | }; | 584 | }; |
541 | 585 | ||
542 | static int64_t cmp_null(void *l, void *r) | 586 | static int64_t cmp_null(void *l, void *r) |
@@ -558,15 +602,17 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | |||
558 | } | 602 | } |
559 | 603 | ||
560 | static size_t | 604 | static size_t |
561 | sort__thread_print(FILE *fp, struct hist_entry *self) | 605 | sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) |
562 | { | 606 | { |
563 | return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid); | 607 | return repsep_fprintf(fp, "%*s:%5d", width - 6, |
608 | self->thread->comm ?: "", self->thread->pid); | ||
564 | } | 609 | } |
565 | 610 | ||
566 | static struct sort_entry sort_thread = { | 611 | static struct sort_entry sort_thread = { |
567 | .header = " Command: Pid", | 612 | .header = "Command: Pid", |
568 | .cmp = sort__thread_cmp, | 613 | .cmp = sort__thread_cmp, |
569 | .print = sort__thread_print, | 614 | .print = sort__thread_print, |
615 | .width = &threads__col_width, | ||
570 | }; | 616 | }; |
571 | 617 | ||
572 | /* --sort comm */ | 618 | /* --sort comm */ |
@@ -590,16 +636,17 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | |||
590 | } | 636 | } |
591 | 637 | ||
592 | static size_t | 638 | static size_t |
593 | sort__comm_print(FILE *fp, struct hist_entry *self) | 639 | sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) |
594 | { | 640 | { |
595 | return fprintf(fp, "%16s", self->thread->comm); | 641 | return repsep_fprintf(fp, "%*s", width, self->thread->comm); |
596 | } | 642 | } |
597 | 643 | ||
598 | static struct sort_entry sort_comm = { | 644 | static struct sort_entry sort_comm = { |
599 | .header = " Command", | 645 | .header = "Command", |
600 | .cmp = sort__comm_cmp, | 646 | .cmp = sort__comm_cmp, |
601 | .collapse = sort__comm_collapse, | 647 | .collapse = sort__comm_collapse, |
602 | .print = sort__comm_print, | 648 | .print = sort__comm_print, |
649 | .width = &comms__col_width, | ||
603 | }; | 650 | }; |
604 | 651 | ||
605 | /* --sort dso */ | 652 | /* --sort dso */ |
@@ -617,18 +664,19 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | |||
617 | } | 664 | } |
618 | 665 | ||
619 | static size_t | 666 | static size_t |
620 | sort__dso_print(FILE *fp, struct hist_entry *self) | 667 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) |
621 | { | 668 | { |
622 | if (self->dso) | 669 | if (self->dso) |
623 | return fprintf(fp, "%-25s", self->dso->name); | 670 | return repsep_fprintf(fp, "%-*s", width, self->dso->name); |
624 | 671 | ||
625 | return fprintf(fp, "%016llx ", (u64)self->ip); | 672 | return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); |
626 | } | 673 | } |
627 | 674 | ||
628 | static struct sort_entry sort_dso = { | 675 | static struct sort_entry sort_dso = { |
629 | .header = "Shared Object ", | 676 | .header = "Shared Object", |
630 | .cmp = sort__dso_cmp, | 677 | .cmp = sort__dso_cmp, |
631 | .print = sort__dso_print, | 678 | .print = sort__dso_print, |
679 | .width = &dsos__col_width, | ||
632 | }; | 680 | }; |
633 | 681 | ||
634 | /* --sort symbol */ | 682 | /* --sort symbol */ |
@@ -648,22 +696,23 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |||
648 | } | 696 | } |
649 | 697 | ||
650 | static size_t | 698 | static size_t |
651 | sort__sym_print(FILE *fp, struct hist_entry *self) | 699 | sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) |
652 | { | 700 | { |
653 | size_t ret = 0; | 701 | size_t ret = 0; |
654 | 702 | ||
655 | if (verbose) | 703 | if (verbose) |
656 | ret += fprintf(fp, "%#018llx ", (u64)self->ip); | 704 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, |
705 | dso__symtab_origin(self->dso)); | ||
657 | 706 | ||
707 | ret += repsep_fprintf(fp, "[%c] ", self->level); | ||
658 | if (self->sym) { | 708 | if (self->sym) { |
659 | ret += fprintf(fp, "[%c] %s", | 709 | ret += repsep_fprintf(fp, "%s", self->sym->name); |
660 | self->dso == kernel_dso ? 'k' : | ||
661 | self->dso == hypervisor_dso ? 'h' : '.', self->sym->name); | ||
662 | 710 | ||
663 | if (self->sym->module) | 711 | if (self->sym->module) |
664 | ret += fprintf(fp, "\t[%s]", self->sym->module->name); | 712 | ret += repsep_fprintf(fp, "\t[%s]", |
713 | self->sym->module->name); | ||
665 | } else { | 714 | } else { |
666 | ret += fprintf(fp, "%#016llx", (u64)self->ip); | 715 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); |
667 | } | 716 | } |
668 | 717 | ||
669 | return ret; | 718 | return ret; |
@@ -690,19 +739,19 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | |||
690 | } | 739 | } |
691 | 740 | ||
692 | static size_t | 741 | static size_t |
693 | sort__parent_print(FILE *fp, struct hist_entry *self) | 742 | sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) |
694 | { | 743 | { |
695 | size_t ret = 0; | 744 | return repsep_fprintf(fp, "%-*s", width, |
696 | 745 | self->parent ? self->parent->name : "[other]"); | |
697 | ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]"); | ||
698 | |||
699 | return ret; | ||
700 | } | 746 | } |
701 | 747 | ||
748 | static unsigned int parent_symbol__col_width; | ||
749 | |||
702 | static struct sort_entry sort_parent = { | 750 | static struct sort_entry sort_parent = { |
703 | .header = "Parent symbol ", | 751 | .header = "Parent symbol", |
704 | .cmp = sort__parent_cmp, | 752 | .cmp = sort__parent_cmp, |
705 | .print = sort__parent_print, | 753 | .print = sort__parent_print, |
754 | .width = &parent_symbol__col_width, | ||
706 | }; | 755 | }; |
707 | 756 | ||
708 | static int sort__need_collapse = 0; | 757 | static int sort__need_collapse = 0; |
@@ -843,6 +892,21 @@ ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, | |||
843 | return ret; | 892 | return ret; |
844 | } | 893 | } |
845 | 894 | ||
895 | static struct symbol *rem_sq_bracket; | ||
896 | static struct callchain_list rem_hits; | ||
897 | |||
898 | static void init_rem_hits(void) | ||
899 | { | ||
900 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
901 | if (!rem_sq_bracket) { | ||
902 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
903 | return; | ||
904 | } | ||
905 | |||
906 | strcpy(rem_sq_bracket->name, "[...]"); | ||
907 | rem_hits.sym = rem_sq_bracket; | ||
908 | } | ||
909 | |||
846 | static size_t | 910 | static size_t |
847 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 911 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, |
848 | u64 total_samples, int depth, int depth_mask) | 912 | u64 total_samples, int depth, int depth_mask) |
@@ -852,25 +916,34 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
852 | struct callchain_list *chain; | 916 | struct callchain_list *chain; |
853 | int new_depth_mask = depth_mask; | 917 | int new_depth_mask = depth_mask; |
854 | u64 new_total; | 918 | u64 new_total; |
919 | u64 remaining; | ||
855 | size_t ret = 0; | 920 | size_t ret = 0; |
856 | int i; | 921 | int i; |
857 | 922 | ||
858 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 923 | if (callchain_param.mode == CHAIN_GRAPH_REL) |
859 | new_total = self->cumul_hit; | 924 | new_total = self->children_hit; |
860 | else | 925 | else |
861 | new_total = total_samples; | 926 | new_total = total_samples; |
862 | 927 | ||
928 | remaining = new_total; | ||
929 | |||
863 | node = rb_first(&self->rb_root); | 930 | node = rb_first(&self->rb_root); |
864 | while (node) { | 931 | while (node) { |
932 | u64 cumul; | ||
933 | |||
865 | child = rb_entry(node, struct callchain_node, rb_node); | 934 | child = rb_entry(node, struct callchain_node, rb_node); |
935 | cumul = cumul_hits(child); | ||
936 | remaining -= cumul; | ||
866 | 937 | ||
867 | /* | 938 | /* |
868 | * The depth mask manages the output of pipes that show | 939 | * The depth mask manages the output of pipes that show |
869 | * the depth. We don't want to keep the pipes of the current | 940 | * the depth. We don't want to keep the pipes of the current |
870 | * level for the last child of this depth | 941 | * level for the last child of this depth. |
942 | * Except if we have remaining filtered hits. They will | ||
943 | * supersede the last child | ||
871 | */ | 944 | */ |
872 | next = rb_next(node); | 945 | next = rb_next(node); |
873 | if (!next) | 946 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) |
874 | new_depth_mask &= ~(1 << (depth - 1)); | 947 | new_depth_mask &= ~(1 << (depth - 1)); |
875 | 948 | ||
876 | /* | 949 | /* |
@@ -885,7 +958,7 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
885 | ret += ipchain__fprintf_graph(fp, chain, depth, | 958 | ret += ipchain__fprintf_graph(fp, chain, depth, |
886 | new_depth_mask, i++, | 959 | new_depth_mask, i++, |
887 | new_total, | 960 | new_total, |
888 | child->cumul_hit); | 961 | cumul); |
889 | } | 962 | } |
890 | ret += callchain__fprintf_graph(fp, child, new_total, | 963 | ret += callchain__fprintf_graph(fp, child, new_total, |
891 | depth + 1, | 964 | depth + 1, |
@@ -893,6 +966,19 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
893 | node = next; | 966 | node = next; |
894 | } | 967 | } |
895 | 968 | ||
969 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
970 | remaining && remaining != new_total) { | ||
971 | |||
972 | if (!rem_sq_bracket) | ||
973 | return ret; | ||
974 | |||
975 | new_depth_mask &= ~(1 << (depth - 1)); | ||
976 | |||
977 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
978 | new_depth_mask, 0, new_total, | ||
979 | remaining); | ||
980 | } | ||
981 | |||
896 | return ret; | 982 | return ret; |
897 | } | 983 | } |
898 | 984 | ||
@@ -967,17 +1053,25 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | |||
967 | return 0; | 1053 | return 0; |
968 | 1054 | ||
969 | if (total_samples) | 1055 | if (total_samples) |
970 | ret = percent_color_fprintf(fp, " %6.2f%%", | 1056 | ret = percent_color_fprintf(fp, |
971 | (self->count * 100.0) / total_samples); | 1057 | field_sep ? "%.2f" : " %6.2f%%", |
1058 | (self->count * 100.0) / total_samples); | ||
972 | else | 1059 | else |
973 | ret = fprintf(fp, "%12Ld ", self->count); | 1060 | ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count); |
1061 | |||
1062 | if (show_nr_samples) { | ||
1063 | if (field_sep) | ||
1064 | fprintf(fp, "%c%lld", *field_sep, self->count); | ||
1065 | else | ||
1066 | fprintf(fp, "%11lld", self->count); | ||
1067 | } | ||
974 | 1068 | ||
975 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1069 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
976 | if (exclude_other && (se == &sort_parent)) | 1070 | if (se->elide) |
977 | continue; | 1071 | continue; |
978 | 1072 | ||
979 | fprintf(fp, " "); | 1073 | fprintf(fp, "%s", field_sep ?: " "); |
980 | ret += se->print(fp, self); | 1074 | ret += se->print(fp, self, se->width ? *se->width : 0); |
981 | } | 1075 | } |
982 | 1076 | ||
983 | ret += fprintf(fp, "\n"); | 1077 | ret += fprintf(fp, "\n"); |
@@ -992,6 +1086,18 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | |||
992 | * | 1086 | * |
993 | */ | 1087 | */ |
994 | 1088 | ||
1089 | static void dso__calc_col_width(struct dso *self) | ||
1090 | { | ||
1091 | if (!col_width_list_str && !field_sep && | ||
1092 | (!dso_list || strlist__has_entry(dso_list, self->name))) { | ||
1093 | unsigned int slen = strlen(self->name); | ||
1094 | if (slen > dsos__col_width) | ||
1095 | dsos__col_width = slen; | ||
1096 | } | ||
1097 | |||
1098 | self->slen_calculated = 1; | ||
1099 | } | ||
1100 | |||
995 | static struct symbol * | 1101 | static struct symbol * |
996 | resolve_symbol(struct thread *thread, struct map **mapp, | 1102 | resolve_symbol(struct thread *thread, struct map **mapp, |
997 | struct dso **dsop, u64 *ipp) | 1103 | struct dso **dsop, u64 *ipp) |
@@ -1011,6 +1117,14 @@ resolve_symbol(struct thread *thread, struct map **mapp, | |||
1011 | 1117 | ||
1012 | map = thread__find_map(thread, ip); | 1118 | map = thread__find_map(thread, ip); |
1013 | if (map != NULL) { | 1119 | if (map != NULL) { |
1120 | /* | ||
1121 | * We have to do this here as we may have a dso | ||
1122 | * with no symbol hit that has a name longer than | ||
1123 | * the ones with symbols sampled. | ||
1124 | */ | ||
1125 | if (!sort_dso.elide && !map->dso->slen_calculated) | ||
1126 | dso__calc_col_width(map->dso); | ||
1127 | |||
1014 | if (mapp) | 1128 | if (mapp) |
1015 | *mapp = map; | 1129 | *mapp = map; |
1016 | got_map: | 1130 | got_map: |
@@ -1282,35 +1396,69 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
1282 | struct sort_entry *se; | 1396 | struct sort_entry *se; |
1283 | struct rb_node *nd; | 1397 | struct rb_node *nd; |
1284 | size_t ret = 0; | 1398 | size_t ret = 0; |
1399 | unsigned int width; | ||
1400 | char *col_width = col_width_list_str; | ||
1285 | 1401 | ||
1286 | fprintf(fp, "\n"); | 1402 | init_rem_hits(); |
1287 | fprintf(fp, "#\n"); | 1403 | |
1288 | fprintf(fp, "# (%Ld samples)\n", (u64)total_samples); | 1404 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); |
1289 | fprintf(fp, "#\n"); | 1405 | fprintf(fp, "#\n"); |
1290 | 1406 | ||
1291 | fprintf(fp, "# Overhead"); | 1407 | fprintf(fp, "# Overhead"); |
1408 | if (show_nr_samples) { | ||
1409 | if (field_sep) | ||
1410 | fprintf(fp, "%cSamples", *field_sep); | ||
1411 | else | ||
1412 | fputs(" Samples ", fp); | ||
1413 | } | ||
1292 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1414 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
1293 | if (exclude_other && (se == &sort_parent)) | 1415 | if (se->elide) |
1294 | continue; | 1416 | continue; |
1295 | fprintf(fp, " %s", se->header); | 1417 | if (field_sep) { |
1418 | fprintf(fp, "%c%s", *field_sep, se->header); | ||
1419 | continue; | ||
1420 | } | ||
1421 | width = strlen(se->header); | ||
1422 | if (se->width) { | ||
1423 | if (col_width_list_str) { | ||
1424 | if (col_width) { | ||
1425 | *se->width = atoi(col_width); | ||
1426 | col_width = strchr(col_width, ','); | ||
1427 | if (col_width) | ||
1428 | ++col_width; | ||
1429 | } | ||
1430 | } | ||
1431 | width = *se->width = max(*se->width, width); | ||
1432 | } | ||
1433 | fprintf(fp, " %*s", width, se->header); | ||
1296 | } | 1434 | } |
1297 | fprintf(fp, "\n"); | 1435 | fprintf(fp, "\n"); |
1298 | 1436 | ||
1437 | if (field_sep) | ||
1438 | goto print_entries; | ||
1439 | |||
1299 | fprintf(fp, "# ........"); | 1440 | fprintf(fp, "# ........"); |
1441 | if (show_nr_samples) | ||
1442 | fprintf(fp, " .........."); | ||
1300 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1443 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
1301 | unsigned int i; | 1444 | unsigned int i; |
1302 | 1445 | ||
1303 | if (exclude_other && (se == &sort_parent)) | 1446 | if (se->elide) |
1304 | continue; | 1447 | continue; |
1305 | 1448 | ||
1306 | fprintf(fp, " "); | 1449 | fprintf(fp, " "); |
1307 | for (i = 0; i < strlen(se->header); i++) | 1450 | if (se->width) |
1451 | width = *se->width; | ||
1452 | else | ||
1453 | width = strlen(se->header); | ||
1454 | for (i = 0; i < width; i++) | ||
1308 | fprintf(fp, "."); | 1455 | fprintf(fp, "."); |
1309 | } | 1456 | } |
1310 | fprintf(fp, "\n"); | 1457 | fprintf(fp, "\n"); |
1311 | 1458 | ||
1312 | fprintf(fp, "#\n"); | 1459 | fprintf(fp, "#\n"); |
1313 | 1460 | ||
1461 | print_entries: | ||
1314 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { | 1462 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { |
1315 | pos = rb_entry(nd, struct hist_entry, rb_node); | 1463 | pos = rb_entry(nd, struct hist_entry, rb_node); |
1316 | ret += hist_entry__fprintf(fp, pos, total_samples); | 1464 | ret += hist_entry__fprintf(fp, pos, total_samples); |
@@ -1319,11 +1467,13 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
1319 | if (sort_order == default_sort_order && | 1467 | if (sort_order == default_sort_order && |
1320 | parent_pattern == default_parent_pattern) { | 1468 | parent_pattern == default_parent_pattern) { |
1321 | fprintf(fp, "#\n"); | 1469 | fprintf(fp, "#\n"); |
1322 | fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n"); | 1470 | fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n"); |
1323 | fprintf(fp, "#\n"); | 1471 | fprintf(fp, "#\n"); |
1324 | } | 1472 | } |
1325 | fprintf(fp, "\n"); | 1473 | fprintf(fp, "\n"); |
1326 | 1474 | ||
1475 | free(rem_sq_bracket); | ||
1476 | |||
1327 | return ret; | 1477 | return ret; |
1328 | } | 1478 | } |
1329 | 1479 | ||
@@ -1377,11 +1527,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1377 | more_data += sizeof(u64); | 1527 | more_data += sizeof(u64); |
1378 | } | 1528 | } |
1379 | 1529 | ||
1380 | dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n", | 1530 | dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", |
1381 | (void *)(offset + head), | 1531 | (void *)(offset + head), |
1382 | (void *)(long)(event->header.size), | 1532 | (void *)(long)(event->header.size), |
1383 | event->header.misc, | 1533 | event->header.misc, |
1384 | event->ip.pid, | 1534 | event->ip.pid, event->ip.tid, |
1385 | (void *)(long)ip, | 1535 | (void *)(long)ip, |
1386 | (long long)period); | 1536 | (long long)period); |
1387 | 1537 | ||
@@ -1441,10 +1591,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1441 | if (show & show_mask) { | 1591 | if (show & show_mask) { |
1442 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); | 1592 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); |
1443 | 1593 | ||
1444 | if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name)) | 1594 | if (dso_list && (!dso || !dso->name || |
1595 | !strlist__has_entry(dso_list, dso->name))) | ||
1445 | return 0; | 1596 | return 0; |
1446 | 1597 | ||
1447 | if (sym_list && sym && !strlist__has_entry(sym_list, sym->name)) | 1598 | if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name))) |
1448 | return 0; | 1599 | return 0; |
1449 | 1600 | ||
1450 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { | 1601 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { |
@@ -1463,10 +1614,11 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | |||
1463 | struct thread *thread = threads__findnew(event->mmap.pid); | 1614 | struct thread *thread = threads__findnew(event->mmap.pid); |
1464 | struct map *map = map__new(&event->mmap); | 1615 | struct map *map = map__new(&event->mmap); |
1465 | 1616 | ||
1466 | dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n", | 1617 | dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", |
1467 | (void *)(offset + head), | 1618 | (void *)(offset + head), |
1468 | (void *)(long)(event->header.size), | 1619 | (void *)(long)(event->header.size), |
1469 | event->mmap.pid, | 1620 | event->mmap.pid, |
1621 | event->mmap.tid, | ||
1470 | (void *)(long)event->mmap.start, | 1622 | (void *)(long)event->mmap.start, |
1471 | (void *)(long)event->mmap.len, | 1623 | (void *)(long)event->mmap.len, |
1472 | (void *)(long)event->mmap.pgoff, | 1624 | (void *)(long)event->mmap.pgoff, |
@@ -1504,15 +1656,27 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head) | |||
1504 | } | 1656 | } |
1505 | 1657 | ||
1506 | static int | 1658 | static int |
1507 | process_fork_event(event_t *event, unsigned long offset, unsigned long head) | 1659 | process_task_event(event_t *event, unsigned long offset, unsigned long head) |
1508 | { | 1660 | { |
1509 | struct thread *thread = threads__findnew(event->fork.pid); | 1661 | struct thread *thread = threads__findnew(event->fork.pid); |
1510 | struct thread *parent = threads__findnew(event->fork.ppid); | 1662 | struct thread *parent = threads__findnew(event->fork.ppid); |
1511 | 1663 | ||
1512 | dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n", | 1664 | dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n", |
1513 | (void *)(offset + head), | 1665 | (void *)(offset + head), |
1514 | (void *)(long)(event->header.size), | 1666 | (void *)(long)(event->header.size), |
1515 | event->fork.pid, event->fork.ppid); | 1667 | event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT", |
1668 | event->fork.pid, event->fork.tid, | ||
1669 | event->fork.ppid, event->fork.ptid); | ||
1670 | |||
1671 | /* | ||
1672 | * A thread clone will have the same PID for both | ||
1673 | * parent and child. | ||
1674 | */ | ||
1675 | if (thread == parent) | ||
1676 | return 0; | ||
1677 | |||
1678 | if (event->header.type == PERF_EVENT_EXIT) | ||
1679 | return 0; | ||
1516 | 1680 | ||
1517 | if (!thread || !parent || thread__fork(thread, parent)) { | 1681 | if (!thread || !parent || thread__fork(thread, parent)) { |
1518 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); | 1682 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); |
@@ -1524,19 +1688,6 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head) | |||
1524 | } | 1688 | } |
1525 | 1689 | ||
1526 | static int | 1690 | static int |
1527 | process_period_event(event_t *event, unsigned long offset, unsigned long head) | ||
1528 | { | ||
1529 | dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n", | ||
1530 | (void *)(offset + head), | ||
1531 | (void *)(long)(event->header.size), | ||
1532 | event->period.time, | ||
1533 | event->period.id, | ||
1534 | event->period.sample_period); | ||
1535 | |||
1536 | return 0; | ||
1537 | } | ||
1538 | |||
1539 | static int | ||
1540 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) | 1691 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) |
1541 | { | 1692 | { |
1542 | dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", | 1693 | dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", |
@@ -1586,14 +1737,37 @@ static void trace_event(event_t *event) | |||
1586 | dprintf(".\n"); | 1737 | dprintf(".\n"); |
1587 | } | 1738 | } |
1588 | 1739 | ||
1740 | static struct perf_header *header; | ||
1741 | |||
1742 | static struct perf_counter_attr *perf_header__find_attr(u64 id) | ||
1743 | { | ||
1744 | int i; | ||
1745 | |||
1746 | for (i = 0; i < header->attrs; i++) { | ||
1747 | struct perf_header_attr *attr = header->attr[i]; | ||
1748 | int j; | ||
1749 | |||
1750 | for (j = 0; j < attr->ids; j++) { | ||
1751 | if (attr->id[j] == id) | ||
1752 | return &attr->attr; | ||
1753 | } | ||
1754 | } | ||
1755 | |||
1756 | return NULL; | ||
1757 | } | ||
1758 | |||
1589 | static int | 1759 | static int |
1590 | process_read_event(event_t *event, unsigned long offset, unsigned long head) | 1760 | process_read_event(event_t *event, unsigned long offset, unsigned long head) |
1591 | { | 1761 | { |
1592 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n", | 1762 | struct perf_counter_attr *attr = perf_header__find_attr(event->read.id); |
1763 | |||
1764 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", | ||
1593 | (void *)(offset + head), | 1765 | (void *)(offset + head), |
1594 | (void *)(long)(event->header.size), | 1766 | (void *)(long)(event->header.size), |
1595 | event->read.pid, | 1767 | event->read.pid, |
1596 | event->read.tid, | 1768 | event->read.tid, |
1769 | attr ? __event_name(attr->type, attr->config) | ||
1770 | : "FAIL", | ||
1597 | event->read.value); | 1771 | event->read.value); |
1598 | 1772 | ||
1599 | return 0; | 1773 | return 0; |
@@ -1615,10 +1789,8 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1615 | return process_comm_event(event, offset, head); | 1789 | return process_comm_event(event, offset, head); |
1616 | 1790 | ||
1617 | case PERF_EVENT_FORK: | 1791 | case PERF_EVENT_FORK: |
1618 | return process_fork_event(event, offset, head); | 1792 | case PERF_EVENT_EXIT: |
1619 | 1793 | return process_task_event(event, offset, head); | |
1620 | case PERF_EVENT_PERIOD: | ||
1621 | return process_period_event(event, offset, head); | ||
1622 | 1794 | ||
1623 | case PERF_EVENT_LOST: | 1795 | case PERF_EVENT_LOST: |
1624 | return process_lost_event(event, offset, head); | 1796 | return process_lost_event(event, offset, head); |
@@ -1641,8 +1813,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1641 | return 0; | 1813 | return 0; |
1642 | } | 1814 | } |
1643 | 1815 | ||
1644 | static struct perf_header *header; | ||
1645 | |||
1646 | static u64 perf_header__sample_type(void) | 1816 | static u64 perf_header__sample_type(void) |
1647 | { | 1817 | { |
1648 | u64 sample_type = 0; | 1818 | u64 sample_type = 0; |
@@ -1687,6 +1857,11 @@ static int __cmd_report(void) | |||
1687 | exit(-1); | 1857 | exit(-1); |
1688 | } | 1858 | } |
1689 | 1859 | ||
1860 | if (!force && (stat.st_uid != geteuid())) { | ||
1861 | fprintf(stderr, "file: %s not owned by current user\n", input_name); | ||
1862 | exit(-1); | ||
1863 | } | ||
1864 | |||
1690 | if (!stat.st_size) { | 1865 | if (!stat.st_size) { |
1691 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | 1866 | fprintf(stderr, "zero-sized file, nothing to do!\n"); |
1692 | exit(0); | 1867 | exit(0); |
@@ -1710,6 +1885,13 @@ static int __cmd_report(void) | |||
1710 | " -g?\n"); | 1885 | " -g?\n"); |
1711 | exit(-1); | 1886 | exit(-1); |
1712 | } | 1887 | } |
1888 | } else if (callchain_param.mode != CHAIN_NONE && !callchain) { | ||
1889 | callchain = 1; | ||
1890 | if (register_callchain_param(&callchain_param) < 0) { | ||
1891 | fprintf(stderr, "Can't register callchain" | ||
1892 | " params\n"); | ||
1893 | exit(-1); | ||
1894 | } | ||
1713 | } | 1895 | } |
1714 | 1896 | ||
1715 | if (load_kernel() < 0) { | 1897 | if (load_kernel() < 0) { |
@@ -1848,6 +2030,13 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
1848 | else if (!strncmp(tok, "fractal", strlen(arg))) | 2030 | else if (!strncmp(tok, "fractal", strlen(arg))) |
1849 | callchain_param.mode = CHAIN_GRAPH_REL; | 2031 | callchain_param.mode = CHAIN_GRAPH_REL; |
1850 | 2032 | ||
2033 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
2034 | callchain_param.mode = CHAIN_NONE; | ||
2035 | callchain = 0; | ||
2036 | |||
2037 | return 0; | ||
2038 | } | ||
2039 | |||
1851 | else | 2040 | else |
1852 | return -1; | 2041 | return -1; |
1853 | 2042 | ||
@@ -1881,8 +2070,11 @@ static const struct option options[] = { | |||
1881 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 2070 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1882 | "dump raw trace in ASCII"), | 2071 | "dump raw trace in ASCII"), |
1883 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 2072 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
2073 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | ||
1884 | OPT_BOOLEAN('m', "modules", &modules, | 2074 | OPT_BOOLEAN('m', "modules", &modules, |
1885 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 2075 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
2076 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, | ||
2077 | "Show a column with the number of samples"), | ||
1886 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 2078 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
1887 | "sort by key(s): pid, comm, dso, symbol, parent"), | 2079 | "sort by key(s): pid, comm, dso, symbol, parent"), |
1888 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 2080 | OPT_BOOLEAN('P', "full-paths", &full_paths, |
@@ -1891,15 +2083,21 @@ static const struct option options[] = { | |||
1891 | "regex filter to identify parent, see: '--sort parent'"), | 2083 | "regex filter to identify parent, see: '--sort parent'"), |
1892 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, | 2084 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, |
1893 | "Only display entries with parent-match"), | 2085 | "Only display entries with parent-match"), |
1894 | OPT_CALLBACK_DEFAULT('c', "callchain", NULL, "output_type,min_percent", | 2086 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", |
1895 | "Display callchains using output_type and min percent threshold. " | 2087 | "Display callchains using output_type and min percent threshold. " |
1896 | "Default: flat,0", &parse_callchain_opt, callchain_default_opt), | 2088 | "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), |
1897 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", | 2089 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", |
1898 | "only consider symbols in these dsos"), | 2090 | "only consider symbols in these dsos"), |
1899 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", | 2091 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", |
1900 | "only consider symbols in these comms"), | 2092 | "only consider symbols in these comms"), |
1901 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", | 2093 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", |
1902 | "only consider these symbols"), | 2094 | "only consider these symbols"), |
2095 | OPT_STRING('w', "column-widths", &col_width_list_str, | ||
2096 | "width[,width...]", | ||
2097 | "don't try to adjust column width, use these fixed values"), | ||
2098 | OPT_STRING('t', "field-separator", &field_sep, "separator", | ||
2099 | "separator for columns, no spaces will be added between " | ||
2100 | "columns '.' is reserved."), | ||
1903 | OPT_END() | 2101 | OPT_END() |
1904 | }; | 2102 | }; |
1905 | 2103 | ||
@@ -1919,7 +2117,8 @@ static void setup_sorting(void) | |||
1919 | } | 2117 | } |
1920 | 2118 | ||
1921 | static void setup_list(struct strlist **list, const char *list_str, | 2119 | static void setup_list(struct strlist **list, const char *list_str, |
1922 | const char *list_name) | 2120 | struct sort_entry *se, const char *list_name, |
2121 | FILE *fp) | ||
1923 | { | 2122 | { |
1924 | if (list_str) { | 2123 | if (list_str) { |
1925 | *list = strlist__new(true, list_str); | 2124 | *list = strlist__new(true, list_str); |
@@ -1928,6 +2127,11 @@ static void setup_list(struct strlist **list, const char *list_str, | |||
1928 | list_name); | 2127 | list_name); |
1929 | exit(129); | 2128 | exit(129); |
1930 | } | 2129 | } |
2130 | if (strlist__nr_entries(*list) == 1) { | ||
2131 | fprintf(fp, "# %s: %s\n", list_name, | ||
2132 | strlist__entry(*list, 0)->s); | ||
2133 | se->elide = true; | ||
2134 | } | ||
1931 | } | 2135 | } |
1932 | } | 2136 | } |
1933 | 2137 | ||
@@ -1941,9 +2145,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
1941 | 2145 | ||
1942 | setup_sorting(); | 2146 | setup_sorting(); |
1943 | 2147 | ||
1944 | if (parent_pattern != default_parent_pattern) | 2148 | if (parent_pattern != default_parent_pattern) { |
1945 | sort_dimension__add("parent"); | 2149 | sort_dimension__add("parent"); |
1946 | else | 2150 | sort_parent.elide = 1; |
2151 | } else | ||
1947 | exclude_other = 0; | 2152 | exclude_other = 0; |
1948 | 2153 | ||
1949 | /* | 2154 | /* |
@@ -1952,11 +2157,17 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
1952 | if (argc) | 2157 | if (argc) |
1953 | usage_with_options(report_usage, options); | 2158 | usage_with_options(report_usage, options); |
1954 | 2159 | ||
1955 | setup_list(&dso_list, dso_list_str, "dso"); | ||
1956 | setup_list(&comm_list, comm_list_str, "comm"); | ||
1957 | setup_list(&sym_list, sym_list_str, "symbol"); | ||
1958 | |||
1959 | setup_pager(); | 2160 | setup_pager(); |
1960 | 2161 | ||
2162 | setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout); | ||
2163 | setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout); | ||
2164 | setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout); | ||
2165 | |||
2166 | if (field_sep && *field_sep == '.') { | ||
2167 | fputs("'.' is the only non valid --field-separator argument\n", | ||
2168 | stderr); | ||
2169 | exit(129); | ||
2170 | } | ||
2171 | |||
1961 | return __cmd_report(); | 2172 | return __cmd_report(); |
1962 | } | 2173 | } |