From 1ab1fa5dfb429c533fbc791e524788cf0cc43775 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 26 Dec 2013 15:11:52 +0900 Subject: perf hists: Add support for showing relative percentage When filtering by thread, dso or symbol on TUI it also update total period so that the output shows different result than no filter - the percentage changed to relative to filtered entries only. Sometimes this is not desired since users might expect same results with filter. So new filtered_* fields to hists->stats to count them separately. They'll be controlled/used by user later. Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1397145720-8063-2-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/builtin-report.c | 6 ++++++ tools/perf/builtin-top.c | 3 +++ tools/perf/util/hist.c | 17 ++++++++++++++--- tools/perf/util/hist.h | 3 +++ 4 files changed, 26 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index c8f21137dfd8..2fca56c9d68a 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 * evsel->hists.stats.total_period += cost; 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); out: 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; hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); + if (!he->filtered) + evsel->hists.stats.nr_non_filtered_samples++; } else 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); 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); out: return err; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 65aaa5bbf7ec..25269014164a 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, return NULL; hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); + if (!he->filtered) + evsel->hists.stats.nr_non_filtered_samples++; + return he; } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f38590d7561b..1ed3e2b86f0b 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -674,8 +674,8 @@ void hists__output_resort(struct hists *hists) next = rb_first(root); hists->entries = RB_ROOT; - hists->nr_entries = 0; - hists->stats.total_period = 0; + hists->nr_entries = hists->nr_non_filtered_entries = 0; + hists->stats.total_period = hists->stats.total_non_filtered_period = 0; hists__reset_col_len(hists); while (next) { @@ -695,11 +695,16 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h return; ++hists->nr_entries; - if (h->ms.unfolded) + ++hists->nr_non_filtered_entries; + if (h->ms.unfolded) { hists->nr_entries += h->nr_rows; + hists->nr_non_filtered_entries += h->nr_rows; + } h->row_offset = 0; hists->stats.total_period += h->stat.period; + hists->stats.total_non_filtered_period += h->stat.period; hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events; + hists->stats.nr_non_filtered_samples += h->stat.nr_events; hists__calc_col_len(hists, h); } @@ -722,7 +727,9 @@ void hists__filter_by_dso(struct hists *hists) struct rb_node *nd; hists->nr_entries = hists->stats.total_period = 0; + hists->nr_non_filtered_entries = hists->stats.total_non_filtered_period = 0; hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; + hists->stats.nr_non_filtered_samples = 0; hists__reset_col_len(hists); for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { @@ -755,7 +762,9 @@ void hists__filter_by_thread(struct hists *hists) struct rb_node *nd; hists->nr_entries = hists->stats.total_period = 0; + hists->nr_non_filtered_entries = hists->stats.total_non_filtered_period = 0; hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; + hists->stats.nr_non_filtered_samples = 0; hists__reset_col_len(hists); for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { @@ -786,7 +795,9 @@ void hists__filter_by_symbol(struct hists *hists) struct rb_node *nd; hists->nr_entries = hists->stats.total_period = 0; + hists->nr_non_filtered_entries = hists->stats.total_non_filtered_period = 0; hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; + hists->stats.nr_non_filtered_samples = 0; hists__reset_col_len(hists); for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1f1f513dfe7f..213551469f36 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -37,9 +37,11 @@ enum hist_filter { */ struct events_stats { u64 total_period; + u64 total_non_filtered_period; u64 total_lost; u64 total_invalid_chains; u32 nr_events[PERF_RECORD_HEADER_MAX]; + u32 nr_non_filtered_samples; u32 nr_lost_warned; u32 nr_unknown_events; u32 nr_invalid_chains; @@ -83,6 +85,7 @@ struct hists { struct rb_root entries; struct rb_root entries_collapsed; u64 nr_entries; + u64 nr_non_filtered_entries; const struct thread *thread_filter; const struct dso *dso_filter; const char *uid_filter_str; -- cgit v1.2.3 From f2148330544a697481219b5bc34261f6dd049bfb Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 14 Jan 2014 11:52:48 +0900 Subject: perf report: Add --percentage option The --percentage option is for controlling overhead percentage displayed. It can only receive either of "relative" or "absolute". "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. $ perf report -s comm # Overhead Command # ........ ............ # 74.19% cc1 7.61% gcc 6.11% as 4.35% sh 4.14% make 1.13% fixdep ... $ perf report -s comm -c cc1,gcc --percentage absolute # Overhead Command # ........ ............ # 74.19% cc1 7.61% gcc $ perf report -s comm -c cc1,gcc --percentage relative # Overhead Command # ........ ............ # 90.69% cc1 9.31% gcc Note that it has zero effect if no filter was applied. Suggested-by: Arnaldo Carvalho de Melo Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1397145720-8063-3-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/Documentation/perf-report.txt | 24 +++++++++++++++----- tools/perf/builtin-report.c | 30 ++++++++++++++++++++++-- tools/perf/ui/browsers/hists.c | 35 +++++++++++++++++++++------- tools/perf/ui/gtk/hists.c | 11 ++++----- tools/perf/ui/hist.c | 8 +++---- tools/perf/util/hist.c | 39 ++++++++++++++++---------------- tools/perf/util/hist.h | 1 + tools/perf/util/symbol.c | 1 + tools/perf/util/symbol.h | 3 ++- 9 files changed, 106 insertions(+), 46 deletions(-) (limited to 'tools') 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 --verbose:: Be more verbose. (show symbol address, etc) --d:: ---dsos=:: - Only consider symbols in these dsos. CSV that understands - file://filename entries. -n:: --show-nr-samples:: Show the number of samples for each symbol @@ -42,11 +38,18 @@ OPTIONS -c:: --comms=:: 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:: --symbols=:: 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=:: 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. (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:: Show header information in the perf.data file. This includes various information like hostname, OS and perf version, cpu/mem diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 2fca56c9d68a..7ec351bda833 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -343,6 +343,11 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report char buf[512]; 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)) { struct perf_evsel *pos; @@ -350,8 +355,13 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report evname = buf; 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; + } } } @@ -707,6 +717,20 @@ parse_percent_limit(const struct option *opt, const char *str, return 0; } +static int +parse_percentage(const struct option *opt __maybe_unused, const char *str, + int unset __maybe_unused) +{ + if (!strcmp(str, "relative")) + symbol_conf.filter_relative = true; + else if (!strcmp(str, "absolute")) + symbol_conf.filter_relative = false; + else + return -1; + + return 0; +} + int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) { struct perf_session *session; @@ -829,6 +853,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_CALLBACK(0, "percent-limit", &report, "percent", "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_percentage), OPT_END() }; struct perf_data_file file = { diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 7ec871af3f6f..7ad11477a0f5 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) for (nd = browser->top; nd; nd = rb_next(nd)) { 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) continue; + if (total) + percent = h->stat.period * 100.0 / total; + if (percent < hb->min_pcnt) continue; @@ -792,8 +795,11 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd, { while (nd != NULL) { 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) return NULL; @@ -813,8 +819,11 @@ static struct rb_node *hists__filter_prev_entries(struct rb_node *nd, { while (nd != NULL) { 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) return nd; @@ -1189,6 +1198,11 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, char buf[512]; 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)) { struct perf_evsel *pos; @@ -1196,8 +1210,13 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, ev_name = buf; 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; + } } } 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, for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 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) continue; + if (total) + percent = h->stat.period * 100.0 / total; + if (percent < min_pcnt) continue; @@ -261,12 +264,8 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, } if (symbol_conf.use_callchain && sort__has_sym) { - u64 total; - if (callchain_param.mode == CHAIN_GRAPH_REL) total = h->stat.period; - else - total = hists->stats.total_period; perf_gtk__add_callchain(&h->sorted_chain, store, &iter, 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, if (fmt_percent) { 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); } 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) { u64 period = get_field(pair); - u64 total = pair->hists->stats.total_period; + u64 total = hists__total_period(pair->hists); if (!total) continue; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 1ed3e2b86f0b..3ebd89a28257 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) { if (!h->filtered) { 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) @@ -674,8 +676,9 @@ void hists__output_resort(struct hists *hists) next = rb_first(root); hists->entries = RB_ROOT; - hists->nr_entries = hists->nr_non_filtered_entries = 0; - hists->stats.total_period = hists->stats.total_non_filtered_period = 0; + hists->nr_non_filtered_entries = 0; + hists->stats.total_period = 0; + hists->stats.total_non_filtered_period = 0; hists__reset_col_len(hists); while (next) { @@ -694,16 +697,11 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h if (h->filtered) return; - ++hists->nr_entries; ++hists->nr_non_filtered_entries; - if (h->ms.unfolded) { - hists->nr_entries += h->nr_rows; + if (h->ms.unfolded) hists->nr_non_filtered_entries += h->nr_rows; - } h->row_offset = 0; - hists->stats.total_period += h->stat.period; hists->stats.total_non_filtered_period += h->stat.period; - hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events; hists->stats.nr_non_filtered_samples += h->stat.nr_events; hists__calc_col_len(hists, h); @@ -726,9 +724,8 @@ void hists__filter_by_dso(struct hists *hists) { struct rb_node *nd; - hists->nr_entries = hists->stats.total_period = 0; - hists->nr_non_filtered_entries = hists->stats.total_non_filtered_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); @@ -761,9 +758,8 @@ void hists__filter_by_thread(struct hists *hists) { struct rb_node *nd; - hists->nr_entries = hists->stats.total_period = 0; - hists->nr_non_filtered_entries = hists->stats.total_non_filtered_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); @@ -794,9 +790,8 @@ void hists__filter_by_symbol(struct hists *hists) { struct rb_node *nd; - hists->nr_entries = hists->stats.total_period = 0; - hists->nr_non_filtered_entries = hists->stats.total_non_filtered_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); @@ -942,3 +937,9 @@ int hists__link(struct hists *leader, struct hists *other) return 0; } + +u64 hists__total_period(struct hists *hists) +{ + return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period : + hists->stats.total_period; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 213551469f36..3191496bd3b7 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -115,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__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_events(struct hists *hists, u32 type); void events_stats__inc(struct events_stats *stats, u32 type); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 95e249779931..b2eca6c17a70 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -33,6 +33,7 @@ struct symbol_conf symbol_conf = { .try_vmlinux_path = true, .annotate_src = true, .demangle = true, + .filter_relative = true, .symfs = "", }; 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 { annotate_asm_raw, annotate_src, event_group, - demangle; + demangle, + filter_relative; const char *vmlinux_name, *kallsyms_name, *source_prefix, -- cgit v1.2.3 From 33db4568e1f41efe6d0e4695483f968fc1135bf3 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 7 Feb 2014 12:06:07 +0900 Subject: perf top: Add --percentage option The --percentage option is for controlling overhead percentage displayed. It can only receive either of "relative" or "absolute". Move the parser callback function into a common location since it's used by multiple commands now. For more information, please see previous commit same thing done to "perf report". Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1397145720-8063-4-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/Documentation/perf-top.txt | 18 +++++++++++++++--- tools/perf/builtin-report.c | 16 +--------------- tools/perf/builtin-top.c | 5 +++-- tools/perf/util/hist.c | 13 +++++++++++++ tools/perf/util/hist.h | 5 +++++ 5 files changed, 37 insertions(+), 20 deletions(-) (limited to 'tools') 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. Show a column with the sum of periods. --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:: - 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:: - Only consider these symbols. + Only consider these symbols. This option will affect the + percentage of the overhead column. See --percentage for more info. -M:: --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. (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 -------------------------- diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 7ec351bda833..af8cb7a2c9b6 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -717,20 +717,6 @@ parse_percent_limit(const struct option *opt, const char *str, return 0; } -static int -parse_percentage(const struct option *opt __maybe_unused, const char *str, - int unset __maybe_unused) -{ - if (!strcmp(str, "relative")) - symbol_conf.filter_relative = true; - else if (!strcmp(str, "absolute")) - symbol_conf.filter_relative = false; - else - return -1; - - return 0; -} - int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) { struct perf_session *session; @@ -854,7 +840,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) OPT_CALLBACK(0, "percent-limit", &report, "percent", "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_percentage), + "how to display percentage of filtered entries", parse_filter_percentage), OPT_END() }; struct perf_data_file file = { diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 25269014164a..37d30460bada 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -697,8 +697,7 @@ static void perf_event__process_sample(struct perf_tool *tool, if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) 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; if (!top->kptr_restrict_warned && @@ -1119,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_CALLBACK(0, "percent-limit", &top, "percent", "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() }; const char * const top_usage[] = { diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 3ebd89a28257..3c2dd233b98e 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -943,3 +943,16 @@ 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; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 3191496bd3b7..a4ec336ae3fe 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -254,4 +254,9 @@ static inline int script_browse(const char *script_opt __maybe_unused) #endif 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); + #endif /* __PERF_HIST_H */ -- cgit v1.2.3 From 8810f6ced73556c1a63b6269a6cdad8d630aaaf0 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 7 Feb 2014 12:06:07 +0900 Subject: perf diff: Add --percentage option The --percentage option is for controlling overhead percentage displayed. It can only receive either of "relative" or "absolute" and affects -c delta output only. For more information, please see previous commit same thing done to "perf report". Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1397145720-8063-5-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/Documentation/perf-diff.txt | 21 ++++++++++++++++++--- tools/perf/builtin-diff.c | 30 ++++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 11 deletions(-) (limited to 'tools') 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 -d:: --dsos=:: 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:: --comms=:: 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:: --symbols=:: 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:: --sort=:: @@ -89,6 +92,14 @@ OPTIONS --order:: 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 ---------- 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 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 ~~~~~ If specified the 'Ratio' column is displayed with value 'r' computed as: diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 204fffe22532..c903fe13c173 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, 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; } @@ -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, 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, "(%" 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, @@ -327,15 +335,16 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, return -1; } - if (al.filtered) - return 0; - if (hists__add_entry(&evsel->hists, &al, sample->period, sample->weight, sample->transaction)) { pr_warning("problem incrementing symbol period, skipping event\n"); 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; return 0; } @@ -565,7 +574,9 @@ static void hists__compute_resort(struct hists *hists) next = rb_first(root); hists->nr_entries = 0; + hists->nr_non_filtered_entries = 0; hists->stats.total_period = 0; + hists->stats.total_non_filtered_period = 0; hists__reset_col_len(hists); while (next != NULL) { @@ -732,13 +743,16 @@ static const struct option options[] = { OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", "Look for files with symbols relative to this directory"), 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() }; 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, -- cgit v1.2.3 From 0b93da1756df4fe930ee0220a6addce263a6e0ab Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 14 Jan 2014 12:02:15 +0900 Subject: perf tools: Add hist.percentage config option Add hist.percentage option for setting default value of the symbol_conf.filter_relative. It affects the output of various perf commands (like perf report, top and diff) only if filter(s) applied. An user can write .perfconfig file like below to show absolute percentage of filtered entries by default: $ cat ~/.perfconfig [hist] percentage = absolute And it can be changed through command line: $ perf report --percentage relative Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1397145720-8063-6-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/builtin-diff.c | 2 ++ tools/perf/util/config.c | 4 ++++ tools/perf/util/hist.c | 8 ++++++++ tools/perf/util/hist.h | 1 + 4 files changed, 15 insertions(+) (limited to 'tools') diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index c903fe13c173..6ef80f22c1e2 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -1134,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) { + perf_config(perf_default_config, NULL); + sort_order = diff__default_sort_order; argc = parse_options(argc, argv, options, diff_usage, 0); 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 @@ #include "util.h" #include "cache.h" #include "exec_cmd.h" +#include "util/hist.h" /* perf_hist_config */ #define MAXNAME (256) @@ -355,6 +356,9 @@ int perf_default_config(const char *var, const char *value, if (!prefixcmp(var, "core.")) return perf_default_core_config(var, value); + if (!prefixcmp(var, "hist.")) + return perf_hist_config(var, value); + /* Add other config variables here. */ return 0; } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 3c2dd233b98e..5a892477aa50 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -956,3 +956,11 @@ int parse_filter_percentage(const struct option *opt __maybe_unused, 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; +} diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index a4ec336ae3fe..5a0343eb22e2 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -258,5 +258,6 @@ 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 */ -- cgit v1.2.3 From 105eb30f18197a217695eac4ddf87526f2cba867 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 10 Feb 2014 11:20:10 +0900 Subject: perf ui/tui: Add 'F' hotkey to toggle percentage output Add 'F' hotkey to toggle relative and absolute percentage of filtered entries. Suggested-by: Jiri Olsa Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1397145720-8063-7-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/browsers/hists.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 7ad11477a0f5..4d416984c59d 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -1389,6 +1389,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, "C Collapse all callchains\n" \ "d Zoom into current DSO\n" \ "E Expand all callchains\n" \ + "F Toggle percentage of filtered entries\n" \ /* help messages are sorted by lexical order of the hotkey */ const char report_help[] = HIST_BROWSER_HELP_COMMON @@ -1494,6 +1495,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, if (env->arch) tui__header_window(env); continue; + case 'F': + symbol_conf.filter_relative ^= 1; + continue; case K_F1: case 'h': case '?': -- cgit v1.2.3 From 95ce0ba17d5a0a04bbad61720512381d8165d157 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 14 Jan 2014 12:05:27 +0900 Subject: perf tools: Show absolute percentage by default Now perf report will show absolute percentage on filter entries by default. Suggested-by: Jiri Olsa Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1397145720-8063-8-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/symbol.c | 1 - 1 file changed, 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b2eca6c17a70..95e249779931 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -33,7 +33,6 @@ struct symbol_conf symbol_conf = { .try_vmlinux_path = true, .annotate_src = true, .demangle = true, - .filter_relative = true, .symfs = "", }; -- cgit v1.2.3 From 3bca23543b910a84d3c58a819663e0a4fad72bb6 Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Fri, 14 Mar 2014 23:17:51 -0400 Subject: perf kmem: Introduce --list-cmds for use by scripts Signed-off-by: Ramkumar Ramachandra Acked-by: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/r/1394853474-31019-2-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Jiri Olsa --- tools/perf/builtin-kmem.c | 8 +++++--- tools/perf/perf-completion.sh | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'tools') 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) OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), OPT_END() }; - const char * const kmem_usage[] = { - "perf kmem [] {record|stat}", + const char *const kmem_subcommands[] = { "record", "stat", NULL }; + const char *kmem_usage[] = { + 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) usage_with_options(kmem_usage, kmem_options); diff --git a/tools/perf/perf-completion.sh b/tools/perf/perf-completion.sh index ae3a57694b6b..0ef59dd4c78d 100644 --- a/tools/perf/perf-completion.sh +++ b/tools/perf/perf-completion.sh @@ -121,8 +121,8 @@ __perf_main () elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then evts=$($cmd list --raw-dump) __perfcomp_colon "$evts" "$cur" - # List subcommands for 'perf kvm' - elif [[ $prev == "kvm" ]]; then + # List subcommands for perf commands + elif [[ $prev == @(kvm|kmem) ]]; then subcmds=$($cmd $prev --list-cmds) __perfcomp_colon "$subcmds" "$cur" # List long option names -- cgit v1.2.3 From 8d2a2a1d885d36e978c4619a3db8791f9768dd6a Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Fri, 14 Mar 2014 23:17:52 -0400 Subject: perf mem: Introduce --list-cmds for use by scripts Signed-off-by: Ramkumar Ramachandra Acked-by: David Ahern Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/r/1394853474-31019-3-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Jiri Olsa --- tools/perf/builtin-mem.c | 15 ++++++++------- tools/perf/perf-completion.sh | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'tools') 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 { DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); }; -static const char * const mem_usage[] = { - "perf mem [] {record |report}", - NULL -}; - static int __cmd_record(int argc, const char **argv) { 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."), 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)) usage_with_options(mem_usage, mem_options); diff --git a/tools/perf/perf-completion.sh b/tools/perf/perf-completion.sh index 0ef59dd4c78d..f44c04b1015d 100644 --- a/tools/perf/perf-completion.sh +++ b/tools/perf/perf-completion.sh @@ -122,7 +122,7 @@ __perf_main () evts=$($cmd list --raw-dump) __perfcomp_colon "$evts" "$cur" # List subcommands for perf commands - elif [[ $prev == @(kvm|kmem) ]]; then + elif [[ $prev == @(kvm|kmem|mem) ]]; then subcmds=$($cmd $prev --list-cmds) __perfcomp_colon "$subcmds" "$cur" # List long option names -- cgit v1.2.3 From a2368c3199eba493d72c6d0e5b804f908a09706c Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Fri, 14 Mar 2014 23:17:53 -0400 Subject: perf lock: Introduce --list-cmds for use by scripts Signed-off-by: Ramkumar Ramachandra Acked-by: David Ahern Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/r/1394853474-31019-4-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Jiri Olsa --- tools/perf/builtin-lock.c | 10 ++++++---- tools/perf/perf-completion.sh | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'tools') 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) "perf lock info []", NULL }; - const char * const lock_usage[] = { - "perf lock [] {record|report|script|info}", + const char *const lock_subcommands[] = { "record", "report", "script", + "info", NULL }; + const char *lock_usage[] = { + NULL, NULL }; 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++) 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) usage_with_options(lock_usage, lock_options); diff --git a/tools/perf/perf-completion.sh b/tools/perf/perf-completion.sh index f44c04b1015d..ecedab6524e4 100644 --- a/tools/perf/perf-completion.sh +++ b/tools/perf/perf-completion.sh @@ -122,7 +122,7 @@ __perf_main () evts=$($cmd list --raw-dump) __perfcomp_colon "$evts" "$cur" # List subcommands for perf commands - elif [[ $prev == @(kvm|kmem|mem) ]]; then + elif [[ $prev == @(kvm|kmem|mem|lock) ]]; then subcmds=$($cmd $prev --list-cmds) __perfcomp_colon "$subcmds" "$cur" # List long option names -- cgit v1.2.3 From a83edb2dfc5989fbadc594109c933bae528a2809 Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Fri, 14 Mar 2014 23:17:54 -0400 Subject: perf sched: Introduce --list-cmds for use by scripts Signed-off-by: Ramkumar Ramachandra Acked-by: David Ahern Cc: David Ahern Cc: Jiri Olsa Link: http://lkml.kernel.org/r/1394853474-31019-5-git-send-email-artagnon@gmail.com Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Jiri Olsa --- tools/perf/builtin-sched.c | 10 ++++++---- tools/perf/perf-completion.sh | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'tools') 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) "perf sched replay []", NULL }; - const char * const sched_usage[] = { - "perf sched [] {record|latency|map|replay|script}", + const char *const sched_subcommands[] = { "record", "latency", "map", + "replay", "script", NULL }; + const char *sched_usage[] = { + NULL, NULL }; 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++) 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) usage_with_options(sched_usage, sched_options); diff --git a/tools/perf/perf-completion.sh b/tools/perf/perf-completion.sh index ecedab6524e4..33569847fdcc 100644 --- a/tools/perf/perf-completion.sh +++ b/tools/perf/perf-completion.sh @@ -122,7 +122,7 @@ __perf_main () evts=$($cmd list --raw-dump) __perfcomp_colon "$evts" "$cur" # List subcommands for perf commands - elif [[ $prev == @(kvm|kmem|mem|lock) ]]; then + elif [[ $prev == @(kvm|kmem|mem|lock|sched) ]]; then subcmds=$($cmd $prev --list-cmds) __perfcomp_colon "$subcmds" "$cur" # List long option names -- cgit v1.2.3 From 7c2f8164e5415ef48954f6929f4acae5764afdb4 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 16 Apr 2014 20:49:02 +0200 Subject: perf tools: Fix pmu object compilation error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After applying some patches got another shadowing error: CC util/pmu.o util/pmu.c: In function ‘pmu_alias_terms’: util/pmu.c:287:35: error: declaration of ‘clone’ shadows a global declaration [-Werror=shadow] Renaming clone to cloned. Acked-by: David Ahern Cc: David Ahern Cc: Namhyung Kim Link: http://lkml.kernel.org/r/1397674818-27054-1-git-send-email-jolsa@redhat.com Signed-off-by: Jiri Olsa --- tools/perf/util/pmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 00a7dcb2f55c..7a811eb61f75 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -284,17 +284,17 @@ static int pmu_aliases(const char *name, struct list_head *head) static int pmu_alias_terms(struct perf_pmu_alias *alias, struct list_head *terms) { - struct parse_events_term *term, *clone; + struct parse_events_term *term, *cloned; LIST_HEAD(list); int ret; list_for_each_entry(term, &alias->terms, list) { - ret = parse_events_term__clone(&clone, term); + ret = parse_events_term__clone(&cloned, term); if (ret) { parse_events__free_terms(&list); return ret; } - list_add_tail(&clone->list, &list); + list_add_tail(&cloned->list, &list); } list_splice(&list, terms); return 0; -- cgit v1.2.3 From 7780c25bae59fd042281710f5e1243268e2c18dc Mon Sep 17 00:00:00 2001 From: Don Zickus Date: Mon, 7 Apr 2014 14:55:21 -0400 Subject: perf tools: Allow ability to map cpus to nodes easily This patch figures out the max number of cpus and nodes that are on the system and creates a map of cpu to node. This allows us to provide a cpu and quickly get the node associated with it. It was mostly copied from builtin-kmem.c and tweaked slightly to use less memory (use possible cpus instead of max). It also calculates the max number of nodes. Signed-off-by: Don Zickus Reviewed-by: Namhyung Kim Link: http://lkml.kernel.org/r/1396896924-129847-2-git-send-email-dzickus@redhat.com [ Removing out label code in init_cpunode_map ] [ Adding check for snprintf error ] [ Removing unneeded returns ] Signed-off-by: Jiri Olsa --- tools/perf/util/cpumap.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/cpumap.h | 35 +++++++++++ 2 files changed, 195 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 7fe4994eeb63..526da022b3c4 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -317,3 +317,163 @@ int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep) { return cpu_map__build_map(cpus, corep, cpu_map__get_core); } + +/* setup simple routines to easily access node numbers given a cpu number */ +static int get_max_num(char *path, int *max) +{ + size_t num; + char *buf; + int err = 0; + + if (filename__read_str(path, &buf, &num)) + return -1; + + buf[num] = '\0'; + + /* start on the right, to find highest node num */ + while (--num) { + if ((buf[num] == ',') || (buf[num] == '-')) { + num++; + break; + } + } + if (sscanf(&buf[num], "%d", max) < 1) { + err = -1; + goto out; + } + + /* convert from 0-based to 1-based */ + (*max)++; + +out: + free(buf); + return err; +} + +/* Determine highest possible cpu in the system for sparse allocation */ +static void set_max_cpu_num(void) +{ + const char *mnt; + char path[PATH_MAX]; + int ret = -1; + + /* set up default */ + max_cpu_num = 4096; + + mnt = sysfs__mountpoint(); + if (!mnt) + goto out; + + /* get the highest possible cpu number for a sparse allocation */ + ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/kernel_max", mnt); + if (ret == PATH_MAX) { + pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); + goto out; + } + + ret = get_max_num(path, &max_cpu_num); + +out: + if (ret) + pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num); +} + +/* Determine highest possible node in the system for sparse allocation */ +static void set_max_node_num(void) +{ + const char *mnt; + char path[PATH_MAX]; + int ret = -1; + + /* set up default */ + max_node_num = 8; + + mnt = sysfs__mountpoint(); + if (!mnt) + goto out; + + /* get the highest possible cpu number for a sparse allocation */ + ret = snprintf(path, PATH_MAX, "%s/devices/system/node/possible", mnt); + if (ret == PATH_MAX) { + pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); + goto out; + } + + ret = get_max_num(path, &max_node_num); + +out: + if (ret) + pr_err("Failed to read max nodes, using default of %d\n", max_node_num); +} + +static int init_cpunode_map(void) +{ + int i; + + set_max_cpu_num(); + set_max_node_num(); + + cpunode_map = calloc(max_cpu_num, sizeof(int)); + if (!cpunode_map) { + pr_err("%s: calloc failed\n", __func__); + return -1; + } + + for (i = 0; i < max_cpu_num; i++) + cpunode_map[i] = -1; + + return 0; +} + +int cpu__setup_cpunode_map(void) +{ + struct dirent *dent1, *dent2; + DIR *dir1, *dir2; + unsigned int cpu, mem; + char buf[PATH_MAX]; + char path[PATH_MAX]; + const char *mnt; + int n; + + /* initialize globals */ + if (init_cpunode_map()) + return -1; + + mnt = sysfs__mountpoint(); + if (!mnt) + return 0; + + n = snprintf(path, PATH_MAX, "%s/devices/system/node", mnt); + if (n == PATH_MAX) { + pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); + return -1; + } + + dir1 = opendir(path); + if (!dir1) + return 0; + + /* walk tree and setup map */ + while ((dent1 = readdir(dir1)) != NULL) { + if (dent1->d_type != DT_DIR || sscanf(dent1->d_name, "node%u", &mem) < 1) + continue; + + n = snprintf(buf, PATH_MAX, "%s/%s", path, dent1->d_name); + if (n == PATH_MAX) { + pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); + continue; + } + + dir2 = opendir(buf); + if (!dir2) + continue; + while ((dent2 = readdir(dir2)) != NULL) { + if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1) + continue; + cpunode_map[cpu] = mem; + } + closedir(dir2); + } + closedir(dir1); + return 0; +} diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index b123bb9d6f55..61a654849002 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -4,6 +4,9 @@ #include #include +#include "perf.h" +#include "util/debug.h" + struct cpu_map { int nr; int map[]; @@ -46,4 +49,36 @@ static inline bool cpu_map__empty(const struct cpu_map *map) return map ? map->map[0] == -1 : true; } +int max_cpu_num; +int max_node_num; +int *cpunode_map; + +int cpu__setup_cpunode_map(void); + +static inline int cpu__max_node(void) +{ + if (unlikely(!max_node_num)) + pr_debug("cpu_map not initialized\n"); + + return max_node_num; +} + +static inline int cpu__max_cpu(void) +{ + if (unlikely(!max_cpu_num)) + pr_debug("cpu_map not initialized\n"); + + return max_cpu_num; +} + +static inline int cpu__get_node(int cpu) +{ + if (unlikely(cpunode_map == NULL)) { + pr_debug("cpu_map not initialized\n"); + return -1; + } + + return cpunode_map[cpu]; +} + #endif /* __PERF_CPUMAP_H */ -- cgit v1.2.3 From f5b1f4e483d55a88ed120e2c62b45ba5b939fa72 Mon Sep 17 00:00:00 2001 From: Don Zickus Date: Mon, 7 Apr 2014 14:55:22 -0400 Subject: perf tools: Use cpu/possible instead of cpu/kernel_max The system's max configuration is represented by cpu/possible and cpu/kernel_max can be huge (4096 vs. 128), so save space by keeping smaller structures. Signed-off-by: Don Zickus Reviewed-by: Namhyung Kim Link: http://lkml.kernel.org/r/1396896924-129847-3-git-send-email-dzickus@redhat.com Signed-off-by: Jiri Olsa --- tools/perf/util/cpumap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 526da022b3c4..c4e55b71010c 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -365,7 +365,7 @@ static void set_max_cpu_num(void) goto out; /* get the highest possible cpu number for a sparse allocation */ - ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/kernel_max", mnt); + ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/possible", mnt); if (ret == PATH_MAX) { pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); goto out; -- cgit v1.2.3 From 4b6279579c84cca7f162cfbcb98f66418f3062f3 Mon Sep 17 00:00:00 2001 From: Don Zickus Date: Mon, 7 Apr 2014 14:55:23 -0400 Subject: perf kmem: Utilize the new generic cpunode_map Use the previous patch implementation of cpunode_map for builtin-kmem.c Should not be any functional difference. Signed-off-by: Don Zickus Reviewed-by: Namhyung Kim Cc: Li Zefan Link: http://lkml.kernel.org/r/1396896924-129847-4-git-send-email-dzickus@redhat.com Signed-off-by: Jiri Olsa --- tools/perf/builtin-kmem.c | 78 ++--------------------------------------------- 1 file changed, 3 insertions(+), 75 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index bd91de07d2a9..f91fa4376f4b 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -14,6 +14,7 @@ #include "util/parse-options.h" #include "util/trace-event.h" #include "util/data.h" +#include "util/cpumap.h" #include "util/debug.h" @@ -31,9 +32,6 @@ static int caller_lines = -1; static bool raw_ip; -static int *cpunode_map; -static int max_cpu_num; - struct alloc_stat { u64 call_site; u64 ptr; @@ -55,76 +53,6 @@ static struct rb_root root_caller_sorted; static unsigned long total_requested, total_allocated; static unsigned long nr_allocs, nr_cross_allocs; -#define PATH_SYS_NODE "/sys/devices/system/node" - -static int init_cpunode_map(void) -{ - FILE *fp; - int i, err = -1; - - fp = fopen("/sys/devices/system/cpu/kernel_max", "r"); - if (!fp) { - max_cpu_num = 4096; - return 0; - } - - if (fscanf(fp, "%d", &max_cpu_num) < 1) { - pr_err("Failed to read 'kernel_max' from sysfs"); - goto out_close; - } - - max_cpu_num++; - - cpunode_map = calloc(max_cpu_num, sizeof(int)); - if (!cpunode_map) { - pr_err("%s: calloc failed\n", __func__); - goto out_close; - } - - for (i = 0; i < max_cpu_num; i++) - cpunode_map[i] = -1; - - err = 0; -out_close: - fclose(fp); - return err; -} - -static int setup_cpunode_map(void) -{ - struct dirent *dent1, *dent2; - DIR *dir1, *dir2; - unsigned int cpu, mem; - char buf[PATH_MAX]; - - if (init_cpunode_map()) - return -1; - - dir1 = opendir(PATH_SYS_NODE); - if (!dir1) - return 0; - - while ((dent1 = readdir(dir1)) != NULL) { - if (dent1->d_type != DT_DIR || - sscanf(dent1->d_name, "node%u", &mem) < 1) - continue; - - snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name); - dir2 = opendir(buf); - if (!dir2) - continue; - while ((dent2 = readdir(dir2)) != NULL) { - if (dent2->d_type != DT_LNK || - sscanf(dent2->d_name, "cpu%u", &cpu) < 1) - continue; - cpunode_map[cpu] = mem; - } - closedir(dir2); - } - closedir(dir1); - return 0; -} - static int insert_alloc_stat(unsigned long call_site, unsigned long ptr, int bytes_req, int bytes_alloc, int cpu) { @@ -235,7 +163,7 @@ static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel, int ret = perf_evsel__process_alloc_event(evsel, sample); if (!ret) { - int node1 = cpunode_map[sample->cpu], + int node1 = cpu__get_node(sample->cpu), node2 = perf_evsel__intval(evsel, sample, "node"); if (node1 != node2) @@ -772,7 +700,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) if (!strncmp(argv[0], "rec", 3)) { return __cmd_record(argc, argv); } else if (!strcmp(argv[0], "stat")) { - if (setup_cpunode_map()) + if (cpu__setup_cpunode_map()) return -1; if (list_empty(&caller_sort)) -- cgit v1.2.3 From cff6bb46d477383092f46682a0d12e323e4b84d2 Mon Sep 17 00:00:00 2001 From: Don Zickus Date: Mon, 7 Apr 2014 14:55:24 -0400 Subject: perf callchain: Add generic report parse callchain callback function This takes the parse_callchain_opt function and copies it into the callchain.c file. Now the c2c tool can use it too without duplicating. Update perf-report to use the new routine too. Signed-off-by: Don Zickus Reviewed-by: Namhyung Kim Link: http://lkml.kernel.org/r/1396896924-129847-5-git-send-email-dzickus@redhat.com [ Adding missing braces to multiline if condition ] Signed-off-by: Jiri Olsa --- tools/perf/builtin-report.c | 81 ++------------------------------------------- tools/perf/util/callchain.c | 78 +++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/callchain.h | 1 + 3 files changed, 82 insertions(+), 78 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index af8cb7a2c9b6..76e2bb6cf571 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -589,11 +589,9 @@ static int __cmd_report(struct report *rep) } static int -parse_callchain_opt(const struct option *opt, const char *arg, int unset) +report_parse_callchain_opt(const struct option *opt, const char *arg, int unset) { struct report *rep = (struct report *)opt->value; - char *tok, *tok2; - char *endptr; /* * --no-call-graph @@ -603,80 +601,7 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) return 0; } - symbol_conf.use_callchain = true; - - if (!arg) - return 0; - - tok = strtok((char *)arg, ","); - if (!tok) - return -1; - - /* get the output mode */ - if (!strncmp(tok, "graph", strlen(arg))) - callchain_param.mode = CHAIN_GRAPH_ABS; - - else if (!strncmp(tok, "flat", strlen(arg))) - callchain_param.mode = CHAIN_FLAT; - - else if (!strncmp(tok, "fractal", strlen(arg))) - callchain_param.mode = CHAIN_GRAPH_REL; - - else if (!strncmp(tok, "none", strlen(arg))) { - callchain_param.mode = CHAIN_NONE; - symbol_conf.use_callchain = false; - - return 0; - } - - else - return -1; - - /* get the min percentage */ - tok = strtok(NULL, ","); - if (!tok) - goto setup; - - callchain_param.min_percent = strtod(tok, &endptr); - if (tok == endptr) - return -1; - - /* get the print limit */ - tok2 = strtok(NULL, ","); - if (!tok2) - goto setup; - - if (tok2[0] != 'c') { - callchain_param.print_limit = strtoul(tok2, &endptr, 0); - tok2 = strtok(NULL, ","); - if (!tok2) - goto setup; - } - - /* get the call chain order */ - if (!strncmp(tok2, "caller", strlen("caller"))) - callchain_param.order = ORDER_CALLER; - else if (!strncmp(tok2, "callee", strlen("callee"))) - callchain_param.order = ORDER_CALLEE; - else - return -1; - - /* Get the sort key */ - tok2 = strtok(NULL, ","); - if (!tok2) - goto setup; - if (!strncmp(tok2, "function", strlen("function"))) - callchain_param.key = CCKEY_FUNCTION; - else if (!strncmp(tok2, "address", strlen("address"))) - callchain_param.key = CCKEY_ADDRESS; - else - return -1; -setup: - if (callchain_register_param(&callchain_param) < 0) { - pr_err("Can't register callchain params\n"); - return -1; - } - return 0; + return parse_callchain_report_opt(arg); } int @@ -788,7 +713,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) "Only display entries with parent-match"), OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " - "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt), + "Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt), OPT_INTEGER(0, "max-stack", &report.max_stack, "Set the maximum stack depth when parsing the callchain, " "anything beyond the specified depth will be ignored. " diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 8d9db454f1a9..9a42382b3921 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -25,6 +25,84 @@ __thread struct callchain_cursor callchain_cursor; +int +parse_callchain_report_opt(const char *arg) +{ + char *tok, *tok2; + char *endptr; + + symbol_conf.use_callchain = true; + + if (!arg) + return 0; + + tok = strtok((char *)arg, ","); + if (!tok) + return -1; + + /* get the output mode */ + if (!strncmp(tok, "graph", strlen(arg))) { + callchain_param.mode = CHAIN_GRAPH_ABS; + + } else if (!strncmp(tok, "flat", strlen(arg))) { + callchain_param.mode = CHAIN_FLAT; + } else if (!strncmp(tok, "fractal", strlen(arg))) { + callchain_param.mode = CHAIN_GRAPH_REL; + } else if (!strncmp(tok, "none", strlen(arg))) { + callchain_param.mode = CHAIN_NONE; + symbol_conf.use_callchain = false; + return 0; + } else { + return -1; + } + + /* get the min percentage */ + tok = strtok(NULL, ","); + if (!tok) + goto setup; + + callchain_param.min_percent = strtod(tok, &endptr); + if (tok == endptr) + return -1; + + /* get the print limit */ + tok2 = strtok(NULL, ","); + if (!tok2) + goto setup; + + if (tok2[0] != 'c') { +