Tests: fix: select_poll_epoll: test assumes epoll fd value
authorJérémie Galarneau <jeremie.galarneau@efficios.com>
Thu, 10 Feb 2022 16:59:31 +0000 (11:59 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Tue, 22 Feb 2022 21:34:42 +0000 (16:34 -0500)
The test currently assumes that epoll fds are always == 3, which
is not always the case depending on the execution environment.

This change causes `select_poll_epoll` to produce a JSON file
containing the application's pid and epoll fd values that is
then used by the validation script.

Note that the test is converted to C++ to allow the use of
internal utils (common/error.h/cpp) without changing their linkage.

However, the code is still regular C to ease the backport of this
fix.

Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
Change-Id: Ie373c63f6e6b9267ae2d785c9f0a532a5de37905

tests/regression/kernel/Makefile.am
tests/regression/kernel/select_poll_epoll.c
tests/regression/kernel/test_select_poll_epoll
tests/regression/kernel/validate_select_poll_epoll.py

index 040b76423a207e45494cd75107d4280b392aab3a..0bbae30e91454e555aa8b3d51e7a29c0492fc15d 100644 (file)
@@ -18,7 +18,7 @@ EXTRA_DIST = test_all_events \
 
 noinst_PROGRAMS = select_poll_epoll
 select_poll_epoll_SOURCES = select_poll_epoll.c
-select_poll_epoll_LDADD = $(POPT_LIBS)
+select_poll_epoll_LDADD = $(POPT_LIBS) $(top_builddir)/src/common/libcommon-lgpl.la
 select_poll_epoll_CFLAGS = $(POPT_CFLAGS) -fno-stack-protector -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 $(AM_CFLAGS)
 
 all-local:
index d0d7e4aeafae124566b73157c2565a4aef4a1bcb..abedc0aa929557c7aa859e76788d71d53e0f0cb4 100644 (file)
@@ -25,6 +25,7 @@
 #include <pthread.h>
 #include <sys/mman.h>
 #include <common/compat/time.h>
+#include <common/error.h>
 
 #define BUF_SIZE 256
 #define NB_FD 1
@@ -41,6 +42,41 @@ static int timeout; /* seconds, -1 to disable */
 static volatile int stop_thread;
 static int wait_fd;
 
+/* Used by logging utils. */
+int lttng_opt_quiet, lttng_opt_verbose, lttng_opt_mi;
+
+static void run_working_cases(FILE *validation_output_file);
+static void pselect_invalid_fd(FILE *validation_output_file);
+static void test_ppoll_big(FILE *validation_output_file);
+static void ppoll_fds_buffer_overflow(FILE *validation_output_file);
+static void pselect_invalid_pointer(FILE *validation_output_file);
+static void ppoll_fds_ulong_max(FILE *validation_output_file);
+static void epoll_pwait_invalid_pointer(FILE *validation_output_file);
+static void epoll_pwait_int_max(FILE *validation_output_file);
+static void ppoll_concurrent_write(FILE *validation_output_file);
+static void epoll_pwait_concurrent_munmap(FILE *validation_output_file);
+
+typedef void (*test_case_cb)(FILE *output_file);
+
+static const struct test_case {
+       test_case_cb run;
+       bool produces_validation_info;
+       int timeout;
+} test_cases [] =
+{
+       { .run = run_working_cases, .produces_validation_info = true, .timeout = -1 },
+       { .run = run_working_cases, .produces_validation_info = true, .timeout = 1 },
+       { .run = pselect_invalid_fd, .produces_validation_info = false },
+       { .run = test_ppoll_big, .produces_validation_info = false },
+       { .run = ppoll_fds_buffer_overflow, .produces_validation_info = false },
+       { .run = pselect_invalid_pointer, .produces_validation_info = false },
+       { .run = ppoll_fds_ulong_max, .produces_validation_info = false },
+       { .run = epoll_pwait_invalid_pointer, .produces_validation_info = true },
+       { .run = epoll_pwait_int_max, .produces_validation_info = true },
+       { .run = ppoll_concurrent_write, .produces_validation_info = false },
+       { .run = epoll_pwait_concurrent_munmap, .produces_validation_info = true },
+};
+
 struct ppoll_thread_data {
        struct pollfd *ufds;
        int value;
@@ -61,7 +97,7 @@ void test_select_big(void)
 
        fd2 = dup2(wait_fd, BIG_SELECT_FD);
        if (fd2 < 0) {
-               perror("dup2");
+               PERROR("dup2");
                goto end;
        }
        FD_SET(fd2, &rfds);
@@ -76,20 +112,17 @@ void test_select_big(void)
        }
 
        if (ret == -1) {
-               perror("select()");
+               PERROR("select()");
        } else if (ret) {
-               printf("# [select] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[select] read");
+                       PERROR("[select] read");
                }
-       } else {
-               printf("# [select] timeout\n");
        }
 
        ret = close(BIG_SELECT_FD);
        if (ret) {
-               perror("close");
+               PERROR("close");
        }
 
 end:
@@ -117,17 +150,13 @@ void test_pselect(void)
        }
 
        if (ret == -1) {
-               perror("pselect()");
+               PERROR("pselect()");
        } else if (ret) {
-               printf("# [pselect] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[pselect] read");
+                       PERROR("[pselect] read");
                }
-       } else {
-               printf("# [pselect] timeout\n");
        }
-
 }
 
 static
@@ -151,17 +180,13 @@ void test_select(void)
        }
 
        if (ret == -1) {
-               perror("select()");
+               PERROR("select()");
        } else if (ret) {
-               printf("# [select] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[select] read");
+                       PERROR("[select] read");
                }
-       } else {
-               printf("# [select] timeout\n");
        }
-
 }
 
 static
@@ -177,15 +202,12 @@ void test_poll(void)
        ret = poll(ufds, 1, timeout);
 
        if (ret < 0) {
-               perror("poll");
+               PERROR("poll");
        } else if (ret > 0) {
-               printf("# [poll] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[poll] read");
+                       PERROR("[poll] read");
                }
-       } else {
-               printf("# [poll] timeout\n");
        }
 }
 
@@ -210,20 +232,17 @@ void test_ppoll(void)
 
 
        if (ret < 0) {
-               perror("ppoll");
+               PERROR("ppoll");
        } else if (ret > 0) {
-               printf("# [ppoll] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[ppoll] read");
+                       PERROR("[ppoll] read");
                }
