Blame SOURCES/0027-Implement-queueing-of-threads-before-dispatching-the.patch

4d44fe
From 496f922dad9b58b891f417b937ac5fb0b0a8649a Mon Sep 17 00:00:00 2001
4d44fe
From: Eugene Syromyatnikov <evgsyr@gmail.com>
4d44fe
Date: Wed, 8 Aug 2018 21:41:39 +0200
4d44fe
Subject: [PATCH 27/27] Implement queueing of threads before dispatching them
4d44fe
4d44fe
It is possible that some tracees call a lot of cheap syscalls too fast,
4d44fe
and that can stress the tracer to the point some tracees are not served
4d44fe
for indefinite amount of time.  In order to avoid that unfairness, try
4d44fe
to collect all the pending tracees first along with the relevant
4d44fe
information and only then dispatch the events.
4d44fe
4d44fe
* defs.h: Include "list.h".
4d44fe
(struct tcb): Add wait_data_idx, delayed_wait_data, and wait_list
4d44fe
fields.
4d44fe
* strace.c (struct tcb_wait_data): Add "msg" field.
4d44fe
(tcb_wait_tab, tcb_wait_tab_size): New static variables.
4d44fe
(alloctcb): Initialize wait_list.
4d44fe
(droptcb): Remove tcp from wait_list.
4d44fe
(maybe_switch_tcbs): Get old pid from
4d44fe
tcb_wait_tab[tcp->wait_data_idx].msg instead of calling
4d44fe
ptrace(PTRACE_GETEVENTMSG).
4d44fe
(trace_wait_data_size, init_trace_wait_data, copy_trace_wait_data,
4d44fe
free_trace_wait_data, tcb_wait_tab_check_size): New functions, in order
4d44fe
to allow the code outside next_event to operate with wait_data as with
4d44fe
an opaque object (needed for dispatch_event and restart_delayed_tcb).
4d44fe
(next_event): Add pending_tcps, extra_tcp, wait_nohang, elem, and
4d44fe
wait_tab_pos variables; check for elements in pending_tcps and skip
4d44fe
waiting if the list is not empty; check for extra_tcp and skip waiting
4d44fe
along with swapping wait_data_idx with wait_extra_data_idx;
4d44fe
after the initial wait4(), call wait4() in loop with WNOHANG flag set;
4d44fe
fetch siginfo on signal and eventmsg on PTRACE_EVENT_EXEC;
4d44fe
return the first tcp in pending_tcps list.
4d44fe
(dispatch_event): Store a pointer to a copy of tcb_wait_data in
4d44fe
tcp->delayed_wait_data if tcp's restart has to be delayed.
4d44fe
(restart_delayed_tcb): Use tcp->delayed_wait_data, create a stub
4d44fe
tcb_wait_data if it is NULL, free temporary trace_wait_data.
4d44fe
* tests/Makefile.am (XFAIL_TEST): Remove looping_threads.test.
4d44fe
4d44fe
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=478419
4d44fe
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=526740
4d44fe
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=851457
4d44fe
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1609318
4d44fe
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1610774
4d44fe
Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
4d44fe
Co-Authored-by: Denys Vlasenko <dvlasenk@redhat.com>
4d44fe
Co-Authored-by: Andreas Schwab <aschwab@redhat.com>
4d44fe
Co-Authored-by: Jeff Law <law@redhat.com>
4d44fe
Co-Authored-by: DJ Delorie <dj@redhat.com>
4d44fe
4d44fe
Conflicts:
4d44fe
	defs.h
4d44fe
	tests/Makefile.am
4d44fe
---
4d44fe
 defs.h            |  12 ++
4d44fe
 strace.c          | 393 +++++++++++++++++++++++++++++++++++++-----------------
4d44fe
 tests/Makefile.am |   3 +-
4d44fe
 3 files changed, 283 insertions(+), 125 deletions(-)
4d44fe
4d44fe
diff --git a/defs.h b/defs.h
4d44fe
index 811bb0d..2a614a7 100644
4d44fe
--- a/defs.h
4d44fe
+++ b/defs.h
4d44fe
@@ -36,6 +36,7 @@
4d44fe
 #include "arch_defs.h"
