test: cover proctest and restarttest
This commit is contained in:
1 parent
aea99e3f67
commit
d589332de4
2 files changed
+198
No files matched your search
@@ -0,0 +1,83 @@
|
||||
#include "ulib.h"
|
||||
|
||||
/*
|
||||
* Exercises the process-management syscalls added this wave: sysSpawn
|
||||
* (posix_spawn()-lite -- loads a binary into a brand new thread) and
|
||||
* sysWait (threadJoin, sched.c/sched.h) together, then sysExec (execve()-
|
||||
* lite -- replaces this very process's image in place, see syscall.c
|
||||
* doExec). Split across two helper binaries deliberately: spawntarget.c is
|
||||
* only ever spawned (never exec'd), exectarget.c is only ever exec'd
|
||||
* (never spawned) -- frostbyte has one shared address space system-wide
|
||||
* (paging.c has a single page table, no per-process CR3), so two binaries
|
||||
* whose fixed link addresses collide can never be genuinely live at the
|
||||
* same time. Keeping "the spawn target" and "the exec target" as distinct
|
||||
* binaries, and waiting for the spawned child before ever calling exec,
|
||||
* means this test never asks the kernel to do something the architecture
|
||||
* cannot support.
|
||||
*
|
||||
* This function never returns on its own success path: the final call is
|
||||
* userExec("/bin/exectarget"), which -- if the syscall does its job --
|
||||
* replaces this thread's own code/stack/pid identity with exectarget's, so
|
||||
* "proctest" never gets to print anything past that call. kmain.c's
|
||||
* execReaperThread separately threadJoin()s this same pid and checks it
|
||||
* eventually reports exectarget's exit code, which is the only way to
|
||||
* observe that the exec() actually happened from outside this process.
|
||||
*/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
print("proctest: start, pid=");
|
||||
printDec(userGetpid());
|
||||
print("\n");
|
||||
|
||||
long child = userSpawn("/bin/spawntarget", 0);
|
||||
if (child < 0) {
|
||||
print("proctest: FAIL spawn /bin/spawntarget\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
long status = userWait(child);
|
||||
if (status != 3) {
|
||||
print("proctest: FAIL wait returned unexpected status\n");
|
||||
return 1;
|
||||
}
|
||||
print("proctest: spawn+wait ok\n");
|
||||
|
||||
if (userSpawn("/bin/does-not-exist", 0) != -1) {
|
||||
print("proctest: FAIL spawn of missing binary should fail\n");
|
||||
return 1;
|
||||
}
|
||||
print("proctest: spawn bad path rejected\n");
|
||||
|
||||
if (userWait(999999) != -1) {
|
||||
print("proctest: FAIL wait on bogus pid should fail immediately\n");
|
||||
return 1;
|
||||
}
|
||||
print("proctest: wait bad pid rejected\n");
|
||||
|
||||
/* argtest.c checks its own argc/argv against exactly this list --
|
||||
proves the whole chain (copyUserArgv copying these out of THIS
|
||||
process's memory, elfBuildInitialStack laying them onto the child's
|
||||
brand new stack, crt.S's _start reading them back off of it) rather
|
||||
than just that spawn/wait still works with a null argv. */
|
||||
char* argtestArgv[] = { "/bin/argtest", "alpha", "beta", "gamma", 0 };
|
||||
long argChild = userSpawn("/bin/argtest", argtestArgv);
|
||||
if (argChild < 0) {
|
||||
print("proctest: FAIL spawn /bin/argtest\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
long argStatus = userWait(argChild);
|
||||
if (argStatus != 0) {
|
||||
print("proctest: FAIL argtest reported bad argv\n");
|
||||
return 1;
|
||||
}
|
||||
print("proctest: spawn with argv ok\n");
|
||||
|
||||
userExec("/bin/exectarget", 0);
|
||||
|
||||
/* Only reached if userExec failed outright (bad path, load failure) --
|
||||
a successful exec never returns here at all. */
|
||||
print("proctest: FAIL exec did not take over\n");
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
#include "ulib.h"
|
||||
|
||||
/*
|
||||
* Boot-time driver for user/restarttarget.c -- see that file's own comment
|
||||
* for the full two-phase SA_RESTART/EINTR story. Four real happens-before
|
||||
* rendezvous pipes (ready/announce1/announce2/data), never a sleep guess,
|
||||
* the same discipline nesttest.c already established for a comparable
|
||||
* race: read announce1 before sending the no-restart signal (sigUsr1), then
|
||||
* read announce2 -- which restarttarget.c only ever writes AFTER phase 1's
|
||||
* read has already come back as errInterrupted and been validated -- before
|
||||
* sending the restart-flagged signal (sigUsr2) and writing the real data
|
||||
* byte phase 2 expects to receive back.
|
||||
*/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
print("restarttest: start, pid=");
|
||||
printDec(userGetpid());
|
||||
print("\n");
|
||||
|
||||
long readyHandle = userPipeCreate();
|
||||
long announce1Handle = userPipeCreate();
|
||||
long announce2Handle = userPipeCreate();
|
||||
long dataHandle = userPipeCreate();
|
||||
if (readyHandle < 0 || announce1Handle < 0 || announce2Handle < 0 || dataHandle < 0) {
|
||||
print("restarttest: FAIL create pipes\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
long readyFd = userPipeAttach(readyHandle, 0);
|
||||
long announce1Fd = userPipeAttach(announce1Handle, 0);
|
||||
long announce2Fd = userPipeAttach(announce2Handle, 0);
|
||||
long dataFd = userPipeAttach(dataHandle, 1);
|
||||
if (readyFd < 0 || announce1Fd < 0 || announce2Fd < 0 || dataFd < 0) {
|
||||
print("restarttest: FAIL attach pipes\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
char readyHandleStr[24];
|
||||
char announce1HandleStr[24];
|
||||
char announce2HandleStr[24];
|
||||
char dataHandleStr[24];
|
||||
decToString(readyHandle, readyHandleStr);
|
||||
decToString(announce1Handle, announce1HandleStr);
|
||||
decToString(announce2Handle, announce2HandleStr);
|
||||
decToString(dataHandle, dataHandleStr);
|
||||
|
||||
char* targetArgv[] = { "/bin/restarttarget", readyHandleStr, announce1HandleStr,
|
||||
announce2HandleStr, dataHandleStr, 0 };
|
||||
long target = userSpawn("/bin/restarttarget", targetArgv);
|
||||
if (target < 0) {
|
||||
print("restarttest: FAIL spawn restarttarget\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
char byte;
|
||||
if (userRead((int)readyFd, &byte, 1) != 1) {
|
||||
print("restarttest: FAIL read ready byte\n");
|
||||
return 1;
|
||||
}
|
||||
userClose((int)readyFd);
|
||||
print("restarttest: restarttarget ready\n");
|
||||
|
||||
/* Real blocking read: does not return until restarttarget has actually
|
||||
written announce1, right before it calls its own victim read -- the
|
||||
happens-before that makes "sigUsr1 lands while genuinely blocked"
|
||||
meaningful rather than a coincidence of timing (same guarantee
|
||||
nesttest.c's handlerReadyFd already gives a different race). */
|
||||
if (userRead((int)announce1Fd, &byte, 1) != 1) {
|
||||
print("restarttest: FAIL read announce1 byte\n");
|
||||
return 1;
|
||||
}
|
||||
userClose((int)announce1Fd);
|
||||
print("restarttest: restarttarget about to block (phase1)\n");
|
||||
|
||||
if (userSigSend(target, sigUsr1) != 0) {
|
||||
print("restarttest: FAIL send sigUsr1 to restarttarget\n");
|
||||
return 1;
|
||||
}
|
||||
print("restarttest: sent sigUsr1 (no restart) to restarttarget\n");
|
||||
|
||||
/* restarttarget only ever writes announce2 after phase 1's read has
|
||||
already come back as errInterrupted and been validated, so this read
|
||||
doubles as confirmation phase 1 actually passed. */
|
||||
if (userRead((int)announce2Fd, &byte, 1) != 1) {
|
||||
print("restarttest: FAIL read announce2 byte\n");
|
||||
return 1;
|
||||
}
|
||||
userClose((int)announce2Fd);
|
||||
print("restarttest: restarttarget about to block (phase2)\n");
|
||||
|
||||
if (userSigSend(target, sigUsr2) != 0) {
|
||||
print("restarttest: FAIL send sigUsr2 to restarttarget\n");
|
||||
return 1;
|
||||
}
|
||||
print("restarttest: sent sigUsr2 (restart) to restarttarget\n");
|
||||
|
||||
if (userWrite((int)dataFd, "d", 1) != 1) {
|
||||
print("restarttest: FAIL write data byte\n");
|
||||
return 1;
|
||||
}
|
||||
userClose((int)dataFd);
|
||||
|
||||
long status = userWait(target);
|
||||
print("restarttest: restarttarget exit status=");
|
||||
printDec(status);
|
||||
print("\n");
|
||||
if (status != 55) {
|
||||
print("restarttest: FAIL expected restarttarget exit status 55\n");
|
||||
return 1;
|
||||
}
|
||||
print("restarttest: sa-restart-eintr-distinction-ok\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user