summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_fork.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2011-01-25 10:59:21 +0000
committerkib <kib@FreeBSD.org>2011-01-25 10:59:21 +0000
commit72112368cfef3fb8d740abf0ef9b4024db441d68 (patch)
tree8bd4ca91acf483d0b07a7a1a8c354a245fa549b1 /sys/kern/kern_fork.c
parentab3c67159918d2800cfde8043251533028ab1e5a (diff)
downloadFreeBSD-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.c73
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);
OpenPOWER on IntegriCloud