summaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2016-03-11 09:40:25 +0100
committerIngo Molnar <mingo@kernel.org>2016-03-11 09:40:25 +0100
commitced30bc9129777d715057d06fc8dbdfd3b81e94d (patch)
treeab682f7130a4ad1ce0d4fcb0f77f91db391bf34a /tools/perf/util
parent3a99e6db539e53cc9c79282e80f8362b0cb96ac8 (diff)
parent206cab651d07563d766c7f4cb73f858c5df3dec5 (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/Build3
-rw-r--r--tools/perf/util/evsel.h6
-rw-r--r--tools/perf/util/hist.c144
-rw-r--r--tools/perf/util/hist.h6
-rw-r--r--tools/perf/util/pmu.c4
-rw-r--r--tools/perf/util/sort.c147
-rw-r--r--tools/perf/util/sort.h2
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 {