summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2015-12-29 23:25:26 +0000
committerjhb <jhb@FreeBSD.org>2015-12-29 23:25:26 +0000
commitfb5720f7be0d2eab3253696e244c058a229b5473 (patch)
tree38cac37b4cace20e5e9e11b5763940c44fe6be7f /tests
parent79ec12eeb6f427ceaaaa94ca98073316d8c24e47 (diff)
downloadFreeBSD-src-fb5720f7be0d2eab3253696e244c058a229b5473.zip
FreeBSD-src-fb5720f7be0d2eab3253696e244c058a229b5473.tar.gz
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.
Diffstat (limited to 'tests')
-rw-r--r--tests/sys/kern/ptrace_test.c193
1 files changed, 186 insertions, 7 deletions
diff --git a/tests/sys/kern/ptrace_test.c b/tests/sys/kern/ptrace_test.c
index f611221..177f412 100644
--- a/tests/sys/kern/ptrace_test.c
+++ b/tests/sys/kern/ptrace_test.c
@@ -1094,6 +1094,16 @@ simple_thread(void *arg __unused)
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_TC_BODY(ptrace__new_child_pl_syscall_code_thread, tc)
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_TC_BODY(ptrace__new_child_pl_syscall_code_thread, tc)
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_TCS(tp)
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());
}
OpenPOWER on IntegriCloud