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
This commit is contained in:
+63
-10
@@ -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);
|
||||
|
||||
@@ -672,12 +688,20 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2,
|
||||
knote_fork(&p1->p_klist, p2->p_pid);
|
||||
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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -392,9 +392,9 @@ syscallret(struct thread *td, int error, struct syscall_args *sa __unused)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-1
@@ -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_lwpinfo *pl,
|
||||
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, pid_t pid, void *addr, int data)
|
||||
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, pid_t pid, void *addr, int data)
|
||||
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, pid_t pid, void *addr, int data)
|
||||
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, pid_t pid, void *addr, int data)
|
||||
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);
|
||||
|
||||
+9
-2
@@ -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. */
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user