summaryrefslogtreecommitdiffstats
path: root/sys/kern
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2016-08-12 19:43:06 +0000
committerjhb <jhb@FreeBSD.org>2016-08-12 19:43:06 +0000
commitc6f0dbfd1595d345cf15a1b64b1ef174e21206a2 (patch)
tree2b023915269e488d18e1d560fe148306d6176aa9 /sys/kern
parent65cab655fa20d4845c4d317c464c0835a03be574 (diff)
downloadFreeBSD-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.c7
-rw-r--r--sys/kern/kern_sig.c7
-rw-r--r--sys/kern/kern_thr.c63
-rw-r--r--sys/kern/sys_process.c15
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);
OpenPOWER on IntegriCloud