Module Name: src Committed By: mgorny Date: Thu Feb 13 15:27:05 UTC 2020
Modified Files: src/tests/lib/libc/sys: t_ptrace_wait.c t_ptrace_x86_wait.h Log Message: Extend concurrent events test to breakpoints Add testing for concurrent breakpoint hits. The code is currently x86-specific but since it reuses most of the generic concurrent event test code, it's put in t_ptrace_wait.c with arch-specific hooks. To generate a diff of this commit: cvs rdiff -u -r1.159 -r1.160 src/tests/lib/libc/sys/t_ptrace_wait.c cvs rdiff -u -r1.19 -r1.20 src/tests/lib/libc/sys/t_ptrace_x86_wait.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/tests/lib/libc/sys/t_ptrace_wait.c diff -u src/tests/lib/libc/sys/t_ptrace_wait.c:1.159 src/tests/lib/libc/sys/t_ptrace_wait.c:1.160 --- src/tests/lib/libc/sys/t_ptrace_wait.c:1.159 Thu Feb 13 15:26:45 2020 +++ src/tests/lib/libc/sys/t_ptrace_wait.c Thu Feb 13 15:27:05 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: t_ptrace_wait.c,v 1.159 2020/02/13 15:26:45 mgorny Exp $ */ +/* $NetBSD: t_ptrace_wait.c,v 1.160 2020/02/13 15:27:05 mgorny Exp $ */ /*- * Copyright (c) 2016, 2017, 2018, 2019 The NetBSD Foundation, Inc. @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: t_ptrace_wait.c,v 1.159 2020/02/13 15:26:45 mgorny Exp $"); +__RCSID("$NetBSD: t_ptrace_wait.c,v 1.160 2020/02/13 15:27:05 mgorny Exp $"); #define __LEGACY_PT_LWPINFO @@ -8620,6 +8620,7 @@ ATF_TC_BODY(core_dump_procinfo, tc) #if defined(TWAIT_HAVE_STATUS) +#define THREAD_CONCURRENT_BREAKPOINT_NUM 50 #define THREAD_CONCURRENT_SIGNALS_NUM 50 /* List of signals to use for the test */ @@ -8647,6 +8648,16 @@ enum thread_concurrent_signal_handling { static pthread_barrier_t thread_concurrent_barrier; static pthread_key_t thread_concurrent_key; +static void * +thread_concurrent_breakpoint_thread(void *arg) +{ + static volatile int watchme = 1; + pthread_barrier_wait(&thread_concurrent_barrier); + DPRINTF("Before entering breakpoint func from LWP %d\n", _lwp_self()); + check_happy(watchme); + return NULL; +} + static void thread_concurrent_sig_handler(int sig) { @@ -8676,9 +8687,21 @@ thread_concurrent_signals_thread(void *a return NULL; } +#if defined(__i386__) || defined(__x86_64__) +enum thread_concurrent_sigtrap_event { + TCSE_UNKNOWN, + TCSE_BREAKPOINT +}; + +static void +thread_concurrent_lwp_setup(pid_t child, lwpid_t lwpid); +static enum thread_concurrent_sigtrap_event +thread_concurrent_handle_sigtrap(pid_t child, ptrace_siginfo_t *info); +#endif + static void thread_concurrent_test(enum thread_concurrent_signal_handling signal_handle, - int signal_threads) + int breakpoint_threads, int signal_threads) { const int exitval = 5; const int sigval = SIGSTOP; @@ -8686,6 +8709,8 @@ thread_concurrent_test(enum thread_concu int status; struct lwp_event_count signal_counts[THREAD_CONCURRENT_SIGNALS_NUM] = {{0, 0}}; + struct lwp_event_count bp_counts[THREAD_CONCURRENT_BREAKPOINT_NUM] + = {{0, 0}}; ptrace_event_t event; int i; @@ -8695,11 +8720,13 @@ thread_concurrent_test(enum thread_concu atf_tc_skip("PR kern/54960"); /* Protect against out-of-bounds array access. */ + ATF_REQUIRE(breakpoint_threads <= THREAD_CONCURRENT_BREAKPOINT_NUM); ATF_REQUIRE(signal_threads <= THREAD_CONCURRENT_SIGNALS_NUM); DPRINTF("Before forking process PID=%d\n", getpid()); SYSCALL_REQUIRE((child = fork()) != -1); if (child == 0) { + pthread_t bp_threads[THREAD_CONCURRENT_BREAKPOINT_NUM]; pthread_t sig_threads[THREAD_CONCURRENT_SIGNALS_NUM]; DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid()); @@ -8730,7 +8757,7 @@ thread_concurrent_test(enum thread_concu DPRINTF("Before starting threads from the child\n"); FORKEE_ASSERT(pthread_barrier_init( &thread_concurrent_barrier, NULL, - signal_threads) == 0); + breakpoint_threads + signal_threads) == 0); FORKEE_ASSERT(pthread_key_create(&thread_concurrent_key, NULL) == 0); @@ -8739,8 +8766,15 @@ thread_concurrent_test(enum thread_concu thread_concurrent_signals_thread, &signal_handle) == 0); } + for (i = 0; i < breakpoint_threads; i++) { + FORKEE_ASSERT(pthread_create(&bp_threads[i], NULL, + thread_concurrent_breakpoint_thread, NULL) == 0); + } DPRINTF("Before joining threads from the child\n"); + for (i = 0; i < breakpoint_threads; i++) { + FORKEE_ASSERT(pthread_join(bp_threads[i], NULL) == 0); + } for (i = 0; i < signal_threads; i++) { FORKEE_ASSERT(pthread_join(sig_threads[i], NULL) == 0); } @@ -8804,10 +8838,25 @@ thread_concurrent_test(enum thread_concu expected_sig, WSTOPSIG(status)); *FIND_EVENT_COUNT(signal_counts, info.psi_lwpid) += 1; + } else if (info.psi_siginfo.si_code == TRAP_LWP) { +#if defined(__i386__) || defined(__x86_64__) + thread_concurrent_lwp_setup(child, info.psi_lwpid); +#endif } else { - ATF_CHECK_EQ_MSG(info.psi_siginfo.si_code, TRAP_LWP, - "lwp=%d, expected TRAP_LWP (%d), got %d", - info.psi_lwpid, TRAP_LWP, info.psi_siginfo.si_code); +#if defined(__i386__) || defined(__x86_64__) + switch (thread_concurrent_handle_sigtrap(child, &info)) { + case TCSE_UNKNOWN: + /* already reported inside the function */ + break; + case TCSE_BREAKPOINT: + *FIND_EVENT_COUNT(bp_counts, + info.psi_lwpid) += 1; + break; + } +#else + ATF_CHECK_MSG(0, "Unexpected SIGTRAP, si_code=%d\n", + info.psi_siginfo.si_code); +#endif } DPRINTF("Before resuming the child process\n"); @@ -8825,10 +8874,19 @@ thread_concurrent_test(enum thread_concu "extraneous signal_counts[%d].lec_count=%d; lec_lwp=%d", i, signal_counts[i].lec_count, signal_counts[i].lec_lwp); + for (i = 0; i < breakpoint_threads; i++) + ATF_CHECK_EQ_MSG(bp_counts[i].lec_count, 1, + "bp_counts[%d].lec_count=%d; lec_lwp=%d", + i, bp_counts[i].lec_count, bp_counts[i].lec_lwp); + for (i = breakpoint_threads; i < THREAD_CONCURRENT_BREAKPOINT_NUM; i++) + ATF_CHECK_EQ_MSG(bp_counts[i].lec_count, 0, + "extraneous bp_counts[%d].lec_count=%d; lec_lwp=%d", + i, bp_counts[i].lec_count, bp_counts[i].lec_lwp); + validate_status_exited(status, exitval); } -#define THREAD_CONCURRENT_TEST(test, sig_hdl, sigs, descr) \ +#define THREAD_CONCURRENT_TEST(test, sig_hdl, bps, sigs, descr) \ ATF_TC(test); \ ATF_TC_HEAD(test, tc) \ { \ @@ -8837,22 +8895,26 @@ ATF_TC_HEAD(test, tc) \ \ ATF_TC_BODY(test, tc) \ { \ - thread_concurrent_test(sig_hdl, sigs); \ + thread_concurrent_test(sig_hdl, bps, sigs); \ } THREAD_CONCURRENT_TEST(thread_concurrent_signals, TCSH_DISCARD, - THREAD_CONCURRENT_SIGNALS_NUM, + 0, THREAD_CONCURRENT_SIGNALS_NUM, "Verify that concurrent signals issued to a single thread are reported " "correctly"); THREAD_CONCURRENT_TEST(thread_concurrent_signals_sig_ign, TCSH_SIG_IGN, - THREAD_CONCURRENT_SIGNALS_NUM, + 0, THREAD_CONCURRENT_SIGNALS_NUM, "Verify that concurrent signals issued to a single thread are reported " "correctly and passed back to SIG_IGN handler"); THREAD_CONCURRENT_TEST(thread_concurrent_signals_handler, TCSH_HANDLER, - THREAD_CONCURRENT_SIGNALS_NUM, + 0, THREAD_CONCURRENT_SIGNALS_NUM, "Verify that concurrent signals issued to a single thread are reported " "correctly and passed back to a handler function"); +THREAD_CONCURRENT_TEST(thread_concurrent_breakpoints, TCSH_DISCARD, + THREAD_CONCURRENT_BREAKPOINT_NUM, 0, + "Verify that concurrent breakpoints are reported correctly"); + #endif /*defined(TWAIT_HAVE_STATUS)*/ /// ---------------------------------------------------------------------------- @@ -9445,6 +9507,9 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, thread_concurrent_signals); ATF_TP_ADD_TC(tp, thread_concurrent_signals_sig_ign); ATF_TP_ADD_TC(tp, thread_concurrent_signals_handler); +#if defined(__i386__) || defined(__x86_64__) + ATF_TP_ADD_TC(tp, thread_concurrent_breakpoints); +#endif #endif ATF_TP_ADD_TCS_PTRACE_WAIT_AMD64(); Index: src/tests/lib/libc/sys/t_ptrace_x86_wait.h diff -u src/tests/lib/libc/sys/t_ptrace_x86_wait.h:1.19 src/tests/lib/libc/sys/t_ptrace_x86_wait.h:1.20 --- src/tests/lib/libc/sys/t_ptrace_x86_wait.h:1.19 Thu Feb 13 02:53:46 2020 +++ src/tests/lib/libc/sys/t_ptrace_x86_wait.h Thu Feb 13 15:27:05 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: t_ptrace_x86_wait.h,v 1.19 2020/02/13 02:53:46 christos Exp $ */ +/* $NetBSD: t_ptrace_x86_wait.h,v 1.20 2020/02/13 15:27:05 mgorny Exp $ */ /*- * Copyright (c) 2016, 2017, 2018, 2019 The NetBSD Foundation, Inc. @@ -3551,6 +3551,82 @@ X86_REGISTER_TEST(x86_xstate_ymm_core, T /// ---------------------------------------------------------------------------- +#if defined(TWAIT_HAVE_STATUS) + +static void +thread_concurrent_lwp_setup(pid_t child, lwpid_t lwpid) +{ + struct dbreg r; + union u dr7; + + /* We need to set debug registers for every child */ + DPRINTF("Call GETDBREGS for LWP %d\n", lwpid); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r, lwpid) != -1); + + dr7.raw = 0; + /* should be set to 1 according to Intel manual, 17.2 */ + dr7.bits.reserved_10 = 1; + dr7.bits.local_exact_breakpt = 1; + dr7.bits.global_exact_breakpt = 1; + /* use DR0 for breakpoints */ + dr7.bits.global_dr0_breakpoint = 1; + dr7.bits.condition_dr0 = 0; /* exec */ + dr7.bits.len_dr0 = 0; + r.dr[7] = dr7.raw; + r.dr[0] = (long)(intptr_t)check_happy; + DPRINTF("dr0=%" PRIxREGISTER "\n", r.dr[0]); + DPRINTF("dr7=%" PRIxREGISTER "\n", r.dr[7]); + + DPRINTF("Call SETDBREGS for LWP %d\n", lwpid); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r, lwpid) != -1); +} + +static enum thread_concurrent_sigtrap_event +thread_concurrent_handle_sigtrap(pid_t child, ptrace_siginfo_t *info) +{ + enum thread_concurrent_sigtrap_event ret = TCSE_UNKNOWN; + struct dbreg r; + union u dr7; + + ATF_CHECK_EQ_MSG(info->psi_siginfo.si_code, TRAP_DBREG, + "lwp=%d, expected TRAP_DBREG (%d), got %d", info->psi_lwpid, + TRAP_DBREG, info->psi_siginfo.si_code); + + DPRINTF("Call GETDBREGS for LWP %d\n", info->psi_lwpid); + SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &r, info->psi_lwpid) != -1); + DPRINTF("dr6=%" PRIxREGISTER ", dr7=%" PRIxREGISTER "\n", + r.dr[6], r.dr[7]); + + ATF_CHECK_MSG(r.dr[6] & 1, "lwp=%d, got DR6=%" PRIxREGISTER, + info->psi_lwpid, r.dr[6]); + + if (r.dr[6] & 1) { + r.dr[6] &= ~1; + + /* We need to disable the breakpoint to move + * past it. + * + * TODO: single-step and reenable it? + */ + dr7.raw = r.dr[7]; + dr7.bits.global_dr0_breakpoint = 0; + r.dr[7] = dr7.raw; + + ret = TCSE_BREAKPOINT; + } + + DPRINTF("Call SETDBREGS for LWP %d\n", info->psi_lwpid); + DPRINTF("dr6=%" PRIxREGISTER ", dr7=%" PRIxREGISTER "\n", + r.dr[6], r.dr[7]); + SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &r, info->psi_lwpid) != -1); + + return ret; +} + +#endif /*defined(TWAIT_HAVE_STATUS)*/ + +/// ---------------------------------------------------------------------------- + #define ATF_TP_ADD_TCS_PTRACE_WAIT_X86() \ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_print); \ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr0); \