diff options
Diffstat (limited to 'proc/wait.c')
-rw-r--r-- | proc/wait.c | 183 |
1 files changed, 133 insertions, 50 deletions
diff --git a/proc/wait.c b/proc/wait.c index 60ec58ff..6fc94e83 100644 --- a/proc/wait.c +++ b/proc/wait.c @@ -1,5 +1,5 @@ /* Implementation of wait - Copyright (C) 1994, 1995, 1996 Free Software Foundation + Copyright (C) 1994, 1995, 1996, 2001 Free Software Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -19,6 +19,8 @@ #include <sys/types.h> #include <hurd/hurd_types.h> #include <sys/resource.h> +#include <sys/time.h> +#include <mach/task_info.h> #include "proc.h" @@ -32,7 +34,95 @@ #include "process_S.h" #include <mach/mig_errors.h> -#define EWOULDBLOCK EAGAIN /* XXX */ +static inline void +rusage_add (struct rusage *acc, const struct rusage *b) +{ + timeradd (&acc->ru_utime, &b->ru_utime, &acc->ru_utime); + timeradd (&acc->ru_stime, &b->ru_stime, &acc->ru_stime); + + /* Check <bits/resource.h> definition of `struct rusage' + to make sure this gets all the fields. */ + acc->ru_maxrss += b->ru_maxrss; + acc->ru_ixrss += b->ru_ixrss; + acc->ru_idrss += b->ru_idrss; + acc->ru_isrss += b->ru_isrss; + acc->ru_minflt += b->ru_minflt; + acc->ru_majflt += b->ru_majflt; + acc->ru_nswap += b->ru_nswap; + acc->ru_inblock += b->ru_inblock; + acc->ru_oublock += b->ru_oublock; + acc->ru_msgsnd += b->ru_msgsnd; + acc->ru_msgrcv += b->ru_msgrcv; + acc->ru_nsignals += b->ru_nsignals; + acc->ru_nvcsw += b->ru_nvcsw; + acc->ru_nivcsw += b->ru_nivcsw; +} + +/* XXX This is real half-assed. + + We want to collect usage statistics from dead processes to return to its + parent for its proc_wait calls and its aggregate child statistics. + + The microkernel provides no access to this information once the task is + terminated. So the best we can do is take a sample at some time while + the task is still alive but not too long before it dies. Our results + are always inaccurate, because they don't account for the final part of + the task's lifetime. But perhaps it's better than nothing at all. + + The obvious place to take this sample is in proc_mark_exit, which in + normal circumstances a task is calling immediately before terminating + itself. So in the best of cases, our data omits only the interval in + which our RPC returns to the task and it calls task_terminate. We could + take samples in other places just to have something rather than nothing + if the task dies unexpectedly (e.g. SIGKILL); but it may not be worthwhile + since the end result is never going to be accurate anyway. + + The only way to get correct results is by adding some microkernel + feature to report the task statistics data post-mortem. */ + +void +sample_rusage (struct proc *p) +{ + struct task_basic_info bi; + struct task_events_info ei; + struct task_thread_times_info tti; + mach_msg_type_number_t count; + error_t err; + + count = TASK_BASIC_INFO_COUNT; + err = task_info (p->p_task, TASK_BASIC_INFO, + (task_info_t) &bi, &count); + if (err) + memset (&bi, 0, sizeof bi); + + count = TASK_EVENTS_INFO_COUNT; + err = task_info (p->p_task, TASK_EVENTS_INFO, + (task_info_t) &ei, &count); + if (err) + memset (&ei, 0, sizeof ei); + + count = TASK_THREAD_TIMES_INFO_COUNT; + err = task_info (p->p_task, TASK_THREAD_TIMES_INFO, + (task_info_t) &tti, &count); + if (err) + memset (&tti, 0, sizeof tti); + + time_value_add (&bi.user_time, &tti.user_time); + time_value_add (&bi.system_time, &tti.system_time); + + memset (&p->p_rusage, 0, sizeof (struct rusage)); + + p->p_rusage.ru_utime.tv_sec = bi.user_time.seconds; + p->p_rusage.ru_utime.tv_usec = bi.user_time.microseconds; + p->p_rusage.ru_stime.tv_sec = bi.system_time.seconds; + p->p_rusage.ru_stime.tv_usec = bi.system_time.microseconds; + + /* These statistics map only approximately. */ + p->p_rusage.ru_majflt = ei.pageins; + p->p_rusage.ru_minflt = ei.faults - ei.pageins; + p->p_rusage.ru_msgsnd = ei.messages_sent; /* Mach IPC, not SysV IPC */ + p->p_rusage.ru_msgrcv = ei.messages_received; /* ditto */ +} /* Return nonzero if a `waitpid' on WAIT_PID by a process in MYPGRP cares about the death of PID/PGRP. */ @@ -46,11 +136,14 @@ waiter_cares (pid_t wait_pid, pid_t mypgrp, (wait_pid == WAIT_MYPGRP && pgrp == mypgrp)); } -/* A process is dying. Send SIGCHLD to the parent. Wake the parent -if it is waiting for us to exit. */ +/* A process is dying. Send SIGCHLD to the parent. + Wake the parent if it is waiting for us to exit. */ void alert_parent (struct proc *p) { + /* We accumulate the aggregate usage stats of all our dead children. */ + rusage_add (&p->p_parent->p_child_rusage, &p->p_rusage); + send_signal (p->p_parent->p_msgport, SIGCHLD, p->p_parent->p_task); if (!p->p_exiting) @@ -78,73 +171,59 @@ S_proc_wait (struct proc *p, pid_t *pid_status) { int cancel; - - int child_ready (struct proc *child) + + int reap (struct proc *child) { - if (child->p_waited) + if (child->p_waited + || (!child->p_dead + && (!child->p_stopped + || !(child->p_traced || (options & WUNTRACED))))) return 0; + child->p_waited = 1; + *status = child->p_status; + *sigcode = child->p_sigcode; + *ru = child->p_rusage; /* all zeros if !p_dead */ + *pid_status = child->p_pid; if (child->p_dead) - return 1; - if (!child->p_stopped) - return 0; - if (child->p_traced || (options & WUNTRACED)) - return 1; - return 0; + complete_exit (child); + return 1; } if (!p) return EOPNOTSUPP; - + start_over: /* See if we can satisfy the request with a stopped child; also check for invalid arguments here. */ - if (!p->p_ochild) + if (!p->p_ochild) return ECHILD; - + if (pid > 0) { struct proc *child = pid_find_allow_zombie (pid); if (!child || child->p_parent != p) return ECHILD; - if (child_ready (child)) - { - child->p_waited = 1; - *status = child->p_status; - *sigcode = child->p_sigcode; - if (child->p_dead) - complete_exit (child); - bzero (ru, sizeof (struct rusage)); - *pid_status = pid; - return 0; - } + if (reap (child)) + return 0; } else { struct proc *child; int had_a_match = pid == 0; - + for (child = p->p_ochild; child; child = child->p_sib) if (waiter_cares (pid, p->p_pgrp->pg_pgid, child->p_pid, child->p_pgrp->pg_pgid)) { + if (reap (child)) + return 0; had_a_match = 1; - if (child_ready (child)) - { - child->p_waited = 1; - *status = child->p_status; - *sigcode = child->p_sigcode; - *pid_status = child->p_pid; - if (child->p_dead) - complete_exit (child); - bzero (ru, sizeof (struct rusage)); - return 0; - } } if (!had_a_match) return ECHILD; } - + if (options & WNOHANG) return EWOULDBLOCK; @@ -157,7 +236,7 @@ S_proc_wait (struct proc *p, goto start_over; } -/* Implement proc_mark_stop as described in <hurd/proc.defs>. */ +/* Implement proc_mark_stop as described in <hurd/process.defs>. */ kern_return_t S_proc_mark_stop (struct proc *p, int signo, @@ -170,20 +249,20 @@ S_proc_mark_stop (struct proc *p, p->p_status = W_STOPCODE (signo); p->p_sigcode = sigcode; p->p_waited = 0; - + if (p->p_parent->p_waiting) { condition_broadcast (&p->p_parent->p_wakeup); p->p_parent->p_waiting = 0; } - + if (!p->p_parent->p_nostopcld) send_signal (p->p_parent->p_msgport, SIGCHLD, p->p_parent->p_task); return 0; } -/* Implement proc_mark_exit as described in <hurd/proc.defs>. */ +/* Implement proc_mark_exit as described in <hurd/process.defs>. */ kern_return_t S_proc_mark_exit (struct proc *p, int status, @@ -191,17 +270,22 @@ S_proc_mark_exit (struct proc *p, { if (!p) return EOPNOTSUPP; - + if (WIFSTOPPED (status)) return EINVAL; - + + sample_rusage (p); /* See comments above sample_rusage. */ + + if (p->p_exiting) + return EBUSY; + p->p_exiting = 1; p->p_status = status; p->p_sigcode = sigcode; return 0; } -/* Implement proc_mark_cont as described in <hurd/proc.defs>. */ +/* Implement proc_mark_cont as described in <hurd/process.defs>. */ kern_return_t S_proc_mark_cont (struct proc *p) { @@ -211,7 +295,7 @@ S_proc_mark_cont (struct proc *p) return 0; } -/* Implement proc_mark_traced as described in <hurd/proc.defs>. */ +/* Implement proc_mark_traced as described in <hurd/process.defs>. */ kern_return_t S_proc_mark_traced (struct proc *p) { @@ -221,7 +305,7 @@ S_proc_mark_traced (struct proc *p) return 0; } -/* Implement proc_mark_nostopchild as described in <hurd/proc.defs>. */ +/* Implement proc_mark_nostopchild as described in <hurd/process.defs>. */ kern_return_t S_proc_mod_stopchild (struct proc *p, int value) @@ -232,4 +316,3 @@ S_proc_mod_stopchild (struct proc *p, p->p_nostopcld = ! value; return 0; } - |