EXTRA_DIST = test_event_basic test_all_events test_syscall \
- test_clock_override test_rotation_destroy_flush
+ test_clock_override test_rotation_destroy_flush \
+ test_select_poll_epoll
+
+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
all-local:
@if [ x"$(srcdir)" != x"$(builddir)" ]; then \
--- /dev/null
+#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 */
+
+#define MIN_NR_FDS 5 /* the minimum number of open FDs required for the test to run */
+#define BIG_SELECT_FD 1022
+
+#define MSEC_PER_USEC 1000
+#define MSEC_PER_NSEC (MSEC_PER_USEC * 1000)
+
+static int timeout; /* seconds, -1 to disable */
+volatile static int stop_thread;
+static int wait_fd;
+
+struct ppoll_thread_data {
+ struct pollfd *ufds;
+ int value;
+};
+
+void test_select_big(void)
+{
+ 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, BIG_SELECT_FD);
+ if (fd2 < 0) {
+ perror("dup2");
+ goto end;
+ }
+ FD_SET(fd2, &rfds);
+
+ tv.tv_sec = 0;
+ tv.tv_usec = timeout * MSEC_PER_USEC;
+
+ 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");
+ }
+
+ ret = close(BIG_SELECT_FD);
+ if (ret) {
+ perror("close");
+ }
+
+end:
+ return;
+}
+
+void test_pselect(void)
+{
+ 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 = timeout * MSEC_PER_NSEC;
+
+ 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(void)
+{
+ 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 * MSEC_PER_USEC;
+
+ 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(void)
+{
+ 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");
+ }
+}
+
+void test_ppoll(void)
+{
+ 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 * MSEC_PER_NSEC;
+ 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");
+ }
+}
+
+void test_ppoll_big(void)
+{
+ 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(void)
+{
+ 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(void)
+{
+ 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 run_working_cases(void)
+{
+ int ret;
+ int pipe_fds[2];
+
+ if (timeout > 0) {
+ /*
+ * We need an input pipe for some cases and stdin might
+ * have random data, so we create a dummy pipe for this
+ * test to make sure we are running under clean conditions.
+ */
+ ret = pipe(pipe_fds);
+ if (ret != 0) {
+ perror("pipe");
+ goto end;
+ }
+ wait_fd = pipe_fds[0];
+ }
+ test_select();
+ test_pselect();
+ test_select_big();
+ test_poll();
+ test_ppoll();
+ test_epoll();
+ test_pepoll();
+
+ if (timeout > 0) {
+ ret = close(pipe_fds[0]);
+ if (ret) {
+ perror("close");
+ }
+ ret = close(pipe_fds[1]);
+ if (ret) {
+ perror("close");
+ }
+ }
+
+end:
+ return;
+}
+
+/*
+ * 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(void)
+{
+ 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(void)
+{
+ 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(void)
+{
+ 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(void)
+{
+ 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(void)
+{
+ 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(void)
+{
+ 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 * MSEC_PER_NSEC;
+
+ 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, ret;
+ struct ppoll_thread_data thread_data;
+ struct pollfd ufds[MAX_FDS];
+
+ thread_data.ufds = ufds;
+ thread_data.value = value;
+
+ stop_thread = 0;
+ ret = pthread_create(&writer, NULL, &ppoll_writer, (void *) &thread_data);
+ if (ret != 0) {
+ fprintf(stderr, "[error] pthread_create\n");
+ goto end;
+ }
+ for (iter = 0; iter < NR_ITER; iter++) {
+ do_ppoll(fds, ufds);
+ }
+ stop_thread = 1;
+ pthread_join(writer, NULL);
+end:
+ return;
+}
+
+/*
+ * 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(void)
+{
+ 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(void)
+{
+ 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(void)
+{
+ 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 + MIN_NR_FDS;
+ open_lim.rlim_max = MAX_FDS + MIN_NR_FDS;
+
+ 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;
+ run_working_cases();
+ break;
+ case 2:
+ timeout = 1;
+ run_working_cases();
+ 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;
+}
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) - 2016 Julien Desfossez <jdesfossez@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|i686|arm|aarch64" >/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 2>&1
+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"
+
+ # arm64 does not have epoll_wait
+ uname -m | grep -E "aarch64" >/dev/null 2>&1
+ if test $? = 0; then
+ SYSCALL_LIST="select,pselect6,poll,ppoll,epoll_ctl,epoll_pwait"
+ else
+ SYSCALL_LIST="select,pselect6,poll,ppoll,epoll_ctl,epoll_wait,epoll_pwait"
+ fi
+
+ 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
+
+ rm -rf $TRACE_PATH
+}
+
+function test_timeout_cases()
+{
+ TRACE_PATH=$(mktemp -d)
+ SESSION_NAME="syscall_payload"
+
+ # arm64 does not have epoll_wait
+ uname -m | grep -E "aarch64" >/dev/null 2>&1
+ if test $? = 0; then
+ SYSCALL_LIST="select,pselect6,poll,ppoll,epoll_ctl,epoll_pwait"
+ else
+ SYSCALL_LIST="select,pselect6,poll,ppoll,epoll_ctl,epoll_wait,epoll_pwait"
+ fi
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ 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
+}
--- /dev/null
+#!/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):
+ pass
+
+ def epoll_ctl_exit(self, event):
+ pass
+
+ # 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):
+ pass
+
+ def epoll_wait_exit(self, event):
+ pass
+
+ ## 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):
+ pass
+
+ def poll_exit(self, event):
+ pass
+
+ # 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 epoll_create_entry(self, event):
+ pass
+
+ def epoll_create_exit(self, event):
+ pass
+
+ # 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):
+ pass
+
+ def select_exit(self, event):
+ pass
+
+
+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"]
+
+ # check that the FD 0 is actually set in the readfds
+ if n == 1 and readfds[0] == 1:
+ self.expect["select_in_fd0"] = 1
+ if n == 1023:
+ # check that the FD 1023 is actually set in the readfds
+ 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:
+ # check that the FD 0 is actually set in the readfds
+ if readfds[0] == 1:
+ self.expect["select_out_fd0"] = 1
+ # check that the FD 1023 is actually set in the readfds
+ 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"]
+
+ # check that only one FD is set, that it has the POLLIN flag and that
+ # the raw value matches the events bit field.
+ 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"]
+
+ # check that only one FD is set, that it has the POLLIN flag and that
+ # the raw value matches the events bit field.
+ 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"]
+
+ # check that we have FD 0 waiting for EPOLLIN|EPOLLPRI and that
+ # data.fd = 0
+ 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"]
+
+ # check that FD 0 returned with EPOLLIN and the right data.fd
+ 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"]
+
+ # check that we wait on FD 0 for POLLIN and that the raw_events
+ # field matches the value of POLLIN
+ 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"]
+
+ # make sure we see a EPOLLIN|EPOLLPRI
+ 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"]
+
+ # make sure an invalid value still produces a valid event
+ 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"]
+
+ # make sure an invalid value still produces a valid event
+ 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"]
+
+ # test of big list of FDs and the behaviour of the overflow
+ 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"]
+
+ # test of big list of FDs and the behaviour of the overflow
+ 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"]
+
+ # test that event in valid even though the target buffer is too small
+ # and the program segfaults
+ 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"]
+
+ # test that event in valid even though the target buffer is too small
+ # and the program segfaults
+ 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"]
+
+ # test that event in valid even though the target buffer pointer is
+ # invalid and the program segfaults
+ 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"]
+
+ # test that event in valid even though the target buffer pointer is
+ # invalid and the program segfaults
+ 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"]
+
+ # check the proper working of INT_MAX maxevent value
+ 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"]
+
+ # check the proper working of UINT_MAX maxevent value
+ 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"]
+
+ # test that event in valid even though the target buffer pointer is
+ # invalid and the program segfaults
+ 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"]
+
+ # test that event in valid even though the target buffer pointer is
+ # invalid and the program segfaults
+ 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"]
+
+ # check the proper working of INT_MAX maxevent value
+ 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"]
+
+ # check the proper working of INT_MAX maxevent value
+ 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)
regression/kernel/test_syscall
regression/kernel/test_clock_override
regression/kernel/test_rotation_destroy_flush
+regression/kernel/test_select_poll_epoll
regression/tools/live/test_kernel
regression/tools/live/test_lttng_kernel
regression/tools/streaming/test_high_throughput_limits