Skip to content

Commit

Permalink
Merge tag 'perf-fdarray-for-mingo' of git://git.kernel.org/pub/scm/li…
Browse files Browse the repository at this point in the history
…nux/kernel/git/acme/linux into perf/core

Pull perf tooling updates from Arnaldo Carvalho de Melo.

Infrastructure changes:

  * We were not handling POLLHUP notifications for event file descriptors.

    Fix it by filtering entries in the events file descriptor array after
    poll() returns, refcounting mmaps so that when the last fd pointing to
    a perf mmap goes away we do the unmap. (Arnaldo Carvalho de Melo)

User visible changes:

  * Now 'record' and 'trace' properly exit when a target thread exits.
    (Arnaldo Carvalho de Melo)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
  • Loading branch information
Ingo Molnar committed Sep 26, 2014
2 parents 521e8ba + 46fb3c2 commit cf8102f
Show file tree
Hide file tree
Showing 17 changed files with 501 additions and 43 deletions.
7 changes: 6 additions & 1 deletion tools/lib/api/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ LIB_OBJS=

LIB_H += fs/debugfs.h
LIB_H += fs/fs.h
# See comment below about piggybacking...
LIB_H += fd/array.h

LIB_OBJS += $(OUTPUT)fs/debugfs.o
LIB_OBJS += $(OUTPUT)fs/fs.o
# XXX piggybacking here, need to introduce libapikfd, or rename this
# to plain libapik.a and make it have it all api goodies
LIB_OBJS += $(OUTPUT)fd/array.o

LIBFILE = libapikfs.a

Expand All @@ -29,7 +34,7 @@ $(LIBFILE): $(LIB_OBJS)
$(LIB_OBJS): $(LIB_H)

libapi_dirs:
$(QUIET_MKDIR)mkdir -p $(OUTPUT)fs/
$(QUIET_MKDIR)mkdir -p $(OUTPUT)fd $(OUTPUT)fs

$(OUTPUT)%.o: %.c libapi_dirs
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
Expand Down
127 changes: 127 additions & 0 deletions tools/lib/api/fd/array.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright (C) 2014, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Released under the GPL v2. (and only v2, not any later version)
*/
#include "array.h"
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdlib.h>
#include <unistd.h>

void fdarray__init(struct fdarray *fda, int nr_autogrow)
{
fda->entries = NULL;
fda->priv = NULL;
fda->nr = fda->nr_alloc = 0;
fda->nr_autogrow = nr_autogrow;
}

int fdarray__grow(struct fdarray *fda, int nr)
{
void *priv;
int nr_alloc = fda->nr_alloc + nr;
size_t psize = sizeof(fda->priv[0]) * nr_alloc;
size_t size = sizeof(struct pollfd) * nr_alloc;
struct pollfd *entries = realloc(fda->entries, size);

if (entries == NULL)
return -ENOMEM;

priv = realloc(fda->priv, psize);
if (priv == NULL) {
free(entries);
return -ENOMEM;
}

fda->nr_alloc = nr_alloc;
fda->entries = entries;
fda->priv = priv;
return 0;
}

struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow)
{
struct fdarray *fda = calloc(1, sizeof(*fda));

if (fda != NULL) {
if (fdarray__grow(fda, nr_alloc)) {
free(fda);
fda = NULL;
} else {
fda->nr_autogrow = nr_autogrow;
}
}

return fda;
}

void fdarray__exit(struct fdarray *fda)
{
free(fda->entries);
free(fda->priv);
fdarray__init(fda, 0);
}

void fdarray__delete(struct fdarray *fda)
{
fdarray__exit(fda);
free(fda);
}

int fdarray__add(struct fdarray *fda, int fd, short revents)
{
int pos = fda->nr;

if (fda->nr == fda->nr_alloc &&
fdarray__grow(fda, fda->nr_autogrow) < 0)
return -ENOMEM;

fda->entries[fda->nr].fd = fd;
fda->entries[fda->nr].events = revents;
fda->nr++;
return pos;
}

