소스 검색

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>
Ingo Molnar 11 년 전
부모
커밋
b3d5fc3c29

+ 18 - 3
tools/perf/Documentation/perf-diff.txt

@@ -33,17 +33,20 @@ OPTIONS
 -d::
 -d::
 --dsos=::
 --dsos=::
 	Only consider symbols in these dsos. CSV that understands
 	Only consider symbols in these dsos. CSV that understands
-	file://filename entries.
+	file://filename entries.  This option will affect the percentage
+	of the Baseline/Delta column.  See --percentage for more info.
 
 
 -C::
 -C::
 --comms=::
 --comms=::
 	Only consider symbols in these comms. CSV that understands
 	Only consider symbols in these comms. CSV that understands
-	file://filename entries.
+	file://filename entries.  This option will affect the percentage
+	of the Baseline/Delta column.  See --percentage for more info.
 
 
 -S::
 -S::
 --symbols=::
 --symbols=::
 	Only consider these symbols. CSV that understands
 	Only consider these symbols. CSV that understands
-	file://filename entries.
+	file://filename entries.  This option will affect the percentage
+	of the Baseline/Delta column.  See --percentage for more info.
 
 
 -s::
 -s::
 --sort=::
 --sort=::
@@ -89,6 +92,14 @@ OPTIONS
 --order::
 --order::
        Specify compute sorting column number.
        Specify compute sorting column number.
 
 
+--percentage::
+	Determine how to display the overhead percentage of filtered entries.
+	Filters can be applied by --comms, --dsos and/or --symbols options.
+
+	"relative" means it's relative to filtered entries only so that the
+	sum of shown entries will be always 100%.  "absolute" means it retains
+	the original value before and after the filter is applied.
+
 COMPARISON
 COMPARISON
 ----------
 ----------
 The comparison is governed by the baseline file. The baseline perf.data
 The comparison is governed by the baseline file. The baseline perf.data
@@ -157,6 +168,10 @@ with:
   - period_percent being the % of the hist entry period value within
   - period_percent being the % of the hist entry period value within
     single data file
     single data file
 
 
+  - with filtering by -C, -d and/or -S, period_percent might be changed
+    relative to how entries are filtered.  Use --percentage=absolute to
+    prevent such fluctuation.
+
 ratio
 ratio
 ~~~~~
 ~~~~~
 If specified the 'Ratio' column is displayed with value 'r' computed as:
 If specified the 'Ratio' column is displayed with value 'r' computed as:

+ 18 - 6
tools/perf/Documentation/perf-report.txt

@@ -25,10 +25,6 @@ OPTIONS
 --verbose::
 --verbose::
         Be more verbose. (show symbol address, etc)
         Be more verbose. (show symbol address, etc)
 
 
--d::
---dsos=::
-	Only consider symbols in these dsos. CSV that understands
-	file://filename entries.
 -n::
 -n::
 --show-nr-samples::
 --show-nr-samples::
 	Show the number of samples for each symbol
 	Show the number of samples for each symbol
@@ -42,11 +38,18 @@ OPTIONS
 -c::
 -c::
 --comms=::
 --comms=::
 	Only consider symbols in these comms. CSV that understands
 	Only consider symbols in these comms. CSV that understands
-	file://filename entries.
+	file://filename entries.  This option will affect the percentage of
+	the overhead column.  See --percentage for more info.
+-d::
+--dsos=::
+	Only consider symbols in these dsos. CSV that understands
+	file://filename entries.  This option will affect the percentage of
+	the overhead column.  See --percentage for more info.
 -S::
 -S::
 --symbols=::
 --symbols=::
 	Only consider these symbols. CSV that understands
 	Only consider these symbols. CSV that understands
-	file://filename entries.
+	file://filename entries.  This option will affect the percentage of
+	the overhead column.  See --percentage for more info.
 
 
 --symbol-filter=::
 --symbol-filter=::
 	Only show symbols that match (partially) with this filter.
 	Only show symbols that match (partially) with this filter.
@@ -237,6 +240,15 @@ OPTIONS
 	Do not show entries which have an overhead under that percent.
 	Do not show entries which have an overhead under that percent.
 	(Default: 0).
 	(Default: 0).
 
 
