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:
parent
bc2d75fa05
commit
29346ab043
17 changed files with 268 additions and 161 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
26
minix/lib/libc/sys/wait4.c
Normal file
26
minix/lib/libc/sys/wait4.c
Normal 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;
|
||||
}
|
|
@ -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}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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' */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in a new issue