Module Name: src
Committed By: kamil
Date: Mon Nov 7 21:09:03 UTC 2016
Modified Files:
src/distrib/sets/lists/debug: mi
src/distrib/sets/lists/tests: mi
src/tests/kernel: Makefile t_ptrace.c
Added Files:
src/tests/kernel: t_ptrace_wait.c t_ptrace_wait3.c t_ptrace_wait4.c
t_ptrace_wait6.c t_ptrace_waitid.c t_ptrace_waitpid.c
Log Message:
Add new tests for combination of wait(2) interfaces with ptrace(2)
Move out wait(2) specific tests from t_ptrace and put them to t_ptrace_wait
Add generic code fragments to reuse the same source-code for every member
of the wait(2) family, namely:
- wait(2)
- waitpid(2)
- waitid(2)
- wait3(2)
- wait4(2)
- wait6(2)
Currently in the new test-suite there are the following tests:
- traceme1
- traceme2
- traceme3
- traceme4
- attach1
Not all tests are possible to be executed against every wait(2)-like
interface, therefore they will be disabled in such case. Currently this
limits attach1 to waitpid(2), waitid(2), wait4(2), wait6(2), while the
other tests (traceme 1-4) run with all of the interfaces.
The construct of this file is dedicated for addition of new tests in the
close future.
As of now all of the tests pass correctly.
Thanks for Robert Elz for suggestions on improving the code (earlier draft
of this new form).
Sponsored by <The NetBSD Foundation>.
To generate a diff of this commit:
cvs rdiff -u -r1.172 -r1.173 src/distrib/sets/lists/debug/mi
cvs rdiff -u -r1.695 -r1.696 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.40 -r1.41 src/tests/kernel/Makefile
cvs rdiff -u -r1.12 -r1.13 src/tests/kernel/t_ptrace.c
cvs rdiff -u -r0 -r1.1 src/tests/kernel/t_ptrace_wait.c \
src/tests/kernel/t_ptrace_wait3.c src/tests/kernel/t_ptrace_wait4.c \
src/tests/kernel/t_ptrace_wait6.c src/tests/kernel/t_ptrace_waitid.c \
src/tests/kernel/t_ptrace_waitpid.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/distrib/sets/lists/debug/mi
diff -u src/distrib/sets/lists/debug/mi:1.172 src/distrib/sets/lists/debug/mi:1.173
--- src/distrib/sets/lists/debug/mi:1.172 Sun Nov 6 17:07:50 2016
+++ src/distrib/sets/lists/debug/mi Mon Nov 7 21:09:03 2016
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.172 2016/11/06 17:07:50 alnsn Exp $
+# $NetBSD: mi,v 1.173 2016/11/07 21:09:03 kamil Exp $
./etc/mtree/set.debug comp-sys-root
./usr/lib comp-sys-usr compatdir
./usr/lib/i18n/libBIG5_g.a comp-c-debuglib debuglib,compatfile
@@ -1692,6 +1692,12 @@
./usr/libdata/debug/usr/tests/kernel/t_pollts.debug tests-obsolete obsolete,compattestfile
./usr/libdata/debug/usr/tests/kernel/t_posix_fadvise.debug tests-obsolete obsolete,compattestfile
./usr/libdata/debug/usr/tests/kernel/t_ptrace.debug tests-kernel-tests debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/kernel/t_ptrace_wait.debug tests-kernel-tests debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/kernel/t_ptrace_wait3.debug tests-kernel-tests debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/kernel/t_ptrace_wait4.debug tests-kernel-tests debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/kernel/t_ptrace_wait6.debug tests-kernel-tests debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/kernel/t_ptrace_waitid.debug tests-kernel-tests debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/kernel/t_ptrace_waitpid.debug tests-kernel-tests debug,atf,compattestfile
./usr/libdata/debug/usr/tests/kernel/t_pty.debug tests-kernel-tests debug,atf,compattestfile
./usr/libdata/debug/usr/tests/kernel/t_rnd.debug tests-kernel-tests debug,atf,rump
./usr/libdata/debug/usr/tests/kernel/t_sigaction.debug tests-obsolete obsolete,compattestfile
Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.695 src/distrib/sets/lists/tests/mi:1.696
--- src/distrib/sets/lists/tests/mi:1.695 Mon Nov 7 00:54:48 2016
+++ src/distrib/sets/lists/tests/mi Mon Nov 7 21:09:03 2016
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.695 2016/11/07 00:54:48 ozaki-r Exp $
+# $NetBSD: mi,v 1.696 2016/11/07 21:09:03 kamil Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@@ -2127,6 +2127,12 @@
./usr/tests/kernel/t_posix_fallocate tests-obsolete obsolete
./usr/tests/kernel/t_ps_strings tests-kernel-tests compattestfile,atf
./usr/tests/kernel/t_ptrace tests-kernel-tests compattestfile,atf
+./usr/tests/kernel/t_ptrace_wait tests-kernel-tests compattestfile,atf
+./usr/tests/kernel/t_ptrace_wait3 tests-kernel-tests compattestfile,atf
+./usr/tests/kernel/t_ptrace_wait4 tests-kernel-tests compattestfile,atf
+./usr/tests/kernel/t_ptrace_wait6 tests-kernel-tests compattestfile,atf
+./usr/tests/kernel/t_ptrace_waitid tests-kernel-tests compattestfile,atf
+./usr/tests/kernel/t_ptrace_waitpid tests-kernel-tests compattestfile,atf
./usr/tests/kernel/t_pty tests-kernel-tests compattestfile,atf
./usr/tests/kernel/t_rnd tests-kernel-tests atf,rump
./usr/tests/kernel/t_sigaction tests-obsolete obsolete
Index: src/tests/kernel/Makefile
diff -u src/tests/kernel/Makefile:1.40 src/tests/kernel/Makefile:1.41
--- src/tests/kernel/Makefile:1.40 Wed Nov 2 12:51:22 2016
+++ src/tests/kernel/Makefile Mon Nov 7 21:09:03 2016
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.40 2016/11/02 12:51:22 kamil Exp $
+# $NetBSD: Makefile,v 1.41 2016/11/07 21:09:03 kamil Exp $
NOMAN= # defined
@@ -10,6 +10,12 @@ TESTS_SUBDIRS= kqueue
TESTS_C= t_lock
TESTS_C+= t_lockf
TESTS_C+= t_ptrace
+TESTS_C+= t_ptrace_wait
+TESTS_C+= t_ptrace_wait3
+TESTS_C+= t_ptrace_wait4
+TESTS_C+= t_ptrace_wait6
+TESTS_C+= t_ptrace_waitid
+TESTS_C+= t_ptrace_waitpid
TESTS_C+= t_pty
TESTS_C+= t_mqueue
TESTS_C+= t_sysv
Index: src/tests/kernel/t_ptrace.c
diff -u src/tests/kernel/t_ptrace.c:1.12 src/tests/kernel/t_ptrace.c:1.13
--- src/tests/kernel/t_ptrace.c:1.12 Sun Nov 6 21:47:53 2016
+++ src/tests/kernel/t_ptrace.c Mon Nov 7 21:09:03 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: t_ptrace.c,v 1.12 2016/11/06 21:47:53 kamil Exp $ */
+/* $NetBSD: t_ptrace.c,v 1.13 2016/11/07 21:09:03 kamil Exp $ */
/*-
* Copyright (c) 2016 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__RCSID("$NetBSD: t_ptrace.c,v 1.12 2016/11/06 21:47:53 kamil Exp $");
+__RCSID("$NetBSD: t_ptrace.c,v 1.13 2016/11/07 21:09:03 kamil Exp $");
#include <sys/param.h>
#include <sys/types.h>
@@ -70,558 +70,6 @@ do { \
__FILE__, __LINE__, __func__, #x); \
} while (0)
-/* This function is currently designed to be run in the main/parent process */
-static void
-await_zombie(pid_t process)
-{
- struct kinfo_proc2 p;
- size_t len = sizeof(p);
-
- const int name[] = {
- [0] = CTL_KERN,
- [1] = KERN_PROC2,
- [2] = KERN_PROC_PID,
- [3] = process,
- [4] = sizeof(p),
- [5] = 1
- };
-
- const size_t namelen = __arraycount(name);
-
- /* Await the process becoming a zombie */
- while(1) {
- ATF_REQUIRE(sysctl(name, namelen, &p, &len, NULL, 0) == 0);
-
- if (p.p_stat == LSZOMB)
- break;
-
- ATF_REQUIRE(usleep(1000) == 0);
- }
-}
-
-ATF_TC(traceme1);
-ATF_TC_HEAD(traceme1, tc)
-{
- atf_tc_set_md_var(tc, "descr",
- "Verify SIGSTOP followed by _exit(2) in a child");
-}
-
-ATF_TC_BODY(traceme1, tc)
-{
- int status;
- const int exitval = 5;
- const int sigval = SIGSTOP;
- pid_t child, wpid;
-
- printf("Before forking process PID=%d\n", getpid());
- ATF_REQUIRE((child = fork()) != -1);
- if (child == 0) {
- FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
-
- FORKEE_ASSERT(raise(sigval) == 0);
-
- _exit(exitval);
- }
- printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
-
- printf("Before calling waitpid() for the child\n");
- wpid = waitpid(child, &status, 0);
-
- printf("Validating child's PID (expected %d, got %d)\n", child, wpid);
- ATF_REQUIRE(child == wpid);
-
- printf("Ensuring that the child has not been exited\n");
- ATF_REQUIRE(!WIFEXITED(status));
-
- printf("Ensuring that the child has not been continued\n");
- ATF_REQUIRE(!WIFCONTINUED(status));
-
- printf("Ensuring that the child has not been terminated with a "
- "signal\n");
- ATF_REQUIRE(!WIFSIGNALED(status));
-
- printf("Ensuring that the child has been stopped\n");
- ATF_REQUIRE(WIFSTOPPED(status));
-
- printf("Verifying that he child has been stopped with the %s signal "
- "(received %s)\n", sys_signame[sigval],
- sys_signame[WSTOPSIG(status)]);
- ATF_REQUIRE(WSTOPSIG(status) == sigval);
-
- printf("Before resuming the child process where it left off and "
- "without signal to be sent\n");
- ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
-
- printf("Before calling waitpid() for the child\n");
- wpid = waitpid(child, &status, 0);
-
- printf("Validating that child's PID is still there\n");
- ATF_REQUIRE(wpid == child);
-
- printf("Ensuring that the child has been exited\n");
- ATF_REQUIRE(WIFEXITED(status));
-
- printf("Ensuring that the child has not been continued\n");
- ATF_REQUIRE(!WIFCONTINUED(status));
-
- printf("Ensuring that the child has not been terminated with a "
- "signal\n");
- ATF_REQUIRE(!WIFSIGNALED(status));
-
- printf("Ensuring that the child has not been stopped\n");
- ATF_REQUIRE(!WIFSTOPPED(status));
-
- printf("Verifying that he child has exited with the %d status "
- "(received %d)\n", exitval, WEXITSTATUS(status));
- ATF_REQUIRE(WEXITSTATUS(status) == exitval);
-
- printf("Before calling waitpid() for the exited child\n");
- wpid = waitpid(child, &status, 0);
-
- printf("Validating that child's PID no longer exists\n");
- ATF_REQUIRE(wpid == -1);
-
- printf("Validating that errno is set to %s (got %s)\n",
- strerror(ECHILD), strerror(errno));
- ATF_REQUIRE(errno == ECHILD);
-}
-
-ATF_TC(traceme2);
-ATF_TC_HEAD(traceme2, tc)
-{
- atf_tc_set_md_var(tc, "descr",
- "Verify SIGSTOP followed by _exit(2) in a child");
-}
-
-static int traceme2_caught = 0;
-
-static void
-traceme2_sighandler(int sig)
-{
- FORKEE_ASSERTX(sig == SIGINT);
-
- ++traceme2_caught;
-}
-
-ATF_TC_BODY(traceme2, tc)
-{
- int status;
- const int exitval = 5;
- const int sigval = SIGSTOP, sigsent = SIGINT;
- pid_t child, wpid;
- struct sigaction sa;
-
- printf("Before forking process PID=%d\n", getpid());
- ATF_REQUIRE((child = fork()) != -1);
- if (child == 0) {
- FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
-
- sa.sa_handler = traceme2_sighandler;
- sa.sa_flags = SA_SIGINFO;
- sigemptyset(&sa.sa_mask);
-
- FORKEE_ASSERT(sigaction(sigsent, &sa, NULL) != -1);
-
- FORKEE_ASSERT(raise(sigval) == 0);
-
- FORKEE_ASSERTX(traceme2_caught == 1);
-
- _exit(exitval);
- }
- printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
-
- printf("Before calling waitpid() for the child\n");
- wpid = waitpid(child, &status, 0);
-
- printf("Validating child's PID (expected %d, got %d)\n", child, wpid);
- ATF_REQUIRE(child == wpid);
-
- printf("Ensuring that the child has not been exited\n");
- ATF_REQUIRE(!WIFEXITED(status));
-
- printf("Ensuring that the child has not been continued\n");
- ATF_REQUIRE(!WIFCONTINUED(status));
-
- printf("Ensuring that the child has not been terminated with a "
- "signal\n");
- ATF_REQUIRE(!WIFSIGNALED(status));
-
- printf("Ensuring that the child has been stopped\n");
- ATF_REQUIRE(WIFSTOPPED(status));
-
- printf("Verifying that he child has been stopped with the %s signal "
- "(received %s)\n", sys_signame[sigval],
- sys_signame[WSTOPSIG(status)]);
- ATF_REQUIRE(WSTOPSIG(status) == sigval);
-
- printf("Before resuming the child process where it left off and with "
- "signal %s to be sent\n", sys_signame[sigsent]);
- ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1);
-
- printf("Before calling waitpid() for the child\n");
- wpid = waitpid(child, &status, 0);
-
- printf("Validating that child's PID is still there\n");
- ATF_REQUIRE(wpid == child);
-
- printf("Ensuring that the child has been exited\n");
- ATF_REQUIRE(WIFEXITED(status));
-
- printf("Ensuring that the child has not been continued\n");
- ATF_REQUIRE(!WIFCONTINUED(status));
-
- printf("Ensuring that the child has not been terminated with a "
- "signal\n");
- ATF_REQUIRE(!WIFSIGNALED(status));
-
- printf("Ensuring that the child has not been stopped\n");
- ATF_REQUIRE(!WIFSTOPPED(status));
-
- printf("Verifying that he child has exited with the %d status "
- "(received %d)\n", exitval, WEXITSTATUS(status));
- ATF_REQUIRE(WEXITSTATUS(status) == exitval);
-
- printf("Before calling waitpid() for the exited child\n");
- wpid = waitpid(child, &status, 0);
-
- printf("Validating that child's PID no longer exists\n");
- ATF_REQUIRE(wpid == -1);
-
- printf("Validating that errno is set to %s (got %s)\n",
- strerror(ECHILD), strerror(errno));
- ATF_REQUIRE(errno == ECHILD);
-}
-
-ATF_TC(traceme3);
-ATF_TC_HEAD(traceme3, tc)
-{
- atf_tc_set_md_var(tc, "descr",
- "Verify SIGSTOP followed by termination by a signal in a child");
-}
-
-ATF_TC_BODY(traceme3, tc)
-{
- int status;
- const int sigval = SIGSTOP, sigsent = SIGINT /* Without core-dump */;
- pid_t child, wpid;
-
- printf("Before forking process PID=%d\n", getpid());
- ATF_REQUIRE((child = fork()) != -1);
- if (child == 0) {
- FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
-
- FORKEE_ASSERT(raise(sigval) == 0);
-
- /* NOTREACHED */
- FORKEE_ASSERTX(0 &&
- "Child should be terminated by a signal from its parent");
- }
- printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
-
- printf("Before calling waitpid() for the child\n");
- wpid = waitpid(child, &status, 0);
-
- printf("Validating child's PID (expected %d, got %d)\n", child, wpid);
- ATF_REQUIRE(child == wpid);
-
- printf("Ensuring that the child has not been exited\n");
- ATF_REQUIRE(!WIFEXITED(status));
-
- printf("Ensuring that the child has not been continued\n");
- ATF_REQUIRE(!WIFCONTINUED(status));
-
- printf("Ensuring that the child has not been terminated with a "
- "signal\n");
- ATF_REQUIRE(!WIFSIGNALED(status));
-
- printf("Ensuring that the child has been stopped\n");
- ATF_REQUIRE(WIFSTOPPED(status));
-
- printf("Verifying that he child has been stopped with the %s signal "
- "(received %s)\n", sys_signame[sigval],
- sys_signame[WSTOPSIG(status)]);
- ATF_REQUIRE(WSTOPSIG(status) == sigval);
-
- printf("Before resuming the child process where it left off and with "
- "signal %s to be sent\n", sys_signame[sigsent]);
- ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1);
-
- printf("Before calling waitpid() for the child\n");
- wpid = waitpid(child, &status, 0);
-
- printf("Validating that child's PID is still there\n");
- ATF_REQUIRE(wpid == child);
-
- printf("Ensuring that the child has not been exited\n");
- ATF_REQUIRE(!WIFEXITED(status));
-
- printf("Ensuring that the child has not been continued\n");
- ATF_REQUIRE(!WIFCONTINUED(status));
-
- printf("Ensuring that the child hast been terminated with a "
- "signal\n");
- ATF_REQUIRE(WIFSIGNALED(status));
-
- printf("Ensuring that the child has not been stopped\n");
- ATF_REQUIRE(!WIFSTOPPED(status));
-
- printf("Verifying that he child has not core dumped for the %s signal "
- "(it is the default behavior)\n", sys_signame[sigsent]);
- ATF_REQUIRE(!WCOREDUMP(status));
-
- printf("Verifying that he child has been stopped with the %s "
- "signal (received %s)\n", sys_signame[sigsent],
- sys_signame[WTERMSIG(status)]);
- ATF_REQUIRE(WTERMSIG(status) == sigsent);
-
- printf("Before calling waitpid() for the exited child\n");
- wpid = waitpid(child, &status, 0);
-
- printf("Validating that child's PID no longer exists\n");
- ATF_REQUIRE(wpid == -1);
-
- printf("Validating that errno is set to %s (got %s)\n",
- strerror(ECHILD), strerror(errno));
- ATF_REQUIRE(errno == ECHILD);
-}
-
-ATF_TC(traceme4);
-ATF_TC_HEAD(traceme4, tc)
-{
- atf_tc_set_md_var(tc, "descr",
- "Verify SIGSTOP followed by SIGCONT and _exit(2) in a child");
-}
-
-ATF_TC_BODY(traceme4, tc)
-{
- int status;
- const int exitval = 5;
- const int sigval = SIGSTOP, sigsent = SIGCONT;
- pid_t child, wpid;
-
- printf("Before forking process PID=%d\n", getpid());
- ATF_REQUIRE((child = fork()) != -1);
- if (child == 0) {
- FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
-
- FORKEE_ASSERT(raise(sigval) == 0);
-
- FORKEE_ASSERT(raise(sigsent) == 0);
-
- _exit(exitval);
- }
- printf("Parent process PID=%d, child's PID=%d\n", getpid(),child);
-
- printf("Before calling waitpid() for the child\n");
- wpid = waitpid(child, &status, 0);
-
- printf("Validating child's PID (expected %d, got %d)\n", child, wpid);
- ATF_REQUIRE(child == wpid);
-
- printf("Ensuring that the child has not been exited\n");
- ATF_REQUIRE(!WIFEXITED(status));
-
- printf("Ensuring that the child has not been continued\n");
- ATF_REQUIRE(!WIFCONTINUED(status));
-
- printf("Ensuring that the child has not been terminated with a "
- "signal\n");
- ATF_REQUIRE(!WIFSIGNALED(status));
-
- printf("Ensuring that the child has been stopped\n");
- ATF_REQUIRE(WIFSTOPPED(status));
-
- printf("Verifying that he child has been stopped with the %s signal "
- "(received %s)\n", sys_signame[sigval],
- sys_signame[WSTOPSIG(status)]);
- ATF_REQUIRE(WSTOPSIG(status) == sigval);
-
- printf("Before resuming the child process where it left off and "
- "without signal to be sent\n");
- ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
-
- printf("Before calling waitpid() for the child\n");
- wpid = waitpid(child, &status, WALLSIG);
-
- printf("Validating that child's PID is still there\n");
- ATF_REQUIRE(wpid == child);
-
- printf("Ensuring that the child has not been exited\n");
- ATF_REQUIRE(!WIFEXITED(status));
-
- printf("Ensuring that the child has not been continued\n");
- ATF_REQUIRE(!WIFCONTINUED(status));
-
- printf("Ensuring that the child has not been terminated with a "
- "signal\n");
- ATF_REQUIRE(!WIFSIGNALED(status));
-
- printf("Ensuring that the child has not been stopped\n");
- ATF_REQUIRE(WIFSTOPPED(status));
-
- printf("Before resuming the child process where it left off and "
- "without signal to be sent\n");
- ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
-
- printf("Before calling waitpid() for the child\n");
- wpid = waitpid(child, &status, 0);
-
- printf("Validating that child's PID is still there\n");
- ATF_REQUIRE(wpid == child);
-
- printf("Ensuring that the child has been exited\n");
- ATF_REQUIRE(WIFEXITED(status));
-
- printf("Ensuring that the child has not been continued\n");
- ATF_REQUIRE(!WIFCONTINUED(status));
-
- printf("Ensuring that the child has not been terminated with a "
- "signal\n");
- ATF_REQUIRE(!WIFSIGNALED(status));
-
- printf("Ensuring that the child has not been stopped\n");
- ATF_REQUIRE(!WIFSTOPPED(status));
-
- printf("Verifying that he child has exited with the %d status "
- "(received %d)\n", exitval, WEXITSTATUS(status));
- ATF_REQUIRE(WEXITSTATUS(status) == exitval);
-
- printf("Before calling waitpid() for the exited child\n");
- wpid = waitpid(child, &status, 0);
-
- printf("Validating that child's PID no longer exists\n");
- ATF_REQUIRE(wpid == -1);
-
- printf("Validating that errno is set to %s (got %s)\n",
- strerror(ECHILD), strerror(errno));
- ATF_REQUIRE(errno == ECHILD);
-}
-
-ATF_TC(attach1);
-ATF_TC_HEAD(attach1, tc)
-{
- atf_tc_set_md_var(tc, "descr",
- "Assert that tracer sees process termination before the parent");
-}
-
-ATF_TC_BODY(attach1, tc)
-{
- int fds_totracee[2], fds_totracer[2], fds_fromtracer[2];
- int status, rv;
- const int exitval_tracee = 5;
- const int exitval_tracer = 10;
- pid_t tracee, tracer, wpid;
- uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
-
- printf("Spawn tracee\n");
- ATF_REQUIRE(pipe(fds_totracee) == 0);
- ATF_REQUIRE((tracee = fork()) != -1);
- if (tracee == 0) {
- FORKEE_ASSERT(close(fds_totracee[1]) == 0);
-
- /* Wait for message from the parent */
- rv = read(fds_totracee[0], &msg, sizeof(msg));
- FORKEE_ASSERT(rv == sizeof(msg));
-
- _exit(exitval_tracee);
- }
- ATF_REQUIRE(close(fds_totracee[0]) == 0);
-
- printf("Spawn debugger\n");
- ATF_REQUIRE(pipe(fds_totracer) == 0);
- ATF_REQUIRE(pipe(fds_fromtracer) == 0);
- ATF_REQUIRE((tracer = fork()) != -1);
- if (tracer == 0) {
- /* No IPC to communicate with the child */
- FORKEE_ASSERT(close(fds_totracee[1]) == 0);
-
- FORKEE_ASSERT(close(fds_totracer[1]) == 0);
- FORKEE_ASSERT(close(fds_fromtracer[0]) == 0);
-
- FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
-
- /* Wait for tracee and assert that it was stopped w/ SIGSTOP */
- wpid = waitpid(tracee, &status, 0);
- FORKEE_ASSERTX(wpid == tracee);
- FORKEE_ASSERTX(!WIFEXITED(status));
- FORKEE_ASSERTX(!WIFCONTINUED(status));
- FORKEE_ASSERTX(!WIFSIGNALED(status));
- FORKEE_ASSERTX(WIFSTOPPED(status));
- FORKEE_ASSERTX(WSTOPSIG(status) == SIGSTOP);
-
- /* Resume tracee with PT_CONTINUE */
- FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
-
- /* Inform parent that tracer has attached to tracee */
- rv = write(fds_fromtracer[1], &msg, sizeof(msg));
- FORKEE_ASSERT(rv == sizeof(msg));
-
- /* Wait for parent */
- rv = read(fds_totracer[0], &msg, sizeof(msg));
- FORKEE_ASSERT(rv == sizeof(msg));
-
- /* Wait for tracee and assert that it exited */
- wpid = waitpid(tracee, &status, 0);
- FORKEE_ASSERTX(wpid == tracee);
- FORKEE_ASSERTX(WIFEXITED(status));
- FORKEE_ASSERTX(!WIFCONTINUED(status));
- FORKEE_ASSERTX(!WIFSIGNALED(status));
- FORKEE_ASSERTX(!WIFSTOPPED(status));
- FORKEE_ASSERTX(WEXITSTATUS(status) == exitval_tracee);
-
- _exit(exitval_tracer);
- }
- ATF_REQUIRE(close(fds_totracer[0]) == 0);
- ATF_REQUIRE(close(fds_fromtracer[1]) == 0);
-
- printf("Wait for the tracer to attach to the tracee\n");
- rv = read(fds_fromtracer[0], &msg, sizeof(msg));
- ATF_REQUIRE(rv == sizeof(msg));
-
- printf("Resume the tracee and let it exit\n");
- rv = write(fds_totracee[1], &msg, sizeof(msg));
- ATF_REQUIRE(rv == sizeof(msg));
-
- printf("fds_totracee is no longer needed - close it\n");
- ATF_REQUIRE(close(fds_totracee[1]) == 0);
-
- printf("Detect that tracee is zombie\n");
- await_zombie(tracee);
-
- printf("Assert that there is no status about tracee - "
- "Tracer must detect zombie first\n");
- wpid = waitpid(tracee, &status, WNOHANG);
- ATF_REQUIRE(wpid == 0);
-
- printf("Resume the tracer and let it detect exited tracee\n");
- rv = write(fds_totracer[1], &msg, sizeof(msg));
- ATF_REQUIRE(rv == sizeof(msg));
-
- printf("Wait for tracer to finish its job and exit\n");
- wpid = waitpid(tracer, &status, 0);
- ATF_REQUIRE(wpid == tracer);
- ATF_REQUIRE(WIFEXITED(status));
- ATF_REQUIRE(!WIFCONTINUED(status));
- ATF_REQUIRE(!WIFSIGNALED(status));
- ATF_REQUIRE(!WIFSTOPPED(status));
- ATF_REQUIRE(WEXITSTATUS(status) == exitval_tracer);
-
- printf("Wait for tracer to finish its job and exit\n");
- wpid = waitpid(tracee, &status, WNOHANG);
- ATF_REQUIRE(wpid == tracee);
- ATF_REQUIRE(WIFEXITED(status));
- ATF_REQUIRE(!WIFCONTINUED(status));
- ATF_REQUIRE(!WIFSIGNALED(status));
- ATF_REQUIRE(!WIFSTOPPED(status));
- ATF_REQUIRE(WEXITSTATUS(status) == exitval_tracee);
-
- printf("fds_fromtracer is no longer needed - close it\n");
- ATF_REQUIRE(close(fds_fromtracer[0]) == 0);
-
- printf("fds_totracer is no longer needed - close it\n");
- ATF_REQUIRE(close(fds_totracer[1]) == 0);
-}
-
ATF_TC(attach_pid0);
ATF_TC_HEAD(attach_pid0, tc)
{
@@ -632,8 +80,7 @@ ATF_TC_HEAD(attach_pid0, tc)
ATF_TC_BODY(attach_pid0, tc)
{
errno = 0;
- ATF_REQUIRE(ptrace(PT_ATTACH, 0, NULL, 0) == -1);
- ATF_REQUIRE(errno == EPERM);
+ ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 0, NULL, 0) == -1);
}
ATF_TC(attach_pid1);
@@ -647,18 +94,11 @@ ATF_TC_HEAD(attach_pid1, tc)
ATF_TC_BODY(attach_pid1, tc)
{
- errno = 0;
- ATF_REQUIRE(ptrace(PT_ATTACH, 1, NULL, 0) == -1);
- ATF_REQUIRE(errno == EPERM);
+ ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1);
}
ATF_TP_ADD_TCS(tp)
{
- ATF_TP_ADD_TC(tp, traceme1);
- ATF_TP_ADD_TC(tp, traceme2);
- ATF_TP_ADD_TC(tp, traceme3);
- ATF_TP_ADD_TC(tp, traceme4);
- ATF_TP_ADD_TC(tp, attach1);
ATF_TP_ADD_TC(tp, attach_pid0);
ATF_TP_ADD_TC(tp, attach_pid1);
Added files:
Index: src/tests/kernel/t_ptrace_wait.c
diff -u /dev/null src/tests/kernel/t_ptrace_wait.c:1.1
--- /dev/null Mon Nov 7 21:09:03 2016
+++ src/tests/kernel/t_ptrace_wait.c Mon Nov 7 21:09:03 2016
@@ -0,0 +1,694 @@
+/* $NetBSD: t_ptrace_wait.c,v 1.1 2016/11/07 21:09:03 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_ptrace_wait.c,v 1.1 2016/11/07 21:09:03 kamil Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "../h_macros.h"
+
+/* Detect plain wait(2) use-case */
+#if !defined(TWAIT_WAITPID) && \
+ !defined(TWAIT_WAITID) && \
+ !defined(TWAIT_WAIT3) && \
+ !defined(TWAIT_WAIT4) && \
+ !defined(TWAIT_WAIT6)
+#define TWAIT_WAIT
+#endif
+
+/*
+ * There are two classes of wait(2)-like functions:
+ * - wait4(2)-like accepting pid_t, optional options parameter, struct rusage*
+ * - wait6(2)-like accepting idtype_t, id_t, struct wrusage, mandatory options
+ *
+ * The TWAIT_FNAME value is to be used for convenience in debug messages.
+ *
+ * The TWAIT_GENERIC() macro is designed to reuse the same unmodified
+ * code with as many wait(2)-like functions as possible.
+ *
+ * In a common use-case wait4(2) and wait6(2)-like function can work the almost
+ * the same way, however there are few important differences:
+ * wait6(2) must specify P_PID for idtype to match wpid from wait4(2).
+ * To behave like wait4(2), wait6(2) the 'options' to wait must include
+ * WEXITED|WTRUNCATED.
+ *
+ * There are two helper macros (they purpose it to mach more than one
+ * wait(2)-like function):
+ * The TWAIT_HAVE_STATUS - specifies whether a function can retrieve
+ * status (as integer value).
+ * The TWAIT_HAVE_PID - specifies whether a function can request
+ * exact process identifier
+ * The TWAIT_HAVE_RUSAGE - specifies whether a function can request
+ * the struct rusage value
+ *
+ */
+
+#if defined(TWAIT_WAIT)
+# define TWAIT_FNAME "wait"
+# define TWAIT_WAIT4TYPE(a,b,c,d) wait((b))
+# define TWAIT_GENERIC(a,b,c) wait((b))
+# define TWAIT_HAVE_STATUS 1
+#elif defined(TWAIT_WAITPID)
+# define TWAIT_FNAME "waitpid"
+# define TWAIT_WAIT4TYPE(a,b,c,d) waitpid((a),(b),(c))
+# define TWAIT_GENERIC(a,b,c) waitpid((a),(b),(c))
+# define TWAIT_HAVE_PID 1
+# define TWAIT_HAVE_STATUS 1
+#elif defined(TWAIT_WAITID)
+# define TWAIT_FNAME "waitid"
+# define TWAIT_GENERIC(a,b,c) \
+ waitid(P_PID,(a),NULL,(c)|WEXITED|WTRAPPED)
+# define TWAIT_WAIT6TYPE(a,b,c,d,e,f) waitid((a),(b),(f),(d))
+# define TWAIT_HAVE_PID 1
+#elif defined(TWAIT_WAIT3)
+# define TWAIT_FNAME "wait3"
+# define TWAIT_WAIT4TYPE(a,b,c,d) wait3((b),(c),(d))
+# define TWAIT_GENERIC(a,b,c) wait3((b),(c),NULL)
+# define TWAIT_HAVE_STATUS 1
+# define TWAIT_HAVE_RUSAGE 1
+#elif defined(TWAIT_WAIT4)
+# define TWAIT_FNAME "wait4"
+# define TWAIT_WAIT4TYPE(a,b,c,d) wait4((a),(b),(c),(d))
+# define TWAIT_GENERIC(a,b,c) wait4((a),(b),(c),NULL)
+# define TWAIT_HAVE_PID 1
+# define TWAIT_HAVE_STATUS 1
+# define TWAIT_HAVE_RUSAGE 1
+#elif defined(TWAIT_WAIT6)
+# define TWAIT_FNAME "wait6"
+# define TWAIT_WAIT6TYPE(a,b,c,d,e,f) wait6((a),(b),(c),(d),(e),(f))
+# define TWAIT_GENERIC(a,b,c) \
+ wait6(P_PID,(a),(b),(c)|WEXITED|WTRAPPED,NULL,NULL)
+# define TWAIT_HAVE_PID 1
+# define TWAIT_HAVE_STATUS 1
+#endif
+
+/*
+ * There are 3 groups of tests:
+ * - TWAIT_GENERIC() (wait, wait2, waitpid, wait3, wait4, wait6)
+ * - TWAIT_WAIT4TYPE() (wait2, waitpid, wait3, wait4)
+ * - TWAIT_WAIT6TYPE() (waitid, wait6)
+ *
+ * Tests only in the above categories are allowed. However some tests are not
+ * possible in the context requested functionality to be verified, therefore
+ * there are helper macros:
+ * - TWAIT_HAVE_PID (wait2, waitpid, waitid, wait4, wait6)
+ * - TWAIT_HAVE_STATUS (wait, wait2, waitpid, wait3, wait4, wait6)
+ * - TWAIT_HAVE_RUSAGE (wait3, wait4)
+ * - TWAIT_HAVE_RETPID (wait, wait2, waitpid, wait3, wait4, wait6)
+ *
+ * If there is an intention to test e.g. wait6(2) specific features in the
+ * ptrace(2) context, find the most matching group and with #ifdefs reduce
+ * functionality of less featured than wait6(2) interface (TWAIT_WAIT6TYPE).
+ *
+ * For clarity never use negative preprocessor checks, like:
+ * #if !defined(TWAIT_WAIT4)
+ * always refer to checks for positive values.
+ */
+
+/*
+ * A child process cannot call atf functions and expect them to magically
+ * work like in the parent.
+ * The printf(3) messaging from a child will not work out of the box as well
+ * without estabilishing a communication protocol with its parent. To not
+ * overcomplicate the tests - do not log from a child and use err(3)/errx(3)
+ * wrapped with FORKEE_ASSERT()/FORKEE_ASSERTX() as that is guaranteed to work.
+ */
+#define FORKEE_ASSERTX(x) \
+do { \
+ int ret = (x); \
+ if (!ret) \
+ errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \
+ __FILE__, __LINE__, __func__, #x); \
+} while (0)
+
+#define FORKEE_ASSERT(x) \
+do { \
+ int ret = (x); \
+ if (!ret) \
+ err(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \
+ __FILE__, __LINE__, __func__, #x); \
+} while (0)
+
+/*
+ * If waitid(2) returns because one or more processes have a state change to
+ * report, 0 is returned. If an error is detected, a value of -1 is returned
+ * and errno is set to indicate the error. If WNOHANG is specified and there
+ * are no stopped, continued or exited children, 0 is returned.
+ */
+#if defined(TWAIT_WAITID)
+#define TWAIT_REQUIRE_SUCCESS(a,b) ATF_REQUIRE((a) == 0)
+#define TWAIT_REQUIRE_FAILURE(a,b) ATF_REQUIRE_ERRNO((a),(b) == -1)
+#define FORKEE_REQUIRE_SUCCESS(a,b) FORKEE_ASSERTX((a) == 0)
+#define FORKEE_REQUIRE_FAILURE(a,b) \
+ FORKEE_ASSERTX(((a) == errno) && ((b) == -1))
+#else
+#define TWAIT_REQUIRE_SUCCESS(a,b) ATF_REQUIRE((a) == (b))
+#define TWAIT_REQUIRE_FAILURE(a,b) ATF_REQUIRE_ERRNO((a),(b) == -1)
+#define FORKEE_REQUIRE_SUCCESS(a,b) FORKEE_ASSERTX((a) == (b))
+#define FORKEE_REQUIRE_FAILURE(a,b) \
+ FORKEE_ASSERTX(((a) == errno) && ((b) == -1))
+#endif
+
+/*
+ * Helper tools to verify whether status reports exited value
+ */
+#if TWAIT_HAVE_STATUS
+static void __used
+validate_status_exited(int status, int expected)
+{
+ ATF_REQUIRE_MSG(WIFEXITED(status), "Reported exited process");
+ ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process");
+ ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process");
+ ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process");
+
+ ATF_REQUIRE_EQ_MSG(WEXITSTATUS(status), expected,
+ "The process has exited with invalid value");
+}
+
+static void __used
+forkee_status_exited(int status, int expected)
+{
+ FORKEE_ASSERTX(WIFEXITED(status));
+ FORKEE_ASSERTX(!WIFCONTINUED(status));
+ FORKEE_ASSERTX(!WIFSIGNALED(status));
+ FORKEE_ASSERTX(!WIFSTOPPED(status));
+
+ FORKEE_ASSERTX(WEXITSTATUS(status) == expected);
+}
+
+static void __used
+validate_status_continued(int status)
+{
+ ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process");
+ ATF_REQUIRE_MSG(WIFCONTINUED(status), "Reported continued process");
+ ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process");
+ ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process");
+}
+
+static void __used
+forkee_status_continued(int status)
+{
+ FORKEE_ASSERTX(!WIFEXITED(status));
+ FORKEE_ASSERTX(WIFCONTINUED(status));
+ FORKEE_ASSERTX(!WIFSIGNALED(status));
+ FORKEE_ASSERTX(!WIFSTOPPED(status));
+}
+
+static void __used
+validate_status_signaled(int status, int expected_termsig, int expected_core)
+{
+ ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process");
+ ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process");
+ ATF_REQUIRE_MSG(WIFSIGNALED(status), "Reported signaled process");
+ ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process");
+
+ ATF_REQUIRE_EQ_MSG(WTERMSIG(status), expected_termsig,
+ "Unexpected signal received");
+
+ ATF_REQUIRE_EQ_MSG(WCOREDUMP(status), expected_core,
+ "Unexpectedly core file %s generated", expected_core ? "not" : "");
+}
+
+static void __used
+forkee_status_signaled(int status, int expected_termsig, int expected_core)
+{
+ FORKEE_ASSERTX(!WIFEXITED(status));
+ FORKEE_ASSERTX(!WIFCONTINUED(status));
+ FORKEE_ASSERTX(WIFSIGNALED(status));
+ FORKEE_ASSERTX(!WIFSTOPPED(status));
+
+ FORKEE_ASSERTX(WTERMSIG(status) == expected_termsig);
+ FORKEE_ASSERTX(WCOREDUMP(status) == expected_core);
+}
+
+static void __used
+validate_status_stopped(int status, int expected)
+{
+ ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process");
+ ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process");
+ ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process");
+ ATF_REQUIRE_MSG(WIFSTOPPED(status), "Reported stopped process");
+
+ ATF_REQUIRE_EQ_MSG(WSTOPSIG(status), expected,
+ "Unexpected stop signal received");
+}
+
+static void __used
+forkee_status_stopped(int status, int expected)
+{
+ FORKEE_ASSERTX(!WIFEXITED(status));
+ FORKEE_ASSERTX(!WIFCONTINUED(status));
+ FORKEE_ASSERTX(!WIFSIGNALED(status));
+ FORKEE_ASSERTX(WIFSTOPPED(status));
+
+ FORKEE_ASSERTX(WSTOPSIG(status) == expected);
+}
+#else
+#define validate_status_exited(a,b)
+#define forkee_status_exited(a,b)
+#define validate_status_continued(a,b)
+#define forkee_status_continued(a,b)
+#define validate_status_signaled(a,b,c)
+#define forkee_status_signaled(a,b,c)
+#define validate_status_stopped(a,b)
+#define forkee_status_stopped(a,b)
+#endif
+
+#if defined(TWAIT_HAVE_PID)
+/* This function is currently designed to be run in the main/parent process */
+static void
+await_zombie(pid_t process)
+{
+ struct kinfo_proc2 p;
+ size_t len = sizeof(p);
+
+ const int name[] = {
+ [0] = CTL_KERN,
+ [1] = KERN_PROC2,
+ [2] = KERN_PROC_PID,
+ [3] = process,
+ [4] = sizeof(p),
+ [5] = 1
+ };
+
+ const size_t namelen = __arraycount(name);
+
+ /* Await the process becoming a zombie */
+ while(1) {
+ ATF_REQUIRE(sysctl(name, namelen, &p, &len, NULL, 0) == 0);
+
+ if (p.p_stat == LSZOMB)
+ break;
+
+ ATF_REQUIRE(usleep(1000) == 0);
+ }
+}
+#endif
+
+ATF_TC(traceme1);
+ATF_TC_HEAD(traceme1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify SIGSTOP followed by _exit(2) in a child");
+}
+
+ATF_TC_BODY(traceme1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ child = atf_utils_fork();
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());fflush(stdout);
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));fflush(stdout);
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");fflush(stdout);
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(traceme2);
+ATF_TC_HEAD(traceme2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify SIGSTOP followed by _exit(2) in a child");
+}
+
+static int traceme2_caught = 0;
+
+static void
+traceme2_sighandler(int sig)
+{
+ FORKEE_ASSERTX(sig == SIGINT);
+
+ ++traceme2_caught;
+}
+
+ATF_TC_BODY(traceme2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP, sigsent = SIGINT;
+ pid_t child, wpid;
+ struct sigaction sa;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ child = atf_utils_fork();
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sa.sa_handler = traceme2_sighandler;
+ sa.sa_flags = SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+
+ FORKEE_ASSERT(sigaction(sigsent, &sa, NULL) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERTX(traceme2_caught == 1);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and with "
+ "signal %s to be sent\n", strsignal(sigsent));
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the exited child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(traceme3);
+ATF_TC_HEAD(traceme3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify SIGSTOP followed by termination by a signal in a child");
+}
+
+ATF_TC_BODY(traceme3, tc)
+{
+ const int sigval = SIGSTOP, sigsent = SIGINT /* Without core-dump */;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ child = atf_utils_fork();
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ /* NOTREACHED */
+ FORKEE_ASSERTX(0 &&
+ "Child should be terminated by a signal from its parent");
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and with "
+ "signal %s to be sent\n", strsignal(sigsent));
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_signaled(status, sigsent, 0);
+
+ printf("Before calling %s() for the exited child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(traceme4);
+ATF_TC_HEAD(traceme4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify SIGSTOP followed by SIGCONT and _exit(2) in a child");
+}
+
+ATF_TC_BODY(traceme4, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP, sigsent = SIGCONT;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ child = atf_utils_fork();
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before raising %s from child\n", strsignal(sigsent));
+ FORKEE_ASSERT(raise(sigsent) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(),child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigsent);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the exited child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+#if defined(TWAIT_HAVE_PID)
+ATF_TC(attach1);
+ATF_TC_HEAD(attach1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that tracer sees process termination before the parent");
+}
+
+ATF_TC_BODY(attach1, tc)
+{
+ int fds_totracee[2], fds_totracer[2], fds_fromtracer[2];
+ int rv;
+ const int exitval_tracee = 5;
+ const int exitval_tracer = 10;
+ pid_t tracee, tracer, wpid;
+ uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Spawn tracee\n");
+ ATF_REQUIRE(pipe(fds_totracee) == 0);
+ tracee = atf_utils_fork();
+ if (tracee == 0) {
+ FORKEE_ASSERT(close(fds_totracee[1]) == 0);
+
+ /* Wait for message from the parent */
+ rv = read(fds_totracee[0], &msg, sizeof(msg));
+ FORKEE_ASSERT(rv == sizeof(msg));
+
+ _exit(exitval_tracee);
+ }
+ ATF_REQUIRE(close(fds_totracee[0]) == 0);
+
+ printf("Spawn debugger\n");
+ ATF_REQUIRE(pipe(fds_totracer) == 0);
+ ATF_REQUIRE(pipe(fds_fromtracer) == 0);
+ tracer = atf_utils_fork();
+ if (tracer == 0) {
+ /* No IPC to communicate with the child */
+ FORKEE_ASSERT(close(fds_totracee[1]) == 0);
+
+ FORKEE_ASSERT(close(fds_totracer[1]) == 0);
+ FORKEE_ASSERT(close(fds_fromtracer[0]) == 0);
+
+ printf("Before calling PT_ATTACH from tracee %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
+
+ /* Wait for tracee and assert that it was stopped w/ SIGSTOP */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_stopped(status, SIGSTOP);
+
+ /* Resume tracee with PT_CONTINUE */
+ FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
+
+ /* Inform parent that tracer has attached to tracee */
+ rv = write(fds_fromtracer[1], &msg, sizeof(msg));
+ FORKEE_ASSERT(rv == sizeof(msg));
+
+ /* Wait for parent */
+ rv = read(fds_totracer[0], &msg, sizeof(msg));
+ FORKEE_ASSERT(rv == sizeof(msg));
+
+ /* Wait for tracee and assert that it exited */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_exited(status, exitval_tracee);
+
+ printf("Before exiting of the tracer process\n");
+ _exit(exitval_tracer);
+ }
+ ATF_REQUIRE(close(fds_totracer[0]) == 0);
+ ATF_REQUIRE(close(fds_fromtracer[1]) == 0);
+
+ printf("Wait for the tracer to attach to the tracee\n");
+ rv = read(fds_fromtracer[0], &msg, sizeof(msg));
+ ATF_REQUIRE(rv == sizeof(msg));
+
+ printf("Resume the tracee and let it exit\n");
+ rv = write(fds_totracee[1], &msg, sizeof(msg));
+ ATF_REQUIRE(rv == sizeof(msg));
+
+ printf("fds_totracee is no longer needed - close it\n");
+ ATF_REQUIRE(close(fds_totracee[1]) == 0);
+
+ printf("Detect that tracee is zombie\n");
+ await_zombie(tracee);
+
+ printf("Assert that there is no status about tracee - "
+ "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
+
+ printf("Resume the tracer and let it detect exited tracee\n");
+ rv = write(fds_totracer[1], &msg, sizeof(msg));
+ ATF_REQUIRE(rv == sizeof(msg));
+
+ printf("Wait for tracer to finish its job and exit - calling %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
+ tracer);
+
+ validate_status_exited(status, exitval_tracer);
+
+ printf("Wait for tracee to finish its job and exit - calling %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG),
+ tracee);
+
+ validate_status_exited(status, exitval_tracee);
+
+ printf("fds_fromtracer is no longer needed - close it\n");
+ ATF_REQUIRE(close(fds_fromtracer[0]) == 0);
+
+ printf("fds_totracer is no longer needed - close it\n");
+ ATF_REQUIRE(close(fds_totracer[1]) == 0);
+}
+#endif
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, traceme1);
+ ATF_TP_ADD_TC(tp, traceme2);
+ ATF_TP_ADD_TC(tp, traceme3);
+ ATF_TP_ADD_TC(tp, traceme4);
+
+#if defined(TWAIT_HAVE_PID)
+ ATF_TP_ADD_TC(tp, attach1);
+#endif
+
+#if defined(TWAIT_WAIT4TYPE)
+ /* TODO */
+#endif
+
+#if defined(TWAIT_WAIT6TYPE)
+ /* TODO */
+#endif
+
+ return atf_no_error();
+}
Index: src/tests/kernel/t_ptrace_wait3.c
diff -u /dev/null src/tests/kernel/t_ptrace_wait3.c:1.1
--- /dev/null Mon Nov 7 21:09:03 2016
+++ src/tests/kernel/t_ptrace_wait3.c Mon Nov 7 21:09:03 2016
@@ -0,0 +1,30 @@
+/* $NetBSD: t_ptrace_wait3.c,v 1.1 2016/11/07 21:09:03 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define TWAIT_WAIT3
+#include "t_ptrace_wait.c"
Index: src/tests/kernel/t_ptrace_wait4.c
diff -u /dev/null src/tests/kernel/t_ptrace_wait4.c:1.1
--- /dev/null Mon Nov 7 21:09:03 2016
+++ src/tests/kernel/t_ptrace_wait4.c Mon Nov 7 21:09:03 2016
@@ -0,0 +1,30 @@
+/* $NetBSD: t_ptrace_wait4.c,v 1.1 2016/11/07 21:09:03 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define TWAIT_WAIT4
+#include "t_ptrace_wait.c"
Index: src/tests/kernel/t_ptrace_wait6.c
diff -u /dev/null src/tests/kernel/t_ptrace_wait6.c:1.1
--- /dev/null Mon Nov 7 21:09:03 2016
+++ src/tests/kernel/t_ptrace_wait6.c Mon Nov 7 21:09:03 2016
@@ -0,0 +1,30 @@
+/* $NetBSD: t_ptrace_wait6.c,v 1.1 2016/11/07 21:09:03 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define TWAIT_WAIT6
+#include "t_ptrace_wait.c"
Index: src/tests/kernel/t_ptrace_waitid.c
diff -u /dev/null src/tests/kernel/t_ptrace_waitid.c:1.1
--- /dev/null Mon Nov 7 21:09:03 2016
+++ src/tests/kernel/t_ptrace_waitid.c Mon Nov 7 21:09:03 2016
@@ -0,0 +1,30 @@
+/* $NetBSD: t_ptrace_waitid.c,v 1.1 2016/11/07 21:09:03 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define TWAIT_WAITID
+#include "t_ptrace_wait.c"
Index: src/tests/kernel/t_ptrace_waitpid.c
diff -u /dev/null src/tests/kernel/t_ptrace_waitpid.c:1.1
--- /dev/null Mon Nov 7 21:09:03 2016
+++ src/tests/kernel/t_ptrace_waitpid.c Mon Nov 7 21:09:03 2016
@@ -0,0 +1,30 @@
+/* $NetBSD: t_ptrace_waitpid.c,v 1.1 2016/11/07 21:09:03 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define TWAIT_WAITPID
+#include "t_ptrace_wait.c"