Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 25 Jan 2011 10:59:21 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r217819 - in head/sys: kern sys
Message-ID:  <201101251059.p0PAxLTG029327@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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. */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201101251059.p0PAxLTG029327>