Add regression tests for exit()

This commit is contained in:
Ruihan Li 2024-11-25 08:09:18 +08:00 committed by Tate, Hongliang Tian
parent 35c20620bc
commit 1ab63c7bcb
5 changed files with 192 additions and 0 deletions

View File

@ -18,6 +18,7 @@ TEST_APPS := \
epoll \ epoll \
eventfd2 \ eventfd2 \
execve \ execve \
exit \
fdatasync \ fdatasync \
file_io \ file_io \
fork \ fork \

5
test/apps/exit/Makefile Normal file
View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: MPL-2.0
include ../test_common.mk
EXTRA_C_FLAGS := -static -lpthread

View File

@ -0,0 +1,87 @@
// SPDX-License-Identifier: MPL-2.0
#include "../network/test.h"
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/wait.h>
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()

View File

@ -0,0 +1,97 @@
// SPDX-License-Identifier: MPL-2.0
#include "../network/test.h"
#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#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()

View File

@ -15,6 +15,8 @@ clone3/clone_no_exit_signal
clone3/clone_process clone3/clone_process
cpu_affinity/cpu_affinity cpu_affinity/cpu_affinity
execve/execve execve/execve
exit/exit_code
exit/exit_procfs
eventfd2/eventfd2 eventfd2/eventfd2
fork/fork fork/fork
fork_c/fork fork_c/fork