Module Name:    src
Committed By:   kamil
Date:           Sat Dec  3 01:41:15 UTC 2016

Modified Files:
        src/tests/kernel/arch/amd64: t_ptrace_wait.c

Log Message:
Define new tests for CPU Debug Registers in t_ptrace_wait{,3,4,6,id,pid}

Rename dbregs1 to dbregs_print
Rename dbregs[2345] to dbregs_preserve_dr[0123]

Add new tests dbregs_preserve_dr[0123]_yield.

dbregs_preserve_dr0_yield:
     Verify that setting DR0 is preserved across ptrace(2) calls with
     scheduler yield

dbregs_preserve_dr1_yield:
     Verify that setting DR1 is preserved across ptrace(2) calls with
     scheduler yield

dbregs_preserve_dr2_yield:
     Verify that setting DR2 is preserved across ptrace(2) calls with
     scheduler yield

dbregs_preserve_dr3_yield:
     Verify that setting DR3 is preserved across ptrace(2) calls with
     scheduler yield

Add new tests dbregs_preserve_dr[0123]_continued.

dbregs_preserve_dr0_continued:
    Verify that setting DR0 is preserved across ptrace(2) calls and with
    continued child

dbregs_preserve_dr1_continued:
    Verify that setting DR1 is preserved across ptrace(2) calls and with
    continued child

dbregs_preserve_dr2_continued:
    Verify that setting DR2 is preserved across ptrace(2) calls and with
    continued child

dbregs_preserve_dr3_continued:
    Verify that setting DR3 is preserved across ptrace(2) calls and with
    continued child

Use more meaningful names for these tests as they are MD specific and
testing precise functionality. Also there will be a growing number of
tests in this category and prefixing everything with plain dbregs and
trailing with a number cannot be verbose.

Sponsored by <The NetBSD Foundation>


