diff options
| author | Ingo Molnar <mingo@kernel.org> | 2016-03-11 09:40:25 +0100 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2016-03-11 09:40:25 +0100 |
| commit | ced30bc9129777d715057d06fc8dbdfd3b81e94d (patch) | |
| tree | ab682f7130a4ad1ce0d4fcb0f77f91db391bf34a /tools/perf/util | |
| parent | 3a99e6db539e53cc9c79282e80f8362b0cb96ac8 (diff) | |
| parent | 206cab651d07563d766c7f4cb73f858c5df3dec5 (diff) | |
Merge tag 'perf-core-for-mingo-20160310' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
User visible changes:
- Implement 'perf stat --metric-only' (Andi Kleen)
- Fix perf script python database export crash (Chris Phlipot)
Infrastructure changes:
- perf top/report --hierarchy assorted fixes for problems introduced in this
perf/core cycle (Namhyung Kim)
- Support '~' operation in libtraceevent (Steven Rosted)
Build fixes:
- Fix bulding of jitdump on opensuse on ubuntu systems when the DWARF
devel files are not installed (Arnaldo Carvalho de Melo)
- Do not try building jitdump on unsupported arches (Jiri Olsa)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/perf/util')
| -rw-r--r-- | tools/perf/util/Build | 3 | ||||
| -rw-r--r-- | tools/perf/util/evsel.h | 6 | ||||
| -rw-r--r-- | tools/perf/util/hist.c | 144 | ||||
| -rw-r--r-- | tools/perf/util/hist.h | 6 | ||||
| -rw-r--r-- | tools/perf/util/pmu.c | 4 | ||||
| -rw-r--r-- | tools/perf/util/sort.c | 147 | ||||
| -rw-r--r-- | tools/perf/util/sort.h | 2 |
7 files changed, 210 insertions, 102 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index df2b690970ac..eea25e2424e9 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -107,9 +107,12 @@ libperf-y += scripting-engines/ libperf-$(CONFIG_ZLIB) += zlib.o libperf-$(CONFIG_LZMA) += lzma.o libperf-y += demangle-java.o + +ifdef CONFIG_JITDUMP libperf-$(CONFIG_LIBELF) += jitdump.o libperf-$(CONFIG_LIBELF) += genelf.o libperf-$(CONFIG_LIBELF) += genelf_debug.o +endif CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" # avoid compiler warnings in 32-bit mode diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index efad78f811ad..501ea6e565f1 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -93,10 +93,8 @@ struct perf_evsel { const char *unit; struct event_format *tp_format; off_t id_offset; - union { - void *priv; - u64 db_id; - }; + void *priv; + u64 db_id; struct cgroup_sel *cgrp; void *handler; struct cpu_map *cpus; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 29da9e0d8db9..290b3cbf6877 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1087,10 +1087,103 @@ int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp, */ static void hists__apply_filters(struct hists *hists, struct hist_entry *he); +static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *he, + enum hist_filter type); + +typedef bool (*fmt_chk_fn)(struct perf_hpp_fmt *fmt); + +static bool check_thread_entry(struct perf_hpp_fmt *fmt) +{ + return perf_hpp__is_thread_entry(fmt) || perf_hpp__is_comm_entry(fmt); +} + +static void hist_entry__check_and_remove_filter(struct hist_entry *he, + enum hist_filter type, + fmt_chk_fn check) +{ + struct perf_hpp_fmt *fmt; + bool type_match = false; + struct hist_entry *parent = he->parent_he; + + switch (type) { + case HIST_FILTER__THREAD: + if (symbol_conf.comm_list == NULL && + symbol_conf.pid_list == NULL && + symbol_conf.tid_list == NULL) + return; + break; + case HIST_FILTER__DSO: + if (symbol_conf.dso_list == NULL) + return; + break; + case HIST_FILTER__SYMBOL: + if (symbol_conf.sym_list == NULL) + return; + break; + case HIST_FILTER__PARENT: + case HIST_FILTER__GUEST: + case HIST_FILTER__HOST: + case HIST_FILTER__SOCKET: + default: + return; + } + + /* if it's filtered by own fmt, it has to have filter bits */ + perf_hpp_list__for_each_format(he->hpp_list, fmt) { + if (check(fmt)) { + type_match = true; + break; + } + } + + if (type_match) { + /* + * If the filter is for current level entry, propagate + * filter marker to parents. The marker bit was + * already set by default so it only needs to clear + * non-filtered entries. + */ + if (!(he->filtered & (1 << type))) { + while (parent) { + parent->filtered &= ~(1 << type); + parent = parent->parent_he; + } + } + } else { + /* + * If current entry doesn't have matching formats, set + * filter marker for upper level entries. it will be + * cleared if its lower level entries is not filtered. + * + * For lower-level entries, it inherits parent's + * filter bit so that lower level entries of a + * non-filtered entry won't set the filter marker. + */ + if (parent == NULL) + he->filtered |= (1 << type); + else + he->filtered |= (parent->filtered & (1 << type)); + } +} + +static void hist_entry__apply_hierarchy_filters(struct hist_entry *he) +{ + hist_entry__check_and_remove_filter(he, HIST_FILTER__THREAD, + check_thread_entry); + + hist_entry__check_and_remove_filter(he, HIST_FILTER__DSO, + perf_hpp__is_dso_entry); + + hist_entry__check_and_remove_filter(he, HIST_FILTER__SYMBOL, + perf_hpp__is_sym_entry); + + hists__apply_filters(he->hists, he); +} static struct hist_entry *hierarchy_insert_entry(struct hists *hists, struct rb_root *root, struct hist_entry *he, + struct hist_entry *parent_he, struct perf_hpp_list *hpp_list) { struct rb_node **p = &root->rb_node; @@ -1125,11 +1218,13 @@ static struct hist_entry *hierarchy_insert_entry(struct hists *hists, if (new == NULL) return NULL; - hists__apply_filters(hists, new); hists->nr_entries++; /* save related format list for output */ new->hpp_list = hpp_list; + new->parent_he = parent_he; + + hist_entry__apply_hierarchy_filters(new); /* some fields are now passed to 'new' */ perf_hpp_list__for_each_sort_list(hpp_list, fmt) { @@ -1170,14 +1265,13 @@ static int hists__hierarchy_insert_entry(struct hists *hists, continue; /* insert copy of 'he' for each fmt into the hierarchy */ - new_he = hierarchy_insert_entry(hists, root, he, &node->hpp); + new_he = hierarchy_insert_entry(hists, root, he, parent, &node->hpp); if (new_he == NULL) { ret = -1; break; } root = &new_he->hroot_in; - new_he->parent_he = parent; new_he->depth = depth++; parent = new_he; } @@ -1359,6 +1453,31 @@ void hists__inc_stats(struct hists *hists, struct hist_entry *h) hists->stats.total_period += h->stat.period; } +static void hierarchy_recalc_total_periods(struct hists *hists) +{ + struct rb_node *node; + struct hist_entry *he; + + node = rb_first(&hists->entries); + + hists->stats.total_period = 0; + hists->stats.total_non_filtered_period = 0; + + /* + * recalculate total period using top-level entries only + * since lower level entries only see non-filtered entries + * but upper level entries have sum of both entries. + */ + while (node) { + he = rb_entry(node, struct hist_entry, rb_node); + node = rb_next(node); + + hists->stats.total_period += he->stat.period; + if (!he->filtered) + hists->stats.total_non_filtered_period += he->stat.period; + } +} + static void hierarchy_insert_output_entry(struct rb_root *root, struct hist_entry *he) { @@ -1424,11 +1543,6 @@ static void hists__hierarchy_output_resort(struct hists *hists, continue; } - /* only update stat for leaf entries to avoid duplication */ - hists__inc_stats(hists, he); - if (!he->filtered) - hists__calc_col_len(hists, he); - if (!use_callchain) continue; @@ -1508,11 +1622,13 @@ static void output_resort(struct hists *hists, struct ui_progress *prog, hists__reset_col_len(hists); if (symbol_conf.report_hierarchy) { - return hists__hierarchy_output_resort(hists, prog, - &hists->entries_collapsed, - &hists->entries, - min_callchain_hits, - use_callchain); + hists__hierarchy_output_resort(hists, prog, + &hists->entries_collapsed, + &hists->entries, + min_callchain_hits, + use_callchain); + hierarchy_recalc_total_periods(hists); + return; } if (sort__need_collapse) @@ -1833,6 +1949,8 @@ static void hists__filter_hierarchy(struct hists *hists, int type, const void *a } } + hierarchy_recalc_total_periods(hists); + /* * resort output after applying a new filter since filter in a lower * hierarchy can change periods in a upper hierarchy. diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 2cb017f28f9e..ead18c82294f 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -79,7 +79,6 @@ struct hists { int socket_filter; struct perf_hpp_list *hpp_list; struct list_head hpp_formats; - int nr_sort_keys; int nr_hpp_node; }; @@ -241,7 +240,6 @@ struct perf_hpp_fmt { struct perf_hpp_list { struct list_head fields; struct list_head sorts; - int nr_sort_keys; }; extern struct perf_hpp_list perf_hpp_list; @@ -318,6 +316,10 @@ bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *his bool perf_hpp__is_trace_entry(struct perf_hpp_fmt *fmt); bool perf_hpp__is_srcline_entry(struct perf_hpp_fmt *fmt); bool perf_hpp__is_srcfile_entry(struct perf_hpp_fmt *fmt); +bool perf_hpp__is_thread_entry(struct perf_hpp_fmt *fmt); +bool perf_hpp__is_comm_entry(struct perf_hpp_fmt *fmt); +bool perf_hpp__is_dso_entry(struct perf_hpp_fmt *fmt); +bool perf_hpp__is_sym_entry(struct perf_hpp_fmt *fmt); struct perf_hpp_fmt *perf_hpp_fmt__dup(struct perf_hpp_fmt *fmt); diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index d8cd038baed2..adef23b1352e 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -98,7 +98,7 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char * char scale[128]; int fd, ret = -1; char path[PATH_MAX]; - const char *lc; + char *lc; snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); @@ -146,7 +146,7 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char * /* restore locale */ setlocale(LC_NUMERIC, lc); - free((char *) lc); + free(lc); ret = 0; error: diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 041f236379e0..93fa136b0025 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -27,6 +27,7 @@ int sort__has_sym = 0; int sort__has_dso = 0; int sort__has_socket = 0; int sort__has_thread = 0; +int sort__has_comm = 0; enum sort_mode sort__mode = SORT_MODE__NORMAL; /* @@ -1488,38 +1489,26 @@ bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format) return format->header == __sort__hpp_header; } -bool perf_hpp__is_trace_entry(struct perf_hpp_fmt *fmt) -{ - struct hpp_sort_entry *hse; - - if (!perf_hpp__is_sort_entry(fmt)) - return false; - - hse = container_of(fmt, struct hpp_sort_entry, hpp); - return hse->se == &sort_trace; +#define MK_SORT_ENTRY_CHK(key) \ +bool perf_hpp__is_ ## key ## _entry(struct perf_hpp_fmt *fmt) \ +{ \ + struct hpp_sort_entry *hse; \ + \ + if (!perf_hpp__is_sort_entry(fmt)) \ + return false; \ + \ + hse = container_of(fmt, struct hpp_sort_entry, hpp); \ + return hse->se == &sort_ ## key ; \ } -bool perf_hpp__is_srcline_entry(struct perf_hpp_fmt *fmt) -{ - struct hpp_sort_entry *hse; +MK_SORT_ENTRY_CHK(trace) +MK_SORT_ENTRY_CHK(srcline) +MK_SORT_ENTRY_CHK(srcfile) +MK_SORT_ENTRY_CHK(thread) +MK_SORT_ENTRY_CHK(comm) +MK_SORT_ENTRY_CHK(dso) +MK_SORT_ENTRY_CHK(sym) - if (!perf_hpp__is_sort_entry(fmt)) - return false; - - hse = container_of(fmt, struct hpp_sort_entry, hpp); - return hse->se == &sort_srcline; -} - -bool perf_hpp__is_srcfile_entry(struct perf_hpp_fmt *fmt) -{ - struct hpp_sort_entry *hse; - - if (!perf_hpp__is_sort_entry(fmt)) - return false; - - hse = container_of(fmt, struct hpp_sort_entry, hpp); - return hse->se == &sort_srcfile; -} static bool __sort__hpp_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) { @@ -1602,31 +1591,47 @@ int hist_entry__filter(struct hist_entry *he, int type, const void *arg) { struct perf_hpp_fmt *fmt; struct hpp_sort_entry *hse; + int ret = -1; + int r; - fmt = he->fmt; - if (fmt == NULL || !perf_hpp__is_sort_entry(fmt)) - return -1; + perf_hpp_list__for_each_format(he->hpp_list, fmt) { + if (!perf_hpp__is_sort_entry(fmt)) + continue; - hse = container_of(fmt, struct hpp_sort_entry, hpp); - if (hse->se->se_filter == NULL) - return -1; + hse = container_of(fmt, struct hpp_sort_entry, hpp); + if (hse->se->se_filter == NULL) + continue; + + /* + * hist entry is filtered if any of sort key in the hpp list + * is applied. But it should skip non-matched filter types. + */ + r = hse->se->se_filter(he, type, arg); + if (r >= 0) { + if (ret < 0) + ret = 0; + ret |= r; + } + } - return hse->se->se_filter(he, type, arg); + return ret; } -static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd, int level) +static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd, + struct perf_hpp_list *list, + int level) { struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level); if (hse == NULL) return -1; - perf_hpp__register_sort_field(&hse->hpp); + perf_hpp_list__register_sort_field(list, &hse->hpp); return 0; } -static int __sort_dimension__add_hpp_output(struct perf_hpp_list *list, - struct sort_dimension *sd) +static int __sort_dimension__add_hpp_output(struct sort_dimension *sd, + struct perf_hpp_list *list) { struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, 0); @@ -2147,12 +2152,14 @@ out: return ret; } -static int __sort_dimension__add(struct sort_dimension *sd, int level) +static int __sort_dimension__add(struct sort_dimension *sd, + struct perf_hpp_list *list, + int level) { if (sd->taken) return 0; - if (__sort_dimension__add_hpp_sort(sd, level) < 0) + if (__sort_dimension__add_hpp_sort(sd, list, level) < 0) return -1; if (sd->entry->se_collapse) @@ -2163,7 +2170,9 @@ static int __sort_dimension__add(struct sort_dimension *sd, int level) return 0; } -static int __hpp_dimension__add(struct hpp_dimension *hd, int level) +static int __hpp_dimension__add(struct hpp_dimension *hd, + struct perf_hpp_list *list, + int level) { struct perf_hpp_fmt *fmt; @@ -2175,7 +2184,7 @@ static int __hpp_dimension__add(struct hpp_dimension *hd, int level) return -1; hd->taken = 1; - perf_hpp__register_sort_field(fmt); + perf_hpp_list__register_sort_field(list, fmt); return 0; } @@ -2185,7 +2194,7 @@ static int __sort_dimension__add_output(struct perf_hpp_list *list, if (sd->taken) return 0; - if (__sort_dimension__add_hpp_output(list, sd) < 0) + if (__sort_dimension__add_hpp_output(sd, list) < 0) return -1; sd->taken = 1; @@ -2215,7 +2224,8 @@ int hpp_dimension__add_output(unsigned col) return __hpp_dimension__add_output(&perf_hpp_list, &hpp_sort_dimensions[col]); } -static int sort_dimension__add(const char *tok, struct perf_evlist *evlist, +static int sort_dimension__add(struct perf_hpp_list *list, const char *tok, + struct perf_evlist *evlist __maybe_unused, int level) { unsigned int i; @@ -2253,9 +2263,11 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist, sort__has_socket = 1; } else if (sd->entry == &sort_thread) { sort__has_thread = 1; + } else if (sd->entry == &sort_comm) { + sort__has_comm = 1; } - return __sort_dimension__add(sd, level); + return __sort_dimension__add(sd, list, level); } for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { @@ -2264,7 +2276,7 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist, if (strncasecmp(tok, hd->name, strlen(tok))) continue; - return __hpp_dimension__add(hd, level); + return __hpp_dimension__add(hd, list, level); } for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { @@ -2279,7 +2291,7 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist, if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) sort__has_sym = 1; - __sort_dimension__add(sd, level); + __sort_dimension__add(sd, list, level); return 0; } @@ -2295,7 +2307,7 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist, if (sd->entry == &sort_mem_daddr_sym) sort__has_sym = 1; - __sort_dimension__add(sd, level); + __sort_dimension__add(sd, list, level); return 0; } @@ -2305,7 +2317,8 @@ static int sort_dimension__add(const char *tok, struct perf_evlist *evlist, return -ESRCH; } -static int setup_sort_list(char *str, struct perf_evlist *evlist) +static int setup_sort_list(struct perf_hpp_list *list, char *str, + struct perf_evlist *evlist) { char *tmp, *tok; int ret = 0; @@ -2332,7 +2345,7 @@ static int setup_sort_list(char *str, struct perf_evlist *evlist) } if (*tok) { - ret = sort_dimension__add(tok, evlist, level); + ret = sort_dimension__add(list, tok, evlist, level); if (ret == -EINVAL) { error("Invalid --sort key: `%s'", tok); break; @@ -2480,7 +2493,7 @@ static int __setup_sorting(struct perf_evlist *evlist) } } - ret = setup_sort_list(str, evlist); + ret = setup_sort_list(&perf_hpp_list, str, evlist); free(str); return ret; @@ -2693,29 +2706,6 @@ out: return ret; } -static void evlist__set_hists_nr_sort_keys(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel; - - evlist__for_each(evlist, evsel) { - struct perf_hpp_fmt *fmt; - struct hists *hists = evsel__hists(evsel); - - hists->nr_sort_keys = perf_hpp_list.nr_sort_keys; - - /* - * If dynamic entries were used, it might add multiple - * entries to each evsel for a single field name. Set - * actual number of sort keys for each hists. - */ - perf_hpp_list__for_each_sort_list(&perf_hpp_list, fmt) { - if (perf_hpp__is_dynamic_entry(fmt) && - !perf_hpp__defined_dynamic_entry(fmt, hists)) - hists->nr_sort_keys--; - } - } -} - int setup_sorting(struct perf_evlist *evlist) { int err; @@ -2725,14 +2715,11 @@ int setup_sorting(struct perf_evlist *evlist) return err; if (parent_pattern != default_parent_pattern) { - err = sort_dimension__add("parent", evlist, -1); + err = sort_dimension__add(&perf_hpp_list, "parent", evlist, -1); if (err < 0) return err; } - if (evlist != NULL) - evlist__set_hists_nr_sort_keys(evlist); - reset_dimensions(); /* diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index ea1f722cffea..3f4e35998119 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -37,6 +37,7 @@ extern int sort__has_parent; extern int sort__has_sym; extern int sort__has_socket; extern int sort__has_thread; +extern int sort__has_comm; extern enum sort_mode sort__mode; extern struct sort_entry sort_comm; extern struct sort_entry sort_dso; @@ -129,7 +130,6 @@ struct hist_entry { void *raw_data; u32 raw_size; void *trace_output; - struct perf_hpp_fmt *fmt; struct perf_hpp_list *hpp_list; struct hist_entry *parent_he; union { |