+--percentage::
+	Determine how to display the overhead percentage of filtered entries.
+	Filters can be applied by --comms, --dsos and/or --symbols options and
+	Zoom operations on the TUI (thread, dso, etc).
+
+	"relative" means it's relative to filtered entries only so that the
+	sum of shown entries will be always 100%.  "absolute" means it retains
+	the original value before and after the filter is applied.
+
 --header::
 --header::
 	Show header information in the perf.data file.  This includes
 	Show header information in the perf.data file.  This includes
 	various information like hostname, OS and perf version, cpu/mem
 	various information like hostname, OS and perf version, cpu/mem

+ 15 - 3
tools/perf/Documentation/perf-top.txt

@@ -123,13 +123,16 @@ Default is to monitor all CPUS.
 	Show a column with the sum of periods.
 	Show a column with the sum of periods.
 
 
 --dsos::
 --dsos::
-	Only consider symbols in these dsos.
+	Only consider symbols in these dsos.  This option will affect the
+	percentage of the overhead column.  See --percentage for more info.
 
 
 --comms::
 --comms::
-	Only consider symbols in these comms.
+	Only consider symbols in these comms.  This option will affect the
+	percentage of the overhead column.  See --percentage for more info.
 
 
 --symbols::
 --symbols::
-	Only consider these symbols.
+	Only consider these symbols.  This option will affect the
+	percentage of the overhead column.  See --percentage for more info.
 
 
 -M::
 -M::
 --disassembler-style=:: Set disassembler style for objdump.
 --disassembler-style=:: Set disassembler style for objdump.
@@ -165,6 +168,15 @@ Default is to monitor all CPUS.
 	Do not show entries which have an overhead under that percent.
 	Do not show entries which have an overhead under that percent.
 	(Default: 0).
 	(Default: 0).
 
 
+--percentage::
+	Determine how to display the overhead percentage of filtered entries.
+	Filters can be applied by --comms, --dsos and/or --symbols options and
+	Zoom operations on the TUI (thread, dso, etc).
+
+	"relative" means it's relative to filtered entries only so that the
+	sum of shown entries will be always 100%. "absolute" means it retains
+	the original value before and after the filter is applied.
+
 INTERACTIVE PROMPTING KEYS
 INTERACTIVE PROMPTING KEYS
 --------------------------
 --------------------------
 
 

+ 24 - 8
tools/perf/builtin-diff.c

@@ -220,7 +220,8 @@ static int setup_compute(const struct option *opt, const char *str,
 
 
 static double period_percent(struct hist_entry *he, u64 period)
 static double period_percent(struct hist_entry *he, u64 period)
 {
 {
-	u64 total = he->hists->stats.total_period;
+	u64 total = hists__total_period(he->hists);
+
 	return (period * 100.0) / total;
 	return (period * 100.0) / total;
 }
 }
 
 
@@ -259,11 +260,18 @@ static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
 static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
 static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
 			 char *buf, size_t size)
 			 char *buf, size_t size)
 {
 {
+	u64 he_total = he->hists->stats.total_period;
+	u64 pair_total = pair->hists->stats.total_period;
+
+	if (symbol_conf.filter_relative) {
+		he_total = he->hists->stats.total_non_filtered_period;
+		pair_total = pair->hists->stats.total_non_filtered_period;
+	}
 	return scnprintf(buf, size,
 	return scnprintf(buf, size,
 			 "(%" PRIu64 " * 100 / %" PRIu64 ") - "
 			 "(%" PRIu64 " * 100 / %" PRIu64 ") - "
 			 "(%" PRIu64 " * 100 / %" PRIu64 ")",
 			 "(%" PRIu64 " * 100 / %" PRIu64 ")",
-			  pair->stat.period, pair->hists->stats.total_period,
-			  he->stat.period, he->hists->stats.total_period);
+			 pair->stat.period, pair_total,
+			 he->stat.period, he_total);
 }
 }
 
 
 static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
 static 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,
 		return -1;
 		return -1;
 	}
 	}
 
 
-	if (al.filtered)
-		return 0;
-
 	if (hists__add_entry(&evsel->hists, &al, sample->period,
 	if (hists__add_entry(&evsel->hists, &al, sample->period,
 			     sample->weight, sample->transaction)) {
 			     sample->weight, sample->transaction)) {
 		pr_warning("problem incrementing symbol period, skipping event\n");
 		pr_warning("problem incrementing symbol period, skipping event\n");
 		return -1;
 		return -1;
 	}
 	}
 
 
+	if (al.filtered == 0) {
+		evsel->hists.stats.total_non_filtered_period += sample->period;
+		evsel->hists.nr_non_filtered_entries++;
+	}
 	evsel->hists.stats.total_period += sample->period;
 	evsel->hists.stats.total_period += sample->period;
 	return 0;
 	return 0;
 }
 }
