Blob Blame History Raw
diff -rup a/defs.h b/defs.h
--- a/defs.h	2017-04-24 19:14:57.000000000 -0400
+++ b/defs.h	2017-06-15 14:24:50.121965636 -0400
@@ -213,6 +213,9 @@ struct tcb {
 	int flags;		/* See below for TCB_ values */
 	int pid;		/* If 0, this tcb is free */
 	int qual_flg;		/* qual_flags[scno] or DEFAULT_QUAL_FLAGS + RAW */
+	int wait_status;        /* Status from last wait() */
+	struct tcb *next_need_service;
+				/* Linked list of tracees found by wait()s */
 	unsigned long u_error;	/* Error code */
 	kernel_ulong_t scno;	/* System call number */
 	kernel_ulong_t u_arg[MAX_ARGS];	/* System call arguments */
@@ -413,6 +416,7 @@ extern void count_syscall(struct tcb *,
 extern void call_summary(FILE *);
 
 extern void clear_regs(void);
+extern void get_regs(pid_t pid);
 extern int get_scno(struct tcb *);
 extern kernel_ulong_t get_rt_sigframe_addr(struct tcb *);
 
diff -rup a/strace.c b/strace.c
--- a/strace.c	2017-05-22 13:33:51.000000000 -0400
+++ b/strace.c	2017-06-15 18:32:19.985800720 -0400
@@ -2251,17 +2251,40 @@ print_event_exit(struct tcb *tcp)
 	line_ended();
 }
 
+static int remembered_pid;
+static int remembered_status;
+
 /* Returns true iff the main trace loop has to continue. */
 static bool
 trace(void)
 {
 	int pid;
+	struct tcb *tcp;
+	struct tcb *found_tcps;
+	struct tcb **nextp;
+	struct tcb *next;
+	int wnohang = 0;
+
+	if (remembered_pid) {
+		pid = remembered_pid;
+		remembered_pid = 0;
+		if (debug_flag)
+			fprintf(stderr, " [remembered wait(%#x) = %u]\n",
+						remembered_status, pid);
+		tcp = pid2tcb(pid); /* can't be NULL */
+		tcp->wait_status = remembered_status;
+		tcp->next_need_service = NULL;
+		found_tcps = tcp;
+		goto process_saved_tcbs;
+	}
+
+	nextp = &found_tcps;
+	found_tcps = NULL;
+
+	while (1) { /* RH 851457 - collect tcbs */
 	int wait_errno;
 	int status;
-	bool stopped;
-	unsigned int sig;
 	unsigned int event;
-	struct tcb *tcp;
 	struct rusage ru;
 
 	if (interrupted)
@@ -2290,14 +2313,24 @@ trace(void)
 
 	if (interactive)
 		sigprocmask(SIG_SETMASK, &empty_set, NULL);
-	pid = wait4(-1, &status, __WALL, (cflag ? &ru : NULL));
+	pid = wait4(-1, &status, __WALL | wnohang, (cflag ? &ru : NULL));
 	wait_errno = errno;
 	if (interactive)
 		sigprocmask(SIG_BLOCK, &blocked_set, NULL);
 
+	if (pid <= 0 && wnohang) {
+		/* We had at least one successful
+		 * wait() before. We waited
+		 * with WNOHANG second time.
+		 * Stop collecting more tracees,
+		 * process what we already have.
+		 */
+		break; /* out of collect tcbs */
+	}
+
 	if (pid < 0) {
 		if (wait_errno == EINTR)
-			return true;
+			break; /* out of collect tcbs */
 		if (nprocs == 0 && wait_errno == ECHILD)
 			return false;
 		/*
@@ -2311,7 +2344,7 @@ trace(void)
 	if (pid == popen_pid) {
 		if (!WIFSTOPPED(status))
 			popen_pid = 0;
-		return true;
+		break; /* out of collect tcbs */
 	}
 
 	if (debug_flag)
@@ -2323,7 +2356,7 @@ trace(void)
 	if (!tcp) {
 		tcp = maybe_allocate_tcb(pid, status);
 		if (!tcp)
-			return true;
+			break; /* out of collect tcbs */
 	}
 
 	clear_regs();
@@ -2354,29 +2387,83 @@ trace(void)
 				tcp->flags &= ~TCB_SKIP_DETACH_ON_FIRST_EXEC;
 			} else {
 				detach(tcp); /* do "-b execve" thingy */
-				return true;
+				break; /* out of collect tcbs */
 			}
 		}
 	}
 
