From 69aad6f1ee69546dea8535ab8f3da9f445d57328 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 3 Jan 2011 16:39:04 -0200 Subject: perf tools: Introduce event selectors Out of ad-hoc code and global arrays with hard coded sizes. This is the first step on having a library that will be first used on regression tests in the 'perf test' tool. [acme@felicio linux]$ size /tmp/perf.before text data bss dec hex filename 1273776 97384 5104416 6475576 62cf38 /tmp/perf.before [acme@felicio linux]$ size /tmp/perf.new text data bss dec hex filename 1275422 97416 1392416 2765254 2a31c6 /tmp/perf.new Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tools/perf/util/evsel.c (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c new file mode 100644 index 000000000000..6539ec912c70 --- /dev/null +++ b/tools/perf/util/evsel.c @@ -0,0 +1,35 @@ +#include "evsel.h" +#include "util.h" + +struct perf_evsel *perf_evsel__new(u32 type, u64 config, int idx) +{ + struct perf_evsel *evsel = zalloc(sizeof(*evsel)); + + if (evsel != NULL) { + evsel->idx = idx; + evsel->attr.type = type; + evsel->attr.config = config; + INIT_LIST_HEAD(&evsel->node); + } + + return evsel; +} + +int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) +{ + evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); + return evsel->fd != NULL ? 0 : -ENOMEM; +} + +void perf_evsel__free_fd(struct perf_evsel *evsel) +{ + xyarray__delete(evsel->fd); + evsel->fd = NULL; +} + +void perf_evsel__delete(struct perf_evsel *evsel) +{ + assert(list_empty(&evsel->node)); + xyarray__delete(evsel->fd); + free(evsel); +} -- cgit v1.2.2 From c52b12ed2511e6c031a0295fd903ea72b93701fb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 3 Jan 2011 17:45:52 -0200 Subject: perf evsel: Steal the counter reading routines from stat Making them hopefully generic enough to be used in 'perf test', well see. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 6539ec912c70..3f5de5196231 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1,6 +1,8 @@ #include "evsel.h" #include "util.h" +#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) + struct perf_evsel *perf_evsel__new(u32 type, u64 config, int idx) { struct perf_evsel *evsel = zalloc(sizeof(*evsel)); @@ -21,15 +23,101 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) return evsel->fd != NULL ? 0 : -ENOMEM; } +int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) +{ + evsel->counts = zalloc((sizeof(*evsel->counts) + + (ncpus * sizeof(struct perf_counts_values)))); + return evsel->counts != NULL ? 0 : -ENOMEM; +} + void perf_evsel__free_fd(struct perf_evsel *evsel) { xyarray__delete(evsel->fd); evsel->fd = NULL; } +void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) +{ + int cpu, thread; + + for (cpu = 0; cpu < ncpus; cpu++) + for (thread = 0; thread < nthreads; ++thread) { + close(FD(evsel, cpu, thread)); + FD(evsel, cpu, thread) = -1; + } +} + void perf_evsel__delete(struct perf_evsel *evsel) { assert(list_empty(&evsel->node)); xyarray__delete(evsel->fd); free(evsel); } + +int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, + int cpu, int thread, bool scale) +{ + struct perf_counts_values count; + size_t nv = scale ? 3 : 1; + + if (FD(evsel, cpu, thread) < 0) + return -EINVAL; + + if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) + return -errno; + + if (scale) { + if (count.run == 0) + count.val = 0; + else if (count.run < count.ena) + count.val = (u64)((double)count.val * count.ena / count.run + 0.5); + } else + count.ena = count.run = 0; + + evsel->counts->cpu[cpu] = count; + return 0; +} + +int __perf_evsel__read(struct perf_evsel *evsel, + int ncpus, int nthreads, bool scale) +{ + size_t nv = scale ? 3 : 1; + int cpu, thread; + struct perf_counts_values *aggr = &evsel->counts->aggr, count; + + aggr->val = 0; + + for (cpu = 0; cpu < ncpus; cpu++) { + for (thread = 0; thread < nthreads; thread++) { + if (FD(evsel, cpu, thread) < 0) + continue; + + if (readn(FD(evsel, cpu, thread), + &count, nv * sizeof(u64)) < 0) + return -errno; + + aggr->val += count.val; + if (scale) { + aggr->ena += count.ena; + aggr->run += count.run; + } + } + } + + evsel->counts->scaled = 0; + if (scale) { + if (aggr->run == 0) { + evsel->counts->scaled = -1; + aggr->val = 0; + return 0; + } + + if (aggr->run < aggr->ena) { + evsel->counts->scaled = 1; + aggr->val = (u64)((double)aggr->val * aggr->ena / aggr->run + 0.5); + } + } else + aggr->ena = aggr->run = 0; + + return 0; +} -- cgit v1.2.2 From 48290609c0d265f5dac0fca6fd4e3c5732542f67 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 3 Jan 2011 17:48:12 -0200 Subject: perf evsel: Introduce per cpu and per thread open helpers Abstracting away the loops needed to create the various event fd handlers. The users have to pass a confiruged perf->evsel.attr field, which is already usable after perf_evsel__new (constructor) time, using defaults. Comes out of the ad-hoc routines in builtin-stat, that now uses it. Fixed a small silly bug where we were die()ing before killing our children, dysfunctional family this one 8-) Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 3f5de5196231..e62cc5e050ab 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1,4 +1,5 @@ #include "evsel.h" +#include "../perf.h" #include "util.h" #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) @@ -121,3 +122,54 @@ int __perf_evsel__read(struct perf_evsel *evsel, return 0; } + +int perf_evsel__open_per_cpu(struct perf_evsel *evsel, int ncpus, int *cpu_map) +{ + int cpu; + + for (cpu = 0; cpu < ncpus; cpu++) { + FD(evsel, cpu, 0) = sys_perf_event_open(&evsel->attr, -1, + cpu_map[cpu], -1, 0); + if (FD(evsel, cpu, 0) < 0) + goto out_close; + } + + return 0; + +out_close: + while (--cpu >= 0) { + close(FD(evsel, cpu, 0)); + FD(evsel, cpu, 0) = -1; + } + return -1; +} + +int perf_evsel__open_per_thread(struct perf_evsel *evsel, int nthreads, int *thread_map) +{ + int thread; + + for (thread = 0; thread < nthreads; thread++) { + FD(evsel, 0, thread) = sys_perf_event_open(&evsel->attr, + thread_map[thread], -1, -1, 0); + if (FD(evsel, 0, thread) < 0) + goto out_close; + } + + return 0; + +out_close: + while (--thread >= 0) { + close(FD(evsel, 0, thread)); + FD(evsel, 0, thread) = -1; + } + return -1; +} + +int perf_evsel__open(struct perf_evsel *evsel, int ncpus, int nthreads, + int *cpu_map, int *thread_map) +{ + if (nthreads < 0) + return perf_evsel__open_per_cpu(evsel, ncpus, cpu_map); + + return perf_evsel__open_per_thread(evsel, nthreads, thread_map); +} -- cgit v1.2.2 From 86bd5e8603b00b06189328c6d7034d2dc434d6bb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 3 Jan 2011 23:09:46 -0200 Subject: perf evsel: Use {cpu,thread}_map to shorten list of parameters Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index e62cc5e050ab..e44be528c09f 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1,6 +1,8 @@ #include "evsel.h" #include "../perf.h" #include "util.h" +#include "cpumap.h" +#include "thread.h" #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) @@ -123,13 +125,13 @@ int __perf_evsel__read(struct perf_evsel *evsel, return 0; } -int perf_evsel__open_per_cpu(struct perf_evsel *evsel, int ncpus, int *cpu_map) +int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) { int cpu; - for (cpu = 0; cpu < ncpus; cpu++) { + for (cpu = 0; cpu < cpus->nr; cpu++) { FD(evsel, cpu, 0) = sys_perf_event_open(&evsel->attr, -1, - cpu_map[cpu], -1, 0); + cpus->map[cpu], -1, 0); if (FD(evsel, cpu, 0) < 0) goto out_close; } @@ -144,13 +146,13 @@ out_close: return -1; } -int perf_evsel__open_per_thread(struct perf_evsel *evsel, int nthreads, int *thread_map) +int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) { int thread; - for (thread = 0; thread < nthreads; thread++) { + for (thread = 0; thread < threads->nr; thread++) { FD(evsel, 0, thread) = sys_perf_event_open(&evsel->attr, - thread_map[thread], -1, -1, 0); + threads->map[thread], -1, -1, 0); if (FD(evsel, 0, thread) < 0) goto out_close; } @@ -165,11 +167,11 @@ out_close: return -1; } -int perf_evsel__open(struct perf_evsel *evsel, int ncpus, int nthreads, - int *cpu_map, int *thread_map) +int perf_evsel__open(struct perf_evsel *evsel, + struct cpu_map *cpus, struct thread_map *threads) { - if (nthreads < 0) - return perf_evsel__open_per_cpu(evsel, ncpus, cpu_map); + if (threads == NULL) + return perf_evsel__open_per_cpu(evsel, cpus); - return perf_evsel__open_per_thread(evsel, nthreads, thread_map); + return perf_evsel__open_per_thread(evsel, threads); } -- cgit v1.2.2 From 4eed11d5e24540dc133003b6e8f904cb747ac4bb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 4 Jan 2011 00:13:17 -0200 Subject: perf evsel: Auto allocate resources needed for some methods While writing the first user of the routines created from the ad-hoc routines in the existing builtins I noticed that the resulting set of calls was too long, reduce it by doing some best effort allocations. Tools that need to operate on multiple threads and cpus should pre-allocate enough resources by explicitely calling the perf_evsel__alloc_{fd,counters} methods. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index e44be528c09f..c95267e63c5b 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -66,6 +66,9 @@ int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, if (FD(evsel, cpu, thread) < 0) return -EINVAL; + if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0) + return -ENOMEM; + if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) return -errno; @@ -129,6 +132,9 @@ int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) { int cpu; + if (evsel->fd == NULL && perf_evsel__alloc_fd(evsel, cpus->nr, 1) < 0) + return -1; + for (cpu = 0; cpu < cpus->nr; cpu++) { FD(evsel, cpu, 0) = sys_perf_event_open(&evsel->attr, -1, cpus->map[cpu], -1, 0); @@ -150,6 +156,9 @@ int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *thr { int thread; + if (evsel->fd == NULL && perf_evsel__alloc_fd(evsel, 1, threads->nr)) + return -1; + for (thread = 0; thread < threads->nr; thread++) { FD(evsel, 0, thread) = sys_perf_event_open(&evsel->attr, threads->map[thread], -1, -1, 0); -- cgit v1.2.2 From 23a2f3ab46596d9fd0b0e592d2101bea90970594 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Fri, 7 Jan 2011 11:11:09 +0800 Subject: perf tools: Pass whole attr to event selectors Since commit 69aad6f1(perf tools: Introduce event selectors), only perf_event_attr::type and ::config are passed to event selector, which makes perf tool not work correctly. For example, PEBS does not work because perf_event_attr::precise_ip is not passed to the syscall. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra LKML-Reference: <1294369869.20563.19.camel@minggr.sh.intel.com> Signed-off-by: Lin Ming Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index c95267e63c5b..1a5591d7a245 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -6,14 +6,13 @@ #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) -struct perf_evsel *perf_evsel__new(u32 type, u64 config, int idx) +struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) { struct perf_evsel *evsel = zalloc(sizeof(*evsel)); if (evsel != NULL) { evsel->idx = idx; - evsel->attr.type = type; - evsel->attr.config = config; + evsel->attr = *attr; INIT_LIST_HEAD(&evsel->node); } -- cgit v1.2.2 From 0252208eb52f6fe8731a47804eddc7ba93f60a87 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 4 Jan 2011 11:55:27 -0200 Subject: perf evsel: Support perf_evsel__open(cpus > 1 && threads > 1) And a test for it: [acme@felicio linux]$ perf test 1: vmlinux symtab matches kallsyms: Ok 2: detect open syscall event: Ok 3: detect open syscall event on all cpus: Ok [acme@felicio linux]$ Translating C the test does: 1. generates different number of open syscalls on each CPU by using sched_setaffinity 2. Verifies that the expected number of events is generated on each CPU It works as expected. LKML-Reference: Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 82 +++++++++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 33 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 1a5591d7a245..f5cfed60af98 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -127,59 +127,75 @@ int __perf_evsel__read(struct perf_evsel *evsel, return 0; } -int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) +static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, + struct thread_map *threads) { - int cpu; + int cpu, thread; - if (evsel->fd == NULL && perf_evsel__alloc_fd(evsel, cpus->nr, 1) < 0) + if (evsel->fd == NULL && + perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) return -1; for (cpu = 0; cpu < cpus->nr; cpu++) { - FD(evsel, cpu, 0) = sys_perf_event_open(&evsel->attr, -1, - cpus->map[cpu], -1, 0); - if (FD(evsel, cpu, 0) < 0) - goto out_close; + for (thread = 0; thread < threads->nr; thread++) { + FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, + threads->map[thread], + cpus->map[cpu], -1, 0); + if (FD(evsel, cpu, thread) < 0) + goto out_close; + } } return 0; out_close: - while (--cpu >= 0) { - close(FD(evsel, cpu, 0)); - FD(evsel, cpu, 0) = -1; - } + do { + while (--thread >= 0) { + close(FD(evsel, cpu, thread)); + FD(evsel, cpu, thread) = -1; + } + thread = threads->nr; + } while (--cpu >= 0); return -1; } -int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) +static struct { + struct cpu_map map; + int cpus[1]; +} empty_cpu_map = { + .map.nr = 1, + .cpus = { -1, }, +}; + +static struct { + struct thread_map map; + int threads[1]; +} empty_thread_map = { + .map.nr = 1, + .threads = { -1, }, +}; + +int perf_evsel__open(struct perf_evsel *evsel, + struct cpu_map *cpus, struct thread_map *threads) { - int thread; - - if (evsel->fd == NULL && perf_evsel__alloc_fd(evsel, 1, threads->nr)) - return -1; - for (thread = 0; thread < threads->nr; thread++) { - FD(evsel, 0, thread) = sys_perf_event_open(&evsel->attr, - threads->map[thread], -1, -1, 0); - if (FD(evsel, 0, thread) < 0) - goto out_close; + if (cpus == NULL) { + /* Work around old compiler warnings about strict aliasing */ + cpus = &empty_cpu_map.map; } - return 0; + if (threads == NULL) + threads = &empty_thread_map.map; -out_close: - while (--thread >= 0) { - close(FD(evsel, 0, thread)); - FD(evsel, 0, thread) = -1; - } - return -1; + return __perf_evsel__open(evsel, cpus, threads); } -int perf_evsel__open(struct perf_evsel *evsel, - struct cpu_map *cpus, struct thread_map *threads) +int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) { - if (threads == NULL) - return perf_evsel__open_per_cpu(evsel, cpus); + return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); +} - return perf_evsel__open_per_thread(evsel, threads); +int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) +{ + return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); } -- cgit v1.2.2 From f08199d314458610d4ca52f8e86e0a4ec7a7bc54 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 11 Jan 2011 23:42:19 -0200 Subject: perf evsel: Support event groups The perf_evsel__open now have an extra boolean argument specifying if event grouping is desired. The first file descriptor created on a CPU becomes the group leader. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f5cfed60af98..da473ec93c75 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -128,7 +128,7 @@ int __perf_evsel__read(struct perf_evsel *evsel, } static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads) + struct thread_map *threads, bool group) { int cpu, thread; @@ -137,12 +137,18 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, return -1; for (cpu = 0; cpu < cpus->nr; cpu++) { + int group_fd = -1; + for (thread = 0; thread < threads->nr; thread++) { FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, threads->map[thread], - cpus->map[cpu], -1, 0); + cpus->map[cpu], + group_fd, 0); if (FD(evsel, cpu, thread) < 0) goto out_close; + + if (group && group_fd == -1) + group_fd = FD(evsel, cpu, thread); } } @@ -175,10 +181,9 @@ static struct { .threads = { -1, }, }; -int perf_evsel__open(struct perf_evsel *evsel, - struct cpu_map *cpus, struct thread_map *threads) +int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, + struct thread_map *threads, bool group) { - if (cpus == NULL) { /* Work around old compiler warnings about strict aliasing */ cpus = &empty_cpu_map.map; @@ -187,15 +192,17 @@ int perf_evsel__open(struct perf_evsel *evsel, if (threads == NULL) threads = &empty_thread_map.map; - return __perf_evsel__open(evsel, cpus, threads); + return __perf_evsel__open(evsel, cpus, threads, group); } -int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) +int perf_evsel__open_per_cpu(struct perf_evsel *evsel, + struct cpu_map *cpus, bool group) { - return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); + return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group); } -int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) +int perf_evsel__open_per_thread(struct perf_evsel *evsel, + struct thread_map *threads, bool group) { - return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); + return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group); } -- cgit v1.2.2 From 9d04f1781772e11bd58806391555fc23ebb54377 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Jan 2011 00:08:18 -0200 Subject: perf evsel: Allow specifying if the inherit bit should be set As this is a per-cpu attribute, we can't set it up in advance and use it for all the calls. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index da473ec93c75..82a00536892a 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -128,7 +128,7 @@ int __perf_evsel__read(struct perf_evsel *evsel, } static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads, bool group) + struct thread_map *threads, bool group, bool inherit) { int cpu, thread; @@ -139,6 +139,8 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, for (cpu = 0; cpu < cpus->nr; cpu++) { int group_fd = -1; + evsel->attr.inherit = (cpus->map[cpu] < 0) && inherit; + for (thread = 0; thread < threads->nr; thread++) { FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, threads->map[thread], @@ -182,7 +184,7 @@ static struct { }; int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads, bool group) + struct thread_map *threads, bool group, bool inherit) { if (cpus == NULL) { /* Work around old compiler warnings about strict aliasing */ @@ -192,17 +194,17 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, if (threads == NULL) threads = &empty_thread_map.map; - return __perf_evsel__open(evsel, cpus, threads, group); + return __perf_evsel__open(evsel, cpus, threads, group, inherit); } int perf_evsel__open_per_cpu(struct perf_evsel *evsel, - struct cpu_map *cpus, bool group) + struct cpu_map *cpus, bool group, bool inherit) { - return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group); + return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit); } int perf_evsel__open_per_thread(struct perf_evsel *evsel, - struct thread_map *threads, bool group) + struct thread_map *threads, bool group, bool inherit) { - return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group); + return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit); } -- cgit v1.2.2 From 70082dd92c4b288bd723a77897e2b555f0e63113 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Jan 2011 17:03:24 -0200 Subject: perf evsel: Introduce mmap support Out of the code in 'perf top'. Record is next in line. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 82a00536892a..f5006958f8da 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1,9 +1,13 @@ #include "evsel.h" +#include "evlist.h" #include "../perf.h" #include "util.h" #include "cpumap.h" #include "thread.h" +#include +#include + #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) @@ -49,10 +53,32 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) } } +void perf_evsel__munmap(struct perf_evsel *evsel, int ncpus, int nthreads) +{ + struct perf_mmap *mm; + int cpu, thread; + + for (cpu = 0; cpu < ncpus; cpu++) + for (thread = 0; thread < nthreads; ++thread) { + mm = xyarray__entry(evsel->mmap, cpu, thread); + if (mm->base != NULL) { + munmap(mm->base, evsel->mmap_len); + mm->base = NULL; + } + } +} + +int perf_evsel__alloc_mmap(struct perf_evsel *evsel, int ncpus, int nthreads) +{ + evsel->mmap = xyarray__new(ncpus, nthreads, sizeof(struct perf_mmap)); + return evsel->mmap != NULL ? 0 : -ENOMEM; +} + void perf_evsel__delete(struct perf_evsel *evsel) { assert(list_empty(&evsel->node)); xyarray__delete(evsel->fd); + xyarray__delete(evsel->mmap); free(evsel); } @@ -208,3 +234,48 @@ int perf_evsel__open_per_thread(struct perf_evsel *evsel, { return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit); } + +int perf_evsel__mmap(struct perf_evsel *evsel, struct cpu_map *cpus, + struct thread_map *threads, int pages, + struct perf_evlist *evlist) +{ + unsigned int page_size = sysconf(_SC_PAGE_SIZE); + int mask = pages * page_size - 1, cpu; + struct perf_mmap *mm; + int thread; + + if (evsel->mmap == NULL && + perf_evsel__alloc_mmap(evsel, cpus->nr, threads->nr) < 0) + return -ENOMEM; + + evsel->mmap_len = (pages + 1) * page_size; + + for (cpu = 0; cpu < cpus->nr; cpu++) { + for (thread = 0; thread < threads->nr; thread++) { + mm = xyarray__entry(evsel->mmap, cpu, thread); + mm->prev = 0; + mm->mask = mask; + mm->base = mmap(NULL, evsel->mmap_len, PROT_READ, + MAP_SHARED, FD(evsel, cpu, thread), 0); + if (mm->base == MAP_FAILED) + goto out_unmap; + + if (evlist != NULL) + perf_evlist__add_pollfd(evlist, FD(evsel, cpu, thread)); + } + } + + return 0; + +out_unmap: + do { + while (--thread >= 0) { + mm = xyarray__entry(evsel->mmap, cpu, thread); + munmap(mm->base, evsel->mmap_len); + mm->base = NULL; + } + thread = threads->nr; + } while (--cpu >= 0); + + return -1; +} -- cgit v1.2.2 From 70db7533caef02350ec8d6852e589491bca3a951 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Jan 2011 22:39:13 -0200 Subject: perf evlist: Move the mmap array from perf_evsel Adopting the new model used in 'perf record', where we don't have a map per thread per cpu, instead we have an mmap per cpu, established on the first fd for that cpu and ask the kernel using the PERF_EVENT_IOC_SET_OUTPUT ioctl to send events for the other fds on that cpu for the one with the mmap. The methods moved from perf_evsel to perf_evlist, but for easing review they were modified in place, in evsel.c, the next patch will move the migrated methods to evlist.c. With this 'perf top' now uses the same mmap model used by 'perf record' and the next patches will make 'perf record' use these new routines, establishing a common codebase for both tools. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 160 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 118 insertions(+), 42 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f5006958f8da..ee490356c817 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -8,7 +8,11 @@ #include #include +#include +#include + #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) +#define SID(e, x, y) xyarray__entry(e->id, x, y) struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) { @@ -29,6 +33,12 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) return evsel->fd != NULL ? 0 : -ENOMEM; } +int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) +{ + evsel->id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); + return evsel->id != NULL ? 0 : -ENOMEM; +} + int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) { evsel->counts = zalloc((sizeof(*evsel->counts) + @@ -42,6 +52,12 @@ void perf_evsel__free_fd(struct perf_evsel *evsel) evsel->fd = NULL; } +void perf_evsel__free_id(struct perf_evsel *evsel) +{ + xyarray__delete(evsel->id); + evsel->id = NULL; +} + void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) { int cpu, thread; @@ -53,32 +69,29 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) } } -void perf_evsel__munmap(struct perf_evsel *evsel, int ncpus, int nthreads) +void perf_evlist__munmap(struct perf_evlist *evlist, int ncpus) { - struct perf_mmap *mm; - int cpu, thread; + int cpu; - for (cpu = 0; cpu < ncpus; cpu++) - for (thread = 0; thread < nthreads; ++thread) { - mm = xyarray__entry(evsel->mmap, cpu, thread); - if (mm->base != NULL) { - munmap(mm->base, evsel->mmap_len); - mm->base = NULL; - } + for (cpu = 0; cpu < ncpus; cpu++) { + if (evlist->mmap[cpu].base != NULL) { + munmap(evlist->mmap[cpu].base, evlist->mmap_len); + evlist->mmap[cpu].base = NULL; } + } } -int perf_evsel__alloc_mmap(struct perf_evsel *evsel, int ncpus, int nthreads) +int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus) { - evsel->mmap = xyarray__new(ncpus, nthreads, sizeof(struct perf_mmap)); - return evsel->mmap != NULL ? 0 : -ENOMEM; + evlist->mmap = zalloc(ncpus * sizeof(struct perf_mmap)); + return evlist->mmap != NULL ? 0 : -ENOMEM; } void perf_evsel__delete(struct perf_evsel *evsel) { assert(list_empty(&evsel->node)); xyarray__delete(evsel->fd); - xyarray__delete(evsel->mmap); + xyarray__delete(evsel->id); free(evsel); } @@ -235,47 +248,110 @@ int perf_evsel__open_per_thread(struct perf_evsel *evsel, return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit); } -int perf_evsel__mmap(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads, int pages, - struct perf_evlist *evlist) +static int __perf_evlist__mmap(struct perf_evlist *evlist, int cpu, int prot, + int mask, int fd) +{ + evlist->mmap[cpu].prev = 0; + evlist->mmap[cpu].mask = mask; + evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot, + MAP_SHARED, fd, 0); + if (evlist->mmap[cpu].base == MAP_FAILED) + return -1; + + perf_evlist__add_pollfd(evlist, fd); + return 0; +} + +static int perf_evlist__id_hash(struct perf_evlist *evlist, struct perf_evsel *evsel, + int cpu, int thread, int fd) +{ + struct perf_sample_id *sid; + u64 read_data[4] = { 0, }; + int hash, id_idx = 1; /* The first entry is the counter value */ + + if (!(evsel->attr.read_format & PERF_FORMAT_ID) || + read(fd, &read_data, sizeof(read_data)) == -1) + return -1; + + if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + ++id_idx; + if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + ++id_idx; + + sid = SID(evsel, cpu, thread); + sid->id = read_data[id_idx]; + sid->evsel = evsel; + hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); + hlist_add_head(&sid->node, &evlist->heads[hash]); + return 0; +} + +/** perf_evlist__mmap - Create per cpu maps to receive events + * + * @evlist - list of events + * @cpus - cpu map being monitored + * @threads - threads map being monitored + * @pages - map length in pages + * @overwrite - overwrite older events? + * + * If overwrite is false the user needs to signal event consuption using: + * + * struct perf_mmap *m = &evlist->mmap[cpu]; + * unsigned int head = perf_mmap__read_head(m); + * + * perf_mmap__write_tail(m, head) + */ +int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus, + struct thread_map *threads, int pages, bool overwrite) { unsigned int page_size = sysconf(_SC_PAGE_SIZE); int mask = pages * page_size - 1, cpu; - struct perf_mmap *mm; - int thread; + struct perf_evsel *first_evsel, *evsel; + int thread, prot = PROT_READ | (overwrite ? 0 : PROT_WRITE); - if (evsel->mmap == NULL && - perf_evsel__alloc_mmap(evsel, cpus->nr, threads->nr) < 0) + if (evlist->mmap == NULL && + perf_evlist__alloc_mmap(evlist, cpus->nr) < 0) return -ENOMEM; - evsel->mmap_len = (pages + 1) * page_size; + if (evlist->pollfd == NULL && + perf_evlist__alloc_pollfd(evlist, cpus->nr, threads->nr) < 0) + return -ENOMEM; - for (cpu = 0; cpu < cpus->nr; cpu++) { - for (thread = 0; thread < threads->nr; thread++) { - mm = xyarray__entry(evsel->mmap, cpu, thread); - mm->prev = 0; - mm->mask = mask; - mm->base = mmap(NULL, evsel->mmap_len, PROT_READ, - MAP_SHARED, FD(evsel, cpu, thread), 0); - if (mm->base == MAP_FAILED) - goto out_unmap; - - if (evlist != NULL) - perf_evlist__add_pollfd(evlist, FD(evsel, cpu, thread)); + evlist->mmap_len = (pages + 1) * page_size; + first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node); + + list_for_each_entry(evsel, &evlist->entries, node) { + if ((evsel->attr.read_format & PERF_FORMAT_ID) && + evsel->id == NULL && + perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) + return -ENOMEM; + + for (cpu = 0; cpu < cpus->nr; cpu++) { + for (thread = 0; thread < threads->nr; thread++) { + int fd = FD(evsel, cpu, thread); + + if (evsel->idx || thread) { + if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, + FD(first_evsel, cpu, 0)) != 0) + goto out_unmap; + } else if (__perf_evlist__mmap(evlist, cpu, prot, mask, fd) < 0) + goto out_unmap; + + if ((evsel->attr.read_format & PERF_FORMAT_ID) && + perf_evlist__id_hash(evlist, evsel, cpu, thread, fd) < 0) + goto out_unmap; + } } } return 0; out_unmap: - do { - while (--thread >= 0) { - mm = xyarray__entry(evsel->mmap, cpu, thread); - munmap(mm->base, evsel->mmap_len); - mm->base = NULL; + for (cpu = 0; cpu < cpus->nr; cpu++) { + if (evlist->mmap[cpu].base != NULL) { + munmap(evlist->mmap[cpu].base, evlist->mmap_len); + evlist->mmap[cpu].base = NULL; } - thread = threads->nr; - } while (--cpu >= 0); - + } return -1; } -- cgit v1.2.2 From fd78260b5376173faeb17127bd63b3c99a8e8bfb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 18 Jan 2011 15:15:24 -0200 Subject: perf threads: Move thread_map to separate file To untangle it from struct thread handling, that is tied to symbols, etc. Right now in the python bindings I'm working on I need just a subset of the util/ files, untangling it allows me to do that. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index ee490356c817..9a6d94299ab8 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -3,7 +3,7 @@ #include "../perf.h" #include "util.h" #include "cpumap.h" -#include "thread.h" +#include "thread_map.h" #include #include -- cgit v1.2.2 From d0dd74e853a0a6f37e8061d6d50be41c7034c54c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 21 Jan 2011 13:46:41 -0200 Subject: perf tools: Move event__parse_sample to evsel.c To avoid linking more stuff in the python binding I'm working on, future csets will make the sample type be taken from the evsel itself, but for that we need to first have one file per cpu and per sample_type, not a single perf.data file. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 9a6d94299ab8..a85ae12845ea 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -355,3 +355,121 @@ out_unmap: } return -1; } + +static int event__parse_id_sample(const event_t *event, u64 type, + struct sample_data *sample) +{ + const u64 *array = event->sample.array; + + array += ((event->header.size - + sizeof(event->header)) / sizeof(u64)) - 1; + + if (type & PERF_SAMPLE_CPU) { + u32 *p = (u32 *)array; + sample->cpu = *p; + array--; + } + + if (type & PERF_SAMPLE_STREAM_ID) { + sample->stream_id = *array; + array--; + } + + if (type & PERF_SAMPLE_ID) { + sample->id = *array; + array--; + } + + if (type & PERF_SAMPLE_TIME) { + sample->time = *array; + array--; + } + + if (type & PERF_SAMPLE_TID) { + u32 *p = (u32 *)array; + sample->pid = p[0]; + sample->tid = p[1]; + } + + return 0; +} + +int event__parse_sample(const event_t *event, u64 type, bool sample_id_all, + struct sample_data *data) +{ + const u64 *array; + + data->cpu = data->pid = data->tid = -1; + data->stream_id = data->id = data->time = -1ULL; + + if (event->header.type != PERF_RECORD_SAMPLE) { + if (!sample_id_all) + return 0; + return event__parse_id_sample(event, type, data); + } + + array = event->sample.array; + + if (type & PERF_SAMPLE_IP) { + data->ip = event->ip.ip; + array++; + } + + if (type & PERF_SAMPLE_TID) { + u32 *p = (u32 *)array; + data->pid = p[0]; + data->tid = p[1]; + array++; + } + + if (type & PERF_SAMPLE_TIME) { + data->time = *array; + array++; + } + + if (type & PERF_SAMPLE_ADDR) { + data->addr = *array; + array++; + } + + data->id = -1ULL; + if (type & PERF_SAMPLE_ID) { + data->id = *array; + array++; + } + + if (type & PERF_SAMPLE_STREAM_ID) { + data->stream_id = *array; + array++; + } + + if (type & PERF_SAMPLE_CPU) { + u32 *p = (u32 *)array; + data->cpu = *p; + array++; + } + + if (type & PERF_SAMPLE_PERIOD) { + data->period = *array; + array++; + } + + if (type & PERF_SAMPLE_READ) { + fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); + return -1; + } + + if (type & PERF_SAMPLE_CALLCHAIN) { + data->callchain = (struct ip_callchain *)array; + array += 1 + data->callchain->nr; + } + + if (type & PERF_SAMPLE_RAW) { + u32 *p = (u32 *)array; + data->raw_size = *p; + p++; + data->raw_data = p; + } + + return 0; +} -- cgit v1.2.2 From ef1d1af28ca37fdbc2745da040529cd2953c1af5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 18 Jan 2011 21:41:45 -0200 Subject: perf evsel: Introduce perf_evsel__{in,ex}it Out of the {con,des}structor, as in interpreted language bindings we will need to go back from the wrapper object to the real thing. In that case using container_of will save us to have an extra pointer in the perf_evsel struct. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index a85ae12845ea..76ab553637d6 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -14,15 +14,20 @@ #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) #define SID(e, x, y) xyarray__entry(e->id, x, y) +void perf_evsel__init(struct perf_evsel *evsel, + struct perf_event_attr *attr, int idx) +{ + evsel->idx = idx; + evsel->attr = *attr; + INIT_LIST_HEAD(&evsel->node); +} + struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) { struct perf_evsel *evsel = zalloc(sizeof(*evsel)); - if (evsel != NULL) { - evsel->idx = idx; - evsel->attr = *attr; - INIT_LIST_HEAD(&evsel->node); - } + if (evsel != NULL) + perf_evsel__init(evsel, attr, idx); return evsel; } @@ -87,11 +92,16 @@ int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus) return evlist->mmap != NULL ? 0 : -ENOMEM; } -void perf_evsel__delete(struct perf_evsel *evsel) +void perf_evsel__exit(struct perf_evsel *evsel) { assert(list_empty(&evsel->node)); xyarray__delete(evsel->fd); xyarray__delete(evsel->id); +} + +void perf_evsel__delete(struct perf_evsel *evsel) +{ + perf_evsel__exit(evsel); free(evsel); } -- cgit v1.2.2 From 7bb41152b9be7e31f10d8919bce5034135525d9d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 29 Jan 2011 09:08:13 -0200 Subject: perf evlist: Support non overwrite mode in perf_evlist__read_on_cpu I.e. stash the overwrite mode in struct perf_evlist and act accordingly in perf_evlist__read_on_cpu, not checking for overwrites and touching the tail after consuming one event, like perf record does, for instance. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 76ab553637d6..f98b3e585039 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -327,6 +327,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus, perf_evlist__alloc_pollfd(evlist, cpus->nr, threads->nr) < 0) return -ENOMEM; + evlist->overwrite = overwrite; evlist->mmap_len = (pages + 1) * page_size; first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node); -- cgit v1.2.2 From 8d50e5b4171a69cf48ca94a1e7c14033d0b4771d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 29 Jan 2011 13:02:00 -0200 Subject: perf tools: Rename 'struct sample_data' to 'struct perf_sample' Making the namespace more uniform. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f98b3e585039..a13488511880 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -368,7 +368,7 @@ out_unmap: } static int event__parse_id_sample(const event_t *event, u64 type, - struct sample_data *sample) + struct perf_sample *sample) { const u64 *array = event->sample.array; @@ -406,7 +406,7 @@ static int event__parse_id_sample(const event_t *event, u64 type, } int event__parse_sample(const event_t *event, u64 type, bool sample_id_all, - struct sample_data *data) + struct perf_sample *data) { const u64 *array; -- cgit v1.2.2 From 8115d60c323dd9931b95221c0a392aeddc1d6ef3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 29 Jan 2011 14:01:45 -0200 Subject: perf tools: Kill event_t typedef, use 'union perf_event' instead And move the event_t methods to the perf_event__ too. No code changes, just namespace consistency. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index a13488511880..fddeb08f48a7 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -367,8 +367,8 @@ out_unmap: return -1; } -static int event__parse_id_sample(const event_t *event, u64 type, - struct perf_sample *sample) +static int perf_event__parse_id_sample(const union perf_event *event, u64 type, + struct perf_sample *sample) { const u64 *array = event->sample.array; @@ -405,8 +405,8 @@ static int event__parse_id_sample(const event_t *event, u64 type, return 0; } -int event__parse_sample(const event_t *event, u64 type, bool sample_id_all, - struct perf_sample *data) +int perf_event__parse_sample(const union perf_event *event, u64 type, + bool sample_id_all, struct perf_sample *data) { const u64 *array; @@ -416,7 +416,7 @@ int event__parse_sample(const event_t *event, u64 type, bool sample_id_all, if (event->header.type != PERF_RECORD_SAMPLE) { if (!sample_id_all) return 0; - return event__parse_id_sample(event, type, data); + return perf_event__parse_id_sample(event, type, data); } array = event->sample.array; -- cgit v1.2.2 From f8a9530939ed87b9a1b1a038b90e355098b679a2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 30 Jan 2011 10:46:46 -0200 Subject: perf evlist: Move evlist methods to evlist.c They were on evsel.c because they came from refactoring existing evsel methods, so, to make reviewing the changes easier, I kept it there, now its a plain move. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 144 +++--------------------------------------------- 1 file changed, 9 insertions(+), 135 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index fddeb08f48a7..2720bc1d578b 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1,18 +1,19 @@ +/* + * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo + * + * Parts came from builtin-{top,stat,record}.c, see those files for further + * copyright notes. + * + * Released under the GPL v2. (and only v2, not any later version) + */ + #include "evsel.h" #include "evlist.h" -#include "../perf.h" #include "util.h" #include "cpumap.h" #include "thread_map.h" -#include -#include - -#include -#include - #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) -#define SID(e, x, y) xyarray__entry(e->id, x, y) void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr, int idx) @@ -74,24 +75,6 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) } } -void perf_evlist__munmap(struct perf_evlist *evlist, int ncpus) -{ - int cpu; - - for (cpu = 0; cpu < ncpus; cpu++) { - if (evlist->mmap[cpu].base != NULL) { - munmap(evlist->mmap[cpu].base, evlist->mmap_len); - evlist->mmap[cpu].base = NULL; - } - } -} - -int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus) -{ - evlist->mmap = zalloc(ncpus * sizeof(struct perf_mmap)); - return evlist->mmap != NULL ? 0 : -ENOMEM; -} - void perf_evsel__exit(struct perf_evsel *evsel) { assert(list_empty(&evsel->node)); @@ -258,115 +241,6 @@ int perf_evsel__open_per_thread(struct perf_evsel *evsel, return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit); } -static int __perf_evlist__mmap(struct perf_evlist *evlist, int cpu, int prot, - int mask, int fd) -{ - evlist->mmap[cpu].prev = 0; - evlist->mmap[cpu].mask = mask; - evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot, - MAP_SHARED, fd, 0); - if (evlist->mmap[cpu].base == MAP_FAILED) - return -1; - - perf_evlist__add_pollfd(evlist, fd); - return 0; -} - -static int perf_evlist__id_hash(struct perf_evlist *evlist, struct perf_evsel *evsel, - int cpu, int thread, int fd) -{ - struct perf_sample_id *sid; - u64 read_data[4] = { 0, }; - int hash, id_idx = 1; /* The first entry is the counter value */ - - if (!(evsel->attr.read_format & PERF_FORMAT_ID) || - read(fd, &read_data, sizeof(read_data)) == -1) - return -1; - - if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) - ++id_idx; - if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) - ++id_idx; - - sid = SID(evsel, cpu, thread); - sid->id = read_data[id_idx]; - sid->evsel = evsel; - hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); - hlist_add_head(&sid->node, &evlist->heads[hash]); - return 0; -} - -/** perf_evlist__mmap - Create per cpu maps to receive events - * - * @evlist - list of events - * @cpus - cpu map being monitored - * @threads - threads map being monitored - * @pages - map length in pages - * @overwrite - overwrite older events? - * - * If overwrite is false the user needs to signal event consuption using: - * - * struct perf_mmap *m = &evlist->mmap[cpu]; - * unsigned int head = perf_mmap__read_head(m); - * - * perf_mmap__write_tail(m, head) - */ -int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus, - struct thread_map *threads, int pages, bool overwrite) -{ - unsigned int page_size = sysconf(_SC_PAGE_SIZE); - int mask = pages * page_size - 1, cpu; - struct perf_evsel *first_evsel, *evsel; - int thread, prot = PROT_READ | (overwrite ? 0 : PROT_WRITE); - - if (evlist->mmap == NULL && - perf_evlist__alloc_mmap(evlist, cpus->nr) < 0) - return -ENOMEM; - - if (evlist->pollfd == NULL && - perf_evlist__alloc_pollfd(evlist, cpus->nr, threads->nr) < 0) - return -ENOMEM; - - evlist->overwrite = overwrite; - evlist->mmap_len = (pages + 1) * page_size; - first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node); - - list_for_each_entry(evsel, &evlist->entries, node) { - if ((evsel->attr.read_format & PERF_FORMAT_ID) && - evsel->id == NULL && - perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) - return -ENOMEM; - - for (cpu = 0; cpu < cpus->nr; cpu++) { - for (thread = 0; thread < threads->nr; thread++) { - int fd = FD(evsel, cpu, thread); - - if (evsel->idx || thread) { - if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, - FD(first_evsel, cpu, 0)) != 0) - goto out_unmap; - } else if (__perf_evlist__mmap(evlist, cpu, prot, mask, fd) < 0) - goto out_unmap; - - if ((evsel->attr.read_format & PERF_FORMAT_ID) && - perf_evlist__id_hash(evlist, evsel, cpu, thread, fd) < 0) - goto out_unmap; - } - } - } - - return 0; - -out_unmap: - for (cpu = 0; cpu < cpus->nr; cpu++) { - if (evlist->mmap[cpu].base != NULL) { - munmap(evlist->mmap[cpu].base, evlist->mmap_len); - evlist->mmap[cpu].base = NULL; - } - } - return -1; -} - static int perf_event__parse_id_sample(const union perf_event *event, u64 type, struct perf_sample *sample) { -- cgit v1.2.2 From 52bcd9947bffae88bb1758f19277d678aaebc2ec Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 3 Feb 2011 17:26:06 -0200 Subject: perf stat: Fix aggreate counter reading accounting Introduced in: c52b12ed, when this sequence: count[0] = count[1] = count[2] = 0; Was replaced with: aggr->val = 0; Which is equivalent to zeroing just the first entry in the 'count' array. Fix it by zeroing the three entries with: aggr->val = aggr->ena = aggr->run = 0; Reported-by: Ingo Molnar Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f5cfed60af98..d8575d31ee6c 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -90,7 +90,7 @@ int __perf_evsel__read(struct perf_evsel *evsel, int cpu, thread; struct perf_counts_values *aggr = &evsel->counts->aggr, count; - aggr->val = 0; + aggr->val = aggr->ena = aggr->run = 0; for (cpu = 0; cpu < ncpus; cpu++) { for (thread = 0; thread < nthreads; thread++) { -- cgit v1.2.2 From 023695d96ee06f36cf5014e286edcd623e9fb847 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Mon, 14 Feb 2011 11:20:01 +0200 Subject: perf tool: Add cgroup support This patch adds the ability to filter monitoring based on container groups (cgroups) for both perf stat and perf record. It is possible to monitor multiple cgroup in parallel. There is one cgroup per event. The cgroups to monitor are passed via a new -G option followed by a comma separated list of cgroup names. The cgroup filesystem has to be mounted. Given a cgroup name, the perf tool finds the corresponding directory in the cgroup filesystem and opens it. It then passes that file descriptor to the kernel. Example: $ perf stat -B -a -e cycles:u,cycles:u,cycles:u -G test1,,test2 -- sleep 1 Performance counter stats for 'sleep 1': 2,368,667,414 cycles test1 2,369,661,459 cycles cycles test2 1.001856890 seconds time elapsed Signed-off-by: Stephane Eranian Signed-off-by: Peter Zijlstra LKML-Reference: <4d590290.825bdf0a.7d0a.4890@mx.google.com> Signed-off-by: Ingo Molnar --- tools/perf/util/evsel.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 211063eed474..c974e08d07ab 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -85,6 +85,7 @@ void perf_evsel__exit(struct perf_evsel *evsel) void perf_evsel__delete(struct perf_evsel *evsel) { perf_evsel__exit(evsel); + close_cgroup(evsel->cgrp); free(evsel); } @@ -163,21 +164,32 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, struct thread_map *threads, bool group, bool inherit) { int cpu, thread; + unsigned long flags = 0; + int pid = -1; if (evsel->fd == NULL && perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) return -1; + if (evsel->cgrp) { + flags = PERF_FLAG_PID_CGROUP; + pid = evsel->cgrp->fd; + } + for (cpu = 0; cpu < cpus->nr; cpu++) { int group_fd = -1; evsel->attr.inherit = (cpus->map[cpu] < 0) && inherit; for (thread = 0; thread < threads->nr; thread++) { + + if (!evsel->cgrp) + pid = threads->map[thread]; + FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, - threads->map[thread], + pid, cpus->map[cpu], - group_fd, 0); + group_fd, flags); if (FD(evsel, cpu, thread) < 0) goto out_close; -- cgit v1.2.2 From f0c55bcf4aa41b4b1dbee826513b1acb01bf65e1 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Wed, 16 Feb 2011 15:10:01 +0200 Subject: perf: make perf stat print user provided full event names This patch changes the way perf stat prints event names at the end of a run. Until now, it was trying to reconstruct the event name from its encoding. The problem is that it would only print generic events without their modifiers (u, k, pp). This patch saves the event name as passed by the user in the evsel struct and uses it to print the final event name. This would also work in case perf is linked with a library (such as libpfm4) which provides full PMU event tables. $ perf stat -e cycles:u,cycles:k date Wed Feb 16 14:58:52 CET 2011 Performance counter stats for 'date': 568600 cycles:u 2779715 cycles:k 0.001908182 seconds time elapsed Cc: Arun Sharma Cc: David S. Miller Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Robert Richter Cc: Stephane Eranian LPU-Reference: <4d5bdc64.98a1df0a.7aa3.06c2@mx.google.com> Signed-off-by: Stephane Eranian [ committer note: Fixed a merge problem with 023695d "Add cgroup support" ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index c974e08d07ab..63cadaf3e208 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -86,6 +86,7 @@ void perf_evsel__delete(struct perf_evsel *evsel) { perf_evsel__exit(evsel); close_cgroup(evsel->cgrp); + free(evsel->name); free(evsel); } -- cgit v1.2.2 From e603dc15072c7fec0ae263597e6dabc3bb4c5c5b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 21 Feb 2011 16:05:50 -0300 Subject: perf evsel: Fix inverted test for fixing up attr.inherit flag The kernel refuses mmapping an event with the inherit flag set for something that is systemwide (cpu == -1), and the evsel layer got this reversed at some point, fix it. The symtom was that the --pid and --tid parameters for 'perf record' and 'perf top' returned with -EINVAL, like: # /tmp/build-perf/perf record -v -fo/tmp/perf.data -p 1042 Warning: ... trying to fall back to cpu-clock-ticks Fatal: failed to mmap with 22 (Invalid argument) Reported-by: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 63cadaf3e208..8083d5126fca 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -179,8 +179,19 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, for (cpu = 0; cpu < cpus->nr; cpu++) { int group_fd = -1; - - evsel->attr.inherit = (cpus->map[cpu] < 0) && inherit; + /* + * Don't allow mmap() of inherited per-task counters. This + * would create a performance issue due to all children writing + * to the same buffer. + * + * FIXME: + * Proper fix is not to pass 'inherit' to perf_evsel__open*, + * but a 'flags' parameter, with 'group' folded there as well, + * then introduce a PERF_O_{MMAP,GROUP,INHERIT} enum, and if + * O_MMAP is set, emit a warning if cpu < 0 and O_INHERIT is + * set. Lets go for the minimal fix first tho. + */ + evsel->attr.inherit = (cpus->map[cpu] >= 0) && inherit; for (thread = 0; thread < threads->nr; thread++) { -- cgit v1.2.2 From a91e5431d54f5359fccb5ec2512f252eb217707e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 10 Mar 2011 11:15:54 -0300 Subject: perf session: Use evlist/evsel for managing perf.data attributes So that we can reuse things like the id to attr lookup routine (perf_evlist__id2evsel) that uses a hash table instead of the linear lookup done in the older perf_header_attr routines, etc. Also to make evsels/evlist more pervasive an API, simplyfing using the emerging perf lib. cc: Arun Sharma Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 8083d5126fca..662596afd7f1 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -41,8 +41,18 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) { - evsel->id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); - return evsel->id != NULL ? 0 : -ENOMEM; + evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); + if (evsel->sample_id == NULL) + return -ENOMEM; + + evsel->id = zalloc(ncpus * nthreads * sizeof(u64)); + if (evsel->id == NULL) { + xyarray__delete(evsel->sample_id); + evsel->sample_id = NULL; + return -ENOMEM; + } + + return 0; } int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) @@ -60,7 +70,9 @@ void perf_evsel__free_fd(struct perf_evsel *evsel) void perf_evsel__free_id(struct perf_evsel *evsel) { - xyarray__delete(evsel->id); + xyarray__delete(evsel->sample_id); + evsel->sample_id = NULL; + free(evsel->id); evsel->id = NULL; } @@ -79,7 +91,8 @@ void perf_evsel__exit(struct perf_evsel *evsel) { assert(list_empty(&evsel->node)); xyarray__delete(evsel->fd); - xyarray__delete(evsel->id); + xyarray__delete(evsel->sample_id); + free(evsel->id); } void perf_evsel__delete(struct perf_evsel *evsel) -- cgit v1.2.2 From 5d2cd90922c778908bd0cd669e572a5b5eafd737 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 14 Apr 2011 11:20:14 -0300 Subject: perf evsel: Fix use of inherit perf stat doesn't mmap and its perfectly fine for it to use task-bound counters with inheritance. So set the attr.inherit on the caller and leave the syscall itself to validate it. When the mmap fails perf_evlist__mmap will just emit a warning if this is the failure reason. Reported-by: Peter Zijlstra Acked-by: Peter Zijlstra Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tom Zanussi Link: http://lkml.kernel.org/r/20110414170121.GC3229@ghostprotocols.net Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 662596afd7f1..d6fd59beb860 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -175,7 +175,7 @@ int __perf_evsel__read(struct perf_evsel *evsel, } static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads, bool group, bool inherit) + struct thread_map *threads, bool group) { int cpu, thread; unsigned long flags = 0; @@ -192,19 +192,6 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, for (cpu = 0; cpu < cpus->nr; cpu++) { int group_fd = -1; - /* - * Don't allow mmap() of inherited per-task counters. This - * would create a performance issue due to all children writing - * to the same buffer. - * - * FIXME: - * Proper fix is not to pass 'inherit' to perf_evsel__open*, - * but a 'flags' parameter, with 'group' folded there as well, - * then introduce a PERF_O_{MMAP,GROUP,INHERIT} enum, and if - * O_MMAP is set, emit a warning if cpu < 0 and O_INHERIT is - * set. Lets go for the minimal fix first tho. - */ - evsel->attr.inherit = (cpus->map[cpu] >= 0) && inherit; for (thread = 0; thread < threads->nr; thread++) { @@ -253,7 +240,7 @@ static struct { }; int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads, bool group, bool inherit) + struct thread_map *threads, bool group) { if (cpus == NULL) { /* Work around old compiler warnings about strict aliasing */ @@ -263,19 +250,19 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, if (threads == NULL) threads = &empty_thread_map.map; - return __perf_evsel__open(evsel, cpus, threads, group, inherit); + return __perf_evsel__open(evsel, cpus, threads, group); } int perf_evsel__open_per_cpu(struct perf_evsel *evsel, - struct cpu_map *cpus, bool group, bool inherit) + struct cpu_map *cpus, bool group) { - return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit); + return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group); } int perf_evsel__open_per_thread(struct perf_evsel *evsel, - struct thread_map *threads, bool group, bool inherit) + struct thread_map *threads, bool group) { - return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit); + return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group); } static int perf_event__parse_id_sample(const union perf_event *event, u64 type, -- cgit v1.2.2 From a285412479b6d5af3e48273a92ec2f1987df8cd1 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 21 May 2011 19:33:04 +0200 Subject: perf tools: Pre-check sample size before parsing Check that the total size of the sample fields having a fixed size do not exceed the one of the whole event. This robustifies the sample parsing. Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian --- tools/perf/util/evsel.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index d6fd59beb860..bfce8bf642fa 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -304,7 +304,8 @@ static int perf_event__parse_id_sample(const union perf_event *event, u64 type, } int perf_event__parse_sample(const union perf_event *event, u64 type, - bool sample_id_all, struct perf_sample *data) + int sample_size, bool sample_id_all, + struct perf_sample *data) { const u64 *array; @@ -319,6 +320,9 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, array = event->sample.array; + if (sample_size + sizeof(event->header) > event->header.size) + return -EFAULT; + if (type & PERF_SAMPLE_IP) { data->ip = event->ip.ip; array++; -- cgit v1.2.2 From 98e1da905cbe64bb023a165c7c01eef5e800609e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sat, 21 May 2011 20:08:15 +0200 Subject: perf tools: Robustify dynamic sample content fetch Ensure the size of the dynamic fields such as callchains or raw events don't overlap the whole event boundaries. This prevents from dereferencing junk if the given size of the callchain goes too eager. Reported-by: Linus Torvalds Reported-by: Ingo Molnar Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Stephane Eranian --- tools/perf/util/evsel.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index bfce8bf642fa..ee0fe0dffa71 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -303,6 +303,17 @@ static int perf_event__parse_id_sample(const union perf_event *event, u64 type, return 0; } +static bool sample_overlap(const union perf_event *event, + const void *offset, u64 size) +{ + const void *base = event; + + if (offset + size > base + event->header.size) + return true; + + return false; +} + int perf_event__parse_sample(const union perf_event *event, u64 type, int sample_size, bool sample_id_all, struct perf_sample *data) @@ -373,14 +384,29 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, } if (type & PERF_SAMPLE_CALLCHAIN) { + if (sample_overlap(event, array, sizeof(data->callchain->nr))) + return -EFAULT; + data->callchain = (struct ip_callchain *)array; + + if (sample_overlap(event, array, data->callchain->nr)) + return -EFAULT; + array += 1 + data->callchain->nr; } if (type & PERF_SAMPLE_RAW) { u32 *p = (u32 *)array; + + if (sample_overlap(event, array, sizeof(u32))) + return -EFAULT; + data->raw_size = *p; p++; + + if (sample_overlap(event, p, data->raw_size)) + return -EFAULT; + data->raw_data = p; } -- cgit v1.2.2 From 4af4c9550ccaaf0b53013ff730bc15068ffe6abc Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 27 May 2011 09:58:34 -0600 Subject: perf events: initialize fd array to -1 instead of 0 perf_evsel__alloc_fd allocates an array of file descriptors with the memory initialized to 0. The array has dimensions for cpus and threads. Later, __perf_evsel__open calls sys_perf_event_open for each cpu and thread dimensions. If the open fails for any of the cpus or threads then the fd's for this event are closed and the fd entry in the array is set to -1. Now, if the first attempt fails for the event (e.g., the event is not supported) the remaining dimensions (cpu > 0 and thread > 0) are not touched and left at the initialized value of 0. builtin-stat catches ENOENT and ENOSYS failures and allows the command to continue. The end result is that stat attempts to read from an fd of 0 which of course is stdin and so the command hangs until you type ctrl-D. Resolve by initializing the array to -1 since an fd < 0 is already handled. Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1306511914-8016-1-git-send-email-dsahern@gmail.com Signed-off-by: David Ahern Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index ee0fe0dffa71..cca29ededb5b 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -35,7 +35,17 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) { + int cpu, thread; evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); + + if (evsel->fd) { + for (cpu = 0; cpu < ncpus; cpu++) { + for (thread = 0; thread < nthreads; thread++) { + FD(evsel, cpu, thread) = -1; + } + } + } + return evsel->fd != NULL ? 0 : -ENOMEM; } -- cgit v1.2.2 From 56722381b8506733852c44dacf6d7bc5f90aedaf Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 2 Jun 2011 11:04:54 -0300 Subject: perf evlist: Don't die if sample_{id_all|type} is invalid Fixes two more cases where the python binding would not load: . Not finding die(), which it shouldn't anyway, not good to just stop the world because some particular perf.data file is invalid, just propagate the error to the caller. . Not finding perf_sample_size: fix it by moving it from event.c to evsel, where it belongs, as most cases are moving to operate on an evsel object.o One of the fixed problems: [root@emilia ~]# python >>> import perf Traceback (most recent call last): File "", line 1, in ImportError: /home/acme/git/build/perf/python/perf.so: undefined symbol: perf_sample_size >>> [root@emilia ~]# Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-1hkj7b2cvgbfnoizsekjb6c9@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'tools/perf/util/evsel.c') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index cca29ededb5b..0239eb87b232 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -15,6 +15,22 @@ #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) +int __perf_evsel__sample_size(u64 sample_type) +{ + u64 mask = sample_type & PERF_SAMPLE_MASK; + int size = 0; + int i; + + for (i = 0; i < 64; i++) { + if (mask & (1ULL << i)) + size++; + } + + size *= sizeof(u64); + + return size; +} + void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr, int idx) { -- cgit v1.2.2