@@ -565,7 +574,9 @@ static void hists__compute_resort(struct hists *hists)
 	next = rb_first(root);
 	next = rb_first(root);
 
 
 	hists->nr_entries = 0;
 	hists->nr_entries = 0;
+	hists->nr_non_filtered_entries = 0;
 	hists->stats.total_period = 0;
 	hists->stats.total_period = 0;
+	hists->stats.total_non_filtered_period = 0;
 	hists__reset_col_len(hists);
 	hists__reset_col_len(hists);
 
 
 	while (next != NULL) {
 	while (next != NULL) {
@@ -732,13 +743,16 @@ static const struct option options[] = {
 	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
 	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
 		    "Look for files with symbols relative to this directory"),
 		    "Look for files with symbols relative to this directory"),
 	OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
 	OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
+	OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
+		     "How to display percentage of filtered entries", parse_filter_percentage),
 	OPT_END()
 	OPT_END()
 };
 };
 
 
 static double baseline_percent(struct hist_entry *he)
 static double baseline_percent(struct hist_entry *he)
 {
 {
-	struct hists *hists = he->hists;
-	return 100.0 * he->stat.period / hists->stats.total_period;
+	u64 total = hists__total_period(he->hists);
+
+	return 100.0 * he->stat.period / total;
 }
 }
 
 
 static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
 static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
@@ -1120,6 +1134,8 @@ static int data_init(int argc, const char **argv)
 
 
 int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
 int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
 {
 {
+	perf_config(perf_default_config, NULL);
+
 	sort_order = diff__default_sort_order;
 	sort_order = diff__default_sort_order;
 	argc = parse_options(argc, argv, options, diff_usage, 0);
 	argc = parse_options(argc, argv, options, diff_usage, 0);
 
 

+ 5 - 3
tools/perf/builtin-kmem.c

@@ -756,11 +756,13 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
 	OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
 	OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
 	OPT_END()
 	OPT_END()
 	};
 	};
-	const char * const kmem_usage[] = {
-		"perf kmem [<options>] {record|stat}",
+	const char *const kmem_subcommands[] = { "record", "stat", NULL };
+	const char *kmem_usage[] = {
+		NULL,
 		NULL
 		NULL
 	};
 	};
-	argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);
+	argc = parse_options_subcommand(argc, argv, kmem_options,
+					kmem_subcommands, kmem_usage, 0);
 
 
 	if (!argc)
 	if (!argc)
 		usage_with_options(kmem_usage, kmem_options);
 		usage_with_options(kmem_usage, kmem_options);

+ 6 - 4
tools/perf/builtin-lock.c

@@ -961,8 +961,10 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)
 		"perf lock info [<options>]",
 		"perf lock info [<options>]",
 		NULL
 		NULL
 	};
 	};
-	const char * const lock_usage[] = {
-		"perf lock [<options>] {record|report|script|info}",
+	const char *const lock_subcommands[] = { "record", "report", "script",
+						 "info", NULL };
+	const char *lock_usage[] = {
+		NULL,
 		NULL
 		NULL
 	};
 	};
 	const char * const report_usage[] = {
 	const char * const report_usage[] = {
@@ -976,8 +978,8 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)
 	for (i = 0; i < LOCKHASH_SIZE; i++)
 	for (i = 0; i < LOCKHASH_SIZE; i++)
 		INIT_LIST_HEAD(lockhash_table + i);
 		INIT_LIST_HEAD(lockhash_table + i);
 
 
-	argc = parse_options(argc, argv, lock_options, lock_usage,
-			     PARSE_OPT_STOP_AT_NON_OPTION);
+	argc = parse_options_subcommand(argc, argv, lock_options, lock_subcommands,
+					lock_usage, PARSE_OPT_STOP_AT_NON_OPTION);
 	if (!argc)
 	if (!argc)
 		usage_with_options(lock_usage, lock_options);
 		usage_with_options(lock_usage, lock_options);
 
 

+ 8 - 7
tools/perf/builtin-mem.c

@@ -21,11 +21,6 @@ struct perf_mem {
 	DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
 	DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
 };
 };
 
 
-static const char * const mem_usage[] = {
-	"perf mem [<options>] {record <command> |report}",
-	NULL
-};
-
 static int __cmd_record(int argc, const char **argv)
 static int __cmd_record(int argc, const char **argv)
 {
 {
 	int rec_argc, i = 0, j;
 	int rec_argc, i = 0, j;
@@ -220,9 +215,15 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
 		   " between columns '.' is reserved."),
 		   " between columns '.' is reserved."),
 	OPT_END()
 	OPT_END()
 	};
 	};
