aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2014-05-22 05:37:40 -0400
committerIngo Molnar <mingo@kernel.org>2014-05-22 05:37:40 -0400
commite450f90e8c7d0bf70519223c1b848446ae63f313 (patch)
tree078bfe88e5c3fbe397c9ca0d4d8ab2e0ff63979d /tools
parent6480c56130ba073df84d57d61062ec4118b10bbe (diff)
parenteca8183699964579ca8a0b8d116bd1f4da0136f7 (diff)
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf into perf/core
Pull perf/core improvements and fixes from Jiri Olsa: * Android related fixes for pager and map dso resolving (Michael Lentine) * Add -F option for specifying output fields (Namhyung Kim) Signed-off-by: Jiri Olsa <jolsa@kernel.org> Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/Documentation/perf-diff.txt5
-rw-r--r--tools/perf/Documentation/perf-report.txt19
-rw-r--r--tools/perf/Documentation/perf-top.txt14
-rw-r--r--tools/perf/Makefile.perf1
-rw-r--r--tools/perf/builtin-diff.c7
-rw-r--r--tools/perf/builtin-report.c41
-rw-r--r--tools/perf/builtin-top.c20
-rw-r--r--tools/perf/tests/builtin-test.c4
-rw-r--r--tools/perf/tests/hists_common.c57
-rw-r--r--tools/perf/tests/hists_common.h3
-rw-r--r--tools/perf/tests/hists_filter.c38
-rw-r--r--tools/perf/tests/hists_link.c30
-rw-r--r--tools/perf/tests/hists_output.c618
-rw-r--r--tools/perf/tests/tests.h1
-rw-r--r--tools/perf/ui/browsers/hists.c104
-rw-r--r--tools/perf/ui/gtk/hists.c41
-rw-r--r--tools/perf/ui/hist.c244
-rw-r--r--tools/perf/ui/setup.c2
-rw-r--r--tools/perf/ui/stdio/hist.c79
-rw-r--r--tools/perf/util/hist.c83
-rw-r--r--tools/perf/util/hist.h27
-rw-r--r--tools/perf/util/map.c95
-rw-r--r--tools/perf/util/pager.c12
-rw-r--r--tools/perf/util/sort.c436
-rw-r--r--tools/perf/util/sort.h6
25 files changed, 1601 insertions, 386 deletions
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt
index fbfa1192923c..b3b8abae62b8 100644
--- a/tools/perf/Documentation/perf-diff.txt
+++ b/tools/perf/Documentation/perf-diff.txt
@@ -50,7 +50,8 @@ OPTIONS
50 50
51-s:: 51-s::
52--sort=:: 52--sort=::
53 Sort by key(s): pid, comm, dso, symbol. 53 Sort by key(s): pid, comm, dso, symbol, cpu, parent, srcline.
54 Please see description of --sort in the perf-report man page.
54 55
55-t:: 56-t::
56--field-separator=:: 57--field-separator=::
@@ -202,4 +203,4 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as:
202 203
203SEE ALSO 204SEE ALSO
204-------- 205--------
205linkperf:perf-record[1] 206linkperf:perf-record[1], linkperf:perf-report[1]
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 09af66298564..a1b5185402d5 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -79,6 +79,15 @@ OPTIONS
79 abort cost. This is the global weight. 79 abort cost. This is the global weight.
80 - local_weight: Local weight version of the weight above. 80 - local_weight: Local weight version of the weight above.
81 - transaction: Transaction abort flags. 81 - transaction: Transaction abort flags.
82 - overhead: Overhead percentage of sample
83 - overhead_sys: Overhead percentage of sample running in system mode
84 - overhead_us: Overhead percentage of sample running in user mode
85 - overhead_guest_sys: Overhead percentage of sample running in system mode
86 on guest machine
87 - overhead_guest_us: Overhead percentage of sample running in user mode on
88 guest machine
89 - sample: Number of sample
90 - period: Raw number of event count of sample
82 91
83 By default, comm, dso and symbol keys are used. 92 By default, comm, dso and symbol keys are used.
84 (i.e. --sort comm,dso,symbol) 93 (i.e. --sort comm,dso,symbol)
@@ -98,6 +107,16 @@ OPTIONS
98 And default sort keys are changed to comm, dso_from, symbol_from, dso_to 107 And default sort keys are changed to comm, dso_from, symbol_from, dso_to
99 and symbol_to, see '--branch-stack'. 108 and symbol_to, see '--branch-stack'.
100 109
110-F::
111--fields=::
112 Specify output field - multiple keys can be specified in CSV format.
113 Following fields are available:
114 overhead, overhead_sys, overhead_us, sample and period.
115 Also it can contain any sort key(s).
116
117 By default, every sort keys not specified in -F will be appended
118 automatically.
119
101-p:: 120-p::
102--parent=<regex>:: 121--parent=<regex>::
103 A regex filter to identify parent. The parent is a caller of this 122 A regex filter to identify parent. The parent is a caller of this
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index 64ed79c43639..dcfa54c851e9 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -113,7 +113,17 @@ Default is to monitor all CPUS.
113-s:: 113-s::
114--sort:: 114--sort::
115 Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, 115 Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight,
116 local_weight, abort, in_tx, transaction 116 local_weight, abort, in_tx, transaction, overhead, sample, period.
117 Please see description of --sort in the perf-report man page.
118
119--fields=::
120 Specify output field - multiple keys can be specified in CSV format.
121 Following fields are available:
122 overhead, overhead_sys, overhead_us, sample and period.
123 Also it can contain any sort key(s).
124
125 By default, every sort keys not specified in --field will be appended
126 automatically.
117 127
118-n:: 128-n::
119--show-nr-samples:: 129--show-nr-samples::
@@ -212,4 +222,4 @@ Pressing any unmapped key displays a menu, and prompts for input.
212 222
213SEE ALSO 223SEE ALSO
214-------- 224--------
215linkperf:perf-stat[1], linkperf:perf-list[1] 225linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-report[1]
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index dea2d633c374..02f0a4dd1a80 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -399,6 +399,7 @@ LIB_OBJS += $(OUTPUT)tests/pmu.o
399LIB_OBJS += $(OUTPUT)tests/hists_common.o 399LIB_OBJS += $(OUTPUT)tests/hists_common.o
400LIB_OBJS += $(OUTPUT)tests/hists_link.o 400LIB_OBJS += $(OUTPUT)tests/hists_link.o
401LIB_OBJS += $(OUTPUT)tests/hists_filter.o 401LIB_OBJS += $(OUTPUT)tests/hists_filter.o
402LIB_OBJS += $(OUTPUT)tests/hists_output.o
402LIB_OBJS += $(OUTPUT)tests/python-use.o 403LIB_OBJS += $(OUTPUT)tests/python-use.o
403LIB_OBJS += $(OUTPUT)tests/bp_signal.o 404LIB_OBJS += $(OUTPUT)tests/bp_signal.o
404LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o 405LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index f3b10dcf6838..8bff543acaab 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -60,7 +60,6 @@ static int data__files_cnt;
60#define data__for_each_file(i, d) data__for_each_file_start(i, d, 0) 60#define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
61#define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1) 61#define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)
62 62
63static char diff__default_sort_order[] = "dso,symbol";
64static bool force; 63static bool force;
65static bool show_period; 64static bool show_period;
66static bool show_formula; 65static bool show_formula;
@@ -741,7 +740,8 @@ static const struct option options[] = {
741 OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", 740 OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
742 "only consider these symbols"), 741 "only consider these symbols"),
743 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 742 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
744 "sort by key(s): pid, comm, dso, symbol, parent"), 743 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
744 " Please refer the man page for the complete list."),
745 OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", 745 OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
746 "separator for columns, no spaces will be added between " 746 "separator for columns, no spaces will be added between "
747 "columns '.' is reserved."), 747 "columns '.' is reserved."),
@@ -1141,7 +1141,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
1141{ 1141{
1142 perf_config(perf_default_config, NULL); 1142 perf_config(perf_default_config, NULL);
1143 1143
1144 sort_order = diff__default_sort_order;
1145 argc = parse_options(argc, argv, options, diff_usage, 0); 1144 argc = parse_options(argc, argv, options, diff_usage, 0);
1146 1145
1147 if (symbol__init() < 0) 1146 if (symbol__init() < 0)
@@ -1152,6 +1151,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
1152 1151
1153 ui_init(); 1152 ui_init();
1154 1153
1154 sort__mode = SORT_MODE__DIFF;
1155
1155 if (setup_sorting() < 0) 1156 if (setup_sorting() < 0)
1156 usage_with_options(diff_usage, options); 1157 usage_with_options(diff_usage, options);
1157 1158
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 89c95289fd51..bc0eec1ce4be 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -699,10 +699,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
699 OPT_BOOLEAN(0, "header-only", &report.header_only, 699 OPT_BOOLEAN(0, "header-only", &report.header_only,
700 "Show only data header."), 700 "Show only data header."),
701 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 701 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
702 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," 702 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
703 " dso_to, dso_from, symbol_to, symbol_from, mispredict," 703 " Please refer the man page for the complete list."),
704 " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, " 704 OPT_STRING('F', "fields", &field_order, "key[,keys...]",
705 "snoop, locked, abort, in_tx, transaction"), 705 "output field(s): overhead, period, sample plus all of sort keys"),
706 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, 706 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
707 "Show sample percentage for different cpu modes"), 707 "Show sample percentage for different cpu modes"),
708 OPT_STRING('p', "parent", &parent_pattern, "regex", 708 OPT_STRING('p', "parent", &parent_pattern, "regex",
@@ -807,52 +807,31 @@ repeat:
807 if (branch_mode == -1 && has_br_stack) 807 if (branch_mode == -1 && has_br_stack)
808 sort__mode = SORT_MODE__BRANCH; 808 sort__mode = SORT_MODE__BRANCH;
809 809
810 /* sort__mode could be NORMAL if --no-branch-stack */
811 if (sort__mode == SORT_MODE__BRANCH) {
812 /*
813 * if no sort_order is provided, then specify
814 * branch-mode specific order
815 */
816 if (sort_order == default_sort_order)
817 sort_order = "comm,dso_from,symbol_from,"
818 "dso_to,symbol_to";
819
820 }
821 if (report.mem_mode) { 810 if (report.mem_mode) {
822 if (sort__mode == SORT_MODE__BRANCH) { 811 if (sort__mode == SORT_MODE__BRANCH) {
823 pr_err("branch and mem mode incompatible\n"); 812 pr_err("branch and mem mode incompatible\n");
824 goto error; 813 goto error;
825 } 814 }
826 sort__mode = SORT_MODE__MEMORY; 815 sort__mode = SORT_MODE__MEMORY;
827
828 /*
829 * if no sort_order is provided, then specify
830 * branch-mode specific order
831 */
832 if (sort_order == default_sort_order)
833 sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
834 } 816 }
835 817
836 if (setup_sorting() < 0) { 818 if (setup_sorting() < 0) {
837 parse_options_usage(report_usage, options, "s", 1); 819 if (sort_order)
820 parse_options_usage(report_usage, options, "s", 1);
821 if (field_order)
822 parse_options_usage(sort_order ? NULL : report_usage,
823 options, "F", 1);
838 goto error; 824 goto error;
839 } 825 }
840 826
841 if (parent_pattern != default_parent_pattern) {
842 if (sort_dimension__add("parent") < 0)
843 goto error;
844 }
845
846 /* Force tty output for header output. */ 827 /* Force tty output for header output. */
847 if (report.header || report.header_only) 828 if (report.header || report.header_only)
848 use_browser = 0; 829 use_browser = 0;
849 830
850 if (strcmp(input_name, "-") != 0) 831 if (strcmp(input_name, "-") != 0)
851 setup_browser(true); 832 setup_browser(true);
852 else { 833 else
853 use_browser = 0; 834 use_browser = 0;
854 perf_hpp__init();
855 }
856 835
857 if (report.header || report.header_only) { 836 if (report.header || report.header_only) {
858 perf_session__fprintf_info(session, stdout, 837 perf_session__fprintf_info(session, stdout,
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 37d30460bada..5b389ce4cd15 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1083,8 +1083,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1083 OPT_INCR('v', "verbose", &verbose, 1083 OPT_INCR('v', "verbose", &verbose,
1084 "be more verbose (show counter open errors, etc)"), 1084 "be more verbose (show counter open errors, etc)"),
1085 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 1085 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
1086 "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight," 1086 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
1087 " abort, in_tx, transaction"), 1087 " Please refer the man page for the complete list."),
1088 OPT_STRING(0, "fields", &field_order, "key[,keys...]",
1089 "output field(s): overhead, period, sample plus all of sort keys"),
1088 OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, 1090 OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
1089 "Show a column with the number of samples"), 1091 "Show a column with the number of samples"),
1090 OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts, 1092 OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts,
@@ -1137,17 +1139,19 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1137 if (argc) 1139 if (argc)
1138 usage_with_options(top_usage, options); 1140 usage_with_options(top_usage, options);
1139 1141
1140 if (sort_order == default_sort_order) 1142 sort__mode = SORT_MODE__TOP;
1141 sort_order = "dso,symbol"; 1143 /* display thread wants entries to be collapsed in a different tree */
1144 sort__need_collapse = 1;
1142 1145
1143 if (setup_sorting() < 0) { 1146 if (setup_sorting() < 0) {
1144 parse_options_usage(top_usage, options, "s", 1); 1147 if (sort_order)
1148 parse_options_usage(top_usage, options, "s", 1);
1149 if (field_order)
1150 parse_options_usage(sort_order ? NULL : top_usage,
1151 options, "fields", 0);
1145 goto out_delete_evlist; 1152 goto out_delete_evlist;
1146 } 1153 }
1147 1154
1148 /* display thread wants entries to be collapsed in a different tree */
1149 sort__need_collapse = 1;
1150
1151 if (top.use_stdio) 1155 if (top.use_stdio)
1152 use_browser = 0; 1156 use_browser = 0;
1153 else if (top.use_tui) 1157 else if (top.use_tui)
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 5e0764b09317..831f52cae197 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -136,6 +136,10 @@ static struct test {
136 .func = test__thread_mg_share, 136 .func = test__thread_mg_share,
137 }, 137 },
138 { 138 {
139 .desc = "Test output sorting of hist entries",
140 .func = test__hists_output,
141 },
142 {
139 .func = NULL, 143 .func = NULL,
140 }, 144 },
141}; 145};
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c
index 44655b395bb9..e4e01aadc3be 100644
--- a/tools/perf/tests/hists_common.c
+++ b/tools/perf/tests/hists_common.c
@@ -146,3 +146,60 @@ out:
146 machine__delete(machine); 146 machine__delete(machine);
147 return NULL; 147 return NULL;
148} 148}
149
150void print_hists_in(struct hists *hists)
151{
152 int i = 0;
153 struct rb_root *root;
154 struct rb_node *node;
155
156 if (sort__need_collapse)
157 root = &hists->entries_collapsed;
158 else
159 root = hists->entries_in;
160
161 pr_info("----- %s --------\n", __func__);
162 node = rb_first(root);
163 while (node) {
164 struct hist_entry *he;
165
166 he = rb_entry(node, struct hist_entry, rb_node_in);
167
168 if (!he->filtered) {
169 pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
170 i, thread__comm_str(he->thread),
171 he->ms.map->dso->short_name,
172 he->ms.sym->name, he->stat.period);
173 }
174
175 i++;
176 node = rb_next(node);
177 }
178}
179
180void print_hists_out(struct hists *hists)
181{
182 int i = 0;
183 struct rb_root *root;
184 struct rb_node *node;
185
186 root = &hists->entries;
187
188 pr_info("----- %s --------\n", __func__);
189 node = rb_first(root);
190 while (node) {
191 struct hist_entry *he;
192
193 he = rb_entry(node, struct hist_entry, rb_node);
194
195 if (!he->filtered) {
196 pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"\n",
197 i, thread__comm_str(he->thread), he->thread->tid,
198 he->ms.map->dso->short_name,
199 he->ms.sym->name, he->stat.period);
200 }
201
202 i++;
203 node = rb_next(node);
204 }
205}
diff --git a/tools/perf/tests/hists_common.h b/tools/perf/tests/hists_common.h
index 2528b8fc105a..1415ae69d7b6 100644
--- a/tools/perf/tests/hists_common.h
+++ b/tools/perf/tests/hists_common.h
@@ -41,4 +41,7 @@ struct machines;
41 */ 41 */
42struct machine *setup_fake_machine(struct machines *machines); 42struct machine *setup_fake_machine(struct machines *machines);
43 43
44void print_hists_in(struct hists *hists);
45void print_hists_out(struct hists *hists);
46
44#endif /* __PERF_TESTS__HISTS_COMMON_H__ */ 47#endif /* __PERF_TESTS__HISTS_COMMON_H__ */
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c
index 4617a8bee29b..c5ba924a3581 100644
--- a/tools/perf/tests/hists_filter.c
+++ b/tools/perf/tests/hists_filter.c
@@ -98,33 +98,6 @@ out:
98 return TEST_FAIL; 98 return TEST_FAIL;
99} 99}
100 100
101static void print_hists(struct hists *hists)
102{
103 int i = 0;
104 struct rb_root *root;
105 struct rb_node *node;
106
107 root = &hists->entries;
108
109 pr_info("----- %s --------\n", __func__);
110 node = rb_first(root);
111 while (node) {
112 struct hist_entry *he;
113
114 he = rb_entry(node, struct hist_entry, rb_node);
115
116 if (!he->filtered) {
117 pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
118 i, thread__comm_str(he->thread),
119 he->ms.map->dso->short_name,
120 he->ms.sym->name, he->stat.period);
121 }
122
123 i++;
124 node = rb_next(node);
125 }
126}
127
128int test__hists_filter(void) 101int test__hists_filter(void)
129{ 102{
130 int err = TEST_FAIL; 103 int err = TEST_FAIL;
@@ -169,7 +142,7 @@ int test__hists_filter(void)
169 142
170 if (verbose > 2) { 143 if (verbose > 2) {
171 pr_info("Normal histogram\n"); 144 pr_info("Normal histogram\n");
172 print_hists(hists); 145 print_hists_out(hists);
173 } 146 }
174 147
175 TEST_ASSERT_VAL("Invalid nr samples", 148 TEST_ASSERT_VAL("Invalid nr samples",
@@ -193,7 +166,7 @@ int test__hists_filter(void)
193 166
194 if (verbose > 2) { 167 if (verbose > 2) {
195 pr_info("Histogram for thread filter\n"); 168 pr_info("Histogram for thread filter\n");
196 print_hists(hists); 169 print_hists_out(hists);
197 } 170 }
198 171
199 /* normal stats should be invariant */ 172 /* normal stats should be invariant */
@@ -222,7 +195,7 @@ int test__hists_filter(void)
222 195
223 if (verbose > 2) { 196 if (verbose > 2) {
224 pr_info("Histogram for dso filter\n"); 197 pr_info("Histogram for dso filter\n");
225 print_hists(hists); 198 print_hists_out(hists);
226 } 199 }
227 200
228 /* normal stats should be invariant */ 201 /* normal stats should be invariant */
@@ -257,7 +230,7 @@ int test__hists_filter(void)
257 230
258 if (verbose > 2) { 231 if (verbose > 2) {
259 pr_info("Histogram for symbol filter\n"); 232 pr_info("Histogram for symbol filter\n");
260 print_hists(hists); 233 print_hists_out(hists);
261 } 234 }
262 235
263 /* normal stats should be invariant */ 236 /* normal stats should be invariant */
@@ -284,7 +257,7 @@ int test__hists_filter(void)
284 257
285 if (verbose > 2) { 258 if (verbose > 2) {
286 pr_info("Histogram for all filters\n"); 259 pr_info("Histogram for all filters\n");
287 print_hists(hists); 260 print_hists_out(hists);
288 } 261 }
289 262
290 /* normal stats should be invariant */ 263 /* normal stats should be invariant */
@@ -310,6 +283,7 @@ int test__hists_filter(void)
310out: 283out:
311 /* tear down everything */ 284 /* tear down everything */
312 perf_evlist__delete(evlist); 285 perf_evlist__delete(evlist);
286 reset_output_field();
313 machines__exit(&machines); 287 machines__exit(&machines);
314 288
315 return err; 289 return err;
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c
index b009bbf440d9..5ffa2c3eb77d 100644
--- a/tools/perf/tests/hists_link.c
+++ b/tools/perf/tests/hists_link.c
@@ -268,33 +268,6 @@ static int validate_link(struct hists *leader, struct hists *other)
268 return __validate_link(leader, 0) || __validate_link(other, 1); 268 return __validate_link(leader, 0) || __validate_link(other, 1);
269} 269}
270 270
271static void print_hists(struct hists *hists)
272{
273 int i = 0;
274 struct rb_root *root;
275 struct rb_node *node;
276
277 if (sort__need_collapse)
278 root = &hists->entries_collapsed;
279 else
280 root = hists->entries_in;
281
282 pr_info("----- %s --------\n", __func__);
283 node = rb_first(root);
284 while (node) {
285 struct hist_entry *he;
286
287 he = rb_entry(node, struct hist_entry, rb_node_in);
288
289 pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
290 i, thread__comm_str(he->thread), he->ms.map->dso->short_name,
291 he->ms.sym->name, he->stat.period);
292
293 i++;
294 node = rb_next(node);
295 }
296}
297
298int test__hists_link(void) 271int test__hists_link(void)
299{ 272{
300 int err = -1; 273 int err = -1;
@@ -336,7 +309,7 @@ int test__hists_link(void)
336 hists__collapse_resort(&evsel->hists, NULL); 309 hists__collapse_resort(&evsel->hists, NULL);
337 310
338 if (verbose > 2) 311 if (verbose > 2)
339 print_hists(&evsel->hists); 312 print_hists_in(&evsel->hists);
340 } 313 }
341 314
342 first = perf_evlist__first(evlist); 315 first = perf_evlist__first(evlist);
@@ -359,6 +332,7 @@ int test__hists_link(void)
359out: 332out:
360 /* tear down everything */ 333 /* tear down everything */
361 perf_evlist__delete(evlist); 334 perf_evlist__delete(evlist);
335 reset_output_field();
362 machines__exit(&machines); 336 machines__exit(&machines);
363 337
364 return err; 338 return err;
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c
new file mode 100644
index 000000000000..a16850551797
--- /dev/null
+++ b/tools/perf/tests/hists_output.c
@@ -0,0 +1,618 @@
1#include "perf.h"
2#include "util/debug.h"
3#include "util/symbol.h"
4#include "util/sort.h"
5#include "util/evsel.h"
6#include "util/evlist.h"
7#include "util/machine.h"
8#include "util/thread.h"
9#include "util/parse-events.h"
10#include "tests/tests.h"
11#include "tests/hists_common.h"
12
13struct sample {
14 u32 cpu;
15 u32 pid;
16 u64 ip;
17 struct thread *thread;
18 struct map *map;
19 struct symbol *sym;
20};
21
22/* For the numbers, see hists_common.c */
23static struct sample fake_samples[] = {
24 /* perf [kernel] schedule() */
25 { .cpu = 0, .pid = 100, .ip = 0xf0000 + 700, },
26 /* perf [perf] main() */
27 { .cpu = 1, .pid = 100, .ip = 0x40000 + 700, },
28 /* perf [perf] cmd_record() */
29 { .cpu = 1, .pid = 100, .ip = 0x40000 + 900, },
30 /* perf [libc] malloc() */
31 { .cpu = 1, .pid = 100, .ip = 0x50000 + 700, },
32 /* perf [libc] free() */
33 { .cpu = 2, .pid = 100, .ip = 0x50000 + 800, },
34 /* perf [perf] main() */
35 { .cpu = 2, .pid = 200, .ip = 0x40000 + 700, },
36 /* perf [kernel] page_fault() */
37 { .cpu = 2, .pid = 200, .ip = 0xf0000 + 800, },
38 /* bash [bash] main() */
39 { .cpu = 3, .pid = 300, .ip = 0x40000 + 700, },
40 /* bash [bash] xmalloc() */
41 { .cpu = 0, .pid = 300, .ip = 0x40000 + 800, },
42 /* bash [kernel] page_fault() */
43 { .cpu = 1, .pid = 300, .ip = 0xf0000 + 800, },
44};
45
46static int add_hist_entries(struct hists *hists, struct machine *machine)
47{
48 struct addr_location al;
49 struct hist_entry *he;
50 struct perf_sample sample = { .period = 100, };
51 size_t i;
52
53 for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
54 const union perf_event event = {
55 .header = {
56 .misc = PERF_RECORD_MISC_USER,
57 },
58 };
59
60 sample.cpu = fake_samples[i].cpu;
61 sample.pid = fake_samples[i].pid;
62 sample.tid = fake_samples[i].pid;
63 sample.ip = fake_samples[i].ip;
64
65 if (perf_event__preprocess_sample(&event, machine, &al,
66 &sample) < 0)
67 goto out;
68
69 he = __hists__add_entry(hists, &al, NULL, NULL, NULL,
70 sample.period, 1, 0);
71 if (he == NULL)
72 goto out;
73
74 fake_samples[i].thread = al.thread;
75 fake_samples[i].map = al.map;
76 fake_samples[i].sym = al.sym;
77 }
78
79 return TEST_OK;
80
81out:
82 pr_debug("Not enough memory for adding a hist entry\n");
83 return TEST_FAIL;
84}
85
86static void del_hist_entries(struct hists *hists)
87{
88 struct hist_entry *he;
89 struct rb_root *root_in;
90 struct rb_root *root_out;
91 struct rb_node *node;
92
93 if (sort__need_collapse)
94 root_in = &hists->entries_collapsed;
95 else
96 root_in = hists->entries_in;
97
98 root_out = &hists->entries;
99
100 while (!RB_EMPTY_ROOT(root_out)) {
101 node = rb_first(root_out);
102
103 he = rb_entry(node, struct hist_entry, rb_node);
104 rb_erase(node, root_out);
105 rb_erase(&he->rb_node_in, root_in);
106 hist_entry__free(he);
107 }
108}
109
110typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
111
112#define COMM(he) (thread__comm_str(he->thread))
113#define DSO(he) (he->ms.map->dso->short_name)
114#define SYM(he) (he->ms.sym->name)
115#define CPU(he) (he->cpu)
116#define PID(he) (he->thread->tid)
117
118/* default sort keys (no field) */
119static int test1(struct perf_evsel *evsel, struct machine *machine)
120{
121 int err;
122 struct hists *hists = &evsel->hists;
123 struct hist_entry *he;
124 struct rb_root *root;
125 struct rb_node *node;
126
127 field_order = NULL;
128 sort_order = NULL; /* equivalent to sort_order = "comm,dso,sym" */
129
130 setup_sorting();
131
132 /*
133 * expected output:
134 *
135 * Overhead Command Shared Object Symbol
136 * ======== ======= ============= ==============
137 * 20.00% perf perf [.] main
138 * 10.00% bash [kernel] [k] page_fault
139 * 10.00% bash bash [.] main
140 * 10.00% bash bash [.] xmalloc
141 * 10.00% perf [kernel] [k] page_fault
142 * 10.00% perf [kernel] [k] schedule
143 * 10.00% perf libc [.] free
144 * 10.00% perf libc [.] malloc
145 * 10.00% perf perf [.] cmd_record
146 */
147 err = add_hist_entries(hists, machine);
148 if (err < 0)
149 goto out;
150
151 hists__collapse_resort(hists, NULL);
152 hists__output_resort(hists);
153
154 if (verbose > 2) {
155 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
156 print_hists_out(hists);
157 }
158
159 root = &evsel->hists.entries;
160 node = rb_first(root);
161 he = rb_entry(node, struct hist_entry, rb_node);
162 TEST_ASSERT_VAL("Invalid hist entry",
163 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
164 !strcmp(SYM(he), "main") && he->stat.period == 200);
165
166 node = rb_next(node);
167 he = rb_entry(node, struct hist_entry, rb_node);
168 TEST_ASSERT_VAL("Invalid hist entry",
169 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") &&
170 !strcmp(SYM(he), "page_fault") && he->stat.period == 100);
171
172 node = rb_next(node);
173 he = rb_entry(node, struct hist_entry, rb_node);
174 TEST_ASSERT_VAL("Invalid hist entry",
175 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
176 !strcmp(SYM(he), "main") && he->stat.period == 100);
177
178 node = rb_next(node);
179 he = rb_entry(node, struct hist_entry, rb_node);
180 TEST_ASSERT_VAL("Invalid hist entry",
181 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
182 !strcmp(SYM(he), "xmalloc") && he->stat.period == 100);
183
184 node = rb_next(node);
185 he = rb_entry(node, struct hist_entry, rb_node);
186 TEST_ASSERT_VAL("Invalid hist entry",
187 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
188 !strcmp(SYM(he), "page_fault") && he->stat.period == 100);
189
190 node = rb_next(node);
191 he = rb_entry(node, struct hist_entry, rb_node);
192 TEST_ASSERT_VAL("Invalid hist entry",
193 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
194 !strcmp(SYM(he), "schedule") && he->stat.period == 100);
195
196 node = rb_next(node);
197 he = rb_entry(node, struct hist_entry, rb_node);
198 TEST_ASSERT_VAL("Invalid hist entry",
199 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
200 !strcmp(SYM(he), "free") && he->stat.period == 100);
201
202 node = rb_next(node);
203 he = rb_entry(node, struct hist_entry, rb_node);
204 TEST_ASSERT_VAL("Invalid hist entry",
205 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
206 !strcmp(SYM(he), "malloc") && he->stat.period == 100);
207
208 node = rb_next(node);
209 he = rb_entry(node, struct hist_entry, rb_node);
210 TEST_ASSERT_VAL("Invalid hist entry",
211 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
212 !strcmp(SYM(he), "cmd_record") && he->stat.period == 100);
213
214out:
215 del_hist_entries(hists);
216 reset_output_field();
217 return err;
218}
219
220/* mixed fields and sort keys */
221static int test2(struct perf_evsel *evsel, struct machine *machine)
222{
223 int err;
224 struct hists *hists = &evsel->hists;
225 struct hist_entry *he;
226 struct rb_root *root;
227 struct rb_node *node;
228
229 field_order = "overhead,cpu";
230 sort_order = "pid";
231
232 setup_sorting();
233
234 /*
235 * expected output:
236 *
237 * Overhead CPU Command: Pid
238 * ======== === =============
239 * 30.00% 1 perf : 100
240 * 10.00% 0 perf : 100
241 * 10.00% 2 perf : 100
242 * 20.00% 2 perf : 200
243 * 10.00% 0 bash : 300
244 * 10.00% 1 bash : 300
245 * 10.00% 3 bash : 300
246 */
247 err = add_hist_entries(hists, machine);
248 if (err < 0)
249 goto out;
250
251 hists__collapse_resort(hists, NULL);
252 hists__output_resort(hists);
253
254 if (verbose > 2) {
255 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
256 print_hists_out(hists);
257 }
258
259 root = &evsel->hists.entries;
260 node = rb_first(root);
261 he = rb_entry(node, struct hist_entry, rb_node);
262 TEST_ASSERT_VAL("Invalid hist entry",
263 CPU(he) == 1 && PID(he) == 100 && he->stat.period == 300);
264
265 node = rb_next(node);
266 he = rb_entry(node, struct hist_entry, rb_node);
267 TEST_ASSERT_VAL("Invalid hist entry",
268 CPU(he) == 0 && PID(he) == 100 && he->stat.period == 100);
269
270out:
271 del_hist_entries(hists);
272 reset_output_field();
273 return err;
274}
275
276/* fields only (no sort key) */
277static int test3(struct perf_evsel *evsel, struct machine *machine)
278{
279 int err;
280 struct hists *hists = &evsel->hists;
281 struct hist_entry *he;
282 struct rb_root *root;
283 struct rb_node *node;
284
285 field_order = "comm,overhead,dso";
286 sort_order = NULL;
287
288 setup_sorting();
289
290 /*
291 * expected output:
292 *
293 * Command Overhead Shared Object
294 * ======= ======== =============
295 * bash 20.00% bash
296 * bash 10.00% [kernel]
297 * perf 30.00% perf
298 * perf 20.00% [kernel]
299 * perf 20.00% libc
300 */
301 err = add_hist_entries(hists, machine);
302 if (err < 0)
303 goto out;
304
305 hists__collapse_resort(hists, NULL);
306 hists__output_resort(hists);
307
308 if (verbose > 2) {
309 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
310 print_hists_out(hists);
311 }
312
313 root = &evsel->hists.entries;
314 node = rb_first(root);
315 he = rb_entry(node, struct hist_entry, rb_node);
316 TEST_ASSERT_VAL("Invalid hist entry",
317 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
318 he->stat.period == 200);
319
320 node = rb_next(node);
321 he = rb_entry(node, struct hist_entry, rb_node);
322 TEST_ASSERT_VAL("Invalid hist entry",
323 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") &&
324 he->stat.period == 100);
325
326 node = rb_next(node);
327 he = rb_entry(node, struct hist_entry, rb_node);
328 TEST_ASSERT_VAL("Invalid hist entry",
329 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
330 he->stat.period == 300);
331
332 node = rb_next(node);
333 he = rb_entry(node, struct hist_entry, rb_node);
334 TEST_ASSERT_VAL("Invalid hist entry",
335 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
336 he->stat.period == 200);
337
338 node = rb_next(node);
339 he = rb_entry(node, struct hist_entry, rb_node);
340 TEST_ASSERT_VAL("Invalid hist entry",
341 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
342 he->stat.period == 200);
343
344out:
345 del_hist_entries(hists);
346 reset_output_field();
347 return err;
348}
349
350/* handle duplicate 'dso' field */
351static int test4(struct perf_evsel *evsel, struct machine *machine)
352{
353 int err;
354 struct hists *hists = &evsel->hists;
355 struct hist_entry *he;
356 struct rb_root *root;
357 struct rb_node *node;
358
359 field_order = "dso,sym,comm,overhead,dso";
360 sort_order = "sym";
361
362 setup_sorting();
363
364 /*
365 * expected output:
366 *
367 * Shared Object Symbol Command Overhead
368 * ============= ============== ======= ========
369 * perf [.] cmd_record perf 10.00%
370 * libc [.] free perf 10.00%
371 * bash [.] main bash 10.00%
372 * perf [.] main perf 20.00%
373 * libc [.] malloc perf 10.00%
374 * [kernel] [k] page_fault bash 10.00%
375 * [kernel] [k] page_fault perf 10.00%
376 * [kernel] [k] schedule perf 10.00%
377 * bash [.] xmalloc bash 10.00%
378 */
379 err = add_hist_entries(hists, machine);
380 if (err < 0)
381 goto out;
382
383 hists__collapse_resort(hists, NULL);
384 hists__output_resort(hists);
385
386 if (verbose > 2) {
387 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
388 print_hists_out(hists);
389 }
390
391 root = &evsel->hists.entries;
392 node = rb_first(root);
393 he = rb_entry(node, struct hist_entry, rb_node);
394 TEST_ASSERT_VAL("Invalid hist entry",
395 !strcmp(DSO(he), "perf") && !strcmp(SYM(he), "cmd_record") &&
396 !strcmp(COMM(he), "perf") && he->stat.period == 100);
397
398 node = rb_next(node);
399 he = rb_entry(node, struct hist_entry, rb_node);
400 TEST_ASSERT_VAL("Invalid hist entry",
401 !strcmp(DSO(he), "libc") && !strcmp(SYM(he), "free") &&
402 !strcmp(COMM(he), "perf") && he->stat.period == 100);
403
404 node = rb_next(node);
405 he = rb_entry(node, struct hist_entry, rb_node);
406 TEST_ASSERT_VAL("Invalid hist entry",
407 !strcmp(DSO(he), "bash") && !strcmp(SYM(he), "main") &&
408 !strcmp(COMM(he), "bash") && he->stat.period == 100);
409
410 node = rb_next(node);
411 he = rb_entry(node, struct hist_entry, rb_node);
412 TEST_ASSERT_VAL("Invalid hist entry",
413 !strcmp(DSO(he), "perf") && !strcmp(SYM(he), "main") &&
414 !strcmp(COMM(he), "perf") && he->stat.period == 200);
415
416 node = rb_next(node);
417 he = rb_entry(node, struct hist_entry, rb_node);
418 TEST_ASSERT_VAL("Invalid hist entry",
419 !strcmp(DSO(he), "libc") && !strcmp(SYM(he), "malloc") &&
420 !strcmp(COMM(he), "perf") && he->stat.period == 100);
421
422 node = rb_next(node);
423 he = rb_entry(node, struct hist_entry, rb_node);
424 TEST_ASSERT_VAL("Invalid hist entry",
425 !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") &&
426 !strcmp(COMM(he), "bash") && he->stat.period == 100);
427
428 node = rb_next(node);
429 he = rb_entry(node, struct hist_entry, rb_node);
430 TEST_ASSERT_VAL("Invalid hist entry",
431 !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") &&
432 !strcmp(COMM(he), "perf") && he->stat.period == 100);
433
434 node = rb_next(node);
435 he = rb_entry(node, struct hist_entry, rb_node);
436 TEST_ASSERT_VAL("Invalid hist entry",
437 !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "schedule") &&
438 !strcmp(COMM(he), "perf") && he->stat.period == 100);
439
440 node = rb_next(node);
441 he = rb_entry(node, struct hist_entry, rb_node);
442 TEST_ASSERT_VAL("Invalid hist entry",
443 !strcmp(DSO(he), "bash") && !strcmp(SYM(he), "xmalloc") &&
444 !strcmp(COMM(he), "bash") && he->stat.period == 100);
445
446out:
447 del_hist_entries(hists);
448 reset_output_field();
449 return err;
450}
451
452/* full sort keys w/o overhead field */
453static int test5(struct perf_evsel *evsel, struct machine *machine)
454{
455 int err;
456 struct hists *hists = &evsel->hists;
457 struct hist_entry *he;
458 struct rb_root *root;
459 struct rb_node *node;
460
461 field_order = "cpu,pid,comm,dso,sym";
462 sort_order = "dso,pid";
463
464 setup_sorting();
465
466 /*
467 * expected output:
468 *
469 * CPU Command: Pid Command Shared Object Symbol
470 * === ============= ======= ============= ==============
471 * 0 perf: 100 perf [kernel] [k] schedule
472 * 2 perf: 200 perf [kernel] [k] page_fault
473 * 1 bash: 300 bash [kernel] [k] page_fault
474 * 0 bash: 300 bash bash [.] xmalloc
475 * 3 bash: 300 bash bash [.] main
476 * 1 perf: 100 perf libc [.] malloc
477 * 2 perf: 100 perf libc [.] free
478 * 1 perf: 100 perf perf [.] cmd_record
479 * 1 perf: 100 perf perf [.] main
480 * 2 perf: 200 perf perf [.] main
481 */
482 err = add_hist_entries(hists, machine);
483 if (err < 0)
484 goto out;
485
486 hists__collapse_resort(hists, NULL);
487 hists__output_resort(hists);
488
489 if (verbose > 2) {
490 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
491 print_hists_out(hists);
492 }
493
494 root = &evsel->hists.entries;
495 node = rb_first(root);
496 he = rb_entry(node, struct hist_entry, rb_node);
497
498 TEST_ASSERT_VAL("Invalid hist entry",
499 CPU(he) == 0 && PID(he) == 100 &&
500 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
501 !strcmp(SYM(he), "schedule") && he->stat.period == 100);
502
503 node = rb_next(node);
504 he = rb_entry(node, struct hist_entry, rb_node);
505 TEST_ASSERT_VAL("Invalid hist entry",
506 CPU(he) == 2 && PID(he) == 200 &&
507 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
508 !strcmp(SYM(he), "page_fault") && he->stat.period == 100);
509
510 node = rb_next(node);
511 he = rb_entry(node, struct hist_entry, rb_node);
512 TEST_ASSERT_VAL("Invalid hist entry",
513 CPU(he) == 1 && PID(he) == 300 &&
514 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") &&
515 !strcmp(SYM(he), "page_fault") && he->stat.period == 100);
516
517 node = rb_next(node);
518 he = rb_entry(node, struct hist_entry, rb_node);
519 TEST_ASSERT_VAL("Invalid hist entry",
520 CPU(he) == 0 && PID(he) == 300 &&
521 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
522 !strcmp(SYM(he), "xmalloc") && he->stat.period == 100);
523
524 node = rb_next(node);
525 he = rb_entry(node, struct hist_entry, rb_node);
526 TEST_ASSERT_VAL("Invalid hist entry",
527 CPU(he) == 3 && PID(he) == 300 &&
528 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
529 !strcmp(SYM(he), "main") && he->stat.period == 100);
530
531 node = rb_next(node);
532 he = rb_entry(node, struct hist_entry, rb_node);
533 TEST_ASSERT_VAL("Invalid hist entry",
534 CPU(he) == 1 && PID(he) == 100 &&
535 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
536 !strcmp(SYM(he), "malloc") && he->stat.period == 100);
537
538 node = rb_next(node);
539 he = rb_entry(node, struct hist_entry, rb_node);
540 TEST_ASSERT_VAL("Invalid hist entry",
541 CPU(he) == 2 && PID(he) == 100 &&
542 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
543 !strcmp(SYM(he), "free") && he->stat.period == 100);
544
545 node = rb_next(node);
546 he = rb_entry(node, struct hist_entry, rb_node);
547 TEST_ASSERT_VAL("Invalid hist entry",
548 CPU(he) == 1 && PID(he) == 100 &&
549 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
550 !strcmp(SYM(he), "cmd_record") && he->stat.period == 100);
551
552 node = rb_next(node);
553 he = rb_entry(node, struct hist_entry, rb_node);
554 TEST_ASSERT_VAL("Invalid hist entry",
555 CPU(he) == 1 && PID(he) == 100 &&
556 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
557 !strcmp(SYM(he), "main") && he->stat.period == 100);
558
559 node = rb_next(node);
560 he = rb_entry(node, struct hist_entry, rb_node);
561 TEST_ASSERT_VAL("Invalid hist entry",
562 CPU(he) == 2 && PID(he) == 200 &&
563 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
564 !strcmp(SYM(he), "main") && he->stat.period == 100);
565
566out:
567 del_hist_entries(hists);
568 reset_output_field();
569 return err;
570}
571
572int test__hists_output(void)
573{
574 int err = TEST_FAIL;
575 struct machines machines;
576 struct machine *machine;
577 struct perf_evsel *evsel;
578 struct perf_evlist *evlist = perf_evlist__new();
579 size_t i;
580 test_fn_t testcases[] = {
581 test1,
582 test2,
583 test3,
584 test4,
585 test5,
586 };
587
588 TEST_ASSERT_VAL("No memory", evlist);
589
590 err = parse_events(evlist, "cpu-clock");
591 if (err)
592 goto out;
593
594 machines__init(&machines);
595
596 /* setup threads/dso/map/symbols also */
597 machine = setup_fake_machine(&machines);
598 if (!machine)
599 goto out;
600
601 if (verbose > 1)
602 machine__fprintf(machine, stderr);
603
604 evsel = perf_evlist__first(evlist);
605
606 for (i = 0; i < ARRAY_SIZE(testcases); i++) {
607 err = testcases[i](evsel, machine);
608 if (err < 0)
609 break;
610 }
611
612out:
613 /* tear down everything */
614 perf_evlist__delete(evlist);
615 machines__exit(&machines);
616
617 return err;
618}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 8f91fb051ef1..d76c0e2e6635 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -44,6 +44,7 @@ int test__dwarf_unwind(void);
44int test__hists_filter(void); 44int test__hists_filter(void);
45int test__mmap_thread_lookup(void); 45int test__mmap_thread_lookup(void);
46int test__thread_mg_share(void); 46int test__thread_mg_share(void);
47int test__hists_output(void);
47 48
48#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) 49#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
49#ifdef HAVE_DWARF_UNWIND_SUPPORT 50#ifdef HAVE_DWARF_UNWIND_SUPPORT
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index b0861e3e50a5..1c331b934ffc 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -616,35 +616,6 @@ struct hpp_arg {
616 bool current_entry; 616 bool current_entry;
617}; 617};
618 618
619static int __hpp__overhead_callback(struct perf_hpp *hpp, bool front)
620{
621 struct hpp_arg *arg = hpp->ptr;
622
623 if (arg->current_entry && arg->b->navkeypressed)
624 ui_browser__set_color(arg->b, HE_COLORSET_SELECTED);
625 else
626 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
627
628 if (front) {
629 if (!symbol_conf.use_callchain)
630 return 0;
631
632 slsmg_printf("%c ", arg->folded_sign);
633 return 2;
634 }
635
636 return 0;
637}
638
639static int __hpp__color_callback(struct perf_hpp *hpp, bool front __maybe_unused)
640{
641 struct hpp_arg *arg = hpp->ptr;
642
643 if (!arg->current_entry || !arg->b->navkeypressed)
644 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
645 return 0;
646}
647
648static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) 619static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
649{ 620{
650 struct hpp_arg *arg = hpp->ptr; 621 struct hpp_arg *arg = hpp->ptr;
@@ -665,7 +636,7 @@ static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
665 return ret; 636 return ret;
666} 637}
667 638
668#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \ 639#define __HPP_COLOR_PERCENT_FN(_type, _field) \
669static u64 __hpp_get_##_field(struct hist_entry *he) \ 640static u64 __hpp_get_##_field(struct hist_entry *he) \
670{ \ 641{ \
671 return he->stat._field; \ 642 return he->stat._field; \
@@ -676,22 +647,20 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
676 struct perf_hpp *hpp, \ 647 struct perf_hpp *hpp, \
677 struct hist_entry *he) \ 648 struct hist_entry *he) \
678{ \ 649{ \
679 return __hpp__fmt(hpp, he, __hpp_get_##_field, _cb, " %6.2f%%", \ 650 return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \
680 __hpp__slsmg_color_printf, true); \ 651 __hpp__slsmg_color_printf, true); \
681} 652}
682 653
683__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__overhead_callback) 654__HPP_COLOR_PERCENT_FN(overhead, period)
684__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, __hpp__color_callback) 655__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
685__HPP_COLOR_PERCENT_FN(overhead_us, period_us, __hpp__color_callback) 656__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
686__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, __hpp__color_callback) 657__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
687__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, __hpp__color_callback) 658__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
688 659
689#undef __HPP_COLOR_PERCENT_FN 660#undef __HPP_COLOR_PERCENT_FN
690 661
691void hist_browser__init_hpp(void) 662void hist_browser__init_hpp(void)
692{ 663{
693 perf_hpp__init();
694
695 perf_hpp__format[PERF_HPP__OVERHEAD].color = 664 perf_hpp__format[PERF_HPP__OVERHEAD].color =
696 hist_browser__hpp_color_overhead; 665 hist_browser__hpp_color_overhead;
697 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = 666 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
@@ -729,7 +698,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
729 698
730 if (row_offset == 0) { 699 if (row_offset == 0) {
731 struct hpp_arg arg = { 700 struct hpp_arg arg = {
732 .b = &browser->b, 701 .b = &browser->b,
733 .folded_sign = folded_sign, 702 .folded_sign = folded_sign,
734 .current_entry = current_entry, 703 .current_entry = current_entry,
735 }; 704 };
@@ -742,11 +711,27 @@ static int hist_browser__show_entry(struct hist_browser *browser,
742 ui_browser__gotorc(&browser->b, row, 0); 711 ui_browser__gotorc(&browser->b, row, 0);
743 712
744 perf_hpp__for_each_format(fmt) { 713 perf_hpp__for_each_format(fmt) {
745 if (!first) { 714 if (perf_hpp__should_skip(fmt))
715 continue;
716
717 if (current_entry && browser->b.navkeypressed) {
718 ui_browser__set_color(&browser->b,
719 HE_COLORSET_SELECTED);
720 } else {
721 ui_browser__set_color(&browser->b,
722 HE_COLORSET_NORMAL);
723 }
724
725 if (first) {
726 if (symbol_conf.use_callchain) {
727 slsmg_printf("%c ", folded_sign);
728 width -= 2;
729 }
730 first = false;
731 } else {
746 slsmg_printf(" "); 732 slsmg_printf(" ");
747 width -= 2; 733 width -= 2;
748 } 734 }
749 first = false;
750 735
751 if (fmt->color) { 736 if (fmt->color) {
752 width -= fmt->color(fmt, &hpp, entry); 737 width -= fmt->color(fmt, &hpp, entry);
@@ -760,8 +745,8 @@ static int hist_browser__show_entry(struct hist_browser *browser,
760 if (!browser->b.navkeypressed) 745 if (!browser->b.navkeypressed)
761 width += 1; 746 width += 1;
762 747
763 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists); 748 slsmg_write_nstring("", width);
764 slsmg_write_nstring(s, width); 749
765 ++row; 750 ++row;
766 ++printed; 751 ++printed;
767 } else 752 } else
@@ -830,10 +815,7 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd,
830 if (total) 815 if (total)
831 percent = h->stat.period * 100.0 / total; 816 percent = h->stat.period * 100.0 / total;
832 817
833 if (percent < min_pcnt) 818 if (!h->filtered && percent >= min_pcnt)
834 return NULL;
835
836 if (!h->filtered)
837 return nd; 819 return nd;
838 820
839 nd = rb_next(nd); 821 nd = rb_next(nd);
@@ -1104,27 +1086,35 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
1104 struct hist_entry *he, FILE *fp) 1086 struct hist_entry *he, FILE *fp)
1105{ 1087{
1106 char s[8192]; 1088 char s[8192];
1107 double percent;
1108 int printed = 0; 1089 int printed = 0;
1109 char folded_sign = ' '; 1090 char folded_sign = ' ';
1091 struct perf_hpp hpp = {
1092 .buf = s,
1093 .size = sizeof(s),
1094 };
1095 struct perf_hpp_fmt *fmt;
1096 bool first = true;
1097 int ret;
1110 1098
1111 if (symbol_conf.use_callchain) 1099 if (symbol_conf.use_callchain)
1112 folded_sign = hist_entry__folded(he); 1100 folded_sign = hist_entry__folded(he);
1113 1101
1114 hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1115 percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1116
1117 if (symbol_conf.use_callchain) 1102 if (symbol_conf.use_callchain)
1118 printed += fprintf(fp, "%c ", folded_sign); 1103 printed += fprintf(fp, "%c ", folded_sign);
1119 1104
1120 printed += fprintf(fp, " %5.2f%%", percent); 1105 perf_hpp__for_each_format(fmt) {
1121 1106 if (perf_hpp__should_skip(fmt))
1122 if (symbol_conf.show_nr_samples) 1107 continue;
1123 printed += fprintf(fp, " %11u", he->stat.nr_events);
1124 1108
1125 if (symbol_conf.show_total_period) 1109 if (!first) {
1126 printed += fprintf(fp, " %12" PRIu64, he->stat.period); 1110 ret = scnprintf(hpp.buf, hpp.size, " ");
1111 advance_hpp(&hpp, ret);
1112 } else
1113 first = false;
1127 1114
1115 ret = fmt->entry(fmt, &hpp, he);
1116 advance_hpp(&hpp, ret);
1117 }
1128 printed += fprintf(fp, "%s\n", rtrim(s)); 1118 printed += fprintf(fp, "%s\n", rtrim(s));
1129 1119
1130 if (folded_sign == '-') 1120 if (folded_sign == '-')
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index 91f10f3f6dd1..9d90683914d4 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -43,7 +43,7 @@ static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,
43 struct perf_hpp *hpp, \ 43 struct perf_hpp *hpp, \
44 struct hist_entry *he) \ 44 struct hist_entry *he) \
45{ \ 45{ \
46 return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%", \ 46 return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
47 __percent_color_snprintf, true); \ 47 __percent_color_snprintf, true); \
48} 48}
49 49
@@ -58,8 +58,6 @@ __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
58 58
59void perf_gtk__init_hpp(void) 59void perf_gtk__init_hpp(void)
60{ 60{
61 perf_hpp__init();
62
63 perf_hpp__format[PERF_HPP__OVERHEAD].color = 61 perf_hpp__format[PERF_HPP__OVERHEAD].color =
64 perf_gtk__hpp_color_overhead; 62 perf_gtk__hpp_color_overhead;
65 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = 63 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
@@ -153,7 +151,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
153 struct perf_hpp_fmt *fmt; 151 struct perf_hpp_fmt *fmt;
154 GType col_types[MAX_COLUMNS]; 152 GType col_types[MAX_COLUMNS];
155 GtkCellRenderer *renderer; 153 GtkCellRenderer *renderer;
156 struct sort_entry *se;
157 GtkTreeStore *store; 154 GtkTreeStore *store;
158 struct rb_node *nd; 155 struct rb_node *nd;
159 GtkWidget *view; 156 GtkWidget *view;
@@ -172,16 +169,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
172 perf_hpp__for_each_format(fmt) 169 perf_hpp__for_each_format(fmt)
173 col_types[nr_cols++] = G_TYPE_STRING; 170 col_types[nr_cols++] = G_TYPE_STRING;
174 171
175 list_for_each_entry(se, &hist_entry__sort_list, list) {
176 if (se->elide)
177 continue;
178
179 if (se == &sort_sym)
180 sym_col = nr_cols;
181
182 col_types[nr_cols++] = G_TYPE_STRING;
183 }
184
185 store = gtk_tree_store_newv(nr_cols, col_types); 172 store = gtk_tree_store_newv(nr_cols, col_types);
186 173
187 view = gtk_tree_view_new(); 174 view = gtk_tree_view_new();
@@ -191,6 +178,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
191 col_idx = 0; 178 col_idx = 0;
192 179
193 perf_hpp__for_each_format(fmt) { 180 perf_hpp__for_each_format(fmt) {
181 if (perf_hpp__should_skip(fmt))
182 continue;
183
194 fmt->header(fmt, &hpp, hists_to_evsel(hists)); 184 fmt->header(fmt, &hpp, hists_to_evsel(hists));
195 185
196 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), 186 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
@@ -199,16 +189,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
199 col_idx++, NULL); 189 col_idx++, NULL);
200 } 190 }
201 191
202 list_for_each_entry(se, &hist_entry__sort_list, list) {
203 if (se->elide)
204 continue;
205
206 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
207 -1, se->se_header,
208 renderer, "text",
209 col_idx++, NULL);
210 }
211
212 for (col_idx = 0; col_idx < nr_cols; col_idx++) { 192 for (col_idx = 0; col_idx < nr_cols; col_idx++) {
213 GtkTreeViewColumn *column; 193 GtkTreeViewColumn *column;
214 194
@@ -245,6 +225,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
245 col_idx = 0; 225 col_idx = 0;
246 226
247 perf_hpp__for_each_format(fmt) { 227 perf_hpp__for_each_format(fmt) {
228 if (perf_hpp__should_skip(fmt))
229 continue;
230
248 if (fmt->color) 231 if (fmt->color)
249 fmt->color(fmt, &hpp, h); 232 fmt->color(fmt, &hpp, h);
250 else 233 else
@@ -253,16 +236,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
253 gtk_tree_store_set(store, &iter, col_idx++, s, -1); 236 gtk_tree_store_set(store, &iter, col_idx++, s, -1);
254 } 237 }
255 238
256 list_for_each_entry(se, &hist_entry__sort_list, list) {
257 if (se->elide)
258 continue;
259
260 se->se_snprintf(h, s, ARRAY_SIZE(s),
261 hists__col_len(hists, se->se_width_idx));
262
263 gtk_tree_store_set(store, &iter, col_idx++, s, -1);
264 }
265
266 if (symbol_conf.use_callchain && sort__has_sym) { 239 if (symbol_conf.use_callchain && sort__has_sym) {
267 if (callchain_param.mode == CHAIN_GRAPH_REL) 240 if (callchain_param.mode == CHAIN_GRAPH_REL)
268 total = h->stat.period; 241 total = h->stat.period;
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index 0912805c08f4..4484f5bd1b14 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -16,20 +16,15 @@
16}) 16})
17 17
18int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, 18int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
19 hpp_field_fn get_field, hpp_callback_fn callback, 19 hpp_field_fn get_field, const char *fmt,
20 const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent) 20 hpp_snprint_fn print_fn, bool fmt_percent)
21{ 21{
22 int ret = 0; 22 int ret;
23 struct hists *hists = he->hists; 23 struct hists *hists = he->hists;
24 struct perf_evsel *evsel = hists_to_evsel(hists); 24 struct perf_evsel *evsel = hists_to_evsel(hists);
25 char *buf = hpp->buf; 25 char *buf = hpp->buf;
26 size_t size = hpp->size; 26 size_t size = hpp->size;
27 27
28 if (callback) {
29 ret = callback(hpp, true);
30 advance_hpp(hpp, ret);
31 }
32
33 if (fmt_percent) { 28 if (fmt_percent) {
34 double percent = 0.0; 29 double percent = 0.0;
35 u64 total = hists__total_period(hists); 30 u64 total = hists__total_period(hists);
@@ -37,9 +32,9 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
37 if (total) 32 if (total)
38 percent = 100.0 * get_field(he) / total; 33 percent = 100.0 * get_field(he) / total;
39 34
40 ret += hpp__call_print_fn(hpp, print_fn, fmt, percent); 35 ret = hpp__call_print_fn(hpp, print_fn, fmt, percent);
41 } else 36 } else
42 ret += hpp__call_print_fn(hpp, print_fn, fmt, get_field(he)); 37 ret = hpp__call_print_fn(hpp, print_fn, fmt, get_field(he));
43 38
44 if (perf_evsel__is_group_event(evsel)) { 39 if (perf_evsel__is_group_event(evsel)) {
45 int prev_idx, idx_delta; 40 int prev_idx, idx_delta;
@@ -99,13 +94,6 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
99 } 94 }
100 } 95 }
101 96
102 if (callback) {
103 int __ret = callback(hpp, false);
104
105 advance_hpp(hpp, __ret);
106 ret += __ret;
107 }
108
109 /* 97 /*
110 * Restore original buf and size as it's where caller expects 98 * Restore original buf and size as it's where caller expects
111 * the result will be saved. 99 * the result will be saved.
@@ -116,6 +104,62 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
116 return ret; 104 return ret;
117} 105}
118 106
107static int field_cmp(u64 field_a, u64 field_b)
108{
109 if (field_a > field_b)
110 return 1;
111 if (field_a < field_b)
112 return -1;
113 return 0;
114}
115
116static int __hpp__sort(struct hist_entry *a, struct hist_entry *b,
117 hpp_field_fn get_field)
118{
119 s64 ret;
120 int i, nr_members;
121 struct perf_evsel *evsel;
122 struct hist_entry *pair;
123 u64 *fields_a, *fields_b;
124
125 ret = field_cmp(get_field(a), get_field(b));
126 if (ret || !symbol_conf.event_group)
127 return ret;
128
129 evsel = hists_to_evsel(a->hists);
130 if (!perf_evsel__is_group_event(evsel))
131 return ret;
132
133 nr_members = evsel->nr_members;
134 fields_a = calloc(sizeof(*fields_a), nr_members);
135 fields_b = calloc(sizeof(*fields_b), nr_members);
136
137 if (!fields_a || !fields_b)
138 goto out;
139
140 list_for_each_entry(pair, &a->pairs.head, pairs.node) {
141 evsel = hists_to_evsel(pair->hists);
142 fields_a[perf_evsel__group_idx(evsel)] = get_field(pair);
143 }
144
145 list_for_each_entry(pair, &b->pairs.head, pairs.node) {
146 evsel = hists_to_evsel(pair->hists);
147 fields_b[perf_evsel__group_idx(evsel)] = get_field(pair);
148 }
149
150 for (i = 1; i < nr_members; i++) {
151 ret = field_cmp(fields_a[i], fields_b[i]);
152 if (ret)
153 break;
154 }
155
156out:
157 free(fields_a);
158 free(fields_b);
159
160 return ret;
161}
162
119#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ 163#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
120static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ 164static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
121 struct perf_hpp *hpp, \ 165 struct perf_hpp *hpp, \
@@ -179,7 +223,7 @@ static u64 he_get_##_field(struct hist_entry *he) \
179static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ 223static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
180 struct perf_hpp *hpp, struct hist_entry *he) \ 224 struct perf_hpp *hpp, struct hist_entry *he) \
181{ \ 225{ \
182 return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%", \ 226 return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
183 hpp_color_scnprintf, true); \ 227 hpp_color_scnprintf, true); \
184} 228}
185 229
@@ -188,10 +232,16 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
188 struct perf_hpp *hpp, struct hist_entry *he) \ 232 struct perf_hpp *hpp, struct hist_entry *he) \
189{ \ 233{ \
190 const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ 234 const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
191 return __hpp__fmt(hpp, he, he_get_##_field, NULL, fmt, \ 235 return __hpp__fmt(hpp, he, he_get_##_field, fmt, \
192 hpp_entry_scnprintf, true); \ 236 hpp_entry_scnprintf, true); \
193} 237}
194 238
239#define __HPP_SORT_FN(_type, _field) \
240static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \
241{ \
242 return __hpp__sort(a, b, he_get_##_field); \
243}
244
195#define __HPP_ENTRY_RAW_FN(_type, _field) \ 245#define __HPP_ENTRY_RAW_FN(_type, _field) \
196static u64 he_get_raw_##_field(struct hist_entry *he) \ 246static u64 he_get_raw_##_field(struct hist_entry *he) \
197{ \ 247{ \
@@ -202,20 +252,29 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
202 struct perf_hpp *hpp, struct hist_entry *he) \ 252 struct perf_hpp *hpp, struct hist_entry *he) \
203{ \ 253{ \
204 const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ 254 const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \
205 return __hpp__fmt(hpp, he, he_get_raw_##_field, NULL, fmt, \ 255 return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, \
206 hpp_entry_scnprintf, false); \ 256 hpp_entry_scnprintf, false); \
207} 257}
208 258
259#define __HPP_SORT_RAW_FN(_type, _field) \
260static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \
261{ \
262 return __hpp__sort(a, b, he_get_raw_##_field); \
263}
264
265
209#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \ 266#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \
210__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ 267__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
211__HPP_WIDTH_FN(_type, _min_width, _unit_width) \ 268__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
212__HPP_COLOR_PERCENT_FN(_type, _field) \ 269__HPP_COLOR_PERCENT_FN(_type, _field) \
213__HPP_ENTRY_PERCENT_FN(_type, _field) 270__HPP_ENTRY_PERCENT_FN(_type, _field) \
271__HPP_SORT_FN(_type, _field)
214 272
215#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \ 273#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \
216__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ 274__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
217__HPP_WIDTH_FN(_type, _min_width, _unit_width) \ 275__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
218__HPP_ENTRY_RAW_FN(_type, _field) 276__HPP_ENTRY_RAW_FN(_type, _field) \
277__HPP_SORT_RAW_FN(_type, _field)
219 278
220 279
221HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) 280HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)
@@ -227,19 +286,31 @@ HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
227HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) 286HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
228HPP_RAW_FNS(period, "Period", period, 12, 12) 287HPP_RAW_FNS(period, "Period", period, 12, 12)
229 288
289static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused,
290 struct hist_entry *b __maybe_unused)
291{
292 return 0;
293}
294
230#define HPP__COLOR_PRINT_FNS(_name) \ 295#define HPP__COLOR_PRINT_FNS(_name) \
231 { \ 296 { \
232 .header = hpp__header_ ## _name, \ 297 .header = hpp__header_ ## _name, \
233 .width = hpp__width_ ## _name, \ 298 .width = hpp__width_ ## _name, \
234 .color = hpp__color_ ## _name, \ 299 .color = hpp__color_ ## _name, \
235 .entry = hpp__entry_ ## _name \ 300 .entry = hpp__entry_ ## _name, \
301 .cmp = hpp__nop_cmp, \
302 .collapse = hpp__nop_cmp, \
303 .sort = hpp__sort_ ## _name, \
236 } 304 }
237 305
238#define HPP__PRINT_FNS(_name) \ 306#define HPP__PRINT_FNS(_name) \
239 { \ 307 { \
240 .header = hpp__header_ ## _name, \ 308 .header = hpp__header_ ## _name, \
241 .width = hpp__width_ ## _name, \ 309 .width = hpp__width_ ## _name, \
242 .entry = hpp__entry_ ## _name \ 310 .entry = hpp__entry_ ## _name, \
311 .cmp = hpp__nop_cmp, \
312 .collapse = hpp__nop_cmp, \
313 .sort = hpp__sort_ ## _name, \
243 } 314 }
244 315
245struct perf_hpp_fmt perf_hpp__format[] = { 316struct perf_hpp_fmt perf_hpp__format[] = {
@@ -253,6 +324,7 @@ struct perf_hpp_fmt perf_hpp__format[] = {
253}; 324};
254 325
255LIST_HEAD(perf_hpp__list); 326LIST_HEAD(perf_hpp__list);
327LIST_HEAD(perf_hpp__sort_list);
256 328
257 329
258#undef HPP__COLOR_PRINT_FNS 330#undef HPP__COLOR_PRINT_FNS
@@ -270,6 +342,25 @@ LIST_HEAD(perf_hpp__list);
270 342
271void perf_hpp__init(void) 343void perf_hpp__init(void)
272{ 344{
345 struct list_head *list;
346 int i;
347
348 for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
349 struct perf_hpp_fmt *fmt = &perf_hpp__format[i];
350
351 INIT_LIST_HEAD(&fmt->list);
352
353 /* sort_list may be linked by setup_sorting() */
354 if (fmt->sort_list.next == NULL)
355 INIT_LIST_HEAD(&fmt->sort_list);
356 }
357
358 /*
359 * If user specified field order, no need to setup default fields.
360 */
361 if (field_order)
362 return;
363
273 perf_hpp__column_enable(PERF_HPP__OVERHEAD); 364 perf_hpp__column_enable(PERF_HPP__OVERHEAD);
274 365
275 if (symbol_conf.show_cpu_utilization) { 366 if (symbol_conf.show_cpu_utilization) {
@@ -287,6 +378,11 @@ void perf_hpp__init(void)
287 378
288 if (symbol_conf.show_total_period) 379 if (symbol_conf.show_total_period)
289 perf_hpp__column_enable(PERF_HPP__PERIOD); 380 perf_hpp__column_enable(PERF_HPP__PERIOD);
381
382 /* prepend overhead field for backward compatiblity. */
383 list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list;
384 if (list_empty(list))
385 list_add(list, &perf_hpp__sort_list);
290} 386}
291 387
292void perf_hpp__column_register(struct perf_hpp_fmt *format) 388void perf_hpp__column_register(struct perf_hpp_fmt *format)
@@ -294,29 +390,90 @@ void perf_hpp__column_register(struct perf_hpp_fmt *format)
294 list_add_tail(&format->list, &perf_hpp__list); 390 list_add_tail(&format->list, &perf_hpp__list);
295} 391}
296 392
393void perf_hpp__register_sort_field(struct perf_hpp_fmt *format)
394{
395 list_add_tail(&format->sort_list, &perf_hpp__sort_list);
396}
397
297void perf_hpp__column_enable(unsigned col) 398void perf_hpp__column_enable(unsigned col)
298{ 399{
299 BUG_ON(col >= PERF_HPP__MAX_INDEX); 400 BUG_ON(col >= PERF_HPP__MAX_INDEX);
300 perf_hpp__column_register(&perf_hpp__format[col]); 401 perf_hpp__column_register(&perf_hpp__format[col]);
301} 402}
302 403
303int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, 404void perf_hpp__setup_output_field(void)
304 struct hists *hists)
305{ 405{
306 const char *sep = symbol_conf.field_sep; 406 struct perf_hpp_fmt *fmt;
307 struct sort_entry *se;
308 int ret = 0;
309 407
310 list_for_each_entry(se, &hist_entry__sort_list, list) { 408 /* append sort keys to output field */
311 if (se->elide) 409 perf_hpp__for_each_sort_list(fmt) {
410 if (!list_empty(&fmt->list))
312 continue; 411 continue;
313 412
314 ret += scnprintf(s + ret, size - ret, "%s", sep ?: " "); 413 /*
315 ret += se->se_snprintf(he, s + ret, size - ret, 414 * sort entry fields are dynamically created,
316 hists__col_len(hists, se->se_width_idx)); 415 * so they can share a same sort key even though
416 * the list is empty.
417 */
418 if (perf_hpp__is_sort_entry(fmt)) {
419 struct perf_hpp_fmt *pos;
420
421 perf_hpp__for_each_format(pos) {
422 if (perf_hpp__same_sort_entry(pos, fmt))
423 goto next;
424 }
425 }
426
427 perf_hpp__column_register(fmt);
428next:
429 continue;
317 } 430 }
431}
318 432
319 return ret; 433void perf_hpp__append_sort_keys(void)
434{
435 struct perf_hpp_fmt *fmt;
436
437 /* append output fields to sort keys */
438 perf_hpp__for_each_format(fmt) {
439 if (!list_empty(&fmt->sort_list))
440 continue;
441
442 /*
443 * sort entry fields are dynamically created,
444 * so they can share a same sort key even though
445 * the list is empty.
446 */
447 if (perf_hpp__is_sort_entry(fmt)) {
448 struct perf_hpp_fmt *pos;
449
450 perf_hpp__for_each_sort_list(pos) {
451 if (perf_hpp__same_sort_entry(pos, fmt))
452 goto next;
453 }
454 }
455
456 perf_hpp__register_sort_field(fmt);
457next:
458 continue;
459 }
460}
461
462void perf_hpp__reset_output_field(void)
463{
464 struct perf_hpp_fmt *fmt, *tmp;
465
466 /* reset output fields */
467 perf_hpp__for_each_format_safe(fmt, tmp) {
468 list_del_init(&fmt->list);
469 list_del_init(&fmt->sort_list);
470 }
471
472 /* reset sort keys */
473 perf_hpp__for_each_sort_list_safe(fmt, tmp) {
474 list_del_init(&fmt->list);
475 list_del_init(&fmt->sort_list);
476 }
320} 477}
321 478
322/* 479/*
@@ -325,22 +482,23 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size,
325unsigned int hists__sort_list_width(struct hists *hists) 482unsigned int hists__sort_list_width(struct hists *hists)
326{ 483{
327 struct perf_hpp_fmt *fmt; 484 struct perf_hpp_fmt *fmt;
328 struct sort_entry *se; 485 int ret = 0;
329 int i = 0, ret = 0; 486 bool first = true;
330 struct perf_hpp dummy_hpp; 487 struct perf_hpp dummy_hpp;
331 488
332 perf_hpp__for_each_format(fmt) { 489 perf_hpp__for_each_format(fmt) {
333 if (i) 490 if (perf_hpp__should_skip(fmt))
491 continue;
492
493 if (first)
494 first = false;
495 else
334 ret += 2; 496 ret += 2;
335 497
336 ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists)); 498 ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));
337 } 499 }
338 500
339 list_for_each_entry(se, &hist_entry__sort_list, list) 501 if (verbose && sort__has_sym) /* Addr + origin */
340 if (!se->elide)
341 ret += 2 + hists__col_len(hists, se->se_width_idx);
342
343 if (verbose) /* Addr + origin */
344 ret += 3 + BITS_PER_LONG / 4; 502 ret += 3 + BITS_PER_LONG / 4;
345 503
346 return ret; 504 return ret;
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c
index 5df5140a9f29..ba51fa8a1176 100644
--- a/tools/perf/ui/setup.c
+++ b/tools/perf/ui/setup.c
@@ -86,8 +86,6 @@ void setup_browser(bool fallback_to_pager)
86 use_browser = 0; 86 use_browser = 0;
87 if (fallback_to_pager) 87 if (fallback_to_pager)
88 setup_pager(); 88 setup_pager();
89
90 perf_hpp__init();
91 break; 89 break;
92 } 90 }
93} 91}
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index 9eccf7f4f367..9f57991025a9 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -183,7 +183,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
183 * the symbol. No need to print it otherwise it appears as 183 * the symbol. No need to print it otherwise it appears as
184 * displayed twice. 184 * displayed twice.
185 */ 185 */
186 if (!i++ && sort__first_dimension == SORT_SYM) 186 if (!i++ && field_order == NULL &&
187 sort_order && !prefixcmp(sort_order, "sym"))
187 continue; 188 continue;
188 if (!printed) { 189 if (!printed) {
189 ret += callchain__fprintf_left_margin(fp, left_margin); 190 ret += callchain__fprintf_left_margin(fp, left_margin);
@@ -296,18 +297,24 @@ static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
296 int left_margin = 0; 297 int left_margin = 0;
297 u64 total_period = hists->stats.total_period; 298 u64 total_period = hists->stats.total_period;
298 299
299 if (sort__first_dimension == SORT_COMM) { 300 if (field_order == NULL && (sort_order == NULL ||
300 struct sort_entry *se = list_first_entry(&hist_entry__sort_list, 301 !prefixcmp(sort_order, "comm"))) {
301 typeof(*se), list); 302 struct perf_hpp_fmt *fmt;
302 left_margin = hists__col_len(hists, se->se_width_idx); 303
303 left_margin -= thread__comm_len(he->thread); 304 perf_hpp__for_each_format(fmt) {
304 } 305 if (!perf_hpp__is_sort_entry(fmt))
306 continue;
305 307
308 /* must be 'comm' sort entry */
309 left_margin = fmt->width(fmt, NULL, hists_to_evsel(hists));
310 left_margin -= thread__comm_len(he->thread);
311 break;
312 }
313 }
306 return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); 314 return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
307} 315}
308 316
309static int hist_entry__period_snprintf(struct perf_hpp *hpp, 317static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
310 struct hist_entry *he)
311{ 318{
312 const char *sep = symbol_conf.field_sep; 319 const char *sep = symbol_conf.field_sep;
313 struct perf_hpp_fmt *fmt; 320 struct perf_hpp_fmt *fmt;
@@ -319,6 +326,9 @@ static int hist_entry__period_snprintf(struct perf_hpp *hpp,
319 return 0; 326 return 0;
320 327
321 perf_hpp__for_each_format(fmt) { 328 perf_hpp__for_each_format(fmt) {
329 if (perf_hpp__should_skip(fmt))
330 continue;
331
322 /* 332 /*
323 * If there's no field_sep, we still need 333 * If there's no field_sep, we still need
324 * to display initial ' '. 334 * to display initial ' '.
@@ -353,8 +363,7 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
353 if (size == 0 || size > bfsz) 363 if (size == 0 || size > bfsz)
354 size = hpp.size = bfsz; 364 size = hpp.size = bfsz;
355 365
356 ret = hist_entry__period_snprintf(&hpp, he); 366 hist_entry__snprintf(he, &hpp);
357 hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
358 367
359 ret = fprintf(fp, "%s\n", bf); 368 ret = fprintf(fp, "%s\n", bf);
360 369
@@ -368,12 +377,10 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
368 int max_cols, float min_pcnt, FILE *fp) 377 int max_cols, float min_pcnt, FILE *fp)
369{ 378{
370 struct perf_hpp_fmt *fmt; 379 struct perf_hpp_fmt *fmt;
371 struct sort_entry *se;
372 struct rb_node *nd; 380 struct rb_node *nd;
373 size_t ret = 0; 381 size_t ret = 0;
374 unsigned int width; 382 unsigned int width;
375 const char *sep = symbol_conf.field_sep; 383 const char *sep = symbol_conf.field_sep;
376 const char *col_width = symbol_conf.col_width_list_str;
377 int nr_rows = 0; 384 int nr_rows = 0;
378 char bf[96]; 385 char bf[96];
379 struct perf_hpp dummy_hpp = { 386 struct perf_hpp dummy_hpp = {
@@ -386,12 +393,19 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
386 393
387 init_rem_hits(); 394 init_rem_hits();
388 395
396
397 perf_hpp__for_each_format(fmt)
398 perf_hpp__reset_width(fmt, hists);
399
389 if (!show_header) 400 if (!show_header)
390 goto print_entries; 401 goto print_entries;
391 402
392 fprintf(fp, "# "); 403 fprintf(fp, "# ");
393 404
394 perf_hpp__for_each_format(fmt) { 405 perf_hpp__for_each_format(fmt) {
406 if (perf_hpp__should_skip(fmt))
407 continue;
408
395 if (!first) 409 if (!first)
396 fprintf(fp, "%s", sep ?: " "); 410 fprintf(fp, "%s", sep ?: " ");
397 else 411 else
@@ -401,28 +415,6 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
401 fprintf(fp, "%s", bf); 415 fprintf(fp, "%s", bf);
402 } 416 }
403 417
404 list_for_each_entry(se, &hist_entry__sort_list, list) {
405 if (se->elide)
406 continue;
407 if (sep) {
408 fprintf(fp, "%c%s", *sep, se->se_header);
409 continue;
410 }
411 width = strlen(se->se_header);
412 if (symbol_conf.col_width_list_str) {
413 if (col_width) {
414 hists__set_col_len(hists, se->se_width_idx,
415 atoi(col_width));
416 col_width = strchr(col_width, ',');
417 if (col_width)
418 ++col_width;
419 }
420 }
421 if (!hists__new_col_len(hists, se->se_width_idx, width))
422 width = hists__col_len(hists, se->se_width_idx);
423 fprintf(fp, " %*s", width, se->se_header);
424 }
425
426 fprintf(fp, "\n"); 418 fprintf(fp, "\n");
427 if (max_rows && ++nr_rows >= max_rows) 419 if (max_rows && ++nr_rows >= max_rows)
428 goto out; 420 goto out;
@@ -437,6 +429,9 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
437 perf_hpp__for_each_format(fmt) { 429 perf_hpp__for_each_format(fmt) {
438 unsigned int i; 430 unsigned int i;
439 431
432 if (perf_hpp__should_skip(fmt))
433 continue;
434
440 if (!first) 435 if (!first)
441 fprintf(fp, "%s", sep ?: " "); 436 fprintf(fp, "%s", sep ?: " ");
442 else 437 else
@@ -447,20 +442,6 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
447 fprintf(fp, "."); 442 fprintf(fp, ".");
448 } 443 }
449 444
450 list_for_each_entry(se, &hist_entry__sort_list, list) {
451 unsigned int i;
452
453 if (se->elide)
454 continue;
455
456 fprintf(fp, " ");
457 width = hists__col_len(hists, se->se_width_idx);
458 if (width == 0)
459 width = strlen(se->se_header);
460 for (i = 0; i < width; i++)
461 fprintf(fp, ".");
462 }
463
464 fprintf(fp, "\n"); 445 fprintf(fp, "\n");
465 if (max_rows && ++nr_rows >= max_rows) 446 if (max_rows && ++nr_rows >= max_rows)
466 goto out; 447 goto out;
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 7f0236cea4fe..b262b44b7a65 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -432,11 +432,14 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
432int64_t 432int64_t
433hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) 433hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
434{ 434{
435 struct sort_entry *se; 435 struct perf_hpp_fmt *fmt;
436 int64_t cmp = 0; 436 int64_t cmp = 0;
437 437
438 list_for_each_entry(se, &hist_entry__sort_list, list) { 438 perf_hpp__for_each_sort_list(fmt) {
439 cmp = se->se_cmp(left, right); 439 if (perf_hpp__should_skip(fmt))
440 continue;
441
442 cmp = fmt->cmp(left, right);
440 if (cmp) 443 if (cmp)
441 break; 444 break;
442 } 445 }
@@ -447,15 +450,14 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
447int64_t 450int64_t
448hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) 451hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
449{ 452{
450 struct sort_entry *se; 453 struct perf_hpp_fmt *fmt;
451 int64_t cmp = 0; 454 int64_t cmp = 0;
452 455
453 list_for_each_entry(se, &hist_entry__sort_list, list) { 456 perf_hpp__for_each_sort_list(fmt) {
454 int64_t (*f)(struct hist_entry *, struct hist_entry *); 457 if (perf_hpp__should_skip(fmt))
455 458 continue;
456 f = se->se_collapse ?: se->se_cmp;
457 459
458 cmp = f(left, right); 460 cmp = fmt->collapse(left, right);
459 if (cmp) 461 if (cmp)
460 break; 462 break;
461 } 463 }
@@ -568,64 +570,21 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
568 } 570 }
569} 571}
570 572
571/* 573static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
572 * reverse the map, sort on period.
573 */
574
575static int period_cmp(u64 period_a, u64 period_b)
576{ 574{
577 if (period_a > period_b) 575 struct perf_hpp_fmt *fmt;
578 return 1; 576 int64_t cmp = 0;
579 if (period_a < period_b)
580 return -1;
581 return 0;
582}
583
584static int hist_entry__sort_on_period(struct hist_entry *a,
585 struct hist_entry *b)
586{
587 int ret;
588 int i, nr_members;
589 struct perf_evsel *evsel;
590 struct hist_entry *pair;
591 u64 *periods_a, *periods_b;
592
593 ret = period_cmp(a->stat.period, b->stat.period);
594 if (ret || !symbol_conf.event_group)
595 return ret;
596
597 evsel = hists_to_evsel(a->hists);
598 nr_members = evsel->nr_members;
599 if (nr_members <= 1)
600 return ret;
601
602 periods_a = zalloc(sizeof(periods_a) * nr_members);
603 periods_b = zalloc(sizeof(periods_b) * nr_members);
604
605 if (!periods_a || !periods_b)
606 goto out;
607
608 list_for_each_entry(pair, &a->pairs.head, pairs.node) {
609 evsel = hists_to_evsel(pair->hists);
610 periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period;
611 }
612 577
613 list_for_each_entry(pair, &b->pairs.head, pairs.node) { 578 perf_hpp__for_each_sort_list(fmt) {
614 evsel = hists_to_evsel(pair->hists); 579 if (perf_hpp__should_skip(fmt))
615 periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period; 580 continue;
616 }
617 581
618 for (i = 1; i < nr_members; i++) { 582 cmp = fmt->sort(a, b);
619 ret = period_cmp(periods_a[i], periods_b[i]); 583 if (cmp)
620 if (ret)
621 break; 584 break;
622 } 585 }
623 586
624out: 587 return cmp;
625 free(periods_a);
626 free(periods_b);
627
628 return ret;
629} 588}
630 589
631static void hists__reset_filter_stats(struct hists *hists) 590static void hists__reset_filter_stats(struct hists *hists)
@@ -673,7 +632,7 @@ static void __hists__insert_output_entry(struct rb_root *entries,
673 parent = *p; 632 parent = *p;
674 iter = rb_entry(parent, struct hist_entry, rb_node); 633 iter = rb_entry(parent, struct hist_entry, rb_node);
675 634
676 if (hist_entry__sort_on_period(he, iter) > 0) 635 if (hist_entry__sort(he, iter) > 0)
677 p = &(*p)->rb_left; 636 p = &(*p)->rb_left;
678 else 637 else
679 p = &(*p)->rb_right; 638 p = &(*p)->rb_right;
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 38c3e874c164..a8418d19808d 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -160,15 +160,29 @@ struct perf_hpp_fmt {
160 struct hist_entry *he); 160 struct hist_entry *he);
161 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 161 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
162 struct hist_entry *he); 162 struct hist_entry *he);
163 int64_t (*cmp)(struct hist_entry *a, struct hist_entry *b);
164 int64_t (*collapse)(struct hist_entry *a, struct hist_entry *b);
165 int64_t (*sort)(struct hist_entry *a, struct hist_entry *b);
163 166
164 struct list_head list; 167 struct list_head list;
168 struct list_head sort_list;
165}; 169};
166 170
167extern struct list_head perf_hpp__list; 171extern struct list_head perf_hpp__list;
172extern struct list_head perf_hpp__sort_list;
168 173
169#define perf_hpp__for_each_format(format) \ 174#define perf_hpp__for_each_format(format) \
170 list_for_each_entry(format, &perf_hpp__list, list) 175 list_for_each_entry(format, &perf_hpp__list, list)
171 176
177#define perf_hpp__for_each_format_safe(format, tmp) \
178 list_for_each_entry_safe(format, tmp, &perf_hpp__list, list)
179
180#define perf_hpp__for_each_sort_list(format) \
181 list_for_each_entry(format, &perf_hpp__sort_list, sort_list)
182
183#define perf_hpp__for_each_sort_list_safe(format, tmp) \
184 list_for_each_entry_safe(format, tmp, &perf_hpp__sort_list, sort_list)
185
172extern struct perf_hpp_fmt perf_hpp__format[]; 186extern struct perf_hpp_fmt perf_hpp__format[];
173 187
174enum { 188enum {
@@ -187,14 +201,23 @@ enum {
187void perf_hpp__init(void); 201void perf_hpp__init(void);
188void perf_hpp__column_register(struct perf_hpp_fmt *format); 202void perf_hpp__column_register(struct perf_hpp_fmt *format);
189void perf_hpp__column_enable(unsigned col); 203void perf_hpp__column_enable(unsigned col);
204void perf_hpp__register_sort_field(struct perf_hpp_fmt *format);
205void perf_hpp__setup_output_field(void);
206void perf_hpp__reset_output_field(void);
207void perf_hpp__append_sort_keys(void);
208
209bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format);
210bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b);
211bool perf_hpp__should_skip(struct perf_hpp_fmt *format);
212void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists);
190 213
191typedef u64 (*hpp_field_fn)(struct hist_entry *he); 214typedef u64 (*hpp_field_fn)(struct hist_entry *he);
192typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); 215typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front);
193typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); 216typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...);
194 217
195int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, 218int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
196 hpp_field_fn get_field, hpp_callback_fn callback, 219 hpp_field_fn get_field, const char *fmt,
197 const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent); 220 hpp_snprint_fn print_fn, bool fmt_percent);
198 221
199static inline void advance_hpp(struct perf_hpp *hpp, int inc) 222static inline void advance_hpp(struct perf_hpp *hpp, int inc)
200{ 223{
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index ba5f5c0c838b..8ccbb32eda25 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -32,6 +32,93 @@ static inline int is_no_dso_memory(const char *filename)
32 !strcmp(filename, "[heap]"); 32 !strcmp(filename, "[heap]");
33} 33}
34 34
35static inline int is_android_lib(const char *filename)
36{
37 return !strncmp(filename, "/data/app-lib", 13) ||
38 !strncmp(filename, "/system/lib", 11);
39}
40
41static inline bool replace_android_lib(const char *filename, char *newfilename)
42{
43 const char *libname;
44 char *app_abi;
45 size_t app_abi_length, new_length;
46 size_t lib_length = 0;
47
48 libname = strrchr(filename, '/');
49 if (libname)
50 lib_length = strlen(libname);
51
52 app_abi = getenv("APP_ABI");
53 if (!app_abi)
54 return false;
55
56 app_abi_length = strlen(app_abi);
57
58 if (!strncmp(filename, "/data/app-lib", 13)) {
59 char *apk_path;
60
61 if (!app_abi_length)
62 return false;
63
64 new_length = 7 + app_abi_length + lib_length;
65
66 apk_path = getenv("APK_PATH");
67 if (apk_path) {
68 new_length += strlen(apk_path) + 1;
69 if (new_length > PATH_MAX)
70 return false;
71 snprintf(newfilename, new_length,
72 "%s/libs/%s/%s", apk_path, app_abi, libname);
73 } else {
74 if (new_length > PATH_MAX)
75 return false;
76 snprintf(newfilename, new_length,
77 "libs/%s/%s", app_abi, libname);
78 }
79 return true;
80 }
81
82 if (!strncmp(filename, "/system/lib/", 11)) {
83 char *ndk, *app;
84 const char *arch;
85 size_t ndk_length;
86 size_t app_length;
87
88 ndk = getenv("NDK_ROOT");
89 app = getenv("APP_PLATFORM");
90
91 if (!(ndk && app))
92 return false;
93
94 ndk_length = strlen(ndk);
95 app_length = strlen(app);
96
97 if (!(ndk_length && app_length && app_abi_length))
98 return false;
99
100 arch = !strncmp(app_abi, "arm", 3) ? "arm" :
101 !strncmp(app_abi, "mips", 4) ? "mips" :
102 !strncmp(app_abi, "x86", 3) ? "x86" : NULL;
103
104 if (!arch)
105 return false;
106
107 new_length = 27 + ndk_length +
108 app_length + lib_length
109 + strlen(arch);
110
111 if (new_length > PATH_MAX)
112 return false;
113 snprintf(newfilename, new_length,
114 "%s/platforms/%s/arch-%s/usr/lib/%s",
115 ndk, app, arch, libname);
116
117 return true;
118 }
119 return false;
120}
121
35void map__init(struct map *map, enum map_type type, 122void map__init(struct map *map, enum map_type type,
36 u64 start, u64 end, u64 pgoff, struct dso *dso) 123 u64 start, u64 end, u64 pgoff, struct dso *dso)
37{ 124{
@@ -59,8 +146,9 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
59 if (map != NULL) { 146 if (map != NULL) {
60 char newfilename[PATH_MAX]; 147 char newfilename[PATH_MAX];
61 struct dso *dso; 148 struct dso *dso;
62 int anon, no_dso, vdso; 149 int anon, no_dso, vdso, android;
63 150
151 android = is_android_lib(filename);
64 anon = is_anon_memory(filename); 152 anon = is_anon_memory(filename);
65 vdso = is_vdso_map(filename); 153 vdso = is_vdso_map(filename);
66 no_dso = is_no_dso_memory(filename); 154 no_dso = is_no_dso_memory(filename);
@@ -75,6 +163,11 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
75 filename = newfilename; 163 filename = newfilename;
76 } 164 }
77 165
166 if (android) {
167 if (replace_android_lib(filename, newfilename))
168 filename = newfilename;
169 }
170
78 if (vdso) { 171 if (vdso) {
79 pgoff = 0; 172 pgoff = 0;
80 dso = vdso__dso_findnew(dsos__list); 173 dso = vdso__dso_findnew(dsos__list);
diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c
index 3322b8446e89..31ee02d4e988 100644
--- a/tools/perf/util/pager.c
+++ b/tools/perf/util/pager.c
@@ -57,13 +57,13 @@ void setup_pager(void)
57 } 57 }
58 if (!pager) 58 if (!pager)
59 pager = getenv("PAGER"); 59 pager = getenv("PAGER");
60 if (!pager) { 60 if (!(pager || access("/usr/bin/pager", X_OK)))
61 if (!access("/usr/bin/pager", X_OK)) 61 pager = "/usr/bin/pager";
62 pager = "/usr/bin/pager"; 62 if (!(pager || access("/usr/bin/less", X_OK)))
63 } 63 pager = "/usr/bin/less";
64 if (!pager) 64 if (!pager)
65 pager = "less"; 65 pager = "cat";
66 else if (!*pager || !strcmp(pager, "cat")) 66 if (!*pager || !strcmp(pager, "cat"))
67 return; 67 return;
68 68
69 spawned_pager = 1; /* means we are emitting to terminal */ 69 spawned_pager = 1; /* means we are emitting to terminal */
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 635cd8f8b22e..901b9bece2ee 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -2,12 +2,18 @@
2#include "hist.h" 2#include "hist.h"
3#include "comm.h" 3#include "comm.h"
4#include "symbol.h" 4#include "symbol.h"
5#include "evsel.h"
5 6
6regex_t parent_regex; 7regex_t parent_regex;
7const char default_parent_pattern[] = "^sys_|^do_page_fault"; 8const char default_parent_pattern[] = "^sys_|^do_page_fault";
8const char *parent_pattern = default_parent_pattern; 9const char *parent_pattern = default_parent_pattern;
9const char default_sort_order[] = "comm,dso,symbol"; 10const char default_sort_order[] = "comm,dso,symbol";
10const char *sort_order = default_sort_order; 11const char default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to";
12const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
13const char default_top_sort_order[] = "dso,symbol";
14const char default_diff_sort_order[] = "dso,symbol";
15const char *sort_order;
16const char *field_order;
11regex_t ignore_callees_regex; 17regex_t ignore_callees_regex;
12int have_ignore_callees = 0; 18int have_ignore_callees = 0;
13int sort__need_collapse = 0; 19int sort__need_collapse = 0;
@@ -16,9 +22,6 @@ int sort__has_sym = 0;
16int sort__has_dso = 0; 22int sort__has_dso = 0;
17enum sort_mode sort__mode = SORT_MODE__NORMAL; 23enum sort_mode sort__mode = SORT_MODE__NORMAL;
18 24
19enum sort_type sort__first_dimension;
20
21LIST_HEAD(hist_entry__sort_list);
22 25
23static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) 26static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
24{ 27{
@@ -93,6 +96,12 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
93 return comm__str(right->comm) - comm__str(left->comm); 96 return comm__str(right->comm) - comm__str(left->comm);
94} 97}
95 98
99static int64_t
100sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
101{
102 return strcmp(comm__str(right->comm), comm__str(left->comm));
103}
104
96static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, 105static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
97 size_t size, unsigned int width) 106 size_t size, unsigned int width)
98{ 107{
@@ -103,6 +112,7 @@ struct sort_entry sort_comm = {
103 .se_header = "Command", 112 .se_header = "Command",
104 .se_cmp = sort__comm_cmp, 113 .se_cmp = sort__comm_cmp,
105 .se_collapse = sort__comm_collapse, 114 .se_collapse = sort__comm_collapse,
115 .se_sort = sort__comm_sort,
106 .se_snprintf = hist_entry__comm_snprintf, 116 .se_snprintf = hist_entry__comm_snprintf,
107 .se_width_idx = HISTC_COMM, 117 .se_width_idx = HISTC_COMM,
108}; 118};
@@ -116,7 +126,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
116 const char *dso_name_l, *dso_name_r; 126 const char *dso_name_l, *dso_name_r;
117 127
118 if (!dso_l || !dso_r) 128 if (!dso_l || !dso_r)
119 return cmp_null(dso_l, dso_r); 129 return cmp_null(dso_r, dso_l);
120 130
121 if (verbose) { 131 if (verbose) {
122 dso_name_l = dso_l->long_name; 132 dso_name_l = dso_l->long_name;
@@ -132,7 +142,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
132static int64_t 142static int64_t
133sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) 143sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
134{ 144{
135 return _sort__dso_cmp(left->ms.map, right->ms.map); 145 return _sort__dso_cmp(right->ms.map, left->ms.map);
136} 146}
137 147
138static int _hist_entry__dso_snprintf(struct map *map, char *bf, 148static int _hist_entry__dso_snprintf(struct map *map, char *bf,
@@ -204,6 +214,15 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
204 return _sort__sym_cmp(left->ms.sym, right->ms.sym); 214 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
205} 215}
206 216
217static int64_t
218sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
219{
220 if (!left->ms.sym || !right->ms.sym)
221 return cmp_null(left->ms.sym, right->ms.sym);
222
223 return strcmp(right->ms.sym->name, left->ms.sym->name);
224}
225
207static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, 226static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
208 u64 ip, char level, char *bf, size_t size, 227 u64 ip, char level, char *bf, size_t size,
209 unsigned int width) 228 unsigned int width)
@@ -250,6 +269,7 @@ static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
250struct sort_entry sort_sym = { 269struct sort_entry sort_sym = {
251 .se_header = "Symbol", 270 .se_header = "Symbol",
252 .se_cmp = sort__sym_cmp, 271 .se_cmp = sort__sym_cmp,
272 .se_sort = sort__sym_sort,
253 .se_snprintf = hist_entry__sym_snprintf, 273 .se_snprintf = hist_entry__sym_snprintf,
254 .se_width_idx = HISTC_SYMBOL, 274 .se_width_idx = HISTC_SYMBOL,
255}; 275};
@@ -277,7 +297,7 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
277 map__rip_2objdump(map, right->ip)); 297 map__rip_2objdump(map, right->ip));
278 } 298 }
279 } 299 }
280 return strcmp(left->srcline, right->srcline); 300 return strcmp(right->srcline, left->srcline);
281} 301}
282 302
283static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, 303static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
@@ -305,7 +325,7 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
305 if (!sym_l || !sym_r) 325 if (!sym_l || !sym_r)
306 return cmp_null(sym_l, sym_r); 326 return cmp_null(sym_l, sym_r);
307 327
308 return strcmp(sym_l->name, sym_r->name); 328 return strcmp(sym_r->name, sym_l->name);
309} 329}
310 330
311static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, 331static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
@@ -1027,19 +1047,192 @@ static struct sort_dimension memory_sort_dimensions[] = {
1027 1047
1028#undef DIM 1048#undef DIM
1029 1049
1030static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) 1050struct hpp_dimension {
1051 const char *name;
1052 struct perf_hpp_fmt *fmt;
1053 int taken;
1054};
1055
1056#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1057
1058static struct hpp_dimension hpp_sort_dimensions[] = {
1059 DIM(PERF_HPP__OVERHEAD, "overhead"),
1060 DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
1061 DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
1062 DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
1063 DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
1064 DIM(PERF_HPP__SAMPLES, "sample"),
1065 DIM(PERF_HPP__PERIOD, "period"),
1066};
1067
1068#undef DIM
1069
1070struct hpp_sort_entry {
1071 struct perf_hpp_fmt hpp;
1072 struct sort_entry *se;
1073};
1074
1075bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1031{ 1076{
1032 if (sd->taken) 1077 struct hpp_sort_entry *hse_a;
1078 struct hpp_sort_entry *hse_b;
1079
1080 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1081 return false;
1082
1083 hse_a = container_of(a, struct hpp_sort_entry, hpp);
1084 hse_b = container_of(b, struct hpp_sort_entry, hpp);
1085
1086 return hse_a->se == hse_b->se;
1087}
1088
1089void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
1090{
1091 struct hpp_sort_entry *hse;
1092
1093 if (!perf_hpp__is_sort_entry(fmt))
1033 return; 1094 return;
1034 1095
1096 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1097 hists__new_col_len(hists, hse->se->se_width_idx,
1098 strlen(hse->se->se_header));
1099}
1100
1101static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1102 struct perf_evsel *evsel)
1103{
1104 struct hpp_sort_entry *hse;
1105 size_t len;
1106
1107 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1108 len = hists__col_len(&evsel->hists, hse->se->se_width_idx);
1109
1110 return scnprintf(hpp->buf, hpp->size, "%*s", len, hse->se->se_header);
1111}
1112
1113static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
1114 struct perf_hpp *hpp __maybe_unused,
1115 struct perf_evsel *evsel)
1116{
1117 struct hpp_sort_entry *hse;
1118
1119 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1120
1121 return hists__col_len(&evsel->hists, hse->se->se_width_idx);
1122}
1123
1124static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1125 struct hist_entry *he)
1126{
1127 struct hpp_sort_entry *hse;
1128 size_t len;
1129
1130 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1131 len = hists__col_len(he->hists, hse->se->se_width_idx);
1132
1133 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1134}
1135
1136static struct hpp_sort_entry *
1137__sort_dimension__alloc_hpp(struct sort_dimension *sd)
1138{
1139 struct hpp_sort_entry *hse;
1140
1141 hse = malloc(sizeof(*hse));
1142 if (hse == NULL) {
1143 pr_err("Memory allocation failed\n");
1144 return NULL;
1145 }
1146
1147 hse->se = sd->entry;
1148 hse->hpp.header = __sort__hpp_header;
1149 hse->hpp.width = __sort__hpp_width;
1150 hse->hpp.entry = __sort__hpp_entry;
1151 hse->hpp.color = NULL;
1152
1153 hse->hpp.cmp = sd->entry->se_cmp;
1154 hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp;
1155 hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse;
1156
1157 INIT_LIST_HEAD(&hse->hpp.list);
1158 INIT_LIST_HEAD(&hse->hpp.sort_list);
1159
1160 return hse;
1161}
1162
1163bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1164{
1165 return format->header == __sort__hpp_header;
1166}
1167
1168static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
1169{
1170 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1171
1172 if (hse == NULL)
1173 return -1;
1174
1175 perf_hpp__register_sort_field(&hse->hpp);
1176 return 0;
1177}
1178
1179static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
1180{
1181 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1182
1183 if (hse == NULL)
1184 return -1;
1185
1186 perf_hpp__column_register(&hse->hpp);
1187 return 0;
1188}
1189
1190static int __sort_dimension__add(struct sort_dimension *sd)
1191{
1192 if (sd->taken)
1193 return 0;
1194
1195 if (__sort_dimension__add_hpp_sort(sd) < 0)
1196 return -1;
1197
1035 if (sd->entry->se_collapse) 1198 if (sd->entry->se_collapse)
1036 sort__need_collapse = 1; 1199 sort__need_collapse = 1;
1037 1200
1038 if (list_empty(&hist_entry__sort_list)) 1201 sd->taken = 1;
1039 sort__first_dimension = idx; 1202
1203 return 0;
1204}
1205
1206static int __hpp_dimension__add(struct hpp_dimension *hd)
1207{
1208 if (!hd->taken) {
1209 hd->taken = 1;
1210
1211 perf_hpp__register_sort_field(hd->fmt);
1212 }
1213 return 0;
1214}
1215
1216static int __sort_dimension__add_output(struct sort_dimension *sd)
1217{
1218 if (sd->taken)
1219 return 0;
1220
1221 if (__sort_dimension__add_hpp_output(sd) < 0)
1222 return -1;
1040 1223
1041 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
1042 sd->taken = 1; 1224 sd->taken = 1;
1225 return 0;
1226}
1227
1228static int __hpp_dimension__add_output(struct hpp_dimension *hd)
1229{
1230 if (!hd->taken) {
1231 hd->taken = 1;
1232
1233 perf_hpp__column_register(hd->fmt);
1234 }
1235 return 0;
1043} 1236}
1044 1237
1045int sort_dimension__add(const char *tok) 1238int sort_dimension__add(const char *tok)
@@ -1068,8 +1261,16 @@ int sort_dimension__add(const char *tok)
1068 sort__has_dso = 1; 1261 sort__has_dso = 1;
1069 } 1262 }
1070 1263
1071 __sort_dimension__add(sd, i); 1264 return __sort_dimension__add(sd);
1072 return 0; 1265 }
1266
1267 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1268 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1269
1270 if (strncasecmp(tok, hd->name, strlen(tok)))
1271 continue;
1272
1273 return __hpp_dimension__add(hd);
1073 } 1274 }
1074 1275
1075 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { 1276 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
@@ -1084,7 +1285,7 @@ int sort_dimension__add(const char *tok)
1084 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) 1285 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
1085 sort__has_sym = 1; 1286 sort__has_sym = 1;
1086 1287
1087 __sort_dimension__add(sd, i + __SORT_BRANCH_STACK); 1288 __sort_dimension__add(sd);
1088 return 0; 1289 return 0;
1089 } 1290 }
1090 1291
@@ -1100,18 +1301,47 @@ int sort_dimension__add(const char *tok)
1100 if (sd->entry == &sort_mem_daddr_sym) 1301 if (sd->entry == &sort_mem_daddr_sym)
1101 sort__has_sym = 1; 1302 sort__has_sym = 1;
1102 1303
1103 __sort_dimension__add(sd, i + __SORT_MEMORY_MODE); 1304 __sort_dimension__add(sd);
1104 return 0; 1305 return 0;
1105 } 1306 }
1106 1307
1107 return -ESRCH; 1308 return -ESRCH;
1108} 1309}
1109 1310
1110int setup_sorting(void) 1311static const char *get_default_sort_order(void)
1111{ 1312{
1112 char *tmp, *tok, *str = strdup(sort_order); 1313 const char *default_sort_orders[] = {
1314 default_sort_order,
1315 default_branch_sort_order,
1316 default_mem_sort_order,
1317 default_top_sort_order,
1318 default_diff_sort_order,
1319 };
1320
1321 BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
1322
1323 return default_sort_orders[sort__mode];
1324}
1325
1326static int __setup_sorting(void)
1327{
1328 char *tmp, *tok, *str;
1329 const char *sort_keys = sort_order;
1113 int ret = 0; 1330 int ret = 0;
1114 1331
1332 if (sort_keys == NULL) {
1333 if (field_order) {
1334 /*
1335 * If user specified field order but no sort order,
1336 * we'll honor it and not add default sort orders.
1337 */
1338 return 0;
1339 }
1340
1341 sort_keys = get_default_sort_order();
1342 }
1343
1344 str = strdup(sort_keys);
1115 if (str == NULL) { 1345 if (str == NULL) {
1116 error("Not enough memory to setup sort keys"); 1346 error("Not enough memory to setup sort keys");
1117 return -ENOMEM; 1347 return -ENOMEM;
@@ -1133,6 +1363,17 @@ int setup_sorting(void)
1133 return ret; 1363 return ret;
1134} 1364}
1135 1365
1366bool perf_hpp__should_skip(struct perf_hpp_fmt *format)
1367{
1368 if (perf_hpp__is_sort_entry(format)) {
1369 struct hpp_sort_entry *hse;
1370
1371 hse = container_of(format, struct hpp_sort_entry, hpp);
1372 return hse->se->elide;
1373 }
1374 return false;
1375}
1376
1136static void sort_entry__setup_elide(struct sort_entry *se, 1377static void sort_entry__setup_elide(struct sort_entry *se,
1137 struct strlist *list, 1378 struct strlist *list,
1138 const char *list_name, FILE *fp) 1379 const char *list_name, FILE *fp)
@@ -1147,7 +1388,8 @@ static void sort_entry__setup_elide(struct sort_entry *se,
1147 1388
1148void sort__setup_elide(FILE *output) 1389void sort__setup_elide(FILE *output)
1149{ 1390{
1150 struct sort_entry *se; 1391 struct perf_hpp_fmt *fmt;
1392 struct hpp_sort_entry *hse;
1151 1393
1152 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, 1394 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1153 "dso", output); 1395 "dso", output);
@@ -1188,11 +1430,157 @@ void sort__setup_elide(FILE *output)
1188 * It makes no sense to elide all of sort entries. 1430 * It makes no sense to elide all of sort entries.
1189 * Just revert them to show up again. 1431 * Just revert them to show up again.
1190 */ 1432 */
1191 list_for_each_entry(se, &hist_entry__sort_list, list) { 1433 perf_hpp__for_each_format(fmt) {
1192 if (!se->elide) 1434 if (!perf_hpp__is_sort_entry(fmt))
1435 continue;
1436
1437 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1438 if (!hse->se->elide)
1193 return; 1439 return;
1194 } 1440 }
1195 1441
1196 list_for_each_entry(se, &hist_entry__sort_list, list) 1442 perf_hpp__for_each_format(fmt) {
1197 se->elide = false; 1443 if (!perf_hpp__is_sort_entry(fmt))
1444 continue;
1445
1446 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1447 hse->se->elide = false;
1448 }
1449}
1450
1451static int output_field_add(char *tok)
1452{
1453 unsigned int i;
1454
1455 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1456 struct sort_dimension *sd = &common_sort_dimensions[i];
1457
1458 if (strncasecmp(tok, sd->name, strlen(tok)))
1459 continue;
1460
1461 return __sort_dimension__add_output(sd);
1462 }
1463
1464 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1465 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1466
1467 if (strncasecmp(tok, hd->name, strlen(tok)))
1468 continue;
1469
1470 return __hpp_dimension__add_output(hd);
1471 }
1472
1473 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1474 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1475
1476 if (strncasecmp(tok, sd->name, strlen(tok)))
1477 continue;
1478
1479 return __sort_dimension__add_output(sd);
1480 }
1481
1482 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1483 struct sort_dimension *sd = &memory_sort_dimensions[i];
1484
1485 if (strncasecmp(tok, sd->name, strlen(tok)))
1486 continue;
1487
1488 return __sort_dimension__add_output(sd);
1489 }
1490
1491 return -ESRCH;
1492}
1493
1494static void reset_dimensions(void)
1495{
1496 unsigned int i;
1497
1498 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
1499 common_sort_dimensions[i].taken = 0;
1500
1501 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
1502 hpp_sort_dimensions[i].taken = 0;
1503
1504 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
1505 bstack_sort_dimensions[i].taken = 0;
1506
1507 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
1508 memory_sort_dimensions[i].taken = 0;
1509}
1510
1511static int __setup_output_field(void)
1512{
1513 char *tmp, *tok, *str;
1514 int ret = 0;
1515
1516 if (field_order == NULL)
1517 return 0;
1518
1519 reset_dimensions();
1520
1521 str = strdup(field_order);
1522 if (str == NULL) {
1523 error("Not enough memory to setup output fields");
1524 return -ENOMEM;
1525 }
1526
1527 for (tok = strtok_r(str, ", ", &tmp);
1528 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1529 ret = output_field_add(tok);
1530 if (ret == -EINVAL) {
1531 error("Invalid --fields key: `%s'", tok);
1532 break;
1533 } else if (ret == -ESRCH) {
1534 error("Unknown --fields key: `%s'", tok);
1535 break;
1536 }
1537 }
1538
1539 free(str);
1540 return ret;
1541}
1542
1543int setup_sorting(void)
1544{
1545 int err;
1546
1547 err = __setup_sorting();
1548 if (err < 0)
1549 return err;
1550
1551 if (parent_pattern != default_parent_pattern) {
1552 err = sort_dimension__add("parent");
1553 if (err < 0)
1554 return err;
1555 }
1556
1557 reset_dimensions();
1558
1559 /*
1560 * perf diff doesn't use default hpp output fields.
1561 */
1562 if (sort__mode != SORT_MODE__DIFF)
1563 perf_hpp__init();
1564
1565 err = __setup_output_field();
1566 if (err < 0)
1567 return err;
1568
1569 /* copy sort keys to output fields */
1570 perf_hpp__setup_output_field();
1571 /* and then copy output fields to sort keys */
1572 perf_hpp__append_sort_keys();
1573
1574 return 0;
1575}
1576
1577void reset_output_field(void)
1578{
1579 sort__need_collapse = 0;
1580 sort__has_parent = 0;
1581 sort__has_sym = 0;
1582 sort__has_dso = 0;
1583
1584 reset_dimensions();
1585 perf_hpp__reset_output_field();
1198} 1586}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 43e5ff42a609..5f38d925e92f 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -25,6 +25,7 @@
25 25
26extern regex_t parent_regex; 26extern regex_t parent_regex;
27extern const char *sort_order; 27extern const char *sort_order;
28extern const char *field_order;
28extern const char default_parent_pattern[]; 29extern const char default_parent_pattern[];
29extern const char *parent_pattern; 30extern const char *parent_pattern;
30extern const char default_sort_order[]; 31extern const char default_sort_order[];
@@ -133,6 +134,8 @@ enum sort_mode {
133 SORT_MODE__NORMAL, 134 SORT_MODE__NORMAL,
134 SORT_MODE__BRANCH, 135 SORT_MODE__BRANCH,
135 SORT_MODE__MEMORY, 136 SORT_MODE__MEMORY,
137 SORT_MODE__TOP,
138 SORT_MODE__DIFF,
136}; 139};
137 140
138enum sort_type { 141enum sort_type {
@@ -179,6 +182,7 @@ struct sort_entry {
179 182
180 int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); 183 int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);
181 int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); 184 int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *);
185 int64_t (*se_sort)(struct hist_entry *, struct hist_entry *);
182 int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size, 186 int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size,
183 unsigned int width); 187 unsigned int width);
184 u8 se_width_idx; 188 u8 se_width_idx;
@@ -189,6 +193,8 @@ extern struct sort_entry sort_thread;
189extern struct list_head hist_entry__sort_list; 193extern struct list_head hist_entry__sort_list;
190 194
191int setup_sorting(void); 195int setup_sorting(void);
196int setup_output_field(void);
197void reset_output_field(void);
192extern int sort_dimension__add(const char *); 198extern int sort_dimension__add(const char *);
193void sort__setup_elide(FILE *fp); 199void sort__setup_elide(FILE *fp);
194 200