diff options
Diffstat (limited to 'tools/testing/selftests/kselftest_harness.h')
-rw-r--r-- | tools/testing/selftests/kselftest_harness.h | 170 |
1 files changed, 72 insertions, 98 deletions
diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h index 666c9fde76da..2925e47db995 100644 --- a/tools/testing/selftests/kselftest_harness.h +++ b/tools/testing/selftests/kselftest_harness.h @@ -56,6 +56,8 @@ #include <asm/types.h> #include <ctype.h> #include <errno.h> +#include <linux/unistd.h> +#include <poll.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> @@ -65,7 +67,6 @@ #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> -#include <setjmp.h> #include "kselftest.h" @@ -172,14 +173,11 @@ #define __TEST_IMPL(test_name, _signal) \ static void test_name(struct __test_metadata *_metadata); \ - static inline void wrapper_##test_name( \ + static void wrapper_##test_name( \ struct __test_metadata *_metadata, \ - struct __fixture_variant_metadata *variant) \ + struct __fixture_variant_metadata __attribute__((unused)) *variant) \ { \ - _metadata->setup_completed = true; \ - if (setjmp(_metadata->env) == 0) \ - test_name(_metadata); \ - __test_check_assert(_metadata); \ + test_name(_metadata); \ } \ static struct __test_metadata _##test_name##_object = \ { .name = #test_name, \ @@ -258,7 +256,7 @@ * A bare "return;" statement may be used to return early. */ #define FIXTURE_SETUP(fixture_name) \ - void fixture_name##_setup( \ + static void fixture_name##_setup( \ struct __test_metadata __attribute__((unused)) *_metadata, \ FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \ const FIXTURE_VARIANT(fixture_name) \ @@ -307,7 +305,7 @@ __FIXTURE_TEARDOWN(fixture_name) #define __FIXTURE_TEARDOWN(fixture_name) \ - void fixture_name##_teardown( \ + static void fixture_name##_teardown( \ struct __test_metadata __attribute__((unused)) *_metadata, \ FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \ const FIXTURE_VARIANT(fixture_name) \ @@ -401,7 +399,7 @@ struct __test_metadata *_metadata, \ FIXTURE_DATA(fixture_name) *self, \ const FIXTURE_VARIANT(fixture_name) *variant); \ - static inline void wrapper_##fixture_name##_##test_name( \ + static void wrapper_##fixture_name##_##test_name( \ struct __test_metadata *_metadata, \ struct __fixture_variant_metadata *variant) \ { \ @@ -410,9 +408,9 @@ pid_t child = 1; \ int status = 0; \ /* Makes sure there is only one teardown, even when child forks again. */ \ - bool *teardown = mmap(NULL, sizeof(*teardown), \ + _metadata->no_teardown = mmap(NULL, sizeof(*_metadata->no_teardown), \ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); \ - *teardown = false; \ + *_metadata->no_teardown = true; \ if (sizeof(*self) > 0) { \ if (fixture_name##_teardown_parent) { \ self = mmap(NULL, sizeof(*self), PROT_READ | PROT_WRITE, \ @@ -422,31 +420,26 @@ self = &self_private; \ } \ } \ - if (setjmp(_metadata->env) == 0) { \ - /* _metadata and potentially self are shared with all forks. */ \ - child = fork(); \ - if (child == 0) { \ - fixture_name##_setup(_metadata, self, variant->data); \ - /* Let setup failure terminate early. */ \ - if (_metadata->exit_code) \ - _exit(0); \ - _metadata->setup_completed = true; \ - fixture_name##_##test_name(_metadata, self, variant->data); \ - } else if (child < 0 || child != waitpid(child, &status, 0)) { \ - ksft_print_msg("ERROR SPAWNING TEST GRANDCHILD\n"); \ - _metadata->exit_code = KSFT_FAIL; \ - } \ - } \ + _metadata->variant = variant->data; \ + _metadata->self = self; \ + /* _metadata and potentially self are shared with all forks. */ \ + child = fork(); \ if (child == 0) { \ - if (_metadata->setup_completed && !fixture_name##_teardown_parent && \ - __sync_bool_compare_and_swap(teardown, false, true)) \ - fixture_name##_teardown(_metadata, self, variant->data); \ + fixture_name##_setup(_metadata, self, variant->data); \ + /* Let setup failure terminate early. */ \ + if (_metadata->exit_code) \ + _exit(0); \ + *_metadata->no_teardown = false; \ + fixture_name##_##test_name(_metadata, self, variant->data); \ + _metadata->teardown_fn(false, _metadata, self, variant->data); \ _exit(0); \ + } else if (child < 0 || child != waitpid(child, &status, 0)) { \ + ksft_print_msg("ERROR SPAWNING TEST GRANDCHILD\n"); \ + _metadata->exit_code = KSFT_FAIL; \ } \ - if (_metadata->setup_completed && fixture_name##_teardown_parent && \ - __sync_bool_compare_and_swap(teardown, false, true)) \ - fixture_name##_teardown(_metadata, self, variant->data); \ - munmap(teardown, sizeof(*teardown)); \ + _metadata->teardown_fn(true, _metadata, self, variant->data); \ + munmap(_metadata->no_teardown, sizeof(*_metadata->no_teardown)); \ + _metadata->no_teardown = NULL; \ if (self && fixture_name##_teardown_parent) \ munmap(self, sizeof(*self)); \ if (WIFEXITED(status)) { \ @@ -456,7 +449,14 @@ /* Forward signal to __wait_for_test(). */ \ kill(getpid(), WTERMSIG(status)); \ } \ - __test_check_assert(_metadata); \ + } \ + static void wrapper_##fixture_name##_##test_name##_teardown( \ + bool in_parent, struct __test_metadata *_metadata, \ + void *self, const void *variant) \ + { \ + if (fixture_name##_teardown_parent == in_parent && \ + !__atomic_test_and_set(_metadata->no_teardown, __ATOMIC_RELAXED)) \ + fixture_name##_teardown(_metadata, self, variant); \ } \ static struct __test_metadata *_##fixture_name##_##test_name##_object; \ static void __attribute__((constructor)) \ @@ -467,6 +467,7 @@ object->name = #test_name; \ object->fn = &wrapper_##fixture_name##_##test_name; \ object->fixture = &_##fixture_name##_fixture_object; \ + object->teardown_fn = &wrapper_##fixture_name##_##test_name##_teardown; \ object->termsig = signal; \ object->timeout = tmout; \ _##fixture_name##_##test_name##_object = object; \ @@ -910,14 +911,16 @@ struct __test_metadata { struct __fixture_variant_metadata *); pid_t pid; /* pid of test when being run */ struct __fixture_metadata *fixture; + void (*teardown_fn)(bool in_parent, struct __test_metadata *_metadata, + void *self, const void *variant); int termsig; int exit_code; int trigger; /* extra handler after the evaluation */ int timeout; /* seconds to wait for test timeout */ - bool timed_out; /* did this test timeout instead of exiting? */ bool aborted; /* stopped test due to failed ASSERT */ - bool setup_completed; /* did setup finish? */ - jmp_buf env; /* for exiting out of test early */ + bool *no_teardown; /* fixture needs teardown */ + void *self; + const void *variant; struct __test_results *results; struct __test_metadata *prev, *next; }; @@ -951,88 +954,60 @@ static inline int __bail(int for_realz, struct __test_metadata *t) { /* if this is ASSERT, return immediately. */ if (for_realz) { - t->aborted = true; - longjmp(t->env, 1); + if (t->teardown_fn) + t->teardown_fn(false, t, t->self, t->variant); + abort(); } /* otherwise, end the for loop and continue. */ return 0; } -static inline void __test_check_assert(struct __test_metadata *t) -{ - if (t->aborted) - abort(); -} - -struct __test_metadata *__active_test; -static void __timeout_handler(int sig, siginfo_t *info, void *ucontext) -{ - struct __test_metadata *t = __active_test; - - /* Sanity check handler execution environment. */ - if (!t) { - fprintf(TH_LOG_STREAM, - "# no active test in SIGALRM handler!?\n"); - abort(); - } - if (sig != SIGALRM || sig != info->si_signo) { - fprintf(TH_LOG_STREAM, - "# %s: SIGALRM handler caught signal %d!?\n", - t->name, sig != SIGALRM ? sig : info->si_signo); - abort(); - } - - t->timed_out = true; - // signal process group - kill(-(t->pid), SIGKILL); -} - -void __wait_for_test(struct __test_metadata *t) +static void __wait_for_test(struct __test_metadata *t) { - struct sigaction action = { - .sa_sigaction = __timeout_handler, - .sa_flags = SA_SIGINFO, - }; - struct sigaction saved_action; /* * Sets status so that WIFEXITED(status) returns true and * WEXITSTATUS(status) returns KSFT_FAIL. This safe default value * should never be evaluated because of the waitpid(2) check and - * SIGALRM handling. + * timeout handling. */ int status = KSFT_FAIL << 8; - int child; + struct pollfd poll_child; + int ret, child, childfd; + bool timed_out = false; - if (sigaction(SIGALRM, &action, &saved_action)) { + childfd = syscall(__NR_pidfd_open, t->pid, 0); + if (childfd == -1) { t->exit_code = KSFT_FAIL; fprintf(TH_LOG_STREAM, - "# %s: unable to install SIGALRM handler\n", + "# %s: unable to open pidfd\n", t->name); return; } - __active_test = t; - t->timed_out = false; - alarm(t->timeout); - child = waitpid(t->pid, &status, 0); - if (child == -1 && errno != EINTR) { + + poll_child.fd = childfd; + poll_child.events = POLLIN; + ret = poll(&poll_child, 1, t->timeout * 1000); + if (ret == -1) { t->exit_code = KSFT_FAIL; fprintf(TH_LOG_STREAM, - "# %s: Failed to wait for PID %d (errno: %d)\n", - t->name, t->pid, errno); + "# %s: unable to wait on child pidfd\n", + t->name); return; + } else if (ret == 0) { + timed_out = true; + /* signal process group */ + kill(-(t->pid), SIGKILL); } - - alarm(0); - if (sigaction(SIGALRM, &saved_action, NULL)) { + child = waitpid(t->pid, &status, WNOHANG); + if (child == -1 && errno != EINTR) { t->exit_code = KSFT_FAIL; fprintf(TH_LOG_STREAM, - "# %s: unable to uninstall SIGALRM handler\n", - t->name); + "# %s: Failed to wait for PID %d (errno: %d)\n", + t->name, t->pid, errno); return; } - __active_test = NULL; - if (t->timed_out) { + if (timed_out) { t->exit_code = KSFT_FAIL; fprintf(TH_LOG_STREAM, "# %s: Test terminated by timeout\n", t->name); @@ -1205,9 +1180,9 @@ static bool test_enabled(int argc, char **argv, return !has_positive; } -void __run_test(struct __fixture_metadata *f, - struct __fixture_variant_metadata *variant, - struct __test_metadata *t) +static void __run_test(struct __fixture_metadata *f, + struct __fixture_variant_metadata *variant, + struct __test_metadata *t) { struct __test_xfail *xfail; char test_name[1024]; @@ -1218,8 +1193,7 @@ void __run_test(struct __fixture_metadata *f, t->exit_code = KSFT_PASS; t->trigger = 0; t->aborted = false; - t->setup_completed = false; - memset(t->env, 0, sizeof(t->env)); + t->no_teardown = NULL; memset(t->results->reason, 0, sizeof(t->results->reason)); snprintf(test_name, sizeof(test_name), "%s%s%s.%s", |