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: <new-submission>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Tom Zanussi <tzanussi@gmail.com>
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 12f7e036
...@@ -234,6 +234,7 @@ static int test__vmlinux_matches_kallsyms(void) ...@@ -234,6 +234,7 @@ static int test__vmlinux_matches_kallsyms(void)
return err; return err;
} }
#include "util/cpumap.h"
#include "util/evsel.h" #include "util/evsel.h"
#include <sys/types.h> #include <sys/types.h>
...@@ -321,6 +322,111 @@ static int test__open_syscall_event(void) ...@@ -321,6 +322,111 @@ static int test__open_syscall_event(void)
return err; return err;
} }
#include <sched.h>
static int test__open_syscall_event_on_all_cpus(void)
{
int err = -1, fd, cpu;
struct thread_map *threads;
struct cpu_map *cpus;
struct perf_evsel *evsel;
struct perf_event_attr attr;
unsigned int nr_open_calls = 111, i;
cpu_set_t *cpu_set;
size_t cpu_set_size;
int id = trace_event__id("sys_enter_open");
if (id < 0) {
pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
return -1;
}
threads = thread_map__new(-1, getpid());
if (threads == NULL) {
pr_debug("thread_map__new\n");
return -1;
}
cpus = cpu_map__new(NULL);
if (threads == NULL) {
pr_debug("thread_map__new\n");
return -1;
}
cpu_set = CPU_ALLOC(cpus->nr);
if (cpu_set == NULL)
goto out_thread_map_delete;
cpu_set_size = CPU_ALLOC_SIZE(cpus->nr);
CPU_ZERO_S(cpu_set_size, cpu_set);
memset(&attr, 0, sizeof(attr));
attr.type = PERF_TYPE_TRACEPOINT;
attr.config = id;
evsel = perf_evsel__new(&attr, 0);
if (evsel == NULL) {
pr_debug("perf_evsel__new\n");
goto out_cpu_free;
}
if (perf_evsel__open(evsel, cpus, threads) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno));
goto out_evsel_delete;
}
for (cpu = 0; cpu < cpus->nr; ++cpu) {
unsigned int ncalls = nr_open_calls + cpu;
CPU_SET(cpu, cpu_set);
sched_setaffinity(0, cpu_set_size, cpu_set);
for (i = 0; i < ncalls; ++i) {
fd = open("/etc/passwd", O_RDONLY);
close(fd);
}
CPU_CLR(cpu, cpu_set);
}
/*
* Here we need to explicitely preallocate the counts, as if
* we use the auto allocation it will allocate just for 1 cpu,
* as we start by cpu 0.
*/
if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) {
pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr);
goto out_close_fd;
}
for (cpu = 0; cpu < cpus->nr; ++cpu) {
unsigned int expected;
if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) {
pr_debug("perf_evsel__open_read_on_cpu\n");
goto out_close_fd;
}
expected = nr_open_calls + cpu;
if (evsel->counts->cpu[cpu].val != expected) {
pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %Ld\n",
expected, cpu, evsel->counts->cpu[cpu].val);
goto out_close_fd;
}
}
err = 0;
out_close_fd:
perf_evsel__close_fd(evsel, 1, threads->nr);
out_evsel_delete:
perf_evsel__delete(evsel);
out_cpu_free:
CPU_FREE(cpu_set);
out_thread_map_delete:
thread_map__delete(threads);
return err;
}
static struct test { static struct test {
const char *desc; const char *desc;
int (*func)(void); int (*func)(void);
...@@ -333,6 +439,10 @@ static struct test { ...@@ -333,6 +439,10 @@ static struct test {
.desc = "detect open syscall event", .desc = "detect open syscall event",
.func = test__open_syscall_event, .func = test__open_syscall_event,
}, },
{
.desc = "detect open syscall event on all cpus",
.func = test__open_syscall_event_on_all_cpus,
},
{ {
.func = NULL, .func = NULL,
}, },
......
...@@ -127,59 +127,75 @@ int __perf_evsel__read(struct perf_evsel *evsel, ...@@ -127,59 +127,75 @@ int __perf_evsel__read(struct perf_evsel *evsel,
return 0; 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; return -1;
for (cpu = 0; cpu < cpus->nr; cpu++) { for (cpu = 0; cpu < cpus->nr; cpu++) {
FD(evsel, cpu, 0) = sys_perf_event_open(&evsel->attr, -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], -1, 0);
if (FD(evsel, cpu, 0) < 0) if (FD(evsel, cpu, thread) < 0)
goto out_close; goto out_close;
} }
}
return 0; return 0;
out_close: out_close:
while (--cpu >= 0) { do {
close(FD(evsel, cpu, 0)); while (--thread >= 0) {
FD(evsel, cpu, 0) = -1; close(FD(evsel, cpu, thread));
FD(evsel, cpu, thread) = -1;
} }
thread = threads->nr;
} while (--cpu >= 0);
return -1; return -1;
} }
int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) static struct {
{ struct cpu_map map;
int thread; 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, },
};
if (evsel->fd == NULL && perf_evsel__alloc_fd(evsel, 1, threads->nr)) int perf_evsel__open(struct perf_evsel *evsel,
return -1; struct cpu_map *cpus, struct thread_map *threads)
{
for (thread = 0; thread < threads->nr; thread++) { if (cpus == NULL) {
FD(evsel, 0, thread) = sys_perf_event_open(&evsel->attr, /* Work around old compiler warnings about strict aliasing */
threads->map[thread], -1, -1, 0); cpus = &empty_cpu_map.map;
if (FD(evsel, 0, thread) < 0)
goto out_close;
} }
return 0; if (threads == NULL)
threads = &empty_thread_map.map;
out_close: return __perf_evsel__open(evsel, cpus, threads);
while (--thread >= 0) {
close(FD(evsel, 0, thread));
FD(evsel, 0, thread) = -1;
}
return -1;
} }
int perf_evsel__open(struct perf_evsel *evsel, int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus)
struct cpu_map *cpus, struct thread_map *threads)
{ {
if (threads == NULL) return __perf_evsel__open(evsel, cpus, &empty_thread_map.map);
return perf_evsel__open_per_cpu(evsel, cpus); }
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);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment