diff options
author | kib <kib@FreeBSD.org> | 2011-01-25 10:59:21 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2011-01-25 10:59:21 +0000 |
commit | 72112368cfef3fb8d740abf0ef9b4024db441d68 (patch) | |
tree | 8bd4ca91acf483d0b07a7a1a8c354a245fa549b1 /sys/kern/kern_fork.c | |
parent | ab3c67159918d2800cfde8043251533028ab1e5a (diff) | |
download | FreeBSD-src-72112368cfef3fb8d740abf0ef9b4024db441d68.zip FreeBSD-src-72112368cfef3fb8d740abf0ef9b4024db441d68.tar.gz |
Allow debugger to specify that children of the traced process should be
automatically traced. Extend the ptrace(PL_LWPINFO) to report that child
just forked.
Reviewed by: davidxu, jhb
MFC after: 2 weeks
Diffstat (limited to 'sys/kern/kern_fork.c')
-rw-r--r-- | sys/kern/kern_fork.c | 73 |
1 files changed, 63 insertions, 10 deletions
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index a24872a..1bc20a5 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -338,7 +338,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, struct vmspace *vm2) { struct proc *p1, *pptr; - int trypid; + int p2_held, trypid; struct filedesc *fd; struct filedesc_to_leader *fdtol; struct sigacts *newsigacts; @@ -346,6 +346,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, sx_assert(&proctree_lock, SX_SLOCKED); sx_assert(&allproc_lock, SX_XLOCKED); + p2_held = 0; p1 = td->td_proc; /* @@ -632,6 +633,8 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, PROC_SLOCK(p2); p2->p_state = PRS_NORMAL; PROC_SUNLOCK(p2); + + PROC_LOCK(p1); #ifdef KDTRACE_HOOKS /* * Tell the DTrace fasttrap provider about the new process @@ -640,19 +643,33 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, * later on. */ if (dtrace_fasttrap_fork) { - PROC_LOCK(p1); PROC_LOCK(p2); dtrace_fasttrap_fork(p1, p2); PROC_UNLOCK(p2); - PROC_UNLOCK(p1); } #endif - - /* - * If RFSTOPPED not requested, make child runnable and add to - * run queue. - */ + if ((p1->p_flag & (P_TRACED | P_FOLLOWFORK)) == (P_TRACED | + P_FOLLOWFORK)) { + /* + * Arrange for debugger to receive the fork event. + * + * We can report PL_FLAG_FORKED regardless of + * P_FOLLOWFORK settings, but it does not make a sense + * for runaway child. + */ + td->td_dbgflags |= TDB_FORK; + td->td_dbg_forked = p2->p_pid; + PROC_LOCK(p2); + td2->td_dbgflags |= TDB_STOPATFORK; + _PHOLD(p2); + p2_held = 1; + PROC_UNLOCK(p2); + } if ((flags & RFSTOPPED) == 0) { + /* + * If RFSTOPPED not requested, make child runnable and + * add to run queue. + */ thread_lock(td2); TD_SET_CAN_RUN(td2); sched_add(td2, SRQ_BORING); @@ -662,7 +679,6 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, /* * Now can be swapped. */ - PROC_LOCK(p1); _PRELE(p1); PROC_UNLOCK(p1); @@ -673,11 +689,19 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2, SDT_PROBE(proc, kernel, , create, p2, p1, flags, 0, 0); /* + * Wait until debugger is attached to child. + */ + PROC_LOCK(p2); + while ((td2->td_dbgflags & TDB_STOPATFORK) != 0) + cv_wait(&p2->p_dbgwait, &p2->p_mtx); + if (p2_held) + _PRELE(p2); + + /* * Preserve synchronization semantics of vfork. If waiting for * child to exec or exit, set P_PPWAIT on child, and sleep on our * proc (in case of exit). */ - PROC_LOCK(p2); while (p2->p_flag & P_PPWAIT) cv_wait(&p2->p_pwait, &p2->p_mtx); PROC_UNLOCK(p2); @@ -883,8 +907,37 @@ fork_exit(void (*callout)(void *, struct trapframe *), void *arg, void fork_return(struct thread *td, struct trapframe *frame) { + struct proc *p, *dbg; + + if (td->td_dbgflags & TDB_STOPATFORK) { + p = td->td_proc; + sx_xlock(&proctree_lock); + PROC_LOCK(p); + if ((p->p_pptr->p_flag & (P_TRACED | P_FOLLOWFORK)) == + (P_TRACED | P_FOLLOWFORK)) { + /* + * If debugger still wants auto-attach for the + * parent's children, do it now. + */ + dbg = p->p_pptr->p_pptr; + p->p_flag |= P_TRACED; + p->p_oppid = p->p_pptr->p_pid; + proc_reparent(p, dbg); + sx_xunlock(&proctree_lock); + ptracestop(td, SIGSTOP); + } else { + /* + * ... otherwise clear the request. + */ + sx_xunlock(&proctree_lock); + td->td_dbgflags &= ~TDB_STOPATFORK; + cv_broadcast(&p->p_dbgwait); + } + PROC_UNLOCK(p); + } userret(td, frame); + #ifdef KTRACE if (KTRPOINT(td, KTR_SYSRET)) ktrsysret(SYS_fork, 0, 0); |