int fdarray__filter(struct fdarray *fda, short revents,
void (*entry_destructor)(struct fdarray *fda, int fd))
{
int fd, nr = 0;

if (fda->nr == 0)
return 0;

for (fd = 0; fd < fda->nr; ++fd) {
if (fda->entries[fd].revents & revents) {
if (entry_destructor)
entry_destructor(fda, fd);

continue;
}

if (fd != nr) {
fda->entries[nr] = fda->entries[fd];
fda->priv[nr] = fda->priv[fd];
}

++nr;
}

return fda->nr = nr;
}

int fdarray__poll(struct fdarray *fda, int timeout)
{
return poll(fda->entries, fda->nr, timeout);
}

int fdarray__fprintf(struct fdarray *fda, FILE *fp)
{
int fd, printed = fprintf(fp, "%d [ ", fda->nr);

for (fd = 0; fd < fda->nr; ++fd)
printed += fprintf(fp, "%s%d", fd ? ", " : "", fda->entries[fd].fd);

return printed + fprintf(fp, " ]");
}
46 changes: 46 additions & 0 deletions tools/lib/api/fd/array.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#ifndef __API_FD_ARRAY__
#define __API_FD_ARRAY__

#include <stdio.h>

struct pollfd;

/**
* struct fdarray: Array of file descriptors
*
* @priv: Per array entry priv area, users should access just its contents,
* not set it to anything, as it is kept in synch with @entries, being
* realloc'ed, * for instance, in fdarray__{grow,filter}.
*
* I.e. using 'fda->priv[N].idx = * value' where N < fda->nr is ok,
* but doing 'fda->priv = malloc(M)' is not allowed.
*/
struct fdarray {
int nr;
int nr_alloc;
int nr_autogrow;
struct pollfd *entries;
union {
int idx;
} *priv;
};

void fdarray__init(struct fdarray *fda, int nr_autogrow);
void fdarray__exit(struct fdarray *fda);

struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow);
void fdarray__delete(struct fdarray *fda);

int fdarray__add(struct fdarray *fda, int fd, short revents);
int fdarray__poll(struct fdarray *fda, int timeout);
int fdarray__filter(struct fdarray *fda, short revents,
void (*entry_destructor)(struct fdarray *fda, int fd));
int fdarray__grow(struct fdarray *fda, int extra);
int fdarray__fprintf(struct fdarray *fda, FILE *fp);

static inline int fdarray__available_entries(struct fdarray *fda)
{
return fda->nr_alloc - fda->nr;
}

#endif /* __API_FD_ARRAY__ */
3 changes: 2 additions & 1 deletion tools/perf/Makefile.perf
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ LIB_OBJS += $(OUTPUT)tests/perf-record.o
LIB_OBJS += $(OUTPUT)tests/rdpmc.o
LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o
LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o
LIB_OBJS += $(OUTPUT)tests/fdarray.o
LIB_OBJS += $(OUTPUT)tests/pmu.o
LIB_OBJS += $(OUTPUT)tests/hists_common.o
LIB_OBJS += $(OUTPUT)tests/hists_link.o
Expand Down Expand Up @@ -769,7 +770,7 @@ $(LIBTRACEEVENT)-clean:
install-traceevent-plugins: $(LIBTRACEEVENT)
$(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) install_plugins

LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch])
LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch] $(LIB_PATH)fd/*.[ch])

# if subdir is set, we've been called from above so target has been built
# already
Expand Down
24 changes: 10 additions & 14 deletions tools/perf/builtin-kvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -919,33 +919,30 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm)
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);

/* copy pollfds -- need to add timerfd and stdin */
nr_fds = kvm->evlist->nr_fds;
pollfds = zalloc(sizeof(struct pollfd) * (nr_fds + 2));
if (!pollfds) {
err = -ENOMEM;
goto out;
}
memcpy(pollfds, kvm->evlist->pollfd,
sizeof(struct pollfd) * kvm->evlist->nr_fds);
/* use pollfds -- need to add timerfd and stdin */
nr_fds = kvm->evlist->pollfd.nr;

