PM: add support for wait4(2)

This patch adds support for the wait4 system call, and with that the
wait3 call as well.  The implementation is absolutely minimal: only
user and system times of the exited child are returned (with all other
rusage fields left zero), and there is no support for tracers.  Still,
this should cover the main use cases of wait4.

Change-Id: I7a04589a8423a23990ab39aa38e85d535556743a
This commit is contained in:
David van Moolenbroek 2015-09-28 11:11:55 +00:00
parent bc2d75fa05
commit 29346ab043
17 changed files with 268 additions and 161 deletions

View File

@ -19,7 +19,6 @@ SRCS+= dhcp_gettag.c dhcp_settag.c fsversion.c gcov.c gcov_flush.c itoa.c \
# closefrom.c confstr.c extattr.c getdevmajor.c \
# pthread_atfork.c \
# sysctlbyname.c sysctlgetmibinfo.c sysctlnametomib.c
# wait3.c
# To be ported
# nlist.c nlist_aout.c nlist_coff.c nlist_ecoff.c nlist_elf32.c nlist_elf64.c
@ -55,7 +54,7 @@ SRCS+= _errno.c alarm.c alphasort.c arc4random.c assert.c basename.c clock.c \
syslog.c telldir.c time.c \
times.c toascii.c tolower_.c ttyname.c ttyslot.c toupper_.c ualarm.c \
ulimit.c uname.c unvis.c usleep.c utime.c utimens.c utmp.c \
utmpx.c valloc.c vis.c wait.c waitpid.c warn.c warnx.c \
utmpx.c valloc.c vis.c wait.c wait3.c waitpid.c warn.c warnx.c \
vwarn.c vwarnx.c verr.c verrx.c wordexp.c
.endif

View File

@ -13,7 +13,7 @@
/* Message type 0 is traditionally reserved. */
#define PM_EXIT (PM_BASE + 1)
#define PM_FORK (PM_BASE + 2)
#define PM_WAITPID (PM_BASE + 3)
#define PM_WAIT4 (PM_BASE + 3)
#define PM_GETPID (PM_BASE + 4)
#define PM_SETUID (PM_BASE + 5)
#define PM_GETUID (PM_BASE + 6)

View File

