summaryrefslogtreecommitdiffstats
path: root/kernel/tools/perf/util/evlist.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/tools/perf/util/evlist.c')
-rw-r--r--kernel/tools/perf/util/evlist.c289
1 files changed, 237 insertions, 52 deletions
diff --git a/kernel/tools/perf/util/evlist.c b/kernel/tools/perf/util/evlist.c
index 080be93ee..d1392194a 100644
--- a/kernel/tools/perf/util/evlist.c
+++ b/kernel/tools/perf/util/evlist.c
@@ -25,6 +25,7 @@
#include <linux/bitops.h>
#include <linux/hash.h>
#include <linux/log2.h>
+#include <linux/err.h>
static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx);
static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx);
@@ -98,6 +99,7 @@ static void perf_evlist__purge(struct perf_evlist *evlist)
evlist__for_each_safe(evlist, n, pos) {
list_del_init(&pos->node);
+ pos->evlist = NULL;
perf_evsel__delete(pos);
}
@@ -114,8 +116,8 @@ void perf_evlist__delete(struct perf_evlist *evlist)
{
perf_evlist__munmap(evlist);
perf_evlist__close(evlist);
- cpu_map__delete(evlist->cpus);
- thread_map__delete(evlist->threads);
+ cpu_map__put(evlist->cpus);
+ thread_map__put(evlist->threads);
evlist->cpus = NULL;
evlist->threads = NULL;
perf_evlist__purge(evlist);
@@ -123,26 +125,62 @@ void perf_evlist__delete(struct perf_evlist *evlist)
free(evlist);
}
+static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
+ struct perf_evsel *evsel)
+{
+ /*
+ * We already have cpus for evsel (via PMU sysfs) so
+ * keep it, if there's no target cpu list defined.
+ */
+ if (!evsel->own_cpus || evlist->has_user_cpus) {
+ cpu_map__put(evsel->cpus);
+ evsel->cpus = cpu_map__get(evlist->cpus);
+ } else if (evsel->cpus != evsel->own_cpus) {
+ cpu_map__put(evsel->cpus);
+ evsel->cpus = cpu_map__get(evsel->own_cpus);
+ }
+
+ thread_map__put(evsel->threads);
+ evsel->threads = thread_map__get(evlist->threads);
+}
+
+static void perf_evlist__propagate_maps(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel;
+
+ evlist__for_each(evlist, evsel)
+ __perf_evlist__propagate_maps(evlist, evsel);
+}
+
void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
{
+ entry->evlist = evlist;
list_add_tail(&entry->node, &evlist->entries);
entry->idx = evlist->nr_entries;
entry->tracking = !entry->idx;
if (!evlist->nr_entries++)
perf_evlist__set_id_pos(evlist);
+
+ __perf_evlist__propagate_maps(evlist, entry);
+}
+
+void perf_evlist__remove(struct perf_evlist *evlist, struct perf_evsel *evsel)
+{
+ evsel->evlist = NULL;
+ list_del_init(&evsel->node);
+ evlist->nr_entries -= 1;
}
void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
- struct list_head *list,
- int nr_entries)
+ struct list_head *list)
{
- bool set_id_pos = !evlist->nr_entries;
+ struct perf_evsel *evsel, *temp;
- list_splice_tail(list, &evlist->entries);
- evlist->nr_entries += nr_entries;
- if (set_id_pos)
- perf_evlist__set_id_pos(evlist);
+ __evlist__for_each_safe(list, temp, evsel) {
+ list_del_init(&evsel->node);
+ perf_evlist__add(evlist, evsel);
+ }
}
void __perf_evlist__set_leader(struct list_head *list)
@@ -167,6 +205,20 @@ void perf_evlist__set_leader(struct perf_evlist *evlist)
}
}
+void perf_event_attr__set_max_precise_ip(struct perf_event_attr *attr)
+{
+ attr->precise_ip = 3;
+
+ while (attr->precise_ip != 0) {
+ int fd = sys_perf_event_open(attr, 0, -1, -1, 0);
+ if (fd != -1) {
+ close(fd);
+ break;
+ }
+ --attr->precise_ip;
+ }
+}
+
int perf_evlist__add_default(struct perf_evlist *evlist)
{
struct perf_event_attr attr = {
@@ -177,13 +229,15 @@ int perf_evlist__add_default(struct perf_evlist *evlist)
event_attr_init(&attr);
+ perf_event_attr__set_max_precise_ip(&attr);
+
evsel = perf_evsel__new(&attr);
if (evsel == NULL)
goto error;
- /* use strdup() because free(evsel) assumes name is allocated */
- evsel->name = strdup("cycles");
- if (!evsel->name)
+ /* use asprintf() because free(evsel) assumes name is allocated */
+ if (asprintf(&evsel->name, "cycles%.*s",
+ attr.precise_ip ? attr.precise_ip + 1 : 0, ":ppp") < 0)
goto error_free;
perf_evlist__add(evlist, evsel);
@@ -208,7 +262,7 @@ static int perf_evlist__add_attrs(struct perf_evlist *evlist,
list_add_tail(&evsel->node, &head);
}
- perf_evlist__splice_list_tail(evlist, &head, nr_attrs);
+ perf_evlist__splice_list_tail(evlist, &head);
return 0;
@@ -263,7 +317,7 @@ int perf_evlist__add_newtp(struct perf_evlist *evlist,
{
struct perf_evsel *evsel = perf_evsel__newtp(sys, name);
- if (evsel == NULL)
+ if (IS_ERR(evsel))
return -1;
evsel->handler = handler;
@@ -297,6 +351,8 @@ void perf_evlist__disable(struct perf_evlist *evlist)
PERF_EVENT_IOC_DISABLE, 0);
}
}
+
+ evlist->enabled = false;
}
void perf_evlist__enable(struct perf_evlist *evlist)
@@ -316,6 +372,13 @@ void perf_evlist__enable(struct perf_evlist *evlist)
PERF_EVENT_IOC_ENABLE, 0);
}
}
+
+ evlist->enabled = true;
+}
+
+void perf_evlist__toggle_enable(struct perf_evlist *evlist)
+{
+ (evlist->enabled ? perf_evlist__disable : perf_evlist__enable)(evlist);
}
int perf_evlist__disable_event(struct perf_evlist *evlist,
@@ -539,7 +602,7 @@ static void perf_evlist__set_sid_idx(struct perf_evlist *evlist,
else
sid->cpu = -1;
if (!evsel->system_wide && evlist->threads && thread >= 0)
- sid->tid = evlist->threads->map[thread];
+ sid->tid = thread_map__pid(evlist->threads, thread);
else
sid->tid = -1;
}
@@ -564,7 +627,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
{
struct perf_sample_id *sid;
- if (evlist->nr_entries == 1)
+ if (evlist->nr_entries == 1 || !id)
return perf_evlist__first(evlist);
sid = perf_evlist__id2sid(evlist, id);
@@ -577,6 +640,21 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
return NULL;
}
+struct perf_evsel *perf_evlist__id2evsel_strict(struct perf_evlist *evlist,
+ u64 id)
+{
+ struct perf_sample_id *sid;
+
+ if (!id)
+ return NULL;
+
+ sid = perf_evlist__id2sid(evlist, id);
+ if (sid)
+ return sid->evsel;
+
+ return NULL;
+}
+
static int perf_evlist__event2id(struct perf_evlist *evlist,
union perf_event *event, u64 *id)
{
@@ -634,11 +712,18 @@ static struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist,
union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
{
struct perf_mmap *md = &evlist->mmap[idx];
- u64 head = perf_mmap__read_head(md);
+ u64 head;
u64 old = md->prev;
unsigned char *data = md->base + page_size;
union perf_event *event = NULL;
+ /*
+ * Check if event was unmapped due to a POLLHUP/POLLERR.
+ */
+ if (!atomic_read(&md->refcnt))
+ return NULL;
+
+ head = perf_mmap__read_head(md);
if (evlist->overwrite) {
/*
* If we're further behind than half the buffer, there's a chance
@@ -695,19 +780,19 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
static bool perf_mmap__empty(struct perf_mmap *md)
{
- return perf_mmap__read_head(md) == md->prev;
+ return perf_mmap__read_head(md) == md->prev && !md->auxtrace_mmap.base;
}
static void perf_evlist__mmap_get(struct perf_evlist *evlist, int idx)
{
- ++evlist->mmap[idx].refcnt;
+ atomic_inc(&evlist->mmap[idx].refcnt);
}
static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx)
{
- BUG_ON(evlist->mmap[idx].refcnt == 0);
+ BUG_ON(atomic_read(&evlist->mmap[idx].refcnt) == 0);
- if (--evlist->mmap[idx].refcnt == 0)
+ if (atomic_dec_and_test(&evlist->mmap[idx].refcnt))
__perf_evlist__munmap(evlist, idx);
}
@@ -721,17 +806,46 @@ void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx)
perf_mmap__write_tail(md, old);
}
- if (md->refcnt == 1 && perf_mmap__empty(md))
+ if (atomic_read(&md->refcnt) == 1 && perf_mmap__empty(md))
perf_evlist__mmap_put(evlist, idx);
}
+int __weak auxtrace_mmap__mmap(struct auxtrace_mmap *mm __maybe_unused,
+ struct auxtrace_mmap_params *mp __maybe_unused,
+ void *userpg __maybe_unused,
+ int fd __maybe_unused)
+{
+ return 0;
+}
+
+void __weak auxtrace_mmap__munmap(struct auxtrace_mmap *mm __maybe_unused)
+{
+}
+
+void __weak auxtrace_mmap_params__init(
+ struct auxtrace_mmap_params *mp __maybe_unused,
+ off_t auxtrace_offset __maybe_unused,
+ unsigned int auxtrace_pages __maybe_unused,
+ bool auxtrace_overwrite __maybe_unused)
+{
+}
+
+void __weak auxtrace_mmap_params__set_idx(
+ struct auxtrace_mmap_params *mp __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused,
+ int idx __maybe_unused,
+ bool per_cpu __maybe_unused)
+{
+}
+
static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)
{
if (evlist->mmap[idx].base != NULL) {
munmap(evlist->mmap[idx].base, evlist->mmap_len);
evlist->mmap[idx].base = NULL;
- evlist->mmap[idx].refcnt = 0;
+ atomic_set(&evlist->mmap[idx].refcnt, 0);
}
+ auxtrace_mmap__munmap(&evlist->mmap[idx].auxtrace_mmap);
}
void perf_evlist__munmap(struct perf_evlist *evlist)
@@ -759,6 +873,7 @@ static int perf_evlist__alloc_mmap(struct perf_evlist *evlist)
struct mmap_params {
int prot;
int mask;
+ struct auxtrace_mmap_params auxtrace_mp;
};
static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx,
@@ -777,7 +892,7 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx,
* evlist layer can't just drop it when filtering events in
* perf_evlist__filter_pollfd().
*/
- evlist->mmap[idx].refcnt = 2;
+ atomic_set(&evlist->mmap[idx].refcnt, 2);
evlist->mmap[idx].prev = 0;
evlist->mmap[idx].mask = mp->mask;
evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot,
@@ -789,6 +904,10 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx,
return -1;
}
+ if (auxtrace_mmap__mmap(&evlist->mmap[idx].auxtrace_mmap,
+ &mp->auxtrace_mp, evlist->mmap[idx].base, fd))
+ return -1;
+
return 0;
}
@@ -853,6 +972,9 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist,
for (cpu = 0; cpu < nr_cpus; cpu++) {
int output = -1;
+ auxtrace_mmap_params__set_idx(&mp->auxtrace_mp, evlist, cpu,
+ true);
+
for (thread = 0; thread < nr_threads; thread++) {
if (perf_evlist__mmap_per_evsel(evlist, cpu, mp, cpu,
thread, &output))
@@ -878,6 +1000,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist,
for (thread = 0; thread < nr_threads; thread++) {
int output = -1;
+ auxtrace_mmap_params__set_idx(&mp->auxtrace_mp, evlist, thread,
+ false);
+
if (perf_evlist__mmap_per_evsel(evlist, thread, mp, 0, thread,
&output))
goto out_unmap;
@@ -960,10 +1085,8 @@ static long parse_pages_arg(const char *str, unsigned long min,
return pages;
}
-int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,
- int unset __maybe_unused)
+int __perf_evlist__parse_mmap_pages(unsigned int *mmap_pages, const char *str)
{
- unsigned int *mmap_pages = opt->value;
unsigned long max = UINT_MAX;
long pages;
@@ -980,20 +1103,32 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,
return 0;
}
+int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,
+ int unset __maybe_unused)
+{
+ return __perf_evlist__parse_mmap_pages(opt->value, str);
+}
+
/**
- * perf_evlist__mmap - Create mmaps to receive events.
+ * perf_evlist__mmap_ex - Create mmaps to receive events.
* @evlist: list of events
* @pages: map length in pages
* @overwrite: overwrite older events?
+ * @auxtrace_pages - auxtrace map length in pages
+ * @auxtrace_overwrite - overwrite older auxtrace data?
*
* If @overwrite is %false the user needs to signal event consumption using
* perf_mmap__write_tail(). Using perf_evlist__mmap_read() does this
* automatically.
*
+ * Similarly, if @auxtrace_overwrite is %false the user needs to signal data
+ * consumption using auxtrace_mmap__write_tail().
+ *
* Return: %0 on success, negative error code otherwise.
*/
-int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
- bool overwrite)
+int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
+ bool overwrite, unsigned int auxtrace_pages,
+ bool auxtrace_overwrite)
{
struct perf_evsel *evsel;
const struct cpu_map *cpus = evlist->cpus;
@@ -1013,6 +1148,9 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
pr_debug("mmap size %zuB\n", evlist->mmap_len);
mp.mask = evlist->mmap_len - page_size - 1;
+ auxtrace_mmap_params__init(&mp.auxtrace_mp, evlist->mmap_len,
+ auxtrace_pages, auxtrace_overwrite);
+
evlist__for_each(evlist, evsel) {
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
evsel->sample_id == NULL &&
@@ -1026,30 +1164,64 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
return perf_evlist__mmap_per_cpu(evlist, &mp);
}
+int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
+ bool overwrite)
+{
+ return perf_evlist__mmap_ex(evlist, pages, overwrite, 0, false);
+}
+
int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
{
- evlist->threads = thread_map__new_str(target->pid, target->tid,
- target->uid);
+ struct cpu_map *cpus;
+ struct thread_map *threads;
+
+ threads = thread_map__new_str(target->pid, target->tid, target->uid);
- if (evlist->threads == NULL)
+ if (!threads)
return -1;
if (target__uses_dummy_map(target))
- evlist->cpus = cpu_map__dummy_new();
+ cpus = cpu_map__dummy_new();
else
- evlist->cpus = cpu_map__new(target->cpu_list);
+ cpus = cpu_map__new(target->cpu_list);
- if (evlist->cpus == NULL)
+ if (!cpus)
goto out_delete_threads;
+ evlist->has_user_cpus = !!target->cpu_list;
+
+ perf_evlist__set_maps(evlist, cpus, threads);
+
return 0;
out_delete_threads:
- thread_map__delete(evlist->threads);
- evlist->threads = NULL;
+ thread_map__put(threads);
return -1;
}
+void perf_evlist__set_maps(struct perf_evlist *evlist, struct cpu_map *cpus,
+ struct thread_map *threads)
+{
+ /*
+ * Allow for the possibility that one or another of the maps isn't being
+ * changed i.e. don't put it. Note we are assuming the maps that are
+ * being applied are brand new and evlist is taking ownership of the
+ * original reference count of 1. If that is not the case it is up to
+ * the caller to increase the reference count.
+ */
+ if (cpus != evlist->cpus) {
+ cpu_map__put(evlist->cpus);
+ evlist->cpus = cpus;
+ }
+
+ if (threads != evlist->threads) {
+ thread_map__put(evlist->threads);
+ evlist->threads = threads;
+ }
+
+ perf_evlist__propagate_maps(evlist);
+}
+
int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **err_evsel)
{
struct perf_evsel *evsel;
@@ -1061,7 +1233,11 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **e
if (evsel->filter == NULL)
continue;
- err = perf_evsel__set_filter(evsel, ncpus, nthreads, evsel->filter);
+ /*
+ * filters only work for tracepoint event, which doesn't have cpu limit.
+ * So evlist and evsel should always be same.
+ */
+ err = perf_evsel__apply_filter(evsel, ncpus, nthreads, evsel->filter);
if (err) {
*err_evsel = evsel;
break;
@@ -1075,11 +1251,9 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter)
{
struct perf_evsel *evsel;
int err = 0;
- const int ncpus = cpu_map__nr(evlist->cpus),
- nthreads = thread_map__nr(evlist->threads);
evlist__for_each(evlist, evsel) {
- err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter);
+ err = perf_evsel__set_filter(evsel, filter);
if (err)
break;
}
@@ -1157,6 +1331,16 @@ u64 perf_evlist__combined_sample_type(struct perf_evlist *evlist)
return __perf_evlist__combined_sample_type(evlist);
}
+u64 perf_evlist__combined_branch_type(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel;
+ u64 branch_type = 0;
+
+ evlist__for_each(evlist, evsel)
+ branch_type |= evsel->attr.branch_sample_type;
+ return branch_type;
+}
+
bool perf_evlist__valid_read_format(struct perf_evlist *evlist)
{
struct perf_evsel *first = perf_evlist__first(evlist), *pos = first;
@@ -1255,6 +1439,8 @@ void perf_evlist__close(struct perf_evlist *evlist)
static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist)
{
+ struct cpu_map *cpus;
+ struct thread_map *threads;
int err = -ENOMEM;
/*
@@ -1266,20 +1452,19 @@ static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist)
* error, and we may not want to do that fallback to a
* default cpu identity map :-\
*/
- evlist->cpus = cpu_map__new(NULL);
- if (evlist->cpus == NULL)
+ cpus = cpu_map__new(NULL);
+ if (!cpus)
goto out;
- evlist->threads = thread_map__new_dummy();
- if (evlist->threads == NULL)
- goto out_free_cpus;
+ threads = thread_map__new_dummy();
+ if (!threads)
+ goto out_put;
- err = 0;
+ perf_evlist__set_maps(evlist, cpus, threads);
out:
return err;
-out_free_cpus:
- cpu_map__delete(evlist->cpus);
- evlist->cpus = NULL;
+out_put:
+ cpu_map__put(cpus);
goto out;
}
@@ -1400,7 +1585,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar
__func__, __LINE__);
goto out_close_pipes;
}
- evlist->threads->map[0] = evlist->workload.pid;
+ thread_map__set_pid(evlist->threads, 0, evlist->workload.pid);
}
close(child_ready_pipe[1]);