/* add timer fd */
if (perf_kvm__timerfd_create(kvm) < 0) {
err = -1;
goto out;
}

pollfds[nr_fds].fd = kvm->timerfd;
pollfds[nr_fds].events = POLLIN;
if (perf_evlist__add_pollfd(kvm->evlist, kvm->timerfd))
goto out;

nr_fds++;

pollfds[nr_fds].fd = fileno(stdin);
pollfds[nr_fds].events = POLLIN;
if (perf_evlist__add_pollfd(kvm->evlist, fileno(stdin)))
goto out;

nr_stdin = nr_fds;
nr_fds++;
if (fd_set_nonblock(fileno(stdin)) != 0)
goto out;

pollfds = kvm->evlist->pollfd.entries;

/* everything is good - enable the events and process */
perf_evlist__enable(kvm->evlist);

Expand Down Expand Up @@ -979,7 +976,6 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm)
close(kvm->timerfd);

tcsetattr(0, TCSAFLUSH, &save);
free(pollfds);
return err;
}

Expand Down
9 changes: 6 additions & 3 deletions tools/perf/builtin-record.c
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
struct record_opts *opts = &rec->opts;
struct perf_data_file *file = &rec->file;
struct perf_session *session;
bool disabled = false;
bool disabled = false, draining = false;

rec->progname = argv[0];

Expand Down Expand Up @@ -457,16 +457,19 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
}

if (hits == rec->samples) {
if (done)
if (done || draining)
break;
err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1);
err = perf_evlist__poll(rec->evlist, -1);
/*
* Propagate error, only if there's any. Ignore positive
* number of returned events and interrupt error.
*/
if (err > 0 || (err < 0 && errno == EINTR))
err = 0;
waking++;

if (perf_evlist__filter_pollfd(rec->evlist, POLLERR | POLLHUP) == 0)
draining = true;
}

/*
Expand Down
4 changes: 2 additions & 2 deletions tools/perf/builtin-top.c
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,7 @@ static int __cmd_top(struct perf_top *top)
perf_evlist__enable(top->evlist);

/* Wait for a minimal set of events before starting the snapshot */
poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
perf_evlist__poll(top->evlist, 100);

perf_top__mmap_read(top);

Expand All @@ -991,7 +991,7 @@ static int __cmd_top(struct perf_top *top)
perf_top__mmap_read(top);

if (hits == top->samples)
ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
ret = perf_evlist__poll(top->evlist, 100);
}

ret = 0;
Expand Down
7 changes: 6 additions & 1 deletion tools/perf/builtin-trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -2044,6 +2044,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
int err = -1, i;
unsigned long before;
const bool forks = argc > 0;
bool draining = false;
char sbuf[STRERR_BUFSIZE];

trace->live = true;
Expand Down Expand Up @@ -2171,8 +2172,12 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
if (trace->nr_events == before) {
int timeout = done ? 100 : -1;

if (poll(evlist->pollfd, evlist->nr_fds, timeout) > 0)
if (!draining && perf_evlist__poll(evlist, timeout) > 0) {
if (perf_evlist__filter_pollfd(evlist, POLLERR | POLLHUP) == 0)
draining = true;

goto again;
}
} else {
goto again;
}
Expand Down
8 changes: 8 additions & 0 deletions tools/perf/tests/builtin-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,14 @@ static struct test {
.desc = "Test tracking with sched_switch",
.func = test__switch_tracking,
},
{
.desc = "Filter fds with revents mask in a fdarray",
.func = test__fdarray__filter,
},
{
.desc = "Add fd to a fdarray, making it autogrow",
.func = test__fdarray__add,
},
{
.func = NULL,
},
Expand Down
Loading

0 comments on commit cf8102f

Please sign in to comment.