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/util/hist.c | 17 ++++++++++++++--- tools/perf/util/hist.h | 3 +++ 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') 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/perf/util') 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/perf/util') 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 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/perf/util') 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 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/perf/util') 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 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/perf/util') 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/perf/util') 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/perf/util') 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 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/perf/util') 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') { + 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; +} + static void rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, enum chain_mode mode) diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 8ad97e9b119f..dda4cf8b534c 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -157,4 +157,5 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); extern const char record_callchain_help[]; +int parse_callchain_report_opt(const char *arg); #endif /* __PERF_CALLCHAIN_H */ -- cgit v1.2.3 From 6263835a1b1ad137f3c26a1383c0487a9388d06e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 24 Apr 2014 16:21:46 +0900 Subject: perf hists: Rename hists__inc_stats() The existing hists__inc_nr_entries() is a misnomer as it's not only increasing ->nr_entries but also other stats. So rename it to more general hists__inc_stats(). Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1398327843-31845-3-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/builtin-diff.c | 2 +- tools/perf/util/hist.c | 6 +++--- tools/perf/util/hist.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 6ef80f22c1e2..0e46fa1b5ca0 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -586,7 +586,7 @@ static void hists__compute_resort(struct hists *hists) next = rb_next(&he->rb_node_in); insert_hist_entry_by_compute(&hists->entries, he, compute); - hists__inc_nr_entries(hists, he); + hists__inc_stats(hists, he); } } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 5a892477aa50..12d6c1bd761d 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -317,7 +317,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) return he; } -void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) +void hists__inc_stats(struct hists *hists, struct hist_entry *h) { if (!h->filtered) { hists__calc_col_len(hists, h); @@ -686,7 +686,7 @@ void hists__output_resort(struct hists *hists) next = rb_next(&n->rb_node_in); __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); - hists__inc_nr_entries(hists, n); + hists__inc_stats(hists, n); } } @@ -853,7 +853,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists, he->hists = hists; rb_link_node(&he->rb_node_in, parent, p); rb_insert_color(&he->rb_node_in, root); - hists__inc_nr_entries(hists, he); + hists__inc_stats(hists, he); he->dummy = true; } out: diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 5a0343eb22e2..51478c94d976 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -116,7 +116,7 @@ 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_stats(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); size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); -- cgit v1.2.3 From ae993efc9c6bd109b027d2799a442892067e9230 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 24 Apr 2014 16:25:19 +0900 Subject: perf hists: Move column length calculation out of hists__inc_stats() It's not the part of logic of hists__inc_stats() so it'd be better to move it out of the function. Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1398327843-31845-4-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/builtin-diff.c | 3 +++ tools/perf/util/hist.c | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 0e46fa1b5ca0..c9cc771f182a 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -587,6 +587,9 @@ static void hists__compute_resort(struct hists *hists) insert_hist_entry_by_compute(&hists->entries, he, compute); hists__inc_stats(hists, he); + + if (!he->filtered) + hists__calc_col_len(hists, he); } } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 12d6c1bd761d..f5b388e50265 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -320,7 +320,6 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) void hists__inc_stats(struct hists *hists, struct hist_entry *h) { if (!h->filtered) { - hists__calc_col_len(hists, h); hists->nr_non_filtered_entries++; hists->stats.total_non_filtered_period += h->stat.period; } @@ -687,6 +686,9 @@ void hists__output_resort(struct hists *hists) __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); hists__inc_stats(hists, n); + + if (!n->filtered) + hists__calc_col_len(hists, n); } } -- cgit v1.2.3 From 9283ba9bd77a6940ecad8721429131d773c704b8 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 24 Apr 2014 16:37:26 +0900 Subject: perf hists: Add a couple of hists stat helper functions Add hists__{reset,inc}_[filter_]stats() functions to cleanup accesses to hist stats (for output). Note that number of samples in the stat is not handled here since it belongs to the input stage. Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1398327843-31845-5-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/builtin-diff.c | 5 +--- tools/perf/util/hist.c | 59 ++++++++++++++++++++++++++++++----------------- tools/perf/util/hist.h | 1 + 3 files changed, 40 insertions(+), 25 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index c9cc771f182a..52d91ac4e6c8 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -573,10 +573,7 @@ static void hists__compute_resort(struct hists *hists) hists->entries = RB_ROOT; 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_stats(hists); hists__reset_col_len(hists); while (next != NULL) { diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f5b388e50265..b675857883a2 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -317,16 +317,6 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) return he; } -void hists__inc_stats(struct hists *hists, struct hist_entry *h) -{ - if (!h->filtered) { - 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) { if (symbol_conf.exclude_other && parent == NULL) @@ -632,6 +622,35 @@ out: return ret; } +static void hists__reset_filter_stats(struct hists *hists) +{ + hists->nr_non_filtered_entries = 0; + hists->stats.total_non_filtered_period = 0; +} + +void hists__reset_stats(struct hists *hists) +{ + hists->nr_entries = 0; + hists->stats.total_period = 0; + + hists__reset_filter_stats(hists); +} + +static void hists__inc_filter_stats(struct hists *hists, struct hist_entry *h) +{ + hists->nr_non_filtered_entries++; + hists->stats.total_non_filtered_period += h->stat.period; +} + +void hists__inc_stats(struct hists *hists, struct hist_entry *h) +{ + if (!h->filtered) + hists__inc_filter_stats(hists, h); + + hists->nr_entries++; + hists->stats.total_period += h->stat.period; +} + static void __hists__insert_output_entry(struct rb_root *entries, struct hist_entry *he, u64 min_callchain_hits) @@ -675,9 +694,7 @@ void hists__output_resort(struct hists *hists) next = rb_first(root); hists->entries = RB_ROOT; - hists->nr_non_filtered_entries = 0; - hists->stats.total_period = 0; - hists->stats.total_non_filtered_period = 0; + hists__reset_stats(hists); hists__reset_col_len(hists); while (next) { @@ -699,13 +716,13 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h if (h->filtered) return; - ++hists->nr_non_filtered_entries; if (h->ms.unfolded) hists->nr_non_filtered_entries += h->nr_rows; h->row_offset = 0; - hists->stats.total_non_filtered_period += h->stat.period; + hists->stats.nr_non_filtered_samples += h->stat.nr_events; + hists__inc_filter_stats(hists, h); hists__calc_col_len(hists, h); } @@ -726,9 +743,9 @@ void hists__filter_by_dso(struct hists *hists) { struct rb_node *nd; - hists->nr_non_filtered_entries = 0; - hists->stats.total_non_filtered_period = 0; hists->stats.nr_non_filtered_samples = 0; + + hists__reset_filter_stats(hists); hists__reset_col_len(hists); for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { @@ -760,9 +777,9 @@ void hists__filter_by_thread(struct hists *hists) { struct rb_node *nd; - hists->nr_non_filtered_entries = 0; - hists->stats.total_non_filtered_period = 0; hists->stats.nr_non_filtered_samples = 0; + + hists__reset_filter_stats(hists); hists__reset_col_len(hists); for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { @@ -792,9 +809,9 @@ void hists__filter_by_symbol(struct hists *hists) { struct rb_node *nd; - hists->nr_non_filtered_entries = 0; - hists->stats.total_non_filtered_period = 0; hists->stats.nr_non_filtered_samples = 0; + + hists__reset_filter_stats(hists); 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 51478c94d976..ef1ad7a948c0 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -116,6 +116,7 @@ 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__reset_stats(struct hists *hists); void hists__inc_stats(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); -- cgit v1.2.3 From 87e90f43285f4096e9ba5fc18b05c2e04caf3fab Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 24 Apr 2014 16:44:16 +0900 Subject: perf hists: Collapse expanded callchains after filter is applied When a filter is applied a hist entry checks whether its callchain was folded and account it to the output stat. But this is rather hacky and only TUI-specific. Simply fold the callchains for the entry looks like a simpler and more generic solution IMHO. Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1398327843-31845-6-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/hist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index b675857883a2..8d5cfcc3bc63 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -716,8 +716,8 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h if (h->filtered) return; - if (h->ms.unfolded) - hists->nr_non_filtered_entries += h->nr_rows; + /* force fold unfiltered entry for simplicity */ + h->ms.unfolded = false; h->row_offset = 0; hists->stats.nr_non_filtered_samples += h->stat.nr_events; -- cgit v1.2.3 From 820bc81f4cdaac09a8f25040d3a20d86f3da292b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 22 Apr 2014 11:44:21 +0900 Subject: perf tools: Account entry stats when it's added to the output tree Currently, accounting each sample is done in multiple places - once when adding them to the input tree, other when adding them to the output tree. It's not only confusing but also can cause a subtle problem since concurrent processing like in perf top might see the updated stats before adding entries into the output tree - like seeing more (blank) lines at the end and/or slight inaccurate percentage. To fix this, only account the entries when it's moved into the output tree so that they cannot be seen prematurely. There're some exceptional cases here and there - they should be addressed separately with comments. Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1398327843-31845-7-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/builtin-annotate.c | 3 +-- tools/perf/builtin-diff.c | 13 +++++++++---- tools/perf/builtin-report.c | 24 ++++++++++-------------- tools/perf/util/hist.c | 1 - 4 files changed, 20 insertions(+), 21 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 0da603b79b61..d30d2c2e2a7a 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -46,7 +46,7 @@ struct perf_annotate { }; static int perf_evsel__add_sample(struct perf_evsel *evsel, - struct perf_sample *sample, + struct perf_sample *sample __maybe_unused, struct addr_location *al, struct perf_annotate *ann) { @@ -70,7 +70,6 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, return -ENOMEM; ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); - evsel->hists.stats.total_period += sample->period; hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); return ret; } diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 52d91ac4e6c8..f3b10dcf6838 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -341,11 +341,16 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, return -1; } - if (al.filtered == 0) { - evsel->hists.stats.total_non_filtered_period += sample->period; - evsel->hists.nr_non_filtered_entries++; - } + /* + * The total_period is updated here before going to the output + * tree since normally only the baseline hists will call + * hists__output_resort() and precompute needs the total + * period in order to sort entries by percentage delta. + */ evsel->hists.stats.total_period += sample->period; + if (!al.filtered) + evsel->hists.stats.total_non_filtered_period += sample->period; + return 0; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index aed52036468d..89c95289fd51 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -85,6 +85,16 @@ static void report__inc_stats(struct report *rep, struct hist_entry *he) */ if (he->stat.nr_events == 1) rep->nr_entries++; + + /* + * Only counts number of samples at this stage as it's more + * natural to do it here and non-sample events are also + * counted in perf_session_deliver_event(). The dump_trace + * requires this info is ready before going to the output tree. + */ + hists__inc_nr_events(he->hists, PERF_RECORD_SAMPLE); + if (!he->filtered) + he->hists->stats.nr_non_filtered_samples++; } static int report__add_mem_hist_entry(struct report *rep, struct addr_location *al, @@ -135,10 +145,6 @@ static int report__add_mem_hist_entry(struct report *rep, struct addr_location * report__inc_stats(rep, he); - 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; @@ -189,13 +195,7 @@ static int report__add_branch_hist_entry(struct report *rep, struct addr_locatio if (err) goto out; } - report__inc_stats(rep, he); - - 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; } @@ -230,10 +230,6 @@ static int report__add_hist_entry(struct report *rep, struct perf_evsel *evsel, report__inc_stats(rep, he); - 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/util/hist.c b/tools/perf/util/hist.c index 8d5cfcc3bc63..6d0d2d75db68 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -382,7 +382,6 @@ static struct hist_entry *add_hist_entry(struct hists *hists, if (!he) return NULL; - hists->nr_entries++; rb_link_node(&he->rb_node_in, parent, p); rb_insert_color(&he->rb_node_in, hists->entries_in); out: -- cgit v1.2.3 From 3186b6815d49b5e0defbd884223da3778edb59fc Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 22 Apr 2014 13:44:23 +0900 Subject: perf hists: Add missing update on filtered stats in hists__decay_entries() When a filter is used for perf top, its hists->nr_non_filtered_entries was not updated after it removed an entry in hists__decay_entries(). Also hists->stats.total_non_filtered_period was missed too. Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1398327843-31845-8-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/hist.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 6d0d2d75db68..7f0236cea4fe 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -225,14 +225,18 @@ static void he_stat__decay(struct he_stat *he_stat) static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) { u64 prev_period = he->stat.period; + u64 diff; if (prev_period == 0) return true; he_stat__decay(&he->stat); + diff = prev_period - he->stat.period; + + hists->stats.total_period -= diff; if (!he->filtered) - hists->stats.total_period -= prev_period - he->stat.period; + hists->stats.total_non_filtered_period -= diff; return he->stat.period == 0; } @@ -259,8 +263,11 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) if (sort__need_collapse) rb_erase(&n->rb_node_in, &hists->entries_collapsed); - hist_entry__free(n); --hists->nr_entries; + if (!n->filtered) + --hists->nr_non_filtered_entries; + + hist_entry__free(n); } } } -- cgit v1.2.3 From 268397cb2a47ce6e1c0298d9de1762143867f9d3 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 22 Apr 2014 14:49:31 +0900 Subject: perf top/tui: Update nr_entries properly after a filter is applied The hist_browser__reset() is only called right after a filter is applied so it needs to udpate browser->nr_entries properly. We cannot use hists->nr_non_filtered_entreis directly since it's possible that such entries are also filtered out by minimum percentage limit. In addition when a filter is used for perf top, hist browser's nr_entries field was not updated after applying the filter. But it needs to be updated as new samples are coming. Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1398327843-31845-11-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/browsers/hists.c | 20 ++++++++++++++++---- tools/perf/util/hist.h | 6 ++++++ 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 769295bf2c10..886eee0062e0 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -35,6 +35,11 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, const char *ev_name); static void hist_browser__update_nr_entries(struct hist_browser *hb); +static bool hist_browser__has_filter(struct hist_browser *hb) +{ + return hists__has_filter(hb->hists) || hb->min_pcnt; +} + static void hist_browser__refresh_dimensions(struct hist_browser *browser) { /* 3 == +/- toggle symbol before actual hist_entry rendering */ @@ -44,7 +49,8 @@ static void hist_browser__refresh_dimensions(struct hist_browser *browser) static void hist_browser__reset(struct hist_browser *browser) { - browser->b.nr_entries = browser->hists->nr_entries; + hist_browser__update_nr_entries(browser); + browser->b.nr_entries = browser->nr_non_filtered_entries; hist_browser__refresh_dimensions(browser); ui_browser__reset_index(&browser->b); } @@ -319,9 +325,10 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name, int delay_secs = hbt ? hbt->refresh : 0; browser->b.entries = &browser->hists->entries; - browser->b.nr_entries = browser->hists->nr_entries; - if (browser->min_pcnt) + if (hist_browser__has_filter(browser)) browser->b.nr_entries = browser->nr_non_filtered_entries; + else + browser->b.nr_entries = browser->hists->nr_entries; hist_browser__refresh_dimensions(browser); hists__browser_title(browser->hists, title, sizeof(title), ev_name); @@ -338,7 +345,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name, u64 nr_entries; hbt->timer(hbt->arg); - if (browser->min_pcnt) { + if (hist_browser__has_filter(browser)) { hist_browser__update_nr_entries(browser); nr_entries = browser->nr_non_filtered_entries; } else { @@ -1347,6 +1354,11 @@ static void hist_browser__update_nr_entries(struct hist_browser *hb) u64 nr_entries = 0; struct rb_node *nd = rb_first(&hb->hists->entries); + if (hb->min_pcnt == 0) { + hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; + return; + } + while ((nd = hists__filter_entries(nd, hb->hists, hb->min_pcnt)) != NULL) { nr_entries++; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ef1ad7a948c0..38c3e874c164 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -129,6 +129,12 @@ void hists__filter_by_dso(struct hists *hists); void hists__filter_by_thread(struct hists *hists); void hists__filter_by_symbol(struct hists *hists); +static inline bool hists__has_filter(struct hists *hists) +{ + return hists->thread_filter || hists->dso_filter || + hists->symbol_filter_str; +} + u16 hists__col_len(struct hists *hists, enum hist_column col); void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len); bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len); -- cgit v1.2.3 From 93d5731dcb5b8cb7fa56ee11a5891f10c96c2a45 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 21 Mar 2014 17:57:01 -0300 Subject: perf tools: Allocate thread map_groups's dynamically Moving towards sharing map groups within a process threads. Because of this we need the map groups to be dynamically allocated. No other functional change is intended in here. Based on a patch by Jiri Olsa, but this time _just_ making the conversion from statically allocating thread->mg to turning it into a pointer and instead of initializing it at thread's constructor, introduce a constructor/destructor for the map_groups class and call at thread creation time. Later we will introduce the get/put methods when we move to sharing those map_groups, when the get/put refcounting semantics will be needed. Signed-off-by: Arnaldo Carvalho de Melo Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Corey Ashford Cc: David Ahern Cc: Don Zickus Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1397490723-1992-3-git-send-email-jolsa@redhat.com Signed-off-by: Jiri Olsa --- tools/perf/arch/x86/tests/dwarf-unwind.c | 2 +- tools/perf/ui/stdio/hist.c | 2 +- tools/perf/util/event.c | 2 +- tools/perf/util/map.c | 16 ++++++++++++++++ tools/perf/util/map.h | 3 +++ tools/perf/util/thread.c | 18 ++++++++++++------ tools/perf/util/thread.h | 2 +- 7 files changed, 35 insertions(+), 10 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/arch/x86/tests/dwarf-unwind.c b/tools/perf/arch/x86/tests/dwarf-unwind.c index b8c0102c70c8..c916656259f7 100644 --- a/tools/perf/arch/x86/tests/dwarf-unwind.c +++ b/tools/perf/arch/x86/tests/dwarf-unwind.c @@ -23,7 +23,7 @@ static int sample_ustack(struct perf_sample *sample, sp = (unsigned long) regs[PERF_REG_X86_SP]; - map = map_groups__find(&thread->mg, MAP__FUNCTION, (u64) sp); + map = map_groups__find(thread->mg, MAP__FUNCTION, (u64) sp); if (!map) { pr_debug("failed to get stack map\n"); free(buf); diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index d59893edf031..9eccf7f4f367 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -495,7 +495,7 @@ print_entries: break; if (h->ms.map == NULL && verbose > 1) { - __map_groups__fprintf_maps(&h->thread->mg, + __map_groups__fprintf_maps(h->thread->mg, MAP__FUNCTION, verbose, fp); fprintf(fp, "%.10s end\n", graph_dotted_line); } diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 9d12aa6dd485..dbcaea1a8180 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -699,7 +699,7 @@ void thread__find_addr_map(struct thread *thread, enum map_type type, u64 addr, struct addr_location *al) { - struct map_groups *mg = &thread->mg; + struct map_groups *mg = thread->mg; bool load_map = false; al->machine = machine; diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 39cd2d0faff6..ae4c5e12debd 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -358,6 +358,22 @@ void map_groups__exit(struct map_groups *mg) } } +struct map_groups *map_groups__new(void) +{ + struct map_groups *mg = malloc(sizeof(*mg)); + + if (mg != NULL) + map_groups__init(mg); + + return mg; +} + +void map_groups__delete(struct map_groups *mg) +{ + map_groups__exit(mg); + free(mg); +} + void map_groups__flush(struct map_groups *mg) { int type; diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index f00f058afb3b..1073e2d8b797 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -61,6 +61,9 @@ struct map_groups { struct machine *machine; }; +struct map_groups *map_groups__new(void); +void map_groups__delete(struct map_groups *mg); + static inline struct kmap *map__kmap(struct map *map) { return (struct kmap *)(map + 1); diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 3ce0498bdae6..dc51d1632e92 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -15,7 +15,10 @@ struct thread *thread__new(pid_t pid, pid_t tid) struct thread *thread = zalloc(sizeof(*thread)); if (thread != NULL) { - map_groups__init(&thread->mg); + thread->mg = map_groups__new(); + if (thread->mg == NULL) + goto out_free; + thread->pid_ = pid; thread->tid = tid; thread->ppid = -1; @@ -37,6 +40,8 @@ struct thread *thread__new(pid_t pid, pid_t tid) return thread; err_thread: + map_groups__delete(thread->mg); +out_free: free(thread); return NULL; } @@ -45,7 +50,8 @@ void thread__delete(struct thread *thread) { struct comm *comm, *tmp; - map_groups__exit(&thread->mg); + map_groups__delete(thread->mg); + thread->mg = NULL; list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { list_del(&comm->list); comm__free(comm); @@ -111,13 +117,13 @@ int thread__comm_len(struct thread *thread) size_t thread__fprintf(struct thread *thread, FILE *fp) { return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + - map_groups__fprintf(&thread->mg, verbose, fp); + map_groups__fprintf(thread->mg, verbose, fp); } void thread__insert_map(struct thread *thread, struct map *map) { - map_groups__fixup_overlappings(&thread->mg, map, verbose, stderr); - map_groups__insert(&thread->mg, map); + map_groups__fixup_overlappings(thread->mg, map, verbose, stderr); + map_groups__insert(thread->mg, map); } int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) @@ -135,7 +141,7 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) } for (i = 0; i < MAP__NR_TYPES; ++i) - if (map_groups__clone(&thread->mg, &parent->mg, i) < 0) + if (map_groups__clone(thread->mg, parent->mg, i) < 0) return -ENOMEM; thread->ppid = parent->tid; diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 9b29f085aede..bee1eb0f73bc 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -13,7 +13,7 @@ struct thread { struct rb_node rb_node; struct list_head node; }; - struct map_groups mg; + struct map_groups *mg; pid_t pid_; /* Not all tools update this */ pid_t tid; pid_t ppid; -- cgit v1.2.3 From a26ca6716a6c683f40bd676cea7e89704653b98d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 25 Mar 2014 15:26:44 -0300 Subject: perf tools: Reference count map_groups objects We will share it among threads in the same process. Adding map_groups__get/map_groups__put interface for that. Signed-off-by: Arnaldo Carvalho de Melo Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Corey Ashford Cc: David Ahern Cc: Don Zickus Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1397490723-1992-4-git-send-email-jolsa@redhat.com Signed-off-by: Jiri Olsa --- tools/perf/util/map.c | 7 +++++++ tools/perf/util/map.h | 9 +++++++++ tools/perf/util/thread.c | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index ae4c5e12debd..ba5f5c0c838b 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -323,6 +323,7 @@ void map_groups__init(struct map_groups *mg) INIT_LIST_HEAD(&mg->removed_maps[i]); } mg->machine = NULL; + mg->refcnt = 1; } static void maps__delete(struct rb_root *maps) @@ -374,6 +375,12 @@ void map_groups__delete(struct map_groups *mg) free(mg); } +void map_groups__put(struct map_groups *mg) +{ + if (--mg->refcnt == 0) + map_groups__delete(mg); +} + void map_groups__flush(struct map_groups *mg) { int type; diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 1073e2d8b797..d6445b27d672 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -59,11 +59,20 @@ struct map_groups { struct rb_root maps[MAP__NR_TYPES]; struct list_head removed_maps[MAP__NR_TYPES]; struct machine *machine; + int refcnt; }; struct map_groups *map_groups__new(void); void map_groups__delete(struct map_groups *mg); +static inline struct map_groups *map_groups__get(struct map_groups *mg) +{ + ++mg->refcnt; + return mg; +} + +void map_groups__put(struct map_groups *mg); + static inline struct kmap *map__kmap(struct map *map) { return (struct kmap *)(map + 1); diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index dc51d1632e92..b501848a8424 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -50,7 +50,7 @@ void thread__delete(struct thread *thread) { struct comm *comm, *tmp; - map_groups__delete(thread->mg); + map_groups__put(thread->mg); thread->mg = NULL; list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { list_del(&comm->list); -- cgit v1.2.3 From cddcef607782966f1601808c17fe9c4c5f79f9f4 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 9 Apr 2014 20:54:29 +0200 Subject: perf tools: Share map_groups among threads of the same group Sharing map groups within all process threads. This way there's only one copy of mmap info and it's reachable from any thread within the process. Original-patch-by: Arnaldo Carvalho de Melo Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Corey Ashford Cc: David Ahern Cc: Don Zickus Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1397490723-1992-5-git-send-email-jolsa@redhat.com Signed-off-by: Jiri Olsa --- tools/perf/util/machine.c | 11 +++++++++++ tools/perf/util/thread.c | 48 ++++++++++++++++++++++++++++++++++------------- tools/perf/util/thread.h | 1 + 3 files changed, 47 insertions(+), 13 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index a53cd0b8c151..98ec56dc890b 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -316,6 +316,17 @@ static struct thread *__machine__findnew_thread(struct machine *machine, rb_link_node(&th->rb_node, parent, p); rb_insert_color(&th->rb_node, &machine->threads); machine->last_match = th; + + /* + * We have to initialize map_groups separately + * after rb tree is updated. + * + * The reason is that we call machine__findnew_thread + * within thread__init_map_groups to find the thread + * leader and that would screwed the rb tree. + */ + if (thread__init_map_groups(th, machine)) + return NULL; } return th; diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index b501848a8424..2fde0d5e40b5 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -8,6 +8,22 @@ #include "debug.h" #include "comm.h" +int thread__init_map_groups(struct thread *thread, struct machine *machine) +{ + struct thread *leader; + pid_t pid = thread->pid_; + + if (pid == thread->tid) { + thread->mg = map_groups__new(); + } else { + leader = machine__findnew_thread(machine, pid, pid); + if (leader) + thread->mg = map_groups__get(leader->mg); + } + + return thread->mg ? 0 : -1; +} + struct thread *thread__new(pid_t pid, pid_t tid) { char *comm_str; @@ -15,10 +31,6 @@ struct thread *thread__new(pid_t pid, pid_t tid) struct thread *thread = zalloc(sizeof(*thread)); if (thread != NULL) { - thread->mg = map_groups__new(); - if (thread->mg == NULL) - goto out_free; - thread->pid_ = pid; thread->tid = tid; thread->ppid = -1; @@ -40,8 +52,6 @@ struct thread *thread__new(pid_t pid, pid_t tid) return thread; err_thread: - map_groups__delete(thread->mg); -out_free: free(thread); return NULL; } @@ -126,9 +136,26 @@ void thread__insert_map(struct thread *thread, struct map *map) map_groups__insert(thread->mg, map); } +static int thread__clone_map_groups(struct thread *thread, + struct thread *parent) +{ + int i; + + /* This is new thread, we share map groups for process. */ + if (thread->pid_ == parent->pid_) + return 0; + + /* But this one is new process, copy maps. */ + for (i = 0; i < MAP__NR_TYPES; ++i) + if (map_groups__clone(thread->mg, parent->mg, i) < 0) + return -ENOMEM; + + return 0; +} + int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) { - int i, err; + int err; if (parent->comm_set) { const char *comm = thread__comm_str(parent); @@ -140,13 +167,8 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) thread->comm_set = true; } - for (i = 0; i < MAP__NR_TYPES; ++i) - if (map_groups__clone(thread->mg, parent->mg, i) < 0) - return -ENOMEM; - thread->ppid = parent->tid; - - return 0; + return thread__clone_map_groups(thread, parent); } void thread__find_cpumode_addr_location(struct thread *thread, diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index bee1eb0f73bc..3c0c2724f82c 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -30,6 +30,7 @@ struct machine; struct comm; struct thread *thread__new(pid_t pid, pid_t tid); +int thread__init_map_groups(struct thread *thread, struct machine *machine); void thread__delete(struct thread *thread); static inline void thread__exited(struct thread *thread) { -- cgit v1.2.3 From e148c76083dc06ce618d768c0bee0a0edda96a54 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 24 Apr 2014 22:27:32 +0900 Subject: perf tools: Handle EINTR error for readn/writen Those readn/writen functions are to ensure read/write does I/O for a given size exactly. But ion() - its implementation - does not handle in case it returns prematurely due to a signal. As it's not an error itself so just retry the operation. Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1398346054-3322-1-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/util.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 9f66549562bd..7fff6be07f07 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -166,6 +166,8 @@ static ssize_t ion(bool is_read, int fd, void *buf, size_t n) ssize_t ret = is_read ? read(fd, buf, left) : write(fd, buf, left); + if (ret < 0 && errno == EINTR) + continue; if (ret <= 0) return ret; -- cgit v1.2.3 From 2b9032e0ecb57de819bcf40b440e7cbd2d8f3a8c Mon Sep 17 00:00:00 2001 From: Alexander Yarygin Date: Fri, 25 Apr 2014 17:34:05 +0200 Subject: perf tools: Parse tracepoints with '-' in system name Trace events potentially can have a '-' in their trace system name, e.g. kvm on s390 defines kvm-s390:* tracepoints. We could not parse them, because there was no rule for this: $ sudo ./perf top -e "kvm-s390:*" invalid or unsupported event: 'kvm-s390:*' This patch adds an extra rule to event_legacy_tracepoint which handles those cases. Without the patch, perf will not accept such tracepoints in the -e option. Signed-off-by: Alexander Yarygin Tested-by: Christian Borntraeger Acked-by: Christian Borntraeger Link: http://lkml.kernel.org/r/1398440047-6641-2-git-send-email-yarygin@linux.vnet.ibm.com Signed-off-by: Jiri Olsa --- tools/perf/util/parse-events.y | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 4eb67ec333f1..ac9db9f699f3 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -299,6 +299,18 @@ PE_PREFIX_MEM PE_VALUE sep_dc } event_legacy_tracepoint: +PE_NAME '-' PE_NAME ':' PE_NAME +{ + struct parse_events_evlist *data = _data; + struct list_head *list; + char sys_name[128]; + snprintf(&sys_name, 128, "%s-%s", $1, $3); + + ALLOC_LIST(list); + ABORT_ON(parse_events_add_tracepoint(list, &data->idx, &sys_name, $5)); + $$ = list; +} +| PE_NAME ':' PE_NAME { struct parse_events_evlist *data = _data; -- cgit v1.2.3 From 88080ce7f6af1ad99ad4b2825938411975910116 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 14 Apr 2014 17:38:39 +0200 Subject: perf tools: Move u64_swap union ... to its single user's header, evsel.h. Signed-off-by: Borislav Petkov Link: http://lkml.kernel.org/r/tip-9os1chjyz12upubfsjc71d99@git.kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/evsel.h | 5 +++++ tools/perf/util/types.h | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 0c9926cfb292..0c581d0d5eb6 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -91,6 +91,11 @@ struct perf_evsel { char *group_name; }; +union u64_swap { + u64 val64; + u32 val32[2]; +}; + #define hists_to_evsel(h) container_of(h, struct perf_evsel, hists) struct cpu_map; diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h index c51fa6b70a28..5f3689a3d085 100644 --- a/tools/perf/util/types.h +++ b/tools/perf/util/types.h @@ -16,9 +16,4 @@ typedef signed short s16; typedef unsigned char u8; typedef signed char s8; -union u64_swap { - u64 val64; - u32 val32[2]; -}; - #endif /* __PERF_TYPES_H */ -- cgit v1.2.3 From 5ac3e4b6d1d8fb911bb9c497126c51b02033a412 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Sun, 23 Feb 2014 12:04:53 +0100 Subject: tools: Unify export.h So tools/ has been growing three, at a different stage of their development export.h headers and so we should unite into one. Add tools/include/ to the include path of virtio and liblockdep to pick the shared header now. Signed-off-by: Borislav Petkov Acked-by: Rusty Russell Cc: Arnaldo Carvalho de Melo Cc: Ingo Molnar Cc: Michael S. Tsirkin Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Sasha Levin Cc: virtio-dev@lists.oasis-open.org Cc: virtualization@lists.linux-foundation.org Link: http://lkml.kernel.org/r/1397493185-19521-2-git-send-email-bp@alien8.de Signed-off-by: Jiri Olsa --- tools/include/linux/export.h | 10 ++++++++++ tools/lib/lockdep/Makefile | 2 +- tools/lib/lockdep/uinclude/linux/export.h | 7 ------- tools/perf/MANIFEST | 1 + tools/perf/Makefile.perf | 2 +- tools/perf/util/include/linux/export.h | 6 ------ tools/virtio/Makefile | 2 +- tools/virtio/linux/export.h | 5 ----- 8 files changed, 14 insertions(+), 21 deletions(-) create mode 100644 tools/include/linux/export.h delete mode 100644 tools/lib/lockdep/uinclude/linux/export.h delete mode 100644 tools/perf/util/include/linux/export.h delete mode 100644 tools/virtio/linux/export.h (limited to 'tools/perf/util') diff --git a/tools/include/linux/export.h b/tools/include/linux/export.h new file mode 100644 index 000000000000..d07e586b9ba0 --- /dev/null +++ b/tools/include/linux/export.h @@ -0,0 +1,10 @@ +#ifndef _TOOLS_LINUX_EXPORT_H_ +#define _TOOLS_LINUX_EXPORT_H_ + +#define EXPORT_SYMBOL(sym) +#define EXPORT_SYMBOL_GPL(sym) +#define EXPORT_SYMBOL_GPL_FUTURE(sym) +#define EXPORT_UNUSED_SYMBOL(sym) +#define EXPORT_UNUSED_SYMBOL_GPL(sym) + +#endif diff --git a/tools/lib/lockdep/Makefile b/tools/lib/lockdep/Makefile index cb09d3ff8f58..85ef05da7453 100644 --- a/tools/lib/lockdep/Makefile +++ b/tools/lib/lockdep/Makefile @@ -105,7 +105,7 @@ N = export Q VERBOSE -INCLUDES = -I. -I/usr/local/include -I./uinclude -I./include $(CONFIG_INCLUDES) +INCLUDES = -I. -I/usr/local/include -I./uinclude -I./include -I../../include $(CONFIG_INCLUDES) # Set compile option CFLAGS if not set elsewhere CFLAGS ?= -g -DCONFIG_LOCKDEP -DCONFIG_STACKTRACE -DCONFIG_PROVE_LOCKING -DBITS_PER_LONG=__WORDSIZE -DLIBLOCKDEP_VERSION='"$(LIBLOCKDEP_VERSION)"' -rdynamic -O0 -g diff --git a/tools/lib/lockdep/uinclude/linux/export.h b/tools/lib/lockdep/uinclude/linux/export.h deleted file mode 100644 index 6bdf3492c535..000000000000 --- a/tools/lib/lockdep/uinclude/linux/export.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _LIBLOCKDEP_LINUX_EXPORT_H_ -#define _LIBLOCKDEP_LINUX_EXPORT_H_ - -#define EXPORT_SYMBOL(sym) -#define EXPORT_SYMBOL_GPL(sym) - -#endif diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index c0c87c87b60f..81783c2037fc 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -7,6 +7,7 @@ tools/lib/symbol/kallsyms.h tools/include/asm/bug.h tools/include/linux/compiler.h tools/include/linux/hash.h +tools/include/linux/export.h include/linux/const.h include/linux/perf_event.h include/linux/rbtree.h diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 5e21aad44142..bd11e8a36422 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -222,7 +222,7 @@ LIB_H += util/include/linux/const.h LIB_H += util/include/linux/ctype.h LIB_H += util/include/linux/kernel.h LIB_H += util/include/linux/list.h -LIB_H += util/include/linux/export.h +LIB_H += ../include/linux/export.h LIB_H += util/include/linux/poison.h LIB_H += util/include/linux/rbtree.h LIB_H += util/include/linux/rbtree_augmented.h diff --git a/tools/perf/util/include/linux/export.h b/tools/perf/util/include/linux/export.h deleted file mode 100644 index b43e2dc21e04..000000000000 --- a/tools/perf/util/include/linux/export.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef PERF_LINUX_MODULE_H -#define PERF_LINUX_MODULE_H - -#define EXPORT_SYMBOL(name) - -#endif diff --git a/tools/virtio/Makefile b/tools/virtio/Makefile index 3187c62d9814..9325f4693821 100644 --- a/tools/virtio/Makefile +++ b/tools/virtio/Makefile @@ -3,7 +3,7 @@ test: virtio_test vringh_test virtio_test: virtio_ring.o virtio_test.o vringh_test: vringh_test.o vringh.o virtio_ring.o -CFLAGS += -g -O2 -Wall -I. -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE +CFLAGS += -g -O2 -Wall -I. -I../include/ -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE vpath %.c ../../drivers/virtio ../../drivers/vhost mod: ${MAKE} -C `pwd`/../.. M=`pwd`/vhost_test diff --git a/tools/virtio/linux/export.h b/tools/virtio/linux/export.h deleted file mode 100644 index 7311d326894a..000000000000 --- a/tools/virtio/linux/export.h +++ /dev/null @@ -1,5 +0,0 @@ -#define EXPORT_SYMBOL(sym) -#define EXPORT_SYMBOL_GPL(sym) -#define EXPORT_SYMBOL_GPL_FUTURE(sym) -#define EXPORT_UNUSED_SYMBOL(sym) -#define EXPORT_UNUSED_SYMBOL_GPL(sym) -- cgit v1.2.3 From d944c4eebcf4c0d5e5d9728fec110cbf0047ad7f Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 25 Apr 2014 21:31:02 +0200 Subject: tools: Consolidate types.h Combine all definitions into a common tools/include/linux/types.h and kill the wild growth elsewhere. Move DECLARE_BITMAP to its proper bitmap.h header. Signed-off-by: Borislav Petkov Acked-by: Rusty Russell Link: http://lkml.kernel.org/n/tip-azczs7qcv6h9xek9od10hiv2@git.kernel.org Signed-off-by: Jiri Olsa --- tools/include/linux/types.h | 75 +++++++++++++++++++++++++++++++ tools/lib/lockdep/uinclude/linux/types.h | 58 ------------------------ tools/perf/MANIFEST | 1 + tools/perf/Makefile.perf | 3 +- tools/perf/arch/x86/include/perf_regs.h | 2 +- tools/perf/arch/x86/util/tsc.c | 2 +- tools/perf/arch/x86/util/tsc.h | 2 +- tools/perf/perf.h | 4 +- tools/perf/tests/attr.c | 7 --- tools/perf/tests/code-reading.c | 3 +- tools/perf/tests/dso-data.c | 2 +- tools/perf/tests/dwarf-unwind.c | 2 +- tools/perf/tests/keep-tracking.c | 2 +- tools/perf/tests/parse-no-sample-id-all.c | 2 +- tools/perf/tests/perf-time-to-tsc.c | 3 +- tools/perf/tests/rdpmc.c | 2 +- tools/perf/tests/sample-parsing.c | 2 +- tools/perf/ui/browser.h | 4 +- tools/perf/ui/progress.h | 2 +- tools/perf/util/annotate.h | 2 +- tools/perf/util/build-id.h | 2 +- tools/perf/util/dso.h | 2 +- tools/perf/util/evsel.h | 4 +- tools/perf/util/header.h | 4 +- tools/perf/util/include/linux/bitmap.h | 3 ++ tools/perf/util/include/linux/list.h | 1 + tools/perf/util/include/linux/types.h | 29 ------------ tools/perf/util/map.h | 2 +- tools/perf/util/parse-events.h | 3 +- tools/perf/util/parse-events.y | 2 +- tools/perf/util/perf_regs.h | 2 +- tools/perf/util/pmu.h | 2 +- tools/perf/util/stat.h | 2 +- tools/perf/util/svghelper.c | 2 +- tools/perf/util/svghelper.h | 2 +- tools/perf/util/top.h | 2 +- tools/perf/util/types.h | 19 -------- tools/perf/util/unwind-libdw.c | 2 +- tools/perf/util/unwind.h | 2 +- tools/perf/util/util.h | 2 +- tools/perf/util/values.h | 2 +- tools/virtio/linux/kernel.h | 7 --- tools/virtio/linux/types.h | 28 ------------ 43 files changed, 115 insertions(+), 191 deletions(-) create mode 100644 tools/include/linux/types.h delete mode 100644 tools/lib/lockdep/uinclude/linux/types.h delete mode 100644 tools/perf/util/include/linux/types.h delete mode 100644 tools/perf/util/types.h delete mode 100644 tools/virtio/linux/types.h (limited to 'tools/perf/util') diff --git a/tools/include/linux/types.h b/tools/include/linux/types.h new file mode 100644 index 000000000000..b5cf25e05df2 --- /dev/null +++ b/tools/include/linux/types.h @@ -0,0 +1,75 @@ +#ifndef _TOOLS_LINUX_TYPES_H_ +#define _TOOLS_LINUX_TYPES_H_ + +#include +#include +#include + +#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */ +#include + +struct page; +struct kmem_cache; + +typedef enum { + GFP_KERNEL, + GFP_ATOMIC, + __GFP_HIGHMEM, + __GFP_HIGH +} gfp_t; + +/* + * We define u64 as uint64_t for every architecture + * so that we can print it with "%"PRIx64 without getting warnings. + * + * typedef __u64 u64; + * typedef __s64 s64; + */ +typedef uint64_t u64; +typedef int64_t s64; + +typedef __u32 u32; +typedef __s32 s32; + +typedef __u16 u16; +typedef __s16 s16; + +typedef __u8 u8; +typedef __s8 s8; + +#ifdef __CHECKER__ +#define __bitwise__ __attribute__((bitwise)) +#else +#define __bitwise__ +#endif +#ifdef __CHECK_ENDIAN__ +#define __bitwise __bitwise__ +#else +#define __bitwise +#endif + +#define __force +#define __user +#define __must_check +#define __cold + +typedef __u16 __bitwise __le16; +typedef __u16 __bitwise __be16; +typedef __u32 __bitwise __le32; +typedef __u32 __bitwise __be32; +typedef __u64 __bitwise __le64; +typedef __u64 __bitwise __be64; + +struct list_head { + struct list_head *next, *prev; +}; + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#endif /* _TOOLS_LINUX_TYPES_H_ */ diff --git a/tools/lib/lockdep/uinclude/linux/types.h b/tools/lib/lockdep/uinclude/linux/types.h deleted file mode 100644 index 929938f426de..000000000000 --- a/tools/lib/lockdep/uinclude/linux/types.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef _LIBLOCKDEP_LINUX_TYPES_H_ -#define _LIBLOCKDEP_LINUX_TYPES_H_ - -#include -#include - -#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */ -#include - -struct page; -struct kmem_cache; - -typedef unsigned gfp_t; - -typedef __u64 u64; -typedef __s64 s64; - -typedef __u32 u32; -typedef __s32 s32; - -typedef __u16 u16; -typedef __s16 s16; - -typedef __u8 u8; -typedef __s8 s8; - -#ifdef __CHECKER__ -#define __bitwise__ __attribute__((bitwise)) -#else -#define __bitwise__ -#endif -#ifdef __CHECK_ENDIAN__ -#define __bitwise __bitwise__ -#else -#define __bitwise -#endif - - -typedef __u16 __bitwise __le16; -typedef __u16 __bitwise __be16; -typedef __u32 __bitwise __le32; -typedef __u32 __bitwise __be32; -typedef __u64 __bitwise __le64; -typedef __u64 __bitwise __be64; - -struct list_head { - struct list_head *next, *prev; -}; - -struct hlist_head { - struct hlist_node *first; -}; - -struct hlist_node { - struct hlist_node *next, **pprev; -}; - -#endif diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 81783c2037fc..45da209b6ed3 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -8,6 +8,7 @@ tools/include/asm/bug.h tools/include/linux/compiler.h tools/include/linux/hash.h tools/include/linux/export.h +tools/include/linux/types.h include/linux/const.h include/linux/perf_event.h include/linux/rbtree.h diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index bd11e8a36422..2baf61cec7ff 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -227,7 +227,7 @@ LIB_H += util/include/linux/poison.h LIB_H += util/include/linux/rbtree.h LIB_H += util/include/linux/rbtree_augmented.h LIB_H += util/include/linux/string.h -LIB_H += util/include/linux/types.h +LIB_H += ../include/linux/types.h LIB_H += util/include/linux/linkage.h LIB_H += util/include/asm/asm-offsets.h LIB_H += ../include/asm/bug.h @@ -252,7 +252,6 @@ LIB_H += util/event.h LIB_H += util/evsel.h LIB_H += util/evlist.h LIB_H += util/exec_cmd.h -LIB_H += util/types.h LIB_H += util/levenshtein.h LIB_H += util/machine.h LIB_H += util/map.h diff --git a/tools/perf/arch/x86/include/perf_regs.h b/tools/perf/arch/x86/include/perf_regs.h index fc819ca34a7e..7df517acfef8 100644 --- a/tools/perf/arch/x86/include/perf_regs.h +++ b/tools/perf/arch/x86/include/perf_regs.h @@ -2,7 +2,7 @@ #define ARCH_PERF_REGS_H #include -#include "../../util/types.h" +#include #include void perf_regs_load(u64 *regs); diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c index b2519e49424f..40021fa3129b 100644 --- a/tools/perf/arch/x86/util/tsc.c +++ b/tools/perf/arch/x86/util/tsc.c @@ -4,7 +4,7 @@ #include #include "../../perf.h" -#include "../../util/types.h" +#include #include "../../util/debug.h" #include "tsc.h" diff --git a/tools/perf/arch/x86/util/tsc.h b/tools/perf/arch/x86/util/tsc.h index a24dec81c795..2affe0366b59 100644 --- a/tools/perf/arch/x86/util/tsc.h +++ b/tools/perf/arch/x86/util/tsc.h @@ -1,7 +1,7 @@ #ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ #define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ -#include "../../util/types.h" +#include struct perf_tsc_conversion { u16 time_shift; diff --git a/tools/perf/perf.h b/tools/perf/perf.h index ebdad3376c67..5aa8ac45082f 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -173,9 +173,8 @@ #include #include +#include #include -#include "util/types.h" -#include /* * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all @@ -202,7 +201,6 @@ static inline unsigned long long rdclock(void) /* * Pick up some kernel type conventions: */ -#define __user #define asmlinkage #define unlikely(x) __builtin_expect(!!(x), 0) diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index 00218f503b2e..2dfc9ad0e6f2 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -1,4 +1,3 @@ - /* * The struct perf_event_attr test support. * @@ -19,14 +18,8 @@ * permissions. All the event text files are stored there. */ -/* - * Powerpc needs __SANE_USERSPACE_TYPES__ before to select - * 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu. - */ -#define __SANE_USERSPACE_TYPES__ #include #include -#include #include #include #include "../perf.h" diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index bfb186900ac0..adf3de3e38d6 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -1,8 +1,7 @@ -#include +#include #include #include #include -#include #include #include diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c index 9cc81a3eb9b4..3e6cb171e3d3 100644 --- a/tools/perf/tests/dso-data.c +++ b/tools/perf/tests/dso-data.c @@ -1,7 +1,7 @@ #include "util.h" #include -#include +#include #include #include #include diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index c059ee81c038..108f0cd49f4e 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c @@ -1,5 +1,5 @@ #include -#include +#include #include #include "tests.h" #include "debug.h" diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c index 497957f269d8..7a5ab7b0b8f6 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c @@ -1,4 +1,4 @@ -#include +#include #include #include diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/parse-no-sample-id-all.c index e117b6c6a248..905019f9b740 100644 --- a/tools/perf/tests/parse-no-sample-id-all.c +++ b/tools/perf/tests/parse-no-sample-id-all.c @@ -1,4 +1,4 @@ -#include +#include #include #include "tests.h" diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-time-to-tsc.c index 47146d388dbf..3b7cd4d32dcb 100644 --- a/tools/perf/tests/perf-time-to-tsc.c +++ b/tools/perf/tests/perf-time-to-tsc.c @@ -1,7 +1,6 @@ #include -#include #include -#include +#include #include #include "parse-events.h" diff --git a/tools/perf/tests/rdpmc.c b/tools/perf/tests/rdpmc.c index 46649c25fa5e..e59143fd9e71 100644 --- a/tools/perf/tests/rdpmc.c +++ b/tools/perf/tests/rdpmc.c @@ -2,7 +2,7 @@ #include #include #include -#include "types.h" +#include #include "perf.h" #include "debug.h" #include "tests.h" diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index 0014d3c8c21c..7ae8d17db3d9 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c @@ -1,5 +1,5 @@ #include -#include +#include #include "util.h" #include "event.h" diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index 118cca29dd26..03d4d6295f10 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -1,9 +1,7 @@ #ifndef _PERF_UI_BROWSER_H_ #define _PERF_UI_BROWSER_H_ 1 -#include -#include -#include "../types.h" +#include #define HE_COLORSET_TOP 50 #define HE_COLORSET_MEDIUM 51 diff --git a/tools/perf/ui/progress.h b/tools/perf/ui/progress.h index 29ec8efffefb..f34f89eb607c 100644 --- a/tools/perf/ui/progress.h +++ b/tools/perf/ui/progress.h @@ -1,7 +1,7 @@ #ifndef _PERF_UI_PROGRESS_H_ #define _PERF_UI_PROGRESS_H_ 1 -#include <../types.h> +#include void ui_progress__finish(void); diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 56ad4f5287de..112d6e268150 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -3,7 +3,7 @@ #include #include -#include "types.h" +#include #include "symbol.h" #include "hist.h" #include "sort.h" diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index 845ef865eced..ae392561470b 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -4,7 +4,7 @@ #define BUILD_ID_SIZE 20 #include "tool.h" -#include "types.h" +#include extern struct perf_tool build_id__mark_dso_hit_ops; struct dso; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index ab06f1c03655..38efe95a7fdd 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -4,7 +4,7 @@ #include #include #include -#include "types.h" +#include #include "map.h" #include "build-id.h" diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 0c581d0d5eb6..a52e9a5bb2d0 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -5,12 +5,12 @@ #include #include #include -#include "types.h" +#include #include "xyarray.h" #include "cgroup.h" #include "hist.h" #include "symbol.h" - + struct perf_counts_values { union { struct { diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index a2d047bdf4ef..d08cfe499404 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -4,10 +4,10 @@ #include #include #include -#include "types.h" +#include +#include #include "event.h" -#include enum { HEADER_RESERVED = 0, /* always cleared */ diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index bb162e40c76c..01ffd12dc791 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h @@ -4,6 +4,9 @@ #include #include +#define DECLARE_BITMAP(name,bits) \ + unsigned long name[BITS_TO_LONGS(bits)] + int __bitmap_weight(const unsigned long *bitmap, int bits); void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, const unsigned long *bitmap2, int bits); diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h index bfe0a2afd0d2..76ddbc726343 100644 --- a/tools/perf/util/include/linux/list.h +++ b/tools/perf/util/include/linux/list.h @@ -1,4 +1,5 @@ #include +#include #include "../../../../include/linux/list.h" diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h deleted file mode 100644 index eb464786c084..000000000000 --- a/tools/perf/util/include/linux/types.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef _PERF_LINUX_TYPES_H_ -#define _PERF_LINUX_TYPES_H_ - -#include - -#ifndef __bitwise -#define __bitwise -#endif - -#ifndef __le32 -typedef __u32 __bitwise __le32; -#endif - -#define DECLARE_BITMAP(name,bits) \ - unsigned long name[BITS_TO_LONGS(bits)] - -struct list_head { - struct list_head *next, *prev; -}; - -struct hlist_head { - struct hlist_node *first; -}; - -struct hlist_node { - struct hlist_node *next, **pprev; -}; - -#endif diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index d6445b27d672..ae2d45110588 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -6,7 +6,7 @@ #include #include #include -#include "types.h" +#include enum map_type { MAP__FUNCTION = 0, diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index f1cb4c4b3c70..df094b4ed5ed 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -6,9 +6,8 @@ #include #include -#include "types.h" +#include #include -#include "types.h" struct list_head; struct perf_evsel; diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index ac9db9f699f3..0bc87ba46bf3 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -9,7 +9,7 @@ #include #include -#include "types.h" +#include #include "util.h" #include "parse-events.h" #include "parse-events-bison.h" diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index d6e8b6a8d7f3..79c78f74e0cf 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -1,7 +1,7 @@ #ifndef __PERF_REGS_H #define __PERF_REGS_H -#include "types.h" +#include #include "event.h" #ifdef HAVE_PERF_REGS_SUPPORT diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 8b64125a9281..c14a543ce1f3 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -1,7 +1,7 @@ #ifndef __PMU_H #define __PMU_H -#include +#include #include #include diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index ae8ccd7227cf..5667fc3e39cf 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -1,7 +1,7 @@ #ifndef __PERF_STATS_H #define __PERF_STATS_H -#include "types.h" +#include struct stats { diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index 43262b83c541..6a0a13d07a28 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include "perf.h" #include "svghelper.h" diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h index f7b4d6e699ea..e3aff5332e30 100644 --- a/tools/perf/util/svghelper.h +++ b/tools/perf/util/svghelper.h @@ -1,7 +1,7 @@ #ifndef __PERF_SVGHELPER_H #define __PERF_SVGHELPER_H -#include "types.h" +#include extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end); extern void svg_box(int Yslot, u64 start, u64 end, const char *type); diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index dab14d0ad3d0..f92c37abb0a8 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -2,7 +2,7 @@ #define __PERF_TOP_H 1 #include "tool.h" -#include "types.h" +#include #include #include #include diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h deleted file mode 100644 index 5f3689a3d085..000000000000 --- a/tools/perf/util/types.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __PERF_TYPES_H -#define __PERF_TYPES_H - -#include - -/* - * We define u64 as uint64_t for every architecture - * so that we can print it with "%"PRIx64 without getting warnings. - */ -typedef uint64_t u64; -typedef int64_t s64; -typedef unsigned int u32; -typedef signed int s32; -typedef unsigned short u16; -typedef signed short s16; -typedef unsigned char u8; -typedef signed char s8; - -#endif /* __PERF_TYPES_H */ diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 67db73ec3dab..5ec80a575b50 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -7,7 +7,7 @@ #include "unwind-libdw.h" #include "machine.h" #include "thread.h" -#include "types.h" +#include #include "event.h" #include "perf_regs.h" diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index b031316f221a..f03061260b4e 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -1,7 +1,7 @@ #ifndef __UNWIND_H #define __UNWIND_H -#include "types.h" +#include #include "event.h" #include "symbol.h" diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 6995d66f225c..b03da44e94e4 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -69,7 +69,7 @@ #include #include #include -#include "types.h" +#include #include #include #include diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h index 2fa967e1a88a..b21a80c6cf8d 100644 --- a/tools/perf/util/values.h +++ b/tools/perf/util/values.h @@ -1,7 +1,7 @@ #ifndef __PERF_VALUES_H #define __PERF_VALUES_H -#include "types.h" +#include struct perf_read_values { int threads; diff --git a/tools/virtio/linux/kernel.h b/tools/virtio/linux/kernel.h index fba705963968..1e8ce6979c1e 100644 --- a/tools/virtio/linux/kernel.h +++ b/tools/virtio/linux/kernel.h @@ -38,13 +38,6 @@ struct page { #define __printf(a,b) __attribute__((format(printf,a,b))) -typedef enum { - GFP_KERNEL, - GFP_ATOMIC, - __GFP_HIGHMEM, - __GFP_HIGH -} gfp_t; - #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) extern void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end; diff --git a/tools/virtio/linux/types.h b/tools/virtio/linux/types.h deleted file mode 100644 index f8ebb9a2b3d6..000000000000 --- a/tools/virtio/linux/types.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef TYPES_H -#define TYPES_H -#include - -#define __force -#define __user -#define __must_check -#define __cold - -typedef uint64_t u64; -typedef int64_t s64; -typedef uint32_t u32; -typedef int32_t s32; -typedef uint16_t u16; -typedef int16_t s16; -typedef uint8_t u8; -typedef int8_t s8; - -typedef uint64_t __u64; -typedef int64_t __s64; -typedef uint32_t __u32; -typedef int32_t __s32; -typedef uint16_t __u16; -typedef int16_t __s16; -typedef uint8_t __u8; -typedef int8_t __s8; - -#endif /* TYPES_H */ -- cgit v1.2.3 From 0776eb59354f8b848e5738a612c272d8f48de9bf Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 5 May 2014 12:41:45 +0200 Subject: perf tools: Move sample data structures from perf.h Into util/event.h header where all sample data structures are defined. Acked-by: Arnaldo Carvalho de Melo Acked-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1399293219-8732-7-git-send-email-jolsa@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/perf.h | 24 ------------------------ tools/perf/util/event.h | 24 ++++++++++++++++++++++++ tools/perf/util/symbol.h | 1 + 3 files changed, 25 insertions(+), 24 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 1bf95374a7d6..cf8f2281ad85 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -214,30 +214,6 @@ sys_perf_event_open(struct perf_event_attr *attr, #define MAX_NR_CPUS 256 -struct ip_callchain { - u64 nr; - u64 ips[0]; -}; - -struct branch_flags { - u64 mispred:1; - u64 predicted:1; - u64 in_tx:1; - u64 abort:1; - u64 reserved:60; -}; - -struct branch_entry { - u64 from; - u64 to; - struct branch_flags flags; -}; - -struct branch_stack { - u64 nr; - struct branch_entry entries[0]; -}; - extern const char *input_name; extern bool perf_host, perf_guest; extern const char perf_version_string[]; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 38457d447a13..d970232cb270 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -112,6 +112,30 @@ struct sample_read { }; }; +struct ip_callchain { + u64 nr; + u64 ips[0]; +}; + +struct branch_flags { + u64 mispred:1; + u64 predicted:1; + u64 in_tx:1; + u64 abort:1; + u64 reserved:60; +}; + +struct branch_entry { + u64 from; + u64 to; + struct branch_flags flags; +}; + +struct branch_stack { + u64 nr; + struct branch_entry entries[0]; +}; + struct perf_sample { u64 ip; u32 pid, tid; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index ae94e006a52d..33ede53fa6b9 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -12,6 +12,7 @@ #include #include #include "build-id.h" +#include "event.h" #ifdef HAVE_LIBELF_SUPPORT #include -- cgit v1.2.3 From 2c83bc08e305e135b954d82596864b7d024fc7fd Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 5 May 2014 12:46:17 +0200 Subject: perf tools: Move perf_call_graph_mode enum from perf.h Into util/callchain.h header where all callchain related structures should be. Acked-by: Arnaldo Carvalho de Melo Acked-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1399293219-8732-8-git-send-email-jolsa@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/perf.h | 7 ------- tools/perf/util/callchain.h | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/perf.h b/tools/perf/perf.h index cf8f2281ad85..d51a994f2e64 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -222,13 +222,6 @@ void pthread__unblock_sigwinch(void); #include "util/target.h" -enum perf_call_graph_mode { - CALLCHAIN_NONE, - CALLCHAIN_FP, - CALLCHAIN_DWARF, - CALLCHAIN_MAX -}; - struct record_opts { struct target target; int call_graph; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index dda4cf8b534c..bde2b0cc24cf 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -7,6 +7,13 @@ #include "event.h" #include "symbol.h" +enum perf_call_graph_mode { + CALLCHAIN_NONE, + CALLCHAIN_FP, + CALLCHAIN_DWARF, + CALLCHAIN_MAX +}; + enum chain_mode { CHAIN_NONE, CHAIN_FLAT, -- cgit v1.2.3 From 13ce34df11833482cd698331fdbb3f8ced06340d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 12 May 2014 09:56:42 +0900 Subject: perf tools: Use tid for finding thread I believe that passing pid (instead of tid) as the 3rd arg of the machine__find*_thread() was to find a main thread so that it can search proper map group for symbols. However with the map sharing patch applied, it now can do it in any thread. It fixes a bug when each thread has different name, it only reports a main thread for samples in other threads. Cc: Adrian Hunter Acked-by: David Ahern Acked-by: Stephane Eranian Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1399856202-26221-1-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/builtin-inject.c | 2 +- tools/perf/builtin-kmem.c | 2 +- tools/perf/tests/code-reading.c | 2 +- tools/perf/tests/hists_filter.c | 1 + tools/perf/tests/hists_link.c | 2 ++ tools/perf/util/build-id.c | 2 +- tools/perf/util/event.c | 2 +- 7 files changed, 8 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 3a7387551369..6a3af0013d68 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -209,7 +209,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool, cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - thread = machine__findnew_thread(machine, sample->pid, sample->pid); + thread = machine__findnew_thread(machine, sample->pid, sample->tid); if (thread == NULL) { pr_err("problem processing %d event, skipping it.\n", event->header.type); diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index f91fa4376f4b..bef3376bfaf3 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -235,7 +235,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, struct machine *machine) { struct thread *thread = machine__findnew_thread(machine, sample->pid, - sample->pid); + sample->tid); if (thread == NULL) { pr_debug("problem processing %d event, skipping it.\n", diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index adf3de3e38d6..67f2d6323558 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -256,7 +256,7 @@ static int process_sample_event(struct machine *machine, return -1; } - thread = machine__findnew_thread(machine, sample.pid, sample.pid); + thread = machine__findnew_thread(machine, sample.pid, sample.tid); if (!thread) { pr_debug("machine__findnew_thread failed\n"); return -1; diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 23dc2f4d12c3..4617a8bee29b 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c @@ -69,6 +69,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) evsel->hists.symbol_filter_str = NULL; sample.pid = fake_samples[i].pid; + sample.tid = fake_samples[i].pid; sample.ip = fake_samples[i].ip; if (perf_event__preprocess_sample(&event, machine, &al, diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index e42d6790811a..b009bbf440d9 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -81,6 +81,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) }; sample.pid = fake_common_samples[k].pid; + sample.tid = fake_common_samples[k].pid; sample.ip = fake_common_samples[k].ip; if (perf_event__preprocess_sample(&event, machine, &al, &sample) < 0) @@ -104,6 +105,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) }; sample.pid = fake_samples[i][k].pid; + sample.tid = fake_samples[i][k].pid; sample.ip = fake_samples[i][k].ip; if (perf_event__preprocess_sample(&event, machine, &al, &sample) < 0) diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 6baabe63182b..a904a4cfe7d3 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -25,7 +25,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, struct addr_location al; u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; struct thread *thread = machine__findnew_thread(machine, sample->pid, - sample->pid); + sample->tid); if (thread == NULL) { pr_err("problem processing %d event, skipping it.\n", diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index dbcaea1a8180..65795b835b39 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -788,7 +788,7 @@ int perf_event__preprocess_sample(const union perf_event *event, { u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; struct thread *thread = machine__findnew_thread(machine, sample->pid, - sample->pid); + sample->tid); if (thread == NULL) return -1; -- cgit v1.2.3 From c5765ece8a050836c6255e1276fc8e0e867078da Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Thu, 15 May 2014 02:13:38 +0900 Subject: perf session: Fix possible null pointer dereference in session.c cppcheck detected following warning: [tools/perf/util/session.c:1628] -> [tools/perf/util/session.c:1632]: (warning) Possible null pointer dereference: session - otherwise it is redundant to check it against null. In order to avoide null pointer, check the pointer before use. Signed-off-by: Masanari Iida Link: http://lkml.kernel.org/r/1400087618-13628-1-git-send-email-standby24x7@gmail.com Signed-off-by: Jiri Olsa --- tools/perf/util/session.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 55960f22233c..64a186edc7be 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1625,13 +1625,14 @@ out_delete_map: void perf_session__fprintf_info(struct perf_session *session, FILE *fp, bool full) { - int fd = perf_data_file__fd(session->file); struct stat st; - int ret; + int fd, ret; if (session == NULL || fp == NULL) return; + fd = perf_data_file__fd(session->file); + ret = fstat(fd, &st); if (ret == -1) return; -- cgit v1.2.3 From bc18b7f2e3ca09b360b26c25a7541ba6f170111b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 3 Mar 2014 10:59:57 +0900 Subject: perf tools: Add ->cmp(), ->collapse() and ->sort() to perf_hpp_fmt Those function pointers will be used to sort report output based on the selected fields. This is a preparation of later change. Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Link: http://lkml.kernel.org/r/1400480762-22852-2-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/hist.c | 39 +++++++++++++++++++++++++++++++++++---- tools/perf/util/hist.h | 3 +++ 2 files changed, 38 insertions(+), 4 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 0912805c08f4..d4a4f2e7eb43 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -192,6 +192,14 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ hpp_entry_scnprintf, true); \ } +#define __HPP_SORT_FN(_type, _field) \ +static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ +{ \ + s64 __a = he_get_##_field(a); \ + s64 __b = he_get_##_field(b); \ + return __a - __b; \ +} + #define __HPP_ENTRY_RAW_FN(_type, _field) \ static u64 he_get_raw_##_field(struct hist_entry *he) \ { \ @@ -206,16 +214,27 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ hpp_entry_scnprintf, false); \ } +#define __HPP_SORT_RAW_FN(_type, _field) \ +static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ +{ \ + s64 __a = he_get_raw_##_field(a); \ + s64 __b = he_get_raw_##_field(b); \ + return __a - __b; \ +} + + #define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \ __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ __HPP_COLOR_PERCENT_FN(_type, _field) \ -__HPP_ENTRY_PERCENT_FN(_type, _field) +__HPP_ENTRY_PERCENT_FN(_type, _field) \ +__HPP_SORT_FN(_type, _field) #define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \ __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ -__HPP_ENTRY_RAW_FN(_type, _field) +__HPP_ENTRY_RAW_FN(_type, _field) \ +__HPP_SORT_RAW_FN(_type, _field) HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) @@ -227,19 +246,31 @@ HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) HPP_RAW_FNS(period, "Period", period, 12, 12) +static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused, + struct hist_entry *b __maybe_unused) +{ + return 0; +} + #define HPP__COLOR_PRINT_FNS(_name) \ { \ .header = hpp__header_ ## _name, \ .width = hpp__width_ ## _name, \ .color = hpp__color_ ## _name, \ - .entry = hpp__entry_ ## _name \ + .entry = hpp__entry_ ## _name, \ + .cmp = hpp__nop_cmp, \ + .collapse = hpp__nop_cmp, \ + .sort = hpp__sort_ ## _name, \ } #define HPP__PRINT_FNS(_name) \ { \ .header = hpp__header_ ## _name, \ .width = hpp__width_ ## _name, \ - .entry = hpp__entry_ ## _name \ + .entry = hpp__entry_ ## _name, \ + .cmp = hpp__nop_cmp, \ + .collapse = hpp__nop_cmp, \ + .sort = hpp__sort_ ## _name, \ } struct perf_hpp_fmt perf_hpp__format[] = { diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 38c3e874c164..36dbe00e3cc8 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -160,6 +160,9 @@ struct perf_hpp_fmt { struct hist_entry *he); int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct hist_entry *he); + int64_t (*cmp)(struct hist_entry *a, struct hist_entry *b); + int64_t (*collapse)(struct hist_entry *a, struct hist_entry *b); + int64_t (*sort)(struct hist_entry *a, struct hist_entry *b); struct list_head list; }; -- cgit v1.2.3 From 8b536999cd75e565125c74b2cf2a746d4f053a92 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 3 Mar 2014 11:46:55 +0900 Subject: perf tools: Convert sort entries to hpp formats This is a preparation of consolidating management of output field and sort keys. Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Link: http://lkml.kernel.org/r/1400480762-22852-3-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/hist.c | 6 ++++ tools/perf/util/hist.h | 6 ++++ tools/perf/util/sort.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 88 insertions(+), 4 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index d4a4f2e7eb43..a6eea666b443 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -284,6 +284,7 @@ struct perf_hpp_fmt perf_hpp__format[] = { }; LIST_HEAD(perf_hpp__list); +LIST_HEAD(perf_hpp__sort_list); #undef HPP__COLOR_PRINT_FNS @@ -325,6 +326,11 @@ void perf_hpp__column_register(struct perf_hpp_fmt *format) list_add_tail(&format->list, &perf_hpp__list); } +void perf_hpp__register_sort_field(struct perf_hpp_fmt *format) +{ + list_add_tail(&format->sort_list, &perf_hpp__sort_list); +} + void perf_hpp__column_enable(unsigned col) { BUG_ON(col >= PERF_HPP__MAX_INDEX); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 36dbe00e3cc8..eee154a41723 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -165,13 +165,18 @@ struct perf_hpp_fmt { int64_t (*sort)(struct hist_entry *a, struct hist_entry *b); struct list_head list; + struct list_head sort_list; }; extern struct list_head perf_hpp__list; +extern struct list_head perf_hpp__sort_list; #define perf_hpp__for_each_format(format) \ list_for_each_entry(format, &perf_hpp__list, list) +#define perf_hpp__for_each_sort_list(format) \ + list_for_each_entry(format, &perf_hpp__sort_list, sort_list) + extern struct perf_hpp_fmt perf_hpp__format[]; enum { @@ -190,6 +195,7 @@ enum { void perf_hpp__init(void); void perf_hpp__column_register(struct perf_hpp_fmt *format); void perf_hpp__column_enable(unsigned col); +void perf_hpp__register_sort_field(struct perf_hpp_fmt *format); typedef u64 (*hpp_field_fn)(struct hist_entry *he); typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 635cd8f8b22e..b2829f947053 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -2,6 +2,7 @@ #include "hist.h" #include "comm.h" #include "symbol.h" +#include "evsel.h" regex_t parent_regex; const char default_parent_pattern[] = "^sys_|^do_page_fault"; @@ -1027,10 +1028,80 @@ static struct sort_dimension memory_sort_dimensions[] = { #undef DIM -static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) +struct hpp_sort_entry { + struct perf_hpp_fmt hpp; + struct sort_entry *se; +}; + +static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct perf_evsel *evsel) +{ + struct hpp_sort_entry *hse; + size_t len; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); + len = hists__col_len(&evsel->hists, hse->se->se_width_idx); + + return scnprintf(hpp->buf, hpp->size, "%*s", len, hse->se->se_header); +} + +static int __sort__hpp_width(struct perf_hpp_fmt *fmt, + struct perf_hpp *hpp __maybe_unused, + struct perf_evsel *evsel) +{ + struct hpp_sort_entry *hse; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); + + return hists__col_len(&evsel->hists, hse->se->se_width_idx); +} + +static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct hist_entry *he) +{ + struct hpp_sort_entry *hse; + size_t len; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); + len = hists__col_len(he->hists, hse->se->se_width_idx); + + return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); +} + +static int __sort_dimension__add_hpp(struct sort_dimension *sd) +{ + struct hpp_sort_entry *hse; + + hse = malloc(sizeof(*hse)); + if (hse == NULL) { + pr_err("Memory allocation failed\n"); + return -1; + } + + hse->se = sd->entry; + hse->hpp.header = __sort__hpp_header; + hse->hpp.width = __sort__hpp_width; + hse->hpp.entry = __sort__hpp_entry; + hse->hpp.color = NULL; + + hse->hpp.cmp = sd->entry->se_cmp; + hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp; + hse->hpp.sort = hse->hpp.collapse; + + INIT_LIST_HEAD(&hse->hpp.list); + INIT_LIST_HEAD(&hse->hpp.sort_list); + + perf_hpp__register_sort_field(&hse->hpp); + return 0; +} + +static int __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) { if (sd->taken) - return; + return 0; + + if (__sort_dimension__add_hpp(sd) < 0) + return -1; if (sd->entry->se_collapse) sort__need_collapse = 1; @@ -1040,6 +1111,8 @@ static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) list_add_tail(&sd->entry->list, &hist_entry__sort_list); sd->taken = 1; + + return 0; } int sort_dimension__add(const char *tok) @@ -1068,8 +1141,7 @@ int sort_dimension__add(const char *tok) sort__has_dso = 1; } - __sort_dimension__add(sd, i); - return 0; + return __sort_dimension__add(sd, i); } for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { -- cgit v1.2.3 From 093f0ef34c50ff5cca41c1e18e258ff688e915b6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 3 Mar 2014 12:07:47 +0900 Subject: perf tools: Use hpp formats to sort hist entries It wrapped sort entries to hpp functions, so using the hpp sort list to sort entries. Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Link: http://lkml.kernel.org/r/1400480762-22852-4-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/hist.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 7f0236cea4fe..38373c986e97 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -432,11 +432,11 @@ struct hist_entry *__hists__add_entry(struct hists *hists, int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) { - struct sort_entry *se; + struct perf_hpp_fmt *fmt; int64_t cmp = 0; - list_for_each_entry(se, &hist_entry__sort_list, list) { - cmp = se->se_cmp(left, right); + perf_hpp__for_each_sort_list(fmt) { + cmp = fmt->cmp(left, right); if (cmp) break; } @@ -447,15 +447,11 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) { - struct sort_entry *se; + struct perf_hpp_fmt *fmt; int64_t cmp = 0; - list_for_each_entry(se, &hist_entry__sort_list, list) { - int64_t (*f)(struct hist_entry *, struct hist_entry *); - - f = se->se_collapse ?: se->se_cmp; - - cmp = f(left, right); + perf_hpp__for_each_sort_list(fmt) { + cmp = fmt->collapse(left, right); if (cmp) break; } -- cgit v1.2.3 From 043ca389a3181565b5c19d43a55eae111977d13d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 3 Mar 2014 14:18:00 +0900 Subject: perf tools: Use hpp formats to sort final output Convert output sorting function to use ->sort hpp functions. Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Link: http://lkml.kernel.org/r/1400480762-22852-6-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/hist.c | 62 +++++++------------------------------------------- 1 file changed, 8 insertions(+), 54 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 38373c986e97..c99ae4dd973e 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -564,64 +564,18 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) } } -/* - * reverse the map, sort on period. - */ - -static int period_cmp(u64 period_a, u64 period_b) +static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) { - if (period_a > period_b) - return 1; - if (period_a < period_b) - return -1; - return 0; -} - -static int hist_entry__sort_on_period(struct hist_entry *a, - struct hist_entry *b) -{ - int ret; - int i, nr_members; - struct perf_evsel *evsel; - struct hist_entry *pair; - u64 *periods_a, *periods_b; - - ret = period_cmp(a->stat.period, b->stat.period); - if (ret || !symbol_conf.event_group) - return ret; - - evsel = hists_to_evsel(a->hists); - nr_members = evsel->nr_members; - if (nr_members <= 1) - return ret; - - periods_a = zalloc(sizeof(periods_a) * nr_members); - periods_b = zalloc(sizeof(periods_b) * nr_members); - - if (!periods_a || !periods_b) - goto out; - - list_for_each_entry(pair, &a->pairs.head, pairs.node) { - evsel = hists_to_evsel(pair->hists); - periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period; - } - - list_for_each_entry(pair, &b->pairs.head, pairs.node) { - evsel = hists_to_evsel(pair->hists); - periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period; - } + struct perf_hpp_fmt *fmt; + int64_t cmp = 0; - for (i = 1; i < nr_members; i++) { - ret = period_cmp(periods_a[i], periods_b[i]); - if (ret) + perf_hpp__for_each_format(fmt) { + cmp = fmt->sort(a, b); + if (cmp) break; } -out: - free(periods_a); - free(periods_b); - - return ret; + return cmp; } static void hists__reset_filter_stats(struct hists *hists) @@ -669,7 +623,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); - if (hist_entry__sort_on_period(he, iter) > 0) + if (hist_entry__sort(he, iter) > 0) p = &(*p)->rb_left; else p = &(*p)->rb_right; -- cgit v1.2.3 From 26d8b338271a17a8a9b78000ebaec8b4645f5476 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 3 Mar 2014 16:16:20 +0900 Subject: perf tools: Consolidate output field handling to hpp format routines Until now the hpp and sort functions do similar jobs different ways. Since the sort functions converted/wrapped to hpp formats it can do the job in a uniform way. The perf_hpp__sort_list has a list of hpp formats to sort entries and the perf_hpp__list has a list of hpp formats to print output result. To have a backward compatibility, it automatically adds 'overhead' field in front of sort list. And then all of fields in sort list added to the output list (if it's not already there). Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Link: http://lkml.kernel.org/n/tip-7g3h86woz2sckg3h1lj42ygj@git.kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/browsers/hists.c | 31 ++++++++++++++---------- tools/perf/ui/gtk/hists.c | 31 ------------------------ tools/perf/ui/hist.c | 26 ++++++++++++++++++++ tools/perf/ui/stdio/hist.c | 55 +++++++++++++----------------------------- tools/perf/util/hist.c | 2 +- tools/perf/util/hist.h | 1 + 6 files changed, 63 insertions(+), 83 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index b0861e3e50a5..847de116b9ec 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -760,8 +760,8 @@ static int hist_browser__show_entry(struct hist_browser *browser, if (!browser->b.navkeypressed) width += 1; - hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists); - slsmg_write_nstring(s, width); + slsmg_write_nstring("", width); + ++row; ++printed; } else @@ -1104,27 +1104,32 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, struct hist_entry *he, FILE *fp) { char s[8192]; - double percent; int printed = 0; char folded_sign = ' '; + struct perf_hpp hpp = { + .buf = s, + .size = sizeof(s), + }; + struct perf_hpp_fmt *fmt; + bool first = true; + int ret; if (symbol_conf.use_callchain) folded_sign = hist_entry__folded(he); - hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists); - percent = (he->stat.period * 100.0) / browser->hists->stats.total_period; - if (symbol_conf.use_callchain) printed += fprintf(fp, "%c ", folded_sign); - printed += fprintf(fp, " %5.2f%%", percent); - - if (symbol_conf.show_nr_samples) - printed += fprintf(fp, " %11u", he->stat.nr_events); - - if (symbol_conf.show_total_period) - printed += fprintf(fp, " %12" PRIu64, he->stat.period); + perf_hpp__for_each_format(fmt) { + if (!first) { + ret = scnprintf(hpp.buf, hpp.size, " "); + advance_hpp(&hpp, ret); + } else + first = false; + ret = fmt->entry(fmt, &hpp, he); + advance_hpp(&hpp, ret); + } printed += fprintf(fp, "%s\n", rtrim(s)); if (folded_sign == '-') diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 91f10f3f6dd1..d5c336e1bb14 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -153,7 +153,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, struct perf_hpp_fmt *fmt; GType col_types[MAX_COLUMNS]; GtkCellRenderer *renderer; - struct sort_entry *se; GtkTreeStore *store; struct rb_node *nd; GtkWidget *view; @@ -172,16 +171,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, perf_hpp__for_each_format(fmt) col_types[nr_cols++] = G_TYPE_STRING; - list_for_each_entry(se, &hist_entry__sort_list, list) { - if (se->elide) - continue; - - if (se == &sort_sym) - sym_col = nr_cols; - - col_types[nr_cols++] = G_TYPE_STRING; - } - store = gtk_tree_store_newv(nr_cols, col_types); view = gtk_tree_view_new(); @@ -199,16 +188,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, col_idx++, NULL); } - list_for_each_entry(se, &hist_entry__sort_list, list) { - if (se->elide) - continue; - - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), - -1, se->se_header, - renderer, "text", - col_idx++, NULL); - } - for (col_idx = 0; col_idx < nr_cols; col_idx++) { GtkTreeViewColumn *column; @@ -253,16 +232,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, gtk_tree_store_set(store, &iter, col_idx++, s, -1); } - list_for_each_entry(se, &hist_entry__sort_list, list) { - if (se->elide) - continue; - - se->se_snprintf(h, s, ARRAY_SIZE(s), - hists__col_len(hists, se->se_width_idx)); - - gtk_tree_store_set(store, &iter, col_idx++, s, -1); - } - if (symbol_conf.use_callchain && sort__has_sym) { if (callchain_param.mode == CHAIN_GRAPH_REL) total = h->stat.period; diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 0299385284fd..400437ee60b1 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -354,6 +354,14 @@ LIST_HEAD(perf_hpp__sort_list); void perf_hpp__init(void) { + struct list_head *list; + int i; + + for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { + INIT_LIST_HEAD(&perf_hpp__format[i].list); + INIT_LIST_HEAD(&perf_hpp__format[i].sort_list); + } + perf_hpp__column_enable(PERF_HPP__OVERHEAD); if (symbol_conf.show_cpu_utilization) { @@ -371,6 +379,13 @@ void perf_hpp__init(void) if (symbol_conf.show_total_period) perf_hpp__column_enable(PERF_HPP__PERIOD); + + /* prepend overhead field for backward compatiblity. */ + list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list; + if (list_empty(list)) + list_add(list, &perf_hpp__sort_list); + + perf_hpp__setup_output_field(); } void perf_hpp__column_register(struct perf_hpp_fmt *format) @@ -389,6 +404,17 @@ void perf_hpp__column_enable(unsigned col) perf_hpp__column_register(&perf_hpp__format[col]); } +void perf_hpp__setup_output_field(void) +{ + struct perf_hpp_fmt *fmt; + + /* append sort keys to output field */ + perf_hpp__for_each_sort_list(fmt) { + if (list_empty(&fmt->list)) + perf_hpp__column_register(fmt); + } +} + int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, struct hists *hists) { diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 9eccf7f4f367..49e2e4a7346a 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -306,8 +306,7 @@ static size_t hist_entry__callchain_fprintf(struct hist_entry *he, return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); } -static int hist_entry__period_snprintf(struct perf_hpp *hpp, - struct hist_entry *he) +static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp) { const char *sep = symbol_conf.field_sep; struct perf_hpp_fmt *fmt; @@ -353,8 +352,7 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size, if (size == 0 || size > bfsz) size = hpp.size = bfsz; - ret = hist_entry__period_snprintf(&hpp, he); - hist_entry__sort_snprintf(he, bf + ret, size - ret, hists); + hist_entry__snprintf(he, &hpp); ret = fprintf(fp, "%s\n", bf); @@ -386,28 +384,9 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, init_rem_hits(); - if (!show_header) - goto print_entries; - - fprintf(fp, "# "); - - perf_hpp__for_each_format(fmt) { - if (!first) - fprintf(fp, "%s", sep ?: " "); - else - first = false; - - fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); - fprintf(fp, "%s", bf); - } - list_for_each_entry(se, &hist_entry__sort_list, list) { if (se->elide) continue; - if (sep) { - fprintf(fp, "%c%s", *sep, se->se_header); - continue; - } width = strlen(se->se_header); if (symbol_conf.col_width_list_str) { if (col_width) { @@ -420,7 +399,21 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, } if (!hists__new_col_len(hists, se->se_width_idx, width)) width = hists__col_len(hists, se->se_width_idx); - fprintf(fp, " %*s", width, se->se_header); + } + + if (!show_header) + goto print_entries; + + fprintf(fp, "# "); + + perf_hpp__for_each_format(fmt) { + if (!first) + fprintf(fp, "%s", sep ?: " "); + else + first = false; + + fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); + fprintf(fp, "%s", bf); } fprintf(fp, "\n"); @@ -447,20 +440,6 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, fprintf(fp, "."); } - list_for_each_entry(se, &hist_entry__sort_list, list) { - unsigned int i; - - if (se->elide) - continue; - - fprintf(fp, " "); - width = hists__col_len(hists, se->se_width_idx); - if (width == 0) - width = strlen(se->se_header); - for (i = 0; i < width; i++) - fprintf(fp, "."); - } - fprintf(fp, "\n"); if (max_rows && ++nr_rows >= max_rows) goto out; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c99ae4dd973e..ae13c2dbd27a 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -569,7 +569,7 @@ static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) struct perf_hpp_fmt *fmt; int64_t cmp = 0; - perf_hpp__for_each_format(fmt) { + perf_hpp__for_each_sort_list(fmt) { cmp = fmt->sort(a, b); if (cmp) break; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index eee154a41723..e76d3232672c 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -196,6 +196,7 @@ void perf_hpp__init(void); void perf_hpp__column_register(struct perf_hpp_fmt *format); void perf_hpp__column_enable(unsigned col); void perf_hpp__register_sort_field(struct perf_hpp_fmt *format); +void perf_hpp__setup_output_field(void); typedef u64 (*hpp_field_fn)(struct hist_entry *he); typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); -- cgit v1.2.3 From fb821c9e7135e324ff6d50d030352718a80364b4 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 3 Mar 2014 17:05:19 +0900 Subject: perf ui: Get rid of callback from __hpp__fmt() The callback was used by TUI for determining color of folded sign using percent of first field/column. But it cannot be used anymore since it now support dynamic reordering of output field. So move the logic to the hist_browser__show_entry(). Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Link: http://lkml.kernel.org/r/1400480762-22852-8-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/browsers/hists.c | 62 ++++++++++++++++-------------------------- tools/perf/ui/gtk/hists.c | 2 +- tools/perf/ui/hist.c | 28 ++++++------------- tools/perf/util/hist.h | 4 +-- 4 files changed, 34 insertions(+), 62 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 847de116b9ec..37c5188fd68a 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -616,35 +616,6 @@ struct hpp_arg { bool current_entry; }; -static int __hpp__overhead_callback(struct perf_hpp *hpp, bool front) -{ - struct hpp_arg *arg = hpp->ptr; - - if (arg->current_entry && arg->b->navkeypressed) - ui_browser__set_color(arg->b, HE_COLORSET_SELECTED); - else - ui_browser__set_color(arg->b, HE_COLORSET_NORMAL); - - if (front) { - if (!symbol_conf.use_callchain) - return 0; - - slsmg_printf("%c ", arg->folded_sign); - return 2; - } - - return 0; -} - -static int __hpp__color_callback(struct perf_hpp *hpp, bool front __maybe_unused) -{ - struct hpp_arg *arg = hpp->ptr; - - if (!arg->current_entry || !arg->b->navkeypressed) - ui_browser__set_color(arg->b, HE_COLORSET_NORMAL); - return 0; -} - static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) { struct hpp_arg *arg = hpp->ptr; @@ -665,7 +636,7 @@ static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) return ret; } -#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \ +#define __HPP_COLOR_PERCENT_FN(_type, _field) \ static u64 __hpp_get_##_field(struct hist_entry *he) \ { \ return he->stat._field; \ @@ -676,15 +647,15 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\ struct perf_hpp *hpp, \ struct hist_entry *he) \ { \ - return __hpp__fmt(hpp, he, __hpp_get_##_field, _cb, " %6.2f%%", \ + return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \ __hpp__slsmg_color_printf, true); \ } -__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__overhead_callback) -__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, __hpp__color_callback) -__HPP_COLOR_PERCENT_FN(overhead_us, period_us, __hpp__color_callback) -__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, __hpp__color_callback) -__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, __hpp__color_callback) +__HPP_COLOR_PERCENT_FN(overhead, period) +__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) +__HPP_COLOR_PERCENT_FN(overhead_us, period_us) +__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) +__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) #undef __HPP_COLOR_PERCENT_FN @@ -729,7 +700,7 @@ static int hist_browser__show_entry(struct hist_browser *browser, if (row_offset == 0) { struct hpp_arg arg = { - .b = &browser->b, + .b = &browser->b, .folded_sign = folded_sign, .current_entry = current_entry, }; @@ -742,11 +713,24 @@ static int hist_browser__show_entry(struct hist_browser *browser, ui_browser__gotorc(&browser->b, row, 0); perf_hpp__for_each_format(fmt) { - if (!first) { + if (current_entry && browser->b.navkeypressed) { + ui_browser__set_color(&browser->b, + HE_COLORSET_SELECTED); + } else { + ui_browser__set_color(&browser->b, + HE_COLORSET_NORMAL); + } + + if (first) { + if (symbol_conf.use_callchain) { + slsmg_printf("%c ", folded_sign); + width -= 2; + } + first = false; + } else { slsmg_printf(" "); width -= 2; } - first = false; if (fmt->color) { width -= fmt->color(fmt, &hpp, entry); diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index d5c336e1bb14..2237245bfac0 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -43,7 +43,7 @@ static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp, \ struct hist_entry *he) \ { \ - return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%", \ + return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ __percent_color_snprintf, true); \ } diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 400437ee60b1..e7ac794382c1 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -16,20 +16,15 @@ }) int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, - hpp_field_fn get_field, hpp_callback_fn callback, - const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent) + hpp_field_fn get_field, const char *fmt, + hpp_snprint_fn print_fn, bool fmt_percent) { - int ret = 0; + int ret; struct hists *hists = he->hists; struct perf_evsel *evsel = hists_to_evsel(hists); char *buf = hpp->buf; size_t size = hpp->size; - if (callback) { - ret = callback(hpp, true); - advance_hpp(hpp, ret); - } - if (fmt_percent) { double percent = 0.0; u64 total = hists__total_period(hists); @@ -37,9 +32,9 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, 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 - ret += hpp__call_print_fn(hpp, print_fn, fmt, get_field(he)); + ret = hpp__call_print_fn(hpp, print_fn, fmt, get_field(he)); if (perf_evsel__is_group_event(evsel)) { int prev_idx, idx_delta; @@ -99,13 +94,6 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, } } - if (callback) { - int __ret = callback(hpp, false); - - advance_hpp(hpp, __ret); - ret += __ret; - } - /* * Restore original buf and size as it's where caller expects * the result will be saved. @@ -235,7 +223,7 @@ static u64 he_get_##_field(struct hist_entry *he) \ static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ struct perf_hpp *hpp, struct hist_entry *he) \ { \ - return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%", \ + return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ hpp_color_scnprintf, true); \ } @@ -244,7 +232,7 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ struct perf_hpp *hpp, struct hist_entry *he) \ { \ const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ - return __hpp__fmt(hpp, he, he_get_##_field, NULL, fmt, \ + return __hpp__fmt(hpp, he, he_get_##_field, fmt, \ hpp_entry_scnprintf, true); \ } @@ -264,7 +252,7 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ struct perf_hpp *hpp, struct hist_entry *he) \ { \ const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ - return __hpp__fmt(hpp, he, he_get_raw_##_field, NULL, fmt, \ + return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, \ hpp_entry_scnprintf, false); \ } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index e76d3232672c..76bb72e4302b 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -203,8 +203,8 @@ typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, - hpp_field_fn get_field, hpp_callback_fn callback, - const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent); + hpp_field_fn get_field, const char *fmt, + hpp_snprint_fn print_fn, bool fmt_percent); static inline void advance_hpp(struct perf_hpp *hpp, int inc) { -- cgit v1.2.3 From a2ce067e55e328f1a6fe3dddf77a173381ffdfe1 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 4 Mar 2014 09:06:42 +0900 Subject: perf tools: Allow hpp fields to be sort keys Add overhead{,_sys,_us,_guest_sys,_guest_us}, sample and period sort keys so that they can be selected with --sort/-s option. $ perf report -s period,comm --stdio ... # Overhead Period Command # ........ ............ ............... # 47.06% 152 swapper 13.93% 45 qemu-system-arm 12.38% 40 synergys 3.72% 12 firefox 2.48% 8 xchat Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Link: http://lkml.kernel.org/r/1400480762-22852-9-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/Documentation/perf-diff.txt | 5 ++-- tools/perf/Documentation/perf-report.txt | 9 ++++++++ tools/perf/Documentation/perf-top.txt | 5 ++-- tools/perf/builtin-diff.c | 3 ++- tools/perf/builtin-report.c | 6 ++--- tools/perf/builtin-top.c | 4 ++-- tools/perf/ui/hist.c | 9 ++++++-- tools/perf/util/sort.c | 39 ++++++++++++++++++++++++++++++++ 8 files changed, 67 insertions(+), 13 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index fbfa1192923c..b3b8abae62b8 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt @@ -50,7 +50,8 @@ OPTIONS -s:: --sort=:: - Sort by key(s): pid, comm, dso, symbol. + Sort by key(s): pid, comm, dso, symbol, cpu, parent, srcline. + Please see description of --sort in the perf-report man page. -t:: --field-separator=:: @@ -202,4 +203,4 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as: SEE ALSO -------- -linkperf:perf-record[1] +linkperf:perf-record[1], linkperf:perf-report[1] diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 09af66298564..9babe915b6c4 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -79,6 +79,15 @@ OPTIONS abort cost. This is the global weight. - local_weight: Local weight version of the weight above. - transaction: Transaction abort flags. + - overhead: Overhead percentage of sample + - overhead_sys: Overhead percentage of sample running in system mode + - overhead_us: Overhead percentage of sample running in user mode + - overhead_guest_sys: Overhead percentage of sample running in system mode + on guest machine + - overhead_guest_us: Overhead percentage of sample running in user mode on + guest machine + - sample: Number of sample + - period: Raw number of event count of sample By default, comm, dso and symbol keys are used. (i.e. --sort comm,dso,symbol) diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 64ed79c43639..df863288752a 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -113,7 +113,8 @@ Default is to monitor all CPUS. -s:: --sort:: Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, - local_weight, abort, in_tx, transaction + local_weight, abort, in_tx, transaction, overhead, sample, period. + Please see description of --sort in the perf-report man page. -n:: --show-nr-samples:: @@ -212,4 +213,4 @@ Pressing any unmapped key displays a menu, and prompts for input. SEE ALSO -------- -linkperf:perf-stat[1], linkperf:perf-list[1] +linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-report[1] diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index f3b10dcf6838..b60c711d4e72 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -741,7 +741,8 @@ static const struct option options[] = { OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", "only consider these symbols"), OPT_STRING('s', "sort", &sort_order, "key[,key2...]", - "sort by key(s): pid, comm, dso, symbol, parent"), + "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." + " Please refer the man page for the complete list."), OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", "separator for columns, no spaces will be added between " "columns '.' is reserved."), diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 89c95289fd51..d0180d5de781 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -699,10 +699,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) OPT_BOOLEAN(0, "header-only", &report.header_only, "Show only data header."), OPT_STRING('s', "sort", &sort_order, "key[,key2...]", - "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," - " dso_to, dso_from, symbol_to, symbol_from, mispredict," - " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, " - "snoop, locked, abort, in_tx, transaction"), + "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." + " Please refer the man page for the complete list."), OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, "Show sample percentage for different cpu modes"), OPT_STRING('p', "parent", &parent_pattern, "regex", diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 37d30460bada..4fef1e415129 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1083,8 +1083,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), OPT_STRING('s', "sort", &sort_order, "key[,key2...]", - "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight," - " abort, in_tx, transaction"), + "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." + " Please refer the man page for the complete list."), OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, "Show a column with the number of samples"), OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts, diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index e7ac794382c1..24116a48298f 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -346,8 +346,13 @@ void perf_hpp__init(void) int i; for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { - INIT_LIST_HEAD(&perf_hpp__format[i].list); - INIT_LIST_HEAD(&perf_hpp__format[i].sort_list); + struct perf_hpp_fmt *fmt = &perf_hpp__format[i]; + + INIT_LIST_HEAD(&fmt->list); + + /* sort_list may be linked by setup_sorting() */ + if (fmt->sort_list.next == NULL) + INIT_LIST_HEAD(&fmt->sort_list); } perf_hpp__column_enable(PERF_HPP__OVERHEAD); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index b2829f947053..916652af8304 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1028,6 +1028,26 @@ static struct sort_dimension memory_sort_dimensions[] = { #undef DIM +struct hpp_dimension { + const char *name; + struct perf_hpp_fmt *fmt; + int taken; +}; + +#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], } + +static struct hpp_dimension hpp_sort_dimensions[] = { + DIM(PERF_HPP__OVERHEAD, "overhead"), + DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"), + DIM(PERF_HPP__OVERHEAD_US, "overhead_us"), + DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"), + DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"), + DIM(PERF_HPP__SAMPLES, "sample"), + DIM(PERF_HPP__PERIOD, "period"), +}; + +#undef DIM + struct hpp_sort_entry { struct perf_hpp_fmt hpp; struct sort_entry *se; @@ -1115,6 +1135,16 @@ static int __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) return 0; } +static int __hpp_dimension__add(struct hpp_dimension *hd) +{ + if (!hd->taken) { + hd->taken = 1; + + perf_hpp__register_sort_field(hd->fmt); + } + return 0; +} + int sort_dimension__add(const char *tok) { unsigned int i; @@ -1144,6 +1174,15 @@ int sort_dimension__add(const char *tok) return __sort_dimension__add(sd, i); } + for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { + struct hpp_dimension *hd = &hpp_sort_dimensions[i]; + + if (strncasecmp(tok, hd->name, strlen(tok))) + continue; + + return __hpp_dimension__add(hd); + } + for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { struct sort_dimension *sd = &bstack_sort_dimensions[i]; -- cgit v1.2.3 From 512ae1bd6acb811c72e44e2540099eccd31f773d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 18 Mar 2014 11:31:39 +0900 Subject: perf tools: Consolidate management of default sort orders The perf uses different default sort orders for different use-cases, and this was scattered throughout the code. Add get_default_sort_ order() function to handle this and change initial value of sort_order to NULL to distinguish it from user-given one. Signed-off-by: Namhyung Kim Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1400480762-22852-10-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/builtin-diff.c | 4 ++-- tools/perf/builtin-report.c | 18 ------------------ tools/perf/builtin-top.c | 3 +-- tools/perf/util/sort.c | 28 ++++++++++++++++++++++++++-- tools/perf/util/sort.h | 2 ++ 5 files changed, 31 insertions(+), 24 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index b60c711d4e72..8bff543acaab 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -60,7 +60,6 @@ static int data__files_cnt; #define data__for_each_file(i, d) data__for_each_file_start(i, d, 0) #define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1) -static char diff__default_sort_order[] = "dso,symbol"; static bool force; static bool show_period; static bool show_formula; @@ -1142,7 +1141,6 @@ 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); if (symbol__init() < 0) @@ -1153,6 +1151,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) ui_init(); + sort__mode = SORT_MODE__DIFF; + if (setup_sorting() < 0) usage_with_options(diff_usage, options); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index d0180d5de781..f4d640cfdf16 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -805,30 +805,12 @@ repeat: if (branch_mode == -1 && has_br_stack) sort__mode = SORT_MODE__BRANCH; - /* sort__mode could be NORMAL if --no-branch-stack */ - if (sort__mode == SORT_MODE__BRANCH) { - /* - * if no sort_order is provided, then specify - * branch-mode specific order - */ - if (sort_order == default_sort_order) - sort_order = "comm,dso_from,symbol_from," - "dso_to,symbol_to"; - - } if (report.mem_mode) { if (sort__mode == SORT_MODE__BRANCH) { pr_err("branch and mem mode incompatible\n"); goto error; } sort__mode = SORT_MODE__MEMORY; - - /* - * if no sort_order is provided, then specify - * branch-mode specific order - */ - if (sort_order == default_sort_order) - sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; } if (setup_sorting() < 0) { diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 4fef1e415129..34764b6eabf9 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1137,8 +1137,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) if (argc) usage_with_options(top_usage, options); - if (sort_order == default_sort_order) - sort_order = "dso,symbol"; + sort__mode = SORT_MODE__TOP; if (setup_sorting() < 0) { parse_options_usage(top_usage, options, "s", 1); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 916652af8304..d64c1e58f1b1 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -8,7 +8,11 @@ regex_t parent_regex; const char default_parent_pattern[] = "^sys_|^do_page_fault"; const char *parent_pattern = default_parent_pattern; const char default_sort_order[] = "comm,dso,symbol"; -const char *sort_order = default_sort_order; +const char default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to"; +const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; +const char default_top_sort_order[] = "dso,symbol"; +const char default_diff_sort_order[] = "dso,symbol"; +const char *sort_order; regex_t ignore_callees_regex; int have_ignore_callees = 0; int sort__need_collapse = 0; @@ -1218,11 +1222,31 @@ int sort_dimension__add(const char *tok) return -ESRCH; } +static const char *get_default_sort_order(void) +{ + const char *default_sort_orders[] = { + default_sort_order, + default_branch_sort_order, + default_mem_sort_order, + default_top_sort_order, + default_diff_sort_order, + }; + + BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders)); + + return default_sort_orders[sort__mode]; +} + int setup_sorting(void) { - char *tmp, *tok, *str = strdup(sort_order); + char *tmp, *tok, *str; + const char *sort_keys = sort_order; int ret = 0; + if (sort_keys == NULL) + sort_keys = get_default_sort_order(); + + str = strdup(sort_keys); if (str == NULL) { error("Not enough memory to setup sort keys"); return -ENOMEM; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 43e5ff42a609..1a7295255dc9 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -133,6 +133,8 @@ enum sort_mode { SORT_MODE__NORMAL, SORT_MODE__BRANCH, SORT_MODE__MEMORY, + SORT_MODE__TOP, + SORT_MODE__DIFF, }; enum sort_type { -- cgit v1.2.3 From a7d945bc91602f916d2d0c794c179d9a784859e7 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 4 Mar 2014 10:46:34 +0900 Subject: perf report: Add -F option to specify output fields The -F/--fields option is to allow user setup output field in any order. It can receive any sort keys and following (hpp) fields: overhead, overhead_sys, overhead_us, sample and period If guest profiling is enabled, overhead_guest_{sys,us} will be available too. The output fields also affect sort order unless you give -s/--sort option. And any keys specified on -s option, will also be added to the output field list automatically. $ perf report -F sym,sample,overhead ... # Symbol Samples Overhead # .......................... ............ ........ # [.] __cxa_atexit 2 2.50% [.] __libc_csu_init 4 5.00% [.] __new_exitfn 3 3.75% [.] _dl_check_map_versions 1 1.25% [.] _dl_name_match_p 4 5.00% [.] _dl_setup_hash 1 1.25% [.] _dl_sysdep_start 1 1.25% [.] _init 5 6.25% [.] _setjmp 6 7.50% [.] a 8 10.00% [.] b 8 10.00% [.] brk 1 1.25% [.] c 8 10.00% Note that, the example output above is captured after applying next patch which fixes sort/comparing behavior. Requested-by: Ingo Molnar Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Link: http://lkml.kernel.org/r/1400480762-22852-12-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/Documentation/perf-report.txt | 10 ++ tools/perf/builtin-report.c | 15 ++- tools/perf/ui/hist.c | 59 ++++++++- tools/perf/util/hist.h | 4 + tools/perf/util/sort.c | 209 ++++++++++++++++++++++++++++++- tools/perf/util/sort.h | 2 + 6 files changed, 282 insertions(+), 17 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 9babe915b6c4..a1b5185402d5 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -107,6 +107,16 @@ OPTIONS And default sort keys are changed to comm, dso_from, symbol_from, dso_to and symbol_to, see '--branch-stack'. +-F:: +--fields=:: + Specify output field - multiple keys can be specified in CSV format. + Following fields are available: + overhead, overhead_sys, overhead_us, sample and period. + Also it can contain any sort key(s). + + By default, every sort keys not specified in -F will be appended + automatically. + -p:: --parent=:: A regex filter to identify parent. The parent is a caller of this diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index c4dab7acbdbb..bc0eec1ce4be 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -701,6 +701,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) OPT_STRING('s', "sort", &sort_order, "key[,key2...]", "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." " Please refer the man page for the complete list."), + OPT_STRING('F', "fields", &field_order, "key[,keys...]", + "output field(s): overhead, period, sample plus all of sort keys"), OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, "Show sample percentage for different cpu modes"), OPT_STRING('p', "parent", &parent_pattern, "regex", @@ -814,17 +816,14 @@ repeat: } if (setup_sorting() < 0) { - parse_options_usage(report_usage, options, "s", 1); + if (sort_order) + parse_options_usage(report_usage, options, "s", 1); + if (field_order) + parse_options_usage(sort_order ? NULL : report_usage, + options, "F", 1); goto error; } - if (parent_pattern != default_parent_pattern) { - if (sort_dimension__add("parent") < 0) - goto error; - } - - perf_hpp__init(); - /* Force tty output for header output. */ if (report.header || report.header_only) use_browser = 0; diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 24116a48298f..b114c6668865 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -355,6 +355,12 @@ void perf_hpp__init(void) INIT_LIST_HEAD(&fmt->sort_list); } + /* + * If user specified field order, no need to setup default fields. + */ + if (field_order) + return; + perf_hpp__column_enable(PERF_HPP__OVERHEAD); if (symbol_conf.show_cpu_utilization) { @@ -377,8 +383,6 @@ void perf_hpp__init(void) list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list; if (list_empty(list)) list_add(list, &perf_hpp__sort_list); - - perf_hpp__setup_output_field(); } void perf_hpp__column_register(struct perf_hpp_fmt *format) @@ -403,8 +407,55 @@ void perf_hpp__setup_output_field(void) /* append sort keys to output field */ perf_hpp__for_each_sort_list(fmt) { - if (list_empty(&fmt->list)) - perf_hpp__column_register(fmt); + if (!list_empty(&fmt->list)) + continue; + + /* + * sort entry fields are dynamically created, + * so they can share a same sort key even though + * the list is empty. + */ + if (perf_hpp__is_sort_entry(fmt)) { + struct perf_hpp_fmt *pos; + + perf_hpp__for_each_format(pos) { + if (perf_hpp__same_sort_entry(pos, fmt)) + goto next; + } + } + + perf_hpp__column_register(fmt); +next: + continue; + } +} + +void perf_hpp__append_sort_keys(void) +{ + struct perf_hpp_fmt *fmt; + + /* append output fields to sort keys */ + perf_hpp__for_each_format(fmt) { + if (!list_empty(&fmt->sort_list)) + continue; + + /* + * sort entry fields are dynamically created, + * so they can share a same sort key even though + * the list is empty. + */ + if (perf_hpp__is_sort_entry(fmt)) { + struct perf_hpp_fmt *pos; + + perf_hpp__for_each_sort_list(pos) { + if (perf_hpp__same_sort_entry(pos, fmt)) + goto next; + } + } + + perf_hpp__register_sort_field(fmt); +next: + continue; } } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 76bb72e4302b..f3713b79742d 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -197,6 +197,10 @@ void perf_hpp__column_register(struct perf_hpp_fmt *format); void perf_hpp__column_enable(unsigned col); void perf_hpp__register_sort_field(struct perf_hpp_fmt *format); void perf_hpp__setup_output_field(void); +void perf_hpp__append_sort_keys(void); + +bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); +bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b); typedef u64 (*hpp_field_fn)(struct hist_entry *he); typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index d64c1e58f1b1..b748b02fcb78 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -13,6 +13,7 @@ const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso const char default_top_sort_order[] = "dso,symbol"; const char default_diff_sort_order[] = "dso,symbol"; const char *sort_order; +const char *field_order; regex_t ignore_callees_regex; int have_ignore_callees = 0; int sort__need_collapse = 0; @@ -1057,6 +1058,20 @@ struct hpp_sort_entry { struct sort_entry *se; }; +bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) +{ + struct hpp_sort_entry *hse_a; + struct hpp_sort_entry *hse_b; + + if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b)) + return false; + + hse_a = container_of(a, struct hpp_sort_entry, hpp); + hse_b = container_of(b, struct hpp_sort_entry, hpp); + + return hse_a->se == hse_b->se; +} + static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct perf_evsel *evsel) { @@ -1092,14 +1107,15 @@ static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); } -static int __sort_dimension__add_hpp(struct sort_dimension *sd) +static struct hpp_sort_entry * +__sort_dimension__alloc_hpp(struct sort_dimension *sd) { struct hpp_sort_entry *hse; hse = malloc(sizeof(*hse)); if (hse == NULL) { pr_err("Memory allocation failed\n"); - return -1; + return NULL; } hse->se = sd->entry; @@ -1115,16 +1131,42 @@ static int __sort_dimension__add_hpp(struct sort_dimension *sd) INIT_LIST_HEAD(&hse->hpp.list); INIT_LIST_HEAD(&hse->hpp.sort_list); + return hse; +} + +bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format) +{ + return format->header == __sort__hpp_header; +} + +static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd) +{ + struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); + + if (hse == NULL) + return -1; + perf_hpp__register_sort_field(&hse->hpp); return 0; } +static int __sort_dimension__add_hpp_output(struct sort_dimension *sd) +{ + struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); + + if (hse == NULL) + return -1; + + perf_hpp__column_register(&hse->hpp); + return 0; +} + static int __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) { if (sd->taken) return 0; - if (__sort_dimension__add_hpp(sd) < 0) + if (__sort_dimension__add_hpp_sort(sd) < 0) return -1; if (sd->entry->se_collapse) @@ -1149,6 +1191,28 @@ static int __hpp_dimension__add(struct hpp_dimension *hd) return 0; } +static int __sort_dimension__add_output(struct sort_dimension *sd) +{ + if (sd->taken) + return 0; + + if (__sort_dimension__add_hpp_output(sd) < 0) + return -1; + + sd->taken = 1; + return 0; +} + +static int __hpp_dimension__add_output(struct hpp_dimension *hd) +{ + if (!hd->taken) { + hd->taken = 1; + + perf_hpp__column_register(hd->fmt); + } + return 0; +} + int sort_dimension__add(const char *tok) { unsigned int i; @@ -1237,14 +1301,23 @@ static const char *get_default_sort_order(void) return default_sort_orders[sort__mode]; } -int setup_sorting(void) +static int __setup_sorting(void) { char *tmp, *tok, *str; const char *sort_keys = sort_order; int ret = 0; - if (sort_keys == NULL) + if (sort_keys == NULL) { + if (field_order) { + /* + * If user specified field order but no sort order, + * we'll honor it and not add default sort orders. + */ + return 0; + } + sort_keys = get_default_sort_order(); + } str = strdup(sort_keys); if (str == NULL) { @@ -1331,3 +1404,129 @@ void sort__setup_elide(FILE *output) list_for_each_entry(se, &hist_entry__sort_list, list) se->elide = false; } + +static int output_field_add(char *tok) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { + struct sort_dimension *sd = &common_sort_dimensions[i]; + + if (strncasecmp(tok, sd->name, strlen(tok))) + continue; + + return __sort_dimension__add_output(sd); + } + + for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { + struct hpp_dimension *hd = &hpp_sort_dimensions[i]; + + if (strncasecmp(tok, hd->name, strlen(tok))) + continue; + + return __hpp_dimension__add_output(hd); + } + + for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { + struct sort_dimension *sd = &bstack_sort_dimensions[i]; + + if (strncasecmp(tok, sd->name, strlen(tok))) + continue; + + return __sort_dimension__add_output(sd); + } + + for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { + struct sort_dimension *sd = &memory_sort_dimensions[i]; + + if (strncasecmp(tok, sd->name, strlen(tok))) + continue; + + return __sort_dimension__add_output(sd); + } + + return -ESRCH; +} + +static void reset_dimensions(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) + common_sort_dimensions[i].taken = 0; + + for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) + hpp_sort_dimensions[i].taken = 0; + + for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) + bstack_sort_dimensions[i].taken = 0; + + for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) + memory_sort_dimensions[i].taken = 0; +} + +static int __setup_output_field(void) +{ + char *tmp, *tok, *str; + int ret = 0; + + if (field_order == NULL) + return 0; + + reset_dimensions(); + + str = strdup(field_order); + if (str == NULL) { + error("Not enough memory to setup output fields"); + return -ENOMEM; + } + + for (tok = strtok_r(str, ", ", &tmp); + tok; tok = strtok_r(NULL, ", ", &tmp)) { + ret = output_field_add(tok); + if (ret == -EINVAL) { + error("Invalid --fields key: `%s'", tok); + break; + } else if (ret == -ESRCH) { + error("Unknown --fields key: `%s'", tok); + break; + } + } + + free(str); + return ret; +} + +int setup_sorting(void) +{ + int err; + + err = __setup_sorting(); + if (err < 0) + return err; + + if (parent_pattern != default_parent_pattern) { + err = sort_dimension__add("parent"); + if (err < 0) + return err; + } + + reset_dimensions(); + + /* + * perf diff doesn't use default hpp output fields. + */ + if (sort__mode != SORT_MODE__DIFF) + perf_hpp__init(); + + err = __setup_output_field(); + if (err < 0) + return err; + + /* copy sort keys to output fields */ + perf_hpp__setup_output_field(); + /* and then copy output fields to sort keys */ + perf_hpp__append_sort_keys(); + + return 0; +} diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 1a7295255dc9..89e5057ca378 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -25,6 +25,7 @@ extern regex_t parent_regex; extern const char *sort_order; +extern const char *field_order; extern const char default_parent_pattern[]; extern const char *parent_pattern; extern const char default_sort_order[]; @@ -191,6 +192,7 @@ extern struct sort_entry sort_thread; extern struct list_head hist_entry__sort_list; int setup_sorting(void); +int setup_output_field(void); extern int sort_dimension__add(const char *); void sort__setup_elide(FILE *fp); -- cgit v1.2.3 From 202e7a6d16127323d03e912d7844aa0d614c315e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 4 Mar 2014 11:01:41 +0900 Subject: perf tools: Add ->sort() member to struct sort_entry Currently, what the sort_entry does is just identifying hist entries so that they can be grouped properly. However, with -F option support, it indeed needs to sort entries appropriately to be shown to users. So add ->sort() member to do it. Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Link: http://lkml.kernel.org/r/1400480762-22852-13-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/sort.c | 27 ++++++++++++++++++++++----- tools/perf/util/sort.h | 1 + 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index b748b02fcb78..5414ba541e47 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -99,6 +99,12 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) return comm__str(right->comm) - comm__str(left->comm); } +static int64_t +sort__comm_sort(struct hist_entry *left, struct hist_entry *right) +{ + return strcmp(comm__str(right->comm), comm__str(left->comm)); +} + static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { @@ -109,6 +115,7 @@ struct sort_entry sort_comm = { .se_header = "Command", .se_cmp = sort__comm_cmp, .se_collapse = sort__comm_collapse, + .se_sort = sort__comm_sort, .se_snprintf = hist_entry__comm_snprintf, .se_width_idx = HISTC_COMM, }; @@ -122,7 +129,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) const char *dso_name_l, *dso_name_r; if (!dso_l || !dso_r) - return cmp_null(dso_l, dso_r); + return cmp_null(dso_r, dso_l); if (verbose) { dso_name_l = dso_l->long_name; @@ -138,7 +145,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) static int64_t sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) { - return _sort__dso_cmp(left->ms.map, right->ms.map); + return _sort__dso_cmp(right->ms.map, left->ms.map); } static int _hist_entry__dso_snprintf(struct map *map, char *bf, @@ -210,6 +217,15 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) return _sort__sym_cmp(left->ms.sym, right->ms.sym); } +static int64_t +sort__sym_sort(struct hist_entry *left, struct hist_entry *right) +{ + if (!left->ms.sym || !right->ms.sym) + return cmp_null(left->ms.sym, right->ms.sym); + + return strcmp(right->ms.sym->name, left->ms.sym->name); +} + static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, u64 ip, char level, char *bf, size_t size, unsigned int width) @@ -256,6 +272,7 @@ static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, struct sort_entry sort_sym = { .se_header = "Symbol", .se_cmp = sort__sym_cmp, + .se_sort = sort__sym_sort, .se_snprintf = hist_entry__sym_snprintf, .se_width_idx = HISTC_SYMBOL, }; @@ -283,7 +300,7 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) map__rip_2objdump(map, right->ip)); } } - return strcmp(left->srcline, right->srcline); + return strcmp(right->srcline, left->srcline); } static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, @@ -311,7 +328,7 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) if (!sym_l || !sym_r) return cmp_null(sym_l, sym_r); - return strcmp(sym_l->name, sym_r->name); + return strcmp(sym_r->name, sym_l->name); } static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, @@ -1126,7 +1143,7 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) hse->hpp.cmp = sd->entry->se_cmp; hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp; - hse->hpp.sort = hse->hpp.collapse; + hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse; INIT_LIST_HEAD(&hse->hpp.list); INIT_LIST_HEAD(&hse->hpp.sort_list); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 89e5057ca378..f5a831c3d0fb 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -182,6 +182,7 @@ struct sort_entry { int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); + int64_t (*se_sort)(struct hist_entry *, struct hist_entry *); int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size, unsigned int width); u8 se_width_idx; -- cgit v1.2.3 From e67d49a72df9aa50841ad400f7a99405e4980ee4 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 18 Mar 2014 13:00:59 +0900 Subject: perf tools: Skip elided sort entries When it converted sort entries to hpp formats, it missed se->elide handling, so add it for compatibility. Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1400480762-22852-16-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/browsers/hists.c | 6 ++++++ tools/perf/ui/gtk/hists.c | 6 ++++++ tools/perf/ui/stdio/hist.c | 9 +++++++++ tools/perf/util/hist.c | 9 +++++++++ tools/perf/util/hist.h | 1 + tools/perf/util/sort.c | 11 +++++++++++ 6 files changed, 42 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 169224c31586..1c331b934ffc 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -711,6 +711,9 @@ static int hist_browser__show_entry(struct hist_browser *browser, ui_browser__gotorc(&browser->b, row, 0); perf_hpp__for_each_format(fmt) { + if (perf_hpp__should_skip(fmt)) + continue; + if (current_entry && browser->b.navkeypressed) { ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED); @@ -1100,6 +1103,9 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, printed += fprintf(fp, "%c ", folded_sign); perf_hpp__for_each_format(fmt) { + if (perf_hpp__should_skip(fmt)) + continue; + if (!first) { ret = scnprintf(hpp.buf, hpp.size, " "); advance_hpp(&hpp, ret); diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index fd52669018ee..9d90683914d4 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -178,6 +178,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, col_idx = 0; perf_hpp__for_each_format(fmt) { + if (perf_hpp__should_skip(fmt)) + continue; + fmt->header(fmt, &hpp, hists_to_evsel(hists)); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), @@ -222,6 +225,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, col_idx = 0; perf_hpp__for_each_format(fmt) { + if (perf_hpp__should_skip(fmt)) + continue; + if (fmt->color) fmt->color(fmt, &hpp, h); else diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 49e2e4a7346a..e3fdf4e869fc 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -318,6 +318,9 @@ static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp) return 0; perf_hpp__for_each_format(fmt) { + if (perf_hpp__should_skip(fmt)) + continue; + /* * If there's no field_sep, we still need * to display initial ' '. @@ -407,6 +410,9 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, fprintf(fp, "# "); perf_hpp__for_each_format(fmt) { + if (perf_hpp__should_skip(fmt)) + continue; + if (!first) fprintf(fp, "%s", sep ?: " "); else @@ -430,6 +436,9 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, perf_hpp__for_each_format(fmt) { unsigned int i; + if (perf_hpp__should_skip(fmt)) + continue; + if (!first) fprintf(fp, "%s", sep ?: " "); else diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index ae13c2dbd27a..b262b44b7a65 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -436,6 +436,9 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) int64_t cmp = 0; perf_hpp__for_each_sort_list(fmt) { + if (perf_hpp__should_skip(fmt)) + continue; + cmp = fmt->cmp(left, right); if (cmp) break; @@ -451,6 +454,9 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) int64_t cmp = 0; perf_hpp__for_each_sort_list(fmt) { + if (perf_hpp__should_skip(fmt)) + continue; + cmp = fmt->collapse(left, right); if (cmp) break; @@ -570,6 +576,9 @@ static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) int64_t cmp = 0; perf_hpp__for_each_sort_list(fmt) { + if (perf_hpp__should_skip(fmt)) + continue; + cmp = fmt->sort(a, b); if (cmp) break; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index f3713b79742d..f67feb432a44 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -201,6 +201,7 @@ void perf_hpp__append_sort_keys(void); bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b); +bool perf_hpp__should_skip(struct perf_hpp_fmt *format); typedef u64 (*hpp_field_fn)(struct hist_entry *he); typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 5414ba541e47..0fe7cbe47ea3 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1358,6 +1358,17 @@ static int __setup_sorting(void) return ret; } +bool perf_hpp__should_skip(struct perf_hpp_fmt *format) +{ + if (perf_hpp__is_sort_entry(format)) { + struct hpp_sort_entry *hse; + + hse = container_of(format, struct hpp_sort_entry, hpp); + return hse->se->elide; + } + return false; +} + static void sort_entry__setup_elide(struct sort_entry *se, struct strlist *list, const char *list_name, FILE *fp) -- cgit v1.2.3 From 678a500d076ec873b8809041c6b718653db2a75f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 20 Mar 2014 11:18:54 +0900 Subject: perf hists: Reset width of output fields with header length Some fields missed to set default column length so it broke align in --stdio output. Add perf_hpp__reset_width() to set it to a sane default value. Note that this change will ignore -w/--column-widths option for now. Before: $ perf report -F cpu,comm,overhead --stdio ... # CPU Command Overhead # ............... ........ # 0 firefox 2.65% 0 kworker/0:0 1.45% 0 swapper 5.52% 0 synergys 0.92% 1 firefox 4.54% After: # CPU Command Overhead # ... ............... ........ # 0 firefox 2.65% 0 kworker/0:0 1.45% 0 swapper 5.52% 0 synergys 0.92% 1 firefox 4.54% Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1400480762-22852-17-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/stdio/hist.c | 21 +++------------------ tools/perf/util/hist.h | 1 + tools/perf/util/sort.c | 12 ++++++++++++ 3 files changed, 16 insertions(+), 18 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index e3fdf4e869fc..cfcd3f6fd1c5 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -369,12 +369,10 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, int max_cols, float min_pcnt, FILE *fp) { struct perf_hpp_fmt *fmt; - struct sort_entry *se; struct rb_node *nd; size_t ret = 0; unsigned int width; const char *sep = symbol_conf.field_sep; - const char *col_width = symbol_conf.col_width_list_str; int nr_rows = 0; char bf[96]; struct perf_hpp dummy_hpp = { @@ -387,22 +385,9 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, init_rem_hits(); - list_for_each_entry(se, &hist_entry__sort_list, list) { - if (se->elide) - continue; - width = strlen(se->se_header); - if (symbol_conf.col_width_list_str) { - if (col_width) { - hists__set_col_len(hists, se->se_width_idx, - atoi(col_width)); - col_width = strchr(col_width, ','); - if (col_width) - ++col_width; - } - } - if (!hists__new_col_len(hists, se->se_width_idx, width)) - width = hists__col_len(hists, se->se_width_idx); - } + + perf_hpp__for_each_format(fmt) + perf_hpp__reset_width(fmt, hists); if (!show_header) goto print_entries; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index f67feb432a44..034db761630e 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -202,6 +202,7 @@ void perf_hpp__append_sort_keys(void); bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b); bool perf_hpp__should_skip(struct perf_hpp_fmt *format); +void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); typedef u64 (*hpp_field_fn)(struct hist_entry *he); typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 0fe7cbe47ea3..9bee7288465f 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1089,6 +1089,18 @@ bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) return hse_a->se == hse_b->se; } +void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) +{ + struct hpp_sort_entry *hse; + + if (!perf_hpp__is_sort_entry(fmt)) + return; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); + hists__new_col_len(hists, hse->se->se_width_idx, + strlen(hse->se->se_header)); +} + static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct perf_evsel *evsel) { -- cgit v1.2.3 From cfaa154b2335d4c8efdfcb65d9b12e944d1b74a6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 19 May 2014 14:19:30 +0900 Subject: perf tools: Get rid of obsolete hist_entry__sort_list Now we moved to the perf_hpp_[_sort]_list so no need to keep the old hist_entry__sort_list and sort__first_dimension. Also the hist_entry__sort_snprintf() can be gone as hist_entry__snprintf() provides the functionality. Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1400480762-22852-18-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/hist.c | 36 +++++++++--------------------------- tools/perf/ui/stdio/hist.c | 22 +++++++++++++++------- tools/perf/util/sort.c | 35 +++++++++++++++++++---------------- 3 files changed, 43 insertions(+), 50 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index b114c6668865..61cf31e094f3 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -459,47 +459,29 @@ next: } } -int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, - struct hists *hists) -{ - const char *sep = symbol_conf.field_sep; - struct sort_entry *se; - int ret = 0; - - list_for_each_entry(se, &hist_entry__sort_list, list) { - if (se->elide) - continue; - - ret += scnprintf(s + ret, size - ret, "%s", sep ?: " "); - ret += se->se_snprintf(he, s + ret, size - ret, - hists__col_len(hists, se->se_width_idx)); - } - - return ret; -} - /* * See hists__fprintf to match the column widths */ unsigned int hists__sort_list_width(struct hists *hists) { struct perf_hpp_fmt *fmt; - struct sort_entry *se; - int i = 0, ret = 0; + int ret = 0; + bool first = true; struct perf_hpp dummy_hpp; perf_hpp__for_each_format(fmt) { - if (i) + if (perf_hpp__should_skip(fmt)) + continue; + + if (first) + first = false; + else ret += 2; ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists)); } - list_for_each_entry(se, &hist_entry__sort_list, list) - if (!se->elide) - ret += 2 + hists__col_len(hists, se->se_width_idx); - - if (verbose) /* Addr + origin */ + if (verbose && sort__has_sym) /* Addr + origin */ ret += 3 + BITS_PER_LONG / 4; return ret; diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index cfcd3f6fd1c5..9f57991025a9 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -183,7 +183,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, * the symbol. No need to print it otherwise it appears as * displayed twice. */ - if (!i++ && sort__first_dimension == SORT_SYM) + if (!i++ && field_order == NULL && + sort_order && !prefixcmp(sort_order, "sym")) continue; if (!printed) { ret += callchain__fprintf_left_margin(fp, left_margin); @@ -296,13 +297,20 @@ static size_t hist_entry__callchain_fprintf(struct hist_entry *he, int left_margin = 0; u64 total_period = hists->stats.total_period; - if (sort__first_dimension == SORT_COMM) { - struct sort_entry *se = list_first_entry(&hist_entry__sort_list, - typeof(*se), list); - left_margin = hists__col_len(hists, se->se_width_idx); - left_margin -= thread__comm_len(he->thread); - } + if (field_order == NULL && (sort_order == NULL || + !prefixcmp(sort_order, "comm"))) { + struct perf_hpp_fmt *fmt; + + perf_hpp__for_each_format(fmt) { + if (!perf_hpp__is_sort_entry(fmt)) + continue; + /* must be 'comm' sort entry */ + left_margin = fmt->width(fmt, NULL, hists_to_evsel(hists)); + left_margin -= thread__comm_len(he->thread); + break; + } + } return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 9bee7288465f..d9132069d3b1 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -22,9 +22,6 @@ int sort__has_sym = 0; int sort__has_dso = 0; enum sort_mode sort__mode = SORT_MODE__NORMAL; -enum sort_type sort__first_dimension; - -LIST_HEAD(hist_entry__sort_list); static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) { @@ -1190,7 +1187,7 @@ static int __sort_dimension__add_hpp_output(struct sort_dimension *sd) return 0; } -static int __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) +static int __sort_dimension__add(struct sort_dimension *sd) { if (sd->taken) return 0; @@ -1201,10 +1198,6 @@ static int __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) if (sd->entry->se_collapse) sort__need_collapse = 1; - if (list_empty(&hist_entry__sort_list)) - sort__first_dimension = idx; - - list_add_tail(&sd->entry->list, &hist_entry__sort_list); sd->taken = 1; return 0; @@ -1268,7 +1261,7 @@ int sort_dimension__add(const char *tok) sort__has_dso = 1; } - return __sort_dimension__add(sd, i); + return __sort_dimension__add(sd); } for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { @@ -1292,7 +1285,7 @@ int sort_dimension__add(const char *tok) if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) sort__has_sym = 1; - __sort_dimension__add(sd, i + __SORT_BRANCH_STACK); + __sort_dimension__add(sd); return 0; } @@ -1308,7 +1301,7 @@ int sort_dimension__add(const char *tok) if (sd->entry == &sort_mem_daddr_sym) sort__has_sym = 1; - __sort_dimension__add(sd, i + __SORT_MEMORY_MODE); + __sort_dimension__add(sd); return 0; } @@ -1395,7 +1388,8 @@ static void sort_entry__setup_elide(struct sort_entry *se, void sort__setup_elide(FILE *output) { - struct sort_entry *se; + struct perf_hpp_fmt *fmt; + struct hpp_sort_entry *hse; sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", output); @@ -1436,13 +1430,22 @@ void sort__setup_elide(FILE *output) * It makes no sense to elide all of sort entries. * Just revert them to show up again. */ - list_for_each_entry(se, &hist_entry__sort_list, list) { - if (!se->elide) + perf_hpp__for_each_format(fmt) { + if (!perf_hpp__is_sort_entry(fmt)) + continue; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); + if (!hse->se->elide) return; } - list_for_each_entry(se, &hist_entry__sort_list, list) - se->elide = false; + perf_hpp__for_each_format(fmt) { + if (!perf_hpp__is_sort_entry(fmt)) + continue; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); + hse->se->elide = false; + } } static int output_field_add(char *tok) -- cgit v1.2.3 From 1c89fe9b0447f0ce393325e51911f8073432b7b4 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 7 May 2014 18:42:24 +0900 Subject: perf tools: Introduce reset_output_field() The reset_output_field() function is for clearing output field settings and will be used for test code in later patch. Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1400480762-22852-19-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/hist.c | 17 +++++++++++++++++ tools/perf/util/hist.h | 7 +++++++ tools/perf/util/sort.c | 11 +++++++++++ tools/perf/util/sort.h | 1 + 4 files changed, 36 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 61cf31e094f3..4484f5bd1b14 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -459,6 +459,23 @@ next: } } +void perf_hpp__reset_output_field(void) +{ + struct perf_hpp_fmt *fmt, *tmp; + + /* reset output fields */ + perf_hpp__for_each_format_safe(fmt, tmp) { + list_del_init(&fmt->list); + list_del_init(&fmt->sort_list); + } + + /* reset sort keys */ + perf_hpp__for_each_sort_list_safe(fmt, tmp) { + list_del_init(&fmt->list); + list_del_init(&fmt->sort_list); + } +} + /* * See hists__fprintf to match the column widths */ diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 034db761630e..a8418d19808d 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -174,9 +174,15 @@ extern struct list_head perf_hpp__sort_list; #define perf_hpp__for_each_format(format) \ list_for_each_entry(format, &perf_hpp__list, list) +#define perf_hpp__for_each_format_safe(format, tmp) \ + list_for_each_entry_safe(format, tmp, &perf_hpp__list, list) + #define perf_hpp__for_each_sort_list(format) \ list_for_each_entry(format, &perf_hpp__sort_list, sort_list) +#define perf_hpp__for_each_sort_list_safe(format, tmp) \ + list_for_each_entry_safe(format, tmp, &perf_hpp__sort_list, sort_list) + extern struct perf_hpp_fmt perf_hpp__format[]; enum { @@ -197,6 +203,7 @@ void perf_hpp__column_register(struct perf_hpp_fmt *format); void perf_hpp__column_enable(unsigned col); void perf_hpp__register_sort_field(struct perf_hpp_fmt *format); void perf_hpp__setup_output_field(void); +void perf_hpp__reset_output_field(void); void perf_hpp__append_sort_keys(void); bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index d9132069d3b1..901b9bece2ee 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1573,3 +1573,14 @@ int setup_sorting(void) return 0; } + +void reset_output_field(void) +{ + sort__need_collapse = 0; + sort__has_parent = 0; + sort__has_sym = 0; + sort__has_dso = 0; + + reset_dimensions(); + perf_hpp__reset_output_field(); +} diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index f5a831c3d0fb..5f38d925e92f 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -194,6 +194,7 @@ extern struct list_head hist_entry__sort_list; int setup_sorting(void); int setup_output_field(void); +void reset_output_field(void); extern int sort_dimension__add(const char *); void sort__setup_elide(FILE *fp); -- cgit v1.2.3 From 21cfc5e10395eba795c0610cf3cf7f0a4f6f33ff Mon Sep 17 00:00:00 2001 From: Michael Lentine Date: Tue, 20 May 2014 11:48:49 +0200 Subject: perf tools: Add cat as fallback pager This patch adds a fallback to cat for the pager. This is useful on environments, such as Android, where less does not exist. It is better to default to cat than to abort. Signed-off-by: Michael Lentine Reviewed-by: Stephane Eranian Link: http://lkml.kernel.org/r/1400579330-5043-2-git-send-email-eranian@google.com Signed-off-by: Jiri Olsa --- tools/perf/util/pager.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c index 3322b8446e89..31ee02d4e988 100644 --- a/tools/perf/util/pager.c +++ b/tools/perf/util/pager.c @@ -57,13 +57,13 @@ void setup_pager(void) } if (!pager) pager = getenv("PAGER"); - if (!pager) { - if (!access("/usr/bin/pager", X_OK)) - pager = "/usr/bin/pager"; - } + if (!(pager || access("/usr/bin/pager", X_OK))) + pager = "/usr/bin/pager"; + if (!(pager || access("/usr/bin/less", X_OK))) + pager = "/usr/bin/less"; if (!pager) - pager = "less"; - else if (!*pager || !strcmp(pager, "cat")) + pager = "cat"; + if (!*pager || !strcmp(pager, "cat")) return; spawned_pager = 1; /* means we are emitting to terminal */ -- cgit v1.2.3 From eca8183699964579ca8a0b8d116bd1f4da0136f7 Mon Sep 17 00:00:00 2001 From: Michael Lentine Date: Tue, 20 May 2014 11:48:50 +0200 Subject: perf tools: Add automatic remapping of Android libraries This patch automatically adjusts the path of MMAP records associated with Android system libraries. The Android system is organized with system libraries found in /system/lib and user libraries in /data/app-lib. On the host system (not running Android), system libraries can be found in the downloaded NDK directory under ${NDK_ROOT}/platforms/${APP_PLATFORM}/arch-${ARCH}/usr/lib and the user libraries are installed under libs/${APP_ABI} within the apk build directory. This patch makes running the reporting tools possible on the host system using the libraries from the NDK. Signed-off-by: Michael Lentine Reviewed-by: Stephane Eranian Link: http://lkml.kernel.org/r/1400579330-5043-3-git-send-email-eranian@google.com [ fixed 'space required before the open parenthesis' checkpatch.pl errors ] Signed-off-by: Jiri Olsa --- tools/perf/util/map.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index ba5f5c0c838b..8ccbb32eda25 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -32,6 +32,93 @@ static inline int is_no_dso_memory(const char *filename) !strcmp(filename, "[heap]"); } +static inline int is_android_lib(const char *filename) +{ + return !strncmp(filename, "/data/app-lib", 13) || + !strncmp(filename, "/system/lib", 11); +} + +static inline bool replace_android_lib(const char *filename, char *newfilename) +{ + const char *libname; + char *app_abi; + size_t app_abi_length, new_length; + size_t lib_length = 0; + + libname = strrchr(filename, '/'); + if (libname) + lib_length = strlen(libname); + + app_abi = getenv("APP_ABI"); + if (!app_abi) + return false; + + app_abi_length = strlen(app_abi); + + if (!strncmp(filename, "/data/app-lib", 13)) { + char *apk_path; + + if (!app_abi_length) + return false; + + new_length = 7 + app_abi_length + lib_length; + + apk_path = getenv("APK_PATH"); + if (apk_path) { + new_length += strlen(apk_path) + 1; + if (new_length > PATH_MAX) + return false; + snprintf(newfilename, new_length, + "%s/libs/%s/%s", apk_path, app_abi, libname); + } else { + if (new_length > PATH_MAX) + return false; + snprintf(newfilename, new_length, + "libs/%s/%s", app_abi, libname); + } + return true; + } + + if (!strncmp(filename, "/system/lib/", 11)) { + char *ndk, *app; + const char *arch; + size_t ndk_length; + size_t app_length; + + ndk = getenv("NDK_ROOT"); + app = getenv("APP_PLATFORM"); + + if (!(ndk && app)) + return false; + + ndk_length = strlen(ndk); + app_length = strlen(app); + + if (!(ndk_length && app_length && app_abi_length)) + return false; + + arch = !strncmp(app_abi, "arm", 3) ? "arm" : + !strncmp(app_abi, "mips", 4) ? "mips" : + !strncmp(app_abi, "x86", 3) ? "x86" : NULL; + + if (!arch) + return false; + + new_length = 27 + ndk_length + + app_length + lib_length + + strlen(arch); + + if (new_length > PATH_MAX) + return false; + snprintf(newfilename, new_length, + "%s/platforms/%s/arch-%s/usr/lib/%s", + ndk, app, arch, libname); + + return true; + } + return false; +} + void map__init(struct map *map, enum map_type type, u64 start, u64 end, u64 pgoff, struct dso *dso) { @@ -59,8 +146,9 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, if (map != NULL) { char newfilename[PATH_MAX]; struct dso *dso; - int anon, no_dso, vdso; + int anon, no_dso, vdso, android; + android = is_android_lib(filename); anon = is_anon_memory(filename); vdso = is_vdso_map(filename); no_dso = is_no_dso_memory(filename); @@ -75,6 +163,11 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, filename = newfilename; } + if (android) { + if (replace_android_lib(filename, newfilename)) + filename = newfilename; + } + if (vdso) { pgoff = 0; dso = vdso__dso_findnew(dsos__list); -- cgit v1.2.3 From 1844dbcbe78503e0f4a8996d69da725d5e7a5177 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 28 May 2014 14:12:18 +0900 Subject: perf tools: Introduce hists__inc_nr_samples() There're some duplicate code for counting number of samples. Add hists__inc_nr_samples() and reuse it. Suggested-by: Jiri Olsa Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/r/1401335910-16832-2-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-report.c | 4 +--- tools/perf/builtin-sched.c | 2 +- tools/perf/builtin-top.c | 5 +---- tools/perf/tests/hists_filter.c | 4 +--- tools/perf/util/hist.c | 7 +++++++ tools/perf/util/hist.h | 1 + 7 files changed, 13 insertions(+), 12 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index d30d2c2e2a7a..bf52461a88bd 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -70,7 +70,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, return -ENOMEM; ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); - hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); + hists__inc_nr_samples(&evsel->hists, true); return ret; } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index bc0eec1ce4be..4a3b84dd4f41 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -92,9 +92,7 @@ static void report__inc_stats(struct report *rep, struct hist_entry *he) * counted in perf_session_deliver_event(). The dump_trace * requires this info is ready before going to the output tree. */ - hists__inc_nr_events(he->hists, PERF_RECORD_SAMPLE); - if (!he->filtered) - he->hists->stats.nr_non_filtered_samples++; + hists__inc_nr_samples(he->hists, he->filtered); } static int report__add_mem_hist_entry(struct report *rep, struct addr_location *al, diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index d7176830b9b2..c38d06c04775 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1428,7 +1428,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_ int err = 0; evsel->hists.stats.total_period += sample->period; - hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); + hists__inc_nr_samples(&evsel->hists, true); if (evsel->handler != NULL) { tracepoint_handler f = evsel->handler; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 5b389ce4cd15..51309264d210 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -252,10 +252,7 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, if (he == NULL) return NULL; - hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); - if (!he->filtered) - evsel->hists.stats.nr_non_filtered_samples++; - + hists__inc_nr_samples(&evsel->hists, he->filtered); return he; } diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index c5ba924a3581..0a71ef4b9158 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c @@ -85,9 +85,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) fake_samples[i].map = al.map; fake_samples[i].sym = al.sym; - hists__inc_nr_events(he->hists, PERF_RECORD_SAMPLE); - if (!he->filtered) - he->hists->stats.nr_non_filtered_samples++; + hists__inc_nr_samples(he->hists, he->filtered); } } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index b262b44b7a65..5943ba60f193 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -800,6 +800,13 @@ void hists__inc_nr_events(struct hists *hists, u32 type) events_stats__inc(&hists->stats, type); } +void hists__inc_nr_samples(struct hists *hists, bool filtered) +{ + events_stats__inc(&hists->stats, PERF_RECORD_SAMPLE); + if (!filtered) + hists->stats.nr_non_filtered_samples++; +} + static struct hist_entry *hists__add_dummy_entry(struct hists *hists, struct hist_entry *pair) { diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index a8418d19808d..03ae1dbb1b15 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -119,6 +119,7 @@ u64 hists__total_period(struct hists *hists); void hists__reset_stats(struct hists *hists); void hists__inc_stats(struct hists *hists, struct hist_entry *h); void hists__inc_nr_events(struct hists *hists, u32 type); +void hists__inc_nr_samples(struct hists *hists, bool filtered); void events_stats__inc(struct events_stats *stats, u32 type); size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); -- cgit v1.2.3 From 69bcb019fc809874f518559c8e5b0a90176f0532 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 30 Oct 2013 09:40:34 +0900 Subject: perf tools: Introduce struct hist_entry_iter There're some duplicate code when adding hist entries. They are different in that some have branch info or mem info but generally do same thing. So introduce new struct hist_entry_iter and add callbacks to customize each case in general way. The new perf_evsel__add_entry() function will look like: iter->prepare_entry(); iter->add_single_entry(); while (iter->next_entry()) iter->add_next_entry(); iter->finish_entry(); This will help further work like the cumulative callchain patchset. Signed-off-by: Namhyung Kim Tested-by: Arun Sharma Tested-by: Rodrigo Campos Cc: David Ahern Cc: Frederic Weisbecker Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1401335910-16832-3-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/builtin-report.c | 192 ++++---------------------- tools/perf/tests/hists_filter.c | 16 ++- tools/perf/tests/hists_output.c | 11 +- tools/perf/util/hist.c | 299 ++++++++++++++++++++++++++++++++++++++++ tools/perf/util/hist.h | 33 +++++ 5 files changed, 372 insertions(+), 179 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 4a3b84dd4f41..3201bdfa8c3f 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -76,163 +76,16 @@ static int report__config(const char *var, const char *value, void *cb) return perf_default_config(var, value, cb); } -static void report__inc_stats(struct report *rep, struct hist_entry *he) +static void report__inc_stats(struct report *rep, + struct hist_entry *he __maybe_unused) { /* - * The @he is either of a newly created one or an existing one - * merging current sample. We only want to count a new one so - * checking ->nr_events being 1. + * We cannot access @he at this time. Just assume it's a new entry. + * It'll be fixed once we have a callback mechanism in hist_iter. */ - if (he->stat.nr_events == 1) - rep->nr_entries++; - - /* - * Only counts number of samples at this stage as it's more - * natural to do it here and non-sample events are also - * counted in perf_session_deliver_event(). The dump_trace - * requires this info is ready before going to the output tree. - */ - hists__inc_nr_samples(he->hists, he->filtered); -} - -static int report__add_mem_hist_entry(struct report *rep, struct addr_location *al, - struct perf_sample *sample, struct perf_evsel *evsel) -{ - struct symbol *parent = NULL; - struct hist_entry *he; - struct mem_info *mi, *mx; - uint64_t cost; - int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); - - if (err) - return err; - - mi = sample__resolve_mem(sample, al); - if (!mi) - return -ENOMEM; - - if (rep->hide_unresolved && !al->sym) - return 0; - - cost = sample->weight; - if (!cost) - cost = 1; - - /* - * must pass period=weight in order to get the correct - * sorting from hists__collapse_resort() which is solely - * based on periods. We want sorting be done on nr_events * weight - * and this is indirectly achieved by passing period=weight here - * and the he_stat__add_period() function. - */ - he = __hists__add_entry(&evsel->hists, al, parent, NULL, mi, - cost, cost, 0); - if (!he) - return -ENOMEM; - - if (ui__has_annotation()) { - err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); - if (err) - goto out; - - mx = he->mem_info; - err = addr_map_symbol__inc_samples(&mx->daddr, evsel->idx); - if (err) - goto out; - } - - report__inc_stats(rep, he); - - err = hist_entry__append_callchain(he, sample); -out: - return err; -} - -static int report__add_branch_hist_entry(struct report *rep, struct addr_location *al, - struct perf_sample *sample, struct perf_evsel *evsel) -{ - struct symbol *parent = NULL; - unsigned i; - struct hist_entry *he; - struct branch_info *bi, *bx; - int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); - - if (err) - return err; - - bi = sample__resolve_bstack(sample, al); - if (!bi) - return -ENOMEM; - - for (i = 0; i < sample->branch_stack->nr; i++) { - if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym)) - continue; - - err = -ENOMEM; - - /* overwrite the 'al' to branch-to info */ - al->map = bi[i].to.map; - al->sym = bi[i].to.sym; - al->addr = bi[i].to.addr; - /* - * The report shows the percentage of total branches captured - * and not events sampled. Thus we use a pseudo period of 1. - */ - he = __hists__add_entry(&evsel->hists, al, parent, &bi[i], NULL, - 1, 1, 0); - if (he) { - if (ui__has_annotation()) { - bx = he->branch_info; - err = addr_map_symbol__inc_samples(&bx->from, - evsel->idx); - if (err) - goto out; - - err = addr_map_symbol__inc_samples(&bx->to, - evsel->idx); - if (err) - goto out; - } - report__inc_stats(rep, he); - } else - goto out; - } - err = 0; -out: - free(bi); - return err; + rep->nr_entries++; } -static int report__add_hist_entry(struct report *rep, struct perf_evsel *evsel, - struct addr_location *al, struct perf_sample *sample) -{ - struct symbol *parent = NULL; - struct hist_entry *he; - int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); - - if (err) - return err; - - he = __hists__add_entry(&evsel->hists, al, parent, NULL, NULL, - sample->period, sample->weight, - sample->transaction); - if (he == NULL) - return -ENOMEM; - - err = hist_entry__append_callchain(he, sample); - if (err) - goto out; - - if (ui__has_annotation()) - err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); - - report__inc_stats(rep, he); - -out: - return err; -} - - static int process_sample_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, @@ -241,6 +94,9 @@ static int process_sample_event(struct perf_tool *tool, { struct report *rep = container_of(tool, struct report, tool); struct addr_location al; + struct hist_entry_iter iter = { + .hide_unresolved = rep->hide_unresolved, + }; int ret; if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { @@ -255,22 +111,22 @@ static int process_sample_event(struct perf_tool *tool, if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) return 0; - if (sort__mode == SORT_MODE__BRANCH) { - ret = report__add_branch_hist_entry(rep, &al, sample, evsel); - if (ret < 0) - pr_debug("problem adding lbr entry, skipping event\n"); - } else if (rep->mem_mode == 1) { - ret = report__add_mem_hist_entry(rep, &al, sample, evsel); - if (ret < 0) - pr_debug("problem adding mem entry, skipping event\n"); - } else { - if (al.map != NULL) - al.map->dso->hit = 1; - - ret = report__add_hist_entry(rep, evsel, &al, sample); - if (ret < 0) - pr_debug("problem incrementing symbol period, skipping event\n"); - } + if (sort__mode == SORT_MODE__BRANCH) + iter.ops = &hist_iter_branch; + else if (rep->mem_mode) + iter.ops = &hist_iter_mem; + else + iter.ops = &hist_iter_normal; + + if (al.map != NULL) + al.map->dso->hit = 1; + + report__inc_stats(rep, NULL); + + ret = hist_entry_iter__add(&iter, &al, evsel, sample, rep->max_stack); + if (ret < 0) + pr_debug("problem adding hist entry, skipping event\n"); + return ret; } diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 0a71ef4b9158..76b02e1de701 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c @@ -42,11 +42,11 @@ static struct sample fake_samples[] = { { .pid = 300, .ip = 0xf0000 + 800, }, }; -static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) +static int add_hist_entries(struct perf_evlist *evlist, + struct machine *machine __maybe_unused) { struct perf_evsel *evsel; struct addr_location al; - struct hist_entry *he; struct perf_sample sample = { .cpu = 0, }; size_t i; @@ -62,6 +62,10 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) .misc = PERF_RECORD_MISC_USER, }, }; + struct hist_entry_iter iter = { + .ops = &hist_iter_normal, + .hide_unresolved = false, + }; /* make sure it has no filter at first */ evsel->hists.thread_filter = NULL; @@ -71,21 +75,19 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) sample.pid = fake_samples[i].pid; sample.tid = fake_samples[i].pid; sample.ip = fake_samples[i].ip; + sample.period = 100; if (perf_event__preprocess_sample(&event, machine, &al, &sample) < 0) goto out; - he = __hists__add_entry(&evsel->hists, &al, NULL, - NULL, NULL, 100, 1, 0); - if (he == NULL) + if (hist_entry_iter__add(&iter, &al, evsel, &sample, + PERF_MAX_STACK_DEPTH) < 0) goto out; fake_samples[i].thread = al.thread; fake_samples[i].map = al.map; fake_samples[i].sym = al.sym; - - hists__inc_nr_samples(he->hists, he->filtered); } } diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index a16850551797..1308f88a9169 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c @@ -46,7 +46,7 @@ static struct sample fake_samples[] = { static int add_hist_entries(struct hists *hists, struct machine *machine) { struct addr_location al; - struct hist_entry *he; + struct perf_evsel *evsel = hists_to_evsel(hists); struct perf_sample sample = { .period = 100, }; size_t i; @@ -56,6 +56,10 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) .misc = PERF_RECORD_MISC_USER, }, }; + struct hist_entry_iter iter = { + .ops = &hist_iter_normal, + .hide_unresolved = false, + }; sample.cpu = fake_samples[i].cpu; sample.pid = fake_samples[i].pid; @@ -66,9 +70,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) &sample) < 0) goto out; - he = __hists__add_entry(hists, &al, NULL, NULL, NULL, - sample.period, 1, 0); - if (he == NULL) + if (hist_entry_iter__add(&iter, &al, evsel, &sample, + PERF_MAX_STACK_DEPTH) < 0) goto out; fake_samples[i].thread = al.thread; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 5943ba60f193..d8662356de20 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -4,6 +4,7 @@ #include "session.h" #include "sort.h" #include "evsel.h" +#include "annotate.h" #include static bool hists__filter_entry_by_dso(struct hists *hists, @@ -429,6 +430,304 @@ struct hist_entry *__hists__add_entry(struct hists *hists, return add_hist_entry(hists, &entry, al); } +static int +iter_next_nop_entry(struct hist_entry_iter *iter __maybe_unused, + struct addr_location *al __maybe_unused) +{ + return 0; +} + +static int +iter_add_next_nop_entry(struct hist_entry_iter *iter __maybe_unused, + struct addr_location *al __maybe_unused) +{ + return 0; +} + +static int +iter_prepare_mem_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ + struct perf_sample *sample = iter->sample; + struct mem_info *mi; + + mi = sample__resolve_mem(sample, al); + if (mi == NULL) + return -ENOMEM; + + iter->priv = mi; + return 0; +} + +static int +iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ + u64 cost; + struct mem_info *mi = iter->priv; + struct hist_entry *he; + + if (mi == NULL) + return -EINVAL; + + cost = iter->sample->weight; + if (!cost) + cost = 1; + + /* + * must pass period=weight in order to get the correct + * sorting from hists__collapse_resort() which is solely + * based on periods. We want sorting be done on nr_events * weight + * and this is indirectly achieved by passing period=weight here + * and the he_stat__add_period() function. + */ + he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi, + cost, cost, 0); + if (!he) + return -ENOMEM; + + iter->he = he; + return 0; +} + +static int +iter_finish_mem_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ + struct perf_evsel *evsel = iter->evsel; + struct hist_entry *he = iter->he; + struct mem_info *mx; + int err = -EINVAL; + + if (he == NULL) + goto out; + + if (ui__has_annotation()) { + err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); + if (err) + goto out; + + mx = he->mem_info; + err = addr_map_symbol__inc_samples(&mx->daddr, evsel->idx); + if (err) + goto out; + } + + hists__inc_nr_samples(&evsel->hists, he->filtered); + + err = hist_entry__append_callchain(he, iter->sample); + +out: + /* + * We don't need to free iter->priv (mem_info) here since + * the mem info was either already freed in add_hist_entry() or + * passed to a new hist entry by hist_entry__new(). + */ + iter->priv = NULL; + + iter->he = NULL; + return err; +} + +static int +iter_prepare_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ + struct branch_info *bi; + struct perf_sample *sample = iter->sample; + + bi = sample__resolve_bstack(sample, al); + if (!bi) + return -ENOMEM; + + iter->curr = 0; + iter->total = sample->branch_stack->nr; + + iter->priv = bi; + return 0; +} + +static int +iter_add_single_branch_entry(struct hist_entry_iter *iter __maybe_unused, + struct addr_location *al __maybe_unused) +{ + return 0; +} + +static int +iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ + struct branch_info *bi = iter->priv; + int i = iter->curr; + + if (bi == NULL) + return 0; + + if (iter->curr >= iter->total) + return 0; + + al->map = bi[i].to.map; + al->sym = bi[i].to.sym; + al->addr = bi[i].to.addr; + return 1; +} + +static int +iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ + struct branch_info *bi, *bx; + struct perf_evsel *evsel = iter->evsel; + struct hist_entry *he = NULL; + int i = iter->curr; + int err = 0; + + bi = iter->priv; + + if (iter->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym)) + goto out; + + /* + * The report shows the percentage of total branches captured + * and not events sampled. Thus we use a pseudo period of 1. + */ + he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL, + 1, 1, 0); + if (he == NULL) + return -ENOMEM; + + if (ui__has_annotation()) { + bx = he->branch_info; + err = addr_map_symbol__inc_samples(&bx->from, evsel->idx); + if (err) + goto out; + + err = addr_map_symbol__inc_samples(&bx->to, evsel->idx); + if (err) + goto out; + } + + hists__inc_nr_samples(&evsel->hists, he->filtered); + +out: + iter->he = he; + iter->curr++; + return err; +} + +static int +iter_finish_branch_entry(struct hist_entry_iter *iter, + struct addr_location *al __maybe_unused) +{ + zfree(&iter->priv); + iter->he = NULL; + + return iter->curr >= iter->total ? 0 : -1; +} + +static int +iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused, + struct addr_location *al __maybe_unused) +{ + return 0; +} + +static int +iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ + struct perf_evsel *evsel = iter->evsel; + struct perf_sample *sample = iter->sample; + struct hist_entry *he; + + he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, + sample->period, sample->weight, + sample->transaction); + if (he == NULL) + return -ENOMEM; + + iter->he = he; + return 0; +} + +static int +iter_finish_normal_entry(struct hist_entry_iter *iter, struct addr_location *al) +{ + int err; + struct hist_entry *he = iter->he; + struct perf_evsel *evsel = iter->evsel; + struct perf_sample *sample = iter->sample; + + if (he == NULL) + return 0; + + iter->he = NULL; + + if (ui__has_annotation()) { + err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); + if (err) + return err; + } + + hists__inc_nr_samples(&evsel->hists, he->filtered); + + return hist_entry__append_callchain(he, sample); +} + +const struct hist_iter_ops hist_iter_mem = { + .prepare_entry = iter_prepare_mem_entry, + .add_single_entry = iter_add_single_mem_entry, + .next_entry = iter_next_nop_entry, + .add_next_entry = iter_add_next_nop_entry, + .finish_entry = iter_finish_mem_entry, +}; + +const struct hist_iter_ops hist_iter_branch = { + .prepare_entry = iter_prepare_branch_entry, + .add_single_entry = iter_add_single_branch_entry, + .next_entry = iter_next_branch_entry, + .add_next_entry = iter_add_next_branch_entry, + .finish_entry = iter_finish_branch_entry, +}; + +const struct hist_iter_ops hist_iter_normal = { + .prepare_entry = iter_prepare_normal_entry, + .add_single_entry = iter_add_single_normal_entry, + .next_entry = iter_next_nop_entry, + .add_next_entry = iter_add_next_nop_entry, + .finish_entry = iter_finish_normal_entry, +}; + +int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, + struct perf_evsel *evsel, struct perf_sample *sample, + int max_stack_depth) +{ + int err, err2; + + err = sample__resolve_callchain(sample, &iter->parent, evsel, al, + max_stack_depth); + if (err) + return err; + + iter->evsel = evsel; + iter->sample = sample; + + err = iter->ops->prepare_entry(iter, al); + if (err) + goto out; + + err = iter->ops->add_single_entry(iter, al); + if (err) + goto out; + + while (iter->ops->next_entry(iter, al)) { + err = iter->ops->add_next_entry(iter, al); + if (err) + break; + } + +out: + err2 = iter->ops->finish_entry(iter, al); + if (!err) + err = err2; + + return err; +} + int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) { diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 03ae1dbb1b15..8894f184357c 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -96,12 +96,45 @@ struct hists { u16 col_len[HISTC_NR_COLS]; }; +struct hist_entry_iter; + +struct hist_iter_ops { + int (*prepare_entry)(struct hist_entry_iter *, struct addr_location *); + int (*add_single_entry)(struct hist_entry_iter *, struct addr_location *); + int (*next_entry)(struct hist_entry_iter *, struct addr_location *); + int (*add_next_entry)(struct hist_entry_iter *, struct addr_location *); + int (*finish_entry)(struct hist_entry_iter *, struct addr_location *); +}; + +struct hist_entry_iter { + int total; + int curr; + + bool hide_unresolved; + + struct perf_evsel *evsel; + struct perf_sample *sample; + struct hist_entry *he; + struct symbol *parent; + void *priv; + + const struct hist_iter_ops *ops; +}; + +extern const struct hist_iter_ops hist_iter_normal; +extern const struct hist_iter_ops hist_iter_branch; +extern const struct hist_iter_ops hist_iter_mem; + struct hist_entry *__hists__add_entry(struct hists *hists, struct addr_location *al, struct symbol *parent, struct branch_info *bi, struct mem_info *mi, u64 period, u64 weight, u64 transaction); +int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, + struct perf_evsel *evsel, struct perf_sample *sample, + int max_stack_depth); + int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); int hist_entry__transaction_len(void); -- cgit v1.2.3 From f8be1c8c48c8469d1ce95ccdc77b1e2c6a29700e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 11 Sep 2012 13:15:07 +0900 Subject: perf hists: Add support for accumulated stat of hist entry Maintain accumulated stat information in hist_entry->stat_acc if symbol_conf.cumulate_callchain is set. Fields in ->stat_acc have same vaules initially, and will be updated as callchain is processed later. Signed-off-by: Namhyung Kim Tested-by: Arun Sharma Tested-by: Rodrigo Campos Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1401335910-16832-4-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/hist.c | 28 ++++++++++++++++++++++++++-- tools/perf/util/sort.h | 1 + tools/perf/util/symbol.h | 1 + 3 files changed, 28 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index d8662356de20..dfff2ee8effb 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -232,6 +232,8 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) return true; he_stat__decay(&he->stat); + if (symbol_conf.cumulate_callchain) + he_stat__decay(he->stat_acc); diff = prev_period - he->stat.period; @@ -279,12 +281,26 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) static struct hist_entry *hist_entry__new(struct hist_entry *template) { - size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; - struct hist_entry *he = zalloc(sizeof(*he) + callchain_size); + size_t callchain_size = 0; + struct hist_entry *he; + + if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) + callchain_size = sizeof(struct callchain_root); + + he = zalloc(sizeof(*he) + callchain_size); if (he != NULL) { *he = *template; + if (symbol_conf.cumulate_callchain) { + he->stat_acc = malloc(sizeof(he->stat)); + if (he->stat_acc == NULL) { + free(he); + return NULL; + } + memcpy(he->stat_acc, &he->stat, sizeof(he->stat)); + } + if (he->ms.map) he->ms.map->referenced = true; @@ -296,6 +312,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) */ he->branch_info = malloc(sizeof(*he->branch_info)); if (he->branch_info == NULL) { + free(he->stat_acc); free(he); return NULL; } @@ -359,6 +376,8 @@ static struct hist_entry *add_hist_entry(struct hists *hists, if (!cmp) { he_stat__add_period(&he->stat, period, weight); + if (symbol_conf.cumulate_callchain) + he_stat__add_period(he->stat_acc, period, weight); /* * This mem info was allocated from sample__resolve_mem @@ -394,6 +413,8 @@ static struct hist_entry *add_hist_entry(struct hists *hists, rb_insert_color(&he->rb_node_in, hists->entries_in); out: he_stat__add_cpumode_period(&he->stat, al->cpumode, period); + if (symbol_conf.cumulate_callchain) + he_stat__add_cpumode_period(he->stat_acc, al->cpumode, period); return he; } @@ -768,6 +789,7 @@ void hist_entry__free(struct hist_entry *he) { zfree(&he->branch_info); zfree(&he->mem_info); + zfree(&he->stat_acc); free_srcline(he->srcline); free(he); } @@ -793,6 +815,8 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, if (!cmp) { he_stat__add_stat(&iter->stat, &he->stat); + if (symbol_conf.cumulate_callchain) + he_stat__add_stat(iter->stat_acc, he->stat_acc); if (symbol_conf.use_callchain) { callchain_cursor_reset(&callchain_cursor); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 5f38d925e92f..c9ffa031becd 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -82,6 +82,7 @@ struct hist_entry { struct list_head head; } pairs; struct he_stat stat; + struct he_stat *stat_acc; struct map_symbol ms; struct thread *thread; struct comm *comm; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 33ede53fa6b9..615c752dd767 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -109,6 +109,7 @@ struct symbol_conf { show_nr_samples, show_total_period, use_callchain, + cumulate_callchain, exclude_other, show_cpu_utilization, initialized, -- cgit v1.2.3 From a0b51af367a6831330564c96dc4cc1ac63413701 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 11 Sep 2012 13:34:27 +0900 Subject: perf hists: Check if accumulated when adding a hist entry To support callchain accumulation, @entry should be recognized if it's accumulated or not when add_hist_entry() called. The period of an accumulated entry should be added to ->stat_acc but not ->stat. Add @sample_self arg for that. Signed-off-by: Namhyung Kim Tested-by: Arun Sharma Tested-by: Rodrigo Campos Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1401335910-16832-5-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/builtin-annotate.c | 3 ++- tools/perf/builtin-diff.c | 2 +- tools/perf/builtin-top.c | 2 +- tools/perf/tests/hists_link.c | 4 ++-- tools/perf/util/hist.c | 29 ++++++++++++++++++----------- tools/perf/util/hist.h | 3 ++- 6 files changed, 26 insertions(+), 17 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index bf52461a88bd..1ec429fef2be 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -65,7 +65,8 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, return 0; } - he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0); + he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0, + true); if (he == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 8bff543acaab..9a5a035cb426 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -315,7 +315,7 @@ static int hists__add_entry(struct hists *hists, u64 weight, u64 transaction) { if (__hists__add_entry(hists, al, NULL, NULL, NULL, period, weight, - transaction) != NULL) + transaction, true) != NULL) return 0; return -ENOMEM; } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 51309264d210..12e2e1227e47 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -247,7 +247,7 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, pthread_mutex_lock(&evsel->hists.lock); he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, sample->period, sample->weight, - sample->transaction); + sample->transaction, true); pthread_mutex_unlock(&evsel->hists.lock); if (he == NULL) return NULL; diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 5ffa2c3eb77d..ca6693b37cd7 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -88,7 +88,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) goto out; he = __hists__add_entry(&evsel->hists, &al, NULL, - NULL, NULL, 1, 1, 0); + NULL, NULL, 1, 1, 0, true); if (he == NULL) goto out; @@ -112,7 +112,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) goto out; he = __hists__add_entry(&evsel->hists, &al, NULL, - NULL, NULL, 1, 1, 0); + NULL, NULL, 1, 1, 0, true); if (he == NULL) goto out; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index dfff2ee8effb..b9facf33b224 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -279,7 +279,8 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) * histogram, sorted on item, collects periods */ -static struct hist_entry *hist_entry__new(struct hist_entry *template) +static struct hist_entry *hist_entry__new(struct hist_entry *template, + bool sample_self) { size_t callchain_size = 0; struct hist_entry *he; @@ -299,6 +300,8 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) return NULL; } memcpy(he->stat_acc, &he->stat, sizeof(he->stat)); + if (!sample_self) + memset(&he->stat, 0, sizeof(he->stat)); } if (he->ms.map) @@ -351,7 +354,8 @@ static u8 symbol__parent_filter(const struct symbol *parent) static struct hist_entry *add_hist_entry(struct hists *hists, struct hist_entry *entry, - struct addr_location *al) + struct addr_location *al, + bool sample_self) { struct rb_node **p; struct rb_node *parent = NULL; @@ -375,7 +379,8 @@ static struct hist_entry *add_hist_entry(struct hists *hists, cmp = hist_entry__cmp(he, entry); if (!cmp) { - he_stat__add_period(&he->stat, period, weight); + if (sample_self) + he_stat__add_period(&he->stat, period, weight); if (symbol_conf.cumulate_callchain) he_stat__add_period(he->stat_acc, period, weight); @@ -405,14 +410,15 @@ static struct hist_entry *add_hist_entry(struct hists *hists, p = &(*p)->rb_right; } - he = hist_entry__new(entry); + he = hist_entry__new(entry, sample_self); if (!he) return NULL; rb_link_node(&he->rb_node_in, parent, p); rb_insert_color(&he->rb_node_in, hists->entries_in); out: - he_stat__add_cpumode_period(&he->stat, al->cpumode, period); + if (sample_self) + he_stat__add_cpumode_period(&he->stat, al->cpumode, period); if (symbol_conf.cumulate_callchain) he_stat__add_cpumode_period(he->stat_acc, al->cpumode, period); return he; @@ -423,7 +429,8 @@ struct hist_entry *__hists__add_entry(struct hists *hists, struct symbol *sym_parent, struct branch_info *bi, struct mem_info *mi, - u64 period, u64 weight, u64 transaction) + u64 period, u64 weight, u64 transaction, + bool sample_self) { struct hist_entry entry = { .thread = al->thread, @@ -448,7 +455,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, .transaction = transaction, }; - return add_hist_entry(hists, &entry, al); + return add_hist_entry(hists, &entry, al, sample_self); } static int @@ -501,7 +508,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al * and the he_stat__add_period() function. */ he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi, - cost, cost, 0); + cost, cost, 0, true); if (!he) return -ENOMEM; @@ -608,7 +615,7 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a * and not events sampled. Thus we use a pseudo period of 1. */ he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL, - 1, 1, 0); + 1, 1, 0, true); if (he == NULL) return -ENOMEM; @@ -657,7 +664,7 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, sample->period, sample->weight, - sample->transaction); + sample->transaction, true); if (he == NULL) return -ENOMEM; @@ -1161,7 +1168,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists, p = &(*p)->rb_right; } - he = hist_entry__new(pair); + he = hist_entry__new(pair, true); if (he) { memset(&he->stat, 0, sizeof(he->stat)); he->hists = hists; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 8894f184357c..bedb24d3643c 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -130,7 +130,8 @@ struct hist_entry *__hists__add_entry(struct hists *hists, struct symbol *parent, struct branch_info *bi, struct mem_info *mi, u64 period, - u64 weight, u64 transaction); + u64 weight, u64 transaction, + bool sample_self); int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, struct perf_evsel *evsel, struct perf_sample *sample, int max_stack_depth); -- cgit v1.2.3 From 7a13aa28aa268359cee006059731f49bcd1f839e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 11 Sep 2012 14:13:04 +0900 Subject: perf hists: Accumulate hist entry stat based on the callchain Call __hists__add_entry() for each callchain node to get an accumulated stat for an entry. Introduce new cumulative_iter ops to process them properly. Signed-off-by: Namhyung Kim Tested-by: Arun Sharma Tested-by: Rodrigo Campos Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1401335910-16832-6-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/builtin-report.c | 2 + tools/perf/util/callchain.c | 3 +- tools/perf/util/hist.c | 96 +++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/hist.h | 1 + 4 files changed, 101 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3201bdfa8c3f..e8fa9fea341f 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -115,6 +115,8 @@ static int process_sample_event(struct perf_tool *tool, iter.ops = &hist_iter_branch; else if (rep->mem_mode) iter.ops = &hist_iter_mem; + else if (symbol_conf.cumulate_callchain) + iter.ops = &hist_iter_cumulative; else iter.ops = &hist_iter_normal; diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 9a42382b3921..2af69c47b725 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -616,7 +616,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent if (sample->callchain == NULL) return 0; - if (symbol_conf.use_callchain || sort__has_parent) { + if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || + sort__has_parent) { return machine__resolve_callchain(al->machine, evsel, al->thread, sample, parent, al, max_stack); } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index b9facf33b224..6079b5acfb6d 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -696,6 +696,94 @@ iter_finish_normal_entry(struct hist_entry_iter *iter, struct addr_location *al) return hist_entry__append_callchain(he, sample); } +static int +iter_prepare_cumulative_entry(struct hist_entry_iter *iter __maybe_unused, + struct addr_location *al __maybe_unused) +{ + callchain_cursor_commit(&callchain_cursor); + return 0; +} + +static int +iter_add_single_cumulative_entry(struct hist_entry_iter *iter, + struct addr_location *al) +{ + struct perf_evsel *evsel = iter->evsel; + struct perf_sample *sample = iter->sample; + struct hist_entry *he; + int err = 0; + + he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, + sample->period, sample->weight, + sample->transaction, true); + if (he == NULL) + return -ENOMEM; + + iter->he = he; + + /* + * The iter->he will be over-written after ->add_next_entry() + * called so inc stats for the original entry now. + */ + if (ui__has_annotation()) + err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); + + hists__inc_nr_samples(&evsel->hists, he->filtered); + + return err; +} + +static int +iter_next_cumulative_entry(struct hist_entry_iter *iter, + struct addr_location *al) +{ + struct callchain_cursor_node *node; + + node = callchain_cursor_current(&callchain_cursor); + if (node == NULL) + return 0; + + al->map = node->map; + al->sym = node->sym; + if (node->map) + al->addr = node->map->map_ip(node->map, node->ip); + else + al->addr = node->ip; + + if (iter->hide_unresolved && al->sym == NULL) + return 0; + + callchain_cursor_advance(&callchain_cursor); + return 1; +} + +static int +iter_add_next_cumulative_entry(struct hist_entry_iter *iter, + struct addr_location *al) +{ + struct perf_evsel *evsel = iter->evsel; + struct perf_sample *sample = iter->sample; + struct hist_entry *he; + + he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, + sample->period, sample->weight, + sample->transaction, false); + if (he == NULL) + return -ENOMEM; + + iter->he = he; + + return 0; +} + +static int +iter_finish_cumulative_entry(struct hist_entry_iter *iter, + struct addr_location *al __maybe_unused) +{ + iter->he = NULL; + return 0; +} + const struct hist_iter_ops hist_iter_mem = { .prepare_entry = iter_prepare_mem_entry, .add_single_entry = iter_add_single_mem_entry, @@ -720,6 +808,14 @@ const struct hist_iter_ops hist_iter_normal = { .finish_entry = iter_finish_normal_entry, }; +const struct hist_iter_ops hist_iter_cumulative = { + .prepare_entry = iter_prepare_cumulative_entry, + .add_single_entry = iter_add_single_cumulative_entry, + .next_entry = iter_next_cumulative_entry, + .add_next_entry = iter_add_next_cumulative_entry, + .finish_entry = iter_finish_cumulative_entry, +}; + int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, struct perf_evsel *evsel, struct perf_sample *sample, int max_stack_depth) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index bedb24d3643c..78409f95d012 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -124,6 +124,7 @@ struct hist_entry_iter { extern const struct hist_iter_ops hist_iter_normal; extern const struct hist_iter_ops hist_iter_branch; extern const struct hist_iter_ops hist_iter_mem; +extern const struct hist_iter_ops hist_iter_cumulative; struct hist_entry *__hists__add_entry(struct hists *hists, struct addr_location *al, -- cgit v1.2.3 From c7405d85d7a354b8ba49e2db7c4b027e6cb997c1 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 31 Oct 2013 13:58:30 +0900 Subject: perf tools: Update cpumode for each cumulative entry The cpumode and level in struct addr_localtion was set for a sample and but updated as cumulative callchains were added. This led to have non-matching symbol and cpumode in the output. Update it accordingly based on the fact whether the map is a part of the kernel or not. This is a reverse of what thread__find_addr_map() does. Signed-off-by: Namhyung Kim Tested-by: Arun Sharma Tested-by: Rodrigo Campos Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1401335910-16832-7-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/callchain.c | 42 ++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/callchain.h | 2 ++ tools/perf/util/hist.c | 13 ++----------- 3 files changed, 46 insertions(+), 11 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 2af69c47b725..48b6d3f50012 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -630,3 +630,45 @@ int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *samp return 0; return callchain_append(he->callchain, &callchain_cursor, sample->period); } + +int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node, + bool hide_unresolved) +{ + al->map = node->map; + al->sym = node->sym; + if (node->map) + al->addr = node->map->map_ip(node->map, node->ip); + else + al->addr = node->ip; + + if (al->sym == NULL) { + if (hide_unresolved) + return 0; + if (al->map == NULL) + goto out; + } + + if (al->map->groups == &al->machine->kmaps) { + if (machine__is_host(al->machine)) { + al->cpumode = PERF_RECORD_MISC_KERNEL; + al->level = 'k'; + } else { + al->cpumode = PERF_RECORD_MISC_GUEST_KERNEL; + al->level = 'g'; + } + } else { + if (machine__is_host(al->machine)) { + al->cpumode = PERF_RECORD_MISC_USER; + al->level = '.'; + } else if (perf_guest) { + al->cpumode = PERF_RECORD_MISC_GUEST_USER; + al->level = 'u'; + } else { + al->cpumode = PERF_RECORD_MISC_HYPERVISOR; + al->level = 'H'; + } + } + +out: + return 1; +} diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index bde2b0cc24cf..24a53d562d0a 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -162,6 +162,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent struct perf_evsel *evsel, struct addr_location *al, int max_stack); int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); +int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node, + bool hide_unresolved); extern const char record_callchain_help[]; int parse_callchain_report_opt(const char *arg); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 6079b5acfb6d..37c28fc13dc3 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -743,18 +743,9 @@ iter_next_cumulative_entry(struct hist_entry_iter *iter, if (node == NULL) return 0; - al->map = node->map; - al->sym = node->sym; - if (node->map) - al->addr = node->map->map_ip(node->map, node->ip); - else - al->addr = node->ip; - - if (iter->hide_unresolved && al->sym == NULL) - return 0; - callchain_cursor_advance(&callchain_cursor); - return 1; + + return fill_callchain_info(al, node, iter->hide_unresolved); } static int -- cgit v1.2.3 From b4d3c8bd86c4eda08456691121f83b4e1db46866 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 31 Oct 2013 10:05:29 +0900 Subject: perf report: Cache cumulative callchains It is possble that a callchain has cycles or recursive calls. In that case it'll end up having entries more than 100% overhead in the output. In order to prevent such entries, cache each callchain node and skip if same entry already cumulated. Signed-off-by: Namhyung Kim Tested-by: Arun Sharma Tested-by: Rodrigo Campos Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1401335910-16832-8-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/hist.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 37c28fc13dc3..bf03db528db6 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -700,7 +700,22 @@ static int iter_prepare_cumulative_entry(struct hist_entry_iter *iter __maybe_unused, struct addr_location *al __maybe_unused) { + struct hist_entry **he_cache; + callchain_cursor_commit(&callchain_cursor); + + /* + * This is for detecting cycles or recursions so that they're + * cumulated only one time to prevent entries more than 100% + * overhead. + */ + he_cache = malloc(sizeof(*he_cache) * (PERF_MAX_STACK_DEPTH + 1)); + if (he_cache == NULL) + return -ENOMEM; + + iter->priv = he_cache; + iter->curr = 0; + return 0; } @@ -710,6 +725,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, { struct perf_evsel *evsel = iter->evsel; struct perf_sample *sample = iter->sample; + struct hist_entry **he_cache = iter->priv; struct hist_entry *he; int err = 0; @@ -720,6 +736,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, return -ENOMEM; iter->he = he; + he_cache[iter->curr++] = he; /* * The iter->he will be over-written after ->add_next_entry() @@ -754,7 +771,29 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, { struct perf_evsel *evsel = iter->evsel; struct perf_sample *sample = iter->sample; + struct hist_entry **he_cache = iter->priv; struct hist_entry *he; + struct hist_entry he_tmp = { + .cpu = al->cpu, + .thread = al->thread, + .comm = thread__comm(al->thread), + .ip = al->addr, + .ms = { + .map = al->map, + .sym = al->sym, + }, + .parent = iter->parent, + }; + int i; + + /* + * Check if there's duplicate entries in the callchain. + * It's possible that it has cycles or recursive calls. + */ + for (i = 0; i < iter->curr; i++) { + if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) + return 0; + } he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, sample->period, sample->weight, @@ -763,6 +802,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, return -ENOMEM; iter->he = he; + he_cache[iter->curr++] = he; return 0; } @@ -771,7 +811,9 @@ static int iter_finish_cumulative_entry(struct hist_entry_iter *iter, struct addr_location *al __maybe_unused) { + zfree(&iter->priv); iter->he = NULL; + return 0; } -- cgit v1.2.3 From be1f13e30862ab6b0fffaecd556856a965cefa0c Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 10 Sep 2012 13:38:00 +0900 Subject: perf callchain: Add callchain_cursor_snapshot() The callchain_cursor_snapshot() is for saving current status of the callchain. It'll be used to accumulate callchain information for each node. Signed-off-by: Namhyung Kim Tested-by: Arun Sharma Tested-by: Rodrigo Campos Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1401335910-16832-9-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/callchain.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 24a53d562d0a..8f84423a75da 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -167,4 +167,13 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node * extern const char record_callchain_help[]; int parse_callchain_report_opt(const char *arg); + +static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, + struct callchain_cursor *src) +{ + *dest = *src; + + dest->first = src->curr; + dest->nr -= src->pos; +} #endif /* __PERF_CALLCHAIN_H */ -- cgit v1.2.3 From be7f855a3eebe07f797b9e4a43bf59bab8ca3dbe Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 26 Dec 2013 17:44:10 +0900 Subject: perf tools: Save callchain info for each cumulative entry When accumulating callchain entry, also save current snapshot of the chain so that it can show the rest of the chain. Signed-off-by: Namhyung Kim Tested-by: Arun Sharma Tested-by: Rodrigo Campos Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1401335910-16832-10-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/hist.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index bf03db528db6..c6f5f5251aad 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -738,6 +738,14 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, iter->he = he; he_cache[iter->curr++] = he; + callchain_append(he->callchain, &callchain_cursor, sample->period); + + /* + * We need to re-initialize the cursor since callchain_append() + * advanced the cursor to the end. + */ + callchain_cursor_commit(&callchain_cursor); + /* * The iter->he will be over-written after ->add_next_entry() * called so inc stats for the original entry now. @@ -760,8 +768,6 @@ iter_next_cumulative_entry(struct hist_entry_iter *iter, if (node == NULL) return 0; - callchain_cursor_advance(&callchain_cursor); - return fill_callchain_info(al, node, iter->hide_unresolved); } @@ -785,6 +791,11 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, .parent = iter->parent, }; int i; + struct callchain_cursor cursor; + + callchain_cursor_snapshot(&cursor, &callchain_cursor); + + callchain_cursor_advance(&callchain_cursor); /* * Check if there's duplicate entries in the callchain. @@ -804,6 +815,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, iter->he = he; he_cache[iter->curr++] = he; + callchain_append(he->callchain, &cursor, sample->period); return 0; } -- cgit v1.2.3 From 594dcbf3186e2e1e5c08fa21e8826b90d347f23f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 30 Oct 2013 16:06:59 +0900 Subject: perf ui/hist: Add support to accumulated hist stat Print accumulated stat of a hist entry if requested. To do that, add new HPP_PERCENT_ACC_FNS macro and generate a perf_hpp_fmt using it. The __hpp__sort_acc() function sorts entries by accumulated period value. When accumulated periods of two entries are same (i.e. single path callchain) put the caller above since accumulation tends to put callers on higher position for obvious reason. Also add "overhead_children" output field to be selected by user. Signed-off-by: Namhyung Kim Tested-by: Arun Sharma Tested-by: Rodrigo Campos Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1401335910-16832-11-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/hist.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/hist.h | 4 ++ tools/perf/util/sort.c | 1 + 3 files changed, 104 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 4484f5bd1b14..0ce3e79b2ca7 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -104,6 +104,18 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, return ret; } +int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he, + hpp_field_fn get_field, const char *fmt, + hpp_snprint_fn print_fn, bool fmt_percent) +{ + if (!symbol_conf.cumulate_callchain) { + return snprintf(hpp->buf, hpp->size, "%*s", + fmt_percent ? 8 : 12, "N/A"); + } + + return __hpp__fmt(hpp, he, get_field, fmt, print_fn, fmt_percent); +} + static int field_cmp(u64 field_a, u64 field_b) { if (field_a > field_b) @@ -160,6 +172,24 @@ out: return ret; } +static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, + hpp_field_fn get_field) +{ + s64 ret = 0; + + if (symbol_conf.cumulate_callchain) { + /* + * Put caller above callee when they have equal period. + */ + ret = field_cmp(get_field(a), get_field(b)); + if (ret) + return ret; + + ret = b->callchain->max_depth - a->callchain->max_depth; + } + return ret; +} + #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ struct perf_hpp *hpp, \ @@ -242,6 +272,34 @@ static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ return __hpp__sort(a, b, he_get_##_field); \ } +#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ +static u64 he_get_acc_##_field(struct hist_entry *he) \ +{ \ + return he->stat_acc->_field; \ +} \ + \ +static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ + struct perf_hpp *hpp, struct hist_entry *he) \ +{ \ + return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%", \ + hpp_color_scnprintf, true); \ +} + +#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \ +static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ + struct perf_hpp *hpp, struct hist_entry *he) \ +{ \ + const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ + return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, fmt, \ + hpp_entry_scnprintf, true); \ +} + +#define __HPP_SORT_ACC_FN(_type, _field) \ +static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ +{ \ + return __hpp__sort_acc(a, b, he_get_acc_##_field); \ +} + #define __HPP_ENTRY_RAW_FN(_type, _field) \ static u64 he_get_raw_##_field(struct hist_entry *he) \ { \ @@ -270,18 +328,27 @@ __HPP_COLOR_PERCENT_FN(_type, _field) \ __HPP_ENTRY_PERCENT_FN(_type, _field) \ __HPP_SORT_FN(_type, _field) +#define HPP_PERCENT_ACC_FNS(_type, _str, _field, _min_width, _unit_width)\ +__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ +__HPP_WIDTH_FN(_type, _min_width, _unit_width) \ +__HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ +__HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \ +__HPP_SORT_ACC_FN(_type, _field) + #define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \ __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ __HPP_ENTRY_RAW_FN(_type, _field) \ __HPP_SORT_RAW_FN(_type, _field) +__HPP_HEADER_FN(overhead_self, "Self", 8, 8) HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8) HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8) HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8) HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) +HPP_PERCENT_ACC_FNS(overhead_acc, "Children", period, 8, 8) HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) HPP_RAW_FNS(period, "Period", period, 12, 12) @@ -303,6 +370,17 @@ static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused, .sort = hpp__sort_ ## _name, \ } +#define HPP__COLOR_ACC_PRINT_FNS(_name) \ + { \ + .header = hpp__header_ ## _name, \ + .width = hpp__width_ ## _name, \ + .color = hpp__color_ ## _name, \ + .entry = hpp__entry_ ## _name, \ + .cmp = hpp__nop_cmp, \ + .collapse = hpp__nop_cmp, \ + .sort = hpp__sort_ ## _name, \ + } + #define HPP__PRINT_FNS(_name) \ { \ .header = hpp__header_ ## _name, \ @@ -319,6 +397,7 @@ struct perf_hpp_fmt perf_hpp__format[] = { HPP__COLOR_PRINT_FNS(overhead_us), HPP__COLOR_PRINT_FNS(overhead_guest_sys), HPP__COLOR_PRINT_FNS(overhead_guest_us), + HPP__COLOR_ACC_PRINT_FNS(overhead_acc), HPP__PRINT_FNS(samples), HPP__PRINT_FNS(period) }; @@ -328,16 +407,23 @@ LIST_HEAD(perf_hpp__sort_list); #undef HPP__COLOR_PRINT_FNS +#undef HPP__COLOR_ACC_PRINT_FNS #undef HPP__PRINT_FNS #undef HPP_PERCENT_FNS +#undef HPP_PERCENT_ACC_FNS #undef HPP_RAW_FNS #undef __HPP_HEADER_FN #undef __HPP_WIDTH_FN #undef __HPP_COLOR_PERCENT_FN #undef __HPP_ENTRY_PERCENT_FN +#undef __HPP_COLOR_ACC_PERCENT_FN +#undef __HPP_ENTRY_ACC_PERCENT_FN #undef __HPP_ENTRY_RAW_FN +#undef __HPP_SORT_FN +#undef __HPP_SORT_ACC_FN +#undef __HPP_SORT_RAW_FN void perf_hpp__init(void) @@ -361,6 +447,13 @@ void perf_hpp__init(void) if (field_order) return; + if (symbol_conf.cumulate_callchain) { + perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC); + + perf_hpp__format[PERF_HPP__OVERHEAD].header = + hpp__header_overhead_self; + } + perf_hpp__column_enable(PERF_HPP__OVERHEAD); if (symbol_conf.show_cpu_utilization) { @@ -383,6 +476,12 @@ void perf_hpp__init(void) list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list; if (list_empty(list)) list_add(list, &perf_hpp__sort_list); + + if (symbol_conf.cumulate_callchain) { + list = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC].sort_list; + if (list_empty(list)) + list_add(list, &perf_hpp__sort_list); + } } void perf_hpp__column_register(struct perf_hpp_fmt *format) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 78409f95d012..efd73e489027 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -228,6 +228,7 @@ enum { PERF_HPP__OVERHEAD_US, PERF_HPP__OVERHEAD_GUEST_SYS, PERF_HPP__OVERHEAD_GUEST_US, + PERF_HPP__OVERHEAD_ACC, PERF_HPP__SAMPLES, PERF_HPP__PERIOD, @@ -254,6 +255,9 @@ typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, hpp_field_fn get_field, const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent); +int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he, + hpp_field_fn get_field, const char *fmt, + hpp_snprint_fn print_fn, bool fmt_percent); static inline void advance_hpp(struct perf_hpp *hpp, int inc) { diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 901b9bece2ee..9da8931d2394 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1061,6 +1061,7 @@ static struct hpp_dimension hpp_sort_dimensions[] = { DIM(PERF_HPP__OVERHEAD_US, "overhead_us"), DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"), DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"), + DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"), DIM(PERF_HPP__SAMPLES, "sample"), DIM(PERF_HPP__PERIOD, "period"), }; -- cgit v1.2.3 From 14135663f1d770bb057f8bf345e5436c985eb29c Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 31 Oct 2013 10:17:39 +0900 Subject: perf tools: Apply percent-limit to cumulative percentage If -g cumulative option is given, it needs to show entries which don't have self overhead. So apply percent-limit to accumulated overhead percentage in this case. Signed-off-by: Namhyung Kim Tested-by: Arun Sharma Tested-by: Rodrigo Campos Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1401335910-16832-14-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/browsers/hists.c | 40 +++++++++++----------------------------- tools/perf/ui/gtk/hists.c | 6 ++---- tools/perf/ui/stdio/hist.c | 4 ++-- tools/perf/util/sort.h | 17 ++++++++++++++++- 4 files changed, 31 insertions(+), 36 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 2dcbe3d15a5f..5905acde5f1d 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -37,7 +37,6 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, static void hist_browser__update_nr_entries(struct hist_browser *hb); static struct rb_node *hists__filter_entries(struct rb_node *nd, - struct hists *hists, float min_pcnt); static bool hist_browser__has_filter(struct hist_browser *hb) @@ -319,7 +318,7 @@ __hist_browser__set_folding(struct hist_browser *browser, bool unfold) struct hists *hists = browser->hists; for (nd = rb_first(&hists->entries); - (nd = hists__filter_entries(nd, hists, browser->min_pcnt)) != NULL; + (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; nd = rb_next(nd)) { struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); hist_entry__set_folding(he, unfold); @@ -808,15 +807,12 @@ 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); - u64 total = hists__total_period(h->hists); - float percent = 0.0; + float percent; if (h->filtered) continue; - if (total) - percent = h->stat.period * 100.0 / total; - + percent = hist_entry__get_percent_limit(h); if (percent < hb->min_pcnt) continue; @@ -829,16 +825,11 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser) } static struct rb_node *hists__filter_entries(struct rb_node *nd, - struct hists *hists, float min_pcnt) { while (nd != NULL) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - u64 total = hists__total_period(hists); - float percent = 0.0; - - if (total) - percent = h->stat.period * 100.0 / total; + float percent = hist_entry__get_percent_limit(h); if (!h->filtered && percent >= min_pcnt) return nd; @@ -850,16 +841,11 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd, } static struct rb_node *hists__filter_prev_entries(struct rb_node *nd, - struct hists *hists, float min_pcnt) { while (nd != NULL) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - u64 total = hists__total_period(hists); - float percent = 0.0; - - if (total) - percent = h->stat.period * 100.0 / total; + float percent = hist_entry__get_percent_limit(h); if (!h->filtered && percent >= min_pcnt) return nd; @@ -888,14 +874,14 @@ static void ui_browser__hists_seek(struct ui_browser *browser, switch (whence) { case SEEK_SET: nd = hists__filter_entries(rb_first(browser->entries), - hb->hists, hb->min_pcnt); + hb->min_pcnt); break; case SEEK_CUR: nd = browser->top; goto do_offset; case SEEK_END: nd = hists__filter_prev_entries(rb_last(browser->entries), - hb->hists, hb->min_pcnt); + hb->min_pcnt); first = false; break; default: @@ -938,8 +924,7 @@ do_offset: break; } } - nd = hists__filter_entries(rb_next(nd), hb->hists, - hb->min_pcnt); + nd = hists__filter_entries(rb_next(nd), hb->min_pcnt); if (nd == NULL) break; --offset; @@ -972,7 +957,7 @@ do_offset: } } - nd = hists__filter_prev_entries(rb_prev(nd), hb->hists, + nd = hists__filter_prev_entries(rb_prev(nd), hb->min_pcnt); if (nd == NULL) break; @@ -1151,7 +1136,6 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) { struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries), - browser->hists, browser->min_pcnt); int printed = 0; @@ -1159,8 +1143,7 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); printed += hist_browser__fprintf_entry(browser, h, fp); - nd = hists__filter_entries(rb_next(nd), browser->hists, - browser->min_pcnt); + nd = hists__filter_entries(rb_next(nd), browser->min_pcnt); } return printed; @@ -1397,8 +1380,7 @@ static void hist_browser__update_nr_entries(struct hist_browser *hb) return; } - while ((nd = hists__filter_entries(nd, hb->hists, - hb->min_pcnt)) != NULL) { + while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { nr_entries++; nd = rb_next(nd); } diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 7e5da4af98d8..03d6812d25dd 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -226,14 +226,12 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); GtkTreeIter iter; u64 total = hists__total_period(h->hists); - float percent = 0.0; + float percent; if (h->filtered) continue; - if (total) - percent = h->stat.period * 100.0 / total; - + percent = hist_entry__get_percent_limit(h); if (percent < min_pcnt) continue; diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 9f57991025a9..475d2f5c7e16 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -461,12 +461,12 @@ print_entries: for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - float percent = h->stat.period * 100.0 / - hists->stats.total_period; + float percent; if (h->filtered) continue; + percent = hist_entry__get_percent_limit(h); if (percent < min_pcnt) continue; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index c9ffa031becd..426b873e16ff 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -20,7 +20,7 @@ #include "parse-options.h" #include "parse-events.h" - +#include "hist.h" #include "thread.h" extern regex_t parent_regex; @@ -131,6 +131,21 @@ static inline void hist_entry__add_pair(struct hist_entry *pair, list_add_tail(&pair->pairs.node, &he->pairs.head); } +static inline float hist_entry__get_percent_limit(struct hist_entry *he) +{ + u64 period = he->stat.period; + u64 total_period = hists__total_period(he->hists); + + if (unlikely(total_period == 0)) + return 0; + + if (symbol_conf.cumulate_callchain) + period = he->stat_acc->period; + + return period * 100.0 / total_period; +} + + enum sort_mode { SORT_MODE__NORMAL, SORT_MODE__BRANCH, -- cgit v1.2.3 From 77284de326e6d8c3b8e866cda5b415c86b522e61 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 16 Dec 2013 16:55:13 +0900 Subject: perf tools: Add more hpp helper functions Sometimes it needs to disable some columns at runtime. Add help functions to support that. Signed-off-by: Namhyung Kim Tested-by: Arun Sharma Tested-by: Rodrigo Campos Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1401335910-16832-15-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/ui/hist.c | 17 +++++++++++++++++ tools/perf/util/hist.h | 4 ++++ 2 files changed, 21 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 0ce3e79b2ca7..8ca638754acc 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -489,6 +489,11 @@ void perf_hpp__column_register(struct perf_hpp_fmt *format) list_add_tail(&format->list, &perf_hpp__list); } +void perf_hpp__column_unregister(struct perf_hpp_fmt *format) +{ + list_del(&format->list); +} + void perf_hpp__register_sort_field(struct perf_hpp_fmt *format) { list_add_tail(&format->sort_list, &perf_hpp__sort_list); @@ -500,6 +505,18 @@ void perf_hpp__column_enable(unsigned col) perf_hpp__column_register(&perf_hpp__format[col]); } +void perf_hpp__column_disable(unsigned col) +{ + BUG_ON(col >= PERF_HPP__MAX_INDEX); + perf_hpp__column_unregister(&perf_hpp__format[col]); +} + +void perf_hpp__cancel_cumulate(void) +{ + perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC); + perf_hpp__format[PERF_HPP__OVERHEAD].header = hpp__header_overhead; +} + void perf_hpp__setup_output_field(void) { struct perf_hpp_fmt *fmt; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index efd73e489027..99ad3cb433fb 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -237,7 +237,11 @@ enum { void perf_hpp__init(void); void perf_hpp__column_register(struct perf_hpp_fmt *format); +void perf_hpp__column_unregister(struct perf_hpp_fmt *format); void perf_hpp__column_enable(unsigned col); +void perf_hpp__column_disable(unsigned col); +void perf_hpp__cancel_cumulate(void); + void perf_hpp__register_sort_field(struct perf_hpp_fmt *format); void perf_hpp__setup_output_field(void); void perf_hpp__reset_output_field(void); -- cgit v1.2.3 From 9d3c02d7188866299eebe3c4a652c08140a71f40 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 7 Jan 2014 17:02:25 +0900 Subject: perf tools: Add callback function to hist_entry_iter The new ->add_entry_cb() will be called after an entry was added to the histogram. It's used for code sharing between perf report and perf top. Note that ops->add_*_entry() should set iter->he properly in order to call the ->add_entry_cb. Also pass @arg to the callback function. It'll be used by perf top later. Signed-off-by: Namhyung Kim Tested-by: Arun Sharma Tested-by: Rodrigo Campos Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/87k393g999.fsf@sejong.aot.lge.com Signed-off-by: Jiri Olsa --- tools/perf/builtin-report.c | 61 ++++++++++++++++++++++++++++++++----- tools/perf/tests/hists_filter.c | 2 +- tools/perf/tests/hists_output.c | 2 +- tools/perf/util/hist.c | 67 +++++++++++++++-------------------------- tools/perf/util/hist.h | 5 ++- 5 files changed, 84 insertions(+), 53 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 6cac509212ee..21d830bafff3 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -80,14 +80,59 @@ static int report__config(const char *var, const char *value, void *cb) return perf_default_config(var, value, cb); } -static void report__inc_stats(struct report *rep, - struct hist_entry *he __maybe_unused) +static void report__inc_stats(struct report *rep, struct hist_entry *he) { /* - * We cannot access @he at this time. Just assume it's a new entry. - * It'll be fixed once we have a callback mechanism in hist_iter. + * The @he is either of a newly created one or an existing one + * merging current sample. We only want to count a new one so + * checking ->nr_events being 1. */ - rep->nr_entries++; + if (he->stat.nr_events == 1) + rep->nr_entries++; +} + +static int hist_iter__report_callback(struct hist_entry_iter *iter, + struct addr_location *al, bool single, + void *arg) +{ + int err = 0; + struct report *rep = arg; + struct hist_entry *he = iter->he; + struct perf_evsel *evsel = iter->evsel; + struct mem_info *mi; + struct branch_info *bi; + + report__inc_stats(rep, he); + + if (!ui__has_annotation()) + return 0; + + if (sort__mode == SORT_MODE__BRANCH) { + bi = he->branch_info; + err = addr_map_symbol__inc_samples(&bi->from, evsel->idx); + if (err) + goto out; + + err = addr_map_symbol__inc_samples(&bi->to, evsel->idx); + + } else if (rep->mem_mode) { + mi = he->mem_info; + err = addr_map_symbol__inc_samples(&mi->daddr, evsel->idx); + if (err) + goto out; + + err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); + + } else if (symbol_conf.cumulate_callchain) { + if (single) + err = hist_entry__inc_addr_samples(he, evsel->idx, + al->addr); + } else { + err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); + } + +out: + return err; } static int process_sample_event(struct perf_tool *tool, @@ -100,6 +145,7 @@ static int process_sample_event(struct perf_tool *tool, struct addr_location al; struct hist_entry_iter iter = { .hide_unresolved = rep->hide_unresolved, + .add_entry_cb = hist_iter__report_callback, }; int ret; @@ -127,9 +173,8 @@ static int process_sample_event(struct perf_tool *tool, if (al.map != NULL) al.map->dso->hit = 1; - report__inc_stats(rep, NULL); - - ret = hist_entry_iter__add(&iter, &al, evsel, sample, rep->max_stack); + ret = hist_entry_iter__add(&iter, &al, evsel, sample, rep->max_stack, + rep); if (ret < 0) pr_debug("problem adding hist entry, skipping event\n"); diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 76b02e1de701..3539403bbad4 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c @@ -82,7 +82,7 @@ static int add_hist_entries(struct perf_evlist *evlist, goto out; if (hist_entry_iter__add(&iter, &al, evsel, &sample, - PERF_MAX_STACK_DEPTH) < 0) + PERF_MAX_STACK_DEPTH, NULL) < 0) goto out; fake_samples[i].thread = al.thread; diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index 1308f88a9169..d40461ecd210 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c @@ -71,7 +71,7 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) goto out; if (hist_entry_iter__add(&iter, &al, evsel, &sample, - PERF_MAX_STACK_DEPTH) < 0) + PERF_MAX_STACK_DEPTH, NULL) < 0) goto out; fake_samples[i].thread = al.thread; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c6f5f5251aad..5a0a4b2cadc4 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -517,27 +517,16 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al } static int -iter_finish_mem_entry(struct hist_entry_iter *iter, struct addr_location *al) +iter_finish_mem_entry(struct hist_entry_iter *iter, + struct addr_location *al __maybe_unused) { struct perf_evsel *evsel = iter->evsel; struct hist_entry *he = iter->he; - struct mem_info *mx; int err = -EINVAL; if (he == NULL) goto out; - if (ui__has_annotation()) { - err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); - if (err) - goto out; - - mx = he->mem_info; - err = addr_map_symbol__inc_samples(&mx->daddr, evsel->idx); - if (err) - goto out; - } - hists__inc_nr_samples(&evsel->hists, he->filtered); err = hist_entry__append_callchain(he, iter->sample); @@ -575,6 +564,9 @@ static int iter_add_single_branch_entry(struct hist_entry_iter *iter __maybe_unused, struct addr_location *al __maybe_unused) { + /* to avoid calling callback function */ + iter->he = NULL; + return 0; } @@ -599,7 +591,7 @@ iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) static int iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al) { - struct branch_info *bi, *bx; + struct branch_info *bi; struct perf_evsel *evsel = iter->evsel; struct hist_entry *he = NULL; int i = iter->curr; @@ -619,17 +611,6 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a if (he == NULL) return -ENOMEM; - if (ui__has_annotation()) { - bx = he->branch_info; - err = addr_map_symbol__inc_samples(&bx->from, evsel->idx); - if (err) - goto out; - - err = addr_map_symbol__inc_samples(&bx->to, evsel->idx); - if (err) - goto out; - } - hists__inc_nr_samples(&evsel->hists, he->filtered); out: @@ -673,9 +654,9 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location } static int -iter_finish_normal_entry(struct hist_entry_iter *iter, struct addr_location *al) +iter_finish_normal_entry(struct hist_entry_iter *iter, + struct addr_location *al __maybe_unused) { - int err; struct hist_entry *he = iter->he; struct perf_evsel *evsel = iter->evsel; struct perf_sample *sample = iter->sample; @@ -685,12 +666,6 @@ iter_finish_normal_entry(struct hist_entry_iter *iter, struct addr_location *al) iter->he = NULL; - if (ui__has_annotation()) { - err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); - if (err) - return err; - } - hists__inc_nr_samples(&evsel->hists, he->filtered); return hist_entry__append_callchain(he, sample); @@ -746,13 +721,6 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, */ callchain_cursor_commit(&callchain_cursor); - /* - * The iter->he will be over-written after ->add_next_entry() - * called so inc stats for the original entry now. - */ - if (ui__has_annotation()) - err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); - hists__inc_nr_samples(&evsel->hists, he->filtered); return err; @@ -802,8 +770,11 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, * It's possible that it has cycles or recursive calls. */ for (i = 0; i < iter->curr; i++) { - if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) + if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) { + /* to avoid calling callback function */ + iter->he = NULL; return 0; + } } he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, @@ -863,7 +834,7 @@ const struct hist_iter_ops hist_iter_cumulative = { int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, struct perf_evsel *evsel, struct perf_sample *sample, - int max_stack_depth) + int max_stack_depth, void *arg) { int err, err2; @@ -883,10 +854,22 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, if (err) goto out; + if (iter->he && iter->add_entry_cb) { + err = iter->add_entry_cb(iter, al, true, arg); + if (err) + goto out; + } + while (iter->ops->next_entry(iter, al)) { err = iter->ops->add_next_entry(iter, al); if (err) break; + + if (iter->he && iter->add_entry_cb) { + err = iter->add_entry_cb(iter, al, false, arg); + if (err) + goto out; + } } out: diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 99ad3cb433fb..82b28ff98062 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -119,6 +119,9 @@ struct hist_entry_iter { void *priv; const struct hist_iter_ops *ops; + /* user-defined callback function (optional) */ + int (*add_entry_cb)(struct hist_entry_iter *iter, + struct addr_location *al, bool single, void *arg); }; extern const struct hist_iter_ops hist_iter_normal; @@ -135,7 +138,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, bool sample_self); int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, struct perf_evsel *evsel, struct perf_sample *sample, - int max_stack_depth); + int max_stack_depth, void *arg); int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); -- cgit v1.2.3 From e511db5e94f056083e821aa3ab74b03ad1216e14 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 24 Dec 2013 16:19:25 +0900 Subject: perf tools: Enable --children option by default Now perf top and perf report will show children column by default if it has callchain information. Requested-by: Ingo Molnar Signed-off-by: Namhyung Kim Tested-by: Rodrigo Campos Tested-by: Arun Sharma Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1401335910-16832-23-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/symbol.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 95e249779931..7b9096f29cdb 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -29,11 +29,12 @@ int vmlinux_path__nr_entries; char **vmlinux_path; struct symbol_conf symbol_conf = { - .use_modules = true, - .try_vmlinux_path = true, - .annotate_src = true, - .demangle = true, - .symfs = "", + .use_modules = true, + .try_vmlinux_path = true, + .annotate_src = true, + .demangle = true, + .cumulate_callchain = true, + .symfs = "", }; static enum dso_binary_type binary_type_symtab[] = { -- cgit v1.2.3 From d69b2962a0aebd431cdda939f4418dd606e2f77e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 23 May 2014 10:59:01 +0900 Subject: perf tools: Reset output/sort order to default When reset_output_field() is called, also reset field/sort order to NULL so that it can have the default values. It's needed for testing. Signed-off-by: Namhyung Kim CC: Arun Sharma Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1401335910-16832-26-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/sort.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 9da8931d2394..254f583a52ab 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1582,6 +1582,9 @@ void reset_output_field(void) sort__has_sym = 0; sort__has_dso = 0; + field_order = NULL; + sort_order = NULL; + reset_dimensions(); perf_hpp__reset_output_field(); } -- cgit v1.2.3 From 2ec85c628c4cecef0f82d177279c579aed0f9706 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 23 May 2014 17:15:46 +0200 Subject: perf tools: Remove elide setup for SORT_MODE__MEMORY mode There's no need to setup elide of sort_dso sort entry again with symbol_conf.dso_list list. The only difference were list names of memory mode data, which does not make much sense to me. Acked-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1400858147-7155-2-git-send-email-jolsa@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/sort.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 254f583a52ab..2aba620a86f6 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1412,19 +1412,6 @@ void sort__setup_elide(FILE *output) sort_entry__setup_elide(&sort_sym_to, symbol_conf.sym_to_list, "sym_to", output); - } else if (sort__mode == SORT_MODE__MEMORY) { - sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, - "symbol_daddr", output); - sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, - "dso_daddr", output); - sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, - "mem", output); - sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, - "local_weight", output); - sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, - "tlb", output); - sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, - "snoop", output); } /* -- cgit v1.2.3 From f29984226978313039d7dfe9b45eaa55a3aad03d Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 23 May 2014 17:15:47 +0200 Subject: perf tools: Move elide bool into perf_hpp_fmt struct After output/sort fields refactoring, it's expensive to check the elide bool in its current location inside the 'struct sort_entry'. The perf_hpp__should_skip function gets highly noticable in workloads with high number of output/sort fields, like for: $ perf report -i perf-test.data -F overhead,sample,period,comm,pid,dso,symbol,cpu --stdio Performance report: 9.70% perf [.] perf_hpp__should_skip Moving the elide bool into the 'struct perf_hpp_fmt', which makes the perf_hpp__should_skip just single struct read. Got speedup of around 22% for my test perf.data workload. The change should not harm any other workload types. Performance counter stats for (10 runs): before: 358,319,732,626 cycles ( +- 0.55% ) 467,129,581,515 instructions # 1.30 insns per cycle ( +- 0.00% ) 150.943975206 seconds time elapsed ( +- 0.62% ) now: 278,785,972,990 cycles ( +- 0.12% ) 370,146,797,640 instructions # 1.33 insns per cycle ( +- 0.00% ) 116.416670507 seconds time elapsed ( +- 0.31% ) Acked-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/20140601142622.GA9131@krava.brq.redhat.com Signed-off-by: Jiri Olsa --- tools/perf/ui/browsers/hists.c | 8 ++-- tools/perf/util/hist.h | 8 +++- tools/perf/util/sort.c | 90 ++++++++++++++++++++++++++---------------- tools/perf/util/sort.h | 2 +- 4 files changed, 68 insertions(+), 40 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 5905acde5f1d..52c03fbbba17 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -1706,14 +1706,14 @@ zoom_dso: zoom_out_dso: ui_helpline__pop(); browser->hists->dso_filter = NULL; - sort_dso.elide = false; + perf_hpp__set_elide(HISTC_DSO, false); } else { if (dso == NULL) continue; ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", dso->kernel ? "the Kernel" : dso->short_name); browser->hists->dso_filter = dso; - sort_dso.elide = true; + perf_hpp__set_elide(HISTC_DSO, true); pstack__push(fstack, &browser->hists->dso_filter); } hists__filter_by_dso(hists); @@ -1725,13 +1725,13 @@ zoom_thread: zoom_out_thread: ui_helpline__pop(); browser->hists->thread_filter = NULL; - sort_thread.elide = false; + perf_hpp__set_elide(HISTC_THREAD, false); } else { ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", thread->comm_set ? thread__comm_str(thread) : "", thread->tid); browser->hists->thread_filter = thread; - sort_thread.elide = true; + perf_hpp__set_elide(HISTC_THREAD, false); pstack__push(fstack, &browser->hists->thread_filter); } hists__filter_by_thread(hists); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 82b28ff98062..d2bf03575d5f 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -205,6 +205,7 @@ struct perf_hpp_fmt { struct list_head list; struct list_head sort_list; + bool elide; }; extern struct list_head perf_hpp__list; @@ -252,7 +253,12 @@ void perf_hpp__append_sort_keys(void); bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b); -bool perf_hpp__should_skip(struct perf_hpp_fmt *format); + +static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format) +{ + return format->elide; +} + void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); typedef u64 (*hpp_field_fn)(struct hist_entry *he); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 2aba620a86f6..45512baaab67 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1157,6 +1157,7 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) INIT_LIST_HEAD(&hse->hpp.list); INIT_LIST_HEAD(&hse->hpp.sort_list); + hse->hpp.elide = false; return hse; } @@ -1364,27 +1365,64 @@ static int __setup_sorting(void) return ret; } -bool perf_hpp__should_skip(struct perf_hpp_fmt *format) +void perf_hpp__set_elide(int idx, bool elide) { - if (perf_hpp__is_sort_entry(format)) { - struct hpp_sort_entry *hse; + struct perf_hpp_fmt *fmt; + struct hpp_sort_entry *hse; - hse = container_of(format, struct hpp_sort_entry, hpp); - return hse->se->elide; + perf_hpp__for_each_format(fmt) { + if (!perf_hpp__is_sort_entry(fmt)) + continue; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); + if (hse->se->se_width_idx == idx) { + fmt->elide = elide; + break; + } } - return false; } -static void sort_entry__setup_elide(struct sort_entry *se, - struct strlist *list, - const char *list_name, FILE *fp) +static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp) { if (list && strlist__nr_entries(list) == 1) { if (fp != NULL) fprintf(fp, "# %s: %s\n", list_name, strlist__entry(list, 0)->s); - se->elide = true; + return true; + } + return false; +} + +static bool get_elide(int idx, FILE *output) +{ + switch (idx) { + case HISTC_SYMBOL: + return __get_elide(symbol_conf.sym_list, "symbol", output); + case HISTC_DSO: + return __get_elide(symbol_conf.dso_list, "dso", output); + case HISTC_COMM: + return __get_elide(symbol_conf.comm_list, "comm", output); + default: + break; } + + if (sort__mode != SORT_MODE__BRANCH) + return false; + + switch (idx) { + case HISTC_SYMBOL_FROM: + return __get_elide(symbol_conf.sym_from_list, "sym_from", output); + case HISTC_SYMBOL_TO: + return __get_elide(symbol_conf.sym_to_list, "sym_to", output); + case HISTC_DSO_FROM: + return __get_elide(symbol_conf.dso_from_list, "dso_from", output); + case HISTC_DSO_TO: + return __get_elide(symbol_conf.dso_to_list, "dso_to", output); + default: + break; + } + + return false; } void sort__setup_elide(FILE *output) @@ -1392,26 +1430,12 @@ void sort__setup_elide(FILE *output) struct perf_hpp_fmt *fmt; struct hpp_sort_entry *hse; - sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, - "dso", output); - sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, - "comm", output); - sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, - "symbol", output); - - if (sort__mode == SORT_MODE__BRANCH) { - sort_entry__setup_elide(&sort_dso_from, - symbol_conf.dso_from_list, - "dso_from", output); - sort_entry__setup_elide(&sort_dso_to, - symbol_conf.dso_to_list, - "dso_to", output); - sort_entry__setup_elide(&sort_sym_from, - symbol_conf.sym_from_list, - "sym_from", output); - sort_entry__setup_elide(&sort_sym_to, - symbol_conf.sym_to_list, - "sym_to", output); + perf_hpp__for_each_format(fmt) { + if (!perf_hpp__is_sort_entry(fmt)) + continue; + + hse = container_of(fmt, struct hpp_sort_entry, hpp); + fmt->elide = get_elide(hse->se->se_width_idx, output); } /* @@ -1422,8 +1446,7 @@ void sort__setup_elide(FILE *output) if (!perf_hpp__is_sort_entry(fmt)) continue; - hse = container_of(fmt, struct hpp_sort_entry, hpp); - if (!hse->se->elide) + if (!fmt->elide) return; } @@ -1431,8 +1454,7 @@ void sort__setup_elide(FILE *output) if (!perf_hpp__is_sort_entry(fmt)) continue; - hse = container_of(fmt, struct hpp_sort_entry, hpp); - hse->se->elide = false; + fmt->elide = false; } } diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 426b873e16ff..5bf0098d6b06 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -202,7 +202,6 @@ struct sort_entry { int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size, unsigned int width); u8 se_width_idx; - bool elide; }; extern struct sort_entry sort_thread; @@ -213,6 +212,7 @@ int setup_output_field(void); void reset_output_field(void); extern int sort_dimension__add(const char *); void sort__setup_elide(FILE *fp); +void perf_hpp__set_elide(int idx, bool elide); int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); -- cgit v1.2.3 From 0c188a07b6a399e3df66534c29fef0a2082aaf57 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 29 May 2014 19:52:32 +0900 Subject: perf probe: Fix a segfault if asked for variable it doesn't find Fix a segfault bug by asking for variable it doesn't find. Since the convert_variable() didn't handle error code returned from convert_variable_location(), it just passed an incomplete variable field and then a segfault was occurred when formatting the field. This fixes that bug by handling success code correctly in convert_variable(). Other callers of convert_variable_location() are correctly checking the return code. This bug was introduced by following commit. But another hidden erroneous error handling has been there previously (-ENOMEM case). commit 3d918a12a1b3088ac16ff37fa52760639d6e2403 Signed-off-by: Masami Hiramatsu Reported-by: Arnaldo Carvalho de Melo Tested-by: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20140529105232.28251.30447.stgit@ltc230.yrl.intra.hitachi.co.jp Signed-off-by: Jiri Olsa --- tools/perf/util/probe-finder.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 562762117639..9d8eb26f0533 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -511,12 +511,12 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, &pf->sp_die, pf->tvar); - if (ret == -ENOENT) + if (ret == -ENOENT || ret == -EINVAL) pr_err("Failed to find the location of %s at this address.\n" " Perhaps, it has been optimized out.\n", pf->pvar->var); else if (ret == -ENOTSUP) pr_err("Sorry, we don't support this variable location yet.\n"); - else if (pf->pvar->field) { + else if (ret == 0 && pf->pvar->field) { ret = convert_variable_fields(vr_die, pf->pvar->var, pf->pvar->field, &pf->tvar->ref, &die_mem); -- cgit v1.2.3 From 082f96a93eb5ba9bf771518a0dda590624568e8e Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 29 May 2014 21:19:30 +0900 Subject: perf probe: Fix perf probe to find correct variable DIE Fix perf probe to find correct variable DIE which has location or external instance by tracking down the lexical blocks. Current die_find_variable() expects that the all variable DIEs which has DW_TAG_variable have a location. However, since recent dwarf information may have declaration variable DIEs at the entry of function (subprogram), die_find_variable() returns it. To solve this problem, it must track down the DIE tree to find a DIE which has an actual location or a reference for external instance. e.g. finding a DIE which origin is <0xdc73>; <1><11496>: Abbrev Number: 95 (DW_TAG_subprogram) <11497> DW_AT_abstract_origin: <0xdc42> <1149b> DW_AT_low_pc : 0x1850 [...] <2><114cc>: Abbrev Number: 119 (DW_TAG_variable) <- this is a declaration <114cd> DW_AT_abstract_origin: <0xdc73> <2><114d1>: Abbrev Number: 119 (DW_TAG_variable) [...] <3><115a7>: Abbrev Number: 105 (DW_TAG_lexical_block) <115a8> DW_AT_ranges : 0xaa0 <4><115ac>: Abbrev Number: 96 (DW_TAG_variable) <- this has a location <115ad> DW_AT_abstract_origin: <0xdc73> <115b1> DW_AT_location : 0x486c (location list) Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Acked-by: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20140529121930.30879.87092.stgit@ltc230.yrl.intra.hitachi.co.jp Signed-off-by: Jiri Olsa --- tools/perf/util/dwarf-aux.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 7defd77105d0..cc66c4049e09 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -747,14 +747,17 @@ struct __find_variable_param { static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) { struct __find_variable_param *fvp = data; + Dwarf_Attribute attr; int tag; tag = dwarf_tag(die_mem); if ((tag == DW_TAG_formal_parameter || tag == DW_TAG_variable) && - die_compare_name(die_mem, fvp->name)) + die_compare_name(die_mem, fvp->name) && + /* Does the DIE have location information or external instance? */ + (dwarf_attr(die_mem, DW_AT_external, &attr) || + dwarf_attr(die_mem, DW_AT_location, &attr))) return DIE_FIND_CB_END; - if (dwarf_haspc(die_mem, fvp->addr)) return DIE_FIND_CB_CONTINUE; else -- cgit v1.2.3 From 473a778a2f2949972b52ad7fc61577f381f2d05e Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 2 Jun 2014 23:20:16 -0400 Subject: tools lib traceevent: Added support for __get_bitmask() macro Coming in v3.16, trace events will be able to save bitmasks in raw format in the ring buffer and output it with the __get_bitmask() macro. In order for userspace tools to parse this, it must be able to handle the __get_bitmask() call and be able to convert the data that's in the ring buffer into a nice bitmask format. The output is similar to what the kernel uses to print bitmasks, with a comma separator every 4 bytes (8 characters). This allows for cpumasks to also be saved efficiently. The first user is the thermal:thermal_power_limit event which has the following output: thermal_power_limit: cpus=0000000f freq=1900000 cdev_state=0 power=5252 Link: http://lkml.kernel.org/r/20140506132238.22e136d1@gandalf.local.home Suggested-by: Javi Merino Signed-off-by: Steven Rostedt Acked-by: Namhyung Kim Tested-by: Javi Merino Link: http://lkml.kernel.org/r/20140603032224.229186537@goodmis.org Signed-off-by: Jiri Olsa --- tools/lib/traceevent/event-parse.c | 113 +++++++++++++++++++++ tools/lib/traceevent/event-parse.h | 7 ++ .../perf/util/scripting-engines/trace-event-perl.c | 1 + .../util/scripting-engines/trace-event-python.c | 1 + 4 files changed, 122 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index b83184f2d484..93825a17dcce 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -765,6 +765,9 @@ static void free_arg(struct print_arg *arg) case PRINT_BSTRING: free(arg->string.string); break; + case PRINT_BITMASK: + free(arg->bitmask.bitmask); + break; case PRINT_DYNAMIC_ARRAY: free(arg->dynarray.index); break; @@ -2268,6 +2271,7 @@ static int arg_num_eval(struct print_arg *arg, long long *val) case PRINT_FIELD ... PRINT_SYMBOL: case PRINT_STRING: case PRINT_BSTRING: + case PRINT_BITMASK: default: do_warning("invalid eval type %d", arg->type); ret = 0; @@ -2296,6 +2300,7 @@ static char *arg_eval (struct print_arg *arg) case PRINT_FIELD ... PRINT_SYMBOL: case PRINT_STRING: case PRINT_BSTRING: + case PRINT_BITMASK: default: do_warning("invalid eval type %d", arg->type); break; @@ -2683,6 +2688,35 @@ process_str(struct event_format *event __maybe_unused, struct print_arg *arg, return EVENT_ERROR; } +static enum event_type +process_bitmask(struct event_format *event __maybe_unused, struct print_arg *arg, + char **tok) +{ + enum event_type type; + char *token; + + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto out_free; + + arg->type = PRINT_BITMASK; + arg->bitmask.bitmask = token; + arg->bitmask.offset = -1; + + if (read_expected(EVENT_DELIM, ")") < 0) + goto out_err; + + type = read_token(&token); + *tok = token; + + return type; + + out_free: + free_token(token); + out_err: + *tok = NULL; + return EVENT_ERROR; +} + static struct pevent_function_handler * find_func_handler(struct pevent *pevent, char *func_name) { @@ -2797,6 +2831,10 @@ process_function(struct event_format *event, struct print_arg *arg, free_token(token); return process_str(event, arg, tok); } + if (strcmp(token, "__get_bitmask") == 0) { + free_token(token); + return process_bitmask(event, arg, tok); + } if (strcmp(token, "__get_dynamic_array") == 0) { free_token(token); return process_dynamic_array(event, arg, tok); @@ -3324,6 +3362,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg return eval_type(val, arg, 0); case PRINT_STRING: case PRINT_BSTRING: + case PRINT_BITMASK: return 0; case PRINT_FUNC: { struct trace_seq s; @@ -3556,6 +3595,60 @@ static void print_str_to_seq(struct trace_seq *s, const char *format, trace_seq_printf(s, format, str); } +static void print_bitmask_to_seq(struct pevent *pevent, + struct trace_seq *s, const char *format, + int len_arg, const void *data, int size) +{ + int nr_bits = size * 8; + int str_size = (nr_bits + 3) / 4; + int len = 0; + char buf[3]; + char *str; + int index; + int i; + + /* + * The kernel likes to put in commas every 32 bits, we + * can do the same. + */ + str_size += (nr_bits - 1) / 32; + + str = malloc(str_size + 1); + if (!str) { + do_warning("%s: not enough memory!", __func__); + return; + } + str[str_size] = 0; + + /* Start out with -2 for the two chars per byte */ + for (i = str_size - 2; i >= 0; i -= 2) { + /* + * data points to a bit mask of size bytes. + * In the kernel, this is an array of long words, thus + * endianess is very important. + */ + if (pevent->file_bigendian) + index = size - (len + 1); + else + index = len; + + snprintf(buf, 3, "%02x", *((unsigned char *)data + index)); + memcpy(str + i, buf, 2); + len++; + if (!(len & 3) && i > 0) { + i--; + str[i] = ','; + } + } + + if (len_arg >= 0) + trace_seq_printf(s, format, len_arg, str); + else + trace_seq_printf(s, format, str); + + free(str); +} + static void print_str_arg(struct trace_seq *s, void *data, int size, struct event_format *event, const char *format, int len_arg, struct print_arg *arg) @@ -3691,6 +3784,23 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, case PRINT_BSTRING: print_str_to_seq(s, format, len_arg, arg->string.string); break; + case PRINT_BITMASK: { + int bitmask_offset; + int bitmask_size; + + if (arg->bitmask.offset == -1) { + struct format_field *f; + + f = pevent_find_any_field(event, arg->bitmask.bitmask); + arg->bitmask.offset = f->offset; + } + bitmask_offset = data2host4(pevent, data + arg->bitmask.offset); + bitmask_size = bitmask_offset >> 16; + bitmask_offset &= 0xffff; + print_bitmask_to_seq(pevent, s, format, len_arg, + data + bitmask_offset, bitmask_size); + break; + } case PRINT_OP: /* * The only op for string should be ? : @@ -4822,6 +4932,9 @@ static void print_args(struct print_arg *args) case PRINT_BSTRING: printf("__get_str(%s)", args->string.string); break; + case PRINT_BITMASK: + printf("__get_bitmask(%s)", args->bitmask.bitmask); + break; case PRINT_TYPE: printf("(%s)", args->typecast.type); print_args(args->typecast.item); diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 56e0e6c12411..7a3873ff9a4f 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h @@ -208,6 +208,11 @@ struct print_arg_string { int offset; }; +struct print_arg_bitmask { + char *bitmask; + int offset; +}; + struct print_arg_field { char *name; struct format_field *field; @@ -274,6 +279,7 @@ enum print_arg_type { PRINT_DYNAMIC_ARRAY, PRINT_OP, PRINT_FUNC, + PRINT_BITMASK, }; struct print_arg { @@ -288,6 +294,7 @@ struct print_arg { struct print_arg_hex hex; struct print_arg_func func; struct print_arg_string string; + struct print_arg_bitmask bitmask; struct print_arg_op op; struct print_arg_dynarray dynarray; }; diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index e108207c5de0..af7da565a750 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -215,6 +215,7 @@ static void define_event_symbols(struct event_format *event, case PRINT_BSTRING: case PRINT_DYNAMIC_ARRAY: case PRINT_STRING: + case PRINT_BITMASK: break; case PRINT_TYPE: define_event_symbols(event, ev_name, args->typecast.item); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index cd9774df3750..c3de0971bfdd 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -197,6 +197,7 @@ static void define_event_symbols(struct event_format *event, case PRINT_BSTRING: case PRINT_DYNAMIC_ARRAY: case PRINT_FUNC: + case PRINT_BITMASK: /* we should warn... */ return; } -- cgit v1.2.3 From e646fe730a324098a718f1c9b2f349efb99d5457 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 29 May 2014 13:44:55 +0900 Subject: perf script/python: Print array argument as string With the Sebastian's change of handling num array argument (of raw syscall enter), the script still failed to work like this: $ perf record -e raw_syscalls:* sleep 1 $ perf script -g python $ perf script -s perf-script.py ... Traceback (most recent call last): File "perf-script.py", line 42, in raw_syscalls__sys_enter (id, args), TypeError: %u format: a number is required, not list Fatal Python error: problem in Python trace event handler Aborted (core dumped) This is because the generated script tries to print the array arg as unsigned integer (%u). Since the python seems to convert arguments to strings by default, just using %s solved the problem for me. Signed-off-by: Namhyung Kim Cc: Tom Zanussi Cc: Sebastian Andrzej Siewior Link: http://lkml.kernel.org/r/1401338695-18837-1-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/scripting-engines/trace-event-python.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index c3de0971bfdd..1c419321f707 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -623,6 +623,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) fprintf(ofp, "%s=", f->name); if (f->flags & FIELD_IS_STRING || f->flags & FIELD_IS_FLAG || + f->flags & FIELD_IS_ARRAY || f->flags & FIELD_IS_SYMBOLIC) fprintf(ofp, "%%s"); else if (f->flags & FIELD_IS_SIGNED) -- cgit v1.2.3 From 7ef807034ef33f8afe33fa7957c73954e8e4f89c Mon Sep 17 00:00:00 2001 From: Don Zickus Date: Mon, 19 May 2014 15:13:49 -0400 Subject: perf tools: Update mmap2 interface with protection and flag bits The kernel piece passes more info now. Update the perf tool to reflect that and adjust the synthesized maps to play along. Signed-off-by: Don Zickus Link: http://lkml.kernel.org/r/1400526833-141779-4-git-send-email-dzickus@redhat.com Signed-off-by: Jiri Olsa --- tools/perf/util/event.c | 23 +++++++++++++++++++++-- tools/perf/util/event.h | 2 ++ tools/perf/util/machine.c | 4 +++- tools/perf/util/map.c | 4 +++- tools/perf/util/map.h | 4 +++- 5 files changed, 32 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 65795b835b39..ce43cbae3bd5 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -1,4 +1,5 @@ #include +#include #include "event.h" #include "debug.h" #include "hist.h" @@ -212,6 +213,21 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, else event->header.misc = PERF_RECORD_MISC_GUEST_USER; + /* map protection and flags bits */ + event->mmap2.prot = 0; + event->mmap2.flags = 0; + if (prot[0] == 'r') + event->mmap2.prot |= PROT_READ; + if (prot[1] == 'w') + event->mmap2.prot |= PROT_WRITE; + if (prot[2] == 'x') + event->mmap2.prot |= PROT_EXEC; + + if (prot[3] == 's') + event->mmap2.flags |= MAP_SHARED; + else + event->mmap2.flags |= MAP_PRIVATE; + if (prot[2] != 'x') { if (!mmap_data || prot[0] != 'r') continue; @@ -612,12 +628,15 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) { return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 - " %02x:%02x %"PRIu64" %"PRIu64"]: %c %s\n", + " %02x:%02x %"PRIu64" %"PRIu64"]: %c%c%c%c %s\n", event->mmap2.pid, event->mmap2.tid, event->mmap2.start, event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, event->mmap2.min, event->mmap2.ino, event->mmap2.ino_generation, - (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', + (event->mmap2.prot & PROT_READ) ? 'r' : '-', + (event->mmap2.prot & PROT_WRITE) ? 'w' : '-', + (event->mmap2.prot & PROT_EXEC) ? 'x' : '-', + (event->mmap2.flags & MAP_SHARED) ? 's' : 'p', event->mmap2.filename); } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index d970232cb270..9ba2eb3bdcfd 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -27,6 +27,8 @@ struct mmap2_event { u32 min; u64 ino; u64 ino_generation; + u32 prot; + u32 flags; char filename[PATH_MAX]; }; diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 7409ac8de51c..0e5fea95d596 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1060,6 +1060,8 @@ int machine__process_mmap2_event(struct machine *machine, event->mmap2.pid, event->mmap2.maj, event->mmap2.min, event->mmap2.ino, event->mmap2.ino_generation, + event->mmap2.prot, + event->mmap2.flags, event->mmap2.filename, type); if (map == NULL) @@ -1105,7 +1107,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event map = map__new(&machine->user_dsos, event->mmap.start, event->mmap.len, event->mmap.pgoff, - event->mmap.pid, 0, 0, 0, 0, + event->mmap.pid, 0, 0, 0, 0, 0, 0, event->mmap.filename, type); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 8ccbb32eda25..25c571f4cba6 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -138,7 +138,7 @@ void map__init(struct map *map, enum map_type type, struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, - u64 ino_gen, char *filename, + u64 ino_gen, u32 prot, u32 flags, char *filename, enum map_type type) { struct map *map = malloc(sizeof(*map)); @@ -157,6 +157,8 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, map->min = d_min; map->ino = ino; map->ino_generation = ino_gen; + map->prot = prot; + map->flags = flags; if ((anon || no_dso) && type == MAP__FUNCTION) { snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index ae2d45110588..7758c72522ef 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -35,6 +35,8 @@ struct map { bool referenced; bool erange_warned; u32 priv; + u32 prot; + u32 flags; u64 pgoff; u64 reloc; u32 maj, min; /* only valid for MMAP2 record */ @@ -118,7 +120,7 @@ void map__init(struct map *map, enum map_type type, u64 start, u64 end, u64 pgoff, struct dso *dso); struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, - u64 ino_gen, + u64 ino_gen, u32 prot, u32 flags, char *filename, enum map_type type); struct map *map__new2(u64 start, struct dso *dso, enum map_type type); void map__delete(struct map *map); -- cgit v1.2.3 From a5a5ba72843dd05f991184d6cb9a4471acce1005 Mon Sep 17 00:00:00 2001 From: Don Zickus Date: Fri, 30 May 2014 10:49:42 -0400 Subject: Revert "perf: Disable PERF_RECORD_MMAP2 support" This reverts commit 3090ffb5a2515990182f3f55b0688a7817325488. Re-enable the mmap2 interface as we will have a user soon. Since things have changed since perf disabled mmap2, small tweaks to the revert had to be done: o commit 9d4ecc88 forced (n!=8) to become (n<7) o a new libunwind test needed updating to use mmap2 interface Signed-off-by: Don Zickus Link: http://lkml.kernel.org/r/1401461382-209586-1-git-send-email-dzickus@redhat.com Signed-off-by: Jiri Olsa --- kernel/events/core.c | 4 ---- tools/perf/tests/dwarf-unwind.c | 2 +- tools/perf/util/event.c | 34 ++++++++++++++++++++-------------- tools/perf/util/evsel.c | 1 + 4 files changed, 22 insertions(+), 19 deletions(-) (limited to 'tools/perf/util') diff --git a/kernel/events/core.c b/kernel/events/core.c index eea1955c7868..cd28335b3d28 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -6929,10 +6929,6 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, if (ret) return -EFAULT; - /* disabled for now */ - if (attr->mmap2) - return -EINVAL; - if (attr->__reserved_1) return -EINVAL; diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 108f0cd49f4e..96adb730b744 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c @@ -15,7 +15,7 @@ static int mmap_handler(struct perf_tool *tool __maybe_unused, struct perf_sample *sample __maybe_unused, struct machine *machine) { - return machine__process_mmap_event(machine, event, NULL); + return machine__process_mmap2_event(machine, event, NULL); } static int init_live_machine(struct machine *machine) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index ce43cbae3bd5..d0281bdfa582 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -179,13 +179,14 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, return -1; } - event->header.type = PERF_RECORD_MMAP; + event->header.type = PERF_RECORD_MMAP2; while (1) { char bf[BUFSIZ]; char prot[5]; char execname[PATH_MAX]; char anonstr[] = "//anon"; + unsigned int ino; size_t size; ssize_t n; @@ -196,15 +197,20 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, strcpy(execname, ""); /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ - n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n", - &event->mmap.start, &event->mmap.len, prot, - &event->mmap.pgoff, - execname); + n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n", + &event->mmap2.start, &event->mmap2.len, prot, + &event->mmap2.pgoff, &event->mmap2.maj, + &event->mmap2.min, + &ino, execname); + /* * Anon maps don't have the execname. */ - if (n < 4) + if (n < 7) continue; + + event->mmap2.ino = (u64)ino; + /* * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c */ @@ -239,15 +245,15 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, strcpy(execname, anonstr); size = strlen(execname) + 1; - memcpy(event->mmap.filename, execname, size); + memcpy(event->mmap2.filename, execname, size); size = PERF_ALIGN(size, sizeof(u64)); - event->mmap.len -= event->mmap.start; - event->mmap.header.size = (sizeof(event->mmap) - - (sizeof(event->mmap.filename) - size)); - memset(event->mmap.filename + size, 0, machine->id_hdr_size); - event->mmap.header.size += machine->id_hdr_size; - event->mmap.pid = tgid; - event->mmap.tid = pid; + event->mmap2.len -= event->mmap.start; + event->mmap2.header.size = (sizeof(event->mmap2) - + (sizeof(event->mmap2.filename) - size)); + memset(event->mmap2.filename + size, 0, machine->id_hdr_size); + event->mmap2.header.size += machine->id_hdr_size; + event->mmap2.pid = tgid; + event->mmap2.tid = pid; if (process(tool, event, &synth_sample, machine) != 0) { rc = -1; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 5c28d82b76c4..21154dabc5fa 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -659,6 +659,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) perf_evsel__set_sample_bit(evsel, WEIGHT); attr->mmap = track; + attr->mmap2 = track && !perf_missing_features.mmap2; attr->comm = track; if (opts->sample_transaction) -- cgit v1.2.3 From 7365be55eee37ddb4f487263b4ba5bc8beb9638f Mon Sep 17 00:00:00 2001 From: Don Zickus Date: Tue, 27 May 2014 12:28:05 -0400 Subject: perf tools: Add cpumode to struct hist_entry The next patch needs to sort on cpumode, so add it to hist_entry to be tracked. Signed-off-by: Don Zickus Link: http://lkml.kernel.org/r/1401208087-181977-6-git-send-email-dzickus@redhat.com Signed-off-by: Jiri Olsa --- tools/perf/util/hist.c | 7 ++++--- tools/perf/util/sort.h | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 5a0a4b2cadc4..d5f47a47c4bf 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -439,9 +439,10 @@ struct hist_entry *__hists__add_entry(struct hists *hists, .map = al->map, .sym = al->sym, }, - .cpu = al->cpu, - .ip = al->addr, - .level = al->level, + .cpu = al->cpu, + .cpumode = al->cpumode, + .ip = al->addr, + .level = al->level, .stat = { .nr_events = 1, .period = period, diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 5bf0098d6b06..6de22f838854 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -89,6 +89,7 @@ struct hist_entry { u64 ip; u64 transaction; s32 cpu; + u8 cpumode; struct hist_entry_diff diff; -- cgit v1.2.3 From 2b1b71003ea809e619bd73e74dfc2a73069de66f Mon Sep 17 00:00:00 2001 From: Don Zickus Date: Fri, 30 May 2014 16:10:05 -0400 Subject: perf tools: Add support to dynamically get cacheline size Different arches may have different cacheline sizes. Look it up and set a global variable for reference. Signed-off-by: Don Zickus Link: http://lkml.kernel.org/r/1401480605-97442-1-git-send-email-dzickus@redhat.com Signed-off-by: Jiri Olsa --- tools/perf/perf.c | 1 + tools/perf/util/util.c | 1 + tools/perf/util/util.h | 1 + 3 files changed, 3 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 78f7b920e548..95c58fc15284 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -458,6 +458,7 @@ int main(int argc, const char **argv) /* The page_size is placed in util object. */ page_size = sysconf(_SC_PAGE_SIZE); + cacheline_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); cmd = perf_extract_argv0_path(argv[0]); if (!cmd) diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 7fff6be07f07..95aefa78bb07 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -17,6 +17,7 @@ * XXX We need to find a better place for these things... */ unsigned int page_size; +int cacheline_size; bool test_attr__enabled; diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index b03da44e94e4..66864364ccb4 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -304,6 +304,7 @@ char *rtrim(char *s); void dump_stack(void); extern unsigned int page_size; +extern int cacheline_size; void get_term_dimensions(struct winsize *ws); -- cgit v1.2.3 From 9b32ba71ba905b90610fc2aad77cb98a373c5624 Mon Sep 17 00:00:00 2001 From: Don Zickus Date: Sun, 1 Jun 2014 15:38:29 +0200 Subject: perf tools: Add dcacheline sort In perf's 'mem-mode', one can get access to a whole bunch of details specific to a particular sample instruction. A bunch of those details relate to the data address. One interesting thing you can do with data addresses is to convert them into a unique cacheline they belong too. Organizing these data cachelines into similar groups and sorting them can reveal cache contention. This patch creates an alogorithm based on various sample details that can help group entries together into data cachelines and allows 'perf report' to sort on it. The algorithm relies on having proper mmap2 support in the kernel to help determine if the memory map the data address belongs to is private to a pid or globally shared. The alogortithm is as follows: o group cpumodes together o group entries with discovered maps together o sort on major, minor, inode and inode generation numbers o if userspace anon, then sort on pid o sort on cachelines based on data addresses The 'dcacheline' sort option in 'perf report' only works in 'mem-mode'. Sample output: # # Samples: 206 of event 'cpu/mem-loads/pp' # Total weight : 2534 # Sort order : dcacheline,pid # # Overhead Samples Data Cacheline Command: Pid # ........ ............ ...................................................................... .................. # 13.22% 1 [k] 0xffff88042f08ebc0 swapper: 0 9.27% 1 [k] 0xffff88082e8cea80 swapper: 0 3.59% 2 [k] 0xffffffff819ba180 swapper: 0 0.32% 1 [k] arch_trigger_all_cpu_backtrace_handler_na.23901+0xffffffffffffffe0 swapper: 0 0.32% 1 [k] timekeeper_seq+0xfffffffffffffff8 swapper: 0 Note: Added a '+1' to symlen size in hists__calc_col_len to prevent the next column from prematurely tabbing over and mis-aligning. Not sure what the problem is. Signed-off-by: Don Zickus Link: http://lkml.kernel.org/r/1401208087-181977-8-git-send-email-dzickus@redhat.com Signed-off-by: Jiri Olsa --- tools/perf/Documentation/perf-report.txt | 3 +- tools/perf/util/hist.c | 2 + tools/perf/util/hist.h | 1 + tools/perf/util/sort.c | 107 +++++++++++++++++++++++++++++++ tools/perf/util/sort.h | 1 + 5 files changed, 113 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 00fbfb6d7f14..d2b59af62bc0 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -119,7 +119,7 @@ OPTIONS If --mem-mode option is used, following sort keys are also available (incompatible with --branch-stack): - symbol_daddr, dso_daddr, locked, tlb, mem, snoop. + symbol_daddr, dso_daddr, locked, tlb, mem, snoop, dcacheline. - symbol_daddr: name of data symbol being executed on at the time of sample - dso_daddr: name of library or module containing the data being executed @@ -128,6 +128,7 @@ OPTIONS - tlb: type of tlb access for the data at the time of sample - mem: type of memory access for the data at the time of sample - snoop: type of snoop (if any) for the data at the time of sample + - dcacheline: the cacheline the data address is on at the time of sample And default sort keys are changed to local_weight, mem, sym, dso, symbol_daddr, dso_daddr, snoop, tlb, locked, see '--mem-mode'. diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index d5f47a47c4bf..30df6187ee02 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -128,6 +128,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) + unresolved_col_width + 2; hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, symlen); + hists__new_col_len(hists, HISTC_MEM_DCACHELINE, + symlen + 1); } else { symlen = unresolved_col_width + 4 + 2; hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index d2bf03575d5f..742f49a85725 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -72,6 +72,7 @@ enum hist_column { HISTC_MEM_TLB, HISTC_MEM_LVL, HISTC_MEM_SNOOP, + HISTC_MEM_DCACHELINE, HISTC_TRANSACTION, HISTC_NR_COLS, /* Last entry */ }; diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 45512baaab67..1ec57dd82284 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,3 +1,4 @@ +#include #include "sort.h" #include "hist.h" #include "comm.h" @@ -784,6 +785,104 @@ static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf, return repsep_snprintf(bf, size, "%-*s", width, out); } +static inline u64 cl_address(u64 address) +{ + /* return the cacheline of the address */ + return (address & ~(cacheline_size - 1)); +} + +static int64_t +sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right) +{ + u64 l, r; + struct map *l_map, *r_map; + + if (!left->mem_info) return -1; + if (!right->mem_info) return 1; + + /* group event types together */ + if (left->cpumode > right->cpumode) return -1; + if (left->cpumode < right->cpumode) return 1; + + l_map = left->mem_info->daddr.map; + r_map = right->mem_info->daddr.map; + + /* if both are NULL, jump to sort on al_addr instead */ + if (!l_map && !r_map) + goto addr; + + if (!l_map) return -1; + if (!r_map) return 1; + + if (l_map->maj > r_map->maj) return -1; + if (l_map->maj < r_map->maj) return 1; + + if (l_map->min > r_map->min) return -1; + if (l_map->min < r_map->min) return 1; + + if (l_map->ino > r_map->ino) return -1; + if (l_map->ino < r_map->ino) return 1; + + if (l_map->ino_generation > r_map->ino_generation) return -1; + if (l_map->ino_generation < r_map->ino_generation) return 1; + + /* + * Addresses with no major/minor numbers are assumed to be + * anonymous in userspace. Sort those on pid then address. + * + * The kernel and non-zero major/minor mapped areas are + * assumed to be unity mapped. Sort those on address. + */ + + if ((left->cpumode != PERF_RECORD_MISC_KERNEL) && + (!(l_map->flags & MAP_SHARED)) && + !l_map->maj && !l_map->min && !l_map->ino && + !l_map->ino_generation) { + /* userspace anonymous */ + + if (left->thread->pid_ > right->thread->pid_) return -1; + if (left->thread->pid_ < right->thread->pid_) return 1; + } + +addr: + /* al_addr does all the right addr - start + offset calculations */ + l = cl_address(left->mem_info->daddr.al_addr); + r = cl_address(right->mem_info->daddr.al_addr); + + if (l > r) return -1; + if (l < r) return 1; + + return 0; +} + +static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf, + size_t size, unsigned int width) +{ + + uint64_t addr = 0; + struct map *map = NULL; + struct symbol *sym = NULL; + char level = he->level; + + if (he->mem_info) { + addr = cl_address(he->mem_info->daddr.al_addr); + map = he->mem_info->daddr.map; + sym = he->mem_info->daddr.sym; + + /* print [s] for shared data mmaps */ + if ((he->cpumode != PERF_RECORD_MISC_KERNEL) && + map && (map->type == MAP__VARIABLE) && + (map->flags & MAP_SHARED) && + (map->maj || map->min || map->ino || + map->ino_generation)) + level = 's'; + else if (!map) + level = 'X'; + } + return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size, + width); +} + struct sort_entry sort_mispredict = { .se_header = "Branch Mispredicted", .se_cmp = sort__mispredict_cmp, @@ -876,6 +975,13 @@ struct sort_entry sort_mem_snoop = { .se_width_idx = HISTC_MEM_SNOOP, }; +struct sort_entry sort_mem_dcacheline = { + .se_header = "Data Cacheline", + .se_cmp = sort__dcacheline_cmp, + .se_snprintf = hist_entry__dcacheline_snprintf, + .se_width_idx = HISTC_MEM_DCACHELINE, +}; + static int64_t sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) { @@ -1043,6 +1149,7 @@ static struct sort_dimension memory_sort_dimensions[] = { DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb), DIM(SORT_MEM_LVL, "mem", sort_mem_lvl), DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), + DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline), }; #undef DIM diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 6de22f838854..041f0c9cea2b 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -186,6 +186,7 @@ enum sort_type { SORT_MEM_TLB, SORT_MEM_LVL, SORT_MEM_SNOOP, + SORT_MEM_DCACHELINE, }; /* -- cgit v1.2.3 From 36d789a4d75f3826faa6e75b018942b63ffed1a0 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 6 Jun 2014 07:13:45 +0000 Subject: perf probe: Improve error message for unknown member of data structure Improve the error message if we can not find given member in the given structure. Currently perf probe shows a wrong error message as below. ----- # perf probe getname_flags:65 "result->BOGUS" result(type:filename) has no member BOGUS. Failed to find 'result' in this function. Error: Failed to add events. (-22) ----- The first message is correct, but the second one is not, since we didn't fail to find a variable but fails to find the member of given variable. ----- # perf probe getname_flags:65 "result->BOGUS" result(type:filename) has no member BOGUS. Error: Failed to add events. (-22) ----- With this patch, the error message shows only the first one. And if we really failed to find given variable, it tells us so. ----- # perf probe getname_flags:65 "BOGUS" Failed to find 'BOGUS' in this function. Error: Failed to add events. (-2) ----- Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Masami Hiramatsu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20140606071345.6788.23744.stgit@kbuild-fedora.novalocal Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 9d8eb26f0533..ce8faf47691a 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -573,14 +573,13 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) { /* Search again in global variables */ if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) + pr_warning("Failed to find '%s' in this function.\n", + pf->pvar->var); ret = -ENOENT; } if (ret >= 0) ret = convert_variable(&vr_die, pf); - if (ret < 0) - pr_warning("Failed to find '%s' in this function.\n", - pf->pvar->var); return ret; } -- cgit v1.2.3 From 69e96eaa4fef04ad543eda3eab787dbae99d8912 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 6 Jun 2014 07:13:59 +0000 Subject: perf probe: Improve an error message of perf probe --vars mode Fix an error message when failed to find given address in --vars mode. Without this fix, perf probe -V doesn't show the final "Error" message if it fails to find given source line. Moreover, it tells it fails to find "variables" instead of the source line. ----- # perf probe -V foo@bar Failed to find variables at foo@bar (0) ----- The result also shows mysterious error code. Actually the error returns 0 or -ENOENT means that it just fails to find the address of given source line. (0 means there is no matching address, and -ENOENT means there is an entry(DIE) but it has no instance, e.g. an empty inlined function) This fixes it to show what happened and the final error message as below. ----- # perf probe -V foo@bar Failed to find the address of foo@bar Error: Failed to show vars. ----- Signed-off-by: Masami Hiramatsu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20140606071359.6788.84716.stgit@kbuild-fedora.novalocal Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 7 ++++++- tools/perf/util/probe-finder.c | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 0d1542f33d87..44c71417262f 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -721,9 +721,14 @@ static int show_available_vars_at(struct debuginfo *dinfo, ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, max_vls, externs); if (ret <= 0) { - pr_err("Failed to find variables at %s (%d)\n", buf, ret); + if (ret == 0 || ret == -ENOENT) { + pr_err("Failed to find the address of %s\n", buf); + ret = -ENOENT; + } else + pr_warning("Debuginfo analysis failed.\n"); goto end; } + /* Some variables are found */ fprintf(stdout, "Available variables at %s\n", buf); for (i = 0; i < ret; i++) { diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ce8faf47691a..98e304766416 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -1280,7 +1280,11 @@ out: return ret; } -/* Find available variables at given probe point */ +/* + * Find available variables at given probe point + * Return the number of found probe points. Return 0 if there is no + * matched probe point. Return <0 if an error occurs. + */ int debuginfo__find_available_vars_at(struct debuginfo *dbg, struct perf_probe_event *pev, struct variable_list **vls, -- cgit v1.2.3 From 5ee05b8801892ecc5df44e03429008dfa89aa361 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 6 Jun 2014 07:14:06 +0000 Subject: perf probe: Improve error messages in --line option Improve error messages of 'perf probe --line' mode. Currently 'perf probe' shows the "Debuginfo analysis failed" message with an error code when the given symbol is not found: ----- # perf probe -L page_cgroup_init_flatmem Debuginfo analysis failed. (-2) Error: Failed to show lines. ----- But -2 (-ENOENT) means that the given source line or function was not found. With this patch, 'perf probe' shows the correct error message: ----- # perf probe -L page_cgroup_init_flatmem Specified source line is not found. Error: Failed to show lines. ----- There is also another debug error code is shown in the same function after get_real_path(). This removes that too. Signed-off-by: Masami Hiramatsu Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20140606071406.6788.47850.stgit@kbuild-fedora.novalocal Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 44c71417262f..9a0a1839a377 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -628,11 +628,11 @@ static int __show_line_range(struct line_range *lr, const char *module) ret = debuginfo__find_line_range(dinfo, lr); debuginfo__delete(dinfo); - if (ret == 0) { + if (ret == 0 || ret == -ENOENT) { pr_warning("Specified source line is not found.\n"); return -ENOENT; } else if (ret < 0) { - pr_warning("Debuginfo analysis failed. (%d)\n", ret); + pr_warning("Debuginfo analysis failed.\n"); return ret; } @@ -641,7 +641,7 @@ static int __show_line_range(struct line_range *lr, const char *module) ret = get_real_path(tmp, lr->comp_dir, &lr->path); free(tmp); /* Free old path */ if (ret < 0) { - pr_warning("Failed to find source file. (%d)\n", ret); + pr_warning("Failed to find source file path.\n"); return ret; } -- cgit v1.2.3 From 17314e2385c6627fcab4b8f97bd6668bb63495c0 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Jun 2014 14:43:37 +0900 Subject: perf record: Fix to honor user freq/interval properly When configuring event perf checked a wrong condition that user specified both of freq (-F) and period (-c) or the event has no default value. This worked because most of events don't have default value and only tracepoint events have default of 1 (and it's not desirable to change it for those events). However, Andi's downloadable event patch changes the situation so it cannot change the value for those events. Fix it by allowing override the default value if user gives one of the options. $ perf record -a -e uops_retired.all -F 4000 sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.325 MB perf.data (~14185 samples) ] $ perf evlist -F cpu/uops_retired.all/: sample_freq=4000 Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Frederic Weisbecker Link: http://lkml.kernel.org/r/1402292617-26278-1-git-send-email-namhyung@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/evsel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 21154dabc5fa..8606175fe1e8 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -589,10 +589,10 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) } /* - * We default some events to a 1 default interval. But keep + * We default some events to have a default interval. But keep * it a weak assumption overridable by the user. */ - if (!attr->sample_period || (opts->user_freq != UINT_MAX && + if (!attr->sample_period || (opts->user_freq != UINT_MAX || opts->user_interval != ULLONG_MAX)) { if (opts->freq) { perf_evsel__set_sample_bit(evsel, PERIOD); -- cgit v1.2.3 From 0c4e774fad0202b91dea8d99c04e9bdf2c2c6647 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 17 Apr 2014 19:39:10 +0200 Subject: perf tools: Cache register accesses for unwind processing Caching registers value into an array. Got about 4% speed up of perf_reg_value function for report command processing dwarf unwind stacks. Output from report over 1.5 GB data with DWARF unwind stacks: (TODO fix perf diff) current code: 5.84% perf perf [.] perf_reg_value change: 1.94% perf perf [.] perf_reg_value And little bit of overall speed up: (perf stat -r 5 -e '{cycles,instructions}:u' ...) current code: 310,298,611,754 cycles ( +- 0.33% ) 439,669,689,341 instructions ( +- 0.03% ) 188.656753166 seconds time elapsed ( +- 0.82% ) change: 291,315,329,878 cycles ( +- 0.22% ) 391,763,485,304 instructions ( +- 0.03% ) 180.742249687 seconds time elapsed ( +- 0.64% ) Acked-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jean Pihet Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1401892622-30848-2-git-send-email-jolsa@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/event.h | 5 +++++ tools/perf/util/perf_regs.c | 10 +++++++++- tools/perf/util/perf_regs.h | 4 +++- 3 files changed, 17 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 9ba2eb3bdcfd..e5dd40addb30 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -7,6 +7,7 @@ #include "../perf.h" #include "map.h" #include "build-id.h" +#include "perf_regs.h" struct mmap_event { struct perf_event_header header; @@ -89,6 +90,10 @@ struct regs_dump { u64 abi; u64 mask; u64 *regs; + + /* Cached values/mask filled by first register access. */ + u64 cache_regs[PERF_REGS_MAX]; + u64 cache_mask; }; struct stack_dump { diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c index a3539ef30b15..43168fb0d9a2 100644 --- a/tools/perf/util/perf_regs.c +++ b/tools/perf/util/perf_regs.c @@ -1,11 +1,15 @@ #include #include "perf_regs.h" +#include "event.h" int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) { int i, idx = 0; u64 mask = regs->mask; + if (regs->cache_mask & (1 << id)) + goto out; + if (!(mask & (1 << id))) return -EINVAL; @@ -14,6 +18,10 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) idx++; } - *valp = regs->regs[idx]; + regs->cache_mask |= (1 << id); + regs->cache_regs[id] = regs->regs[idx]; + +out: + *valp = regs->cache_regs[id]; return 0; } diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 79c78f74e0cf..980dbf76bc98 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -2,7 +2,8 @@ #define __PERF_REGS_H #include -#include "event.h" + +struct regs_dump; #ifdef HAVE_PERF_REGS_SUPPORT #include @@ -11,6 +12,7 @@ int perf_reg_value(u64 *valp, struct regs_dump *regs, int id); #else #define PERF_REGS_MASK 0 +#define PERF_REGS_MAX 0 static inline const char *perf_reg_name(int id __maybe_unused) { -- cgit v1.2.3 From ca40e2af1f75eddf7eb2b93fde6391ea185d8fc8 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 7 May 2014 18:30:45 +0200 Subject: perf tools: Separate dso data related variables Add separated structure/namespace for data related variables. We are going to add mode of them, so this way they will be clearly separated. Acked-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jean Pihet Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1401892622-30848-3-git-send-email-jolsa@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/dso.c | 8 ++++---- tools/perf/util/dso.h | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 64453d63b971..1c3cdaf228c1 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -292,7 +292,7 @@ dso_cache__read(struct dso *dso, struct machine *machine, cache->offset = cache_offset; cache->size = ret; - dso_cache__insert(&dso->cache, cache); + dso_cache__insert(&dso->data.cache, cache); ret = dso_cache__memcpy(cache, offset, data, size); @@ -310,7 +310,7 @@ static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, { struct dso_cache *cache; - cache = dso_cache__find(&dso->cache, offset); + cache = dso_cache__find(&dso->data.cache, offset); if (cache) return dso_cache__memcpy(cache, offset, data, size); else @@ -473,7 +473,7 @@ struct dso *dso__new(const char *name) dso__set_short_name(dso, dso->name, false); for (i = 0; i < MAP__NR_TYPES; ++i) dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; - dso->cache = RB_ROOT; + dso->data.cache = RB_ROOT; dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND; dso->loaded = 0; @@ -506,7 +506,7 @@ void dso__delete(struct dso *dso) dso->long_name_allocated = false; } - dso_cache__free(&dso->cache); + dso_cache__free(&dso->data.cache); dso__free_a2l(dso); zfree(&dso->symsrc_filename); free(dso); diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 38efe95a7fdd..7637fdd680b2 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -76,7 +76,6 @@ struct dso { struct list_head node; struct rb_root symbols[MAP__NR_TYPES]; struct rb_root symbol_names[MAP__NR_TYPES]; - struct rb_root cache; void *a2l; char *symsrc_filename; unsigned int a2l_fails; @@ -99,6 +98,12 @@ struct dso { const char *long_name; u16 long_name_len; u16 short_name_len; + + /* dso data file */ + struct { + struct rb_root cache; + } data; + char name[0]; }; -- cgit v1.2.3 From 53fa8eaa093ad87eb59379de059e76d735a5de45 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 28 Apr 2014 16:43:43 +0200 Subject: perf tools: Add data_fd into dso object Adding data_fd into dso object so we could handle caching of opened dso file data descriptors coming int next patches. Adding dso__data_close interface to keep the data_fd updated when the descriptor is closed. Acked-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jean Pihet Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1401892622-30848-4-git-send-email-jolsa@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/dso.c | 23 +++++++++++++++++++---- tools/perf/util/dso.h | 3 +++ tools/perf/util/unwind-libunwind.c | 4 ++-- 3 files changed, 24 insertions(+), 6 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 1c3cdaf228c1..5acb4b8b35d7 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -159,6 +159,14 @@ static int open_dso(struct dso *dso, struct machine *machine) return fd; } +void dso__data_close(struct dso *dso) +{ + if (dso->data.fd >= 0) { + close(dso->data.fd); + dso->data.fd = -1; + } +} + int dso__data_fd(struct dso *dso, struct machine *machine) { enum dso_binary_type binary_type_data[] = { @@ -168,8 +176,13 @@ int dso__data_fd(struct dso *dso, struct machine *machine) }; int i = 0; - if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) - return open_dso(dso, machine); + if (dso->data.fd >= 0) + return dso->data.fd; + + if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) { + dso->data.fd = open_dso(dso, machine); + return dso->data.fd; + } do { int fd; @@ -178,7 +191,7 @@ int dso__data_fd(struct dso *dso, struct machine *machine) fd = open_dso(dso, machine); if (fd >= 0) - return fd; + return dso->data.fd = fd; } while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND); @@ -301,7 +314,7 @@ dso_cache__read(struct dso *dso, struct machine *machine, if (ret <= 0) free(cache); - close(fd); + dso__data_close(dso); return ret; } @@ -474,6 +487,7 @@ struct dso *dso__new(const char *name) for (i = 0; i < MAP__NR_TYPES; ++i) dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; dso->data.cache = RB_ROOT; + dso->data.fd = -1; dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND; dso->loaded = 0; @@ -506,6 +520,7 @@ void dso__delete(struct dso *dso) dso->long_name_allocated = false; } + dso__data_close(dso); dso_cache__free(&dso->data.cache); dso__free_a2l(dso); zfree(&dso->symsrc_filename); diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 7637fdd680b2..e48dcf59b570 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -102,6 +102,7 @@ struct dso { /* dso data file */ struct { struct rb_root cache; + int fd; } data; char name[0]; @@ -147,6 +148,8 @@ int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type t char *root_dir, char *filename, size_t size); int dso__data_fd(struct dso *dso, struct machine *machine); +void dso__data_close(struct dso *dso); + ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, u64 offset, u8 *data, ssize_t size); ssize_t dso__data_read_addr(struct dso *dso, struct map *map, diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index bd5768d74f01..4f8dd9ee3899 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -250,7 +250,7 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, /* Check the .eh_frame section for unwinding info */ offset = elf_section_offset(fd, ".eh_frame_hdr"); - close(fd); + dso__data_close(dso); if (offset) ret = unwind_spec_ehframe(dso, machine, offset, @@ -271,7 +271,7 @@ static int read_unwind_spec_debug_frame(struct dso *dso, /* Check the .debug_frame section for unwinding info */ *offset = elf_section_offset(fd, ".debug_frame"); - close(fd); + dso__data_close(dso); if (*offset) return 0; -- cgit v1.2.3 From eba5102d2f0b4117edd089f2d882d9386025c829 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 30 Apr 2014 15:00:59 +0200 Subject: perf tools: Add global list of opened dso objects Adding global list of opened dso objects, so we can track them and use the list for caching dso data file descriptors. Acked-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jean Pihet Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1401892622-30848-5-git-send-email-jolsa@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/dso.c | 41 +++++++++++++++++++++++++++++++++++++++-- tools/perf/util/dso.h | 1 + 2 files changed, 40 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 5acb4b8b35d7..5d7c7bcc6276 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -136,7 +136,22 @@ int dso__read_binary_type_filename(const struct dso *dso, return ret; } -static int open_dso(struct dso *dso, struct machine *machine) +/* + * Global list of open DSOs. + */ +static LIST_HEAD(dso__data_open); + +static void dso__list_add(struct dso *dso) +{ + list_add_tail(&dso->data.open_entry, &dso__data_open); +} + +static void dso__list_del(struct dso *dso) +{ + list_del(&dso->data.open_entry); +} + +static int __open_dso(struct dso *dso, struct machine *machine) { int fd; char *root_dir = (char *)""; @@ -159,14 +174,35 @@ static int open_dso(struct dso *dso, struct machine *machine) return fd; } -void dso__data_close(struct dso *dso) +static int open_dso(struct dso *dso, struct machine *machine) +{ + int fd = __open_dso(dso, machine); + + if (fd > 0) + dso__list_add(dso); + + return fd; +} + +static void close_data_fd(struct dso *dso) { if (dso->data.fd >= 0) { close(dso->data.fd); dso->data.fd = -1; + dso__list_del(dso); } } +static void close_dso(struct dso *dso) +{ + close_data_fd(dso); +} + +void dso__data_close(struct dso *dso) +{ + close_dso(dso); +} + int dso__data_fd(struct dso *dso, struct machine *machine) { enum dso_binary_type binary_type_data[] = { @@ -499,6 +535,7 @@ struct dso *dso__new(const char *name) dso->kernel = DSO_TYPE_USER; dso->needs_swap = DSO_SWAP__UNSET; INIT_LIST_HEAD(&dso->node); + INIT_LIST_HEAD(&dso->data.open_entry); } return dso; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index e48dcf59b570..90988bf06641 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -103,6 +103,7 @@ struct dso { struct { struct rb_root cache; int fd; + struct list_head open_entry; } data; char name[0]; -- cgit v1.2.3 From bda6ee4a94d1e1be0c1428d37bc0d3da2e5793ad Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 30 Apr 2014 15:25:10 +0200 Subject: perf tools: Add global count of opened dso objects Adding global count of opened dso objects so we could properly limit the number of opened dso data file descriptors. Acked-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jean Pihet Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1401892622-30848-6-git-send-email-jolsa@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/dso.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 5d7c7bcc6276..76e5c13afc8f 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1,3 +1,4 @@ +#include #include "symbol.h" #include "dso.h" #include "machine.h" @@ -137,18 +138,23 @@ int dso__read_binary_type_filename(const struct dso *dso, } /* - * Global list of open DSOs. + * Global list of open DSOs and the counter. */ static LIST_HEAD(dso__data_open); +static long dso__data_open_cnt; static void dso__list_add(struct dso *dso) { list_add_tail(&dso->data.open_entry, &dso__data_open); + dso__data_open_cnt++; } static void dso__list_del(struct dso *dso) { list_del(&dso->data.open_entry); + WARN_ONCE(dso__data_open_cnt <= 0, + "DSO data fd counter out of bounds."); + dso__data_open_cnt--; } static int __open_dso(struct dso *dso, struct machine *machine) -- cgit v1.2.3 From c658045197814b7d762662f9aa9f652379121a03 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 30 Apr 2014 15:47:27 +0200 Subject: perf tools: Cache dso data file descriptor Caching dso data file descriptors to avoid expensive re-opens especially during DWARF unwind. We keep dsos data file descriptors open until their count reaches the half of the current fd open limit (RLIMIT_NOFILE). In this case we close file descriptor of the first opened dso object. We've got overall speedup (~27% for my workload) of report: 'perf report --stdio -i perf-test.data' (3 runs) (perf-test.data size was around 12GB) current code: 545,640,944,228 cycles ( +- 0.53% ) 785,255,798,320 instructions ( +- 0.03% ) 366.340910010 seconds time elapsed ( +- 3.65% ) after change: 435,895,036,114 cycles ( +- 0.26% ) 636,790,271,176 instructions ( +- 0.04% ) 266.481463387 seconds time elapsed ( +- 0.13% ) Acked-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jean Pihet Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1401892622-30848-7-git-send-email-jolsa@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/dso.c | 61 ++++++++++++++++++++++++++++++++++++-- tools/perf/util/unwind-libunwind.c | 2 -- 2 files changed, 59 insertions(+), 4 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 76e5c13afc8f..fbf6cc98b8a9 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1,4 +1,6 @@ #include +#include +#include #include "symbol.h" #include "dso.h" #include "machine.h" @@ -180,12 +182,20 @@ static int __open_dso(struct dso *dso, struct machine *machine) return fd; } +static void check_data_close(void); + static int open_dso(struct dso *dso, struct machine *machine) { int fd = __open_dso(dso, machine); - if (fd > 0) + if (fd > 0) { dso__list_add(dso); + /* + * Check if we crossed the allowed number + * of opened DSOs and close one if needed. + */ + check_data_close(); + } return fd; } @@ -204,6 +214,54 @@ static void close_dso(struct dso *dso) close_data_fd(dso); } +static void close_first_dso(void) +{ + struct dso *dso; + + dso = list_first_entry(&dso__data_open, struct dso, data.open_entry); + close_dso(dso); +} + +static rlim_t get_fd_limit(void) +{ + struct rlimit l; + rlim_t limit = 0; + + /* Allow half of the current open fd limit. */ + if (getrlimit(RLIMIT_NOFILE, &l) == 0) { + if (l.rlim_cur == RLIM_INFINITY) + limit = l.rlim_cur; + else + limit = l.rlim_cur / 2; + } else { + pr_err("failed to get fd limit\n"); + limit = 1; + } + + return limit; +} + +static bool may_cache_fd(void) +{ + static rlim_t limit; + + if (!limit) + limit = get_fd_limit(); + + if (limit == RLIM_INFINITY) + return true; + + return limit > (rlim_t) dso__data_open_cnt; +} + +static void check_data_close(void) +{ + bool cache_fd = may_cache_fd(); + + if (!cache_fd) + close_first_dso(); +} + void dso__data_close(struct dso *dso) { close_dso(dso); @@ -356,7 +414,6 @@ dso_cache__read(struct dso *dso, struct machine *machine, if (ret <= 0) free(cache); - dso__data_close(dso); return ret; } diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 4f8dd9ee3899..25578b98f5c5 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -250,7 +250,6 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, /* Check the .eh_frame section for unwinding info */ offset = elf_section_offset(fd, ".eh_frame_hdr"); - dso__data_close(dso); if (offset) ret = unwind_spec_ehframe(dso, machine, offset, @@ -271,7 +270,6 @@ static int read_unwind_spec_debug_frame(struct dso *dso, /* Check the .debug_frame section for unwinding info */ *offset = elf_section_offset(fd, ".debug_frame"); - dso__data_close(dso); if (*offset) return 0; -- cgit v1.2.3 From c3fbd2a606c5f88de0079b027727a1fb0ae27b65 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 7 May 2014 18:51:41 +0200 Subject: perf tools: Add file size check and factor dso__data_read_offset Adding file size check, because the lseek will succeed for any offset behind file size and thus succeed when it was expected to fail. Factoring the code to check the offset against file size earlier in the flow. Acked-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jean Pihet Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1401892622-30848-8-git-send-email-jolsa@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/dso.c | 64 +++++++++++++++++++++++++++++++++++++++------------ tools/perf/util/dso.h | 1 + 2 files changed, 50 insertions(+), 15 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index fbf6cc98b8a9..db634383156c 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -205,6 +205,7 @@ static void close_data_fd(struct dso *dso) if (dso->data.fd >= 0) { close(dso->data.fd); dso->data.fd = -1; + dso->data.file_size = 0; dso__list_del(dso); } } @@ -373,16 +374,10 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset, } static ssize_t -dso_cache__read(struct dso *dso, struct machine *machine, - u64 offset, u8 *data, ssize_t size) +dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) { struct dso_cache *cache; ssize_t ret; - int fd; - - fd = dso__data_fd(dso, machine); - if (fd < 0) - return -1; do { u64 cache_offset; @@ -396,10 +391,10 @@ dso_cache__read(struct dso *dso, struct machine *machine, cache_offset = offset & DSO__DATA_CACHE_MASK; ret = -EINVAL; - if (-1 == lseek(fd, cache_offset, SEEK_SET)) + if (-1 == lseek(dso->data.fd, cache_offset, SEEK_SET)) break; - ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE); + ret = read(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE); if (ret <= 0) break; @@ -417,8 +412,8 @@ dso_cache__read(struct dso *dso, struct machine *machine, return ret; } -static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, - u64 offset, u8 *data, ssize_t size) +static ssize_t dso_cache_read(struct dso *dso, u64 offset, + u8 *data, ssize_t size) { struct dso_cache *cache; @@ -426,11 +421,10 @@ static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, if (cache) return dso_cache__memcpy(cache, offset, data, size); else - return dso_cache__read(dso, machine, offset, data, size); + return dso_cache__read(dso, offset, data, size); } -ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, - u64 offset, u8 *data, ssize_t size) +static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size) { ssize_t r = 0; u8 *p = data; @@ -438,7 +432,7 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, do { ssize_t ret; - ret = dso_cache_read(dso, machine, offset, p, size); + ret = dso_cache_read(dso, offset, p, size); if (ret < 0) return ret; @@ -458,6 +452,46 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, return r; } +static int data_file_size(struct dso *dso) +{ + struct stat st; + + if (!dso->data.file_size) { + if (fstat(dso->data.fd, &st)) { + pr_err("dso mmap failed, fstat: %s\n", strerror(errno)); + return -1; + } + dso->data.file_size = st.st_size; + } + + return 0; +} + +static ssize_t data_read_offset(struct dso *dso, u64 offset, + u8 *data, ssize_t size) +{ + if (data_file_size(dso)) + return -1; + + /* Check the offset sanity. */ + if (offset > dso->data.file_size) + return -1; + + if (offset + size < offset) + return -1; + + return cached_read(dso, offset, data, size); +} + +ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, + u64 offset, u8 *data, ssize_t size) +{ + if (dso__data_fd(dso, machine) < 0) + return -1; + + return data_read_offset(dso, offset, data, size); +} + ssize_t dso__data_read_addr(struct dso *dso, struct map *map, struct machine *machine, u64 addr, u8 *data, ssize_t size) diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 90988bf06641..da47b13595f3 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -103,6 +103,7 @@ struct dso { struct { struct rb_root cache; int fd; + size_t file_size; struct list_head open_entry; } data; -- cgit v1.2.3 From a08cae03f430b971afa508a32662dc476d42d8cb Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 7 May 2014 21:35:02 +0200 Subject: perf tools: Allow to close dso fd in case of open failure Adding do_open function that tries to close opened dso objects in case we fail to open the dso due to to crossing the allowed RLIMIT_NOFILE limit. Acked-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jean Pihet Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1401892622-30848-9-git-send-email-jolsa@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/dso.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index db634383156c..c30752c7eebb 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -159,6 +159,27 @@ static void dso__list_del(struct dso *dso) dso__data_open_cnt--; } +static void close_first_dso(void); + +static int do_open(char *name) +{ + int fd; + + do { + fd = open(name, O_RDONLY); + if (fd >= 0) + return fd; + + pr_debug("dso open failed, mmap: %s\n", strerror(errno)); + if (!dso__data_open_cnt || errno != EMFILE) + break; + + close_first_dso(); + } while (1); + + return -1; +} + static int __open_dso(struct dso *dso, struct machine *machine) { int fd; @@ -177,7 +198,7 @@ static int __open_dso(struct dso *dso, struct machine *machine) return -EINVAL; } - fd = open(name, O_RDONLY); + fd = do_open(name); free(name); return fd; } -- cgit v1.2.3 From c1f9aa0a61bde512a68060883d1c3c1955a546ea Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 7 May 2014 21:09:59 +0200 Subject: perf tools: Add dso__data_* interface descriptons Adding descriptions/explanations for dso__data_* interface functions. Acked-by: Namhyung Kim Cc: Arnaldo Carvalho de Melo Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jean Pihet Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1401892622-30848-10-git-send-email-jolsa@kernel.org Signed-off-by: Jiri Olsa --- tools/perf/util/dso.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/dso.h | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index c30752c7eebb..819f10414f08 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -205,6 +205,13 @@ static int __open_dso(struct dso *dso, struct machine *machine) static void check_data_close(void); +/** + * dso_close - Open DSO data file + * @dso: dso object + * + * Open @dso's data file descriptor and updates + * list/count of open DSO objects. + */ static int open_dso(struct dso *dso, struct machine *machine) { int fd = __open_dso(dso, machine); @@ -231,6 +238,13 @@ static void close_data_fd(struct dso *dso) } } +/** + * dso_close - Close DSO data file + * @dso: dso object + * + * Close @dso's data file descriptor and updates + * list/count of open DSO objects. + */ static void close_dso(struct dso *dso) { close_data_fd(dso); @@ -276,6 +290,11 @@ static bool may_cache_fd(void) return limit > (rlim_t) dso__data_open_cnt; } +/* + * Check and close LRU dso if we crossed allowed limit + * for opened dso file descriptors. The limit is half + * of the RLIMIT_NOFILE files opened. +*/ static void check_data_close(void) { bool cache_fd = may_cache_fd(); @@ -284,11 +303,25 @@ static void check_data_close(void) close_first_dso(); } +/** + * dso__data_close - Close DSO data file + * @dso: dso object + * + * External interface to close @dso's data file descriptor. + */ void dso__data_close(struct dso *dso) { close_dso(dso); } +/** + * dso__data_fd - Get dso's data file descriptor + * @dso: dso object + * @machine: machine object + * + * External interface to find dso's file, open it and + * returns file descriptor. + */ int dso__data_fd(struct dso *dso, struct machine *machine) { enum dso_binary_type binary_type_data[] = { @@ -445,6 +478,11 @@ static ssize_t dso_cache_read(struct dso *dso, u64 offset, return dso_cache__read(dso, offset, data, size); } +/* + * Reads and caches dso data DSO__DATA_CACHE_SIZE size chunks + * in the rb_tree. Any read to already cached data is served + * by cached data. + */ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size) { ssize_t r = 0; @@ -504,6 +542,17 @@ static ssize_t data_read_offset(struct dso *dso, u64 offset, return cached_read(dso, offset, data, size); } +/** + * dso__data_read_offset - Read data from dso file offset + * @dso: dso object + * @machine: machine object + * @offset: file offset + * @data: buffer to store data + * @size: size of the @data buffer + * + * External interface to read data from dso file offset. Open + * dso data file and use cached_read to get the data. + */ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, u64 offset, u8 *data, ssize_t size) { @@ -513,6 +562,16 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, return data_read_offset(dso, offset, data, size); } +/** + * dso__data_read_addr - Read data from dso address + * @dso: dso object + * @machine: machine object + * @add: virtual memory address + * @data: buffer to store data + * @size: size of the @data buffer + * + * External interface to read data from dso address. + */ ssize_t dso__data_read_addr(struct dso *dso, struct map *map, struct machine *machine, u64 addr, u8 *data, ssize_t size) diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index da47b13595f3..ad553ba257bf 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -149,6 +149,44 @@ char dso__symtab_origin(const struct dso *dso); int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, char *root_dir, char *filename, size_t size); +/* + * The dso__data_* external interface provides following functions: + * dso__data_fd + * dso__data_close + * dso__data_read_offset + * dso__data_read_addr + * + * Please refer to the dso.c object code for each function and + * arguments documentation. Following text tries to explain the + * dso file descriptor caching. + * + * The dso__data* interface allows caching of opened file descriptors + * to speed up the dso data accesses. The idea is to leave the file + * descriptor opened ideally for the whole life of the dso object. + * + * The current usage of the dso__data_* interface is as follows: + * + * Get DSO's fd: + * int fd = dso__data_fd(dso, machine); + * USE 'fd' SOMEHOW + * + * Read DSO's data: + * n = dso__data_read_offset(dso_0, &machine, 0, buf, BUFSIZE); + * n = dso__data_read_addr(dso_0, &machine, 0, buf, BUFSIZE); + * + * Eventually close DSO's fd: + * dso__data_close(dso); + * + * It is not necessary to close the DSO object data file. Each time new + * DSO data file is opened, the limit (RLIMIT_NOFILE/2) is checked. Once + * it is crossed, the oldest opened DSO object is closed. + * + * The dso__delete function calls close_dso function to ensure the + * data file descriptor gets closed/unmapped before the dso object + * is freed. + * + * TODO +*/ int dso__data_fd(struct dso *dso, struct machine *machine); void dso__data_close(struct dso *dso); -- cgit v1.2.3 From a93f0e551af9e194db38bfe16001e17a3a1d189a Mon Sep 17 00:00:00 2001 From: Simon Que Date: Mon, 16 Jun 2014 11:32:09 -0700 Subject: perf symbols: Get kernel start address by symbol name The function machine__get_kernel_start_addr() was taking the first symbol of kallsyms as the start address. This is incorrect in certain cases where the first symbol is something at 0, while the actual kernel functions begin at a later point (e.g. 0x80200000). This patch fixes machine__get_kernel_start_addr() to search for the symbol "_text" or "_stext", which marks the beginning of kernel mapping. This was already being done in machine__create_kernel_maps(). Thus, this patch is just a refactor, to move that code into machine__get_kernel_start_addr(). Signed-off-by: Simon Que Link: http://lkml.kernel.org/r/1402943529-13244-1-git-send-email-sque@chromium.org Signed-off-by: Jiri Olsa --- tools/perf/util/machine.c | 54 +++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 32 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 0e5fea95d596..c73e1fc12e53 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -496,18 +496,6 @@ struct process_args { u64 start; }; -static int symbol__in_kernel(void *arg, const char *name, - char type __maybe_unused, u64 start) -{ - struct process_args *args = arg; - - if (strchr(name, '[')) - return 0; - - args->start = start; - return 1; -} - static void machine__get_kallsyms_filename(struct machine *machine, char *buf, size_t bufsz) { @@ -517,27 +505,41 @@ static void machine__get_kallsyms_filename(struct machine *machine, char *buf, scnprintf(buf, bufsz, "%s/proc/kallsyms", machine->root_dir); } -/* Figure out the start address of kernel map from /proc/kallsyms */ -static u64 machine__get_kernel_start_addr(struct machine *machine) +const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL}; + +/* Figure out the start address of kernel map from /proc/kallsyms. + * Returns the name of the start symbol in *symbol_name. Pass in NULL as + * symbol_name if it's not that important. + */ +static u64 machine__get_kernel_start_addr(struct machine *machine, + const char **symbol_name) { char filename[PATH_MAX]; - struct process_args args; + int i; + const char *name; + u64 addr = 0; machine__get_kallsyms_filename(machine, filename, PATH_MAX); if (symbol__restricted_filename(filename, "/proc/kallsyms")) return 0; - if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0) - return 0; + for (i = 0; (name = ref_reloc_sym_names[i]) != NULL; i++) { + addr = kallsyms__get_function_start(filename, name); + if (addr) + break; + } + + if (symbol_name) + *symbol_name = name; - return args.start; + return addr; } int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) { enum map_type type; - u64 start = machine__get_kernel_start_addr(machine); + u64 start = machine__get_kernel_start_addr(machine, NULL); for (type = 0; type < MAP__NR_TYPES; ++type) { struct kmap *kmap; @@ -852,23 +854,11 @@ static int machine__create_modules(struct machine *machine) return 0; } -const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL}; - int machine__create_kernel_maps(struct machine *machine) { struct dso *kernel = machine__get_kernel(machine); - char filename[PATH_MAX]; const char *name; - u64 addr = 0; - int i; - - machine__get_kallsyms_filename(machine, filename, PATH_MAX); - - for (i = 0; (name = ref_reloc_sym_names[i]) != NULL; i++) { - addr = kallsyms__get_function_start(filename, name); - if (addr) - break; - } + u64 addr = machine__get_kernel_start_addr(machine, &name); if (!addr) return -1; -- cgit v1.2.3