@ -573,10 +573,11 @@ _ASSERT_MSG_SIZE(mess_lc_pm_time);
typedef struct {
pid_t pid;
int options;
vir_bytes addr; /* struct rusage * */
uint8_t padding[48];
} mess_lc_pm_waitpid;
_ASSERT_MSG_SIZE(mess_lc_pm_waitpid);
uint8_t padding[44];
} mess_lc_pm_wait4;
_ASSERT_MSG_SIZE(mess_lc_pm_wait4);
typedef struct {
cp_grant_id_t grant;
@ -1519,8 +1520,8 @@ typedef struct {
int status;
uint8_t padding[52];
} mess_pm_lc_waitpid;
_ASSERT_MSG_SIZE(mess_pm_lc_waitpid);
} mess_pm_lc_wait4;
_ASSERT_MSG_SIZE(mess_pm_lc_wait4);
typedef struct {
int suid;
@ -2090,7 +2091,7 @@ typedef struct noxfer_message {
mess_lc_pm_sprof m_lc_pm_sprof;
mess_lc_pm_sysuname m_lc_pm_sysuname;
mess_lc_pm_time m_lc_pm_time;
mess_lc_pm_waitpid m_lc_pm_waitpid;
mess_lc_pm_wait4 m_lc_pm_wait4;
mess_lc_readclock_rtcdev m_lc_readclock_rtcdev;
mess_lc_svrctl m_lc_svrctl;
mess_lc_vfs_chown m_lc_vfs_chown;
@ -2196,7 +2197,7 @@ typedef struct noxfer_message {
mess_pm_lc_ptrace m_pm_lc_ptrace;
mess_pm_lc_sigset m_pm_lc_sigset;
mess_pm_lc_time m_pm_lc_time;
mess_pm_lc_waitpid m_pm_lc_waitpid;
mess_pm_lc_wait4 m_pm_lc_wait4;
mess_pm_lexec_exec_new m_pm_lexec_exec_new;
mess_pm_lsys_getepinfo m_pm_lsys_getepinfo;
mess_pm_lsys_getprocnr m_pm_lsys_getprocnr;

View File

@ -1,22 +0,0 @@
#include <sys/cdefs.h>
#include <lib.h>
#include "namespace.h"
#include <string.h>
#include <sys/wait.h>
#ifdef __weak_alias
__weak_alias(wait, _wait)
#endif
pid_t wait(int * status)
{
message m;
memset(&m, 0, sizeof(m));
m.m_lc_pm_waitpid.pid = -1;
m.m_lc_pm_waitpid.options = 0;
if (_syscall(PM_PROC_NR, PM_WAITPID, &m) < 0) return(-1);
if (status != 0) *status = m.m_pm_lc_waitpid.status;
return(m.m_type);
}

View File

@ -1,22 +0,0 @@
#include <sys/cdefs.h>
#include <lib.h>
#include "namespace.h"
#include <string.h>
#include <sys/wait.h>
#ifdef __weak_alias
__weak_alias(waitpid, _waitpid)
#endif
pid_t waitpid(pid_t pid, int *status, int options)
{
message m;
memset(&m, 0, sizeof(m));
m.m_lc_pm_waitpid.pid = pid;
m.m_lc_pm_waitpid.options = options;
if (_syscall(PM_PROC_NR, PM_WAITPID, &m) < 0) return(-1);
if (status != 0) *status = m.m_pm_lc_waitpid.status;
return m.m_type;
}

View File

@ -19,7 +19,8 @@ SRCS+= accept.c access.c adjtime.c bind.c brk.c sbrk.c m_closefrom.c getsid.c \
vectorio.c shutdown.c sigaction.c sigpending.c sigreturn.c sigsuspend.c\
sigprocmask.c socket.c socketpair.c stat.c statvfs.c svrctl.c \
symlink.c \
sync.c syscall.c sysuname.c truncate.c umask.c unlink.c write.c \
sync.c syscall.c sysuname.c truncate.c umask.c unlink.c \
wait4.c write.c \
utimensat.c utimes.c futimes.c lutimes.c futimens.c \
_exit.c _ucontext.c environ.c __getcwd.c vfork.c sizeup.c init.c \
getrusage.c setrlimit.c setpgid.c

View File

@ -0,0 +1,26 @@
#include <sys/cdefs.h>
#include <lib.h>
#include "namespace.h"
#include <string.h>
#include <sys/wait.h>
#ifdef __weak_alias
__weak_alias(wait4, __wait450)
#endif
pid_t
wait4(pid_t pid, int * status, int options, struct rusage * rusage)
{
message m;
memset(&m, 0, sizeof(m));
m.m_lc_pm_wait4.pid = pid;
m.m_lc_pm_wait4.options = options;
m.m_lc_pm_wait4.addr = (vir_bytes)rusage;
if (_syscall(PM_PROC_NR, PM_WAIT4, &m) < 0) return(-1);
if (status != NULL) *status = m.m_pm_lc_wait4.status;
return m.m_type;
}

View File

@ -192,7 +192,7 @@ CLEANFILES+= errlist.c
.for f in \
_errno.o \
getprogname.o setprogname.o execle.o sleep.o time.o \
ctype_.o tolower_.o toupper_.o usleep.o sigsetops.o
ctype_.o tolower_.o toupper_.o usleep.o waitpid.o sigsetops.o
${f} ${f:C/\.o/.bc/}: ${LIBCDIR}/gen/${f:C/\.o/.c/}
OBJS+= ${f}
CLEANFILES+= ${f}
@ -209,7 +209,7 @@ CPPFLAGS.tolower_.c+= -I${LIBCDIR}/locale
CPPFLAGS.toupper_.c+= -I${LIBCDIR}/locale
.for f in \
waitpid.o read_tsc_64.o fslib.o itoa.o oneC_sum.o
read_tsc_64.o fslib.o itoa.o oneC_sum.o
${f} ${f:C/\.o/.bc/}: ${LIBMINIXCDIR}/gen/${f:C/\.o/.c/}
OBJS+= ${f}
CLEANFILES+= ${f}
@ -289,7 +289,7 @@ CPPFLAGS.strcspn.c+= -D_LIBC
init.o kernel_utils.o link.o loadname.o lseek.o _mcontext.o mknod.o \
mmap.o nanosleep.o open.o pread.o pwrite.o read.o sbrk.o \
select.o setuid.o sigprocmask.o stack_utils.o stat.o stime.o \
syscall.o _ucontext.o umask.o unlink.o write.o \
syscall.o _ucontext.o umask.o unlink.o wait4.o write.o \
kill.o
${f} ${f:C/\.o/.bc/}: ${LIBMINIXCDIR}/sys/${f:C/\.o/.c/}
OBJS+= ${f}

View File

@ -1,11 +1,11 @@
/* This file deals with creating processes (via FORK) and deleting them (via
* EXIT/WAITPID). When a process forks, a new slot in the 'mproc' table is
* EXIT/WAIT4). When a process forks, a new slot in the 'mproc' table is
* allocated for it, and a copy of the parent's core image is made for the
* child. Then the kernel and file system are informed. A process is removed
* from the 'mproc' table when two events have occurred: (1) it has exited or
* been killed by a signal, and (2) the parent has done a WAITPID. If the
* been killed by a signal, and (2) the parent has done a WAIT4. If the
* process exits first, it continues to occupy a slot until the parent does a
* WAITPID.
* WAIT4.
*
* The entry points into this file are:
* do_fork: perform the FORK system call
@ -13,7 +13,7 @@
* do_exit: perform the EXIT system call (by calling exit_proc())
* exit_proc: actually do the exiting, and tell VFS about it
* exit_restart: continue exiting a process after VFS has replied
* do_waitpid: perform the WAITPID system call
* do_wait4: perform the WAIT4 system call
* wait_test: check whether a parent is waiting for a child
*/
@ -33,7 +33,7 @@
static void zombify(struct mproc *rmp);
static void check_parent(struct mproc *child, int try_cleanup);
static void tell_parent(struct mproc *child);
static int tell_parent(struct mproc *child, vir_bytes addr);
static void tell_tracer(struct mproc *child);
static void tracer_died(struct mproc *child);
static void cleanup(register struct mproc *rmp);
@ -447,24 +447,26 @@ int dump_core; /* flag indicating whether to dump core */
}
/*===========================================================================*
* do_waitpid *
* do_wait4 *
*===========================================================================*/
int do_waitpid()
int do_wait4()
{
/* A process wants to wait for a child to terminate. If a child is already
* waiting, go clean it up and let this WAITPID call terminate. Otherwise,
* waiting, go clean it up and let this WAIT4 call terminate. Otherwise,
* really wait.
* A process calling WAITPID never gets a reply in the usual way at the end
* A process calling WAIT4 never gets a reply in the usual way at the end
* of the main loop (unless WNOHANG is set or no qualifying child exists).
* If a child has already exited, the routine tell_parent() sends the reply
* to awaken the caller.
*/
register struct mproc *rp;
int i, pidarg, options, children;
vir_bytes addr;
int i, pidarg, options, children, waited_for;
/* Set internal variables. */
pidarg = m_in.m_lc_pm_waitpid.pid; /* 1st param */
options = m_in.m_lc_pm_waitpid.options; /* 3rd param */
pidarg = m_in.m_lc_pm_wait4.pid; /* 1st param */
options = m_in.m_lc_pm_wait4.options; /* 3rd param */
addr = m_in.m_lc_pm_wait4.addr; /* 4th param */
if (pidarg == 0) pidarg = -mp->mp_procgrp; /* pidarg < 0 ==> proc grp */
/* Is there a child waiting to be collected? At this point, pidarg != 0:
@ -497,9 +499,12 @@ int do_waitpid()
*/
for (i = 1; i < _NSIG; i++) {
if (sigismember(&rp->mp_sigtrace, i)) {
/* TODO: rusage support */
sigdelset(&rp->mp_sigtrace, i);
mp->mp_reply.m_pm_lc_waitpid.status = W_STOPCODE(i);
mp->mp_reply.m_pm_lc_wait4.status =
W_STOPCODE(i);
return(rp->mp_pid);
}
}
@ -509,8 +514,9 @@ int do_waitpid()
if (rp->mp_parent == who_p) {
if (rp->mp_flags & ZOMBIE) {
/* This child meets the pid test and has exited. */
tell_parent(rp); /* this child has already exited */
if (!(rp->mp_flags & VFS_CALL))
waited_for = tell_parent(rp, addr);
if (waited_for && !(rp->mp_flags & VFS_CALL))
cleanup(rp);
return(SUSPEND);
}
@ -525,6 +531,7 @@ int do_waitpid()
}
mp->mp_flags |= WAITING; /* parent wants to wait */
mp->mp_wpid = (pid_t) pidarg; /* save pid for later */
mp->mp_waddr = addr; /* save rusage addr for later */
return(SUSPEND); /* do not reply, let it wait */
} else {
/* No child even meets the pid test. Return error immediately. */
@ -614,7 +621,8 @@ int try_cleanup; /* clean up the child when done? */
*/
}
else if (wait_test(p_mp, child)) {
tell_parent(child);
if (!tell_parent(child, p_mp->mp_waddr))
try_cleanup = FALSE; /* child is still there */
/* The 'try_cleanup' flag merely saves us from having to be really
* careful with statement ordering in exit_proc() and exit_restart().
@ -631,11 +639,19 @@ int try_cleanup; /* clean up the child when done? */
/*===========================================================================*
* tell_parent *
*===========================================================================*/
static void tell_parent(child)
register struct mproc *child; /* tells which process is exiting */
static int tell_parent(struct mproc *child, vir_bytes addr)
{
/* Tell the parent of the given process that it has terminated, by satisfying
* the parent's ongoing wait4() call. If the parent has requested the child
* tree's resource usage, copy that information out first. The copy may fail;
* in that case, the parent's wait4() call will return with an error, but the
* child will remain a zombie. Return TRUE if the child is cleaned up, or
* FALSE if the child is still a zombie.
*/
struct rusage r_usage;
int mp_parent;
struct mproc *parent;
int r;
mp_parent= child->mp_parent;
if (mp_parent <= 0)
@ -646,8 +662,26 @@ register struct mproc *child; /* tells which process is exiting */
panic("tell_parent: telling parent again");
parent = &mproc[mp_parent];
/* See if we need to report resource usage to the parent. */
if (addr) {
/* We report only user and system times for now. TODO: support other
* fields, although this is tricky since the child process is already
* gone as far as the kernel and other services are concerned..
*/
memset(&r_usage, 0, sizeof(r_usage));
set_rusage_times(&r_usage, child->mp_child_utime,
child->mp_child_stime);
if ((r = sys_datacopy(SELF, (vir_bytes)&r_usage, parent->mp_endpoint,
addr, sizeof(r_usage))) != OK) {
reply(child->mp_parent, r);
return FALSE; /* copy error - the child is still there */
}
}
/* Wake up the parent by sending the reply message. */
parent->mp_reply.m_pm_lc_waitpid.status =
parent->mp_reply.m_pm_lc_wait4.status =
W_EXITCODE(child->mp_exitstatus, child->mp_sigstatus);
reply(child->mp_parent, child->mp_pid);
parent->mp_flags &= ~WAITING; /* parent no longer waiting */
@ -659,6 +693,8 @@ register struct mproc *child; /* tells which process is exiting */
*/
parent->mp_child_utime += child->mp_child_utime;
parent->mp_child_stime += child->mp_child_stime;
return TRUE; /* child has been waited for */
}
/*===========================================================================*
@ -677,7 +713,9 @@ struct mproc *child; /* tells which process is exiting */
panic("tell_tracer: child not a zombie");
tracer = &mproc[mp_tracer];
tracer->mp_reply.m_pm_lc_waitpid.status =
/* TODO: rusage support */
tracer->mp_reply.m_pm_lc_wait4.status =
W_EXITCODE(child->mp_exitstatus, (child->mp_sigstatus & 0377));
reply(child->mp_tracer, child->mp_pid);
tracer->mp_flags &= ~WAITING; /* tracer no longer waiting */

View File

@ -410,7 +410,6 @@ do_getrusage(void)
{
clock_t user_time, sys_time;
struct rusage r_usage;
u64_t usec;
int r, children;
if (m_in.m_lc_pm_rusage.who != RUSAGE_SELF &&
@ -444,12 +443,7 @@ do_getrusage(void)
}
/* In both cases, convert from clock ticks to microseconds. */
usec = user_time * 1000000 / sys_hz();
r_usage.ru_utime.tv_sec = usec / 1000000;
r_usage.ru_utime.tv_usec = usec % 1000000;
usec = sys_time * 1000000 / sys_hz();
r_usage.ru_stime.tv_sec = usec / 1000000;
r_usage.ru_stime.tv_usec = usec % 1000000;
set_rusage_times(&r_usage, user_time, sys_time);
/* Get additional fields from VM. */
if ((r = vm_getrusage(who_e, &r_usage, children)) != OK)

View File

@ -22,6 +22,7 @@ EXTERN struct mproc {
endpoint_t mp_endpoint; /* kernel endpoint id */
pid_t mp_procgrp; /* pid of process group (used for signals) */
pid_t mp_wpid; /* pid this process is waiting for */
vir_bytes mp_waddr; /* struct rusage address while waiting */
int mp_parent; /* index of parent process */
int mp_tracer; /* index of tracer process, or NO_TRACER */
@ -76,8 +77,8 @@ EXTERN struct mproc {
/* Flag values */
#define IN_USE 0x00001 /* set when 'mproc' slot in use */
#define WAITING 0x00002 /* set by WAITPID system call */
#define ZOMBIE 0x00004 /* waiting for parent to issue WAITPID call */
#define WAITING 0x00002 /* set by WAIT4 system call */
#define ZOMBIE 0x00004 /* waiting for parent to issue WAIT4 call */
#define PROC_STOPPED 0x00008 /* process is stopped in the kernel */
#define ALARM_ON 0x00010 /* set when SIGALRM timer started */
#define EXITING 0x00020 /* set by EXIT, process is now exiting */
@ -90,7 +91,7 @@ EXTERN struct mproc {
#define PRIV_PROC 0x02000 /* system process, special privileges */
#define PARTIAL_EXEC 0x04000 /* process got a new map but no content */
#define TRACE_EXIT 0x08000 /* tracer is forcing this process to exit */
#define TRACE_ZOMBIE 0x10000 /* waiting for tracer to issue WAITPID call */
#define TRACE_ZOMBIE 0x10000 /* waiting for tracer to issue WAIT4 call */
#define DELAY_CALL 0x20000 /* waiting for call before sending signal */
#define TAINTED 0x40000 /* process is 'tainted' */

View File

@ -22,7 +22,7 @@ int do_srv_fork(void);
int do_exit(void);
void exit_proc(struct mproc *rmp, int exit_status, int dump_core);
void exit_restart(struct mproc *rmp, int dump_core);
int do_waitpid(void);
int do_wait4(void);
int wait_test(struct mproc *rmp, struct mproc *child);
/* getset.c */
@ -88,3 +88,5 @@ struct mproc *find_proc(pid_t lpid);
int nice_to_priority(int nice, unsigned *new_q);
int pm_isokendpt(int ep, int *proc);
void tell_vfs(struct mproc *rmp, message *m_ptr);
void set_rusage_times(struct rusage *r_usage, clock_t user_time,
clock_t sys_time);

View File

@ -14,7 +14,7 @@
int (* const call_vec[NR_PM_CALLS])(void) = {
CALL(PM_EXIT) = do_exit, /* _exit(2) */
CALL(PM_FORK) = do_fork, /* fork(2) */
CALL(PM_WAITPID) = do_waitpid, /* waitpid(2) */
CALL(PM_WAIT4) = do_wait4, /* wait4(2) */
CALL(PM_GETPID) = do_get, /* get[p]pid(2) */
CALL(PM_SETUID) = do_set, /* setuid(2) */
CALL(PM_GETUID) = do_get, /* get[e]uid(2) */

View File

@ -265,10 +265,12 @@ int signo;
rmp->mp_flags |= TRACE_STOPPED;
if (wait_test(rpmp, rmp)) {
/* TODO: rusage support */
sigdelset(&rmp->mp_sigtrace, signo);
rpmp->mp_flags &= ~WAITING; /* parent is no longer waiting */
rpmp->mp_reply.m_pm_lc_waitpid.status = W_STOPCODE(signo);
rpmp->mp_reply.m_pm_lc_wait4.status = W_STOPCODE(signo);
reply(rmp->mp_tracer, rmp->mp_pid);
}
}

View File

@ -7,6 +7,7 @@
* nice_to_priority convert nice level to priority queue
* pm_isokendpt: check the validity of an endpoint
* tell_vfs: send a request to VFS on behalf of a process
* set_rusage_times: store user and system times in rusage structure
*/
#include "pm.h"
@ -136,3 +137,20 @@ message *m_ptr;
rmp->mp_flags |= VFS_CALL;
}
/*===========================================================================*
* set_rusage_times *
*===========================================================================*/
void
set_rusage_times(struct rusage * r_usage, clock_t user_time, clock_t sys_time)
{
u64_t usec;
usec = user_time * 1000000 / sys_hz();
r_usage->ru_utime.tv_sec = usec / 1000000;
r_usage->ru_utime.tv_usec = usec % 1000000;
usec = sys_time * 1000000 / sys_hz();
r_usage->ru_stime.tv_sec = usec / 1000000;
r_usage->ru_stime.tv_usec = usec % 1000000;
}

View File

@ -1,4 +1,4 @@
/* Test 75 - getrusage functionality test.
/* Test 75 - getrusage and wait4 test.
*/
#include <sys/resource.h>
@ -7,6 +7,7 @@
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <unistd.h>
#include "common.h"
@ -30,76 +31,137 @@ spin(void)
struct timeval start_time;
struct timeval end_time;
unsigned int loop = 0;
if (gettimeofday(&start_time, NULL) == -1) {
e(1);
exit(1);
}
if (gettimeofday(&start_time, NULL) == -1) e(1);
end_time = start_time;
do {
if ((++loop % 3000000000) == 0) {
if (gettimeofday(&end_time, NULL) == -1) {
e(1);
exit(1);
}
if (gettimeofday(&end_time, NULL) == -1) e(1);
}
} while (start_time.tv_sec + 10 > end_time.tv_sec);
}
int
main(int argc, char *argv[])
/*
* Test getrusage(2).
*/
static void
test75a(void)
{
struct rusage r_usage1;
struct rusage r_usage2;
struct rusage r_usage3;
pid_t child;
int status = 0;
start(75);
if ((getrusage(RUSAGE_SELF + 1, &r_usage1) != -1 || errno != EINVAL) ||
(getrusage(RUSAGE_CHILDREN - 1, &r_usage1) != -1 ||
errno != EINVAL) || (getrusage(RUSAGE_SELF, NULL) != -1 ||
errno != EFAULT)) {
(getrusage(RUSAGE_CHILDREN - 1, &r_usage1) != -1 ||
errno != EINVAL) || (getrusage(RUSAGE_SELF, NULL) != -1 ||
errno != EFAULT))
e(1);
exit(1);
}
spin();
if (getrusage(RUSAGE_SELF, &r_usage1) != 0) {
e(1);
exit(1);
}
if (getrusage(RUSAGE_SELF, &r_usage1) != 0) e(1);
CHECK_NOT_ZERO_FIELD(r_usage1, ru_utime.tv_sec);
CHECK_NOT_ZERO_FIELD(r_usage1, ru_maxrss);
if (getrusage(RUSAGE_CHILDREN, &r_usage2) != 0) {
e(1);
exit(1);
}
if (getrusage(RUSAGE_CHILDREN, &r_usage2) != 0) e(1);
if ((child = fork()) == 0) {
/*
* We cannot do this part of the test in the parent, since
* start() calls system() which spawns a child process.
*/
if (getrusage(RUSAGE_CHILDREN, &r_usage1) != 0) {
e(1);
exit(1);
}
if (getrusage(RUSAGE_CHILDREN, &r_usage1) != 0) e(1);
CHECK_ZERO_FIELD(r_usage1, ru_utime.tv_sec);
CHECK_ZERO_FIELD(r_usage1, ru_utime.tv_usec);
spin();
exit(0);
exit(errct);
} else {
if (child != waitpid(child, &status, 0)) {
e(1);
exit(1);
}
if (WEXITSTATUS(status) != 0) {
e(1);
exit(1);
}
if (getrusage(RUSAGE_CHILDREN, &r_usage3) != 0) {
e(1);
exit(1);
}
if (child != waitpid(child, &status, 0)) e(1);
if (WEXITSTATUS(status) != 0) e(1);
if (getrusage(RUSAGE_CHILDREN, &r_usage3) != 0) e(1);
CHECK_NOT_ZERO_FIELD(r_usage3, ru_utime.tv_sec);
}
}
/*
* Test the wait4 system call with good and bad rusage pointers, and with the
* wait4 either being satisfied immediately or blocking until the child exits:
* - mode 0: child has exited when parent calls wait4;
* - mode 1: parent blocks waiting for child, using a bad rusage pointer;
* - mode 2: parent blocks waiting for child, using a good rusage pointer.
*/
static void
sub75b(int mode, void * bad_ptr)
{
struct rusage r_usage;
pid_t pid;
int status;
pid = fork();
switch (pid) {
case -1:
e(0);
break;
case 0:
if (mode != 0)
spin();
exit(0);
default:
if (mode == 0)
sleep(1);
if (mode != 2) {
/*
* Try with a bad pointer. This call may fail only
* once the child has exited, but it must not clean up
* the child.
*/
if (wait4(-1, &status, 0, bad_ptr) != -1) e(0);
if (errno != EFAULT) e(0);
}
r_usage.ru_nsignals = 1234; /* see if it's written at all */
/* Wait for the actual process. */
if (wait4(-1, &status, 0, &r_usage) != pid) e(0);
if (!WIFEXITED(status)) e(0);
if (WEXITSTATUS(status) != 0) e(0);
if (r_usage.ru_nsignals != 0) e(0);
/* Only check for actual time spent if the child spun. */
if (mode != 0)
CHECK_NOT_ZERO_FIELD(r_usage, ru_utime.tv_sec);
}
}
/*
* Test wait4().
*/
static void
test75b(void)
{
void *ptr;
if ((ptr = mmap(NULL, sizeof(struct rusage), PROT_READ,
MAP_PRIVATE | MAP_ANON, -1, 0)) == MAP_FAILED) e(0);
if (munmap(ptr, sizeof(struct rusage)) != 0) e(0);
/* "ptr" is now a known-bad pointer */
sub75b(0, ptr);
sub75b(1, ptr);
sub75b(2, NULL);
}
int
main(int argc, char *argv[])
{
start(75);
test75a();
test75b();
quit();
return 0;

View File

@ -18,7 +18,7 @@ pm_exit_out(struct trace_proc * proc, const message * m_out)
return CT_NORETURN;
}
static const struct flags waitpid_options[] = {
static const struct flags wait4_options[] = {
FLAG(WNOHANG),
FLAG(WUNTRACED),
FLAG(WALTSIG),
@ -29,7 +29,7 @@ static const struct flags waitpid_options[] = {
};
static void
put_waitpid_status(struct trace_proc * proc, const char * name, int status)
put_wait4_status(struct trace_proc * proc, const char * name, int status)
{
const char *signame;
int sig;
@ -85,16 +85,39 @@ put_waitpid_status(struct trace_proc * proc, const char * name, int status)
}
static int
pm_waitpid_out(struct trace_proc * proc, const message * m_out)
pm_wait4_out(struct trace_proc * proc, const message * m_out)
{
put_value(proc, "pid", "%d", m_out->m_lc_pm_waitpid.pid);
put_value(proc, "pid", "%d", m_out->m_lc_pm_wait4.pid);
return CT_NOTDONE;
}
static void
pm_waitpid_in(struct trace_proc * proc, const message * m_out,
put_struct_rusage(struct trace_proc * proc, const char * name, int flags,
vir_bytes addr)
{
struct rusage ru;
if (!put_open_struct(proc, name, flags, addr, &ru, sizeof(ru)))
return;
put_struct_timeval(proc, "ru_utime", PF_LOCADDR,
(vir_bytes)&ru.ru_utime);
put_struct_timeval(proc, "ru_stime", PF_LOCADDR,
(vir_bytes)&ru.ru_stime);
if (verbose > 0) {
put_value(proc, "ru_maxrss", "%ld", ru.ru_maxrss);
put_value(proc, "ru_minflt", "%ld", ru.ru_minflt);
put_value(proc, "ru_majflt", "%ld", ru.ru_majflt);
}
put_close_struct(proc, verbose > 0);
}
static void
pm_wait4_in(struct trace_proc * proc, const message * m_out,
const message * m_in, int failed)
{
@ -105,12 +128,12 @@ pm_waitpid_in(struct trace_proc * proc, const message * m_out,
* unknown pointer.
*/
if (!failed && m_in->m_type > 0)
put_waitpid_status(proc, "status",
m_in->m_pm_lc_waitpid.status);
put_wait4_status(proc, "status", m_in->m_pm_lc_wait4.status);
else
put_field(proc, "status", "&..");
put_flags(proc, "options", waitpid_options, COUNT(waitpid_options),
"0x%x", m_out->m_lc_pm_waitpid.options);
put_flags(proc, "options", wait4_options, COUNT(wait4_options),
"0x%x", m_out->m_lc_pm_wait4.options);
put_struct_rusage(proc, "rusage", failed, m_out->m_lc_pm_wait4.addr);
put_equals(proc);
put_result(proc);
}
@ -1230,24 +1253,8 @@ static void
pm_getrusage_in(struct trace_proc * proc, const message * m_out,
const message * __unused m_in, int failed)
{
struct rusage buf;
/* Inline; we will certainly not be reusing this anywhere else. */
if (put_open_struct(proc, "rusage", failed, m_out->m_lc_pm_rusage.addr,
&buf, sizeof(buf))) {
put_struct_timeval(proc, "ru_utime", PF_LOCADDR,
(vir_bytes)&buf.ru_utime);
put_struct_timeval(proc, "ru_stime", PF_LOCADDR,
(vir_bytes)&buf.ru_stime);
if (verbose > 0) {
put_value(proc, "ru_maxrss", "%ld", buf.ru_maxrss);
put_value(proc, "ru_minflt", "%ld", buf.ru_minflt);
put_value(proc, "ru_majflt", "%ld", buf.ru_majflt);
}
put_close_struct(proc, verbose > 0);
}
put_struct_rusage(proc, "rusage", failed, m_out->m_lc_pm_rusage.addr);
put_equals(proc);
put_result(proc);
}
@ -1335,7 +1342,7 @@ pm_sprof_out(struct trace_proc * proc, const message * m_out)
static const struct call_handler pm_map[] = {
PM_CALL(EXIT) = HANDLER("exit", pm_exit_out, default_in),
PM_CALL(FORK) = HANDLER("fork", default_out, default_in),
PM_CALL(WAITPID) = HANDLER("waitpid", pm_waitpid_out, pm_waitpid_in),
PM_CALL(WAIT4) = HANDLER("wait4", pm_wait4_out, pm_wait4_in),
PM_CALL(GETPID) = HANDLER("getpid", default_out, pm_getpid_in),
PM_CALL(SETUID) = HANDLER("setuid", pm_setuid_out, default_in),
PM_CALL(GETUID) = HANDLER("getuid", default_out, pm_getuid_in),
@ -1385,7 +1392,7 @@ static const struct call_handler pm_map[] = {
pm_clock_gettime_in),
PM_CALL(CLOCK_SETTIME) = HANDLER_NAME(pm_clock_settime_name,
pm_clock_settime_out, default_in),
PM_CALL(GETRUSAGE) = HANDLER("pm_getrusage", pm_getrusage_out,
PM_CALL(GETRUSAGE) = HANDLER("getrusage", pm_getrusage_out,
pm_getrusage_in),
PM_CALL(REBOOT) = HANDLER("reboot", pm_reboot_out, default_in),
PM_CALL(SVRCTL) = HANDLER("pm_svrctl", pm_svrctl_out, pm_svrctl_in),