-	/* Set current output file */
-	current_tcp = tcp;
-
 	if (cflag) {
 		tv_sub(&tcp->dtime, &ru.ru_stime, &tcp->stime);
 		tcp->stime = ru.ru_stime;
 	}
 
+	/* If we waited and got a stopped task notification,
+	 * subsequent wait may return the same pid again, for example,
+	 * with SIGKILL notification. SIGKILL kills even stopped tasks.
+	 * We must not add it to the list
+	 * (one task can't be inserted twice in the list).
+	 */
+	{
+		struct tcb *f = found_tcps;
+		while (f) {
+			if (f == tcp) {
+				remembered_pid = pid;
+				remembered_status = status;
+				goto process_saved_tcbs;
+			}
+			f = f->next_need_service;
+		}
+	}
+	/* It is important to not invert the order of tasks
+	 * to process. For one, alloc_tcb() above picks newly forked
+	 * threads in some order, processing of them and their parent
+	 * should be in the same order, otherwise bad things happen
+	 * (misinterpreted SIGSTOPs and such).
+	 */
+	tcp->wait_status = status;
+	*nextp = tcp;
+	nextp = &tcp->next_need_service;
+	*nextp = NULL;
+	wnohang = WNOHANG;
+
+	} /* RH 851457 - collect tcbs */
+
+process_saved_tcbs:
+
+	for (tcp = found_tcps;
+	     tcp;
+	     tcp = next) { /* RH 851457 - process tcbs */
+	int status;
+	bool stopped;
+	unsigned int sig;
+	unsigned int event;
+
+	/* If the child exits, the TCP will get dropped and
+	   thus we can't use it to find the next TCP needing
+	   service.  So we save the next TCP needing service
+	   and used the saved value when the loop iterates.  */
+	next = tcp->next_need_service;
+
+	status = tcp->wait_status;
+	pid = tcp->pid;
+
+	event = ((unsigned)status >> 16);
+
+	clear_regs();
+
+	/* Set current output file */
+	current_tcp = tcp;
+
 	if (WIFSIGNALED(status)) {
 		print_signalled(tcp, pid, status);
 		droptcb(tcp);
-		return true;
+		continue; /* processing tcbs */
 	}
 
 	if (WIFEXITED(status)) {
 		print_exited(tcp, pid, status);
 		droptcb(tcp);
-		return true;
+		continue; /* processing tcbs */
 	}
 
 	if (!WIFSTOPPED(status)) {
@@ -2386,7 +2473,7 @@ trace(void)
 		 */
 		error_msg("pid %u not stopped!", pid);
 		droptcb(tcp);
-		return true;
+		continue; /* processing tcbs */
 	}
 
 	/* Is this the very first time we see this tracee stopped? */
@@ -2467,7 +2554,7 @@ show_stopsig:
 				exit_code = 1;
 				return false;
 			}
-			return true;
+			continue; /* processing tcbs */
 		}
 		/* We don't have PTRACE_LISTEN support... */
 		goto restart_tracee;
@@ -2494,7 +2581,7 @@ show_stopsig:
 		 * we can let this process to report its death to us
 		 * normally, via WIFEXITED or WIFSIGNALED wait status.
 		 */
-		return true;
+		continue; /* processing tcbs */
 	}
 	goto restart_tracee;
 
@@ -2508,6 +2595,8 @@ restart_tracee:
 		return false;
 	}
 
+	} /* RH 851457 - process tcbs */
+
 	return true;
 }
 
diff -rup a/syscall.c b/syscall.c
--- a/syscall.c	2017-05-22 13:33:51.000000000 -0400
+++ b/syscall.c	2017-06-15 14:25:56.153524001 -0400
@@ -568,7 +568,6 @@ clear_regs(void)
 	get_regs_error = -1;
 }
 
-static void get_regs(pid_t pid);
 static int get_syscall_args(struct tcb *);
 static int get_syscall_result(struct tcb *);
 static int arch_get_scno(struct tcb *tcp);
@@ -1163,7 +1162,7 @@ ptrace_setregs(pid_t pid)
 
 #endif /* ARCH_REGS_FOR_GETREGSET || ARCH_REGS_FOR_GETREGS */
 
-static void
+void
 get_regs(pid_t pid)
 {
 #undef USE_GET_SYSCALL_RESULT_REGS