-       } else {
-               printf("# [ppoll] timeout\n");
        }
 }
 
 static
-void test_ppoll_big(void)
+void test_ppoll_big(FILE *validation_output_file)
 {
        struct pollfd ufds[MAX_FDS];
        char buf[BUF_SIZE];
@@ -232,7 +251,7 @@ void test_ppoll_big(void)
        for (i = 0; i < MAX_FDS; i++) {
                fds[i] = dup(wait_fd);
                if (fds[i] < 0) {
-                       perror("dup");
+                       PERROR("dup");
                }
                ufds[i].fd = fds[i];
                ufds[i].events = POLLIN|POLLPRI;
@@ -241,21 +260,18 @@ void test_ppoll_big(void)
        ret = ppoll(ufds, MAX_FDS, NULL, NULL);
 
        if (ret < 0) {
-               perror("ppoll");
+               PERROR("ppoll");
        } else if (ret > 0) {
-               printf("# [ppoll] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[ppoll] read");
+                       PERROR("[ppoll] read");
                }
-       } else {
-               printf("# [ppoll] timeout\n");
        }
 
        for (i = 0; i < MAX_FDS; i++) {
                ret = close(fds[i]);
                if (ret != 0) {
-                       perror("close");
+                       PERROR("close");
                }
        }
 
@@ -263,7 +279,7 @@ void test_ppoll_big(void)
 }
 
 static
-void test_epoll(void)
+void test_epoll(FILE *validation_output_file)
 {
        int ret, epollfd;
        char buf[BUF_SIZE];
@@ -271,15 +287,22 @@ void test_epoll(void)
 
        epollfd = epoll_create(NB_FD);
        if (epollfd < 0) {
-               perror("[epoll] create");
+               PERROR("[epoll] create");
                goto end;
        }
 
+       ret = fprintf(validation_output_file,
+                       ", \"epoll_wait_fd\": %i", epollfd);
+       if (ret < 0) {
+               PERROR("[epoll] Failed to write test validation output");
+               goto error;
+       }
+
        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");
+               PERROR("[epoll] add");
                goto error;
        }
 
@@ -290,28 +313,25 @@ void test_epoll(void)
        }
 
        if (ret == 1) {
-               printf("# [epoll] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[epoll] read");
+                       PERROR("[epoll] read");
                }
-       } else if (ret == 0) {
-               printf("# [epoll] timeout\n");
-       } else {
-               perror("epoll_wait");
+       } else if (ret != 0) {
+               PERROR("epoll_wait");
        }
 
 error:
        ret = close(epollfd);
        if (ret) {
-               perror("close");
+               PERROR("close");
        }
 end:
        return;
 }
 
 static
-void test_pepoll(void)
+void test_epoll_pwait(FILE *validation_output_file)
 {
        int ret, epollfd;
        char buf[BUF_SIZE];
@@ -319,15 +339,22 @@ void test_pepoll(void)
 
        epollfd = epoll_create(NB_FD);
        if (epollfd < 0) {
-               perror("[eppoll] create");
+               PERROR("[epoll_pwait] create");
                goto end;
        }
 
+       ret = fprintf(validation_output_file,
+                       ", \"epoll_pwait_fd\": %i", epollfd);
+       if (ret < 0) {
+               PERROR("[epoll_pwait] Failed to write test validation output");
+               goto error;
+       }
+
        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");
+               PERROR("[epoll_pwait] add");
                goto error;
        }
 
@@ -338,28 +365,25 @@ void test_pepoll(void)
        }
 
        if (ret == 1) {
-               printf("# [eppoll] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[eppoll] read");
+                       PERROR("[epoll_pwait] read");
                }
-       } else if (ret == 0) {
-               printf("# [eppoll] timeout\n");
-       } else {
-               perror("epoll_pwait");
+       } else if (ret != 0) {
+               PERROR("epoll_pwait");
        }
 
 error:
        ret = close(epollfd);
        if (ret) {
-               perror("close");
+               PERROR("close");
        }
 end:
        return;
 }
 
 static
-void run_working_cases(void)
+void run_working_cases(FILE *validation_output_file)
 {
        int ret;
        int pipe_fds[2];
@@ -372,7 +396,7 @@ void run_working_cases(void)
                 */
                ret = pipe(pipe_fds);
                if (ret != 0) {
-                       perror("pipe");
+                       PERROR("pipe");
                        goto end;
                }
                wait_fd = pipe_fds[0];
@@ -382,20 +406,33 @@ void run_working_cases(void)
        test_select_big();
        test_poll();
        test_ppoll();
-       test_epoll();
-       test_pepoll();
+
+       ret = fprintf(validation_output_file, "{ \"pid\": %i", getpid());
+       if (ret < 0) {
+               PERROR("Failed to write pid to test validation file");
+               goto end;
+       }
+
+       test_epoll(validation_output_file);
+       test_epoll_pwait(validation_output_file);
 
        if (timeout > 0) {
                ret = close(pipe_fds[0]);
                if (ret) {
-                       perror("close");
+                       PERROR("close");
                }
                ret = close(pipe_fds[1]);
                if (ret) {
-                       perror("close");
+                       PERROR("close");
                }
        }
 
+       ret = fputs(" }", validation_output_file);
+       if (ret < 0) {
+               PERROR("Failed to close JSON dictionary in test validation file");
+               goto end;
+       }
+
 end:
        return;
 }
@@ -406,7 +443,7 @@ end:
  * The event should contain an array of 100 FDs filled with garbage.
  */
 static
-void ppoll_fds_buffer_overflow(void)
+void ppoll_fds_buffer_overflow(FILE *validation_output_file)
 {
        struct pollfd ufds[NB_FD];
        char buf[BUF_SIZE];
@@ -418,18 +455,13 @@ void ppoll_fds_buffer_overflow(void)
        ret = syscall(SYS_ppoll, ufds, 100, NULL, NULL);
 
        if (ret < 0) {
-               perror("ppoll");
+               PERROR("ppoll");
        } else if (ret > 0) {
-               printf("# [ppoll] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[ppoll] read");
+                       PERROR("[ppoll] read");
                }
-       } else {
-               printf("# [ppoll] timeout\n");
        }
-
-       return;
 }
 
 /*
@@ -438,7 +470,7 @@ void ppoll_fds_buffer_overflow(void)
  * The event should contain an empty array of FDs and overflow = 1.
  */
 static
