1070 lines
36 KiB
C
1070 lines
36 KiB
C
/* $NetBSD: t_ptrace_threads_wait.h,v 1.1 2020/05/05 00:50:39 kamil Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2016, 2017, 2018, 2019, 2020 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 TRACE_THREADS_NUM 100
|
|
|
|
static volatile int done;
|
|
pthread_mutex_t trace_threads_mtx = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static void *
|
|
trace_threads_cb(void *arg __unused)
|
|
{
|
|
|
|
pthread_mutex_lock(&trace_threads_mtx);
|
|
done++;
|
|
pthread_mutex_unlock(&trace_threads_mtx);
|
|
|
|
while (done < TRACE_THREADS_NUM)
|
|
sched_yield();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
trace_threads(bool trace_create, bool trace_exit, bool masked)
|
|
{
|
|
const int sigval = SIGSTOP;
|
|
pid_t child, wpid;
|
|
#if defined(TWAIT_HAVE_STATUS)
|
|
int status;
|
|
#endif
|
|
ptrace_state_t state;
|
|
const int slen = sizeof(state);
|
|
ptrace_event_t event;
|
|
const int elen = sizeof(event);
|
|
struct ptrace_siginfo info;
|
|
|
|
sigset_t intmask;
|
|
|
|
pthread_t t[TRACE_THREADS_NUM];
|
|
int rv;
|
|
size_t n;
|
|
lwpid_t lid;
|
|
|
|
/* Track created and exited threads */
|
|
struct lwp_event_count traced_lwps[__arraycount(t)] = {{0, 0}};
|
|
|
|
DPRINTF("Before forking process PID=%d\n", getpid());
|
|
SYSCALL_REQUIRE((child = fork()) != -1);
|
|
if (child == 0) {
|
|
DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
|
|
FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
|
|
|
|
if (masked) {
|
|
sigemptyset(&intmask);
|
|
sigaddset(&intmask, SIGTRAP);
|
|
sigprocmask(SIG_BLOCK, &intmask, NULL);
|
|
}
|
|
|
|
DPRINTF("Before raising %s from child\n", strsignal(sigval));
|
|
FORKEE_ASSERT(raise(sigval) == 0);
|
|
|
|
for (n = 0; n < __arraycount(t); n++) {
|
|
rv = pthread_create(&t[n], NULL, trace_threads_cb,
|
|
NULL);
|
|
FORKEE_ASSERT(rv == 0);
|
|
}
|
|
|
|
for (n = 0; n < __arraycount(t); n++) {
|
|
rv = pthread_join(t[n], NULL);
|
|
FORKEE_ASSERT(rv == 0);
|
|
}
|
|
|
|
/*
|
|
* There is race between _exit() and pthread_join() detaching
|
|
* a thread. For simplicity kill the process after detecting
|
|
* LWP events.
|
|
*/
|
|
while (true)
|
|
continue;
|
|
|
|
FORKEE_ASSERT(0 && "Not reached");
|
|
}
|
|
DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child);
|
|
|
|
DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
|
|
|
|
validate_status_stopped(status, sigval);
|
|
|
|
DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
|
|
SYSCALL_REQUIRE(
|
|
ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
|
|
|
|
DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid);
|
|
DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n",
|
|
info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
|
|
info.psi_siginfo.si_errno);
|
|
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval);
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP);
|
|
|
|
DPRINTF("Set LWP event mask for the child %d\n", child);
|
|
memset(&event, 0, sizeof(event));
|
|
if (trace_create)
|
|
event.pe_set_event |= PTRACE_LWP_CREATE;
|
|
if (trace_exit)
|
|
event.pe_set_event |= PTRACE_LWP_EXIT;
|
|
SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
|
|
|
|
DPRINTF("Before resuming the child process where it left off and "
|
|
"without signal to be sent\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
|
|
|
|
for (n = 0; n < (trace_create ? __arraycount(t) : 0); n++) {
|
|
DPRINTF("Before calling %s() for the child - expected stopped "
|
|
"SIGTRAP\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0),
|
|
child);
|
|
|
|
validate_status_stopped(status, SIGTRAP);
|
|
|
|
DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for "
|
|
"child\n");
|
|
SYSCALL_REQUIRE(
|
|
ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
|
|
|
|
DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid);
|
|
DPRINTF("Signal properties: si_signo=%#x si_code=%#x "
|
|
"si_errno=%#x\n",
|
|
info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
|
|
info.psi_siginfo.si_errno);
|
|
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_LWP);
|
|
|
|
SYSCALL_REQUIRE(
|
|
ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
|
|
|
|
ATF_REQUIRE_EQ_MSG(state.pe_report_event, PTRACE_LWP_CREATE,
|
|
"%d != %d", state.pe_report_event, PTRACE_LWP_CREATE);
|
|
|
|
lid = state.pe_lwp;
|
|
DPRINTF("Reported PTRACE_LWP_CREATE event with lid %d\n", lid);
|
|
|
|
*FIND_EVENT_COUNT(traced_lwps, lid) += 1;
|
|
|
|
DPRINTF("Before resuming the child process where it left off "
|
|
"and without signal to be sent\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
|
|
}
|
|
|
|
for (n = 0; n < (trace_exit ? __arraycount(t) : 0); n++) {
|
|
DPRINTF("Before calling %s() for the child - expected stopped "
|
|
"SIGTRAP\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0),
|
|
child);
|
|
|
|
validate_status_stopped(status, SIGTRAP);
|
|
|
|
DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for "
|
|
"child\n");
|
|
SYSCALL_REQUIRE(
|
|
ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
|
|
|
|
DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid);
|
|
DPRINTF("Signal properties: si_signo=%#x si_code=%#x "
|
|
"si_errno=%#x\n",
|
|
info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
|
|
info.psi_siginfo.si_errno);
|
|
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_LWP);
|
|
|
|
SYSCALL_REQUIRE(
|
|
ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
|
|
|
|
ATF_REQUIRE_EQ_MSG(state.pe_report_event, PTRACE_LWP_EXIT,
|
|
"%d != %d", state.pe_report_event, PTRACE_LWP_EXIT);
|
|
|
|
lid = state.pe_lwp;
|
|
DPRINTF("Reported PTRACE_LWP_EXIT event with lid %d\n", lid);
|
|
|
|
if (trace_create) {
|
|
int *count = FIND_EVENT_COUNT(traced_lwps, lid);
|
|
ATF_REQUIRE_EQ(*count, 1);
|
|
*count = 0;
|
|
}
|
|
|
|
DPRINTF("Before resuming the child process where it left off "
|
|
"and without signal to be sent\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
|
|
}
|
|
|
|
kill(child, SIGKILL);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected exited\n",
|
|
TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
|
|
|
|
validate_status_signaled(status, SIGKILL, 0);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected no process\n",
|
|
TWAIT_FNAME);
|
|
TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
|
|
}
|
|
|
|
#define TRACE_THREADS(test, trace_create, trace_exit, mask) \
|
|
ATF_TC(test); \
|
|
ATF_TC_HEAD(test, tc) \
|
|
{ \
|
|
atf_tc_set_md_var(tc, "descr", \
|
|
"Verify spawning threads with%s tracing LWP create and" \
|
|
"with%s tracing LWP exit", trace_create ? "" : "out", \
|
|
trace_exit ? "" : "out"); \
|
|
} \
|
|
\
|
|
ATF_TC_BODY(test, tc) \
|
|
{ \
|
|
\
|
|
trace_threads(trace_create, trace_exit, mask); \
|
|
}
|
|
|
|
TRACE_THREADS(trace_thread_nolwpevents, false, false, false)
|
|
TRACE_THREADS(trace_thread_lwpexit, false, true, false)
|
|
TRACE_THREADS(trace_thread_lwpcreate, true, false, false)
|
|
TRACE_THREADS(trace_thread_lwpcreate_and_exit, true, true, false)
|
|
|
|
TRACE_THREADS(trace_thread_lwpexit_masked_sigtrap, false, true, true)
|
|
TRACE_THREADS(trace_thread_lwpcreate_masked_sigtrap, true, false, true)
|
|
TRACE_THREADS(trace_thread_lwpcreate_and_exit_masked_sigtrap, true, true, true)
|
|
|
|
/// ----------------------------------------------------------------------------
|
|
|
|
static void *
|
|
thread_and_exec_thread_cb(void *arg __unused)
|
|
{
|
|
|
|
execlp("/bin/echo", "/bin/echo", NULL);
|
|
|
|
abort();
|
|
}
|
|
|
|
static void
|
|
threads_and_exec(void)
|
|
{
|
|
const int sigval = SIGSTOP;
|
|
pid_t child, wpid;
|
|
#if defined(TWAIT_HAVE_STATUS)
|
|
int status;
|
|
#endif
|
|
ptrace_state_t state;
|
|
const int slen = sizeof(state);
|
|
ptrace_event_t event;
|
|
const int elen = sizeof(event);
|
|
struct ptrace_siginfo info;
|
|
|
|
pthread_t t;
|
|
lwpid_t lid;
|
|
|
|
DPRINTF("Before forking process PID=%d\n", getpid());
|
|
SYSCALL_REQUIRE((child = fork()) != -1);
|
|
if (child == 0) {
|
|
DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
|
|
FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
|
|
|
|
DPRINTF("Before raising %s from child\n", strsignal(sigval));
|
|
FORKEE_ASSERT(raise(sigval) == 0);
|
|
|
|
FORKEE_ASSERT(pthread_create(&t, NULL,
|
|
thread_and_exec_thread_cb, NULL) == 0);
|
|
|
|
for (;;)
|
|
continue;
|
|
|
|
FORKEE_ASSERT(0 && "Not reached");
|
|
}
|
|
DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child);
|
|
|
|
DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
|
|
|
|
validate_status_stopped(status, sigval);
|
|
|
|
DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
|
|
SYSCALL_REQUIRE(
|
|
ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
|
|
|
|
DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid);
|
|
DPRINTF("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n",
|
|
info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
|
|
info.psi_siginfo.si_errno);
|
|
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval);
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP);
|
|
|
|
DPRINTF("Set LWP event mask for the child %d\n", child);
|
|
memset(&event, 0, sizeof(event));
|
|
event.pe_set_event |= PTRACE_LWP_CREATE | PTRACE_LWP_EXIT;
|
|
SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
|
|
|
|
DPRINTF("Before resuming the child process where it left off and "
|
|
"without signal to be sent\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected stopped "
|
|
"SIGTRAP\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0),
|
|
child);
|
|
|
|
validate_status_stopped(status, SIGTRAP);
|
|
|
|
DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for "
|
|
"child\n");
|
|
SYSCALL_REQUIRE(
|
|
ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
|
|
|
|
DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid);
|
|
DPRINTF("Signal properties: si_signo=%#x si_code=%#x "
|
|
"si_errno=%#x\n",
|
|
info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
|
|
info.psi_siginfo.si_errno);
|
|
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_LWP);
|
|
|
|
SYSCALL_REQUIRE(
|
|
ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
|
|
|
|
ATF_REQUIRE_EQ_MSG(state.pe_report_event, PTRACE_LWP_CREATE,
|
|
"%d != %d", state.pe_report_event, PTRACE_LWP_CREATE);
|
|
|
|
lid = state.pe_lwp;
|
|
DPRINTF("Reported PTRACE_LWP_CREATE event with lid %d\n", lid);
|
|
|
|
DPRINTF("Before resuming the child process where it left off "
|
|
"and without signal to be sent\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected stopped "
|
|
"SIGTRAP\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0),
|
|
child);
|
|
|
|
validate_status_stopped(status, SIGTRAP);
|
|
|
|
DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for "
|
|
"child\n");
|
|
SYSCALL_REQUIRE(
|
|
ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
|
|
|
|
DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid);
|
|
DPRINTF("Signal properties: si_signo=%#x si_code=%#x "
|
|
"si_errno=%#x\n",
|
|
info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
|
|
info.psi_siginfo.si_errno);
|
|
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_LWP);
|
|
|
|
SYSCALL_REQUIRE(
|
|
ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
|
|
|
|
ATF_REQUIRE_EQ_MSG(state.pe_report_event, PTRACE_LWP_EXIT,
|
|
"%d != %d", state.pe_report_event, PTRACE_LWP_EXIT);
|
|
|
|
lid = state.pe_lwp;
|
|
DPRINTF("Reported PTRACE_LWP_EXIT event with lid %d\n", lid);
|
|
|
|
DPRINTF("Before resuming the child process where it left off "
|
|
"and without signal to be sent\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected stopped "
|
|
"SIGTRAP\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0),
|
|
child);
|
|
|
|
validate_status_stopped(status, SIGTRAP);
|
|
|
|
DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for "
|
|
"child\n");
|
|
SYSCALL_REQUIRE(
|
|
ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
|
|
|
|
DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid);
|
|
DPRINTF("Signal properties: si_signo=%#x si_code=%#x "
|
|
"si_errno=%#x\n",
|
|
info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
|
|
info.psi_siginfo.si_errno);
|
|
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
|
|
ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_EXEC);
|
|
|
|
SYSCALL_REQUIRE(ptrace(PT_KILL, child, NULL, 0) != -1);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected exited\n",
|
|
TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
|
|
|
|
validate_status_signaled(status, SIGKILL, 0);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected no process\n",
|
|
TWAIT_FNAME);
|
|
TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
|
|
}
|
|
|
|
ATF_TC(threads_and_exec);
|
|
ATF_TC_HEAD(threads_and_exec, tc)
|
|
{
|
|
atf_tc_set_md_var(tc, "descr",
|
|
"Verify that multithreaded application on exec() will report "
|
|
"LWP_EXIT events");
|
|
}
|
|
|
|
ATF_TC_BODY(threads_and_exec, tc)
|
|
{
|
|
|
|
threads_and_exec();
|
|
}
|
|
|
|
/// ----------------------------------------------------------------------------
|
|
|
|
ATF_TC(suspend_no_deadlock);
|
|
ATF_TC_HEAD(suspend_no_deadlock, tc)
|
|
{
|
|
atf_tc_set_md_var(tc, "descr",
|
|
"Verify that the while the only thread within a process is "
|
|
"suspended, the whole process cannot be unstopped");
|
|
}
|
|
|
|
ATF_TC_BODY(suspend_no_deadlock, tc)
|
|
{
|
|
const int exitval = 5;
|
|
const int sigval = SIGSTOP;
|
|
pid_t child, wpid;
|
|
#if defined(TWAIT_HAVE_STATUS)
|
|
int status;
|
|
#endif
|
|
struct ptrace_siginfo psi;
|
|
|
|
DPRINTF("Before forking process PID=%d\n", getpid());
|
|
SYSCALL_REQUIRE((child = fork()) != -1);
|
|
if (child == 0) {
|
|
DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
|
|
FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
|
|
|
|
DPRINTF("Before raising %s from child\n", strsignal(sigval));
|
|
FORKEE_ASSERT(raise(sigval) == 0);
|
|
|
|
DPRINTF("Before exiting of the child process\n");
|
|
_exit(exitval);
|
|
}
|
|
DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child);
|
|
|
|
DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
|
|
|
|
validate_status_stopped(status, sigval);
|
|
|
|
DPRINTF("Before reading siginfo and lwpid_t\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &psi, sizeof(psi)) != -1);
|
|
|
|
DPRINTF("Before suspending LWP %d\n", psi.psi_lwpid);
|
|
SYSCALL_REQUIRE(ptrace(PT_SUSPEND, child, NULL, psi.psi_lwpid) != -1);
|
|
|
|
DPRINTF("Before resuming the child process where it left off and "
|
|
"without signal to be sent\n");
|
|
ATF_REQUIRE_ERRNO(EDEADLK,
|
|
ptrace(PT_CONTINUE, child, (void *)1, 0) == -1);
|
|
|
|
DPRINTF("Before resuming LWP %d\n", psi.psi_lwpid);
|
|
SYSCALL_REQUIRE(ptrace(PT_RESUME, child, NULL, psi.psi_lwpid) != -1);
|
|
|
|
DPRINTF("Before resuming the child process where it left off and "
|
|
"without signal to be sent\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected exited\n",
|
|
TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
|
|
|
|
validate_status_exited(status, exitval);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected no process\n",
|
|
TWAIT_FNAME);
|
|
TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
|
|
}
|
|
|
|
/// ----------------------------------------------------------------------------
|
|
|
|
static pthread_barrier_t barrier1_resume;
|
|
static pthread_barrier_t barrier2_resume;
|
|
|
|
static void *
|
|
resume_thread(void *arg)
|
|
{
|
|
|
|
raise(SIGUSR1);
|
|
|
|
pthread_barrier_wait(&barrier1_resume);
|
|
|
|
/* Debugger will suspend the process here */
|
|
|
|
pthread_barrier_wait(&barrier2_resume);
|
|
|
|
raise(SIGUSR2);
|
|
|
|
return infinite_thread(arg);
|
|
}
|
|
|
|
ATF_TC(resume);
|
|
ATF_TC_HEAD(resume, tc)
|
|
{
|
|
atf_tc_set_md_var(tc, "descr",
|
|
"Verify that a thread can be suspended by a debugger and later "
|
|
"resumed by the debugger");
|
|
}
|
|
|
|
ATF_TC_BODY(resume, tc)
|
|
{
|
|
const int sigval = SIGSTOP;
|
|
pid_t child, wpid;
|
|
#if defined(TWAIT_HAVE_STATUS)
|
|
int status;
|
|
#endif
|
|
lwpid_t lid;
|
|
struct ptrace_siginfo psi;
|
|
pthread_t t;
|
|
|
|
DPRINTF("Before forking process PID=%d\n", getpid());
|
|
SYSCALL_REQUIRE((child = fork()) != -1);
|
|
if (child == 0) {
|
|
DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
|
|
FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
|
|
|
|
pthread_barrier_init(&barrier1_resume, NULL, 2);
|
|
pthread_barrier_init(&barrier2_resume, NULL, 2);
|
|
|
|
DPRINTF("Before raising %s from child\n", strsignal(sigval));
|
|
FORKEE_ASSERT(raise(sigval) == 0);
|
|
|
|
DPRINTF("Before creating new thread in child\n");
|
|
FORKEE_ASSERT(pthread_create(&t, NULL, resume_thread, NULL) == 0);
|
|
|
|
pthread_barrier_wait(&barrier1_resume);
|
|
|
|
pthread_barrier_wait(&barrier2_resume);
|
|
|
|
infinite_thread(NULL);
|
|
}
|
|
DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child);
|
|
|
|
DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
|
|
|
|
validate_status_stopped(status, sigval);
|
|
|
|
DPRINTF("Before resuming the child process where it left off and "
|
|
"without signal to be sent\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected stopped "
|
|
"SIGUSR1\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
|
|
|
|
validate_status_stopped(status, SIGUSR1);
|
|
|
|
DPRINTF("Before reading siginfo and lwpid_t\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &psi, sizeof(psi)) != -1);
|
|
|
|
DPRINTF("Before suspending LWP %d\n", psi.psi_lwpid);
|
|
SYSCALL_REQUIRE(ptrace(PT_SUSPEND, child, NULL, psi.psi_lwpid) != -1);
|
|
|
|
lid = psi.psi_lwpid;
|
|
|
|
DPRINTF("Before resuming the child process where it left off and "
|
|
"without signal to be sent\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
|
|
|
|
DPRINTF("Before suspending the parent for 1 second, we expect no signals\n");
|
|
SYSCALL_REQUIRE(sleep(1) == 0);
|
|
|
|
#if defined(TWAIT_HAVE_OPTIONS)
|
|
DPRINTF("Before calling %s() for the child - expected no status\n",
|
|
TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, WNOHANG), 0);
|
|
#endif
|
|
|
|
DPRINTF("Before resuming the child process where it left off and "
|
|
"without signal to be sent\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_STOP, child, NULL, 0) != -1);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected stopped "
|
|
"SIGSTOP\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
|
|
|
|
validate_status_stopped(status, SIGSTOP);
|
|
|
|
DPRINTF("Before resuming LWP %d\n", lid);
|
|
SYSCALL_REQUIRE(ptrace(PT_RESUME, child, NULL, lid) != -1);
|
|
|
|
DPRINTF("Before resuming the child process where it left off and "
|
|
"without signal to be sent\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected stopped "
|
|
"SIGUSR2\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
|
|
|
|
validate_status_stopped(status, SIGUSR2);
|
|
|
|
DPRINTF("Before resuming the child process where it left off and "
|
|
"without signal to be sent\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_KILL, child, (void *)1, 0) != -1);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected exited\n",
|
|
TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
|
|
|
|
validate_status_signaled(status, SIGKILL, 0);
|
|
|
|
DPRINTF("Before calling %s() for the child - expected no process\n",
|
|
TWAIT_FNAME);
|
|
TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
|
|
}
|
|
|
|
/// ----------------------------------------------------------------------------
|
|
|
|
#if defined(TWAIT_HAVE_STATUS)
|
|
|
|
#define THREAD_CONCURRENT_BREAKPOINT_NUM 50
|
|
#define THREAD_CONCURRENT_SIGNALS_NUM 50
|
|
#define THREAD_CONCURRENT_WATCHPOINT_NUM 50
|
|
|
|
/* List of signals to use for the test */
|
|
const int thread_concurrent_signals_list[] = {
|
|
SIGIO,
|
|
SIGXCPU,
|
|
SIGXFSZ,
|
|
SIGVTALRM,
|
|
SIGPROF,
|
|
SIGWINCH,
|
|
SIGINFO,
|
|
SIGUSR1,
|
|
SIGUSR2
|
|
};
|
|
|
|
enum thread_concurrent_signal_handling {
|
|
/* the signal is discarded by debugger */
|
|
TCSH_DISCARD,
|
|
/* the handler is set to SIG_IGN */
|
|
TCSH_SIG_IGN,
|
|
/* an actual handler is used */
|
|
TCSH_HANDLER
|
|
};
|
|
|
|
static pthread_barrier_t thread_concurrent_barrier;
|
|
static pthread_key_t thread_concurrent_key;
|
|
static uint32_t thread_concurrent_watchpoint_var = 0;
|
|
|
|
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)
|
|
{
|
|
void *tls_val = pthread_getspecific(thread_concurrent_key);
|
|
DPRINTF("Before increment, LWP %d tls_val=%p\n", _lwp_self(), tls_val);
|
|
FORKEE_ASSERT(pthread_setspecific(thread_concurrent_key,
|
|
(void*)((uintptr_t)tls_val + 1)) == 0);
|
|
}
|
|
|
|
static void *
|
|
thread_concurrent_signals_thread(void *arg)
|
|
{
|
|
int sigval = thread_concurrent_signals_list[
|
|
_lwp_self() % __arraycount(thread_concurrent_signals_list)];
|
|
enum thread_concurrent_signal_handling *signal_handle = arg;
|
|
void *tls_val;
|
|
|
|
pthread_barrier_wait(&thread_concurrent_barrier);
|
|
DPRINTF("Before raising %s from LWP %d\n", strsignal(sigval),
|
|
_lwp_self());
|
|
pthread_kill(pthread_self(), sigval);
|
|
if (*signal_handle == TCSH_HANDLER) {
|
|
tls_val = pthread_getspecific(thread_concurrent_key);
|
|
DPRINTF("After raising, LWP %d tls_val=%p\n", _lwp_self(), tls_val);
|
|
FORKEE_ASSERT(tls_val == (void*)1);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void *
|
|
thread_concurrent_watchpoint_thread(void *arg)
|
|
{
|
|
pthread_barrier_wait(&thread_concurrent_barrier);
|
|
DPRINTF("Before modifying var from LWP %d\n", _lwp_self());
|
|
thread_concurrent_watchpoint_var = 1;
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(__i386__) || defined(__x86_64__)
|
|
enum thread_concurrent_sigtrap_event {
|
|
TCSE_UNKNOWN,
|
|
TCSE_BREAKPOINT,
|
|
TCSE_WATCHPOINT
|
|
};
|
|
|
|
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 breakpoint_threads, int signal_threads, int watchpoint_threads)
|
|
{
|
|
const int exitval = 5;
|
|
const int sigval = SIGSTOP;
|
|
pid_t child, wpid;
|
|
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}};
|
|
struct lwp_event_count wp_counts[THREAD_CONCURRENT_BREAKPOINT_NUM]
|
|
= {{0, 0}};
|
|
ptrace_event_t event;
|
|
int i;
|
|
|
|
#if defined(HAVE_DBREGS)
|
|
if (!can_we_set_dbregs()) {
|
|
atf_tc_skip("Either run this test as root or set sysctl(3) "
|
|
"security.models.extensions.user_set_dbregs to 1");
|
|
}
|
|
#endif
|
|
|
|
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);
|
|
ATF_REQUIRE(watchpoint_threads <= THREAD_CONCURRENT_WATCHPOINT_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];
|
|
pthread_t wp_threads[THREAD_CONCURRENT_WATCHPOINT_NUM];
|
|
|
|
DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
|
|
FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
|
|
|
|
DPRINTF("Before raising %s from child\n", strsignal(sigval));
|
|
FORKEE_ASSERT(raise(sigval) == 0);
|
|
|
|
if (signal_handle != TCSH_DISCARD) {
|
|
struct sigaction sa;
|
|
unsigned int j;
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
if (signal_handle == TCSH_SIG_IGN)
|
|
sa.sa_handler = SIG_IGN;
|
|
else
|
|
sa.sa_handler = thread_concurrent_sig_handler;
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
for (j = 0;
|
|
j < __arraycount(thread_concurrent_signals_list);
|
|
j++)
|
|
FORKEE_ASSERT(sigaction(
|
|
thread_concurrent_signals_list[j], &sa, NULL)
|
|
!= -1);
|
|
}
|
|
|
|
DPRINTF("Before starting threads from the child\n");
|
|
FORKEE_ASSERT(pthread_barrier_init(
|
|
&thread_concurrent_barrier, NULL,
|
|
breakpoint_threads + signal_threads + watchpoint_threads)
|
|
== 0);
|
|
FORKEE_ASSERT(pthread_key_create(&thread_concurrent_key, NULL)
|
|
== 0);
|
|
|
|
for (i = 0; i < signal_threads; i++) {
|
|
FORKEE_ASSERT(pthread_create(&sig_threads[i], NULL,
|
|
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);
|
|
}
|
|
for (i = 0; i < watchpoint_threads; i++) {
|
|
FORKEE_ASSERT(pthread_create(&wp_threads[i], NULL,
|
|
thread_concurrent_watchpoint_thread, NULL) == 0);
|
|
}
|
|
|
|
DPRINTF("Before joining threads from the child\n");
|
|
for (i = 0; i < watchpoint_threads; i++) {
|
|
FORKEE_ASSERT(pthread_join(wp_threads[i], NULL) == 0);
|
|
}
|
|
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);
|
|
}
|
|
|
|
FORKEE_ASSERT(pthread_key_delete(thread_concurrent_key) == 0);
|
|
FORKEE_ASSERT(pthread_barrier_destroy(
|
|
&thread_concurrent_barrier) == 0);
|
|
|
|
DPRINTF("Before exiting of the child process\n");
|
|
_exit(exitval);
|
|
}
|
|
DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child);
|
|
|
|
DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
|
|
|
|
validate_status_stopped(status, sigval);
|
|
|
|
DPRINTF("Set LWP event mask for the child process\n");
|
|
memset(&event, 0, sizeof(event));
|
|
event.pe_set_event |= PTRACE_LWP_CREATE;
|
|
SYSCALL_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, sizeof(event))
|
|
!= -1);
|
|
|
|
DPRINTF("Before resuming the child process where it left off\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
|
|
|
|
DPRINTF("Before entering signal collection loop\n");
|
|
while (1) {
|
|
ptrace_siginfo_t info;
|
|
|
|
DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
|
|
TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0),
|
|
child);
|
|
if (WIFEXITED(status))
|
|
break;
|
|
/* Note: we use validate_status_stopped() to get nice error
|
|
* message. Signal is irrelevant since it won't be reached.
|
|
*/
|
|
else if (!WIFSTOPPED(status))
|
|
validate_status_stopped(status, 0);
|
|
|
|
DPRINTF("Before calling PT_GET_SIGINFO\n");
|
|
SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info,
|
|
sizeof(info)) != -1);
|
|
|
|
DPRINTF("Received signal %d from LWP %d (wait: %d)\n",
|
|
info.psi_siginfo.si_signo, info.psi_lwpid,
|
|
WSTOPSIG(status));
|
|
|
|
ATF_CHECK_EQ_MSG(info.psi_siginfo.si_signo, WSTOPSIG(status),
|
|
"lwp=%d, WSTOPSIG=%d, psi_siginfo=%d", info.psi_lwpid,
|
|
WSTOPSIG(status), info.psi_siginfo.si_signo);
|
|
|
|
if (WSTOPSIG(status) != SIGTRAP) {
|
|
int expected_sig =
|
|
thread_concurrent_signals_list[info.psi_lwpid %
|
|
__arraycount(thread_concurrent_signals_list)];
|
|
ATF_CHECK_EQ_MSG(WSTOPSIG(status), expected_sig,
|
|
"lwp=%d, expected %d, got %d", info.psi_lwpid,
|
|
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 {
|
|
#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;
|
|
case TCSE_WATCHPOINT:
|
|
*FIND_EVENT_COUNT(wp_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");
|
|
SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1,
|
|
signal_handle != TCSH_DISCARD && WSTOPSIG(status) != SIGTRAP
|
|
? WSTOPSIG(status) : 0) != -1);
|
|
}
|
|
|
|
for (i = 0; i < signal_threads; i++)
|
|
ATF_CHECK_EQ_MSG(signal_counts[i].lec_count, 1,
|
|
"signal_counts[%d].lec_count=%d; lec_lwp=%d",
|
|
i, signal_counts[i].lec_count, signal_counts[i].lec_lwp);
|
|
for (i = signal_threads; i < THREAD_CONCURRENT_SIGNALS_NUM; i++)
|
|
ATF_CHECK_EQ_MSG(signal_counts[i].lec_count, 0,
|
|
"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);
|
|
|
|
for (i = 0; i < watchpoint_threads; i++)
|
|
ATF_CHECK_EQ_MSG(wp_counts[i].lec_count, 1,
|
|
"wp_counts[%d].lec_count=%d; lec_lwp=%d",
|
|
i, wp_counts[i].lec_count, wp_counts[i].lec_lwp);
|
|
for (i = watchpoint_threads; i < THREAD_CONCURRENT_WATCHPOINT_NUM; i++)
|
|
ATF_CHECK_EQ_MSG(wp_counts[i].lec_count, 0,
|
|
"extraneous wp_counts[%d].lec_count=%d; lec_lwp=%d",
|
|
i, wp_counts[i].lec_count, wp_counts[i].lec_lwp);
|
|
|
|
validate_status_exited(status, exitval);
|
|
}
|
|
|
|
#define THREAD_CONCURRENT_TEST(test, sig_hdl, bps, sigs, wps, descr) \
|
|
ATF_TC(test); \
|
|
ATF_TC_HEAD(test, tc) \
|
|
{ \
|
|
atf_tc_set_md_var(tc, "descr", descr); \
|
|
} \
|
|
\
|
|
ATF_TC_BODY(test, tc) \
|
|
{ \
|
|
thread_concurrent_test(sig_hdl, bps, sigs, wps); \
|
|
}
|
|
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_signals, TCSH_DISCARD,
|
|
0, THREAD_CONCURRENT_SIGNALS_NUM, 0,
|
|
"Verify that concurrent signals issued to a single thread are reported "
|
|
"correctly");
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_signals_sig_ign, TCSH_SIG_IGN,
|
|
0, THREAD_CONCURRENT_SIGNALS_NUM, 0,
|
|
"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,
|
|
0, THREAD_CONCURRENT_SIGNALS_NUM, 0,
|
|
"Verify that concurrent signals issued to a single thread are reported "
|
|
"correctly and passed back to a handler function");
|
|
|
|
#if defined(__i386__) || defined(__x86_64__)
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_breakpoints, TCSH_DISCARD,
|
|
THREAD_CONCURRENT_BREAKPOINT_NUM, 0, 0,
|
|
"Verify that concurrent breakpoints are reported correctly");
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_watchpoints, TCSH_DISCARD,
|
|
0, 0, THREAD_CONCURRENT_WATCHPOINT_NUM,
|
|
"Verify that concurrent breakpoints are reported correctly");
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_bp_wp, TCSH_DISCARD,
|
|
THREAD_CONCURRENT_BREAKPOINT_NUM, 0, THREAD_CONCURRENT_WATCHPOINT_NUM,
|
|
"Verify that concurrent breakpoints and watchpoints are reported "
|
|
"correctly");
|
|
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_bp_sig, TCSH_DISCARD,
|
|
THREAD_CONCURRENT_BREAKPOINT_NUM, THREAD_CONCURRENT_SIGNALS_NUM, 0,
|
|
"Verify that concurrent breakpoints and signals are reported correctly");
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_bp_sig_sig_ign, TCSH_SIG_IGN,
|
|
THREAD_CONCURRENT_BREAKPOINT_NUM, THREAD_CONCURRENT_SIGNALS_NUM, 0,
|
|
"Verify that concurrent breakpoints and signals are reported correctly "
|
|
"and passed back to SIG_IGN handler");
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_bp_sig_handler, TCSH_HANDLER,
|
|
THREAD_CONCURRENT_BREAKPOINT_NUM, THREAD_CONCURRENT_SIGNALS_NUM, 0,
|
|
"Verify that concurrent breakpoints and signals are reported correctly "
|
|
"and passed back to a handler function");
|
|
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_wp_sig, TCSH_DISCARD,
|
|
0, THREAD_CONCURRENT_SIGNALS_NUM, THREAD_CONCURRENT_WATCHPOINT_NUM,
|
|
"Verify that concurrent watchpoints and signals are reported correctly");
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_wp_sig_sig_ign, TCSH_SIG_IGN,
|
|
0, THREAD_CONCURRENT_SIGNALS_NUM, THREAD_CONCURRENT_WATCHPOINT_NUM,
|
|
"Verify that concurrent watchpoints and signals are reported correctly "
|
|
"and passed back to SIG_IGN handler");
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_wp_sig_handler, TCSH_HANDLER,
|
|
0, THREAD_CONCURRENT_SIGNALS_NUM, THREAD_CONCURRENT_WATCHPOINT_NUM,
|
|
"Verify that concurrent watchpoints and signals are reported correctly "
|
|
"and passed back to a handler function");
|
|
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_bp_wp_sig, TCSH_DISCARD,
|
|
THREAD_CONCURRENT_BREAKPOINT_NUM, THREAD_CONCURRENT_SIGNALS_NUM,
|
|
THREAD_CONCURRENT_WATCHPOINT_NUM,
|
|
"Verify that concurrent breakpoints, watchpoints and signals are reported "
|
|
"correctly");
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_bp_wp_sig_sig_ign, TCSH_SIG_IGN,
|
|
THREAD_CONCURRENT_BREAKPOINT_NUM, THREAD_CONCURRENT_SIGNALS_NUM,
|
|
THREAD_CONCURRENT_WATCHPOINT_NUM,
|
|
"Verify that concurrent breakpoints, watchpoints and signals are reported "
|
|
"correctly and passed back to SIG_IGN handler");
|
|
THREAD_CONCURRENT_TEST(thread_concurrent_bp_wp_sig_handler, TCSH_HANDLER,
|
|
THREAD_CONCURRENT_BREAKPOINT_NUM, THREAD_CONCURRENT_SIGNALS_NUM,
|
|
THREAD_CONCURRENT_WATCHPOINT_NUM,
|
|
"Verify that concurrent breakpoints, watchpoints and signals are reported "
|
|
"correctly and passed back to a handler function");
|
|
#endif
|
|
|
|
#endif /*defined(TWAIT_HAVE_STATUS)*/
|
|
|
|
#define ATF_TP_ADD_TCS_PTRACE_WAIT_THREADS() \
|
|
ATF_TP_ADD_TC(tp, trace_thread_nolwpevents); \
|
|
ATF_TP_ADD_TC(tp, trace_thread_lwpexit); \
|
|
ATF_TP_ADD_TC(tp, trace_thread_lwpcreate); \
|
|
ATF_TP_ADD_TC(tp, trace_thread_lwpcreate_and_exit); \
|
|
ATF_TP_ADD_TC(tp, trace_thread_lwpexit_masked_sigtrap); \
|
|
ATF_TP_ADD_TC(tp, trace_thread_lwpcreate_masked_sigtrap); \
|
|
ATF_TP_ADD_TC(tp, trace_thread_lwpcreate_and_exit_masked_sigtrap); \
|
|
ATF_TP_ADD_TC(tp, threads_and_exec); \
|
|
ATF_TP_ADD_TC(tp, suspend_no_deadlock); \
|
|
ATF_TP_ADD_TC(tp, resume); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS(tp, thread_concurrent_signals); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS(tp, thread_concurrent_signals_sig_ign); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS(tp, thread_concurrent_signals_handler); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_breakpoints); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_watchpoints); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_wp); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_sig); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_sig_sig_ign); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_sig_handler); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_wp_sig); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_wp_sig_sig_ign); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_wp_sig_handler); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_wp_sig); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_wp_sig_sig_ign); \
|
|
ATF_TP_ADD_TC_HAVE_STATUS_X86(tp, thread_concurrent_bp_wp_sig_handler);
|