summaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/annotate.h2
-rw-r--r--tools/perf/util/build-id.h2
-rw-r--r--tools/perf/util/callchain.c78
-rw-r--r--tools/perf/util/callchain.h8
-rw-r--r--tools/perf/util/config.c4
-rw-r--r--tools/perf/util/cpumap.c160
-rw-r--r--tools/perf/util/cpumap.h35
-rw-r--r--tools/perf/util/dso.h2
-rw-r--r--tools/perf/util/event.c2
-rw-r--r--tools/perf/util/event.h24
-rw-r--r--tools/perf/util/evsel.h9
-rw-r--r--tools/perf/util/header.h4
-rw-r--r--tools/perf/util/hist.c112
-rw-r--r--tools/perf/util/hist.h19
-rw-r--r--tools/perf/util/include/linux/bitmap.h3
-rw-r--r--tools/perf/util/include/linux/export.h6
-rw-r--r--tools/perf/util/include/linux/list.h1
-rw-r--r--tools/perf/util/include/linux/types.h29
-rw-r--r--tools/perf/util/machine.c11
-rw-r--r--tools/perf/util/map.c23
-rw-r--r--tools/perf/util/map.h14
-rw-r--r--tools/perf/util/parse-events.h3
-rw-r--r--tools/perf/util/parse-events.y14
-rw-r--r--tools/perf/util/perf_regs.h2
-rw-r--r--tools/perf/util/pmu.c6
-rw-r--r--tools/perf/util/pmu.h2
-rw-r--r--tools/perf/util/stat.h2
-rw-r--r--tools/perf/util/svghelper.c2
-rw-r--r--tools/perf/util/svghelper.h2
-rw-r--r--tools/perf/util/symbol.h4
-rw-r--r--tools/perf/util/thread.c52
-rw-r--r--tools/perf/util/thread.h3
-rw-r--r--tools/perf/util/top.h2
-rw-r--r--tools/perf/util/types.h24
-rw-r--r--tools/perf/util/unwind-libdw.c2
-rw-r--r--tools/perf/util/unwind.h2
-rw-r--r--tools/perf/util/util.c2
-rw-r--r--tools/perf/util/util.h2
-rw-r--r--tools/perf/util/values.h2
39 files changed, 550 insertions, 126 deletions
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 <stdbool.h>
#include <stdint.h>
-#include "types.h"
+#include <linux/types.h>
#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 <linux/types.h>
extern struct perf_tool build_id__mark_dso_hit_ops;
struct dso;
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..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,
@@ -157,4 +164,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 */
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/cpumap.c b/tools/perf/util/cpumap.c
index 7fe4994eeb63..c4e55b71010c 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/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_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 <stdio.h>
#include <stdbool.h>
+#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 */
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 <linux/types.h>
#include <linux/rbtree.h>
#include <stdbool.h>
-#include "types.h"
+#include <linux/types.h>
#include "map.h"
#include "build-id.h"
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/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/evsel.h b/tools/perf/util/evsel.h
index 0c9926cfb292..a52e9a5bb2d0 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -5,12 +5,12 @@
#include <stdbool.h>
#include <stddef.h>
#include <linux/perf_event.h>
-#include "types.h"
+#include <linux/types.h>
#include "xyarray.h"
#include "cgroup.h"
#include "hist.h"
#include "symbol.h"
-
+
struct perf_counts_values {
union {
struct {
@@ -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/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 <linux/perf_event.h>
#include <sys/types.h>
#include <stdbool.h>
-#include "types.h"
+#include <linux/bitmap.h>
+#include <linux/types.h>
#include "event.h"
-#include <linux/bitmap.h>
enum {
HEADER_RESERVED = 0, /* always cleared */
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index f38590d7561b..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);
}
}
}
@@ -317,15 +324,6 @@ 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)
-{
- if (!h->filtered) {
- hists__calc_col_len(hists, h);
- ++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)
@@ -391,7 +389,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:
@@ -631,6 +628,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)
@@ -674,8 +700,7 @@ 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__reset_stats(hists);
hists__reset_col_len(hists);
while (next) {
@@ -683,7 +708,10 @@ 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);
+
+ if (!n->filtered)
+ hists__calc_col_len(hists, n);
}
}
@@ -694,13 +722,13 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h
if (h->filtered)
return;
- ++hists->nr_entries;
- if (h->ms.unfolded)
- hists->nr_entries += h->nr_rows;
+ /* force fold unfiltered entry for simplicity */
+ h->ms.unfolded = false;
h->row_offset = 0;
- hists->stats.total_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__inc_filter_stats(hists, h);
hists__calc_col_len(hists, h);
}
@@ -721,8 +749,9 @@ void hists__filter_by_dso(struct hists *hists)
{
struct rb_node *nd;
- hists->nr_entries = hists->stats.total_period = 0;
- hists->stats.nr_events[PERF_RECORD_SAMPLE] = 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)) {
@@ -754,8 +783,9 @@ void hists__filter_by_thread(struct hists *hists)
{
struct rb_node *nd;
- hists->nr_entries = hists->stats.total_period = 0;
- hists->stats.nr_events[PERF_RECORD_SAMPLE] = 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)) {
@@ -785,8 +815,9 @@ void hists__filter_by_symbol(struct hists *hists)
{
struct rb_node *nd;
- hists->nr_entries = hists->stats.total_period = 0;
- hists->stats.nr_events[PERF_RECORD_SAMPLE] = 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)) {
@@ -847,7 +878,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:
@@ -931,3 +962,30 @@ 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;
+}
+
+int parse_filter_percentage(const struct option *opt __maybe_unused,
+ const char *arg, int unset __maybe_unused)
+{
+ if (!strcmp(arg, "relative"))
+ symbol_conf.filter_relative = true;
+ else if (!strcmp(arg, "absolute"))
+ symbol_conf.filter_relative = false;
+ else
+ return -1;
+
+ return 0;
+}
+
+int perf_hist_config(const char *var, const char *value)
+{
+ if (!strcmp(var, "hist.percentage"))
+ return parse_filter_percentage(NULL, value, 0);
+
+ return 0;
+}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 1f1f513dfe7f..38c3e874c164 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;
@@ -112,7 +115,9 @@ 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);
-void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h);
+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);
size_t events_stats__fprintf(struct events_stats *stats, FILE *fp);
@@ -124,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);
@@ -250,4 +261,10 @@ 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);
+int perf_hist_config(const char *var, const char *value);
+
#endif /* __PERF_HIST_H */
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 <string.h>
#include <linux/bitops.h>
+#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/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/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 <linux/kernel.h>
+#include <linux/types.h>
#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 <asm/types.h>
-
-#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/machine.c b/tools/perf/util/machine.c
index 27c2a5efe450..7409ac8de51c 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/map.c b/tools/perf/util/map.c
index 39cd2d0faff6..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)
@@ -358,6 +359,28 @@ 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__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 f00f058afb3b..ae2d45110588 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -6,7 +6,7 @@
#include <linux/rbtree.h>
#include <stdio.h>
#include <stdbool.h>
-#include "types.h"
+#include <linux/types.h>
enum map_type {
MAP__FUNCTION = 0,
@@ -59,8 +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/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 <linux/list.h>
#include <stdbool.h>
-#include "types.h"
+#include <linux/types.h>
#include <linux/perf_event.h>
-#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 4eb67ec333f1..0bc87ba46bf3 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -9,7 +9,7 @@
#include <linux/compiler.h>
#include <linux/list.h>
-#include "types.h"
+#include <linux/types.h>
#include "util.h"
#include "parse-events.h"
#include "parse-events-bison.h"
@@ -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;
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 <linux/types.h>
#include "event.h"
#ifdef HAVE_PERF_REGS_SUPPORT
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;
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 <linux/bitops.h>
+#include <linux/bitmap.h>
#include <linux/perf_event.h>
#include <stdbool.h>
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 <linux/types.h>
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 <stdlib.h>
#include <unistd.h>
#include <string.h>
-#include <linux/bitops.h>
+#include <linux/bitmap.h>
#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 <linux/types.h>
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/symbol.h b/tools/perf/util/symbol.h
index 501e4e722e8e..33ede53fa6b9 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -12,6 +12,7 @@
#include <byteswap.h>
#include <libgen.h>
#include "build-id.h"
+#include "event.h"
#ifdef HAVE_LIBELF_SUPPORT
#include <libelf.h>
@@ -115,7 +116,8 @@ struct symbol_conf {
annotate_asm_raw,
annotate_src,
event_group,
- demangle;
+ demangle,
+ filter_relative;
const char *vmlinux_name,
*kallsyms_name,
*source_prefix,
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 3ce0498bdae6..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,7 +31,6 @@ 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->pid_ = pid;
thread->tid = tid;
thread->ppid = -1;
@@ -45,7 +60,8 @@ void thread__delete(struct thread *thread)
{
struct comm *comm, *tmp;
- map_groups__exit(&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);
comm__free(comm);
@@ -111,18 +127,35 @@ 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);
+}
+
+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);
@@ -134,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 9b29f085aede..3c0c2724f82c 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;
@@ -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)
{
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 <linux/types.h>
#include <stddef.h>
#include <stdbool.h>
#include <termios.h>
diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h
deleted file mode 100644
index c51fa6b70a28..000000000000
--- a/tools/perf/util/types.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef __PERF_TYPES_H
-#define __PERF_TYPES_H
-
-#include <stdint.h>
-
-/*
- * 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;
-
-union u64_swap {
- u64 val64;
- u32 val32[2];
-};
-
-#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 <linux/types.h>
#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 <linux/types.h>
#include "event.h"
#include "symbol.h"
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;
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 <sys/ioctl.h>
#include <inttypes.h>
#include <linux/magic.h>
-#include "types.h"
+#include <linux/types.h>
#include <sys/ttydefaults.h>
#include <api/fs/debugfs.h>
#include <termios.h>
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 <linux/types.h>
struct perf_read_values {
int threads;