From 8b3b2b4ff18e5240ef8efa91ee69f7cefbaf5884 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sun, 17 Feb 2008 16:27:17 +0000 Subject: [PATCH] Test program for syscall restarts. Only a few syscalls are tested yet. Nested syscall restarts (interrupted syscall in a signal handler) aren't tested yet. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23985 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/tests/system/kernel/Jamfile | 2 + .../system/kernel/syscall_restart_test.cpp | 689 ++++++++++++++++++ 2 files changed, 691 insertions(+) create mode 100644 src/tests/system/kernel/syscall_restart_test.cpp diff --git a/src/tests/system/kernel/Jamfile b/src/tests/system/kernel/Jamfile index 213677adef..d93c666c19 100644 --- a/src/tests/system/kernel/Jamfile +++ b/src/tests/system/kernel/Jamfile @@ -32,6 +32,8 @@ SimpleTest port_wakeup_test_9 : port_wakeup_test_9.cpp ; SimpleTest select_check : select_check.cpp ; SimpleTest select_close_test : select_close_test.cpp ; +SimpleTest syscall_restart_test : syscall_restart_test.cpp : network ; + SetSupportedPlatformsForTarget syscall_time : $(HAIKU_BEOS_COMPATIBLE_PLATFORMS) ; SimpleTest syscall_time : syscall_time.cpp ; diff --git a/src/tests/system/kernel/syscall_restart_test.cpp b/src/tests/system/kernel/syscall_restart_test.cpp new file mode 100644 index 0000000000..0eadd82109 --- /dev/null +++ b/src/tests/system/kernel/syscall_restart_test.cpp @@ -0,0 +1,689 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +enum run_mode { + RUN_IGNORE_SIGNAL, + RUN_HANDLE_SIGNAL, + RUN_HANDLE_SIGNAL_RESTART +}; + + +class Test { +public: + Test(const char* name) + : fName(name) + { + } + + virtual ~Test() + { + } + + bool Run(run_mode mode) + { + fRunMode = mode; + fSignalHandlerCalled = false; + + status_t error = Prepare(); + if (error != B_OK) + return Error("Failed to prepare test: %s", strerror(error)); + + thread_id thread = spawn_thread(_ThreadEntry, fName, B_NORMAL_PRIORITY, + this); + if (thread < 0) + return Error("Failed to spawn thread: %s\n", strerror(thread)); + + resume_thread(thread); + + // ... + // * interrupt without restart + // * interrupt with restart + + snooze(100000); + kill(thread, SIGINT); + + PrepareFinish(); + + status_t result; + wait_for_thread(thread, &result); + + if (result != (Interrupted() ? B_INTERRUPTED : B_OK)) { + return Error("Unexpected syscall return value: %s\n", + strerror(result)); + } + + if ((RunMode() == RUN_IGNORE_SIGNAL) == fSignalHandlerCalled) { + if (RunMode() == RUN_IGNORE_SIGNAL) + return Error("Handler was called but shouldn't have been!"); + else + return Error("Handler was not called!"); + } + + return Finish(Interrupted()); + } + + void Run() + { + printf("%s\n", fName); + + struct { + const char* name; + run_mode mode; + } tests[] = { + { "ignore signal", RUN_IGNORE_SIGNAL }, + { "handle signal no restart", RUN_HANDLE_SIGNAL }, + { "handle signal restart", RUN_HANDLE_SIGNAL_RESTART }, + {} + }; + + for (int i = 0; tests[i].name != NULL; i++) { + printf(" %-30s: ", tests[i].name); + fflush(stdout); + ClearError(); + if (Run(tests[i].mode)) + printf("ok\n"); + else + printf("failed (%s)\n", fError); + + Cleanup(); + } + } + + run_mode RunMode() const { return fRunMode; } + bool Interrupted() const { return RunMode() == RUN_HANDLE_SIGNAL; } + bigtime_t TimeWaited() const { return fTimeWaited; } + +protected: + virtual status_t Prepare() + { + return B_OK; + } + + virtual status_t DoSyscall() = 0; + + virtual void HandleSignal() + { + } + + virtual void PrepareFinish() + { + } + + virtual bool Finish(bool interrupted) + { + return true; + } + + virtual void Cleanup() + { + } + + bool Error(const char* format,...) + { + va_list args; + va_start(args, format); + vsnprintf(fError, sizeof(fError), format, args); + va_end(args); + + return false; + } + + bool Check(bool condition, const char* format,...) + { + if (condition) + return true; + + va_list args; + va_start(args, format); + vsnprintf(fError, sizeof(fError), format, args); + va_end(args); + + return false; + } + + void ClearError() + { + fError[0] = '\0'; + } + +private: + static status_t _ThreadEntry(void* data) + { + return ((Test*)data)->_TestThread(); + } + + static void _SignalHandler(int signal, char* userData) + { + Test* self = (Test*)userData; + + self->fSignalHandlerCalled = true; + self->HandleSignal(); + } + + status_t _TestThread() + { + // install handler + struct sigaction action; + if (RunMode() == RUN_IGNORE_SIGNAL) + action.sa_handler = SIG_IGN; + else + action.sa_handler = (void (*)(int))_SignalHandler; + + action.sa_flags = RunMode() == RUN_HANDLE_SIGNAL_RESTART + ? SA_RESTART : 0; + + sigemptyset(&action.sa_mask); + action.sa_userdata = this; + + sigaction(SIGINT, &action, NULL); + + bigtime_t startTime = system_time(); + status_t status = DoSyscall(); + fTimeWaited = system_time() - startTime; + + return status; + } + +private: + const char* fName; + run_mode fRunMode; + bool fSignalHandlerCalled; + bigtime_t fTimeWaited; + char fError[1024]; +}; + + +class SnoozeTest : public Test { +public: + SnoozeTest() + : Test("snooze") + { + } + + virtual status_t DoSyscall() + { + return snooze(1000000); + } + + virtual bool Finish(bool interrupted) + { + if (interrupted) + return Check(TimeWaited() < 200000, "waited too long"); + + return Check(TimeWaited() > 900000 && TimeWaited() < 1100000, + "waited %lld us instead of 1000000 us", TimeWaited()); + } +}; + + +class ReadTest : public Test { +public: + ReadTest() + : Test("read") + { + } + + virtual status_t Prepare() + { + fBytesRead = -1; + fFDs = (int[2]){-1, -1}; + + if (pipe(fFDs) != 0) + return errno; + + return B_OK; + } + + virtual status_t DoSyscall() + { + char buffer[256]; + fBytesRead = read(fFDs[0], buffer, sizeof(buffer)); + + return fBytesRead < 0 ? errno : B_OK; + } + + virtual void PrepareFinish() + { + write(fFDs[1], "Ingo", 4); + } + + virtual bool Finish(bool interrupted) + { + if (interrupted) + return Check(fBytesRead < 0, "unexpected read"); + + return Check(fBytesRead == 4, "should have read 4 bytes, read only %ld " + "bytes", fBytesRead); + } + + virtual void Cleanup() + { + close(fFDs[0]); + close(fFDs[1]); + } + +private: + bigtime_t fTimeWaited; + ssize_t fBytesRead; + int fFDs[2]; +}; + + +class WriteTest : public Test { +public: + WriteTest() + : Test("write") + { + } + + virtual status_t Prepare() + { + fBytesWritten = -1; + fFDs = (int[2]){-1, -1}; + + if (pipe(fFDs) != 0) + return errno; + + // fill pipe + fcntl(fFDs[1], F_SETFL, O_NONBLOCK); + while (write(fFDs[1], "a", 1) == 1); + + return B_OK; + } + + virtual status_t DoSyscall() + { + // blocking wait + fcntl(fFDs[1], F_SETFL, 0); + fBytesWritten = write(fFDs[1], "Ingo", 4); + + return fBytesWritten < 0 ? errno : B_OK; + } + + virtual void PrepareFinish() + { + char buffer[256]; + read(fFDs[0], buffer, sizeof(buffer)); + } + + virtual bool Finish(bool interrupted) + { + if (interrupted) + return Check(fBytesWritten < 0, "unexpected write"); + + return Check(fBytesWritten == 4, "should have written 4 bytes, wrote only %ld " + "bytes", fBytesWritten); + } + + virtual void Cleanup() + { + close(fFDs[0]); + close(fFDs[1]); + } + +private: + ssize_t fBytesWritten; + int fFDs[2]; +}; + + +class AcquireSwitchSemTest : public Test { +public: + AcquireSwitchSemTest(bool useSwitch) + : Test(useSwitch ? "switch_sem" : "acquire_sem"), + fSwitch(useSwitch) + { + } + + virtual status_t Prepare() + { + fSemaphore = create_sem(0, "test sem"); + + return (fSemaphore >= 0 ? B_OK : fSemaphore); + } + + virtual status_t DoSyscall() + { + if (fSwitch) + return switch_sem(-1, fSemaphore); + + return acquire_sem(fSemaphore); + } + + virtual void PrepareFinish() + { + release_sem(fSemaphore); + } + +/* + virtual bool Finish(bool interrupted) + { +// int32 semCount = -1; +// get_sem_count(fSemaphore, &semCount); + + if (interrupted) + return true; + + return Check(fBytesWritten == 4, "should have written 4 bytes, wrote only %ld " + "bytes", fBytesWritten); + } +*/ + + virtual void Cleanup() + { + delete_sem(fSemaphore); + } + +protected: + sem_id fSemaphore; + bool fSwitch; +}; + + +class AcquireSwitchSemEtcTest : public Test { +public: + AcquireSwitchSemEtcTest(bool useSwitch) + : Test(useSwitch ? "switch_sem_etc" : "acquire_sem_etc"), + fSwitch(useSwitch) + { + } + + virtual status_t Prepare() + { + fSemaphore = create_sem(0, "test sem"); + + return fSemaphore >= 0 ? B_OK : fSemaphore; + } + + virtual status_t DoSyscall() + { + status_t status; + if (fSwitch) { + status = switch_sem_etc(-1, fSemaphore, 1, B_RELATIVE_TIMEOUT, + 1000000); + } else { + status = acquire_sem_etc(fSemaphore, 1, B_RELATIVE_TIMEOUT, + 1000000); + } + + if (!Interrupted() && status == B_TIMED_OUT) + return B_OK; + + return status; + } + + virtual bool Finish(bool interrupted) + { + if (interrupted) + return Check(TimeWaited() < 200000, "waited too long"); + + return Check(TimeWaited() > 900000 && TimeWaited() < 1100000, + "waited %lld us instead of 1000000 us", TimeWaited()); + } + + virtual void Cleanup() + { + delete_sem(fSemaphore); + } + +protected: + sem_id fSemaphore; + bool fSwitch; +}; + + +class AcceptTest : public Test { +public: + AcceptTest() + : Test("accept") + { + } + + virtual status_t Prepare() + { + fAcceptedSocket = -1; + + fServerSocket = socket(AF_INET, SOCK_STREAM, 0); + if (fServerSocket < 0) { + fprintf(stderr, "Could not open server socket: %s\n", + strerror(errno)); + return errno; + } + + int reuse = 1; + if (setsockopt(fServerSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, + sizeof(int)) == -1) { + fprintf(stderr, "Could not make server socket reusable: %s\n", + strerror(errno)); + return errno; + } + + memset(&fServerAddress, 0, sizeof(sockaddr_in)); + fServerAddress.sin_family = AF_INET; + fServerAddress.sin_addr.s_addr = INADDR_LOOPBACK; + + if (bind(fServerSocket, (struct sockaddr *)&fServerAddress, + sizeof(struct sockaddr)) == -1) { + fprintf(stderr, "Could not bind server socket: %s\n", + strerror(errno)); + return errno; + } + + socklen_t length = sizeof(sockaddr_in); + getsockname(fServerSocket, (sockaddr*)&fServerAddress, + &length); + + if (listen(fServerSocket, 10) == -1) { + fprintf(stderr, "Could not listen on server socket: %s\n", + strerror(errno)); + return errno; + } + + return B_OK; + } + + virtual status_t DoSyscall() + { + sockaddr_in clientAddress; + socklen_t length = sizeof(struct sockaddr_in); + + fAcceptedSocket = accept(fServerSocket, + (struct sockaddr *)&clientAddress, &length); + if (fAcceptedSocket == -1) + return errno; + + return B_OK; + } + + virtual void PrepareFinish() + { + if (Interrupted()) + return; + + int clientSocket = socket(AF_INET, SOCK_STREAM, 0); + if (clientSocket == -1) { + fprintf(stderr, "Could not open client socket: %s\n", + strerror(errno)); + return; + } + + if (connect(clientSocket, (struct sockaddr *)&fServerAddress, + sizeof(struct sockaddr)) == -1) { + fprintf(stderr, "Could not connect to server socket: %s\n", + strerror(errno)); + } + + close(clientSocket); + } + + virtual bool Finish(bool interrupted) + { + if (interrupted) + return Check(fAcceptedSocket < 0, "got socket"); + + return Check(fAcceptedSocket >= 0, "got no socket"); + } + + virtual void Cleanup() + { + close(fAcceptedSocket); + close(fServerSocket); + } + +protected: + int fServerSocket; + sockaddr_in fServerAddress; + int fAcceptedSocket; +}; + + +class ReceiveTest : public Test { +public: + ReceiveTest() + : Test("recv") + { + } + + virtual status_t Prepare() + { + fBytesRead = -1; + fAcceptedSocket = -1; + fClientSocket = -1; + + fServerSocket = socket(AF_INET, SOCK_STREAM, 0); + if (fServerSocket < 0) { + fprintf(stderr, "Could not open server socket: %s\n", + strerror(errno)); + return errno; + } + + int reuse = 1; + if (setsockopt(fServerSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, + sizeof(int)) == -1) { + fprintf(stderr, "Could not make server socket reusable: %s\n", + strerror(errno)); + return errno; + } + + memset(&fServerAddress, 0, sizeof(sockaddr_in)); + fServerAddress.sin_family = AF_INET; + fServerAddress.sin_addr.s_addr = INADDR_LOOPBACK; + + if (bind(fServerSocket, (struct sockaddr *)&fServerAddress, + sizeof(struct sockaddr)) == -1) { + fprintf(stderr, "Could not bind server socket: %s\n", + strerror(errno)); + return errno; + } + + socklen_t length = sizeof(sockaddr_in); + getsockname(fServerSocket, (sockaddr*)&fServerAddress, + &length); + + if (listen(fServerSocket, 10) == -1) { + fprintf(stderr, "Could not listen on server socket: %s\n", + strerror(errno)); + return errno; + } + + fClientSocket = socket(AF_INET, SOCK_STREAM, 0); + if (fClientSocket == -1) { + fprintf(stderr, "Could not open client socket: %s\n", + strerror(errno)); + return errno; + } + + fcntl(fClientSocket, F_SETFL, O_NONBLOCK); + + if (connect(fClientSocket, (struct sockaddr *)&fServerAddress, + sizeof(struct sockaddr)) == -1) { + if (errno != EINPROGRESS) { + fprintf(stderr, "Could not connect to server socket: %s\n", + strerror(errno)); + return errno; + } + } + + sockaddr_in clientAddress; + length = sizeof(struct sockaddr_in); + + fAcceptedSocket = accept(fServerSocket, + (struct sockaddr *)&clientAddress, &length); + if (fAcceptedSocket == -1) + return errno; + + fcntl(fClientSocket, F_SETFL, 0); + + snooze(100000); + + return B_OK; + } + + virtual status_t DoSyscall() + { + char buffer[256]; + fBytesRead = recv(fAcceptedSocket, buffer, sizeof(buffer), 0); + + return fBytesRead < 0 ? errno : B_OK; + } + + virtual void PrepareFinish() + { + write(fClientSocket, "Axel", 4); + } + + virtual bool Finish(bool interrupted) + { + if (interrupted) + return Check(fBytesRead < 0, "unexpected read"); + + return Check(fBytesRead == 4, "should have read 4 bytes, read only %ld " + "bytes", fBytesRead); + } + + virtual void Cleanup() + { + close(fAcceptedSocket); + close(fServerSocket); + close(fClientSocket); + } + +protected: + int fServerSocket; + sockaddr_in fServerAddress; + int fAcceptedSocket; + int fClientSocket; + ssize_t fBytesRead; +}; + + +int +main() +{ + Test* tests[] = { +/* new SnoozeTest, + new ReadTest, + new WriteTest, + new AcquireSwitchSemTest(false), + new AcquireSwitchSemTest(true), + new AcquireSwitchSemEtcTest(false), + new AcquireSwitchSemEtcTest(true), + new AcceptTest, +*/ new ReceiveTest, + NULL + }; + + for (int i = 0; tests[i] != NULL; i++) + tests[i]->Run(); + + return 0; +}