diff options
Diffstat (limited to 'tools/perf/util/hist.c')
-rw-r--r-- | tools/perf/util/hist.c | 214 |
1 files changed, 145 insertions, 69 deletions
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 784ee0bdda77..e7263d49bcf0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -5,11 +5,61 @@ | |||
5 | #include "sort.h" | 5 | #include "sort.h" |
6 | #include <math.h> | 6 | #include <math.h> |
7 | 7 | ||
8 | enum hist_filter { | ||
9 | HIST_FILTER__DSO, | ||
10 | HIST_FILTER__THREAD, | ||
11 | HIST_FILTER__PARENT, | ||
12 | }; | ||
13 | |||
8 | struct callchain_param callchain_param = { | 14 | struct callchain_param callchain_param = { |
9 | .mode = CHAIN_GRAPH_REL, | 15 | .mode = CHAIN_GRAPH_REL, |
10 | .min_percent = 0.5 | 16 | .min_percent = 0.5 |
11 | }; | 17 | }; |
12 | 18 | ||
19 | u16 hists__col_len(struct hists *self, enum hist_column col) | ||
20 | { | ||
21 | return self->col_len[col]; | ||
22 | } | ||
23 | |||
24 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) | ||
25 | { | ||
26 | self->col_len[col] = len; | ||
27 | } | ||
28 | |||
29 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) | ||
30 | { | ||
31 | if (len > hists__col_len(self, col)) { | ||
32 | hists__set_col_len(self, col, len); | ||
33 | return true; | ||
34 | } | ||
35 | return false; | ||
36 | } | ||
37 | |||
38 | static void hists__reset_col_len(struct hists *self) | ||
39 | { | ||
40 | enum hist_column col; | ||
41 | |||
42 | for (col = 0; col < HISTC_NR_COLS; ++col) | ||
43 | hists__set_col_len(self, col, 0); | ||
44 | } | ||
45 | |||
46 | static void hists__calc_col_len(struct hists *self, struct hist_entry *h) | ||
47 | { | ||
48 | u16 len; | ||
49 | |||
50 | if (h->ms.sym) | ||
51 | hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); | ||
52 | |||
53 | len = thread__comm_len(h->thread); | ||
54 | if (hists__new_col_len(self, HISTC_COMM, len)) | ||
55 | hists__set_col_len(self, HISTC_THREAD, len + 6); | ||
56 | |||
57 | if (h->ms.map) { | ||
58 | len = dso__name_len(h->ms.map->dso); | ||
59 | hists__new_col_len(self, HISTC_DSO, len); | ||
60 | } | ||
61 | } | ||
62 | |||
13 | static void hist_entry__add_cpumode_period(struct hist_entry *self, | 63 | static void hist_entry__add_cpumode_period(struct hist_entry *self, |
14 | unsigned int cpumode, u64 period) | 64 | unsigned int cpumode, u64 period) |
15 | { | 65 | { |
@@ -43,6 +93,8 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
43 | if (self != NULL) { | 93 | if (self != NULL) { |
44 | *self = *template; | 94 | *self = *template; |
45 | self->nr_events = 1; | 95 | self->nr_events = 1; |
96 | if (self->ms.map) | ||
97 | self->ms.map->referenced = true; | ||
46 | if (symbol_conf.use_callchain) | 98 | if (symbol_conf.use_callchain) |
47 | callchain_init(self->callchain); | 99 | callchain_init(self->callchain); |
48 | } | 100 | } |
@@ -50,11 +102,19 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
50 | return self; | 102 | return self; |
51 | } | 103 | } |
52 | 104 | ||
53 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) | 105 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) |
54 | { | 106 | { |
55 | if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) | 107 | if (!h->filtered) { |
56 | self->max_sym_namelen = entry->ms.sym->namelen; | 108 | hists__calc_col_len(self, h); |
57 | ++self->nr_entries; | 109 | ++self->nr_entries; |
110 | } | ||
111 | } | ||
112 | |||
113 | static u8 symbol__parent_filter(const struct symbol *parent) | ||
114 | { | ||
115 | if (symbol_conf.exclude_other && parent == NULL) | ||
116 | return 1 << HIST_FILTER__PARENT; | ||
117 | return 0; | ||
58 | } | 118 | } |
59 | 119 | ||
60 | struct hist_entry *__hists__add_entry(struct hists *self, | 120 | struct hist_entry *__hists__add_entry(struct hists *self, |
@@ -70,10 +130,12 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
70 | .map = al->map, | 130 | .map = al->map, |
71 | .sym = al->sym, | 131 | .sym = al->sym, |
72 | }, | 132 | }, |
133 | .cpu = al->cpu, | ||
73 | .ip = al->addr, | 134 | .ip = al->addr, |
74 | .level = al->level, | 135 | .level = al->level, |
75 | .period = period, | 136 | .period = period, |
76 | .parent = sym_parent, | 137 | .parent = sym_parent, |
138 | .filtered = symbol__parent_filter(sym_parent), | ||
77 | }; | 139 | }; |
78 | int cmp; | 140 | int cmp; |
79 | 141 | ||
@@ -191,7 +253,7 @@ void hists__collapse_resort(struct hists *self) | |||
191 | tmp = RB_ROOT; | 253 | tmp = RB_ROOT; |
192 | next = rb_first(&self->entries); | 254 | next = rb_first(&self->entries); |
193 | self->nr_entries = 0; | 255 | self->nr_entries = 0; |
194 | self->max_sym_namelen = 0; | 256 | hists__reset_col_len(self); |
195 | 257 | ||
196 | while (next) { | 258 | while (next) { |
197 | n = rb_entry(next, struct hist_entry, rb_node); | 259 | n = rb_entry(next, struct hist_entry, rb_node); |
@@ -248,7 +310,7 @@ void hists__output_resort(struct hists *self) | |||
248 | next = rb_first(&self->entries); | 310 | next = rb_first(&self->entries); |
249 | 311 | ||
250 | self->nr_entries = 0; | 312 | self->nr_entries = 0; |
251 | self->max_sym_namelen = 0; | 313 | hists__reset_col_len(self); |
252 | 314 | ||
253 | while (next) { | 315 | while (next) { |
254 | n = rb_entry(next, struct hist_entry, rb_node); | 316 | n = rb_entry(next, struct hist_entry, rb_node); |
@@ -515,8 +577,9 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
515 | } | 577 | } |
516 | 578 | ||
517 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | 579 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, |
518 | struct hists *pair_hists, bool show_displacement, | 580 | struct hists *hists, struct hists *pair_hists, |
519 | long displacement, bool color, u64 session_total) | 581 | bool show_displacement, long displacement, |
582 | bool color, u64 session_total) | ||
520 | { | 583 | { |
521 | struct sort_entry *se; | 584 | struct sort_entry *se; |
522 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; | 585 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; |
@@ -620,29 +683,25 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
620 | 683 | ||
621 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); | 684 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); |
622 | ret += se->se_snprintf(self, s + ret, size - ret, | 685 | ret += se->se_snprintf(self, s + ret, size - ret, |
623 | se->se_width ? *se->se_width : 0); | 686 | hists__col_len(hists, se->se_width_idx)); |
624 | } | 687 | } |
625 | 688 | ||
626 | return ret; | 689 | return ret; |
627 | } | 690 | } |
628 | 691 | ||
629 | int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, | 692 | int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, |
630 | bool show_displacement, long displacement, FILE *fp, | 693 | struct hists *pair_hists, bool show_displacement, |
631 | u64 session_total) | 694 | long displacement, FILE *fp, u64 session_total) |
632 | { | 695 | { |
633 | char bf[512]; | 696 | char bf[512]; |
634 | int ret; | 697 | hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, |
635 | 698 | show_displacement, displacement, | |
636 | ret = hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, | 699 | true, session_total); |
637 | show_displacement, displacement, | ||
638 | true, session_total); | ||
639 | if (!ret) | ||
640 | return 0; | ||
641 | |||
642 | return fprintf(fp, "%s\n", bf); | 700 | return fprintf(fp, "%s\n", bf); |
643 | } | 701 | } |
644 | 702 | ||
645 | static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, | 703 | static size_t hist_entry__fprintf_callchain(struct hist_entry *self, |
704 | struct hists *hists, FILE *fp, | ||
646 | u64 session_total) | 705 | u64 session_total) |
647 | { | 706 | { |
648 | int left_margin = 0; | 707 | int left_margin = 0; |
@@ -650,7 +709,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, | |||
650 | if (sort__first_dimension == SORT_COMM) { | 709 | if (sort__first_dimension == SORT_COMM) { |
651 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, | 710 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, |
652 | typeof(*se), list); | 711 | typeof(*se), list); |
653 | left_margin = se->se_width ? *se->se_width : 0; | 712 | left_margin = hists__col_len(hists, se->se_width_idx); |
654 | left_margin -= thread__comm_len(self->thread); | 713 | left_margin -= thread__comm_len(self->thread); |
655 | } | 714 | } |
656 | 715 | ||
@@ -721,17 +780,17 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
721 | continue; | 780 | continue; |
722 | } | 781 | } |
723 | width = strlen(se->se_header); | 782 | width = strlen(se->se_header); |
724 | if (se->se_width) { | 783 | if (symbol_conf.col_width_list_str) { |
725 | if (symbol_conf.col_width_list_str) { | 784 | if (col_width) { |
726 | if (col_width) { | 785 | hists__set_col_len(self, se->se_width_idx, |
727 | *se->se_width = atoi(col_width); | 786 | atoi(col_width)); |
728 | col_width = strchr(col_width, ','); | 787 | col_width = strchr(col_width, ','); |
729 | if (col_width) | 788 | if (col_width) |
730 | ++col_width; | 789 | ++col_width; |
731 | } | ||
732 | } | 790 | } |
733 | width = *se->se_width = max(*se->se_width, width); | ||
734 | } | 791 | } |
792 | if (!hists__new_col_len(self, se->se_width_idx, width)) | ||
793 | width = hists__col_len(self, se->se_width_idx); | ||
735 | fprintf(fp, " %*s", width, se->se_header); | 794 | fprintf(fp, " %*s", width, se->se_header); |
736 | } | 795 | } |
737 | fprintf(fp, "\n"); | 796 | fprintf(fp, "\n"); |
@@ -754,9 +813,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
754 | continue; | 813 | continue; |
755 | 814 | ||
756 | fprintf(fp, " "); | 815 | fprintf(fp, " "); |
757 | if (se->se_width) | 816 | width = hists__col_len(self, se->se_width_idx); |
758 | width = *se->se_width; | 817 | if (width == 0) |
759 | else | ||
760 | width = strlen(se->se_header); | 818 | width = strlen(se->se_header); |
761 | for (i = 0; i < width; i++) | 819 | for (i = 0; i < width; i++) |
762 | fprintf(fp, "."); | 820 | fprintf(fp, "."); |
@@ -767,7 +825,6 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
767 | print_entries: | 825 | print_entries: |
768 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 826 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
769 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 827 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
770 | int cnt; | ||
771 | 828 | ||
772 | if (show_displacement) { | 829 | if (show_displacement) { |
773 | if (h->pair != NULL) | 830 | if (h->pair != NULL) |
@@ -777,17 +834,12 @@ print_entries: | |||
777 | displacement = 0; | 834 | displacement = 0; |
778 | ++position; | 835 | ++position; |
779 | } | 836 | } |
780 | cnt = hist_entry__fprintf(h, pair, show_displacement, | 837 | ret += hist_entry__fprintf(h, self, pair, show_displacement, |
781 | displacement, fp, self->stats.total_period); | 838 | displacement, fp, self->stats.total_period); |
782 | /* Ignore those that didn't match the parent filter */ | ||
783 | if (!cnt) | ||
784 | continue; | ||
785 | |||
786 | ret += cnt; | ||
787 | 839 | ||
788 | if (symbol_conf.use_callchain) | 840 | if (symbol_conf.use_callchain) |
789 | ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); | 841 | ret += hist_entry__fprintf_callchain(h, self, fp, |
790 | 842 | self->stats.total_period); | |
791 | if (h->ms.map == NULL && verbose > 1) { | 843 | if (h->ms.map == NULL && verbose > 1) { |
792 | __map_groups__fprintf_maps(&h->thread->mg, | 844 | __map_groups__fprintf_maps(&h->thread->mg, |
793 | MAP__FUNCTION, verbose, fp); | 845 | MAP__FUNCTION, verbose, fp); |
@@ -800,10 +852,49 @@ print_entries: | |||
800 | return ret; | 852 | return ret; |
801 | } | 853 | } |
802 | 854 | ||
803 | enum hist_filter { | 855 | /* |
804 | HIST_FILTER__DSO, | 856 | * See hists__fprintf to match the column widths |
805 | HIST_FILTER__THREAD, | 857 | */ |
806 | }; | 858 | unsigned int hists__sort_list_width(struct hists *self) |
859 | { | ||
860 | struct sort_entry *se; | ||
861 | int ret = 9; /* total % */ | ||
862 | |||
863 | if (symbol_conf.show_cpu_utilization) { | ||
864 | ret += 7; /* count_sys % */ | ||
865 | ret += 6; /* count_us % */ | ||
866 | if (perf_guest) { | ||
867 | ret += 13; /* count_guest_sys % */ | ||
868 | ret += 12; /* count_guest_us % */ | ||
869 | } | ||
870 | } | ||
871 | |||
872 | if (symbol_conf.show_nr_samples) | ||
873 | ret += 11; | ||
874 | |||
875 | list_for_each_entry(se, &hist_entry__sort_list, list) | ||
876 | if (!se->elide) | ||
877 | ret += 2 + hists__col_len(self, se->se_width_idx); | ||
878 | |||
879 | return ret; | ||
880 | } | ||
881 | |||
882 | static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, | ||
883 | enum hist_filter filter) | ||
884 | { | ||
885 | h->filtered &= ~(1 << filter); | ||
886 | if (h->filtered) | ||
887 | return; | ||
888 | |||
889 | ++self->nr_entries; | ||
890 | if (h->ms.unfolded) | ||
891 | self->nr_entries += h->nr_rows; | ||
892 | h->row_offset = 0; | ||
893 | self->stats.total_period += h->period; | ||
894 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | ||
895 | |||
896 | hists__calc_col_len(self, h); | ||
897 | } | ||
807 | 898 | ||
808 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) | 899 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) |
809 | { | 900 | { |
@@ -811,7 +902,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) | |||
811 | 902 | ||
812 | self->nr_entries = self->stats.total_period = 0; | 903 | self->nr_entries = self->stats.total_period = 0; |
813 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 904 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
814 | self->max_sym_namelen = 0; | 905 | hists__reset_col_len(self); |
815 | 906 | ||
816 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 907 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
817 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 908 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
@@ -824,15 +915,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) | |||
824 | continue; | 915 | continue; |
825 | } | 916 | } |
826 | 917 | ||
827 | h->filtered &= ~(1 << HIST_FILTER__DSO); | 918 | hists__remove_entry_filter(self, h, HIST_FILTER__DSO); |
828 | if (!h->filtered) { | ||
829 | ++self->nr_entries; | ||
830 | self->stats.total_period += h->period; | ||
831 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | ||
832 | if (h->ms.sym && | ||
833 | self->max_sym_namelen < h->ms.sym->namelen) | ||
834 | self->max_sym_namelen = h->ms.sym->namelen; | ||
835 | } | ||
836 | } | 919 | } |
837 | } | 920 | } |
838 | 921 | ||
@@ -842,7 +925,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
842 | 925 | ||
843 | self->nr_entries = self->stats.total_period = 0; | 926 | self->nr_entries = self->stats.total_period = 0; |
844 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 927 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
845 | self->max_sym_namelen = 0; | 928 | hists__reset_col_len(self); |
846 | 929 | ||
847 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 930 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
848 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 931 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
@@ -851,15 +934,8 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
851 | h->filtered |= (1 << HIST_FILTER__THREAD); | 934 | h->filtered |= (1 << HIST_FILTER__THREAD); |
852 | continue; | 935 | continue; |
853 | } | 936 | } |
854 | h->filtered &= ~(1 << HIST_FILTER__THREAD); | 937 | |
855 | if (!h->filtered) { | 938 | hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); |
856 | ++self->nr_entries; | ||
857 | self->stats.total_period += h->period; | ||
858 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | ||
859 | if (h->ms.sym && | ||
860 | self->max_sym_namelen < h->ms.sym->namelen) | ||
861 | self->max_sym_namelen = h->ms.sym->namelen; | ||
862 | } | ||
863 | } | 939 | } |
864 | } | 940 | } |
865 | 941 | ||
@@ -1052,7 +1128,7 @@ fallback: | |||
1052 | dso, dso->long_name, sym, sym->name); | 1128 | dso, dso->long_name, sym, sym->name); |
1053 | 1129 | ||
1054 | snprintf(command, sizeof(command), | 1130 | snprintf(command, sizeof(command), |
1055 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", | 1131 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand", |
1056 | map__rip_2objdump(map, sym->start), | 1132 | map__rip_2objdump(map, sym->start), |
1057 | map__rip_2objdump(map, sym->end), | 1133 | map__rip_2objdump(map, sym->end), |
1058 | filename, filename); | 1134 | filename, filename); |