aboutsummaryrefslogtreecommitdiff
path: root/proc/wait.c
diff options
context:
space:
mode:
Diffstat (limited to 'proc/wait.c')
-rw-r--r--proc/wait.c183
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;
}
-