4d44fe
 #include "error_prints.h"
4d44fe
 #include "gcc_compat.h"
4d44fe
+#include "list.h"
4d44fe
 #include "kernel_types.h"
4d44fe
 #include "macros.h"
4d44fe
 #include "mpers_type.h"
4d44fe
@@ -218,6 +219,17 @@ struct tcb {
4d44fe
 
4d44fe
 	struct mmap_cache_t *mmap_cache;
4d44fe
 
4d44fe
+	/*
4d44fe
+	 * Data that is stored during process wait traversal.
4d44fe
+	 * We use indices as the actual data is stored in an array
4d44fe
+	 * that is realloc'ed at runtime.
4d44fe
+	 */
4d44fe
+	size_t wait_data_idx;
4d44fe
+	/** Wait data storage for a delayed process. */
4d44fe
+	struct tcb_wait_data *delayed_wait_data;
4d44fe
+	struct list_item wait_list;
4d44fe
+
4d44fe
+
4d44fe
 #ifdef HAVE_LINUX_KVM_H
4d44fe
 	struct vcpu_info *vcpu_info_list;
4d44fe
 #endif
4d44fe
diff --git a/strace.c b/strace.c
4d44fe
index 0745838..0914dc7 100644
4d44fe
--- a/strace.c
4d44fe
+++ b/strace.c
4d44fe
@@ -141,6 +141,7 @@ static struct tcb *current_tcp;
4d44fe
 struct tcb_wait_data {
4d44fe
 	enum trace_event te; /**< Event passed to dispatch_event() */
4d44fe
 	int status;          /**< status, returned by wait4() */
4d44fe
+	unsigned long msg;   /**< Value returned by PTRACE_GETEVENTMSG */
4d44fe
 	siginfo_t si;        /**< siginfo, returned by PTRACE_GETSIGINFO */
4d44fe
 };
4d44fe
 
4d44fe
@@ -148,6 +149,10 @@ static struct tcb **tcbtab;
4d44fe
 static unsigned int nprocs;
4d44fe
 static size_t tcbtabsize;
4d44fe
 
4d44fe
+static struct tcb_wait_data *tcb_wait_tab;
4d44fe
+static size_t tcb_wait_tab_size;
4d44fe
+
4d44fe
+
4d44fe
 #ifndef HAVE_PROGRAM_INVOCATION_NAME
4d44fe
 char *program_invocation_name;
4d44fe
 #endif
4d44fe
@@ -745,6 +750,7 @@ alloctcb(int pid)
4d44fe
 		tcp = tcbtab[i];
4d44fe
 		if (!tcp->pid) {
4d44fe
 			memset(tcp, 0, sizeof(*tcp));
4d44fe
+			list_init(&tcp->wait_list);
4d44fe
 			tcp->pid = pid;
4d44fe
 #if SUPPORTED_PERSONALITIES > 1
4d44fe
 			tcp->currpers = current_personality;
4d44fe
@@ -833,6 +839,8 @@ droptcb(struct tcb *tcp)
4d44fe
 	if (printing_tcp == tcp)
4d44fe
 		printing_tcp = NULL;
4d44fe
 
4d44fe
+	list_remove(&tcp->wait_list);
4d44fe
+
4d44fe
 	memset(tcp, 0, sizeof(*tcp));
4d44fe
 }
4d44fe
 
4d44fe
@@ -2051,10 +2059,8 @@ maybe_switch_tcbs(struct tcb *tcp, const int pid)
4d44fe
 {
4d44fe
 	FILE *fp;
4d44fe
 	struct tcb *execve_thread;
4d44fe
-	long old_pid = 0;
4d44fe
+	long old_pid = tcb_wait_tab[tcp->wait_data_idx].msg;
4d44fe
 
4d44fe
-	if (ptrace(PTRACE_GETEVENTMSG, pid, NULL, &old_pid) < 0)
4d44fe
-		return tcp;
4d44fe
 	/* Avoid truncation in pid2tcb() param passing */
4d44fe
 	if (old_pid <= 0 || old_pid == pid)
4d44fe
 		return tcp;
4d44fe
@@ -2207,20 +2213,74 @@ print_event_exit(struct tcb *tcp)
4d44fe
 	line_ended();
4d44fe
 }
4d44fe
 
4d44fe
-static const struct tcb_wait_data *
4d44fe
-next_event(void)
4d44fe
+static size_t
4d44fe
+trace_wait_data_size(struct tcb *tcp)
4d44fe
 {
4d44fe
-	static struct tcb_wait_data wait_data;
4d44fe
+	return sizeof(struct tcb_wait_data);
4d44fe
+}
4d44fe
 
4d44fe
-	int pid;
4d44fe
-	int status;
4d44fe
-	struct tcb *tcp;
4d44fe
-	struct tcb_wait_data *wd = &wait_data;
4d44fe
-	struct rusage ru;
4d44fe
+static struct tcb_wait_data *
4d44fe
+init_trace_wait_data(void *p)
4d44fe
+{
4d44fe
+	struct tcb_wait_data *wd = p;
4d44fe
+
4d44fe
+	memset(wd, 0, sizeof(*wd));
4d44fe
+
4d44fe
+	return wd;
4d44fe
+}
4d44fe
 
4d44fe
+static struct tcb_wait_data *
4d44fe
+copy_trace_wait_data(const struct tcb_wait_data *wd)
4d44fe
+{
4d44fe
+	struct tcb_wait_data *new_wd = xmalloc(sizeof(*new_wd));
4d44fe
+
4d44fe
+	memcpy(new_wd, wd, sizeof(*wd));
4d44fe
+
4d44fe
+	return new_wd;
4d44fe
+}
4d44fe
+
4d44fe
+static void
4d44fe
+free_trace_wait_data(struct tcb_wait_data *wd)
4d44fe
+{
4d44fe
+	free(wd);
4d44fe
+}
4d44fe
+
4d44fe
+static void
4d44fe
+tcb_wait_tab_check_size(const size_t size)
4d44fe
+{
4d44fe
+	while (size >= tcb_wait_tab_size) {
4d44fe
+		tcb_wait_tab = xgrowarray(tcb_wait_tab,
4d44fe
+					  &tcb_wait_tab_size,
4d44fe
+					  sizeof(tcb_wait_tab[0]));
4d44fe
+	}
4d44fe
+}
4d44fe
+
4d44fe
+static const struct tcb_wait_data *
4d44fe
+next_event(void)
4d44fe
+{
4d44fe
 	if (interrupted)
4d44fe
 		return NULL;
4d44fe
 
4d44fe
+	struct tcb *tcp = NULL;
4d44fe
+	struct list_item *elem;
4d44fe
+
4d44fe
+	static EMPTY_LIST(pending_tcps);
4d44fe
+	/* Handle the queued tcbs before waiting for new events.  */
4d44fe
+	if (!list_is_empty(&pending_tcps))
4d44fe
+		goto next_event_get_tcp;
4d44fe
+
4d44fe
+	static struct tcb *extra_tcp;
4d44fe
+	static size_t wait_extra_data_idx;
4d44fe
+	/* Handle the extra tcb event.  */
4d44fe
+	if (extra_tcp) {
4d44fe
+		tcp = extra_tcp;
4d44fe
+		extra_tcp = NULL;
4d44fe
+		tcp->wait_data_idx = wait_extra_data_idx;
4d44fe
+
4d44fe
+		debug_msg("dequeued extra event for pid %u", tcp->pid);
4d44fe
+		goto next_event_exit;
4d44fe
+	}
4d44fe
+
4d44fe
 	/*
4d44fe
 	 * Used to exit simply when nprocs hits zero, but in this testcase:
4d44fe
 	 *  int main(void) { _exit(!!fork()); }
4d44fe
@@ -2262,8 +2322,10 @@ next_event(void)
4d44fe
 	 * then the system call will be interrupted and
4d44fe
 	 * the expiration will be handled by the signal handler.
4d44fe
 	 */
4d44fe
-	pid = wait4(-1, &status, __WALL, (cflag ? &ru : NULL));
4d44fe
-	const int wait_errno = errno;
4d44fe
+	int status;
4d44fe
+	struct rusage ru;
4d44fe
+	int pid = wait4(-1, &status, __WALL, (cflag ? &ru : NULL));
4d44fe
+	int wait_errno = errno;
4d44fe
 
4d44fe
 	/*
4d44fe
 	 * The window of opportunity to handle expirations
4d44fe
@@ -2279,135 +2341,202 @@ next_event(void)
4d44fe
 			return NULL;
4d44fe
 	}
4d44fe
 
4d44fe
-	if (pid < 0) {
4d44fe
-		if (wait_errno == EINTR) {
4d44fe
-			wd->te = TE_NEXT;
4d44fe
-			return wd;
4d44fe
+	size_t wait_tab_pos = 0;
4d44fe
+	bool wait_nohang = false;
4d44fe
+
4d44fe
+	/*
4d44fe
+	 * Wait for new events until wait4() returns 0 (meaning that there's
4d44fe
+	 * nothing more to wait for for now), or a second event for some tcb
4d44fe
+	 * appears (which may happen if a tracee was SIGKILL'ed, for example).
4d44fe
+	 */
4d44fe
+	for (;;) {
4d44fe
+		struct tcb_wait_data *wd;
4d44fe
+
4d44fe
+		if (pid < 0) {
4d44fe
+			if (wait_errno == EINTR)
4d44fe
+				break;
4d44fe
+			if (wait_nohang)
4d44fe
+				break;
4d44fe
+			if (nprocs == 0 && wait_errno == ECHILD)
4d44fe
+				return NULL;
4d44fe
+			/*
4d44fe
+			 * If nprocs > 0, ECHILD is not expected,
4d44fe
+			 * treat it as any other error here:
4d44fe
+			 */
4d44fe
+			errno = wait_errno;
4d44fe
+			perror_msg_and_die("wait4(__WALL)");
4d44fe
 		}
4d44fe
-		if (nprocs == 0 && wait_errno == ECHILD)
4d44fe
-			return NULL;
4d44fe
-		/*
4d44fe
-		 * If nprocs > 0, ECHILD is not expected,
4d44fe
-		 * treat it as any other error here:
4d44fe
-		 */
4d44fe
-		errno = wait_errno;
4d44fe
-		perror_msg_and_die("wait4(__WALL)");
4d44fe
-	}
4d44fe
 
4d44fe
-	wd->status = status;
4d44fe
+		if (!pid)
4d44fe
+			break;
4d44fe
 
4d44fe
-	if (pid == popen_pid) {
4d44fe
-		if (!WIFSTOPPED(status))
4d44fe
-			popen_pid = 0;
4d44fe
-		wd->te = TE_NEXT;
4d44fe
-		return wd;
4d44fe
-	}
4d44fe
+		if (pid == popen_pid) {
4d44fe
+			if (!WIFSTOPPED(status))
4d44fe
+				popen_pid = 0;
4d44fe
+			break;
4d44fe
+		}
4d44fe
 
4d44fe
-	if (debug_flag)
4d44fe
-		print_debug_info(pid, status);
4d44fe
+		if (debug_flag)
4d44fe
+			print_debug_info(pid, status);
4d44fe
 
4d44fe
-	/* Look up 'pid' in our table. */
4d44fe
-	tcp = pid2tcb(pid);
4d44fe
+		/* Look up 'pid' in our table. */
4d44fe
+		tcp = pid2tcb(pid);
4d44fe
 
4d44fe
-	if (!tcp) {
4d44fe
-		tcp = maybe_allocate_tcb(pid, status);
4d44fe
 		if (!tcp) {
4d44fe
-			wd->te = TE_NEXT;
4d44fe
-			return wd;
4d44fe
+			tcp = maybe_allocate_tcb(pid, status);
4d44fe
+			if (!tcp)
4d44fe
+				goto next_event_wait_next;
4d44fe
 		}
4d44fe
-	}
4d44fe
 
4d44fe
-	clear_regs(tcp);
4d44fe
+		if (cflag) {
4d44fe
+			struct timespec stime = {
4d44fe
+				.tv_sec = ru.ru_stime.tv_sec,
4d44fe
+				.tv_nsec = ru.ru_stime.tv_usec * 1000
4d44fe
+			};
4d44fe
+			ts_sub(&tcp->dtime, &stime, &tcp->stime);
4d44fe
+			tcp->stime = stime;
4d44fe
+		}
4d44fe
 
4d44fe
-	/* Set current output file */
4d44fe
-	set_current_tcp(tcp);
4d44fe
+		tcb_wait_tab_check_size(wait_tab_pos);
4d44fe
 
4d44fe
-	if (cflag) {
4d44fe
-		struct timespec stime = {
4d44fe
-			.tv_sec = ru.ru_stime.tv_sec,
4d44fe
-			.tv_nsec = ru.ru_stime.tv_usec * 1000
4d44fe
-		};
4d44fe
-		ts_sub(&tcp->dtime, &stime, &tcp->stime);
4d44fe
-		tcp->stime = stime;
4d44fe
-	}
4d44fe
+		/* Initialise a new wait data structure.  */
4d44fe
+		wd = tcb_wait_tab + wait_tab_pos;
4d44fe
+		init_trace_wait_data(wd);
4d44fe
+		wd->status = status;
4d44fe
 
4d44fe
-	if (WIFSIGNALED(status)) {
4d44fe
-		wd->te = TE_SIGNALLED;
4d44fe
-		return wd;
4d44fe
-	}
4d44fe
+		if (WIFSIGNALED(status)) {
4d44fe
+			wd->te = TE_SIGNALLED;
4d44fe
+		} else if (WIFEXITED(status)) {
4d44fe
+			wd->te = TE_EXITED;
4d44fe
+		} else {
4d44fe
+			/*
4d44fe
+			 * As WCONTINUED flag has not been specified to wait4,
4d44fe
+			 * it cannot be WIFCONTINUED(status), so the only case
4d44fe
+			 * that remains is WIFSTOPPED(status).
4d44fe
+			 */
4d44fe
 
4d44fe
-	if (WIFEXITED(status)) {
4d44fe
-		wd->te = TE_EXITED;
4d44fe
-		return wd;
4d44fe
+			const unsigned int sig = WSTOPSIG(status);
4d44fe
+			const unsigned int event = (unsigned int) status >> 16;
4d44fe
+
4d44fe
+			switch (event) {
4d44fe
+			case 0:
4d44fe
+				/*
4d44fe
+				 * Is this post-attach SIGSTOP?
4d44fe
+				 * Interestingly, the process may stop
4d44fe
+				 * with STOPSIG equal to some other signal
4d44fe
+				 * than SIGSTOP if we happened to attach
4d44fe
+				 * just before the process takes a signal.
4d44fe
+				 */
4d44fe
+				if (sig == SIGSTOP &&
4d44fe
+				    (tcp->flags & TCB_IGNORE_ONE_SIGSTOP)) {
4d44fe
+					debug_func_msg("ignored SIGSTOP on "
4d44fe
+						       "pid %d", tcp->pid);
4d44fe
+					tcp->flags &= ~TCB_IGNORE_ONE_SIGSTOP;
4d44fe
+					wd->te = TE_RESTART;
4d44fe
+				} else if (sig == syscall_trap_sig) {
4d44fe
+					wd->te = TE_SYSCALL_STOP;
4d44fe
+				} else {
4d44fe
+					/*
4d44fe
+					 * True if tracee is stopped by signal
4d44fe
+					 * (as opposed to "tracee received
4d44fe
+					 * signal").
4d44fe
+					 * TODO: shouldn't we check for
4d44fe
+					 * errno == EINVAL too?
4d44fe
+					 * We can get ESRCH instead, you know...
4d44fe
+					 */
4d44fe
+					bool stopped = ptrace(PTRACE_GETSIGINFO,
4d44fe
+						pid, 0, &wd->si) < 0;
4d44fe
+
4d44fe
+					wd->te = stopped ? TE_GROUP_STOP
4d44fe
+							 : TE_SIGNAL_DELIVERY_STOP;
4d44fe
+				}
4d44fe
+				break;
4d44fe
+			case PTRACE_EVENT_STOP:
4d44fe
+				/*
4d44fe
+				 * PTRACE_INTERRUPT-stop or group-stop.
4d44fe
+				 * PTRACE_INTERRUPT-stop has sig == SIGTRAP here.
4d44fe
+				 */
4d44fe
+				switch (sig) {
4d44fe
+				case SIGSTOP:
4d44fe
+				case SIGTSTP:
4d44fe
+				case SIGTTIN:
4d44fe
+				case SIGTTOU:
4d44fe
+					wd->te = TE_GROUP_STOP;
4d44fe
+					break;
4d44fe
+				default:
4d44fe
+					wd->te = TE_RESTART;
4d44fe
+				}
4d44fe
+				break;
4d44fe
+			case PTRACE_EVENT_EXEC:
4d44fe
+					/*
4d44fe
+					 * TODO: shouldn't we check for
4d44fe
+					 * errno == EINVAL here, too?
4d44fe
+					 * We can get ESRCH instead, you know...
4d44fe
+					 */
4d44fe
+				if (ptrace(PTRACE_GETEVENTMSG, pid, NULL,
4d44fe
+				    &wd->msg) < 0)
4d44fe
+					wd->msg = 0;
4d44fe
+
4d44fe
+				wd->te = TE_STOP_BEFORE_EXECVE;
4d44fe
+				break;
4d44fe
+			case PTRACE_EVENT_EXIT:
4d44fe
+				wd->te = TE_STOP_BEFORE_EXIT;
4d44fe
+				break;
4d44fe
+			default:
4d44fe
+				wd->te = TE_RESTART;
4d44fe
+			}
4d44fe
+		}
4d44fe
+
4d44fe
+		if (!wd->te)
4d44fe
+			error_func_msg("Tracing event hasn't been determined "
4d44fe
+				       "for pid %d, status %0#x", pid, status);
4d44fe
+
4d44fe
+		if (!list_is_empty(&tcp->wait_list)) {
4d44fe
+			wait_extra_data_idx = wait_tab_pos;
4d44fe
+			extra_tcp = tcp;
4d44fe
+			debug_func_msg("queued extra pid %d", tcp->pid);
4d44fe
+		} else {
4d44fe
+			tcp->wait_data_idx = wait_tab_pos;
4d44fe
+			list_append(&pending_tcps, &tcp->wait_list);
4d44fe
+			debug_func_msg("queued pid %d", tcp->pid);
4d44fe
+		}
4d44fe
+
4d44fe
+		wait_tab_pos++;
4d44fe
+
4d44fe
+		if (extra_tcp)
4d44fe
+			break;
4d44fe
+
4d44fe
+next_event_wait_next:
4d44fe
+		pid = wait4(-1, &status, __WALL | WNOHANG, (cflag ? &ru : NULL));
4d44fe
+		wait_errno = errno;
4d44fe
+		wait_nohang = true;
4d44fe
 	}
4d44fe
 
4d44fe
-	/*
4d44fe
-	 * As WCONTINUED flag has not been specified to wait4,
4d44fe
-	 * it cannot be WIFCONTINUED(status), so the only case
4d44fe
-	 * that remains is WIFSTOPPED(status).
4d44fe
-	 */
4d44fe
+next_event_get_tcp:
4d44fe
+	elem = list_remove_head(&pending_tcps);
4d44fe
+
4d44fe
+	if (!elem) {
4d44fe
+		tcb_wait_tab_check_size(0);
4d44fe
+		memset(tcb_wait_tab, 0, sizeof(*tcb_wait_tab));
4d44fe
+		tcb_wait_tab->te = TE_NEXT;
4d44fe
 
4d44fe
+		return tcb_wait_tab;
4d44fe
+	} else {
4d44fe
+		tcp = list_elem(elem, struct tcb, wait_list);
4d44fe
+		debug_func_msg("dequeued pid %d", tcp->pid);
4d44fe
+	}
4d44fe
+
4d44fe
+next_event_exit:
4d44fe
 	/* Is this the very first time we see this tracee stopped? */
4d44fe
 	if (tcp->flags & TCB_STARTUP)
4d44fe
 		startup_tcb(tcp);
4d44fe
 
4d44fe
-	const unsigned int sig = WSTOPSIG(status);
4d44fe
-	const unsigned int event = (unsigned int) status >> 16;
4d44fe
+	clear_regs(tcp);
4d44fe
 
4d44fe
-	switch (event) {
4d44fe
-	case 0:
4d44fe
-		/*
4d44fe
-		 * Is this post-attach SIGSTOP?
4d44fe
-		 * Interestingly, the process may stop
4d44fe
-		 * with STOPSIG equal to some other signal
4d44fe
-		 * than SIGSTOP if we happened to attach
4d44fe
-		 * just before the process takes a signal.
4d44fe
-		 */
4d44fe
-		if (sig == SIGSTOP && (tcp->flags & TCB_IGNORE_ONE_SIGSTOP)) {
4d44fe
-			debug_func_msg("ignored SIGSTOP on pid %d", tcp->pid);
4d44fe
-			tcp->flags &= ~TCB_IGNORE_ONE_SIGSTOP;
4d44fe
-			wd->te = TE_RESTART;
4d44fe
-		} else if (sig == syscall_trap_sig) {
4d44fe
-			wd->te = TE_SYSCALL_STOP;
4d44fe
-		} else {
4d44fe
-			memset(&wd->si, 0, sizeof(wd->si));
4d44fe
-			/*
4d44fe
-			 * True if tracee is stopped by signal
4d44fe
-			 * (as opposed to "tracee received signal").
4d44fe
-			 * TODO: shouldn't we check for errno == EINVAL too?
4d44fe
-			 * We can get ESRCH instead, you know...
4d44fe
-			 */
4d44fe
-			bool stopped = ptrace(PTRACE_GETSIGINFO, pid, 0, &wd->si) < 0;
4d44fe
-			wd->te = stopped ? TE_GROUP_STOP : TE_SIGNAL_DELIVERY_STOP;
4d44fe
-		}
4d44fe
-		break;
4d44fe
-	case PTRACE_EVENT_STOP:
4d44fe
-		/*
4d44fe
-		 * PTRACE_INTERRUPT-stop or group-stop.
4d44fe
-		 * PTRACE_INTERRUPT-stop has sig == SIGTRAP here.
4d44fe
-		 */
4d44fe
-		switch (sig) {
4d44fe
-		case SIGSTOP:
4d44fe
-		case SIGTSTP:
4d44fe
-		case SIGTTIN:
4d44fe
-		case SIGTTOU:
4d44fe
-			wd->te = TE_GROUP_STOP;
4d44fe
-			break;
4d44fe
-		default:
4d44fe
-			wd->te = TE_RESTART;
4d44fe
-		}
4d44fe
-		break;
4d44fe
-	case PTRACE_EVENT_EXEC:
4d44fe
-		wd->te = TE_STOP_BEFORE_EXECVE;
4d44fe
-		break;
4d44fe
-	case PTRACE_EVENT_EXIT:
4d44fe
-		wd->te = TE_STOP_BEFORE_EXIT;
4d44fe
-		break;
4d44fe
-	default:
4d44fe
-		wd->te = TE_RESTART;
4d44fe
-	}
4d44fe
+	/* Set current output file */
4d44fe
+	set_current_tcp(tcp);
4d44fe
 
4d44fe
-	return wd;
4d44fe
+	return tcb_wait_tab + tcp->wait_data_idx;
4d44fe
 }
4d44fe
 
4d44fe
 static int
4d44fe
@@ -2569,8 +2698,15 @@ dispatch_event(const struct tcb_wait_data *wd)
4d44fe
 		return false;
4d44fe
 
4d44fe
 	/* If the process is being delayed, do not ptrace_restart just yet */
4d44fe
-	if (syscall_delayed(current_tcp))
4d44fe
+	if (syscall_delayed(current_tcp)) {
4d44fe
+		if (current_tcp->delayed_wait_data)
4d44fe
+			error_func_msg("pid %d has delayed wait data set"
4d44fe
+				       " already", current_tcp->pid);
4d44fe
+
4d44fe
+		current_tcp->delayed_wait_data = copy_trace_wait_data(wd);
4d44fe
+
4d44fe
 		return true;
4d44fe
+	}
4d44fe
 
4d44fe
 	if (ptrace_restart(restart_op, current_tcp, restart_sig) < 0) {
4d44fe
 		/* Note: ptrace_restart emitted error message */
4d44fe
@@ -2583,7 +2719,15 @@ dispatch_event(const struct tcb_wait_data *wd)
4d44fe
 static bool
4d44fe
 restart_delayed_tcb(struct tcb *const tcp)
4d44fe
 {
4d44fe
-	const struct tcb_wait_data wd = { .te = TE_RESTART };
4d44fe
+	struct tcb_wait_data *wd = tcp->delayed_wait_data;
4d44fe
+
4d44fe
+	if (!wd) {
4d44fe
+		error_func_msg("No delayed wait data found for pid %d",
4d44fe
+			       tcp->pid);
4d44fe
+		wd = init_trace_wait_data(alloca(trace_wait_data_size(tcp)));
4d44fe
+	}
4d44fe
+
4d44fe
+	wd->te = TE_RESTART;
4d44fe
 
4d44fe
 	debug_func_msg("pid %d", tcp->pid);
4d44fe
 
4d44fe
@@ -2591,9 +2735,12 @@ restart_delayed_tcb(struct tcb *const tcp)
4d44fe
 
4d44fe
 	struct tcb *const prev_tcp = current_tcp;
4d44fe
 	current_tcp = tcp;
4d44fe
-	bool ret = dispatch_event(&wd;;
4d44fe
+	bool ret = dispatch_event(wd);
4d44fe
 	current_tcp = prev_tcp;
4d44fe
 
4d44fe
+	free_trace_wait_data(tcp->delayed_wait_data);
4d44fe
+	tcp->delayed_wait_data = NULL;
4d44fe
+
4d44fe
 	return ret;
4d44fe
 }
4d44fe
 
4d44fe
diff --git a/tests/Makefile.am b/tests/Makefile.am
4d44fe
index de7a3d2..51f00a9 100644
4d44fe
--- a/tests/Makefile.am
4d44fe
+++ b/tests/Makefile.am
4d44fe
@@ -350,8 +350,7 @@ XFAIL_TESTS_m32 = $(STACKTRACE_TESTS)
4d44fe
 XFAIL_TESTS_mx32 = $(STACKTRACE_TESTS)
4d44fe
 XFAIL_TESTS_x86_64 = int_0x80.gen.test
4d44fe
 XFAIL_TESTS_x32 = int_0x80.gen.test
4d44fe
-XFAIL_TESTS = $(XFAIL_TESTS_$(MPERS_NAME)) $(XFAIL_TESTS_$(ARCH)) \
4d44fe
-	      looping_threads.test
4d44fe
+XFAIL_TESTS = $(XFAIL_TESTS_$(MPERS_NAME)) $(XFAIL_TESTS_$(ARCH))
4d44fe
 
4d44fe
 TEST_LOG_COMPILER = env
4d44fe
 AM_TEST_LOG_FLAGS = STRACE_ARCH=$(ARCH) STRACE_NATIVE_ARCH=$(NATIVE_ARCH) \
4d44fe
-- 
4d44fe
2.1.4
4d44fe