aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2014-04-17 04:06:47 -0400
committerIngo Molnar <mingo@kernel.org>2014-04-17 04:06:47 -0400
commitb3d5fc3c297fd379956689dc4d7d6af077ac35c2 (patch)
tree2c0882061596156ef39e17360c442aef4a64c3b6
parentfbdd17ec5ce2e5e4027356fcfde769b88d15702f (diff)
parenta83edb2dfc5989fbadc594109c933bae528a2809 (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: User visible changes: * Add --percentage option to control absolute/relative percentage output (Namhyung Kim) Plumbing changes: * Add --list-cmds to 'kmem', 'mem', 'lock' and 'sched', for use by completion scripts (Ramkumar Ramachandra) Signed-off-by: Jiri Olsa <jolsa@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--tools/perf/Documentation/perf-diff.txt21
-rw-r--r--tools/perf/Documentation/perf-report.txt24
-rw-r--r--tools/perf/Documentation/perf-top.txt18
-rw-r--r--tools/perf/builtin-diff.c32
-rw-r--r--tools/perf/builtin-kmem.c8
-rw-r--r--tools/perf/builtin-lock.c10
-rw-r--r--tools/perf/builtin-mem.c15
-rw-r--r--tools/perf/builtin-report.c22
-rw-r--r--tools/perf/builtin-sched.c10
-rw-r--r--tools/perf/builtin-top.c8
-rw-r--r--tools/perf/perf-completion.sh4
-rw-r--r--tools/perf/ui/browsers/hists.c39
-rw-r--r--tools/perf/ui/gtk/hists.c11
-rw-r--r--tools/perf/ui/hist.c8
-rw-r--r--tools/perf/util/config.c4
-rw-r--r--tools/perf/util/hist.c59
-rw-r--r--tools/perf/util/hist.h10
-rw-r--r--tools/perf/util/symbol.h3
18 files changed, 230 insertions, 76 deletions
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt
index fdfceee0ffd0..fbfa1192923c 100644
--- a/tools/perf/Documentation/perf-diff.txt
+++ b/tools/perf/Documentation/perf-diff.txt
@@ -33,17 +33,20 @@ OPTIONS
33-d:: 33-d::
34--dsos=:: 34--dsos=::
35 Only consider symbols in these dsos. CSV that understands 35 Only consider symbols in these dsos. CSV that understands
36 file://filename entries. 36 file://filename entries. This option will affect the percentage
37 of the Baseline/Delta column. See --percentage for more info.
37 38
38-C:: 39-C::
39--comms=:: 40--comms=::
40 Only consider symbols in these comms. CSV that understands 41 Only consider symbols in these comms. CSV that understands
41 file://filename entries. 42 file://filename entries. This option will affect the percentage
43 of the Baseline/Delta column. See --percentage for more info.
42 44
43-S:: 45-S::
44--symbols=:: 46--symbols=::
45 Only consider these symbols. CSV that understands 47 Only consider these symbols. CSV that understands
46 file://filename entries. 48 file://filename entries. This option will affect the percentage
49 of the Baseline/Delta column. See --percentage for more info.
47 50
48-s:: 51-s::
49--sort=:: 52--sort=::
@@ -89,6 +92,14 @@ OPTIONS
89--order:: 92--order::
90 Specify compute sorting column number. 93 Specify compute sorting column number.
91 94
95--percentage::
96 Determine how to display the overhead percentage of filtered entries.
97 Filters can be applied by --comms, --dsos and/or --symbols options.
98
99 "relative" means it's relative to filtered entries only so that the
100 sum of shown entries will be always 100%. "absolute" means it retains
101 the original value before and after the filter is applied.
102
92COMPARISON 103COMPARISON
93---------- 104----------
94The comparison is governed by the baseline file. The baseline perf.data 105The comparison is governed by the baseline file. The baseline perf.data
@@ -157,6 +168,10 @@ with:
157 - period_percent being the % of the hist entry period value within 168 - period_percent being the % of the hist entry period value within
158 single data file 169 single data file
159 170
171 - with filtering by -C, -d and/or -S, period_percent might be changed
172 relative to how entries are filtered. Use --percentage=absolute to
173 prevent such fluctuation.
174
160ratio 175ratio
161~~~~~ 176~~~~~
162If specified the 'Ratio' column is displayed with value 'r' computed as: 177If specified the 'Ratio' column is displayed with value 'r' computed as:
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 8eab8a4bdeb8..09af66298564 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -25,10 +25,6 @@ OPTIONS
25--verbose:: 25--verbose::
26 Be more verbose. (show symbol address, etc) 26 Be more verbose. (show symbol address, etc)
27 27
28-d::
29--dsos=::
30 Only consider symbols in these dsos. CSV that understands
31 file://filename entries.
32-n:: 28-n::
33--show-nr-samples:: 29--show-nr-samples::
34 Show the number of samples for each symbol 30 Show the number of samples for each symbol
@@ -42,11 +38,18 @@ OPTIONS
42-c:: 38-c::
43--comms=:: 39--comms=::
44 Only consider symbols in these comms. CSV that understands 40 Only consider symbols in these comms. CSV that understands
45 file://filename entries. 41 file://filename entries. This option will affect the percentage of
42 the overhead column. See --percentage for more info.
43-d::
44--dsos=::
45 Only consider symbols in these dsos. CSV that understands
46 file://filename entries. This option will affect the percentage of
47 the overhead column. See --percentage for more info.
46-S:: 48-S::
47--symbols=:: 49--symbols=::
48 Only consider these symbols. CSV that understands 50 Only consider these symbols. CSV that understands
49 file://filename entries. 51 file://filename entries. This option will affect the percentage of
52 the overhead column. See --percentage for more info.
50 53
51--symbol-filter=:: 54--symbol-filter=::
52 Only show symbols that match (partially) with this filter. 55 Only show symbols that match (partially) with this filter.
@@ -237,6 +240,15 @@ OPTIONS
237 Do not show entries which have an overhead under that percent. 240 Do not show entries which have an overhead under that percent.
238 (Default: 0). 241 (Default: 0).
239 242
243--percentage::
244 Determine how to display the overhead percentage of filtered entries.
245 Filters can be applied by --comms, --dsos and/or --symbols options and
246 Zoom operations on the TUI (thread, dso, etc).
247
248 "relative" means it's relative to filtered entries only so that the
249 sum of shown entries will be always 100%. "absolute" means it retains
250 the original value before and after the filter is applied.
251
240--header:: 252--header::
241 Show header information in the perf.data file. This includes 253 Show header information in the perf.data file. This includes
242 various information like hostname, OS and perf version, cpu/mem 254 various information like hostname, OS and perf version, cpu/mem
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index 976b00c6cdb1..64ed79c43639 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -123,13 +123,16 @@ Default is to monitor all CPUS.
123 Show a column with the sum of periods. 123 Show a column with the sum of periods.
124 124
125--dsos:: 125--dsos::
126 Only consider symbols in these dsos. 126 Only consider symbols in these dsos. This option will affect the
127 percentage of the overhead column. See --percentage for more info.
127 128
128--comms:: 129--comms::
129 Only consider symbols in these comms. 130 Only consider symbols in these comms. This option will affect the
131 percentage of the overhead column. See --percentage for more info.
130 132
131--symbols:: 133--symbols::
132 Only consider these symbols. 134 Only consider these symbols. This option will affect the
135 percentage of the overhead column. See --percentage for more info.
133 136
134-M:: 137-M::
135--disassembler-style=:: Set disassembler style for objdump. 138--disassembler-style=:: Set disassembler style for objdump.
@@ -165,6 +168,15 @@ Default is to monitor all CPUS.
165 Do not show entries which have an overhead under that percent. 168 Do not show entries which have an overhead under that percent.
166 (Default: 0). 169 (Default: 0).
167 170
171--percentage::
172 Determine how to display the overhead percentage of filtered entries.
173 Filters can be applied by --comms, --dsos and/or --symbols options and
174 Zoom operations on the TUI (thread, dso, etc).
175
176 "relative" means it's relative to filtered entries only so that the
177 sum of shown entries will be always 100%. "absolute" means it retains
178 the original value before and after the filter is applied.
179
168INTERACTIVE PROMPTING KEYS 180INTERACTIVE PROMPTING KEYS
169-------------------------- 181--------------------------
170 182
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 204fffe22532..6ef80f22c1e2 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -220,7 +220,8 @@ static int setup_compute(const struct option *opt, const char *str,
220 220
221static double period_percent(struct hist_entry *he, u64 period) 221static double period_percent(struct hist_entry *he, u64 period)
222{ 222{
223 u64 total = he->hists->stats.total_period; 223 u64 total = hists__total_period(he->hists);
224
224 return (period * 100.0) / total; 225 return (period * 100.0) / total;
225} 226}
226 227
@@ -259,11 +260,18 @@ static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
259static int formula_delta(struct hist_entry *he, struct hist_entry *pair, 260static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
260 char *buf, size_t size) 261 char *buf, size_t size)
261{ 262{
263 u64 he_total = he->hists->stats.total_period;
264 u64 pair_total = pair->hists->stats.total_period;
265
266 if (symbol_conf.filter_relative) {
267 he_total = he->hists->stats.total_non_filtered_period;
268 pair_total = pair->hists->stats.total_non_filtered_period;
269 }
262 return scnprintf(buf, size, 270 return scnprintf(buf, size,
263 "(%" PRIu64 " * 100 / %" PRIu64 ") - " 271 "(%" PRIu64 " * 100 / %" PRIu64 ") - "
264 "(%" PRIu64 " * 100 / %" PRIu64 ")", 272 "(%" PRIu64 " * 100 / %" PRIu64 ")",
265 pair->stat.period, pair->hists->stats.total_period, 273 pair->stat.period, pair_total,
266 he->stat.period, he->hists->stats.total_period); 274 he->stat.period, he_total);
267} 275}
268 276
269static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, 277static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
@@ -327,15 +335,16 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
327 return -1; 335 return -1;
328 } 336 }
329 337
330 if (al.filtered)
331 return 0;
332
333 if (hists__add_entry(&evsel->hists, &al, sample->period, 338 if (hists__add_entry(&evsel->hists, &al, sample->period,
334 sample->weight, sample->transaction)) { 339 sample->weight, sample->transaction)) {
335 pr_warning("problem incrementing symbol period, skipping event\n"); 340 pr_warning("problem incrementing symbol period, skipping event\n");
336 return -1; 341 return -1;
337 } 342 }
338 343
344 if (al.filtered == 0) {
345 evsel->hists.stats.total_non_filtered_period += sample->period;
346 evsel->hists.nr_non_filtered_entries++;
347 }
339 evsel->hists.stats.total_period += sample->period; 348 evsel->hists.stats.total_period += sample->period;
340 return 0; 349 return 0;
341} 350}
@@ -565,7 +574,9 @@ static void hists__compute_resort(struct hists *hists)
565 next = rb_first(root); 574 next = rb_first(root);
566 575
567 hists->nr_entries = 0; 576 hists->nr_entries = 0;
577 hists->nr_non_filtered_entries = 0;
568 hists->stats.total_period = 0; 578 hists->stats.total_period = 0;
579 hists->stats.total_non_filtered_period = 0;
569 hists__reset_col_len(hists); 580 hists__reset_col_len(hists);
570 581
571 while (next != NULL) { 582 while (next != NULL) {
@@ -732,13 +743,16 @@ static const struct option options[] = {
732 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", 743 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
733 "Look for files with symbols relative to this directory"), 744 "Look for files with symbols relative to this directory"),
734 OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."), 745 OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
746 OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
747 "How to display percentage of filtered entries", parse_filter_percentage),
735 OPT_END() 748 OPT_END()
736}; 749};
737 750
738static double baseline_percent(struct hist_entry *he) 751static double baseline_percent(struct hist_entry *he)
739{ 752{
740 struct hists *hists = he->hists; 753 u64 total = hists__total_period(he->hists);
741 return 100.0 * he->stat.period / hists->stats.total_period; 754
755 return 100.0 * he->stat.period / total;
742} 756}
743 757
744static int hpp__color_baseline(struct perf_hpp_fmt *fmt, 758static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
@@ -1120,6 +1134,8 @@ static int data_init(int argc, const char **argv)
1120 1134
1121int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) 1135int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
1122{ 1136{
1137 perf_config(perf_default_config, NULL);
1138
1123 sort_order = diff__default_sort_order; 1139 sort_order = diff__default_sort_order;
1124 argc = parse_options(argc, argv, options, diff_usage, 0); 1140 argc = parse_options(argc, argv, options, diff_usage, 0);
1125 1141
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 929462aa4943..bd91de07d2a9 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -756,11 +756,13 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
756 OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), 756 OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
757 OPT_END() 757 OPT_END()
758 }; 758 };
759 const char * const kmem_usage[] = { 759 const char *const kmem_subcommands[] = { "record", "stat", NULL };
760 "perf kmem [<options>] {record|stat}", 760 const char *kmem_usage[] = {
761 NULL,
761 NULL 762 NULL
762 }; 763 };
763 argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); 764 argc = parse_options_subcommand(argc, argv, kmem_options,
765 kmem_subcommands, kmem_usage, 0);
764 766
765 if (!argc) 767 if (!argc)
766 usage_with_options(kmem_usage, kmem_options); 768 usage_with_options(kmem_usage, kmem_options);
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index c852c7a85d32..6148afc995c6 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -961,8 +961,10 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)
961 "perf lock info [<options>]", 961 "perf lock info [<options>]",
962 NULL 962 NULL
963 }; 963 };
964 const char * const lock_usage[] = { 964 const char *const lock_subcommands[] = { "record", "report", "script",
965 "perf lock [<options>] {record|report|script|info}", 965 "info", NULL };
966 const char *lock_usage[] = {
967 NULL,
966 NULL 968 NULL
967 }; 969 };
968 const char * const report_usage[] = { 970 const char * const report_usage[] = {
@@ -976,8 +978,8 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)
976 for (i = 0; i < LOCKHASH_SIZE; i++) 978 for (i = 0; i < LOCKHASH_SIZE; i++)
977 INIT_LIST_HEAD(lockhash_table + i); 979 INIT_LIST_HEAD(lockhash_table + i);
978 980
979 argc = parse_options(argc, argv, lock_options, lock_usage, 981 argc = parse_options_subcommand(argc, argv, lock_options, lock_subcommands,
980 PARSE_OPT_STOP_AT_NON_OPTION); 982 lock_usage, PARSE_OPT_STOP_AT_NON_OPTION);
981 if (!argc) 983 if (!argc)
982 usage_with_options(lock_usage, lock_options); 984 usage_with_options(lock_usage, lock_options);
983 985
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c
index 2e3ade69a58e..4a1a6c94a5eb 100644
--- a/tools/perf/builtin-mem.c
+++ b/tools/perf/builtin-mem.c
@@ -21,11 +21,6 @@ struct perf_mem {
21 DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); 21 DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
22}; 22};
23 23
24static const char * const mem_usage[] = {
25 "perf mem [<options>] {record <command> |report}",
26 NULL
27};
28
29static int __cmd_record(int argc, const char **argv) 24static int __cmd_record(int argc, const char **argv)
30{ 25{
31 int rec_argc, i = 0, j; 26 int rec_argc, i = 0, j;
@@ -220,9 +215,15 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
220 " between columns '.' is reserved."), 215 " between columns '.' is reserved."),
221 OPT_END() 216 OPT_END()
222 }; 217 };
218 const char *const mem_subcommands[] = { "record", "report", NULL };
219 const char *mem_usage[] = {
220 NULL,
221 NULL
222 };
223
223 224
224 argc = parse_options(argc, argv, mem_options, mem_usage, 225 argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands,
225 PARSE_OPT_STOP_AT_NON_OPTION); 226 mem_usage, PARSE_OPT_STOP_AT_NON_OPTION);
226 227
227 if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation)) 228 if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation))
228 usage_with_options(mem_usage, mem_options); 229 usage_with_options(mem_usage, mem_options);
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index c8f21137dfd8..af8cb7a2c9b6 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -123,6 +123,8 @@ static int report__add_mem_hist_entry(struct report *rep, struct addr_location *
123 123
124 evsel->hists.stats.total_period += cost; 124 evsel->hists.stats.total_period += cost;
125 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); 125 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
126 if (!he->filtered)
127 evsel->hists.stats.nr_non_filtered_samples++;
126 err = hist_entry__append_callchain(he, sample); 128 err = hist_entry__append_callchain(he, sample);
127out: 129out:
128 return err; 130 return err;
@@ -176,6 +178,8 @@ static int report__add_branch_hist_entry(struct report *rep, struct addr_locatio
176 178
177 evsel->hists.stats.total_period += 1; 179 evsel->hists.stats.total_period += 1;
178 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); 180 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
181 if (!he->filtered)
182 evsel->hists.stats.nr_non_filtered_samples++;
179 } else 183 } else
180 goto out; 184 goto out;
181 } 185 }
@@ -209,6 +213,8 @@ static int report__add_hist_entry(struct report *rep, struct perf_evsel *evsel,
209 err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); 213 err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
210 214
211 evsel->hists.stats.total_period += sample->period; 215 evsel->hists.stats.total_period += sample->period;
216 if (!he->filtered)
217 evsel->hists.stats.nr_non_filtered_samples++;
212 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); 218 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
213out: 219out:
214 return err; 220 return err;
@@ -337,6 +343,11 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
337 char buf[512]; 343 char buf[512];
338 size_t size = sizeof(buf); 344 size_t size = sizeof(buf);
339 345
346 if (symbol_conf.filter_relative) {
347 nr_samples = hists->stats.nr_non_filtered_samples;
348 nr_events = hists->stats.total_non_filtered_period;
349 }
350
340 if (perf_evsel__is_group_event(evsel)) { 351 if (perf_evsel__is_group_event(evsel)) {
341 struct perf_evsel *pos; 352 struct perf_evsel *pos;
342 353
@@ -344,8 +355,13 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
344 evname = buf; 355 evname = buf;
345 356
346 for_each_group_member(pos, evsel) { 357 for_each_group_member(pos, evsel) {
347 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; 358 if (symbol_conf.filter_relative) {
348 nr_events += pos->hists.stats.total_period; 359 nr_samples += pos->hists.stats.nr_non_filtered_samples;
360 nr_events += pos->hists.stats.total_non_filtered_period;
361 } else {
362 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
363 nr_events += pos->hists.stats.total_period;
364 }
349 } 365 }
350 } 366 }
351 367
@@ -823,6 +839,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
823 OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), 839 OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
824 OPT_CALLBACK(0, "percent-limit", &report, "percent", 840 OPT_CALLBACK(0, "percent-limit", &report, "percent",
825 "Don't show entries under that percent", parse_percent_limit), 841 "Don't show entries under that percent", parse_percent_limit),
842 OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
843 "how to display percentage of filtered entries", parse_filter_percentage),
826 OPT_END() 844 OPT_END()
827 }; 845 };
828 struct perf_data_file file = { 846 struct perf_data_file file = {
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 9ac0a495c954..d3fb0ed7240a 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -1713,8 +1713,10 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
1713 "perf sched replay [<options>]", 1713 "perf sched replay [<options>]",
1714 NULL 1714 NULL
1715 }; 1715 };
1716 const char * const sched_usage[] = { 1716 const char *const sched_subcommands[] = { "record", "latency", "map",
1717 "perf sched [<options>] {record|latency|map|replay|script}", 1717 "replay", "script", NULL };
1718 const char *sched_usage[] = {
1719 NULL,
1718 NULL 1720 NULL
1719 }; 1721 };
1720 struct trace_sched_handler lat_ops = { 1722 struct trace_sched_handler lat_ops = {
@@ -1736,8 +1738,8 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
1736 for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++) 1738 for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++)
1737 sched.curr_pid[i] = -1; 1739 sched.curr_pid[i] = -1;
1738 1740
1739 argc = parse_options(argc, argv, sched_options, sched_usage, 1741 argc = parse_options_subcommand(argc, argv, sched_options, sched_subcommands,
1740 PARSE_OPT_STOP_AT_NON_OPTION); 1742 sched_usage, PARSE_OPT_STOP_AT_NON_OPTION);
1741 if (!argc) 1743 if (!argc)
1742 usage_with_options(sched_usage, sched_options); 1744 usage_with_options(sched_usage, sched_options);
1743 1745
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 65aaa5bbf7ec..37d30460bada 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -253,6 +253,9 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel,
253 return NULL; 253 return NULL;
254 254
255 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); 255 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
256 if (!he->filtered)
257 evsel->hists.stats.nr_non_filtered_samples++;
258
256 return he; 259 return he;
257} 260}
258 261
@@ -694,8 +697,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
694 if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) 697 if (event->header.misc & PERF_RECORD_MISC_EXACT_IP)
695 top->exact_samples++; 698 top->exact_samples++;
696 699
697 if (perf_event__preprocess_sample(event, machine, &al, sample) < 0 || 700 if (perf_event__preprocess_sample(event, machine, &al, sample) < 0)
698 al.filtered)
699 return; 701 return;
700 702
701 if (!top->kptr_restrict_warned && 703 if (!top->kptr_restrict_warned &&
@@ -1116,6 +1118,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1116 OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"), 1118 OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"),
1117 OPT_CALLBACK(0, "percent-limit", &top, "percent", 1119 OPT_CALLBACK(0, "percent-limit", &top, "percent",
1118 "Don't show entries under that percent", parse_percent_limit), 1120 "Don't show entries under that percent", parse_percent_limit),
1121 OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
1122 "How to display percentage of filtered entries", parse_filter_percentage),
1119 OPT_END() 1123 OPT_END()
1120 }; 1124 };
1121 const char * const top_usage[] = { 1125 const char * const top_usage[] = {
diff --git a/tools/perf/perf-completion.sh b/tools/perf/perf-completion.sh
index ae3a57694b6b..33569847fdcc 100644
--- a/tools/perf/perf-completion.sh
+++ b/tools/perf/perf-completion.sh
@@ -121,8 +121,8 @@ __perf_main ()
121 elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then 121 elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then
122 evts=$($cmd list --raw-dump) 122 evts=$($cmd list --raw-dump)
123 __perfcomp_colon "$evts" "$cur" 123 __perfcomp_colon "$evts" "$cur"
124 # List subcommands for 'perf kvm' 124 # List subcommands for perf commands
125 elif [[ $prev == "kvm" ]]; then 125 elif [[ $prev == @(kvm|kmem|mem|lock|sched) ]]; then
126 subcmds=$($cmd $prev --list-cmds) 126 subcmds=$($cmd $prev --list-cmds)
127 __perfcomp_colon "$subcmds" "$cur" 127 __perfcomp_colon "$subcmds" "$cur"
128 # List long option names 128 # List long option names
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 7ec871af3f6f..4d416984c59d 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -769,12 +769,15 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
769 769
770 for (nd = browser->top; nd; nd = rb_next(nd)) { 770 for (nd = browser->top; nd; nd = rb_next(nd)) {
771 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 771 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
772 float percent = h->stat.period * 100.0 / 772 u64 total = hists__total_period(h->hists);
773 hb->hists->stats.total_period; 773 float percent = 0.0;
774 774
775 if (h->filtered) 775 if (h->filtered)
776 continue; 776 continue;
777 777
778 if (total)
779 percent = h->stat.period * 100.0 / total;
780
778 if (percent < hb->min_pcnt) 781 if (percent < hb->min_pcnt)
779 continue; 782 continue;
780 783
@@ -792,8 +795,11 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd,
792{ 795{
793 while (nd != NULL) { 796 while (nd != NULL) {
794 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 797 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
795 float percent = h->stat.period * 100.0 / 798 u64 total = hists__total_period(hists);
796 hists->stats.total_period; 799 float percent = 0.0;
800
801 if (total)
802 percent = h->stat.period * 100.0 / total;
797 803
798 if (percent < min_pcnt) 804 if (percent < min_pcnt)
799 return NULL; 805 return NULL;
@@ -813,8 +819,11 @@ static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
813{ 819{
814 while (nd != NULL) { 820 while (nd != NULL) {
815 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 821 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
816 float percent = h->stat.period * 100.0 / 822 u64 total = hists__total_period(hists);
817 hists->stats.total_period; 823 float percent = 0.0;
824
825 if (total)
826 percent = h->stat.period * 100.0 / total;
818 827
819 if (!h->filtered && percent >= min_pcnt) 828 if (!h->filtered && percent >= min_pcnt)
820 return nd; 829 return nd;
@@ -1189,6 +1198,11 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1189 char buf[512]; 1198 char buf[512];
1190 size_t buflen = sizeof(buf); 1199 size_t buflen = sizeof(buf);
1191 1200
1201 if (symbol_conf.filter_relative) {
1202 nr_samples = hists->stats.nr_non_filtered_samples;
1203 nr_events = hists->stats.total_non_filtered_period;
1204 }
1205
1192 if (perf_evsel__is_group_event(evsel)) { 1206 if (perf_evsel__is_group_event(evsel)) {
1193 struct perf_evsel *pos; 1207 struct perf_evsel *pos;
1194 1208
@@ -1196,8 +1210,13 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1196 ev_name = buf; 1210 ev_name = buf;
1197 1211
1198 for_each_group_member(pos, evsel) { 1212 for_each_group_member(pos, evsel) {
1199 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; 1213 if (symbol_conf.filter_relative) {
1200 nr_events += pos->hists.stats.total_period; 1214 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1215 nr_events += pos->hists.stats.total_non_filtered_period;
1216 } else {
1217 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1218 nr_events += pos->hists.stats.total_period;
1219 }
1201 } 1220 }
1202 } 1221 }
1203 1222
@@ -1370,6 +1389,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1370 "C Collapse all callchains\n" \ 1389 "C Collapse all callchains\n" \
1371 "d Zoom into current DSO\n" \ 1390 "d Zoom into current DSO\n" \
1372 "E Expand all callchains\n" \ 1391 "E Expand all callchains\n" \
1392 "F Toggle percentage of filtered entries\n" \
1373 1393
1374 /* help messages are sorted by lexical order of the hotkey */ 1394 /* help messages are sorted by lexical order of the hotkey */
1375 const char report_help[] = HIST_BROWSER_HELP_COMMON 1395 const char report_help[] = HIST_BROWSER_HELP_COMMON
@@ -1475,6 +1495,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1475 if (env->arch) 1495 if (env->arch)
1476 tui__header_window(env); 1496 tui__header_window(env);
1477 continue; 1497 continue;
1498 case 'F':
1499 symbol_conf.filter_relative ^= 1;
1500 continue;
1478 case K_F1: 1501 case K_F1:
1479 case 'h': 1502 case 'h':
1480 case '?': 1503 case '?':
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index e395ef9b0ae0..91f10f3f6dd1 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -228,12 +228,15 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
228 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 228 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
229 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 229 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
230 GtkTreeIter iter; 230 GtkTreeIter iter;
231 float percent = h->stat.period * 100.0 / 231 u64 total = hists__total_period(h->hists);
232 hists->stats.total_period; 232 float percent = 0.0;
233 233
234 if (h->filtered) 234 if (h->filtered)
235 continue; 235 continue;
236 236
237 if (total)
238 percent = h->stat.period * 100.0 / total;
239
237 if (percent < min_pcnt) 240 if (percent < min_pcnt)
238 continue; 241 continue;
239 242
@@ -261,12 +264,8 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
261 } 264 }
262 265
263 if (symbol_conf.use_callchain && sort__has_sym) { 266 if (symbol_conf.use_callchain && sort__has_sym) {
264 u64 total;
265
266 if (callchain_param.mode == CHAIN_GRAPH_REL) 267 if (callchain_param.mode == CHAIN_GRAPH_REL)
267 total = h->stat.period; 268 total = h->stat.period;
268 else
269 total = hists->stats.total_period;
270 269
271 perf_gtk__add_callchain(&h->sorted_chain, store, &iter, 270 perf_gtk__add_callchain(&h->sorted_chain, store, &iter,
272 sym_col, total); 271 sym_col, total);
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index 0f403b83e9d1..0912805c08f4 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -32,10 +32,10 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
32 32
33 if (fmt_percent) { 33 if (fmt_percent) {
34 double percent = 0.0; 34 double percent = 0.0;
35 u64 total = hists__total_period(hists);
35 36
36 if (hists->stats.total_period) 37 if (total)
37 percent = 100.0 * get_field(he) / 38 percent = 100.0 * get_field(he) / total;
38 hists->stats.total_period;
39 39
40 ret += hpp__call_print_fn(hpp, print_fn, fmt, percent); 40 ret += hpp__call_print_fn(hpp, print_fn, fmt, percent);
41 } else 41 } else
@@ -50,7 +50,7 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
50 50
51 list_for_each_entry(pair, &he->pairs.head, pairs.node) { 51 list_for_each_entry(pair, &he->pairs.head, pairs.node) {
52 u64 period = get_field(pair); 52 u64 period = get_field(pair);
53 u64 total = pair->hists->stats.total_period; 53 u64 total = hists__total_period(pair->hists);
54 54
55 if (!total) 55 if (!total)
56 continue; 56 continue;
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 3e0fdd369ccb..24519e14ac56 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -11,6 +11,7 @@
11#include "util.h" 11#include "util.h"
12#include "cache.h" 12#include "cache.h"
13#include "exec_cmd.h" 13#include "exec_cmd.h"
14#include "util/hist.h" /* perf_hist_config */
14 15
15#define MAXNAME (256) 16#define MAXNAME (256)
16 17
@@ -355,6 +356,9 @@ int perf_default_config(const char *var, const char *value,
355 if (!prefixcmp(var, "core.")) 356 if (!prefixcmp(var, "core."))
356 return perf_default_core_config(var, value); 357 return perf_default_core_config(var, value);
357 358
359 if (!prefixcmp(var, "hist."))
360 return perf_hist_config(var, value);
361
358 /* Add other config variables here. */ 362 /* Add other config variables here. */
359 return 0; 363 return 0;
360} 364}
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index f38590d7561b..5a892477aa50 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -321,9 +321,11 @@ void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
321{ 321{
322 if (!h->filtered) { 322 if (!h->filtered) {
323 hists__calc_col_len(hists, h); 323 hists__calc_col_len(hists, h);
324 ++hists->nr_entries; 324 hists->nr_non_filtered_entries++;
325 hists->stats.total_period += h->stat.period; 325 hists->stats.total_non_filtered_period += h->stat.period;
326 } 326 }
327 hists->nr_entries++;
328 hists->stats.total_period += h->stat.period;
327} 329}
328 330
329static u8 symbol__parent_filter(const struct symbol *parent) 331static u8 symbol__parent_filter(const struct symbol *parent)
@@ -674,8 +676,9 @@ void hists__output_resort(struct hists *hists)
674 next = rb_first(root); 676 next = rb_first(root);
675 hists->entries = RB_ROOT; 677 hists->entries = RB_ROOT;
676 678
677 hists->nr_entries = 0; 679 hists->nr_non_filtered_entries = 0;
678 hists->stats.total_period = 0; 680 hists->stats.total_period = 0;
681 hists->stats.total_non_filtered_period = 0;
679 hists__reset_col_len(hists); 682 hists__reset_col_len(hists);
680 683
681 while (next) { 684 while (next) {
@@ -694,12 +697,12 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h
694 if (h->filtered) 697 if (h->filtered)
695 return; 698 return;
696 699
697 ++hists->nr_entries; 700 ++hists->nr_non_filtered_entries;
698 if (h->ms.unfolded) 701 if (h->ms.unfolded)
699 hists->nr_entries += h->nr_rows; 702 hists->nr_non_filtered_entries += h->nr_rows;
700 h->row_offset = 0; 703 h->row_offset = 0;
701 hists->stats.total_period += h->stat.period; 704 hists->stats.total_non_filtered_period += h->stat.period;
702 hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events; 705 hists->stats.nr_non_filtered_samples += h->stat.nr_events;
703 706
704 hists__calc_col_len(hists, h); 707 hists__calc_col_len(hists, h);
705} 708}
@@ -721,8 +724,9 @@ void hists__filter_by_dso(struct hists *hists)
721{ 724{
722 struct rb_node *nd; 725 struct rb_node *nd;
723 726
724 hists->nr_entries = hists->stats.total_period = 0; 727 hists->nr_non_filtered_entries = 0;
725 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 728 hists->stats.total_non_filtered_period = 0;
729 hists->stats.nr_non_filtered_samples = 0;
726 hists__reset_col_len(hists); 730 hists__reset_col_len(hists);
727 731
728 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 732 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
@@ -754,8 +758,9 @@ void hists__filter_by_thread(struct hists *hists)
754{ 758{
755 struct rb_node *nd; 759 struct rb_node *nd;
756 760
757 hists->nr_entries = hists->stats.total_period = 0; 761 hists->nr_non_filtered_entries = 0;
758 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 762 hists->stats.total_non_filtered_period = 0;
763 hists->stats.nr_non_filtered_samples = 0;
759 hists__reset_col_len(hists); 764 hists__reset_col_len(hists);
760 765
761 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 766 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
@@ -785,8 +790,9 @@ void hists__filter_by_symbol(struct hists *hists)
785{ 790{
786 struct rb_node *nd; 791 struct rb_node *nd;
787 792
788 hists->nr_entries = hists->stats.total_period = 0; 793 hists->nr_non_filtered_entries = 0;
789 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 794 hists->stats.total_non_filtered_period = 0;
795 hists->stats.nr_non_filtered_samples = 0;
790 hists__reset_col_len(hists); 796 hists__reset_col_len(hists);
791 797
792 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 798 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
@@ -931,3 +937,30 @@ int hists__link(struct hists *leader, struct hists *other)
931 937
932 return 0; 938 return 0;
933} 939}
940
941u64 hists__total_period(struct hists *hists)
942{
943 return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period :
944 hists->stats.total_period;
945}
946
947int parse_filter_percentage(const struct option *opt __maybe_unused,
948 const char *arg, int unset __maybe_unused)
949{
950 if (!strcmp(arg, "relative"))
951 symbol_conf.filter_relative = true;
952 else if (!strcmp(arg, "absolute"))
953 symbol_conf.filter_relative = false;
954 else
955 return -1;
956
957 return 0;
958}
959
960int perf_hist_config(const char *var, const char *value)
961{
962 if (!strcmp(var, "hist.percentage"))
963 return parse_filter_percentage(NULL, value, 0);
964
965 return 0;
966}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 1f1f513dfe7f..5a0343eb22e2 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -37,9 +37,11 @@ enum hist_filter {
37 */ 37 */
38struct events_stats { 38struct events_stats {
39 u64 total_period; 39 u64 total_period;
40 u64 total_non_filtered_period;
40 u64 total_lost; 41 u64 total_lost;
41 u64 total_invalid_chains; 42 u64 total_invalid_chains;
42 u32 nr_events[PERF_RECORD_HEADER_MAX]; 43 u32 nr_events[PERF_RECORD_HEADER_MAX];
44 u32 nr_non_filtered_samples;
43 u32 nr_lost_warned; 45 u32 nr_lost_warned;
44 u32 nr_unknown_events; 46 u32 nr_unknown_events;
45 u32 nr_invalid_chains; 47 u32 nr_invalid_chains;
@@ -83,6 +85,7 @@ struct hists {
83 struct rb_root entries; 85 struct rb_root entries;
84 struct rb_root entries_collapsed; 86 struct rb_root entries_collapsed;
85 u64 nr_entries; 87 u64 nr_entries;
88 u64 nr_non_filtered_entries;
86 const struct thread *thread_filter; 89 const struct thread *thread_filter;
87 const struct dso *dso_filter; 90 const struct dso *dso_filter;
88 const char *uid_filter_str; 91 const char *uid_filter_str;
@@ -112,6 +115,7 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog);
112void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); 115void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel);
113void hists__output_recalc_col_len(struct hists *hists, int max_rows); 116void hists__output_recalc_col_len(struct hists *hists, int max_rows);
114 117
118u64 hists__total_period(struct hists *hists);
115void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h); 119void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h);
116void hists__inc_nr_events(struct hists *hists, u32 type); 120void hists__inc_nr_events(struct hists *hists, u32 type);
117void events_stats__inc(struct events_stats *stats, u32 type); 121void events_stats__inc(struct events_stats *stats, u32 type);
@@ -250,4 +254,10 @@ static inline int script_browse(const char *script_opt __maybe_unused)
250#endif 254#endif
251 255
252unsigned int hists__sort_list_width(struct hists *hists); 256unsigned int hists__sort_list_width(struct hists *hists);
257
258struct option;
259int parse_filter_percentage(const struct option *opt __maybe_unused,
260 const char *arg, int unset __maybe_unused);
261int perf_hist_config(const char *var, const char *value);
262
253#endif /* __PERF_HIST_H */ 263#endif /* __PERF_HIST_H */
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 501e4e722e8e..ae94e006a52d 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -115,7 +115,8 @@ struct symbol_conf {
115 annotate_asm_raw, 115 annotate_asm_raw,
116 annotate_src, 116 annotate_src,
117 event_group, 117 event_group,
118 demangle; 118 demangle,
119 filter_relative;
119 const char *vmlinux_name, 120 const char *vmlinux_name,
120 *kallsyms_name, 121 *kallsyms_name,
121 *source_prefix, 122 *source_prefix,