Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F153504258
D4703.id11747.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
14 KB
Referenced Files
None
Subscribers
None
D4703.id11747.diff
View Options
Index: sys/kern/kern_fork.c
===================================================================
--- sys/kern/kern_fork.c
+++ sys/kern/kern_fork.c
@@ -1067,7 +1067,7 @@
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.
@@ -1075,9 +1075,10 @@
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);
}
Index: sys/kern/kern_sig.c
===================================================================
--- sys/kern/kern_sig.c
+++ sys/kern/kern_sig.c
@@ -2501,7 +2501,12 @@
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);
@@ -2531,6 +2536,7 @@
}
}
PROC_SUNLOCK(p);
+ /* XXX: Clear TDB_XSIG in case P_TRACED was never set? */
return (td->td_xsig);
}
Index: sys/kern/kern_thr.c
===================================================================
--- sys/kern/kern_thr.c
+++ sys/kern/kern_thr.c
@@ -253,6 +253,8 @@
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;
/*
* Copy the existing thread VM policy into the new thread.
@@ -322,29 +324,54 @@
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
Index: sys/kern/kern_thread.c
===================================================================
--- sys/kern/kern_thread.c
+++ sys/kern/kern_thread.c
@@ -45,6 +45,7 @@
#include <sys/sched.h>
#include <sys/sleepqueue.h>
#include <sys/selinfo.h>
+#include <sys/syscallsubr.h>
#include <sys/sysent.h>
#include <sys/turnstile.h>
#include <sys/ktr.h>
@@ -942,7 +943,6 @@
*/
if ((p->p_flag & P_SINGLE_EXIT) && (p->p_singlethread != td)) {
PROC_UNLOCK(p);
- tidhash_remove(td);
/*
* Allow Linux emulation layer to do some work
@@ -950,13 +950,8 @@
*/
if (__predict_false(p->p_sysent->sv_thread_detach != NULL))
(p->p_sysent->sv_thread_detach)(td);
-
- PROC_LOCK(p);
- tdsigcleanup(td);
- umtx_thread_exit(td);
- PROC_SLOCK(p);
- thread_stopped(p);
- thread_exit();
+ kern_thr_exit(td);
+ panic("stopped thread did not exit");
}
PROC_SLOCK(p);
Index: sys/kern/sys_process.c
===================================================================
--- sys/kern/sys_process.c
+++ sys/kern/sys_process.c
@@ -710,6 +710,7 @@
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;
@@ -948,6 +949,16 @@
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:
@@ -1252,6 +1263,10 @@
}
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);
Index: sys/sys/proc.h
===================================================================
--- sys/sys/proc.h
+++ sys/sys/proc.h
@@ -412,6 +412,8 @@
#define TDB_STOPATFORK 0x00000080 /* Stop at the return from fork (child
only) */
#define TDB_CHILD 0x00000100 /* New child indicator for ptrace() */
+#define TDB_BORN 0x00000200 /* New LWP indicator for ptrace() */
+#define TDB_EXIT 0x00000400 /* Exiting LWP indicator for ptrace() */
/*
* "Private" flags kept in td_pflags:
@@ -577,6 +579,7 @@
struct itimers *p_itimers; /* (c) POSIX interval timers. */
struct procdesc *p_procdesc; /* (e) Process descriptor, if any. */
u_int p_treeflag; /* (e) P_TREE flags */
+ int p_pendingexits; /* (c) Count of pending thread exits. */
/* End area that is zeroed on creation. */
#define p_endzero p_magic
@@ -695,6 +698,7 @@
#define P2_NOTRACE 0x00000002 /* No ptrace(2) attach or coredumps. */
#define P2_NOTRACE_EXEC 0x00000004 /* Keep P2_NOPTRACE on exec(2). */
#define P2_AST_SU 0x00000008 /* Handles SU ast for kthreads. */
+#define P2_LWP_EVENTS 0x00000010 /* Report LWP events via ptrace(2). */
/* Flags protected by proctree_lock, kept in p_treeflags. */
#define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */
Index: sys/sys/ptrace.h
===================================================================
--- sys/sys/ptrace.h
+++ sys/sys/ptrace.h
@@ -64,6 +64,7 @@
#define PT_SYSCALL 22
#define PT_FOLLOW_FORK 23
+#define PT_LWP_EVENTS 24 /* report LWP birth and exit */
#define PT_GETREGS 33 /* get general-purpose registers */
#define PT_SETREGS 34 /* set general-purpose registers */
@@ -108,6 +109,8 @@
#define PL_FLAG_SI 0x20 /* siginfo is valid */
#define PL_FLAG_FORKED 0x40 /* new child */
#define PL_FLAG_CHILD 0x80 /* I am from child */
+#define PL_FLAG_BORN 0x100 /* new LWP */
+#define PL_FLAG_EXITED 0x200 /* exiting LWP */
sigset_t pl_sigmask; /* LWP signal mask */
sigset_t pl_siglist; /* LWP pending signal */
struct __siginfo pl_siginfo; /* siginfo for signal */
Index: tests/sys/kern/ptrace_test.c
===================================================================
--- tests/sys/kern/ptrace_test.c
+++ tests/sys/kern/ptrace_test.c
@@ -1094,6 +1094,16 @@
pthread_exit(NULL);
}
+static __dead2 void
+simple_thread_main(void)
+{
+ pthread_t thread;
+
+ CHILD_REQUIRE(pthread_create(&thread, NULL, simple_thread, NULL) == 0);
+ CHILD_REQUIRE(pthread_join(thread, NULL) == 0);
+ exit(1);
+}
+
/*
* Verify that pl_syscall_code in struct ptrace_lwpinfo for a new
* thread reports the correct value.
@@ -1108,14 +1118,8 @@
ATF_REQUIRE((fpid = fork()) != -1);
if (fpid == 0) {
- pthread_t thread;
-
trace_me();
-
- CHILD_REQUIRE(pthread_create(&thread, NULL, simple_thread,
- NULL) == 0);
- CHILD_REQUIRE(pthread_join(thread, NULL) == 0);
- exit(1);
+ simple_thread_main();
}
/* The first wait() should report the stop from SIGSTOP. */
@@ -1178,6 +1182,179 @@
ATF_REQUIRE(errno == ECHILD);
}
+/*
+ * Verify that the expected LWP events are reported for a child thread.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__lwp_events);
+ATF_TC_BODY(ptrace__lwp_events, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ lwpid_t lwps[2];
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ simple_thread_main();
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+ lwps[0] = pl.pl_lwpid;
+
+ ATF_REQUIRE(ptrace(PT_LWP_EVENTS, wpid, NULL, 1) == 0);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
+
+ /* The first event should be for the child thread's birth. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)) ==
+ (PL_FLAG_BORN | PL_FLAG_SCX));
+ ATF_REQUIRE(pl.pl_lwpid != lwps[0]);
+ lwps[1] = pl.pl_lwpid;
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
+
+ /* The next event should be for the child thread's death. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE((pl.pl_flags & (PL_FLAG_EXITED | PL_FLAG_SCE)) ==
+ (PL_FLAG_EXITED | PL_FLAG_SCE));
+ ATF_REQUIRE(pl.pl_lwpid == lwps[1]);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
+
+ /* The last event should be for the child process's exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 1);
+
+ wpid = wait(&status);
+ ATF_REQUIRE(wpid == -1);
+ ATF_REQUIRE(errno == ECHILD);
+}
+
+static void *
+exec_thread(void *arg __unused)
+{
+
+ execl("/usr/bin/true", "true", NULL);
+ exit(127);
+}
+
+static __dead2 void
+exec_thread_main(void)
+{
+ pthread_t thread;
+
+ CHILD_REQUIRE(pthread_create(&thread, NULL, exec_thread, NULL) == 0);
+ for (;;)
+ sleep(60);
+ exit(1);
+}
+
+/*
+ * Verify that the expected LWP events are reported for a multithreaded
+ * process that calls execve(2).
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__lwp_events_exec);
+ATF_TC_BODY(ptrace__lwp_events_exec, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ lwpid_t lwps[2];
+ int status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ exec_thread_main();
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl,
+ sizeof(pl)) != -1);
+ lwps[0] = pl.pl_lwpid;
+
+ ATF_REQUIRE(ptrace(PT_LWP_EVENTS, wpid, NULL, 1) == 0);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
+
+ /* The first event should be for the child thread's birth. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE((pl.pl_flags & (PL_FLAG_BORN | PL_FLAG_SCX)) ==
+ (PL_FLAG_BORN | PL_FLAG_SCX));
+ ATF_REQUIRE(pl.pl_lwpid != lwps[0]);
+ lwps[1] = pl.pl_lwpid;
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
+
+ /*
+ * The next event should be for the main thread's death due to
+ * single threading from execve().
+ */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE((pl.pl_flags & (PL_FLAG_EXITED | PL_FLAG_SCE)) ==
+ (PL_FLAG_EXITED));
+ ATF_REQUIRE(pl.pl_lwpid == lwps[0]);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
+
+ /* The next event should be for the child process's exec. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE((pl.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
+ (PL_FLAG_EXEC | PL_FLAG_SCX));
+ ATF_REQUIRE(pl.pl_lwpid == lwps[1]);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
+
+ /* The last event should be for the child process's exit. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 0);
+
+ wpid = wait(&status);
+ ATF_REQUIRE(wpid == -1);
+ ATF_REQUIRE(errno == ECHILD);
+}
+
ATF_TP_ADD_TCS(tp)
{
@@ -1197,6 +1374,8 @@
ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_fork);
ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_vfork);
ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_thread);
+ ATF_TP_ADD_TC(tp, ptrace__lwp_events);
+ ATF_TP_ADD_TC(tp, ptrace__lwp_events_exec);
return (atf_no_error());
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Apr 22, 12:22 PM (17 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31942374
Default Alt Text
D4703.id11747.diff (14 KB)
Attached To
Mode
D4703: Add ptrace(2) reporting for LWP events.
Attached
Detach File
Event Timeline
Log In to Comment