From 1ab63c7bcbf6bf868b63f5062cd9438426a98dd2 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Mon, 25 Nov 2024 08:09:18 +0800 Subject: [PATCH] Add regression tests for `exit()` --- test/apps/Makefile | 1 + test/apps/exit/Makefile | 5 ++ test/apps/exit/exit_code.c | 87 ++++++++++++++++++++++++++++++++ test/apps/exit/exit_procfs.c | 97 ++++++++++++++++++++++++++++++++++++ test/apps/scripts/process.sh | 2 + 5 files changed, 192 insertions(+) create mode 100644 test/apps/exit/Makefile create mode 100644 test/apps/exit/exit_code.c create mode 100644 test/apps/exit/exit_procfs.c diff --git a/test/apps/Makefile b/test/apps/Makefile index 18ea1be3..a04c577b 100644 --- a/test/apps/Makefile +++ b/test/apps/Makefile @@ -18,6 +18,7 @@ TEST_APPS := \ epoll \ eventfd2 \ execve \ + exit \ fdatasync \ file_io \ fork \ diff --git a/test/apps/exit/Makefile b/test/apps/exit/Makefile new file mode 100644 index 00000000..039e6e02 --- /dev/null +++ b/test/apps/exit/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 + +include ../test_common.mk + +EXTRA_C_FLAGS := -static -lpthread diff --git a/test/apps/exit/exit_code.c b/test/apps/exit/exit_code.c new file mode 100644 index 00000000..51ebde31 --- /dev/null +++ b/test/apps/exit/exit_code.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MPL-2.0 + +#include "../network/test.h" + +#include +#include +#include +#include + +struct exit_info { + int should_use_exit_group; + int should_exit_master_first; +}; + +static void *thread_slave(void *info_) +{ + struct exit_info *info = info_; + + if (info->should_exit_master_first) { + if (info->should_use_exit_group) + sleep(3600); + usleep(200 * 1000); + } + + if (info->should_use_exit_group) + syscall(SYS_exit_group, 55); + else + syscall(SYS_exit, 66); + + exit(-1); +} + +static void *thread_master(void *info_) +{ + struct exit_info *info = info_; + pthread_t tid; + + CHECK(pthread_create(&tid, NULL, &thread_slave, info)); + + if (!info->should_exit_master_first) { + if (info->should_use_exit_group) + sleep(3600); + usleep(200 * 1000); + } + + if (info->should_use_exit_group) + syscall(SYS_exit_group, 77); + else + syscall(SYS_exit, 88); + + exit(-1); +} + +FN_TEST(exit_two_threads) +{ + struct exit_info info; + int stat; + + info.should_use_exit_group = 1; + info.should_exit_master_first = 1; + if (CHECK(fork()) == 0) { + thread_master(&info); + } + TEST_RES(wait(&stat), WIFEXITED(stat) && WEXITSTATUS(stat) == 77); + + info.should_use_exit_group = 1; + info.should_exit_master_first = 0; + if (CHECK(fork()) == 0) { + thread_master(&info); + } + TEST_RES(wait(&stat), WIFEXITED(stat) && WEXITSTATUS(stat) == 55); + + info.should_use_exit_group = 0; + info.should_exit_master_first = 1; + if (CHECK(fork()) == 0) { + thread_master(&info); + } + TEST_RES(wait(&stat), WIFEXITED(stat) && WEXITSTATUS(stat) == 66); + + info.should_use_exit_group = 0; + info.should_exit_master_first = 0; + if (CHECK(fork()) == 0) { + thread_master(&info); + } + TEST_RES(wait(&stat), WIFEXITED(stat) && WEXITSTATUS(stat) == 88); +} +END_TEST() diff --git a/test/apps/exit/exit_procfs.c b/test/apps/exit/exit_procfs.c new file mode 100644 index 00000000..dc4f0015 --- /dev/null +++ b/test/apps/exit/exit_procfs.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MPL-2.0 + +#include "../network/test.h" + +#include +#include +#include +#include + +#define EXIT_PARENT_FIRST ((void *)1) +#define EXIT_CHILD_FIRST ((void *)2) + +// TODO: Use `system` directly after we implement `vfork`. +static int mini_system(const char *cmd) +{ + int pid; + int stat; + + pid = CHECK(fork()); + + if (pid == 0) { + CHECK(execlp("sh", "sh", "-c", cmd, NULL)); + exit(-1); + } + + CHECK_WITH(waitpid(pid, &stat, 0), _ret == pid && WIFEXITED(stat)); + + return WEXITSTATUS(stat); +} + +static void *thread_slave(void *arg) +{ + if (arg == EXIT_CHILD_FIRST) { + CHECK_WITH( + mini_system( + "cat /proc/$PPID/status | grep '^Threads:\t2$'"), + _ret == 0); + + syscall(SYS_exit, 0); + } else { + // When the main thread exits, it becomes a zombie, so we still + // have two threads. + usleep(200 * 1000); + CHECK_WITH( + mini_system( + "cat /proc/$PPID/status | grep '^Threads:\t2$'"), + _ret == 0); + + syscall(SYS_exit, 0); + } + + exit(-1); +} + +static void thread_master(void *arg) +{ + pthread_t tid; + + CHECK(pthread_create(&tid, NULL, &thread_slave, arg)); + + if (arg == EXIT_PARENT_FIRST) { + CHECK_WITH( + mini_system( + "cat /proc/$PPID/status | grep '^Threads:\t2$'"), + _ret == 0); + + syscall(SYS_exit, 0); + } else { + // When a non-main thread exits, its resource is automatically + // freed, so we only have one thread. + usleep(200 * 1000); + CHECK_WITH( + mini_system( + "cat /proc/$PPID/status | grep '^Threads:\t1$'"), + _ret == 0); + + syscall(SYS_exit, 0); + } + + exit(-1); +} + +FN_TEST(exit_procfs) +{ + int stat; + + if (CHECK(fork()) == 0) { + thread_master(EXIT_CHILD_FIRST); + } + TEST_RES(wait(&stat), WIFEXITED(stat) && WEXITSTATUS(stat) == 0); + + if (CHECK(fork()) == 0) { + thread_master(EXIT_PARENT_FIRST); + } + TEST_RES(wait(&stat), WIFEXITED(stat) && WEXITSTATUS(stat) == 0); +} +END_TEST() diff --git a/test/apps/scripts/process.sh b/test/apps/scripts/process.sh index 58617068..68845b9e 100755 --- a/test/apps/scripts/process.sh +++ b/test/apps/scripts/process.sh @@ -15,6 +15,8 @@ clone3/clone_no_exit_signal clone3/clone_process cpu_affinity/cpu_affinity execve/execve +exit/exit_code +exit/exit_procfs eventfd2/eventfd2 fork/fork fork_c/fork