-void ppoll_fds_ulong_max(void)
+void ppoll_fds_ulong_max(FILE *validation_output_file)
 {
        struct pollfd ufds[NB_FD];
        char buf[BUF_SIZE];
@@ -448,20 +480,14 @@ void ppoll_fds_ulong_max(void)
        ufds[0].events = POLLIN|POLLPRI;
 
        ret = syscall(SYS_ppoll, ufds, ULONG_MAX, NULL, NULL);
-
        if (ret < 0) {
-               perror("# ppoll");
+               /* Expected error. */
        } else if (ret > 0) {
-               printf("# [ppoll] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[ppoll] read");
+                       PERROR("[ppoll] read");
                }
-       } else {
-               printf("# [ppoll] timeout\n");
        }
-
-       return;
 }
 
 /*
@@ -469,7 +495,7 @@ void ppoll_fds_ulong_max(void)
  * -EBADF. The recorded event should contain a "ret = -EBADF (-9)".
  */
 static
-void pselect_invalid_fd(void)
+void pselect_invalid_fd(FILE *validation_output_file)
 {
        fd_set rfds;
        int ret;
@@ -479,16 +505,15 @@ void pselect_invalid_fd(void)
        /*
         * Open a file, close it and use the closed FD in the pselect6 call.
         */
-
        fd = open("/dev/null", O_RDONLY);
        if (fd == -1) {
-               perror("open");
+               PERROR("open");
                goto error;
        }
 
        ret = close(fd);
        if (ret == -1) {
-               perror("close");
+               PERROR("close");
                goto error;
        }
 
@@ -497,15 +522,12 @@ void pselect_invalid_fd(void)
 
        ret = syscall(SYS_pselect6, fd + 1, &rfds, NULL, NULL, NULL, NULL);
        if (ret == -1) {
-               perror("# pselect()");
+               /* Expected error. */
        } else if (ret) {
-               printf("# [pselect] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[pselect] read");
+                       PERROR("[pselect] read");
                }
-       } else {
-               printf("# [pselect] timeout\n");
        }
 error:
        return;
@@ -516,7 +538,7 @@ error:
  * with 0 FDs.
  */
 static
-void pselect_invalid_pointer(void)
+void pselect_invalid_pointer(FILE *validation_output_file)
 {
        fd_set rfds;
        int ret;
@@ -528,19 +550,14 @@ void pselect_invalid_pointer(void)
 
        ret = syscall(SYS_pselect6, 1, &rfds, (fd_set *) invalid, NULL, NULL,
                        NULL);
-
        if (ret == -1) {
-               perror("# pselect()");
+               /* Expected error. */
        } else if (ret) {
-               printf("# [pselect] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[pselect] read");
+                       PERROR("[pselect] read");
                }
-       } else {
-               printf("# [pselect] timeout\n");
        }
-
 }
 
 /*
@@ -548,7 +565,7 @@ void pselect_invalid_pointer(void)
  * "Bad address", the event returns 0 FDs.
  */
 static
-void epoll_pwait_invalid_pointer(void)
+void epoll_pwait_invalid_pointer(FILE *validation_output_file)
 {
        int ret, epollfd;
        char buf[BUF_SIZE];
@@ -557,15 +574,23 @@ void epoll_pwait_invalid_pointer(void)
 
        epollfd = epoll_create(NB_FD);
        if (epollfd < 0) {
-               perror("[eppoll] create");
+               PERROR("[epoll_pwait] create");
                goto end;
        }
 
+       ret = fprintf(validation_output_file,
+                       "{ \"epollfd\": %i, \"pid\": %i }", epollfd,
+                       getpid());
+       if (ret < 0) {
+               PERROR("[epoll_pwait] Failed to write test validation output");
+               goto error;
+       }
+
        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");
+               PERROR("[epoll_pwait] add");
                goto error;
        }
 
@@ -573,21 +598,18 @@ void epoll_pwait_invalid_pointer(void)
                        (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");
+                       PERROR("[epoll_pwait] read");
                }
-       } else if (ret == 0) {
-               printf("# [eppoll] timeout\n");
-       } else {
-               perror("# epoll_pwait");
+       } else if (ret != 0) {
+               /* Expected error. */
        }
 
 error:
        ret = close(epollfd);
        if (ret) {
-               perror("close");
+               PERROR("close");
        }
 end:
        return;
@@ -598,7 +620,7 @@ end:
  * The event should return an empty array.
  */
 static
-void epoll_pwait_int_max(void)
+void epoll_pwait_int_max(FILE *validation_output_file)
 {
        int ret, epollfd;
        char buf[BUF_SIZE];
@@ -606,15 +628,23 @@ void epoll_pwait_int_max(void)
 
        epollfd = epoll_create(NB_FD);
        if (epollfd < 0) {
-               perror("[eppoll] create");
+               PERROR("[epoll_pwait] create");
                goto end;
        }
 
+       ret = fprintf(validation_output_file,
+                       "{ \"epollfd\": %i, \"pid\": %i }", epollfd,
+                       getpid());
+       if (ret < 0) {
+               PERROR("[epoll_pwait] Failed to write test validation output");
+               goto error;
+       }
+
        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");
+               PERROR("[epoll_pwait] add");
                goto error;
        }
 
@@ -622,21 +652,18 @@ void epoll_pwait_int_max(void)
                        NULL);
 
        if (ret == 1) {
-               printf("# [eppoll] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[eppoll] read");
+                       PERROR("[epoll_pwait] read");
                }
-       } else if (ret == 0) {
-               printf("# [eppoll] timeout\n");
-       } else {
-               perror("# epoll_pwait");
+       } else if (ret != 0) {
+               /* Expected error. */
        }
 
 error:
        ret = close(epollfd);
        if (ret) {
-               perror("close");
+               PERROR("close");
        }
 end:
        return;
@@ -674,15 +701,12 @@ void do_ppoll(int *fds, struct pollfd *ufds)
        ret = ppoll(ufds, MAX_FDS, &ts, NULL);
 
        if (ret < 0) {
-               perror("ppoll");
+               PERROR("ppoll");
        } else if (ret > 0) {
-               printf("# [ppoll] data available\n");
                ret = read(wait_fd, buf, BUF_SIZE);
                if (ret < 0) {
-                       perror("[ppoll] read");
+                       PERROR("[ppoll] read");
                }
-       } else {
-               printf("# [ppoll] timeout\n");
        }
 }
 
