From d40c26206a44d9e247bbcdc01afb34c41d0c490d Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Thu, 10 Feb 2022 11:59:31 -0500 Subject: [PATCH] Tests: fix: select_poll_epoll: test assumes epoll fd value MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Change-Id: Ie373c63f6e6b9267ae2d785c9f0a532a5de37905 --- tests/regression/kernel/Makefile.am | 4 +- ...ect_poll_epoll.c => select_poll_epoll.cpp} | 409 ++++++++++-------- .../regression/kernel/test_select_poll_epoll | 169 ++++---- .../kernel/validate_select_poll_epoll.py | 132 ++++-- 4 files changed, 407 insertions(+), 307 deletions(-) rename tests/regression/kernel/{select_poll_epoll.c => select_poll_epoll.cpp} (67%) diff --git a/tests/regression/kernel/Makefile.am b/tests/regression/kernel/Makefile.am index 040b76423..dfbed73f4 100644 --- a/tests/regression/kernel/Makefile.am +++ b/tests/regression/kernel/Makefile.am @@ -17,8 +17,8 @@ EXTRA_DIST = test_all_events \ validate_select_poll_epoll.py noinst_PROGRAMS = select_poll_epoll -select_poll_epoll_SOURCES = select_poll_epoll.c -select_poll_epoll_LDADD = $(POPT_LIBS) +select_poll_epoll_SOURCES = select_poll_epoll.cpp +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: diff --git a/tests/regression/kernel/select_poll_epoll.c b/tests/regression/kernel/select_poll_epoll.cpp similarity index 67% rename from tests/regression/kernel/select_poll_epoll.c rename to tests/regression/kernel/select_poll_epoll.cpp index d0d7e4aea..abedc0aa9 100644 --- a/tests/regression/kernel/select_poll_epoll.c +++ b/tests/regression/kernel/select_poll_epoll.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #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; } diff --git a/tests/regression/kernel/test_select_poll_epoll b/tests/regression/kernel/test_select_poll_epoll index 16b0da525..6d5509b25 100755 --- a/tests/regression/kernel/test_select_poll_epoll +++ b/tests/regression/kernel/test_select_poll_epoll @@ -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 diff --git a/tests/regression/kernel/validate_select_poll_epoll.py b/tests/regression/kernel/validate_select_poll_epoll.py index b0cb590db..97c7764c0 100755 --- a/tests/regression/kernel/validate_select_poll_epoll.py +++ b/tests/regression/kernel/validate_select_poll_epoll.py @@ -9,6 +9,7 @@ import argparse import pprint import sys import time +import json from collections import defaultdict @@ -110,16 +111,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 @@ -127,6 +128,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) @@ -222,8 +229,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 @@ -234,6 +246,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"] @@ -319,7 +333,7 @@ class Test1(TraceParser): # check that we have FD 0 waiting for EPOLLIN|EPOLLPRI and that # data.fd = 0 - if epfd == 3 and 'EPOLL_CTL_ADD' in op_enum.labels 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: @@ -342,7 +356,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 @@ -363,10 +377,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 @@ -477,8 +516,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 @@ -506,8 +545,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 @@ -546,8 +585,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 @@ -580,8 +619,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 @@ -613,8 +652,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 @@ -644,8 +683,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 @@ -656,7 +699,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 @@ -677,8 +720,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 @@ -688,7 +735,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 @@ -711,39 +758,46 @@ if __name__ == "__main__": parser = argparse.ArgumentParser(description='Trace parser') parser.add_argument('path', metavar="", 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 = bt2.TraceCollectionMessageIterator(args.path) + 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 -- 2.34.1