diff --git a/defs.h b/defs.h index 9889efeb..4138894e 100644 --- a/defs.h +++ b/defs.h @@ -263,6 +263,7 @@ struct tcb { struct tcb *parent; /* Parent of this process */ int nchildren; /* # of traced children */ int waitpid; /* pid(s) this process is waiting for */ + int nzombies; /* # of formerly traced children now dead */ #ifdef LINUX int nclone_threads; /* # of nchildren with CLONE_THREAD */ int nclone_detached; /* # of nchildren with CLONE_DETACHED */ diff --git a/process.c b/process.c index 71b3ca2b..0a6f8398 100644 --- a/process.c +++ b/process.c @@ -1774,7 +1774,17 @@ struct tcb *tcp; /* ??? WTA: fix bug with hanging children */ if (!(tcp->u_arg[2] & WNOHANG)) { - /* There are traced children */ + /* + * There are traced children. We'll make the parent + * block to avoid a false ECHILD error due to our + * ptrace having stolen the children. However, + * we shouldn't block if there are zombies to reap. + * XXX doesn't handle pgrp matches (u_arg[0]==0,<-1) + */ + if (tcp->nzombies > 0 && + (tcp->u_arg[0] == -1 || + pid2tcb(tcp->u_arg[0]) == NULL)) + return 0; tcp->flags |= TCB_SUSPENDED; tcp->waitpid = tcp->u_arg[0]; #ifdef TCB_CLONE_THREAD @@ -1795,6 +1805,14 @@ struct tcb *tcp; "internal_wait: should not have resumed %d\n", tcp->pid); } + else if (exiting(tcp) && tcp->u_error == 0 && tcp->u_rval > 0 && + tcp->nzombies > 0 && pid2tcb(tcp->u_rval) == NULL) { + /* + * We just reaped a child we don't know about, + * presumably a zombie we already droptcb'd. + */ + tcp->nzombies--; + } return 0; } diff --git a/strace.c b/strace.c index 8f70d34b..41194b7f 100644 --- a/strace.c +++ b/strace.c @@ -621,6 +621,7 @@ int pid; tcp->pid = pid; tcp->parent = NULL; tcp->nchildren = 0; + tcp->nzombies = 0; #ifdef TCB_CLONE_THREAD tcp->nclone_threads = tcp->nclone_detached = 0; tcp->nclone_waiting = 0; @@ -1011,6 +1012,10 @@ struct tcb *tcp; if (tcp->flags & TCB_CLONE_THREAD) tcp->parent->nclone_threads--; #endif +#ifdef TCB_CLONE_DETACHED + if (!(tcp->flags & TCB_CLONE_DETACHED)) +#endif + tcp->parent->nzombies++; tcp->parent = NULL; }