X-Git-Url: https://git.liburcu.org/?a=blobdiff_plain;ds=sidebyside;f=tests%2Fregression%2Fkernel%2Fselect_poll_epoll.c;h=abedc0aa929557c7aa859e76788d71d53e0f0cb4;hb=2af9cd67cd3e2f8021559d01dd21c44a4844a27e;hp=d0d7e4aeafae124566b73157c2565a4aef4a1bcb;hpb=6ba36f78b2d1b3ca1dd6e544183bac92915e788f;p=lttng-tools.git diff --git a/tests/regression/kernel/select_poll_epoll.c b/tests/regression/kernel/select_poll_epoll.c index d0d7e4aea..abedc0aa9 100644 --- a/tests/regression/kernel/select_poll_epoll.c +++ b/tests/regression/kernel/select_poll_epoll.c @@ -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; }