@@ -729,14 +753,14 @@ end:
  * ppoll should work as expected and the trace should be readable at the end.
  */
 static
-void ppoll_concurrent_write(void)
+void ppoll_concurrent_write(FILE *validation_output_file)
 {
        int i, ret, fds[MAX_FDS];
 
        for (i = 0; i < MAX_FDS; i++) {
                fds[i] = dup(wait_fd);
                if (fds[i] < 0) {
-                       perror("dup");
+                       PERROR("dup");
                }
        }
 
@@ -747,7 +771,7 @@ void ppoll_concurrent_write(void)
        for (i = 0; i < MAX_FDS; i++) {
                ret = close(fds[i]);
                if (ret != 0) {
-                       perror("close");
+                       PERROR("close");
                }
        }
 
@@ -773,7 +797,7 @@ void *epoll_pwait_writer(void *addr)
  * The trace should be readable and no kernel OOPS should occur.
  */
 static
-void epoll_pwait_concurrent_munmap(void)
+void epoll_pwait_concurrent_munmap(FILE *validation_output_file)
 {
        int ret, epollfd, i, fds[MAX_FDS];
        char buf[BUF_SIZE];
@@ -785,28 +809,37 @@ void epoll_pwait_concurrent_munmap(void)
        }
        epollfd = epoll_create(MAX_FDS);
        if (epollfd < 0) {
-               perror("[eppoll] create");
+               PERROR("[epoll_pwait] create");
                goto end;
        }
 
-       epoll_event = mmap(NULL, MAX_FDS * sizeof(struct epoll_event),
-                       PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
-                       -1, 0);
+       ret = fprintf(validation_output_file,
+                       "{ \"epollfd\": %i, \"pid\": %i }", epollfd,
+                       getpid());
+       if (ret < 0) {
+               PERROR("[epoll_pwait] Failed to write test validation output");
+               goto error;
+       }
+
+       epoll_event = (struct epoll_event *) mmap(NULL,
+                       MAX_FDS * sizeof(struct epoll_event),
+                       PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1,
+                       0);
        if (epoll_event == MAP_FAILED) {
-               perror("mmap");
+               PERROR("mmap");
                goto error;
        }
 
        for (i = 0; i < MAX_FDS; i++) {
                fds[i] = dup(wait_fd);
                if (fds[i] < 0) {
-                       perror("dup");
+                       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");
+                       PERROR("[epoll_pwait] add");
                        goto error_unmap;
                }
        }
@@ -821,15 +854,12 @@ void epoll_pwait_concurrent_munmap(void)
        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");
+                       PERROR("[epoll_pwait] read");
                }
-       } else if (ret == 0) {
-               printf("# [eppoll] timeout\n");
-       } else {
-               perror("# epoll_pwait");
+       } else if (ret != 0) {
+               /* Expected error. */
        }
 
        stop_thread = 1;
@@ -842,19 +872,19 @@ error_unmap:
        for (i = 0; i < MAX_FDS; i++) {
                ret = close(fds[i]);
                if (ret != 0) {
-                       perror("close");
+                       PERROR("close");
                }
        }
 
        ret = munmap(epoll_event, MAX_FDS * sizeof(struct epoll_event));
        if (ret != 0) {
-               perror("munmap");
+               PERROR("munmap");
        }
 
 error:
        ret = close(epollfd);
        if (ret) {
-               perror("close");
+               PERROR("close");
        }
 end:
        return;
@@ -892,15 +922,19 @@ int main(int argc, const char **argv)
        int c, ret, test = -1;
        poptContext optCon;
        struct rlimit open_lim;
-
+       FILE *test_validation_output_file = NULL;
+       const char *test_validation_output_file_path = NULL;
        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 },
+               { "validation-file", 'o', POPT_ARG_STRING, &test_validation_output_file_path, 0,
+                       "Test case output", NULL },
                POPT_AUTOHELP
                { NULL, 0, 0, NULL, 0 }
        };
+       const struct test_case *test_case;
 
        optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
 
@@ -913,74 +947,75 @@ int main(int argc, const char **argv)
        ret = 0;
 
        while ((c = poptGetNextOpt(optCon)) >= 0) {
-               switch(c) {
+               switch (c) {
                case 'l':
                        print_list();
                        goto end;
                }
        }
 
+       if (!test_validation_output_file_path) {
+               fprintf(stderr, "A test validation file path is required (--validation-file/-o)\n");
+               ret = -1;
+               goto end;
+       }
+
+       test_validation_output_file = fopen(test_validation_output_file_path, "w+");
+       if (!test_validation_output_file) {
+               PERROR("Failed to create test validation output file at '%s'",
+                               test_validation_output_file_path);
+               ret = -1;
+               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");
+               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.
+        * for the validation, disabling the buffering on the validation file
+        * works.
         */
-       setbuf(stdout, NULL);
-       printf("%d\n", getpid());
-
+       setbuf(test_validation_output_file, NULL);
        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_invalid_fd();
-               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:
+       /* Test case id is 1-based. */
+       if (test < 1 || test > ARRAY_SIZE(test_cases)) {
                poptPrintUsage(optCon, stderr, 0);
                ret = -1;
-               break;
        }
 
+       test_case = &test_cases[test - 1];
+
+       timeout = test_case->timeout;
+       if (!test_case->produces_validation_info) {
+               /*
+                * All test cases need to provide, at minimum, the pid of the
+                * test application.
+                */
+               ret = fprintf(test_validation_output_file, "{ \"pid\": %i }", getpid());
+               if (ret < 0) {
+                       PERROR("Failed to write application pid to test validation file");
+                       goto end;
+               }
+       }
+
+       test_case->run(test_validation_output_file);
+
 end:
+       if (test_validation_output_file) {
+               const int close_ret = fclose(test_validation_output_file);
+
+               if (close_ret) {
+                       PERROR("Failed to close test output file");
+               }
+       }
        poptFreeContext(optCon);
        return ret;
 }
index 16b0da525c4fae8cf394aa57729dd84bb24641d6..6d5509b25990ea7960e39803cc07b2c54e6b7290 100755 (executable)
@@ -7,7 +7,7 @@
 
 TEST_DESC="Kernel tracer - select, poll and epoll payload extraction"
 
-CURDIR=$(dirname $0)/
+CURDIR=$(dirname "$0")/
 TESTDIR=$CURDIR/../..
 VALIDATE_SCRIPT="$CURDIR/validate_select_poll_epoll.py"
 NUM_TESTS=102
@@ -33,6 +33,7 @@ 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)
 