+	const char *const mem_subcommands[] = { "record", "report", NULL };
+	const char *mem_usage[] = {
+		NULL,
+		NULL
+	};
+
 
 
-	argc = parse_options(argc, argv, mem_options, mem_usage,
-			     PARSE_OPT_STOP_AT_NON_OPTION);
+	argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands,
+					mem_usage, PARSE_OPT_STOP_AT_NON_OPTION);
 
 
 	if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation))
 	if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation))
 		usage_with_options(mem_usage, mem_options);
 		usage_with_options(mem_usage, mem_options);

+ 20 - 2
tools/perf/builtin-report.c

@@ -123,6 +123,8 @@ static int report__add_mem_hist_entry(struct report *rep, struct addr_location *
 
 
 	evsel->hists.stats.total_period += cost;
 	evsel->hists.stats.total_period += cost;
 	hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
 	hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+	if (!he->filtered)
+		evsel->hists.stats.nr_non_filtered_samples++;
 	err = hist_entry__append_callchain(he, sample);
 	err = hist_entry__append_callchain(he, sample);
 out:
 out:
 	return err;
 	return err;
@@ -176,6 +178,8 @@ static int report__add_branch_hist_entry(struct report *rep, struct addr_locatio
 
 
 			evsel->hists.stats.total_period += 1;
 			evsel->hists.stats.total_period += 1;
 			hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
 			hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+			if (!he->filtered)
+				evsel->hists.stats.nr_non_filtered_samples++;
 		} else
 		} else
 			goto out;
 			goto out;
 	}
 	}
@@ -209,6 +213,8 @@ static int report__add_hist_entry(struct report *rep, struct perf_evsel *evsel,
 		err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
 		err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
 
 
 	evsel->hists.stats.total_period += sample->period;
 	evsel->hists.stats.total_period += sample->period;
+	if (!he->filtered)
+		evsel->hists.stats.nr_non_filtered_samples++;
 	hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
 	hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
 out:
 out:
 	return err;
 	return err;
@@ -337,6 +343,11 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
 	char buf[512];
 	char buf[512];
 	size_t size = sizeof(buf);
 	size_t size = sizeof(buf);
 
 
+	if (symbol_conf.filter_relative) {
+		nr_samples = hists->stats.nr_non_filtered_samples;
+		nr_events = hists->stats.total_non_filtered_period;
+	}
+
 	if (perf_evsel__is_group_event(evsel)) {
 	if (perf_evsel__is_group_event(evsel)) {
 		struct perf_evsel *pos;
 		struct perf_evsel *pos;
 
 
@@ -344,8 +355,13 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
 		evname = buf;
 		evname = buf;
 
 
 		for_each_group_member(pos, evsel) {
 		for_each_group_member(pos, evsel) {
-			nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
-			nr_events += pos->hists.stats.total_period;
+			if (symbol_conf.filter_relative) {
+				nr_samples += pos->hists.stats.nr_non_filtered_samples;
+				nr_events += pos->hists.stats.total_non_filtered_period;
+			} else {
+				nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
+				nr_events += pos->hists.stats.total_period;
+			}
 		}
 		}
 	}
 	}
 
 
@@ -823,6 +839,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
 	OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
 	OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
 	OPT_CALLBACK(0, "percent-limit", &report, "percent",
 	OPT_CALLBACK(0, "percent-limit", &report, "percent",
 		     "Don't show entries under that percent", parse_percent_limit),
 		     "Don't show entries under that percent", parse_percent_limit),
+	OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
+		     "how to display percentage of filtered entries", parse_filter_percentage),
 	OPT_END()
 	OPT_END()
 	};
 	};
 	struct perf_data_file file = {
 	struct perf_data_file file = {

+ 6 - 4
tools/perf/builtin-sched.c

@@ -1713,8 +1713,10 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
 		"perf sched replay [<options>]",
 		"perf sched replay [<options>]",
 		NULL
 		NULL
 	};
 	};
-	const char * const sched_usage[] = {
-		"perf sched [<options>] {record|latency|map|replay|script}",
+	const char *const sched_subcommands[] = { "record", "latency", "map",
+						  "replay", "script", NULL };
+	const char *sched_usage[] = {
+		NULL,
 		NULL
 		NULL
 	};
 	};
 	struct trace_sched_handler lat_ops  = {
 	struct trace_sched_handler lat_ops  = {
@@ -1736,8 +1738,8 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
 	for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++)
 	for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++)
 		sched.curr_pid[i] = -1;
 		sched.curr_pid[i] = -1;
 
 
