perf record: Use perf_evsel__open

Now its time to factor out the mmap handling bits into the perf_evsel
class.

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>
LKML-Reference: <new-submission>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Arnaldo Carvalho de Melo 2011-01-12 14:28:51 -02:00
parent 72cb7013e0
commit dd7927f4f8
1 changed files with 113 additions and 120 deletions

View File

@ -72,8 +72,6 @@ static struct perf_evlist *evsel_list;
static long samples = 0; static long samples = 0;
static u64 bytes_written = 0; static u64 bytes_written = 0;
static int nr_cpu = 0;
static int file_new = 1; static int file_new = 1;
static off_t post_processing_offset; static off_t post_processing_offset;
@ -208,8 +206,6 @@ static void sig_atexit(void)
kill(getpid(), signr); kill(getpid(), signr);
} }
static int group_fd;
static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr)
{ {
struct perf_header_attr *h_attr; struct perf_header_attr *h_attr;
@ -234,7 +230,6 @@ static void create_counter(struct perf_evlist *evlist,
char *filter = evsel->filter; char *filter = evsel->filter;
struct perf_event_attr *attr = &evsel->attr; struct perf_event_attr *attr = &evsel->attr;
struct perf_header_attr *h_attr; struct perf_header_attr *h_attr;
int track = !evsel->idx; /* only the first counter needs these */
int thread_index; int thread_index;
int ret; int ret;
struct { struct {
@ -243,19 +238,77 @@ static void create_counter(struct perf_evlist *evlist,
u64 time_running; u64 time_running;
u64 id; u64 id;
} read_data; } read_data;
/*
* Check if parse_single_tracepoint_event has already asked for for (thread_index = 0; thread_index < threads->nr; thread_index++) {
* PERF_SAMPLE_TIME. h_attr = get_header_attr(attr, evsel->idx);
* if (h_attr == NULL)
* XXX this is kludgy but short term fix for problems introduced by die("nomem\n");
* eac23d1c that broke 'perf script' by having different sample_types
* when using multiple tracepoint events when we use a perf binary if (!file_new) {
* that tries to use sample_id_all on an older kernel. if (memcmp(&h_attr->attr, attr, sizeof(*attr))) {
* fprintf(stderr, "incompatible append\n");
* We need to move counter creation to perf_session, support exit(-1);
* different sample_types, etc. }
*/ }
bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
if (read(FD(evsel, cpu, thread_index), &read_data, sizeof(read_data)) == -1) {
perror("Unable to read perf file descriptor");
exit(-1);
}
if (perf_header_attr__add_id(h_attr, read_data.id) < 0) {
pr_warning("Not enough memory to add id\n");
exit(-1);
}
assert(FD(evsel, cpu, thread_index) >= 0);
fcntl(FD(evsel, cpu, thread_index), F_SETFL, O_NONBLOCK);
if (evsel->idx || thread_index) {
struct perf_evsel *first;
first = list_entry(evlist->entries.next, struct perf_evsel, node);
ret = ioctl(FD(evsel, cpu, thread_index),
PERF_EVENT_IOC_SET_OUTPUT,
FD(first, cpu, 0));
if (ret) {
error("failed to set output: %d (%s)\n", errno,
strerror(errno));
exit(-1);
}
} else {
mmap_array[cpu].prev = 0;
mmap_array[cpu].mask = mmap_pages*page_size - 1;
mmap_array[cpu].base = mmap(NULL, (mmap_pages+1)*page_size,
PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, cpu, thread_index), 0);
if (mmap_array[cpu].base == MAP_FAILED) {
error("failed to mmap with %d (%s)\n", errno, strerror(errno));
exit(-1);
}
evlist->pollfd[evlist->nr_fds].fd = FD(evsel, cpu, thread_index);
evlist->pollfd[evlist->nr_fds].events = POLLIN;
evlist->nr_fds++;
}
if (filter != NULL) {
ret = ioctl(FD(evsel, cpu, thread_index),
PERF_EVENT_IOC_SET_FILTER, filter);
if (ret) {
error("failed to set filter with %d (%s)\n", errno,
strerror(errno));
exit(-1);
}
}
}
if (!sample_type)
sample_type = attr->sample_type;
}
static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist)
{
struct perf_event_attr *attr = &evsel->attr;
int track = !evsel->idx; /* only the first counter needs these */
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_TOTAL_TIME_RUNNING |
@ -315,19 +368,39 @@ static void create_counter(struct perf_evlist *evlist,
attr->mmap = track; attr->mmap = track;
attr->comm = track; attr->comm = track;
attr->inherit = !no_inherit;
if (target_pid == -1 && target_tid == -1 && !system_wide) { if (target_pid == -1 && target_tid == -1 && !system_wide) {
attr->disabled = 1; attr->disabled = 1;
attr->enable_on_exec = 1; attr->enable_on_exec = 1;
} }
}
static void open_counters(struct perf_evlist *evlist)
{
struct perf_evsel *pos;
int cpu;
list_for_each_entry(pos, &evlist->entries, node) {
struct perf_event_attr *attr = &pos->attr;
/*
* Check if parse_single_tracepoint_event has already asked for
* PERF_SAMPLE_TIME.
*
* XXX this is kludgy but short term fix for problems introduced by
* eac23d1c that broke 'perf script' by having different sample_types
* when using multiple tracepoint events when we use a perf binary
* that tries to use sample_id_all on an older kernel.
*
* We need to move counter creation to perf_session, support
* different sample_types, etc.
*/
bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
config_attr(pos, evlist);
retry_sample_id: retry_sample_id:
attr->sample_id_all = sample_id_all_avail ? 1 : 0; attr->sample_id_all = sample_id_all_avail ? 1 : 0;
for (thread_index = 0; thread_index < threads->nr; thread_index++) {
try_again: try_again:
FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0); if (perf_evsel__open(pos, cpus, threads, group, !no_inherit) < 0) {
if (FD(evsel, nr_cpu, thread_index) < 0) {
int err = errno; int err = errno;
if (err == EPERM || err == EACCES) if (err == EPERM || err == EACCES)
@ -364,7 +437,7 @@ try_again:
} }
printf("\n"); printf("\n");
error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n",
FD(evsel, nr_cpu, thread_index), strerror(err)); err, strerror(err));
#if defined(__i386__) || defined(__x86_64__) #if defined(__i386__) || defined(__x86_64__)
if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP)
@ -375,90 +448,13 @@ try_again:
#endif #endif
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
exit(-1);
}
h_attr = get_header_attr(attr, evsel->idx);
if (h_attr == NULL)
die("nomem\n");
if (!file_new) {
if (memcmp(&h_attr->attr, attr, sizeof(*attr))) {
fprintf(stderr, "incompatible append\n");
exit(-1);
} }
} }
if (read(FD(evsel, nr_cpu, thread_index), &read_data, sizeof(read_data)) == -1) { for (cpu = 0; cpu < cpus->nr; ++cpu) {
perror("Unable to read perf file descriptor");
exit(-1);
}
if (perf_header_attr__add_id(h_attr, read_data.id) < 0) {
pr_warning("Not enough memory to add id\n");
exit(-1);
}
assert(FD(evsel, nr_cpu, thread_index) >= 0);
fcntl(FD(evsel, nr_cpu, thread_index), F_SETFL, O_NONBLOCK);
/*
* First counter acts as the group leader:
*/
if (group && group_fd == -1)
group_fd = FD(evsel, nr_cpu, thread_index);
if (evsel->idx || thread_index) {
struct perf_evsel *first;
first = list_entry(evlist->entries.next, struct perf_evsel, node);
ret = ioctl(FD(evsel, nr_cpu, thread_index),
PERF_EVENT_IOC_SET_OUTPUT,
FD(first, nr_cpu, 0));
if (ret) {
error("failed to set output: %d (%s)\n", errno,
strerror(errno));
exit(-1);
}
} else {
mmap_array[nr_cpu].prev = 0;
mmap_array[nr_cpu].mask = mmap_pages*page_size - 1;
mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size,
PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, nr_cpu, thread_index), 0);
if (mmap_array[nr_cpu].base == MAP_FAILED) {
error("failed to mmap with %d (%s)\n", errno, strerror(errno));
exit(-1);
}
evlist->pollfd[evlist->nr_fds].fd = FD(evsel, nr_cpu, thread_index);
evlist->pollfd[evlist->nr_fds].events = POLLIN;
evlist->nr_fds++;
}
if (filter != NULL) {
ret = ioctl(FD(evsel, nr_cpu, thread_index),
PERF_EVENT_IOC_SET_FILTER, filter);
if (ret) {
error("failed to set filter with %d (%s)\n", errno,
strerror(errno));
exit(-1);
}
}
}
if (!sample_type)
sample_type = attr->sample_type;
}
static void open_counters(struct perf_evlist *evlist, int cpu)
{
struct perf_evsel *pos;
group_fd = -1;
list_for_each_entry(pos, &evlist->entries, node) list_for_each_entry(pos, &evlist->entries, node)
create_counter(evlist, pos, cpu); create_counter(evlist, pos, cpu);
}
nr_cpu++;
} }
static int process_buildids(void) static int process_buildids(void)
@ -533,7 +529,7 @@ static void mmap_read_all(void)
{ {
int i; int i;
for (i = 0; i < nr_cpu; i++) { for (i = 0; i < cpus->nr; i++) {
if (mmap_array[i].base) if (mmap_array[i].base)
mmap_read(&mmap_array[i]); mmap_read(&mmap_array[i]);
} }
@ -673,12 +669,7 @@ static int __cmd_record(int argc, const char **argv)
close(child_ready_pipe[0]); close(child_ready_pipe[0]);
} }
if (!system_wide && no_inherit && !cpu_list) { open_counters(evsel_list);
open_counters(evsel_list, -1);
} else {
for (i = 0; i < cpus->nr; i++)
open_counters(evsel_list, cpus->map[i]);
}
perf_session__set_sample_type(session, sample_type); perf_session__set_sample_type(session, sample_type);
@ -795,7 +786,7 @@ static int __cmd_record(int argc, const char **argv)
} }
if (done) { if (done) {
for (i = 0; i < nr_cpu; i++) { for (i = 0; i < cpus->nr; i++) {
struct perf_evsel *pos; struct perf_evsel *pos;
list_for_each_entry(pos, &evsel_list->entries, node) { list_for_each_entry(pos, &evsel_list->entries, node) {
@ -933,11 +924,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
usage_with_options(record_usage, record_options); usage_with_options(record_usage, record_options);
} }
if (target_tid != -1)
cpus = cpu_map__dummy_new();
else
cpus = cpu_map__new(cpu_list); cpus = cpu_map__new(cpu_list);
if (cpus == NULL) {
perror("failed to parse CPUs map"); if (cpus == NULL)
return -1; usage_with_options(record_usage, record_options);
}
list_for_each_entry(pos, &evsel_list->entries, node) { list_for_each_entry(pos, &evsel_list->entries, node) {
if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0)