To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 src/tests/kernel/arch/amd64/t_ptrace_wait.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/tests/kernel/arch/amd64/t_ptrace_wait.c
diff -u src/tests/kernel/arch/amd64/t_ptrace_wait.c:1.2 src/tests/kernel/arch/amd64/t_ptrace_wait.c:1.3
--- src/tests/kernel/arch/amd64/t_ptrace_wait.c:1.2	Fri Dec  2 06:49:00 2016
+++ src/tests/kernel/arch/amd64/t_ptrace_wait.c	Sat Dec  3 01:41:15 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: t_ptrace_wait.c,v 1.2 2016/12/02 06:49:00 kamil Exp $	*/
+/*	$NetBSD: t_ptrace_wait.c,v 1.3 2016/12/03 01:41:15 kamil Exp $	*/
 
 /*-
  * Copyright (c) 2016 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: t_ptrace_wait.c,v 1.2 2016/12/02 06:49:00 kamil Exp $");
+__RCSID("$NetBSD: t_ptrace_wait.c,v 1.3 2016/12/03 01:41:15 kamil Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -39,6 +39,7 @@ __RCSID("$NetBSD: t_ptrace_wait.c,v 1.2 
 #include <machine/reg.h>
 #include <err.h>
 #include <errno.h>
+#include <sched.h>
 #include <signal.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -53,14 +54,14 @@ __RCSID("$NetBSD: t_ptrace_wait.c,v 1.2 
 #include "../../t_ptrace_wait.h"
 
 #if defined(HAVE_DBREGS)
-ATF_TC(dbregs1);
-ATF_TC_HEAD(dbregs1, tc)
+ATF_TC(dbregs_print);
+ATF_TC_HEAD(dbregs_print, tc)
 {
 	atf_tc_set_md_var(tc, "descr",
 	    "Verify plain PT_GETDBREGS with printing Debug Registers");
 }
 
-ATF_TC_BODY(dbregs1, tc)
+ATF_TC_BODY(dbregs_print, tc)
 {
 	const int exitval = 5;
 	const int sigval = SIGSTOP;
@@ -112,14 +113,14 @@ ATF_TC_BODY(dbregs1, tc)
 #endif
 
 #if defined(HAVE_DBREGS)
-ATF_TC(dbregs2);
-ATF_TC_HEAD(dbregs2, tc)
+ATF_TC(dbregs_preserve_dr0);
+ATF_TC_HEAD(dbregs_preserve_dr0, tc)
 {
 	atf_tc_set_md_var(tc, "descr",
 	    "Verify that setting DR0 is preserved across ptrace(2) calls");
 }
 
-ATF_TC_BODY(dbregs2, tc)
+ATF_TC_BODY(dbregs_preserve_dr0, tc)
 {
 	const int exitval = 5;
 	const int sigval = SIGSTOP;
@@ -196,14 +197,14 @@ ATF_TC_BODY(dbregs2, tc)
 #endif
 
 #if defined(HAVE_DBREGS)
-ATF_TC(dbregs3);
-ATF_TC_HEAD(dbregs3, tc)
+ATF_TC(dbregs_preserve_dr1);
+ATF_TC_HEAD(dbregs_preserve_dr1, tc)
 {
 	atf_tc_set_md_var(tc, "descr",
 	    "Verify that setting DR1 is preserved across ptrace(2) calls");
 }
 
-ATF_TC_BODY(dbregs3, tc)
+ATF_TC_BODY(dbregs_preserve_dr1, tc)
 {
 	const int exitval = 5;
 	const int sigval = SIGSTOP;
@@ -280,14 +281,14 @@ ATF_TC_BODY(dbregs3, tc)
 #endif
 
 #if defined(HAVE_DBREGS)
-ATF_TC(dbregs4);
-ATF_TC_HEAD(dbregs4, tc)
+ATF_TC(dbregs_preserve_dr2);
+ATF_TC_HEAD(dbregs_preserve_dr2, tc)
 {
 	atf_tc_set_md_var(tc, "descr",
 	    "Verify that setting DR2 is preserved across ptrace(2) calls");
 }
 
-ATF_TC_BODY(dbregs4, tc)
+ATF_TC_BODY(dbregs_preserve_dr2, tc)
 {
 	const int exitval = 5;
 	const int sigval = SIGSTOP;
@@ -364,14 +365,14 @@ ATF_TC_BODY(dbregs4, tc)
 #endif
 
 #if defined(HAVE_DBREGS)
-ATF_TC(dbregs5);
-ATF_TC_HEAD(dbregs5, tc)
+ATF_TC(dbregs_preserve_dr3);
+ATF_TC_HEAD(dbregs_preserve_dr3, tc)
 {
 	atf_tc_set_md_var(tc, "descr",
 	    "Verify that setting DR3 is preserved across ptrace(2) calls");
 }
 
-ATF_TC_BODY(dbregs5, tc)
+ATF_TC_BODY(dbregs_preserve_dr3, tc)
 {
 	const int exitval = 5;
 	const int sigval = SIGSTOP;
@@ -447,16 +448,771 @@ ATF_TC_BODY(dbregs5, tc)
 }
 #endif
 
+#if defined(HAVE_DBREGS)
+ATF_TC(dbregs_preserve_dr0_yield);
+ATF_TC_HEAD(dbregs_preserve_dr0_yield, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify that setting DR0 is preserved across ptrace(2) calls with "
+	    "scheduler yield");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr0_yield, tc)
+{
+	const int exitval = 5;
+	const int sigval = SIGSTOP;
+	pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+	struct dbreg r1;
+	struct dbreg r2;
+	/* Number of available CPU Debug Registers on AMD64 */
+	const size_t len = 16;
+	size_t i;
+	int watchme;
+
+	printf("Assert that known number of Debug Registers (%zu) is valid\n",
+	    len);
+	ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len);
+	ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len);
+
+	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 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("Call GETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+	printf("State of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	r1.dbregs[0] = (long)(intptr_t)&watchme;
+	printf("Set DR0 (r1.dbregs[0]) to new value %#lx\n", r1.dbregs[0]);
+
+	printf("New state of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	printf("Call SETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+	printf("Yields a processor voluntarily and gives other threads a "
+	    "chance to run without waiting for an involuntary preemptive "
+	    "switch\n");
+	sched_yield();
+
+	printf("Call GETDBREGS for the child process (r2)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, 0) != -1);
+
+	printf("Assert that (r1) and (r2) are the same\n");
+	ATF_REQUIRE(memcmp(&r1, &r2, len) == 0);
+
+	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));
+}
+#endif
+
+#if defined(HAVE_DBREGS)
+ATF_TC(dbregs_preserve_dr1_yield);
+ATF_TC_HEAD(dbregs_preserve_dr1_yield, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify that setting DR1 is preserved across ptrace(2) calls with "
+	    "scheduler yield");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr1_yield, tc)
+{
+	const int exitval = 5;
+	const int sigval = SIGSTOP;
+	pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+	struct dbreg r1;
+	struct dbreg r2;
+	/* Number of available CPU Debug Registers on AMD64 */
+	const size_t len = 16;
+	size_t i;
+	int watchme;
+
+	printf("Assert that known number of Debug Registers (%zu) is valid\n",
+	    len);
+	ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len);
+	ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len);
+
+	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 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("Call GETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+	printf("State of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	r1.dbregs[1] = (long)(intptr_t)&watchme;
+	printf("Set DR1 (r1.dbregs[1]) to new value %#lx\n", r1.dbregs[1]);
+
+	printf("New state of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	printf("Call SETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+	printf("Yields a processor voluntarily and gives other threads a "
+	    "chance to run without waiting for an involuntary preemptive "
+	    "switch\n");
+	sched_yield();
+
+	printf("Call GETDBREGS for the child process (r2)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, 0) != -1);
+
+	printf("Assert that (r1) and (r2) are the same\n");
+	ATF_REQUIRE(memcmp(&r1, &r2, len) == 0);
+
+	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));
+}
+#endif
+
+#if defined(HAVE_DBREGS)
+ATF_TC(dbregs_preserve_dr2_yield);
+ATF_TC_HEAD(dbregs_preserve_dr2_yield, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify that setting DR2 is preserved across ptrace(2) calls with "
+	    "scheduler yield");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr2_yield, tc)
+{
+	const int exitval = 5;
+	const int sigval = SIGSTOP;
+	pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+	struct dbreg r1;
+	struct dbreg r2;
+	/* Number of available CPU Debug Registers on AMD64 */
+	const size_t len = 16;
+	size_t i;
+	int watchme;
+
+	printf("Assert that known number of Debug Registers (%zu) is valid\n",
+	    len);
+	ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len);
+	ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len);
+
+	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 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("Call GETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+	printf("State of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	r1.dbregs[2] = (long)(intptr_t)&watchme;
+	printf("Set DR2 (r1.dbregs[2]) to new value %#lx\n", r1.dbregs[2]);
+
+	printf("New state of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	printf("Call SETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+	printf("Yields a processor voluntarily and gives other threads a "
+	    "chance to run without waiting for an involuntary preemptive "
+	    "switch\n");
+	sched_yield();
+
+	printf("Call GETDBREGS for the child process (r2)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, 0) != -1);
+
+	printf("Assert that (r1) and (r2) are the same\n");
+	ATF_REQUIRE(memcmp(&r1, &r2, len) == 0);
+
+	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));
+}
+#endif
+
+#if defined(HAVE_DBREGS)
+ATF_TC(dbregs_preserve_dr3_yield);
+ATF_TC_HEAD(dbregs_preserve_dr3_yield, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify that setting DR3 is preserved across ptrace(2) calls with "
+	    "scheduler yield");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr3_yield, tc)
+{
+	const int exitval = 5;
+	const int sigval = SIGSTOP;
+	pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+	struct dbreg r1;
+	struct dbreg r2;
+	/* Number of available CPU Debug Registers on AMD64 */
+	const size_t len = 16;
+	size_t i;
+	int watchme;
+
+	printf("Assert that known number of Debug Registers (%zu) is valid\n",
+	    len);
+	ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len);
+	ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len);
+
+	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 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("Call GETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+	printf("State of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	r1.dbregs[3] = (long)(intptr_t)&watchme;
+	printf("Set DR3 (r1.dbregs[3]) to new value %#lx\n", r1.dbregs[3]);
+
+	printf("New state of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	printf("Call SETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+	printf("Yields a processor voluntarily and gives other threads a "
+	    "chance to run without waiting for an involuntary preemptive "
+	    "switch\n");
+	sched_yield();
+
+	printf("Call GETDBREGS for the child process (r2)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, 0) != -1);
+
+	printf("Assert that (r1) and (r2) are the same\n");
+	ATF_REQUIRE(memcmp(&r1, &r2, len) == 0);
+
+	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));
+}
+#endif
+
+#if defined(HAVE_DBREGS)
+ATF_TC(dbregs_preserve_dr0_continued);
+ATF_TC_HEAD(dbregs_preserve_dr0_continued, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify that setting DR0 is preserved across ptrace(2) calls and "
+	    "with continued child");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr0_continued, tc)
+{
+	const int exitval = 5;
+	const int sigval = SIGSTOP;
+	pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+	struct dbreg r1;
+	struct dbreg r2;
+	/* Number of available CPU Debug Registers on AMD64 */
+	const size_t len = 16;
+	size_t i;
+	int watchme;
+
+	printf("Assert that known number of Debug Registers (%zu) is valid\n",
+	    len);
+	ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len);
+	ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len);
+
+	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(sigval));
+		FORKEE_ASSERT(raise(sigval) == 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("Call GETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+	printf("State of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	r1.dbregs[0] = (long)(intptr_t)&watchme;
+	printf("Set DR0 (r1.dbregs[0]) to new value %#lx\n", r1.dbregs[0]);
+
+	printf("New state of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	printf("Call SETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+	printf("Call CONTINUE for the child process\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, sigval);
+
+	printf("Call GETDBREGS for the child process (r2)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, 0) != -1);
+
+	printf("Assert that (r1) and (r2) are the same\n");
+	ATF_REQUIRE(memcmp(&r1, &r2, len) == 0);
+
+	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));
+}
+#endif
+
+#if defined(HAVE_DBREGS)
+ATF_TC(dbregs_preserve_dr1_continued);
+ATF_TC_HEAD(dbregs_preserve_dr1_continued, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify that setting DR1 is preserved across ptrace(2) calls and "
+	    "with continued child");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr1_continued, tc)
+{
+	const int exitval = 5;
+	const int sigval = SIGSTOP;
+	pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+	struct dbreg r1;
+	struct dbreg r2;
+	/* Number of available CPU Debug Registers on AMD64 */
+	const size_t len = 16;
+	size_t i;
+	int watchme;
+
+	printf("Assert that known number of Debug Registers (%zu) is valid\n",
+	    len);
+	ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len);
+	ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len);
+
+	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(sigval));
+		FORKEE_ASSERT(raise(sigval) == 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("Call GETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+	printf("State of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	r1.dbregs[1] = (long)(intptr_t)&watchme;
+	printf("Set DR1 (r1.dbregs[1]) to new value %#lx\n", r1.dbregs[1]);
+
+	printf("New state of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	printf("Call SETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+	printf("Call CONTINUE for the child process\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, sigval);
+
+	printf("Call GETDBREGS for the child process (r2)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, 0) != -1);
+
+	printf("Assert that (r1) and (r2) are the same\n");
+	ATF_REQUIRE(memcmp(&r1, &r2, len) == 0);
+
+	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));
+}
+#endif
+
+#if defined(HAVE_DBREGS)
+ATF_TC(dbregs_preserve_dr2_continued);
+ATF_TC_HEAD(dbregs_preserve_dr2_continued, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify that setting DR2 is preserved across ptrace(2) calls and "
+	    "with continued child");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr2_continued, tc)
+{
+	const int exitval = 5;
+	const int sigval = SIGSTOP;
+	pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+	struct dbreg r1;
+	struct dbreg r2;
+	/* Number of available CPU Debug Registers on AMD64 */
+	const size_t len = 16;
+	size_t i;
+	int watchme;
+
+	printf("Assert that known number of Debug Registers (%zu) is valid\n",
+	    len);
+	ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len);
+	ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len);
+
+	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(sigval));
+		FORKEE_ASSERT(raise(sigval) == 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("Call GETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+	printf("State of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	r1.dbregs[2] = (long)(intptr_t)&watchme;
+	printf("Set DR2 (r1.dbregs[2]) to new value %#lx\n", r1.dbregs[2]);
+
+	printf("New state of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	printf("Call SETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+	printf("Call CONTINUE for the child process\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, sigval);
+
+	printf("Call GETDBREGS for the child process (r2)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, 0) != -1);
+
+	printf("Assert that (r1) and (r2) are the same\n");
+	ATF_REQUIRE(memcmp(&r1, &r2, len) == 0);
+
+	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));
+}
+#endif
+
+#if defined(HAVE_DBREGS)
+ATF_TC(dbregs_preserve_dr3_continued);
+ATF_TC_HEAD(dbregs_preserve_dr3_continued, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify that setting DR3 is preserved across ptrace(2) calls and "
+	    "with continued child");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr3_continued, tc)
+{
+	const int exitval = 5;
+	const int sigval = SIGSTOP;
+	pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+	struct dbreg r1;
+	struct dbreg r2;
+	/* Number of available CPU Debug Registers on AMD64 */
+	const size_t len = 16;
+	size_t i;
+	int watchme;
+
+	printf("Assert that known number of Debug Registers (%zu) is valid\n",
+	    len);
+	ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len);
+	ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len);
+
+	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(sigval));
+		FORKEE_ASSERT(raise(sigval) == 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("Call GETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+	printf("State of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	r1.dbregs[3] = (long)(intptr_t)&watchme;
+	printf("Set DR3 (r1.dbregs[3]) to new value %#lx\n", r1.dbregs[3]);
+
+	printf("New state of the debug registers (r1):\n");
+	for (i = 0; i < __arraycount(r1.dbregs); i++)
+		printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]);
+
+	printf("Call SETDBREGS for the child process (r1)\n");
+	ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+	printf("Call CONTINUE for the child process\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, sigval);
+
+	printf("Call GETDBREGS for the child process (r2)\n");
+	ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, 0) != -1);
+
+	printf("Assert that (r1) and (r2) are the same\n");
+	ATF_REQUIRE(memcmp(&r1, &r2, len) == 0);
+
+	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));
+}
+#endif
+
 ATF_TP_ADD_TCS(tp)
 {
 	setvbuf(stdout, NULL, _IONBF, 0);
 	setvbuf(stderr, NULL, _IONBF, 0);
 
-	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs1);
-	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs2);
-	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs3);
-	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs4);
-	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs5);
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_print);
+
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr0);
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr1);
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr2);
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr3);
+
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr0_yield);
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr1_yield);
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr2_yield);
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr3_yield);
+
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr0_continued);
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr1_continued);
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr2_continued);
+	ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr3_continued);
 
 	return atf_no_error();
 }

Reply via email to