-	argc = parse_options(argc, argv, sched_options, sched_usage,
-			     PARSE_OPT_STOP_AT_NON_OPTION);
+	argc = parse_options_subcommand(argc, argv, sched_options, sched_subcommands,
+					sched_usage, PARSE_OPT_STOP_AT_NON_OPTION);
 	if (!argc)
 	if (!argc)
 		usage_with_options(sched_usage, sched_options);
 		usage_with_options(sched_usage, sched_options);
 
 

+ 6 - 2
tools/perf/builtin-top.c

@@ -253,6 +253,9 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel,
 		return NULL;
 		return NULL;
 
 
 	hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
 	hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+	if (!he->filtered)
+		evsel->hists.stats.nr_non_filtered_samples++;
+
 	return he;
 	return he;
 }
 }
 
 
@@ -694,8 +697,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
 	if (event->header.misc & PERF_RECORD_MISC_EXACT_IP)
 	if (event->header.misc & PERF_RECORD_MISC_EXACT_IP)
 		top->exact_samples++;
 		top->exact_samples++;
 
 
-	if (perf_event__preprocess_sample(event, machine, &al, sample) < 0 ||
-	    al.filtered)
+	if (perf_event__preprocess_sample(event, machine, &al, sample) < 0)
 		return;
 		return;
 
 
 	if (!top->kptr_restrict_warned &&
 	if (!top->kptr_restrict_warned &&
@@ -1116,6 +1118,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
 	OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"),
 	OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"),
 	OPT_CALLBACK(0, "percent-limit", &top, "percent",
 	OPT_CALLBACK(0, "percent-limit", &top, "percent",
 		     "Don't show entries under that percent", parse_percent_limit),
 		     "Don't show entries under that percent", parse_percent_limit),
+	OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
+		     "How to display percentage of filtered entries", parse_filter_percentage),
 	OPT_END()
 	OPT_END()
 	};
 	};
 	const char * const top_usage[] = {
 	const char * const top_usage[] = {

+ 2 - 2
tools/perf/perf-completion.sh

@@ -121,8 +121,8 @@ __perf_main ()
 	elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then
 	elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then
 		evts=$($cmd list --raw-dump)
 		evts=$($cmd list --raw-dump)
 		__perfcomp_colon "$evts" "$cur"
 		__perfcomp_colon "$evts" "$cur"
-	# List subcommands for 'perf kvm'
-	elif [[ $prev == "kvm" ]]; then
+	# List subcommands for perf commands
+	elif [[ $prev == @(kvm|kmem|mem|lock|sched) ]]; then
 		subcmds=$($cmd $prev --list-cmds)
 		subcmds=$($cmd $prev --list-cmds)
 		__perfcomp_colon "$subcmds" "$cur"
 		__perfcomp_colon "$subcmds" "$cur"
 	# List long option names
 	# List long option names

+ 31 - 8
tools/perf/ui/browsers/hists.c

@@ -769,12 +769,15 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
 
 
 	for (nd = browser->top; nd; nd = rb_next(nd)) {
 	for (nd = browser->top; nd; nd = rb_next(nd)) {
 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-		float percent = h->stat.period * 100.0 /
-					hb->hists->stats.total_period;
+		u64 total = hists__total_period(h->hists);
+		float percent = 0.0;
 
 
 		if (h->filtered)
 		if (h->filtered)
 			continue;
 			continue;
 
 
+		if (total)
+			percent = h->stat.period * 100.0 / total;
+
 		if (percent < hb->min_pcnt)
 		if (percent < hb->min_pcnt)
 			continue;
 			continue;
 
 
@@ -792,8 +795,11 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd,
 {
 {
 	while (nd != NULL) {
 	while (nd != NULL) {
 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-		float percent = h->stat.period * 100.0 /
-					hists->stats.total_period;
+		u64 total = hists__total_period(hists);
+		float percent = 0.0;
+
+		if (total)
+			percent = h->stat.period * 100.0 / total;
 
 
 		if (percent < min_pcnt)
 		if (percent < min_pcnt)
 			return NULL;
 			return NULL;
@@ -813,8 +819,11 @@ static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
 {
 {
 	while (nd != NULL) {
 	while (nd != NULL) {
 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-		float percent = h->stat.period * 100.0 /
-					hists->stats.total_period;
+		u64 total = hists__total_period(hists);
+		float percent = 0.0;
+
+		if (total)
+			percent = h->stat.period * 100.0 / total;
 
 
 		if (!h->filtered && percent >= min_pcnt)
 		if (!h->filtered && percent >= min_pcnt)
 			return nd;
 			return nd;
@@ -1189,6 +1198,11 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
 	char buf[512];
 	char buf[512];
 	size_t buflen = sizeof(buf);
 	size_t buflen = sizeof(buf);
 
 
+	if (symbol_conf.filter_relative) {
+		nr_samples = hists->stats.nr_non_filtered_samples;
+		nr_events = hists->stats.total_non_filtered_period;
+	}
+
 	if (perf_evsel__is_group_event(evsel)) {
 	if (perf_evsel__is_group_event(evsel)) {
 		struct perf_evsel *pos;
 		struct perf_evsel *pos;
 
 
@@ -1196,8 +1210,13 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
 		ev_name = buf;
 		ev_name = buf;
 
 
 		for_each_group_member(pos, evsel) {
 		for_each_group_member(pos, evsel) {
-			nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
-			nr_events += pos->hists.stats.total_period;
+			if (symbol_conf.filter_relative) {
+				nr_samples += pos->hists.stats.nr_non_filtered_samples;
+				nr_events += pos->hists.stats.total_non_filtered_period;
+			} else {
+				nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
+				nr_events += pos->hists.stats.total_period;
+			}
 		}
 		}
 	}
 	}
 
 
@@ -1370,6 +1389,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
 	"C             Collapse all callchains\n"			\
 	"C             Collapse all callchains\n"			\
 	"d             Zoom into current DSO\n"				\
 	"d             Zoom into current DSO\n"				\
 	"E             Expand all callchains\n"				\
 	"E             Expand all callchains\n"				\
+	"F             Toggle percentage of filtered entries\n"		\
 
 
 	/* help messages are sorted by lexical order of the hotkey */
 	/* help messages are sorted by lexical order of the hotkey */
 	const char report_help[] = HIST_BROWSER_HELP_COMMON
 	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,
 			if (env->arch)
 			if (env->arch)
 				tui__header_window(env);
 				tui__header_window(env);
 			continue;
 			continue;
+		case 'F':
+			symbol_conf.filter_relative ^= 1;
+			continue;
 		case K_F1:
 		case K_F1:
 		case 'h':
 		case 'h':
 		case '?':
 		case '?':

+ 5 - 6
tools/perf/ui/gtk/hists.c

@@ -228,12 +228,15 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 		GtkTreeIter iter;
 		GtkTreeIter iter;
-		float percent = h->stat.period * 100.0 /
-					hists->stats.total_period;
+		u64 total = hists__total_period(h->hists);
+		float percent = 0.0;
 
 
 		if (h->filtered)
 		if (h->filtered)
 			continue;
 			continue;
 
 
+		if (total)
+			percent = h->stat.period * 100.0 / total;
+
 		if (percent < min_pcnt)
 		if (percent < min_pcnt)
 			continue;
 			continue;
 
 
@@ -261,12 +264,8 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
 		}
 		}
 
 
 		if (symbol_conf.use_callchain && sort__has_sym) {
 		if (symbol_conf.use_callchain && sort__has_sym) {
-			u64 total;
-
 			if (callchain_param.mode == CHAIN_GRAPH_REL)
 			if (callchain_param.mode == CHAIN_GRAPH_REL)
 				total = h->stat.period;
 				total = h->stat.period;
-			else
-				total = hists->stats.total_period;
 
 
 			perf_gtk__add_callchain(&h->sorted_chain, store, &iter,
 			perf_gtk__add_callchain(&h->sorted_chain, store, &iter,
 						sym_col, total);
 						sym_col, total);

+ 4 - 4
tools/perf/ui/hist.c

@@ -32,10 +32,10 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
 
 
 	if (fmt_percent) {
 	if (fmt_percent) {
 		double percent = 0.0;
 		double percent = 0.0;
+		u64 total = hists__total_period(hists);
 
 
-		if (hists->stats.total_period)
-			percent = 100.0 * get_field(he) /
-				  hists->stats.total_period;
+		if (total)
+			percent = 100.0 * get_field(he) / total;
 
 
 		ret += hpp__call_print_fn(hpp, print_fn, fmt, percent);
 		ret += hpp__call_print_fn(hpp, print_fn, fmt, percent);
 	} else
 	} else
@@ -50,7 +50,7 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
 
 
 		list_for_each_entry(pair, &he->pairs.head, pairs.node) {
 		list_for_each_entry(pair, &he->pairs.head, pairs.node) {
 			u64 period = get_field(pair);
 			u64 period = get_field(pair);
-			u64 total = pair->hists->stats.total_period;
+			u64 total = hists__total_period(pair->hists);
 
 
 			if (!total)
 			if (!total)
 				continue;
 				continue;

+ 4 - 0
tools/perf/util/config.c

@@ -11,6 +11,7 @@
 #include "util.h"
 #include "util.h"
 #include "cache.h"
 #include "cache.h"
 #include "exec_cmd.h"
 #include "exec_cmd.h"
+#include "util/hist.h"  /* perf_hist_config */
 
 
 #define MAXNAME (256)
 #define MAXNAME (256)
 
 
@@ -355,6 +356,9 @@ int perf_default_config(const char *var, const char *value,
 	if (!prefixcmp(var, "core."))
 	if (!prefixcmp(var, "core."))
 		return perf_default_core_config(var, value);
 		return perf_default_core_config(var, value);
 
 
+	if (!prefixcmp(var, "hist."))
+		return perf_hist_config(var, value);
+
 	/* Add other config variables here. */
 	/* Add other config variables here. */
 	return 0;
 	return 0;
 }
 }

+ 46 - 13
tools/perf/util/hist.c

@@ -321,9 +321,11 @@ void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
 {
 {
 	if (!h->filtered) {
 	if (!h->filtered) {
 		hists__calc_col_len(hists, h);
 		hists__calc_col_len(hists, h);
-		++hists->nr_entries;
-		hists->stats.total_period += h->stat.period;
+		hists->nr_non_filtered_entries++;
+		hists->stats.total_non_filtered_period += h->stat.period;
 	}
 	}
+	hists->nr_entries++;
+	hists->stats.total_period += h->stat.period;
 }
 }
 
 
 static u8 symbol__parent_filter(const struct symbol *parent)
 static u8 symbol__parent_filter(const struct symbol *parent)
@@ -674,8 +676,9 @@ void hists__output_resort(struct hists *hists)
 	next = rb_first(root);
 	next = rb_first(root);
 	hists->entries = RB_ROOT;
 	hists->entries = RB_ROOT;
 
 
-	hists->nr_entries = 0;
+	hists->nr_non_filtered_entries = 0;
 	hists->stats.total_period = 0;
 	hists->stats.total_period = 0;
+	hists->stats.total_non_filtered_period = 0;
 	hists__reset_col_len(hists);
 	hists__reset_col_len(hists);
 
 
 	while (next) {
 	while (next) {
@@ -694,12 +697,12 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h
 	if (h->filtered)
 	if (h->filtered)
 		return;
 		return;
 
 
-	++hists->nr_entries;
+	++hists->nr_non_filtered_entries;
 	if (h->ms.unfolded)
 	if (h->ms.unfolded)
-		hists->nr_entries += h->nr_rows;
+		hists->nr_non_filtered_entries += h->nr_rows;
 	h->row_offset = 0;
 	h->row_offset = 0;
-	hists->stats.total_period += h->stat.period;
-	hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events;
+	hists->stats.total_non_filtered_period += h->stat.period;
+	hists->stats.nr_non_filtered_samples += h->stat.nr_events;
 
 
 	hists__calc_col_len(hists, h);
 	hists__calc_col_len(hists, h);
 }
 }
@@ -721,8 +724,9 @@ void hists__filter_by_dso(struct hists *hists)
 {
 {
 	struct rb_node *nd;
 	struct rb_node *nd;
 
 
-	hists->nr_entries = hists->stats.total_period = 0;
-	hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
+	hists->nr_non_filtered_entries = 0;
+	hists->stats.total_non_filtered_period = 0;
+	hists->stats.nr_non_filtered_samples = 0;
 	hists__reset_col_len(hists);
 	hists__reset_col_len(hists);
 
 
 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
@@ -754,8 +758,9 @@ void hists__filter_by_thread(struct hists *hists)
 {
 {
 	struct rb_node *nd;
 	struct rb_node *nd;
 
 
-	hists->nr_entries = hists->stats.total_period = 0;
-	hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
+	hists->nr_non_filtered_entries = 0;
+	hists->stats.total_non_filtered_period = 0;
+	hists->stats.nr_non_filtered_samples = 0;
 	hists__reset_col_len(hists);
 	hists__reset_col_len(hists);
 
 
 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
@@ -785,8 +790,9 @@ void hists__filter_by_symbol(struct hists *hists)
 {
 {
 	struct rb_node *nd;
 	struct rb_node *nd;
 
 
-	hists->nr_entries = hists->stats.total_period = 0;
-	hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
+	hists->nr_non_filtered_entries = 0;
+	hists->stats.total_non_filtered_period = 0;
+	hists->stats.nr_non_filtered_samples = 0;
 	hists__reset_col_len(hists);
 	hists__reset_col_len(hists);
 
 
 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
@@ -931,3 +937,30 @@ int hists__link(struct hists *leader, struct hists *other)
 
 
 	return 0;
 	return 0;
 }
 }
+
+u64 hists__total_period(struct hists *hists)
+{
+	return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period :
+		hists->stats.total_period;
+}
+
+int parse_filter_percentage(const struct option *opt __maybe_unused,
+			    const char *arg, int unset __maybe_unused)
+{
+	if (!strcmp(arg, "relative"))
+		symbol_conf.filter_relative = true;
+	else if (!strcmp(arg, "absolute"))
+		symbol_conf.filter_relative = false;
+	else
+		return -1;
+
+	return 0;
+}
+
+int perf_hist_config(const char *var, const char *value)
+{
+	if (!strcmp(var, "hist.percentage"))
+		return parse_filter_percentage(NULL, value, 0);
+
+	return 0;
+}

+ 10 - 0
tools/perf/util/hist.h

@@ -37,9 +37,11 @@ enum hist_filter {
  */
  */
 struct events_stats {
 struct events_stats {
 	u64 total_period;
 	u64 total_period;
+	u64 total_non_filtered_period;
 	u64 total_lost;
 	u64 total_lost;
 	u64 total_invalid_chains;
 	u64 total_invalid_chains;
 	u32 nr_events[PERF_RECORD_HEADER_MAX];
 	u32 nr_events[PERF_RECORD_HEADER_MAX];
+	u32 nr_non_filtered_samples;
 	u32 nr_lost_warned;
 	u32 nr_lost_warned;
 	u32 nr_unknown_events;
 	u32 nr_unknown_events;
 	u32 nr_invalid_chains;
 	u32 nr_invalid_chains;
@@ -83,6 +85,7 @@ struct hists {
 	struct rb_root		entries;
 	struct rb_root		entries;
 	struct rb_root		entries_collapsed;
 	struct rb_root		entries_collapsed;
 	u64			nr_entries;
 	u64			nr_entries;
+	u64			nr_non_filtered_entries;
 	const struct thread	*thread_filter;
 	const struct thread	*thread_filter;
 	const struct dso	*dso_filter;
 	const struct dso	*dso_filter;
 	const char		*uid_filter_str;
 	const char		*uid_filter_str;
@@ -112,6 +115,7 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog);
 void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel);
 void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel);
 void hists__output_recalc_col_len(struct hists *hists, int max_rows);
 void hists__output_recalc_col_len(struct hists *hists, int max_rows);
 
 
+u64 hists__total_period(struct hists *hists);
 void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h);
 void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h);
 void hists__inc_nr_events(struct hists *hists, u32 type);
 void hists__inc_nr_events(struct hists *hists, u32 type);
 void events_stats__inc(struct events_stats *stats, u32 type);
 void events_stats__inc(struct events_stats *stats, u32 type);
@@ -250,4 +254,10 @@ static inline int script_browse(const char *script_opt __maybe_unused)
 #endif
 #endif
 
 
 unsigned int hists__sort_list_width(struct hists *hists);
 unsigned int hists__sort_list_width(struct hists *hists);
+
+struct option;
+int parse_filter_percentage(const struct option *opt __maybe_unused,
+			    const char *arg, int unset __maybe_unused);
+int perf_hist_config(const char *var, const char *value);
+
 #endif	/* __PERF_HIST_H */
 #endif	/* __PERF_HIST_H */

+ 2 - 1
tools/perf/util/symbol.h

@@ -115,7 +115,8 @@ struct symbol_conf {
 			annotate_asm_raw,
 			annotate_asm_raw,
 			annotate_src,
 			annotate_src,
 			event_group,
 			event_group,
-			demangle;
+			demangle,
+			filter_relative;
 	const char	*vmlinux_name,
 	const char	*vmlinux_name,
 			*kallsyms_name,
 			*kallsyms_name,
 			*source_prefix,
 			*source_prefix,