Author: kib
Date: Tue Jan 25 10:59:21 2011
New Revision: 217819
URL: http://svn.freebsd.org/changeset/base/217819

Log:
  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

Modified:
  head/sys/kern/kern_fork.c
  head/sys/kern/kern_proc.c
  head/sys/kern/kern_sig.c
  head/sys/kern/subr_trap.c
  head/sys/kern/sys_process.c
  head/sys/sys/proc.h
  head/sys/sys/ptrace.h

Modified: head/sys/kern/kern_fork.c
==============================================================================
--- head/sys/kern/kern_fork.c   Tue Jan 25 10:20:36 2011        (r217818)
+++ head/sys/kern/kern_fork.c   Tue Jan 25 10:59:21 2011        (r217819)
@@ -338,7 +338,7 @@ do_fork(struct thread *td, int flags, st
     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, st
        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, st
        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, st
         * 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, st
        /*
         * Now can be swapped.
         */
-       PROC_LOCK(p1);
        _PRELE(p1);
        PROC_UNLOCK(p1);
 
@@ -673,11 +689,19 @@ do_fork(struct thread *td, int flags, st
        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
 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);

Modified: head/sys/kern/kern_proc.c
==============================================================================
--- head/sys/kern/kern_proc.c   Tue Jan 25 10:20:36 2011        (r217818)
+++ head/sys/kern/kern_proc.c   Tue Jan 25 10:59:21 2011        (r217819)
@@ -230,6 +230,7 @@ proc_init(void *mem, int size, int flags
        mtx_init(&p->p_mtx, "process lock", NULL, MTX_DEF | MTX_DUPOK);
        mtx_init(&p->p_slock, "process slock", NULL, MTX_SPIN | MTX_RECURSE);
        cv_init(&p->p_pwait, "ppwait");
+       cv_init(&p->p_dbgwait, "dbgwait");
        TAILQ_INIT(&p->p_threads);           /* all threads in proc */
        EVENTHANDLER_INVOKE(process_init, p);
        p->p_stats = pstats_alloc();

Modified: head/sys/kern/kern_sig.c
==============================================================================
--- head/sys/kern/kern_sig.c    Tue Jan 25 10:20:36 2011        (r217818)
+++ head/sys/kern/kern_sig.c    Tue Jan 25 10:59:21 2011        (r217819)
@@ -2393,6 +2393,10 @@ ptracestop(struct thread *td, int sig)
                p->p_xthread = td;
                p->p_flag |= (P_STOPPED_SIG|P_STOPPED_TRACE);
                sig_suspend_threads(td, p, 0);
+               if ((td->td_dbgflags & TDB_STOPATFORK) != 0) {
+                       td->td_dbgflags &= ~TDB_STOPATFORK;
+                       cv_broadcast(&p->p_dbgwait);
+               }
 stopme:
                thread_suspend_switch(td);
                if (!(p->p_flag & P_TRACED)) {

Modified: head/sys/kern/subr_trap.c
==============================================================================
--- head/sys/kern/subr_trap.c   Tue Jan 25 10:20:36 2011        (r217818)
+++ head/sys/kern/subr_trap.c   Tue Jan 25 10:59:21 2011        (r217819)
@@ -392,9 +392,9 @@ syscallret(struct thread *td, int error,
         */
        STOPEVENT(p, S_SCX, sa->code);
        PTRACESTOP_SC(p, td, S_PT_SCX);
-       if (traced || (td->td_dbgflags & TDB_EXEC) != 0) {
+       if (traced || (td->td_dbgflags & (TDB_EXEC | TDB_FORK)) != 0) {
                PROC_LOCK(p);
-               td->td_dbgflags &= ~(TDB_SCX | TDB_EXEC);
+               td->td_dbgflags &= ~(TDB_SCX | TDB_EXEC | TDB_FORK);
                PROC_UNLOCK(p);
        }
 }

Modified: head/sys/kern/sys_process.c
==============================================================================
--- head/sys/kern/sys_process.c Tue Jan 25 10:20:36 2011        (r217818)
+++ head/sys/kern/sys_process.c Tue Jan 25 10:59:21 2011        (r217819)
@@ -94,6 +94,7 @@ struct ptrace_lwpinfo32 {
        sigset_t        pl_siglist;     /* LWP pending signal */
        struct siginfo32 pl_siginfo;    /* siginfo for signal */
        char    pl_tdname[MAXCOMLEN + 1];       /* LWP name. */
+       int     pl_child_pid;           /* New child pid */
 };
 
 #endif
@@ -476,6 +477,7 @@ ptrace_lwpinfo_to32(const struct ptrace_
        pl32->pl_siglist = pl->pl_siglist;
        siginfo_to_siginfo32(&pl->pl_siginfo, &pl32->pl_siginfo);
        strcpy(pl32->pl_tdname, pl->pl_tdname);
+       pl32->pl_child_pid = pl->pl_child_pid;
 }
 #endif /* COMPAT_FREEBSD32 */
 
@@ -657,6 +659,7 @@ kern_ptrace(struct thread *td, int req, 
        case PT_TO_SCE:
        case PT_TO_SCX:
        case PT_SYSCALL:
+       case PT_FOLLOW_FORK:
        case PT_DETACH:
                sx_xlock(&proctree_lock);
                proctree_locked = 1;
@@ -852,6 +855,13 @@ kern_ptrace(struct thread *td, int req, 
                td2->td_dbgflags &= ~TDB_SUSPEND;
                break;
 
+       case PT_FOLLOW_FORK:
+               if (data)
+                       p->p_flag |= P_FOLLOWFORK;
+               else
+                       p->p_flag &= ~P_FOLLOWFORK;
+               break;
+
        case PT_STEP:
        case PT_CONTINUE:
        case PT_TO_SCE:
@@ -912,7 +922,7 @@ kern_ptrace(struct thread *td, int req, 
                                if (pp == initproc)
                                        p->p_sigparent = SIGCHLD;
                        }
-                       p->p_flag &= ~(P_TRACED | P_WAITED);
+                       p->p_flag &= ~(P_TRACED | P_WAITED | P_FOLLOWFORK);
                        p->p_oppid = 0;
 
                        /* should we send SIGCHLD? */
@@ -1118,6 +1128,10 @@ kern_ptrace(struct thread *td, int req, 
                        pl->pl_flags |= PL_FLAG_SCX;
                if (td2->td_dbgflags & TDB_EXEC)
                        pl->pl_flags |= PL_FLAG_EXEC;
+               if (td2->td_dbgflags & TDB_FORK) {
+                       pl->pl_flags |= PL_FLAG_FORKED;
+                       pl->pl_child_pid = td2->td_dbg_forked;
+               }
                pl->pl_sigmask = td2->td_sigmask;
                pl->pl_siglist = td2->td_siglist;
                strcpy(pl->pl_tdname, td2->td_name);

Modified: head/sys/sys/proc.h
==============================================================================
--- head/sys/sys/proc.h Tue Jan 25 10:20:36 2011        (r217818)
+++ head/sys/sys/proc.h Tue Jan 25 10:59:21 2011        (r217819)
@@ -265,6 +265,7 @@ struct thread {
        int             td_ng_outbound; /* (k) Thread entered ng from above. */
        struct osd      td_osd;         /* (k) Object specific data. */
        struct vm_map_entry *td_map_def_user; /* (k) Deferred entries. */
+       pid_t           td_dbg_forked;  /* (c) Child pid for debugger. */
 #define        td_endzero td_rqindex
 
 /* Copied during fork1() or thread_sched_upcall(). */
@@ -374,6 +375,10 @@ do {                                                       
                \
 #define        TDB_SCE         0x00000008 /* Thread performs syscall enter */
 #define        TDB_SCX         0x00000010 /* Thread performs syscall exit */
 #define        TDB_EXEC        0x00000020 /* TDB_SCX from exec(2) family */
+#define        TDB_FORK        0x00000040 /* TDB_SCX from fork(2) that created 
new
+                                     process */
+#define        TDB_STOPATFORK  0x00000080 /* Stop at the return from fork 
(child
+                                     only) */
 
 /*
  * "Private" flags kept in td_pflags:
@@ -557,7 +562,9 @@ struct proc {
        STAILQ_HEAD(, ktr_request)      p_ktr;  /* (o) KTR event queue. */
        LIST_HEAD(, mqueue_notifier)    p_mqnotifier; /* (c) mqueue notifiers.*/
        struct kdtrace_proc     *p_dtrace; /* (*) DTrace-specific data. */
-       struct cv       p_pwait;        /* (*) wait cv for exit/exec */
+       struct cv       p_pwait;        /* (*) wait cv for exit/exec. */
+       struct cv       p_dbgwait;      /* (*) wait cv for debugger attach
+                                          after fork. */
 };
 
 #define        p_session       p_pgrp->pg_session
@@ -573,7 +580,7 @@ struct proc {
 #define        P_ADVLOCK       0x00001 /* Process may hold a POSIX advisory 
lock. */
 #define        P_CONTROLT      0x00002 /* Has a controlling terminal. */
 #define        P_KTHREAD       0x00004 /* Kernel thread (*). */
-#define        P_UNUSED0       0x00008 /* available. */
+#define        P_FOLLOWFORK    0x00008 /* Attach parent debugger to children. 
*/
 #define        P_PPWAIT        0x00010 /* Parent is waiting for child to 
exec/exit. */
 #define        P_PROFIL        0x00020 /* Has started profiling. */
 #define        P_STOPPROF      0x00040 /* Has thread requesting to stop 
profiling. */

Modified: head/sys/sys/ptrace.h
==============================================================================
--- head/sys/sys/ptrace.h       Tue Jan 25 10:20:36 2011        (r217818)
+++ head/sys/sys/ptrace.h       Tue Jan 25 10:59:21 2011        (r217819)
@@ -63,6 +63,8 @@
 #define        PT_TO_SCX       21
 #define        PT_SYSCALL      22
 
+#define        PT_FOLLOW_FORK  23
+
 #define PT_GETREGS      33     /* get general-purpose registers */
 #define PT_SETREGS      34     /* set general-purpose registers */
 #define PT_GETFPREGS    35     /* get floating-point registers */
@@ -104,10 +106,12 @@ struct ptrace_lwpinfo {
 #define        PL_FLAG_SCX     0x08    /* syscall leave point */
 #define        PL_FLAG_EXEC    0x10    /* exec(2) succeeded */
 #define        PL_FLAG_SI      0x20    /* siginfo is valid */
+#define        PL_FLAG_FORKED  0x40    /* new child */
        sigset_t        pl_sigmask;     /* LWP signal mask */
        sigset_t        pl_siglist;     /* LWP pending signal */
        struct __siginfo pl_siginfo;    /* siginfo for signal */
        char            pl_tdname[MAXCOMLEN + 1]; /* LWP name */
+       int             pl_child_pid;   /* New child pid */
 };
 
 /* Argument structure for PT_VM_ENTRY. */
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to