[lttng-dev] [PATCH lttng-tools] Test for select, poll and epoll syscall overrides
Julien Desfossez
jdesfossez at efficios.com
Thu May 12 04:17:58 UTC 2016
This test for root_regression checks if the syscall overrides for
select, pselect6, poll, ppoll, epoll_ctl, epoll_wait and epoll_pwait
work as expected on arm and x86 (32 and 64-bit).
There are 11 test cases that check for normal and abnormal behaviour. If
the test system has the Babeltrace python bindings, the test validates
the content of the events, otherwise only the presence of the generated
events is checked.
We also check if kernel OOPS, WARNING or BUG were generated during the
test.
Signed-off-by: Julien Desfossez <jdesfossez at efficios.com>
---
tests/regression/kernel/Makefile.am | 7 +-
tests/regression/kernel/select_poll_epoll.c | 872 +++++++++++++++++++++
tests/regression/kernel/test_select_poll_epoll | 420 ++++++++++
.../kernel/validate_select_poll_epoll.py | 783 ++++++++++++++++++
tests/root_regression | 1 +
5 files changed, 2082 insertions(+), 1 deletion(-)
create mode 100644 tests/regression/kernel/select_poll_epoll.c
create mode 100755 tests/regression/kernel/test_select_poll_epoll
create mode 100755 tests/regression/kernel/validate_select_poll_epoll.py
diff --git a/tests/regression/kernel/Makefile.am b/tests/regression/kernel/Makefile.am
index 36ff6ee..023c4c6 100644
--- a/tests/regression/kernel/Makefile.am
+++ b/tests/regression/kernel/Makefile.am
@@ -1,4 +1,9 @@
-EXTRA_DIST = test_event_basic test_all_events test_syscall
+noinst_PROGRAMS = select_poll_epoll
+select_poll_epoll_SOURCES = select_poll_epoll.c
+select_poll_epoll_LDADD = -lpthread -lpopt
+select_poll_epoll_CFLAGS = -fno-stack-protector -D_FORTIFY_SOURCE=0
+
+EXTRA_DIST = test_event_basic test_all_events test_syscall test_select_poll_epoll
all-local:
@if [ x"$(srcdir)" != x"$(builddir)" ]; then \
diff --git a/tests/regression/kernel/select_poll_epoll.c b/tests/regression/kernel/select_poll_epoll.c
new file mode 100644
index 0000000..9a46191
--- /dev/null
+++ b/tests/regression/kernel/select_poll_epoll.c
@@ -0,0 +1,872 @@
+#include <stdio.h>
+#include <poll.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/select.h>
+#include <sys/epoll.h>
+#include <popt.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <limits.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <time.h>
+
+#define BUF_SIZE 256
+#define NB_FD 1
+#define MAX_FDS 2047
+#define NR_ITER 1000 /* for stress-tests */
+
+static int timeout; /* seconds, -1 to disable */
+static int stop_thread;
+static int wait_fd;
+
+struct ppoll_thread_data {
+ struct pollfd *ufds;
+ int value;
+};
+
+void test_select_big()
+{
+ fd_set rfds, wfds, exfds;
+ struct timeval tv;
+ int ret;
+ int fd2;
+ char buf[BUF_SIZE];
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&exfds);
+
+ fd2 = dup2(wait_fd, 1022);
+ FD_SET(fd2, &rfds);
+
+ tv.tv_sec = 0;
+ tv.tv_usec = timeout * 1000;
+
+ if (timeout > 0)
+ ret = select(fd2 + 1, &rfds, &wfds, &exfds, &tv);
+ else
+ ret = select(fd2 + 1, &rfds, &wfds, &exfds, NULL);
+
+ if (ret == -1) {
+ perror("select()");
+ } else if (ret) {
+ printf("# [select] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[select] read");
+ }
+ } else {
+ printf("# [select] timeout\n");
+ }
+}
+
+void test_pselect()
+{
+ fd_set rfds;
+ struct timespec tv;
+ int ret;
+ char buf[BUF_SIZE];
+
+ FD_ZERO(&rfds);
+ FD_SET(wait_fd, &rfds);
+
+ tv.tv_sec = 0;
+ tv.tv_nsec = 1000 * 1000* timeout;
+
+ if (timeout > 0)
+ ret = pselect(1, &rfds, NULL, NULL, &tv, NULL);
+ else
+ ret = pselect(1, &rfds, NULL, NULL, NULL, NULL);
+
+ if (ret == -1) {
+ perror("pselect()");
+ } else if (ret) {
+ printf("# [pselect] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[pselect] read");
+ }
+ } else {
+ printf("# [pselect] timeout\n");
+ }
+
+}
+
+void test_select()
+{
+ fd_set rfds;
+ struct timeval tv;
+ int ret;
+ char buf[BUF_SIZE];
+
+ FD_ZERO(&rfds);
+ FD_SET(wait_fd, &rfds);
+
+ tv.tv_sec = 0;
+ tv.tv_usec = timeout * 1000;
+
+ if (timeout > 0)
+ ret = select(1, &rfds, NULL, NULL, &tv);
+ else
+ ret = select(1, &rfds, NULL, NULL, NULL);
+
+ if (ret == -1) {
+ perror("select()");
+ } else if (ret) {
+ printf("# [select] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[select] read");
+ }
+ } else {
+ printf("# [select] timeout\n");
+ }
+
+}
+
+void test_poll()
+{
+ struct pollfd ufds[NB_FD];
+ char buf[BUF_SIZE];
+ int ret;
+
+ ufds[0].fd = wait_fd;
+ ufds[0].events = POLLIN|POLLPRI;
+
+ ret = poll(ufds, 1, timeout);
+
+ if (ret < 0) {
+ perror("poll");
+ } else if (ret > 0) {
+ printf("# [poll] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[poll] read");
+ }
+ } else {
+ printf("# [poll] timeout\n");
+ }
+
+ return;
+}
+
+void test_ppoll()
+{
+ struct pollfd ufds[NB_FD];
+ char buf[BUF_SIZE];
+ int ret;
+ struct timespec ts;
+
+ ufds[0].fd = wait_fd;
+ ufds[0].events = POLLIN|POLLPRI;
+
+ if (timeout > 0) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = timeout * 1000 * 1000;
+ ret = ppoll(ufds, 1, &ts, NULL);
+ } else {
+ ret = ppoll(ufds, 1, NULL, NULL);
+ }
+
+
+ if (ret < 0) {
+ perror("ppoll");
+ } else if (ret > 0) {
+ printf("# [ppoll] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[ppoll] read");
+ }
+ } else {
+ printf("# [ppoll] timeout\n");
+ }
+
+ return;
+}
+
+void test_ppoll_big()
+{
+ struct pollfd ufds[MAX_FDS];
+ char buf[BUF_SIZE];
+ int ret, i, fds[MAX_FDS];
+
+ for (i = 0; i < MAX_FDS; i++) {
+ fds[i] = dup(wait_fd);
+ if (fds[i] < 0)
+ perror("dup");
+ ufds[i].fd = fds[i];
+ ufds[i].events = POLLIN|POLLPRI;
+ }
+
+ ret = ppoll(ufds, MAX_FDS, NULL, NULL);
+
+ if (ret < 0) {
+ perror("ppoll");
+ } else if (ret > 0) {
+ printf("# [ppoll] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[ppoll] read");
+ }
+ } else {
+ printf("# [ppoll] timeout\n");
+ }
+
+ for (i = 0; i < MAX_FDS; i++) {
+ ret = close(fds[i]);
+ if (ret != 0)
+ perror("close");
+ }
+
+ return;
+}
+
+void test_epoll()
+{
+ int ret, epollfd;
+ char buf[BUF_SIZE];
+ struct epoll_event epoll_event;
+
+ epollfd = epoll_create(NB_FD);
+ if (epollfd < 0) {
+ perror("[epoll] create");
+ goto end;
+ }
+
+ epoll_event.events = EPOLLIN | EPOLLPRI | EPOLLET;
+ epoll_event.data.fd = wait_fd;
+ ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, wait_fd, &epoll_event);
+ if (ret < 0) {
+ perror("[epoll] add");
+ goto end;
+ }
+
+ if (timeout > 0)
+ ret = epoll_wait(epollfd, &epoll_event, 1, timeout);
+ else
+ ret = epoll_wait(epollfd, &epoll_event, 1, -1);
+
+ if (ret == 1) {
+ printf("# [epoll] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[epoll] read");
+ }
+ } else if (ret == 0) {
+ printf("# [epoll] timeout\n");
+ } else {
+ perror("epoll_wait");
+ }
+
+end:
+ return;
+}
+
+void test_pepoll()
+{
+ int ret, epollfd;
+ char buf[BUF_SIZE];
+ struct epoll_event epoll_event;
+
+ epollfd = epoll_create(NB_FD);
+ if (epollfd < 0) {
+ perror("[eppoll] create");
+ goto end;
+ }
+
+ epoll_event.events = EPOLLIN | EPOLLPRI | EPOLLET;
+ epoll_event.data.fd = wait_fd;
+ ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, wait_fd, &epoll_event);
+ if (ret < 0) {
+ perror("[eppoll] add");
+ goto end;
+ }
+
+ if (timeout > 0)
+ ret = epoll_pwait(epollfd, &epoll_event, 1, timeout, NULL);
+ else
+ ret = epoll_pwait(epollfd, &epoll_event, 1, -1, NULL);
+
+ if (ret == 1) {
+ printf("# [eppoll] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[eppoll] read");
+ }
+ } else if (ret == 0) {
+ printf("# [eppoll] timeout\n");
+ } else {
+ perror("epoll_pwait");
+ }
+
+end:
+ return;
+}
+
+void should_work()
+{
+ test_select();
+ test_pselect();
+ test_select_big();
+ test_poll();
+ test_ppoll();
+ test_epoll();
+ test_pepoll();
+}
+
+/*
+ * Ask for 100 FDs in a buffer for allocated for only 1 FD, should
+ * segfault (eventually with a "*** stack smashing detected ***" message).
+ * The event should contain an array of 100 FDs filled with garbage.
+ */
+void ppoll_fds_buffer_overflow()
+{
+ struct pollfd ufds[NB_FD];
+ char buf[BUF_SIZE];
+ int ret;
+
+ ufds[0].fd = wait_fd;
+ ufds[0].events = POLLIN|POLLPRI;
+
+ ret = syscall(SYS_ppoll, ufds, 100, NULL, NULL);
+
+ if (ret < 0) {
+ perror("ppoll");
+ } else if (ret > 0) {
+ printf("# [ppoll] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[ppoll] read");
+ }
+ } else {
+ printf("# [ppoll] timeout\n");
+ }
+
+ return;
+}
+
+/*
+ * Ask for ULONG_MAX FDs in a buffer for allocated for only 1 FD, should
+ * cleanly fail with a "Invalid argument".
+ * The event should contain an empty array of FDs and overflow = 1.
+ */
+void ppoll_fds_ulong_max()
+{
+ struct pollfd ufds[NB_FD];
+ char buf[BUF_SIZE];
+ int ret;
+
+ ufds[0].fd = wait_fd;
+ ufds[0].events = POLLIN|POLLPRI;
+
+ ret = syscall(SYS_ppoll, ufds, ULONG_MAX, NULL, NULL);
+
+ if (ret < 0) {
+ perror("# ppoll");
+ } else if (ret > 0) {
+ printf("# [ppoll] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[ppoll] read");
+ }
+ } else {
+ printf("# [ppoll] timeout\n");
+ }
+
+ return;
+}
+
+/*
+ * Select is limited to 1024 FDs, should output a pselect event
+ * with 0 FDs.
+ */
+void pselect_fd_too_big()
+{
+ fd_set rfds;
+ int ret;
+ int fd2;
+ char buf[BUF_SIZE];
+
+ /*
+ * Test if nfds > 1024.
+ * Make sure ulimit is set correctly (ulimit -n 2048).
+ */
+ fd2 = dup2(wait_fd, 2047);
+ if (fd2 != 2047) {
+ perror("dup2");
+ return;
+ }
+ FD_ZERO(&rfds);
+ FD_SET(fd2, &rfds);
+
+ ret = syscall(SYS_pselect6, fd2 + 1, &rfds, NULL, NULL, NULL, NULL);
+
+ if (ret == -1) {
+ perror("# pselect()");
+ } else if (ret) {
+ printf("# [pselect] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[pselect] read");
+ }
+ } else {
+ printf("# [pselect] timeout\n");
+ }
+
+}
+
+/*
+ * Invalid pointer as writefds, should output a ppoll event
+ * with 0 FDs.
+ */
+void pselect_invalid_pointer()
+{
+ fd_set rfds;
+ int ret;
+ char buf[BUF_SIZE];
+ void *invalid = (void *) 0x42;
+
+ FD_ZERO(&rfds);
+ FD_SET(wait_fd, &rfds);
+
+ ret = syscall(SYS_pselect6, 1, &rfds, (fd_set *) invalid, NULL, NULL,
+ NULL);
+
+ if (ret == -1) {
+ perror("# pselect()");
+ } else if (ret) {
+ printf("# [pselect] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[pselect] read");
+ }
+ } else {
+ printf("# [pselect] timeout\n");
+ }
+
+}
+
+/*
+ * Pass an invalid pointer to epoll_pwait, should fail with
+ * "Bad address", the event returns 0 FDs.
+ */
+void epoll_pwait_invalid_pointer()
+{
+ int ret, epollfd;
+ char buf[BUF_SIZE];
+ struct epoll_event epoll_event;
+ void *invalid = (void *) 0x42;
+
+ epollfd = epoll_create(NB_FD);
+ if (epollfd < 0) {
+ perror("[eppoll] create");
+ goto end;
+ }
+
+ epoll_event.events = EPOLLIN | EPOLLPRI | EPOLLET;
+ epoll_event.data.fd = wait_fd;
+ ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, wait_fd, &epoll_event);
+ if (ret < 0) {
+ perror("[eppoll] add");
+ goto end;
+ }
+
+ ret = syscall(SYS_epoll_pwait, epollfd, (struct epoll_event *) invalid, 1, -1, NULL);
+
+ if (ret == 1) {
+ printf("# [eppoll] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[eppoll] read");
+ }
+ } else if (ret == 0) {
+ printf("# [eppoll] timeout\n");
+ } else {
+ perror("# epoll_pwait");
+ }
+
+end:
+ return;
+}
+
+/*
+ * Set maxevents to INT_MAX, should output "Invalid argument"
+ * The event should return an empty array.
+ */
+void epoll_pwait_int_max()
+{
+ int ret, epollfd;
+ char buf[BUF_SIZE];
+ struct epoll_event epoll_event;
+
+ epollfd = epoll_create(NB_FD);
+ if (epollfd < 0) {
+ perror("[eppoll] create");
+ goto end;
+ }
+
+ epoll_event.events = EPOLLIN | EPOLLPRI | EPOLLET;
+ epoll_event.data.fd = wait_fd;
+ ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, wait_fd, &epoll_event);
+ if (ret < 0) {
+ perror("[eppoll] add");
+ goto end;
+ }
+
+ ret = syscall(SYS_epoll_pwait, epollfd, &epoll_event, INT_MAX, -1,
+ NULL);
+
+ if (ret == 1) {
+ printf("# [eppoll] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[eppoll] read");
+ }
+ } else if (ret == 0) {
+ printf("# [eppoll] timeout\n");
+ } else {
+ perror("# epoll_pwait");
+ }
+
+end:
+ return;
+}
+
+void *ppoll_writer(void *arg)
+{
+ struct ppoll_thread_data *data = (struct ppoll_thread_data *) arg;
+
+ while (!stop_thread) {
+ memset(data->ufds, data->value,
+ MAX_FDS * sizeof(struct pollfd));
+ usleep(100);
+ }
+
+ return NULL;
+}
+
+void do_ppoll(int *fds, struct pollfd *ufds)
+{
+ int i, ret;
+ struct timespec ts;
+ char buf[BUF_SIZE];
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1 * 1000 * 1000;
+
+ for (i = 0; i < MAX_FDS; i++) {
+ ufds[i].fd = fds[i];
+ ufds[i].events = POLLIN|POLLPRI;
+ }
+
+ ret = ppoll(ufds, MAX_FDS, &ts, NULL);
+
+ if (ret < 0) {
+ perror("ppoll");
+ } else if (ret > 0) {
+ printf("# [ppoll] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[ppoll] read");
+ }
+ } else {
+ printf("# [ppoll] timeout\n");
+ }
+}
+
+void stress_ppoll(int *fds, int value)
+{
+ pthread_t writer;
+ int iter;
+ struct ppoll_thread_data thread_data;
+ struct pollfd ufds[MAX_FDS];
+
+ thread_data.ufds = ufds;
+ thread_data.value = value;
+
+ stop_thread = 0;
+ pthread_create(&writer, NULL, &ppoll_writer, (void *) &thread_data);
+ for (iter = 0; iter < NR_ITER; iter++) {
+ do_ppoll(fds, ufds);
+ }
+ stop_thread = 1;
+ pthread_join(writer, NULL);
+}
+
+/*
+ * 3 rounds of NR_ITER iterations with concurrent updates of the pollfd
+ * structure:
+ * - memset to 0
+ * - memset to 1
+ * - memset to INT_MAX
+ * Waits for input, but also set a timeout in case the input FD is overwritten
+ * before entering in the syscall. We use MAX_FDS FDs (dup of stdin), so the
+ * resulting trace is big (20MB).
+ *
+ * ppoll should work as expected and the trace should be readable at the end.
+ */
+void ppoll_concurrent_write()
+{
+ int i, ret, fds[MAX_FDS];
+
+ for (i = 0; i < MAX_FDS; i++) {
+ fds[i] = dup(wait_fd);
+ if (fds[i] < 0)
+ perror("dup");
+ }
+
+ stress_ppoll(fds, 0);
+ stress_ppoll(fds, 1);
+ stress_ppoll(fds, INT_MAX);
+
+ for (i = 0; i < MAX_FDS; i++) {
+ ret = close(fds[i]);
+ if (ret != 0)
+ perror("close");
+ }
+
+ return;
+}
+
+void *epoll_pwait_writer(void *addr)
+{
+ srand(time(NULL));
+
+ while (!stop_thread) {
+ usleep(rand() % 30);
+ munmap(addr, MAX_FDS * sizeof(struct epoll_event));
+ }
+
+ return NULL;
+}
+
+/*
+ * epoll_pwait on MAX_FDS fds while a concurrent thread munmaps the
+ * buffer allocated for the returned data. This should randomly segfault.
+ * The trace should be readable and no kernel OOPS should occur.
+ */
+void epoll_pwait_concurrent_munmap()
+{
+ int ret, epollfd, i, fds[MAX_FDS];
+ char buf[BUF_SIZE];
+ struct epoll_event *epoll_event;
+ void *addr = NULL;
+ pthread_t writer;
+
+
+ epollfd = epoll_create(MAX_FDS);
+ if (epollfd < 0) {
+ perror("[eppoll] create");
+ goto end;
+ }
+
+ epoll_event = mmap(addr, MAX_FDS * sizeof(struct epoll_event),
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ if (epoll_event == MAP_FAILED) {
+ perror("mmap");
+ goto end;
+ }
+
+ for (i = 0; i < MAX_FDS; i++) {
+ fds[i] = dup(wait_fd);
+ if (fds[i] < 0)
+ perror("dup");
+ epoll_event[i].events = EPOLLIN | EPOLLPRI | EPOLLET;
+ epoll_event[i].data.fd = fds[i];
+ ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fds[i], epoll_event);
+ if (ret < 0) {
+ perror("[eppoll] add");
+ goto end_unmap;
+ }
+ }
+ stop_thread = 0;
+ pthread_create(&writer, NULL, &epoll_pwait_writer, (void *) epoll_event);
+
+ ret = epoll_pwait(epollfd, epoll_event, 1, 1, NULL);
+
+ if (ret == 1) {
+ printf("# [eppoll] data available\n");
+ ret = read(wait_fd, buf, BUF_SIZE);
+ if (ret < 0) {
+ perror("[eppoll] read");
+ }
+ } else if (ret == 0) {
+ printf("# [eppoll] timeout\n");
+ } else {
+ perror("# epoll_pwait");
+ }
+
+ stop_thread = 1;
+ pthread_join(writer, NULL);
+
+end_unmap:
+ for (i = 0; i < MAX_FDS; i++) {
+ ret = close(fds[i]);
+ if (ret != 0)
+ perror("close");
+ }
+
+ ret = munmap(addr, MAX_FDS * sizeof(struct epoll_event));
+ if (ret != 0)
+ perror("munmap");
+
+end:
+ return;
+}
+
+void usage(poptContext optCon, int exitcode, char *error, char *addl)
+{
+ poptPrintUsage(optCon, stderr, 0);
+ if (error)
+ fprintf(stderr, "%s: %s\n", error, addl);
+ exit(exitcode);
+}
+
+void print_list()
+{
+ fprintf(stderr, "Test list (-t X):\n");
+ fprintf(stderr, "\t1: Working cases for select, pselect6, poll, ppoll "
+ "and epoll, waiting for input\n");
+ fprintf(stderr, "\t2: Timeout cases (1ms) for select, pselect6, poll, "
+ "ppoll and epoll\n");
+ fprintf(stderr, "\t3: pselect with a FD > 1023\n");
+ fprintf(stderr, "\t4: ppoll with %d FDs\n", MAX_FDS);
+ fprintf(stderr, "\t5: ppoll buffer overflow, should segfault, waits "
+ "for input\n");
+ fprintf(stderr, "\t6: pselect with invalid pointer, waits for "
+ "input\n");
+ fprintf(stderr, "\t7: ppoll with ulong_max fds, waits for input\n");
+ fprintf(stderr, "\t8: epoll_pwait with invalid pointer, waits for "
+ "input\n");
+ fprintf(stderr, "\t9: epoll_pwait with maxevents set to INT_MAX, "
+ "waits for input\n");
+ fprintf(stderr, "\t10: ppoll with concurrent updates of the structure "
+ "from user-space, stress test (3000 iterations), "
+ "waits for input + timeout 1ms\n");
+ fprintf(stderr, "\t11: epoll_pwait with concurrent munmap of the buffer "
+ "from user-space, should randomly segfault, run "
+ "multiple times, waits for input + timeout 1ms\n");
+}
+
+int main(int argc, const char **argv)
+{
+ int c, ret, test = -1;
+ poptContext optCon;
+ struct rlimit open_lim;
+
+ struct poptOption optionsTable[] = {
+ { "test", 't', POPT_ARG_INT, &test, 0,
+ "Test to run", NULL },
+ { "list", 'l', 0, 0, 'l',
+ "List of tests (-t X)", NULL },
+ POPT_AUTOHELP
+ { NULL, 0, 0, NULL, 0 }
+ };
+
+ optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
+
+ if (argc < 2) {
+ poptPrintUsage(optCon, stderr, 0);
+ ret = -1;
+ goto end;
+ }
+
+ ret = 0;
+
+ while ((c = poptGetNextOpt(optCon)) >= 0) {
+ switch(c) {
+ case 'l':
+ print_list();
+ goto end;
+ }
+ }
+
+ open_lim.rlim_cur = MAX_FDS + 5;
+ open_lim.rlim_max = MAX_FDS + 5;
+
+ ret = setrlimit(RLIMIT_NOFILE, &open_lim);
+ if (ret < 0) {
+ perror("setrlimit");
+ goto end;
+ }
+
+ /*
+ * Some tests might segfault, but we need the getpid() to be output
+ * for the validation, disabling the buffering on stdout works.
+ */
+ setbuf(stdout, NULL);
+ printf("%d\n", getpid());
+
+ wait_fd = STDIN_FILENO;
+
+ switch(test) {
+ case 1:
+ timeout = -1;
+ should_work();
+ break;
+ case 2:
+ {
+ int pipe_fds[2];
+
+ /*
+ * When launched with the run.sh script, stdin randomly
+ * receives some garbage that make the timeout cases
+ * fail, use a dummy pipe for this test.
+ */
+ ret = pipe(pipe_fds);
+ if (ret != 0) {
+ perror("pipe");
+ goto end;
+ }
+ wait_fd = pipe_fds[0];
+ timeout = 1;
+ should_work();
+ break;
+ }
+ case 3:
+ pselect_fd_too_big();
+ break;
+ case 4:
+ test_ppoll_big();
+ break;
+ case 5:
+ ppoll_fds_buffer_overflow();
+ break;
+ case 6:
+ pselect_invalid_pointer();
+ break;
+ case 7:
+ ppoll_fds_ulong_max();
+ break;
+ case 8:
+ epoll_pwait_invalid_pointer();
+ break;
+ case 9:
+ epoll_pwait_int_max();
+ break;
+ case 10:
+ ppoll_concurrent_write();
+ break;
+ case 11:
+ epoll_pwait_concurrent_munmap();
+ break;
+ default:
+ poptPrintUsage(optCon, stderr, 0);
+ ret = -1;
+ break;
+ }
+
+end:
+ poptFreeContext(optCon);
+ return ret;
+}
diff --git a/tests/regression/kernel/test_select_poll_epoll b/tests/regression/kernel/test_select_poll_epoll
new file mode 100755
index 0000000..b361f19
--- /dev/null
+++ b/tests/regression/kernel/test_select_poll_epoll
@@ -0,0 +1,420 @@
+#!/bin/bash
+#
+# Copyright (C) - 2016 Julien Desfossez <jdesfossez at efficios.com>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License, version 2 only, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+TEST_DESC="Kernel tracer - select, poll and epoll payload extraction"
+
+CURDIR=$(dirname $0)/
+TESTDIR=$CURDIR/../..
+VALIDATE_SCRIPT="$CURDIR/validate_select_poll_epoll.py"
+NUM_TESTS=102
+
+# Only run this test on x86 and arm
+uname -m | grep -E "x86|arm" >/dev/null 2>&1
+if test $? != 0; then
+ exit 0
+fi
+
+DISABLE_VALIDATE=0
+# Babeltrace python bindings are required for the validation, but
+# it is not a mandatory dependancy of the project, so fail run the
+# without the content validation, at least we test that we are not
+# crashing the kernel.
+$VALIDATE_SCRIPT --help >/dev/null
+if test $? != 0; then
+ echo "# Failed to run the validation script, Babeltrace Python bindings might be missing"
+ DISABLE_VALIDATE=1
+fi
+
+LAST_WARNING=$(dmesg | grep " WARNING:" | cut -d' ' -f1 | tail -1)
+LAST_OOPS=$(dmesg | grep " OOPS:" | cut -d' ' -f1 | tail -1)
+LAST_BUG=$(dmesg | grep " BUG:" | cut -d' ' -f1 | tail -1)
+
+source $TESTDIR/utils/utils.sh
+
+function check_trace_content()
+{
+ if test $DISABLE_VALIDATE == 1; then
+ ok 0 "Validation skipped"
+ return
+ fi
+
+ $VALIDATE_SCRIPT $@
+ if test $? = 0; then
+ ok 0 "Validation success"
+ else
+ fail "Validation"
+ fi
+}
+
+function test_working_cases()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="syscall_payload"
+ SYSCALL_LIST="select,pselect6,poll,ppoll,epoll_ctl,epoll_wait,epoll_pwait"
+
+ diag "Working cases for select, pselect6, poll, ppoll and epoll, waiting for input"
+
+ create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_syscall_ok $SESSION_NAME $SYSCALL_LIST
+ add_context_kernel_ok $SESSION_NAME channel0 pid
+
+ start_lttng_tracing_ok
+ { out=$(yes | $CURDIR/select_poll_epoll -t 1); } 2>/dev/null
+ stop_lttng_tracing_ok
+ pid=$(echo $out | cut -d' ' -f1)
+
+ validate_trace "$SYSCALL_LIST" $TRACE_PATH
+ check_trace_content -t 1 -p $pid $TRACE_PATH
+
+ destroy_lttng_session_ok $SESSION_NAME
+
+ cp -r $TRACE_PATH test1
+ chown -R julien:julien test1
+# #rm -rf $TRACE_PATH
+}
+
+function test_timeout_cases()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="syscall_payload"
+ SYSCALL_LIST="select,pselect6,poll,ppoll,epoll_ctl,epoll_wait,epoll_pwait"
+
+ diag "Timeout cases (1ms) for select, pselect6, poll, ppoll and epoll"
+
+ create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_syscall_ok $SESSION_NAME "$SYSCALL_LIST"
+ add_context_kernel_ok $SESSION_NAME channel0 pid
+
+ start_lttng_tracing_ok
+ { out=$($CURDIR/select_poll_epoll -t 2); } 2>/dev/null
+ stop_lttng_tracing_ok
+ pid=$(echo $out | cut -d' ' -f1)
+
+ validate_trace "$SYSCALL_LIST" $TRACE_PATH
+ check_trace_content -t 2 -p $pid $TRACE_PATH 2>/dev/null
+
+ destroy_lttng_session_ok $SESSION_NAME
+
+ cp -r $TRACE_PATH test2
+ chown -R julien:julien test2
+ #rm -rf $TRACE_PATH
+}
+
+function test_big_pselect()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="syscall_payload"
+ SYSCALL_LIST="pselect6"
+
+ diag "pselect with a FD > 1023"
+
+ create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_syscall_ok $SESSION_NAME $SYSCALL_LIST
+ add_context_kernel_ok $SESSION_NAME channel0 pid
+
+ start_lttng_tracing_ok
+ { out=$($CURDIR/select_poll_epoll -t 3); } 2>/dev/null
+ stop_lttng_tracing_ok
+ pid=$(echo $out | cut -d' ' -f1)
+
+ validate_trace "$SYSCALL_LIST" $TRACE_PATH
+ check_trace_content -t 3 -p $pid $TRACE_PATH 2>/dev/null
+
+ destroy_lttng_session_ok $SESSION_NAME
+
+ cp -r $TRACE_PATH test3
+ chown -R julien:julien test3
+ #rm -rf $TRACE_PATH
+}
+
+function test_big_ppoll()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="syscall_payload"
+ SYSCALL_LIST="ppoll"
+
+ diag "ppoll with 2047 FDs"
+
+ create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_syscall_ok $SESSION_NAME $SYSCALL_LIST
+ add_context_kernel_ok $SESSION_NAME channel0 pid
+
+ start_lttng_tracing_ok
+ { out=$(yes | $CURDIR/select_poll_epoll -t 4); } 2>/dev/null
+ stop_lttng_tracing_ok
+ pid=$(echo $out | cut -d' ' -f1)
+
+ validate_trace "$SYSCALL_LIST" $TRACE_PATH
+ check_trace_content -t 4 -p $pid $TRACE_PATH 2>/dev/null
+
+ destroy_lttng_session_ok $SESSION_NAME
+
+ cp -r $TRACE_PATH test4
+ chown -R julien:julien test4
+ #rm -rf $TRACE_PATH
+}
+
+function test_ppoll_overflow()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="syscall_payload"
+ SYSCALL_LIST="ppoll"
+
+ diag "ppoll buffer overflow, should segfault, waits for input"
+
+ create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_syscall_ok $SESSION_NAME $SYSCALL_LIST
+ add_context_kernel_ok $SESSION_NAME channel0 pid
+
+ start_lttng_tracing_ok
+ diag "Expect segfaults"
+ { out=$(yes | $CURDIR/select_poll_epoll -t 5); } 2>/dev/null
+ stop_lttng_tracing_ok
+ echo $out
+ pid=$(echo $out | cut -d' ' -f1)
+
+ validate_trace "$SYSCALL_LIST" $TRACE_PATH
+
+ check_trace_content -t 5 -p $pid $TRACE_PATH 2>/dev/null
+
+ destroy_lttng_session_ok $SESSION_NAME
+
+ cp -r $TRACE_PATH test5
+ chown -R julien:julien test5
+ #rm -rf $TRACE_PATH
+}
+
+function test_pselect_invalid_ptr()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="syscall_payload"
+ SYSCALL_LIST="pselect6"
+
+ diag "pselect with invalid pointer, waits for input"
+
+ create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_syscall_ok $SESSION_NAME $SYSCALL_LIST
+ add_context_kernel_ok $SESSION_NAME channel0 pid
+
+ start_lttng_tracing_ok
+ { out=$(yes | $CURDIR/select_poll_epoll -t 6); } 2>/dev/null
+ stop_lttng_tracing_ok
+ pid=$(echo $out | cut -d' ' -f1)
+
+ validate_trace "$SYSCALL_LIST" $TRACE_PATH
+ check_trace_content -t 6 -p $pid $TRACE_PATH 2>/dev/null
+
+ destroy_lttng_session_ok $SESSION_NAME
+
+ cp -r $TRACE_PATH test6
+ chown -R julien:julien test6
+ #rm -rf $TRACE_PATH
+}
+
+function test_ppoll_ulong_max()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="syscall_payload"
+ SYSCALL_LIST="ppoll"
+
+ diag "ppoll with ulong_max fds, waits for input"
+
+ create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_syscall_ok $SESSION_NAME $SYSCALL_LIST
+ add_context_kernel_ok $SESSION_NAME channel0 pid
+
+ start_lttng_tracing_ok
+ { out=$(yes | $CURDIR/select_poll_epoll -t 7); } 2>/dev/null
+ stop_lttng_tracing_ok
+ pid=$(echo $out | cut -d' ' -f1)
+
+ validate_trace "$SYSCALL_LIST" $TRACE_PATH
+ check_trace_content -t 7 -p $pid $TRACE_PATH 2>/dev/null
+
+ destroy_lttng_session_ok $SESSION_NAME
+
+ cp -r $TRACE_PATH test7
+ chown -R julien:julien test7
+ #rm -rf $TRACE_PATH
+}
+
+function test_epoll_pwait_invalid_ptr()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="syscall_payload"
+ SYSCALL_LIST="epoll_pwait"
+
+ diag "epoll_pwait with invalid pointer, waits for input"
+
+ create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_syscall_ok $SESSION_NAME $SYSCALL_LIST
+ add_context_kernel_ok $SESSION_NAME channel0 pid
+
+ start_lttng_tracing_ok
+ { out=$(yes | $CURDIR/select_poll_epoll -t 8); } 2>/dev/null
+ stop_lttng_tracing_ok
+ pid=$(echo $out | cut -d' ' -f1)
+
+ validate_trace "$SYSCALL_LIST" $TRACE_PATH
+ check_trace_content -t 8 -p $pid $TRACE_PATH 2>/dev/null
+
+ destroy_lttng_session_ok $SESSION_NAME
+
+ cp -r $TRACE_PATH test8
+ chown -R julien:julien test8
+ #rm -rf $TRACE_PATH
+}
+
+function test_epoll_pwait_int_max()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="syscall_payload"
+ SYSCALL_LIST="epoll_pwait"
+
+ diag "epoll_pwait with maxevents set to INT_MAX, waits for input"
+
+ create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_syscall_ok $SESSION_NAME $SYSCALL_LIST
+ add_context_kernel_ok $SESSION_NAME channel0 pid
+
+ start_lttng_tracing_ok
+ { out=$(yes | $CURDIR/select_poll_epoll -t 9); } 2>/dev/null
+ stop_lttng_tracing_ok
+ pid=$(echo $out | cut -d' ' -f1)
+
+ validate_trace "$SYSCALL_LIST" $TRACE_PATH
+ check_trace_content -t 9 -p $pid $TRACE_PATH 2>/dev/null
+
+ destroy_lttng_session_ok $SESSION_NAME
+
+ cp -r $TRACE_PATH test9
+ chown -R julien:julien test9
+ #rm -rf $TRACE_PATH
+}
+
+function test_ppoll_concurrent()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="syscall_payload"
+ SYSCALL_LIST="ppoll"
+
+ diag "ppoll with concurrent updates of the structure from user-space, stress test (3000 iterations), waits for input + timeout 1ms"
+
+ create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_syscall_ok $SESSION_NAME $SYSCALL_LIST
+ add_context_kernel_ok $SESSION_NAME channel0 pid
+
+ start_lttng_tracing_ok
+ { out=$(yes | $CURDIR/select_poll_epoll -t 10); } 2>/dev/null
+ stop_lttng_tracing_ok
+ pid=$(echo $out | cut -d' ' -f1)
+
+ validate_trace "$SYSCALL_LIST" $TRACE_PATH
+ check_trace_content -t 10 -p $pid $TRACE_PATH 2>/dev/null
+
+ destroy_lttng_session_ok $SESSION_NAME
+
+ cp -r $TRACE_PATH test10
+ chown -R julien:julien test10
+ #rm -rf $TRACE_PATH
+}
+
+function test_epoll_pwait_concurrent()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="syscall_payload"
+ SYSCALL_LIST="epoll_ctl,epoll_pwait"
+
+ diag "epoll_pwait with concurrent munmap of the buffer from user-space, should randomly segfault, run multiple times, waits for input + timeout 1ms"
+
+ create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+
+ lttng_enable_kernel_syscall_ok $SESSION_NAME $SYSCALL_LIST
+ add_context_kernel_ok $SESSION_NAME channel0 pid
+
+ start_lttng_tracing_ok
+ diag "Expect segfaults"
+ for i in $(seq 1 100); do
+ { out=$($CURDIR/select_poll_epoll -t 11); } 2>/dev/null
+ done
+ pid=$(echo $out | cut -d' ' -f1)
+ stop_lttng_tracing_ok
+
+ # epoll_wait is not always generated in the trace (stress test)
+ validate_trace "epoll_ctl" $TRACE_PATH
+ check_trace_content -t 11 -p $pid $TRACE_PATH 2>/dev/null
+
+ destroy_lttng_session_ok $SESSION_NAME
+
+ cp -r $TRACE_PATH test11
+ chown -R julien:julien test11
+ #rm -rf $TRACE_PATH
+}
+
+# MUST set TESTDIR before calling those functions
+plan_tests $NUM_TESTS
+
+print_test_banner "$TEST_DESC"
+
+if [ "$(id -u)" == "0" ]; then
+ isroot=1
+else
+ isroot=0
+fi
+
+skip $isroot "Root access is needed. Skipping all tests." $NUM_TESTS ||
+{
+ start_lttng_sessiond
+
+ test_working_cases
+ test_timeout_cases
+ test_big_pselect
+ test_big_ppoll
+ test_ppoll_overflow
+ test_pselect_invalid_ptr
+ test_ppoll_ulong_max
+ test_epoll_pwait_invalid_ptr
+ test_epoll_pwait_int_max
+ test_ppoll_concurrent
+ test_epoll_pwait_concurrent
+
+ stop_lttng_sessiond
+
+ NEW_WARNING=$(dmesg | grep " WARNING:" | cut -d' ' -f1 | tail -1)
+ NEW_OOPS=$(dmesg | grep " OOPS:" | cut -d' ' -f1 | tail -1)
+ NEW_BUG=$(dmesg | grep " BUG:" | cut -d' ' -f1 | tail -1)
+
+ if test "$LAST_WARNING" != "$NEW_WARNING"; then
+ fail "New WARNING generated"
+ fi
+ if test "$LAST_OOPS" != "$NEW_OOPS"; then
+ fail "New OOPS generated"
+ fi
+ if test "$LAST_BUG" != "$NEW_BUG"; then
+ fail "New BUG generated"
+ fi
+}
diff --git a/tests/regression/kernel/validate_select_poll_epoll.py b/tests/regression/kernel/validate_select_poll_epoll.py
new file mode 100755
index 0000000..37d4042
--- /dev/null
+++ b/tests/regression/kernel/validate_select_poll_epoll.py
@@ -0,0 +1,783 @@
+#!/usr/bin/env python3
+
+import sys
+import time
+import argparse
+
+NSEC_PER_SEC = 1000000000
+
+try:
+ from babeltrace import TraceCollection
+except ImportError:
+ # quick fix for debian-based distros
+ sys.path.append("/usr/local/lib/python%d.%d/site-packages" %
+ (sys.version_info.major, sys.version_info.minor))
+ from babeltrace import TraceCollection
+
+
+class TraceParser:
+ def __init__(self, trace, pid):
+ self.trace = trace
+ self.pid = pid
+ self.expect = {}
+
+ def ns_to_hour_nsec(self, ns):
+ d = time.localtime(ns/NSEC_PER_SEC)
+ return "%02d:%02d:%02d.%09d" % (d.tm_hour, d.tm_min, d.tm_sec,
+ ns % NSEC_PER_SEC)
+
+ def parse(self):
+ # iterate over all the events
+ for event in self.trace.events:
+ if self.pid is not None and event["pid"] != self.pid:
+ continue
+
+ method_name = "handle_%s" % event.name.replace(":", "_").replace("+", "_")
+ # call the function to handle each event individually
+ if hasattr(TraceParser, method_name):
+ func = getattr(TraceParser, method_name)
+ func(self, event)
+
+ ret = 0
+ for i in self.expect.keys():
+ if self.expect[i] == 0:
+ print("%s not validated" % i)
+ ret = 1
+
+ return ret
+
+ ### epoll_ctl
+ def handle_compat_syscall_entry_epoll_ctl(self, event):
+ self.epoll_ctl_entry(event)
+
+ def handle_compat_syscall_exit_epoll_ctl(self, event):
+ self.epoll_ctl_exit(event)
+
+ def handle_syscall_entry_epoll_ctl(self, event):
+ self.epoll_ctl_entry(event)
+
+ def handle_syscall_exit_epoll_ctl(self, event):
+ self.epoll_ctl_exit(event)
+
+ def epoll_ctl_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ epfd = event["epfd"]
+ op_enum = event["op_enum"]
+ fd = event["fd"]
+ _event = event["event"]
+
+ def epoll_ctl_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+
+ ### epoll_wait + epoll_pwait
+ def handle_compat_syscall_entry_epoll_wait(self, event):
+ self.epoll_wait_entry(event)
+
+ def handle_compat_syscall_exit_epoll_wait(self, event):
+ self.epoll_wait_exit(event)
+
+ def handle_syscall_entry_epoll_wait(self, event):
+ self.epoll_wait_entry(event)
+
+ def handle_syscall_exit_epoll_wait(self, event):
+ self.epoll_wait_exit(event)
+
+ def handle_compat_syscall_entry_epoll_pwait(self, event):
+ self.epoll_wait_entry(event)
+
+ def handle_compat_syscall_exit_epoll_pwait(self, event):
+ self.epoll_wait_exit(event)
+
+ def handle_syscall_entry_epoll_pwait(self, event):
+ self.epoll_wait_entry(event)
+
+ def handle_syscall_exit_epoll_pwait(self, event):
+ self.epoll_wait_exit(event)
+
+ def epoll_wait_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ epfd = event["epfd"]
+ maxevents = event["maxevents"]
+ timeout = event["timeout"]
+
+ def epoll_wait_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ #### poll + ppoll
+ def handle_compat_syscall_entry_poll(self, event):
+ self.poll_entry(event)
+
+ def handle_compat_syscall_exit_poll(self, event):
+ self.poll_exit(event)
+
+ def handle_syscall_entry_poll(self, event):
+ self.poll_entry(event)
+
+ def handle_syscall_exit_poll(self, event):
+ self.poll_exit(event)
+
+ def handle_compat_syscall_entry_ppoll(self, event):
+ self.poll_entry(event)
+
+ def handle_compat_syscall_exit_ppoll(self, event):
+ self.poll_exit(event)
+
+ def handle_syscall_entry_ppoll(self, event):
+ self.poll_entry(event)
+
+ def handle_syscall_exit_ppoll(self, event):
+ self.poll_exit(event)
+
+ def poll_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ nfds = event["nfds"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ def poll_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ nfds = event["nfds"]
+ fds_length = event["fds_length"]
+ fds = event["fds"]
+
+ ### epoll_create
+ def handle_compat_syscall_entry_epoll_create1(self, event):
+ self.epoll_create_entry(event)
+
+ def handle_compat_syscall_exit_epoll_create1(self, event):
+ self.epoll_create_exit(event)
+
+ def handle_compat_syscall_entry_epoll_create(self, event):
+ self.epoll_create_entry(event)
+
+ def handle_compat_syscall_exit_epoll_create(self, event):
+ self.epoll_create_exit(event)
+
+ def handle_syscall_entry_epoll_create1(self, event):
+ self.epoll_create_entry(event)
+
+ def handle_syscall_exit_epoll_create1(self, event):
+ self.epoll_create_exit(event)
+
+ def handle_syscall_entry_epoll_create(self, event):
+ self.epoll_create_entry(event)
+
+ def handle_syscall_exit_epoll_create(self, event):
+ self.epoll_create_exit(event)
+
+ def handle_compat_syscall_entry_epoll_create1(self, event):
+ self.epoll_create_entry(event)
+
+ def handle_compat_syscall_exit_epoll_create(self, event):
+ self.epoll_create_exit(event)
+
+ def epoll_create_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ flags = event["flags"]
+
+ def epoll_create_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+
+ ### select + pselect6
+ def handle_syscall_entry_pselect6(self, event):
+ self.select_entry(event)
+
+ def handle_syscall_exit_pselect6(self, event):
+ self.select_exit(event)
+
+ def handle_compat_syscall_entry_pselect6(self, event):
+ self.select_entry(event)
+
+ def handle_compat_syscall_exit_pselect6(self, event):
+ self.select_exit(event)
+
+ def handle_syscall_entry_select(self, event):
+ self.select_entry(event)
+
+ def handle_syscall_exit_select(self, event):
+ self.select_exit(event)
+
+ def handle_compat_syscall_entry_select(self, event):
+ self.select_entry(event)
+
+ def handle_compat_syscall_exit_select(self, event):
+ self.select_exit(event)
+
+ def select_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ n = event["n"]
+ overflow = event["overflow"]
+ tvp = event["tvp"]
+ _readfds_length = event["_readfds_length"]
+ readfds = event["readfds"]
+ _writefds_length = event["_writefds_length"]
+ writefds = event["writefds"]
+ _exceptfds_length = event["_exceptfds_length"]
+ exceptfds = event["exceptfds"]
+
+ def select_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ overflow = event["overflow"]
+ tvp = event["tvp"]
+ _readfds_length = event["_readfds_length"]
+ readfds = event["readfds"]
+ _writefds_length = event["_writefds_length"]
+ writefds = event["writefds"]
+ _exceptfds_length = event["_exceptfds_length"]
+ exceptfds = event["exceptfds"]
+
+
+class Test1(TraceParser):
+ def __init__(self, trace, pid):
+ super().__init__(trace, pid)
+ self.expect["select_in_fd0"] = 0
+ self.expect["select_in_fd1023"] = 0
+ self.expect["select_out_fd0"] = 0
+ self.expect["select_out_fd1023"] = 0
+ self.expect["poll_in_nfds1"] = 0
+ self.expect["poll_out_nfds1"] = 0
+ self.expect["epoll_ctl_in_add"] = 0
+ self.expect["epoll_ctl_out_ok"] = 0
+ self.expect["epoll_wait_in_ok"] = 0
+ self.expect["epoll_wait_out_fd0"] = 0
+
+ def select_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ n = event["n"]
+ overflow = event["overflow"]
+ tvp = event["tvp"]
+ _readfds_length = event["_readfds_length"]
+ readfds = event["readfds"]
+ _writefds_length = event["_writefds_length"]
+ writefds = event["writefds"]
+ _exceptfds_length = event["_exceptfds_length"]
+ exceptfds = event["exceptfds"]
+
+ if n == 1 and readfds[0] == 1:
+ self.expect["select_in_fd0"] = 1
+ if n == 1023:
+ if readfds[127] == 0x40 and writefds[127] == 0 and \
+ exceptfds[127] == 0 and overflow == 0:
+ self.expect["select_in_fd1023"] = 1
+
+ def select_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ overflow = event["overflow"]
+ tvp = event["tvp"]
+ _readfds_length = event["_readfds_length"]
+ readfds = event["readfds"]
+ _writefds_length = event["_writefds_length"]
+ writefds = event["writefds"]
+ _exceptfds_length = event["_exceptfds_length"]
+ exceptfds = event["exceptfds"]
+
+ if ret == 1:
+ if readfds[0] == 1:
+ self.expect["select_out_fd0"] = 1
+ if _readfds_length == 128 and readfds[127] == 0x40 and \
+ writefds[127] == 0 and exceptfds[127] == 0 and tvp == 0:
+ self.expect["select_out_fd1023"] = 1
+
+ def poll_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ nfds = event["nfds"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ if nfds == 1 and fds_length == 1 and fds[0]["raw_events"] == 0x3 \
+ and fds[0]["events"]["POLLIN"] == 1 and \
+ fds[0]["events"]["padding"] == 0:
+ self.expect["poll_in_nfds1"] = 1
+
+ def poll_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ nfds = event["nfds"]
+ fds_length = event["fds_length"]
+ fds = event["fds"]
+
+ if ret == 1 and fds_length == 1 and fds[0]["raw_events"] == 0x1 \
+ and fds[0]["events"]["POLLIN"] == 1 and \
+ fds[0]["events"]["padding"] == 0:
+ self.expect["poll_out_nfds1"] = 1
+
+ def epoll_ctl_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ epfd = event["epfd"]
+ op_enum = event["op_enum"]
+ fd = event["fd"]
+ _event = event["event"]
+
+ if epfd == 3 and op_enum == "EPOLL_CTL_ADD" and fd == 0 and \
+ _event["data_union"]["fd"] == 0 and \
+ _event["events"]["EPOLLIN"] == 1 and \
+ _event["events"]["EPOLLPRI"] == 1:
+ self.expect["epoll_ctl_in_add"] = 1
+
+ def epoll_ctl_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+
+ if ret == 0:
+ self.expect["epoll_ctl_out_ok"] = 1
+
+ def epoll_wait_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ epfd = event["epfd"]
+ maxevents = event["maxevents"]
+ timeout = event["timeout"]
+
+ if epfd == 3 and maxevents == 1 and timeout == -1:
+ self.expect["epoll_wait_in_ok"] = 1
+
+
+ def epoll_wait_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ if ret == 1 and fds_length == 1 and overflow == 0 and \
+ fds[0]["data_union"]["fd"] == 0 and \
+ fds[0]["events"]["EPOLLIN"] == 1:
+ self.expect["epoll_wait_out_fd0"] = 1
+
+
+class Test2(TraceParser):
+ def __init__(self, trace, pid):
+ super().__init__(trace, pid)
+ self.expect["select_timeout_in_fd0"] = 0
+ self.expect["select_timeout_in_fd1023"] = 0
+ self.expect["select_timeout_out"] = 0
+ self.expect["poll_timeout_in"] = 0
+ self.expect["poll_timeout_out"] = 0
+ self.expect["epoll_ctl_timeout_in_add"] = 0
+ self.expect["epoll_ctl_timeout_out_ok"] = 0
+ self.expect["epoll_wait_timeout_in"] = 0
+ self.expect["epoll_wait_timeout_out"] = 0
+
+ def select_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ n = event["n"]
+ overflow = event["overflow"]
+ tvp = event["tvp"]
+ _readfds_length = event["_readfds_length"]
+ readfds = event["readfds"]
+ _writefds_length = event["_writefds_length"]
+ writefds = event["writefds"]
+ _exceptfds_length = event["_exceptfds_length"]
+ exceptfds = event["exceptfds"]
+
+ if n == 1 and tvp != 0:
+ self.expect["select_timeout_in_fd0"] = 1
+ if n == 1023:
+ if readfds[127] == 0x40 and writefds[127] == 0 and \
+ exceptfds[127] == 0 and tvp != 0:
+ self.expect["select_timeout_in_fd1023"] = 1
+
+ def select_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ overflow = event["overflow"]
+ tvp = event["tvp"]
+ _readfds_length = event["_readfds_length"]
+ readfds = event["readfds"]
+ _writefds_length = event["_writefds_length"]
+ writefds = event["writefds"]
+ _exceptfds_length = event["_exceptfds_length"]
+ exceptfds = event["exceptfds"]
+
+ if ret == 0 and tvp != 0:
+ self.expect["select_timeout_out"] = 1
+
+ def poll_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ nfds = event["nfds"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ if nfds == 1 and fds_length == 1 and fds[0]["raw_events"] == 0x3 \
+ and fds[0]["events"]["POLLIN"] == 1 and \
+ fds[0]["events"]["padding"] == 0:
+ self.expect["poll_timeout_in"] = 1
+
+ def poll_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ nfds = event["nfds"]
+ fds_length = event["fds_length"]
+ fds = event["fds"]
+
+ if ret == 0 and nfds == 1 and fds_length == 0:
+ self.expect["poll_timeout_out"] = 1
+
+ def epoll_ctl_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ epfd = event["epfd"]
+ op_enum = event["op_enum"]
+ fd = event["fd"]
+ _event = event["event"]
+
+ if op_enum == "EPOLL_CTL_ADD" and \
+ _event["events"]["EPOLLIN"] == 1 and \
+ _event["events"]["EPOLLPRI"] == 1:
+ self.expect["epoll_ctl_timeout_in_add"] = 1
+
+ def epoll_ctl_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+
+ if ret == 0:
+ self.expect["epoll_ctl_timeout_out_ok"] = 1
+
+ def epoll_wait_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ epfd = event["epfd"]
+ maxevents = event["maxevents"]
+ timeout = event["timeout"]
+
+ if maxevents == 1 and timeout == 1:
+ self.expect["epoll_wait_timeout_in"] = 1
+
+
+ def epoll_wait_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ if ret == 0 and fds_length == 0 and overflow == 0:
+ self.expect["epoll_wait_timeout_out"] = 1
+
+
+class Test3(TraceParser):
+ def __init__(self, trace, pid):
+ super().__init__(trace, pid)
+ self.expect["select_too_big_in"] = 0
+ self.expect["select_too_big_out"] = 0
+
+ def select_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ n = event["n"]
+ overflow = event["overflow"]
+ tvp = event["tvp"]
+ _readfds_length = event["_readfds_length"]
+ readfds = event["readfds"]
+ _writefds_length = event["_writefds_length"]
+ writefds = event["writefds"]
+ _exceptfds_length = event["_exceptfds_length"]
+ exceptfds = event["exceptfds"]
+
+ if n == 2048 and overflow == 0 and _readfds_length == 0:
+ self.expect["select_too_big_in"] = 1
+
+ def select_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ overflow = event["overflow"]
+ tvp = event["tvp"]
+ _readfds_length = event["_readfds_length"]
+ readfds = event["readfds"]
+ _writefds_length = event["_writefds_length"]
+ writefds = event["writefds"]
+ _exceptfds_length = event["_exceptfds_length"]
+ exceptfds = event["exceptfds"]
+
+ if ret == -9 and overflow == 0 and _readfds_length == 0:
+ self.expect["select_too_big_out"] = 1
+
+
+class Test4(TraceParser):
+ def __init__(self, trace, pid):
+ super().__init__(trace, pid)
+ self.expect["big_poll_in"] = 0
+ self.expect["big_poll_out"] = 0
+
+ def poll_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ nfds = event["nfds"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ if nfds == 2047 and fds_length == 512 and overflow == 1 and \
+ fds[0]["raw_events"] == 0x3 \
+ and fds[0]["events"]["POLLIN"] == 1 and \
+ fds[0]["events"]["padding"] == 0 and \
+ fds[511]["events"]["POLLIN"] == 1 and \
+ fds[511]["events"]["POLLPRI"] == 1:
+ self.expect["big_poll_in"] = 1
+
+ def poll_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ nfds = event["nfds"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ if ret == 2047 and nfds == 2047 and fds_length == 512 and \
+ overflow == 1 and fds[0]["events"]["POLLIN"] == 1 and \
+ fds[511]["events"]["POLLIN"] == 1:
+ self.expect["big_poll_out"] = 1
+
+
+class Test5(TraceParser):
+ def __init__(self, trace, pid):
+ super().__init__(trace, pid)
+ self.expect["poll_overflow_in"] = 0
+ self.expect["poll_overflow_out"] = 0
+
+ def poll_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ nfds = event["nfds"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ if nfds == 100 and fds_length == 100 and overflow == 0 and \
+ fds[0]["events"]["POLLIN"] == 1:
+ self.expect["poll_overflow_in"] = 1
+
+ def poll_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ nfds = event["nfds"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ if nfds == 100 and overflow == 0:
+ self.expect["poll_overflow_out"] = 1
+
+
+class Test6(TraceParser):
+ def __init__(self, trace, pid):
+ super().__init__(trace, pid)
+ self.expect["pselect_invalid_in"] = 0
+ self.expect["pselect_invalid_out"] = 0
+
+ def select_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ n = event["n"]
+ overflow = event["overflow"]
+ tvp = event["tvp"]
+ _readfds_length = event["_readfds_length"]
+ readfds = event["readfds"]
+ _writefds_length = event["_writefds_length"]
+ writefds = event["writefds"]
+ _exceptfds_length = event["_exceptfds_length"]
+ exceptfds = event["exceptfds"]
+
+ if n == 1 and overflow == 0 and _readfds_length == 0:
+ self.expect["pselect_invalid_in"] = 1
+
+ def select_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ overflow = event["overflow"]
+ tvp = event["tvp"]
+ _readfds_length = event["_readfds_length"]
+ readfds = event["readfds"]
+ _writefds_length = event["_writefds_length"]
+ writefds = event["writefds"]
+ _exceptfds_length = event["_exceptfds_length"]
+ exceptfds = event["exceptfds"]
+
+ if ret == -14 and overflow == 0 and _readfds_length == 0:
+ self.expect["pselect_invalid_out"] = 1
+
+
+class Test7(TraceParser):
+ def __init__(self, trace, pid):
+ super().__init__(trace, pid)
+ self.expect["poll_max_in"] = 0
+ self.expect["poll_max_out"] = 0
+
+ def poll_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ nfds = event["nfds"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ if nfds == 4294967295 and overflow == 1:
+ self.expect["poll_max_in"] = 1
+
+ def poll_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ nfds = event["nfds"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ if ret == -22 and nfds == 4294967295 and overflow == 0:
+ self.expect["poll_max_out"] = 1
+
+
+class Test8(TraceParser):
+ def __init__(self, trace, pid):
+ super().__init__(trace, pid)
+ self.expect["epoll_wait_invalid_in"] = 0
+ self.expect["epoll_wait_invalid_out"] = 0
+
+ def epoll_wait_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ epfd = event["epfd"]
+ maxevents = event["maxevents"]
+ timeout = event["timeout"]
+
+ if epfd == 3 and maxevents == 1 and timeout == -1:
+ self.expect["epoll_wait_invalid_in"] = 1
+
+
+ def epoll_wait_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ if ret == -14 and fds_length == 0 and overflow == 0:
+ self.expect["epoll_wait_invalid_out"] = 1
+
+
+class Test9(TraceParser):
+ def __init__(self, trace, pid):
+ super().__init__(trace, pid)
+ self.expect["epoll_wait_max_in"] = 0
+ self.expect["epoll_wait_max_out"] = 0
+
+ def epoll_wait_entry(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ epfd = event["epfd"]
+ maxevents = event["maxevents"]
+ timeout = event["timeout"]
+
+ if epfd == 3 and maxevents == 2147483647 and timeout == -1:
+ self.expect["epoll_wait_max_in"] = 1
+
+
+ def epoll_wait_exit(self, event):
+ timestamp = event.timestamp
+ cpu_id = event["cpu_id"]
+ ret = event["ret"]
+ fds_length = event["fds_length"]
+ overflow = event["overflow"]
+ fds = event["fds"]
+
+ if ret == -22 and fds_length == 0 and overflow == 0:
+ self.expect["epoll_wait_max_out"] = 1
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description='Trace parser')
+ parser.add_argument('path', metavar="<path/to/trace>", help='Trace path')
+ parser.add_argument('-t', '--test', type=int, help='Test to validate')
+ parser.add_argument('-p', '--pid', type=int, help='PID of the app')
+ args = parser.parse_args()
+
+ if not args.test:
+ print("Need to pass a test to validate (-t)")
+ sys.exit(1)
+
+ if not args.pid:
+ print("Need to pass the PID to check (-p)")
+ sys.exit(1)
+
+ traces = TraceCollection()
+ handle = traces.add_traces_recursive(args.path, "ctf")
+ if handle is None:
+ sys.exit(1)
+
+ t = None
+
+ if args.test == 1:
+ t = Test1(traces, args.pid)
+ elif args.test == 2:
+ t = Test2(traces, args.pid)
+ elif args.test == 3:
+ t = Test3(traces, args.pid)
+ elif args.test == 4:
+ t = Test4(traces, args.pid)
+ elif args.test == 5:
+ t = Test5(traces, args.pid)
+ elif args.test == 6:
+ t = Test6(traces, args.pid)
+ elif args.test == 7:
+ t = Test7(traces, args.pid)
+ elif args.test == 8:
+ t = Test8(traces, args.pid)
+ elif args.test == 9:
+ t = Test9(traces, args.pid)
+ elif args.test == 10:
+ # stress test, nothing reliable to check
+ ret = 0
+ elif args.test == 11:
+ # stress test, nothing reliable to check
+ ret = 0
+ else:
+ print("Invalid test case")
+ sys.exit(1)
+
+ if t is not None:
+ ret = t.parse()
+
+ for h in handle.values():
+ traces.remove_trace(h)
+
+ sys.exit(ret)
diff --git a/tests/root_regression b/tests/root_regression
index 869c560..93cad3e 100644
--- a/tests/root_regression
+++ b/tests/root_regression
@@ -1,6 +1,7 @@
regression/kernel/test_all_events
regression/kernel/test_event_basic
regression/kernel/test_syscall
+regression/kernel/test_select_poll_epoll
regression/tools/live/test_kernel
regression/tools/live/test_lttng_kernel
regression/tools/streaming/test_high_throughput_limits
--
1.9.1
More information about the lttng-dev
mailing list