diff options
author | jhb <jhb@FreeBSD.org> | 2016-08-12 19:43:06 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2016-08-12 19:43:06 +0000 |
commit | c6f0dbfd1595d345cf15a1b64b1ef174e21206a2 (patch) | |
tree | 2b023915269e488d18e1d560fe148306d6176aa9 /sys/kern | |
parent | 65cab655fa20d4845c4d317c464c0835a03be574 (diff) | |
download | FreeBSD-src-c6f0dbfd1595d345cf15a1b64b1ef174e21206a2.zip FreeBSD-src-c6f0dbfd1595d345cf15a1b64b1ef174e21206a2.tar.gz |
MFC 292894,292896: Add ptrace(2) reporting for LWP events.
292894:
Add ptrace(2) reporting for LWP events.
Add two new LWPINFO flags: PL_FLAG_BORN and PL_FLAG_EXITED for reporting
thread creation and destruction. Newly created threads will stop to report
PL_FLAG_BORN before returning to userland and exiting threads will stop to
report PL_FLAG_EXIT before exiting completely. Both of these events are
only enabled and reported if PT_LWP_EVENTS is enabled on a process.
292896:
Document the recently added support for ptrace(2) LWP events.
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/kern_fork.c | 7 | ||||
-rw-r--r-- | sys/kern/kern_sig.c | 7 | ||||
-rw-r--r-- | sys/kern/kern_thr.c | 63 | ||||
-rw-r--r-- | sys/kern/sys_process.c | 15 |
4 files changed, 70 insertions, 22 deletions
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index a84b619..3d7bf09 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -1083,7 +1083,7 @@ fork_return(struct thread *td, struct trapframe *frame) cv_broadcast(&p->p_dbgwait); } PROC_UNLOCK(p); - } else if (p->p_flag & P_TRACED) { + } else if (p->p_flag & P_TRACED || td->td_dbgflags & TDB_BORN) { /* * This is the start of a new thread in a traced * process. Report a system call exit event. @@ -1091,9 +1091,10 @@ fork_return(struct thread *td, struct trapframe *frame) PROC_LOCK(p); td->td_dbgflags |= TDB_SCX; _STOPEVENT(p, S_SCX, td->td_dbg_sc_code); - if ((p->p_stops & S_PT_SCX) != 0) + if ((p->p_stops & S_PT_SCX) != 0 || + (td->td_dbgflags & TDB_BORN) != 0) ptracestop(td, SIGTRAP); - td->td_dbgflags &= ~TDB_SCX; + td->td_dbgflags &= ~(TDB_SCX | TDB_BORN); PROC_UNLOCK(p); } diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 29783f8..65675dd 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -2488,7 +2488,12 @@ ptracestop(struct thread *td, int sig) td->td_tid, p->p_pid, td->td_dbgflags, sig); PROC_SLOCK(p); while ((p->p_flag & P_TRACED) && (td->td_dbgflags & TDB_XSIG)) { - if (p->p_flag & P_SINGLE_EXIT) { + if (p->p_flag & P_SINGLE_EXIT && + !(td->td_dbgflags & TDB_EXIT)) { + /* + * Ignore ptrace stops except for thread exit + * events when the process exits. + */ td->td_dbgflags &= ~TDB_XSIG; PROC_SUNLOCK(p); return (sig); diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c index 98965b1..8b19967 100644 --- a/sys/kern/kern_thr.c +++ b/sys/kern/kern_thr.c @@ -252,6 +252,8 @@ thread_create(struct thread *td, struct rtprio *rtp, thread_unlock(td); if (P_SHOULDSTOP(p)) newtd->td_flags |= TDF_ASTPENDING | TDF_NEEDSUSPCHK; + if (p->p_flag2 & P2_LWP_EVENTS) + newtd->td_dbgflags |= TDB_BORN; PROC_UNLOCK(p); tidhash_add(newtd); @@ -314,29 +316,54 @@ kern_thr_exit(struct thread *td) p = td->td_proc; - rw_wlock(&tidhash_lock); + /* + * If all of the threads in a process call this routine to + * exit (e.g. all threads call pthread_exit()), exactly one + * thread should return to the caller to terminate the process + * instead of the thread. + * + * Checking p_numthreads alone is not sufficient since threads + * might be committed to terminating while the PROC_LOCK is + * dropped in either ptracestop() or while removing this thread + * from the tidhash. Instead, the p_pendingexits field holds + * the count of threads in either of those states and a thread + * is considered the "last" thread if all of the other threads + * in a process are already terminating. + */ PROC_LOCK(p); - - if (p->p_numthreads != 1) { - racct_sub(p, RACCT_NTHR, 1); - LIST_REMOVE(td, td_hash); - rw_wunlock(&tidhash_lock); - tdsigcleanup(td); - umtx_thread_exit(td); - PROC_SLOCK(p); - thread_stopped(p); - thread_exit(); - /* NOTREACHED */ + if (p->p_numthreads == p->p_pendingexits + 1) { + /* + * Ignore attempts to shut down last thread in the + * proc. This will actually call _exit(2) in the + * usermode trampoline when it returns. + */ + PROC_UNLOCK(p); + return (0); } + p->p_pendingexits++; + td->td_dbgflags |= TDB_EXIT; + if (p->p_flag & P_TRACED && p->p_flag2 & P2_LWP_EVENTS) + ptracestop(td, SIGTRAP); + PROC_UNLOCK(p); + tidhash_remove(td); + PROC_LOCK(p); + p->p_pendingexits--; + /* - * Ignore attempts to shut down last thread in the proc. This - * will actually call _exit(2) in the usermode trampoline when - * it returns. + * The check above should prevent all other threads from this + * process from exiting while the PROC_LOCK is dropped, so + * there must be at least one other thread other than the + * current thread. */ - PROC_UNLOCK(p); - rw_wunlock(&tidhash_lock); - return (0); + KASSERT(p->p_numthreads > 1, ("too few threads")); + racct_sub(p, RACCT_NTHR, 1); + tdsigcleanup(td); + umtx_thread_exit(td); + PROC_SLOCK(p); + thread_stopped(p); + thread_exit(); + /* NOTREACHED */ } int diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 96e0181..66e9097 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -666,6 +666,7 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) case PT_TO_SCX: case PT_SYSCALL: case PT_FOLLOW_FORK: + case PT_LWP_EVENTS: case PT_DETACH: sx_xlock(&proctree_lock); proctree_locked = 1; @@ -905,6 +906,16 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) p->p_flag &= ~P_FOLLOWFORK; break; + case PT_LWP_EVENTS: + CTR3(KTR_PTRACE, "PT_LWP_EVENTS: pid %d %s -> %s", p->p_pid, + p->p_flag2 & P2_LWP_EVENTS ? "enabled" : "disabled", + data ? "enabled" : "disabled"); + if (data) + p->p_flag2 |= P2_LWP_EVENTS; + else + p->p_flag2 &= ~P2_LWP_EVENTS; + break; + case PT_STEP: case PT_CONTINUE: case PT_TO_SCE: @@ -1227,6 +1238,10 @@ kern_ptrace(struct thread *td, int req, pid_t pid, void *addr, int data) } if (td2->td_dbgflags & TDB_CHILD) pl->pl_flags |= PL_FLAG_CHILD; + if (td2->td_dbgflags & TDB_BORN) + pl->pl_flags |= PL_FLAG_BORN; + if (td2->td_dbgflags & TDB_EXIT) + pl->pl_flags |= PL_FLAG_EXITED; pl->pl_sigmask = td2->td_sigmask; pl->pl_siglist = td2->td_siglist; strcpy(pl->pl_tdname, td2->td_name); |