+# shellcheck source=../../utils/utils.sh
 source $TESTDIR/utils/utils.sh
 
 function check_trace_content()
@@ -52,8 +53,9 @@ function check_trace_content()
 
 function test_working_cases()
 {
-       TRACE_PATH=$(mktemp --tmpdir -d "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
        SESSION_NAME="syscall_payload"
+       TRACE_PATH=$(mktemp --tmpdir -d "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_trace_path.XXXXXX")
+       TEST_VALIDATION_OUTPUT_PATH=$(mktemp --tmpdir -u "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_validation.XXXXXX")
 
        # arm64 does not have epoll_wait
        uname -m | grep -E "aarch64" >/dev/null 2>&1
@@ -65,28 +67,29 @@ function test_working_cases()
 
        diag "Working cases for select, pselect6, poll, ppoll and epoll, waiting for input"
 
-       create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+       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
+       yes | "$CURDIR"/select_poll_epoll --validation-file "$TEST_VALIDATION_OUTPUT_PATH" -t 1
        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
+       validate_trace "$SYSCALL_LIST" "$TRACE_PATH"
+       check_trace_content -t 1 --validation-file "$TEST_VALIDATION_OUTPUT_PATH" "$TRACE_PATH"
 
        destroy_lttng_session_ok $SESSION_NAME
 
-       rm -rf $TRACE_PATH
+       rm -rf "$TRACE_PATH"
+       rm -f "$TEST_VALIDATION_OUTPUT_PATH"
 }
 
 function test_timeout_cases()
 {
-       TRACE_PATH=$(mktemp --tmpdir -d "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
        SESSION_NAME="syscall_payload"
+       TRACE_PATH=$(mktemp --tmpdir -d "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_trace_path.XXXXXX")
+       TEST_VALIDATION_OUTPUT_PATH=$(mktemp --tmpdir -u "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_validation.XXXXXX")
 
        # arm64 does not have epoll_wait
        uname -m | grep -E "aarch64" >/dev/null 2>&1
@@ -98,244 +101,252 @@ function test_timeout_cases()
 
        diag "Timeout cases (1ms) for select, pselect6, poll, ppoll and epoll"
 
-       create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+       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
+       yes | "$CURDIR"/select_poll_epoll --validation-file "$TEST_VALIDATION_OUTPUT_PATH" -t 2
        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
+       validate_trace "$SYSCALL_LIST" "$TRACE_PATH"
+       check_trace_content -t 2 --validation-file "$TEST_VALIDATION_OUTPUT_PATH" "$TRACE_PATH" 2>/dev/null
 
        destroy_lttng_session_ok $SESSION_NAME
 
-       rm -rf $TRACE_PATH
+       rm -rf "$TRACE_PATH"
+       rm -f "$TEST_VALIDATION_OUTPUT_PATH"
 }
 
 function test_pselect_invalid_fd()
 {
-       TRACE_PATH=$(mktemp --tmpdir -d "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
        SESSION_NAME="syscall_payload"
        SYSCALL_LIST="pselect6"
+       TRACE_PATH=$(mktemp --tmpdir -d "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_trace_path.XXXXXX")
+       TEST_VALIDATION_OUTPUT_PATH=$(mktemp --tmpdir -u "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_validation.XXXXXX")
 
        diag "pselect with invalid FD"
 
-       create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+       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
+       yes | "$CURDIR"/select_poll_epoll --validation-file "$TEST_VALIDATION_OUTPUT_PATH" -t 3
        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
+       validate_trace "$SYSCALL_LIST" "$TRACE_PATH"
+       check_trace_content -t 3 --validation-file "$TEST_VALIDATION_OUTPUT_PATH" "$TRACE_PATH" 2>/dev/null
 
        destroy_lttng_session_ok $SESSION_NAME
 
-       rm -rf $TRACE_PATH
+       rm -rf "$TRACE_PATH"
+       rm -f "$TEST_VALIDATION_OUTPUT_PATH"
 }
 
 function test_big_ppoll()
 {
-       TRACE_PATH=$(mktemp --tmpdir -d "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
        SESSION_NAME="syscall_payload"
        SYSCALL_LIST="ppoll"
+       TRACE_PATH=$(mktemp --tmpdir -d "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_trace_path.XXXXXX")
+       TEST_VALIDATION_OUTPUT_PATH=$(mktemp --tmpdir -u "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_validation.XXXXXX")
 
        diag "ppoll with 2047 FDs"
 
-       create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+       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
+       yes | "$CURDIR"/select_poll_epoll --validation-file "$TEST_VALIDATION_OUTPUT_PATH" -t 4
        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
+       validate_trace "$SYSCALL_LIST" "$TRACE_PATH"
+       check_trace_content -t 4 --validation-file "$TEST_VALIDATION_OUTPUT_PATH" "$TRACE_PATH" 2>/dev/null
 
        destroy_lttng_session_ok $SESSION_NAME
 
-       rm -rf $TRACE_PATH
+       rm -rf "$TRACE_PATH"
+       rm -f "$TEST_VALIDATION_OUTPUT_PATH"
 }
 
 function test_ppoll_overflow()
 {
-       TRACE_PATH=$(mktemp --tmpdir -d "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
        SESSION_NAME="syscall_payload"
        SYSCALL_LIST="ppoll"
+       TRACE_PATH=$(mktemp --tmpdir -d "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_trace_path.XXXXXX")
+       TEST_VALIDATION_OUTPUT_PATH=$(mktemp --tmpdir -u "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_validation.XXXXXX")
 
        diag "ppoll buffer overflow, should segfault, waits for input"
 
-       create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+       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
+       yes | "$CURDIR"/select_poll_epoll --validation-file "$TEST_VALIDATION_OUTPUT_PATH" -t 5
        stop_lttng_tracing_ok
-       echo $out
-       pid=$(echo $out | cut -d' ' -f1)
 
-       validate_trace "$SYSCALL_LIST" $TRACE_PATH
+       validate_trace "$SYSCALL_LIST" "$TRACE_PATH"
 
-       check_trace_content -t 5 -p $pid $TRACE_PATH 2>/dev/null
+       check_trace_content -t 5 --validation-file "$TEST_VALIDATION_OUTPUT_PATH" "$TRACE_PATH" 2>/dev/null
 
        destroy_lttng_session_ok $SESSION_NAME
 
-       rm -rf $TRACE_PATH
+       rm -rf "$TRACE_PATH"
+       rm -f "$TEST_VALIDATION_OUTPUT_PATH"
 }
 
 function test_pselect_invalid_ptr()
 {
-       TRACE_PATH=$(mktemp --tmpdir -d "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
        SESSION_NAME="syscall_payload"
        SYSCALL_LIST="pselect6"
+       TRACE_PATH=$(mktemp --tmpdir -d "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_trace_path.XXXXXX")
+       TEST_VALIDATION_OUTPUT_PATH=$(mktemp --tmpdir -u "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_validation.XXXXXX")
 
        diag "pselect with invalid pointer, waits for input"
 
-       create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+       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
+       yes | "$CURDIR"/select_poll_epoll --validation-file "$TEST_VALIDATION_OUTPUT_PATH" -t 6
        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
+       validate_trace "$SYSCALL_LIST" "$TRACE_PATH"
+       check_trace_content -t 6 --validation-file "$TEST_VALIDATION_OUTPUT_PATH" "$TRACE_PATH" 2>/dev/null
 
        destroy_lttng_session_ok $SESSION_NAME
 
-       rm -rf $TRACE_PATH
+       rm -rf "$TRACE_PATH"
+       rm -f "$TEST_VALIDATION_OUTPUT_PATH"
 }
 
 function test_ppoll_ulong_max()
 {
-       TRACE_PATH=$(mktemp --tmpdir -d "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
        SESSION_NAME="syscall_payload"
        SYSCALL_LIST="ppoll"
+       TRACE_PATH=$(mktemp --tmpdir -d "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_trace_path.XXXXXX")
+       TEST_VALIDATION_OUTPUT_PATH=$(mktemp --tmpdir -u "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_validation.XXXXXX")
 
        diag "ppoll with ulong_max fds, waits for input"
 
-       create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+       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
+       yes | "$CURDIR"/select_poll_epoll --validation-file "$TEST_VALIDATION_OUTPUT_PATH" -t 7
        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
+       validate_trace "$SYSCALL_LIST" "$TRACE_PATH"
+       check_trace_content -t 7 --validation-file "$TEST_VALIDATION_OUTPUT_PATH" "$TRACE_PATH" 2>/dev/null
 
        destroy_lttng_session_ok $SESSION_NAME
 
-       rm -rf $TRACE_PATH
+       rm -rf "$TRACE_PATH"
+       rm -f "$TEST_VALIDATION_OUTPUT_PATH"
 }
 
 function test_epoll_pwait_invalid_ptr()
 {
-       TRACE_PATH=$(mktemp --tmpdir -d "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
        SESSION_NAME="syscall_payload"
        SYSCALL_LIST="epoll_pwait"
+       TRACE_PATH=$(mktemp --tmpdir -d "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_trace_path.XXXXXX")
+       TEST_VALIDATION_OUTPUT_PATH=$(mktemp --tmpdir -u "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_validation.XXXXXX")
 
        diag "epoll_pwait with invalid pointer, waits for input"
 
-       create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+       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
+       yes | "$CURDIR"/select_poll_epoll --validation-file "$TEST_VALIDATION_OUTPUT_PATH" -t 8
        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
+       validate_trace "$SYSCALL_LIST" "$TRACE_PATH"
+       check_trace_content -t 8 --validation-file "$TEST_VALIDATION_OUTPUT_PATH" "$TRACE_PATH" 2>/dev/null
 
        destroy_lttng_session_ok $SESSION_NAME
 
-       rm -rf $TRACE_PATH
+       rm -rf "$TRACE_PATH"
+       rm -f "$TEST_VALIDATION_OUTPUT_PATH"
 }
 
 function test_epoll_pwait_int_max()
 {
-       TRACE_PATH=$(mktemp --tmpdir -d "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
        SESSION_NAME="syscall_payload"
        SYSCALL_LIST="epoll_pwait"
+       TRACE_PATH=$(mktemp --tmpdir -d "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_trace_path.XXXXXX")
+       TEST_VALIDATION_OUTPUT_PATH=$(mktemp --tmpdir -u "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_validation.XXXXXX")
 
        diag "epoll_pwait with maxevents set to INT_MAX, waits for input"
 
-       create_lttng_session_ok $SESSION_NAME $TRACE_PATH
+       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
+       yes | "$CURDIR"/select_poll_epoll --validation-file "$TEST_VALIDATION_OUTPUT_PATH" -t 9
        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
+       validate_trace "$SYSCALL_LIST" "$TRACE_PATH"
+       check_trace_content -t 9 --validation-file "$TEST_VALIDATION_OUTPUT_PATH" "$TRACE_PATH" 2>/dev/null
 
        destroy_lttng_session_ok $SESSION_NAME
 
-       rm -rf $TRACE_PATH
+       rm -rf "$TRACE_PATH"
+       rm -f "$TEST_VALIDATION_OUTPUT_PATH"
 }
 
 function test_ppoll_concurrent()
 {
-       TRACE_PATH=$(mktemp --tmpdir -d "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
        SESSION_NAME="syscall_payload"
        SYSCALL_LIST="ppoll"
+       TRACE_PATH=$(mktemp --tmpdir -d "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_trace_path.XXXXXX")
+       TEST_VALIDATION_OUTPUT_PATH=$(mktemp --tmpdir -u "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_validation.XXXXXX")
 
        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
+       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
+       yes | "$CURDIR"/select_poll_epoll --validation-file "$TEST_VALIDATION_OUTPUT_PATH" -t 10
        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
+       validate_trace "$SYSCALL_LIST" "$TRACE_PATH"
+       check_trace_content -t 10 --validation-file "$TEST_VALIDATION_OUTPUT_PATH" "$TRACE_PATH" 2>/dev/null
 
        destroy_lttng_session_ok $SESSION_NAME
 
-       rm -rf $TRACE_PATH
+       rm -rf "$TRACE_PATH"
+       rm -f "$TEST_VALIDATION_OUTPUT_PATH"
 }
 
 function test_epoll_pwait_concurrent()
 {
-       TRACE_PATH=$(mktemp --tmpdir -d "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
        SESSION_NAME="syscall_payload"
        SYSCALL_LIST="epoll_ctl,epoll_pwait"
+       TRACE_PATH=$(mktemp --tmpdir -d "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_trace_path.XXXXXX")
+       TEST_VALIDATION_OUTPUT_PATH=$(mktemp --tmpdir -u "tmp.test_kernel_select_poll_epoll_${FUNCNAME[0]}_validation.XXXXXX")
 
        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
+       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
@@ -343,18 +354,18 @@ function test_epoll_pwait_concurrent()
        start_lttng_tracing_ok
        diag "Expect segfaults"
        for i in $(seq 1 100); do
-               { out=$($CURDIR/select_poll_epoll -t 11); } 2>/dev/null
+               yes | "$CURDIR"/select_poll_epoll --validation-file "$TEST_VALIDATION_OUTPUT_PATH" -t 11
        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
+       validate_trace "epoll_ctl" "$TRACE_PATH"
+       check_trace_content -t 11 --validation-file "$TEST_VALIDATION_OUTPUT_PATH" "$TRACE_PATH" 2>/dev/null
 
        destroy_lttng_session_ok $SESSION_NAME
 
-       rm -rf $TRACE_PATH
+       rm -rf "$TRACE_PATH"
+       rm -f "$TEST_VALIDATION_OUTPUT_PATH"
 }
 
 # MUST set TESTDIR before calling those functions
index 3a62a1f1bba088da910d05a6a871f86c077d3628..b8d28a9816114ebee7f68e202b3f52ea743f9697 100755 (executable)
@@ -9,6 +9,7 @@ import argparse
 import pprint
 import sys
 import time
+import json
 
 from collections import defaultdict
 
@@ -108,16 +109,16 @@ class TraceParser:
         self.epoll_wait_exit(event)
 
     def handle_compat_syscall_entry_epoll_pwait(self, event):
-        self.epoll_wait_entry(event)
+        self.epoll_pwait_entry(event)
 
     def handle_compat_syscall_exit_epoll_pwait(self, event):
-        self.epoll_wait_exit(event)
+        self.epoll_pwait_exit(event)
 
     def handle_syscall_entry_epoll_pwait(self, event):
-        self.epoll_wait_entry(event)
+        self.epoll_pwait_entry(event)
 
     def handle_syscall_exit_epoll_pwait(self, event):
-        self.epoll_wait_exit(event)
+        self.epoll_pwait_exit(event)
 
     def epoll_wait_entry(self, event):
         pass
@@ -125,6 +126,12 @@ class TraceParser:
     def epoll_wait_exit(self, event):
         pass
 
+    def epoll_pwait_entry(self, event):
+        self.epoll_wait_entry(event)
+
+    def epoll_pwait_exit(self, event):
+        self.epoll_wait_exit(event)
+
     ## poll + ppoll
     def handle_compat_syscall_entry_poll(self, event):
         self.poll_entry(event)
@@ -220,8 +227,13 @@ class TraceParser:
 
 
 class Test1(TraceParser):
-    def __init__(self, trace, pid):
-        super().__init__(trace, pid)
+    def __init__(self, trace, validation_args):
+        super().__init__(trace, validation_args['pid'])
+
+        # Values expected in the trace
+        self.epoll_wait_fd = validation_args['epoll_wait_fd']
+        self.epoll_pwait_fd = validation_args['epoll_pwait_fd']
+
         self.expect["select_entry"]["select_in_fd0"] = 0
         self.expect["select_entry"]["select_in_fd1023"] = 0
         self.expect["select_exit"]["select_out_fd0"] = 0
@@ -232,6 +244,8 @@ class Test1(TraceParser):
         self.expect["epoll_ctl_exit"]["epoll_ctl_out_ok"] = 0
         self.expect["epoll_wait_entry"]["epoll_wait_in_ok"] = 0
         self.expect["epoll_wait_exit"]["epoll_wait_out_fd0"] = 0
+        self.expect["epoll_pwait_entry"]["epoll_pwait_in_ok"] = 0
+        self.expect["epoll_pwait_exit"]["epoll_pwait_out_fd0"] = 0
 
     def select_entry(self, event):
         n = event["n"]
@@ -317,7 +331,7 @@ class Test1(TraceParser):
 
         # 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 \
+        if (epfd == self.epoll_wait_fd or epfd == self.epoll_pwait_fd) and 'EPOLL_CTL_ADD' in op_enum.labels and fd == 0 and \
                 _event["data_union"]["fd"] == 0 and \
                 _event["events"]["EPOLLIN"] == 1 and \
                 _event["events"]["EPOLLPRI"] == 1:
@@ -340,7 +354,7 @@ class Test1(TraceParser):
         maxevents = event["maxevents"]
         timeout = event["timeout"]
 
-        if epfd == 3 and maxevents == 1 and timeout == -1:
+        if epfd == self.epoll_wait_fd and maxevents == 1 and timeout == -1:
             self.expect["epoll_wait_entry"]["epoll_wait_in_ok"] = 1
 
         # Save values of local variables to print in case of test failure
@@ -361,10 +375,35 @@ class Test1(TraceParser):
         # Save values of local variables to print in case of test failure
         self.recorded_values["epoll_wait_exit"] = locals()
 
+    def epoll_pwait_entry(self, event):
+        epfd = event["epfd"]
+        maxevents = event["maxevents"]
+        timeout = event["timeout"]
+
+        if epfd == self.epoll_pwait_fd and maxevents == 1 and timeout == -1:
+            self.expect["epoll_pwait_entry"]["epoll_pwait_in_ok"] = 1
+
+        # Save values of local variables to print in case of test failure
+        self.recorded_values["epoll_pwait_entry"] = locals()
+
+    def epoll_pwait_exit(self, event):
+        ret = event["ret"]
+        fds_length = event["fds_length"]
+        overflow = event["overflow"]
+
+        # check that FD 0 returned with EPOLLIN and the right data.fd
+        if ret == 1 and fds_length == 1:
+            fd_0 = event["fds"][0]
+            if overflow == 0 and  fd_0["data_union"]["fd"] == 0 and \
+                fd_0["events"]["EPOLLIN"] == 1:
+                self.expect["epoll_pwait_exit"]["epoll_pwait_out_fd0"] = 1
+
+        # Save values of local variables to print in case of test failure
+        self.recorded_values["epoll_pwait_exit"] = locals()
 
 class Test2(TraceParser):
-    def __init__(self, trace, pid):
-        super().__init__(trace, pid)
+    def __init__(self, trace, validation_args):
+        super().__init__(trace, validation_args['pid'])
         self.expect["select_entry"]["select_timeout_in_fd0"] = 0
         self.expect["select_entry"]["select_timeout_in_fd1023"] = 0
         self.expect["select_exit"]["select_timeout_out"] = 0
@@ -475,8 +514,8 @@ class Test2(TraceParser):
 
 
 class Test3(TraceParser):
-    def __init__(self, trace, pid):
-        super().__init__(trace, pid)
+    def __init__(self, trace, validation_args):
+        super().__init__(trace, validation_args['pid'])
         self.expect["select_entry"]["select_invalid_fd_in"] = 0
         self.expect["select_exit"]["select_invalid_fd_out"] = 0
 
@@ -504,8 +543,8 @@ class Test3(TraceParser):
 
 
 class Test4(TraceParser):
-    def __init__(self, trace, pid):
-        super().__init__(trace, pid)
+    def __init__(self, trace, validation_args):
+        super().__init__(trace, validation_args['pid'])
         self.expect["poll_entry"]["big_poll_in"] = 0
         self.expect["poll_exit"]["big_poll_out"] = 0
 
@@ -544,8 +583,8 @@ class Test4(TraceParser):
         self.recorded_values["poll_exit"] = locals()
 
 class Test5(TraceParser):
-    def __init__(self, trace, pid):
-        super().__init__(trace, pid)
+    def __init__(self, trace, validation_args):
+        super().__init__(trace, validation_args['pid'])
         self.expect["poll_entry"]["poll_overflow_in"] = 0
         self.expect["poll_exit"]["poll_overflow_out"] = 0
 
@@ -578,8 +617,8 @@ class Test5(TraceParser):
 
 
 class Test6(TraceParser):
-    def __init__(self, trace, pid):
-        super().__init__(trace, pid)
+    def __init__(self, trace, validation_args):
+        super().__init__(trace, validation_args['pid'])
         self.expect["select_entry"]["pselect_invalid_in"] = 0
         self.expect["select_exit"]["pselect_invalid_out"] = 0
 
@@ -611,8 +650,8 @@ class Test6(TraceParser):
 
 
 class Test7(TraceParser):
-    def __init__(self, trace, pid):
-        super().__init__(trace, pid)
+    def __init__(self, trace, validation_args):
+        super().__init__(trace, validation_args['pid'])
         self.expect["poll_entry"]["poll_max_in"] = 0
         self.expect["poll_exit"]["poll_max_out"] = 0
 
@@ -642,8 +681,12 @@ class Test7(TraceParser):
 
 
 class Test8(TraceParser):
-    def __init__(self, trace, pid):
-        super().__init__(trace, pid)
+    def __init__(self, trace, validation_args):
+        super().__init__(trace, validation_args['pid'])
+
+        # Values expected in the trace
+        self.epoll_fd = validation_args['epollfd']
+
         self.expect["epoll_wait_entry"]["epoll_wait_invalid_in"] = 0
         self.expect["epoll_wait_exit"]["epoll_wait_invalid_out"] = 0
 
@@ -654,7 +697,7 @@ class Test8(TraceParser):
 
         # 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:
+        if epfd == self.epoll_fd and maxevents == 1 and timeout == -1:
             self.expect["epoll_wait_entry"]["epoll_wait_invalid_in"] = 1
 
         # Save values of local variables to print in case of test failure
@@ -675,8 +718,12 @@ class Test8(TraceParser):
 
 
 class Test9(TraceParser):
-    def __init__(self, trace, pid):
-        super().__init__(trace, pid)
+    def __init__(self, trace, validation_args):
+        super().__init__(trace, validation_args['pid'])
+
+        # Values expected in the trace
+        self.epoll_fd = validation_args['epollfd']
+
         self.expect["epoll_wait_entry"]["epoll_wait_max_in"] = 0
         self.expect["epoll_wait_exit"]["epoll_wait_max_out"] = 0
 
@@ -686,7 +733,7 @@ class Test9(TraceParser):
         timeout = event["timeout"]
 
         # check the proper working of INT_MAX maxevent value
-        if epfd == 3 and maxevents == 2147483647 and timeout == -1:
+        if epfd == self.epoll_fd and maxevents == 2147483647 and timeout == -1:
             self.expect["epoll_wait_entry"]["epoll_wait_max_in"] = 1
 
         # Save values of local variables to print in case of test failure
@@ -709,15 +756,15 @@ 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')
+    parser.add_argument('-o', '--validation-file', type=str, help='Validation file path')
     args = parser.parse_args()
 
     if not args.test:
-        print("Need to pass a test to validate (-t)")
+        print("Need to pass a test to validate (--test/-t)")
         sys.exit(1)
 
-    if not args.pid:
-        print("Need to pass the PID to check (-p)")
+    if not args.validation_file:
+        print("Need to pass the test validation file (--validation-file/-o)")
         sys.exit(1)
 
     traces = TraceCollection()
@@ -725,26 +772,33 @@ if __name__ == "__main__":
     if handle is None:
         sys.exit(1)
 
+    with open(args.validation_file) as f:
+        try:
+            test_validation_args = json.load(f)
+        except Exception as e:
+            print('Failed to parse validation file: ' + str(e))
+            sys.exit(1)
+
     t = None
 
     if args.test == 1:
-        t = Test1(traces, args.pid)
+        t = Test1(traces, test_validation_args)
     elif args.test == 2:
-        t = Test2(traces, args.pid)
+        t = Test2(traces, test_validation_args)
     elif args.test == 3:
-        t = Test3(traces, args.pid)
+        t = Test3(traces, test_validation_args)
     elif args.test == 4:
-        t = Test4(traces, args.pid)
+        t = Test4(traces, test_validation_args)
     elif args.test == 5:
-        t = Test5(traces, args.pid)
+        t = Test5(traces, test_validation_args)
     elif args.test == 6:
-        t = Test6(traces, args.pid)
+        t = Test6(traces, test_validation_args)
     elif args.test == 7:
-        t = Test7(traces, args.pid)
+        t = Test7(traces, test_validation_args)
     elif args.test == 8:
-        t = Test8(traces, args.pid)
+        t = Test8(traces, test_validation_args)
     elif args.test == 9:
-        t = Test9(traces, args.pid)
+        t = Test9(traces, test_validation_args)
     elif args.test == 10:
         # stress test, nothing reliable to check
         ret = 0
This page took 0.050976 seconds and 5 git commands to generate.