Make threaded execve handling code more reabable and somewhat simpler

* strace.c (droptcb): Remove outfname check in "outfname && followfork >= 2" -
with recent changes, followfork >= 2 check guarantees that outfile
was specified, and _is already opened_.
(trace): Move tcb existence check before threaded execve handling.
This allows to remove tcp != NULL checks in threaded execve handling.
Rewrite threaded execve handling code to be less indented,
keeping the same logic.

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/strace.c b/strace.c
index 2c792ff..1ade018 100644
--- a/strace.c
+++ b/strace.c
@@ -684,7 +684,7 @@
 		fprintf(stderr, "dropped tcb for pid %d, %d remain\n", tcp->pid, nprocs);
 
 	if (tcp->outf) {
-		if (outfname && followfork >= 2) {
+		if (followfork >= 2) {
 			if (tcp->curcol != 0)
 				fprintf(tcp->outf, " <detached ...>\n");
 			fclose(tcp->outf);
@@ -1898,68 +1898,7 @@
 		/* Look up 'pid' in our table. */
 		tcp = pid2tcb(pid);
 
-		/* Under Linux, execve changes pid to thread leader's pid,
-		 * and we see this changed pid on EVENT_EXEC and later,
-		 * execve sysexit. Leader "disappears" without exit
-		 * notification. Let user know that, drop leader's tcb,
-		 * and fix up pid in execve thread's tcb.
-		 * Effectively, execve thread's tcb replaces leader's tcb.
-		 *
-		 * BTW, leader is 'stuck undead' (doesn't report WIFEXITED
-		 * on exit syscall) in multithreaded programs exactly
-		 * in order to handle this case.
-		 *
-		 * PTRACE_GETEVENTMSG returns old pid starting from Linux 3.0.
-		 * On 2.6 and earlier, it can return garbage.
-		 */
-		if (event == PTRACE_EVENT_EXEC && os_release >= KERNEL_VERSION(3,0,0)) {
-			long old_pid = 0;
-			if (ptrace(PTRACE_GETEVENTMSG, pid, NULL, (long) &old_pid) >= 0
-			 && old_pid > 0
-			 && old_pid != pid
-			) {
-				struct tcb *execve_thread = pid2tcb(old_pid);
-				if (tcp) {
-					outf = tcp->outf;
-					curcol = tcp->curcol;
-					if (execve_thread) {
-						if (execve_thread->curcol != 0) {
-							/*
-							 * One case we are here is -ff:
-							 * try "strace -oLOG -ff test/threaded_execve"
-							 */
-							fprintf(execve_thread->outf, " <pid changed to %d ...>\n", pid);
-							execve_thread->curcol = 0;
-						}
-						/* swap output FILEs (needed for -ff) */
-						tcp->outf = execve_thread->outf;
-						tcp->curcol = execve_thread->curcol;
-						execve_thread->outf = outf;
-						execve_thread->curcol = curcol;
-					}
-					droptcb(tcp);
-				}
-				tcp = execve_thread;
-				if (tcp) {
-					tcp->pid = pid;
-					if (cflag != CFLAG_ONLY_STATS) {
-						printleader(tcp);
-						tprintf("+++ superseded by execve in pid %lu +++\n", old_pid);
-						line_ended();
-						tcp->flags |= TCB_REPRINT;
-					}
-				}
-			}
-		}
-
-		if (event == PTRACE_EVENT_EXEC && detach_on_execve) {
-			if (!skip_startup_execve)
-				detach(tcp);
-			/* This was initial execve for "strace PROG". Skip. */
-			skip_startup_execve = 0;
-		}
-
-		if (tcp == NULL) {
+		if (!tcp) {
 			if (followfork) {
 				/* This is needed to go with the CLONE_PTRACE
 				   changes in process.c/util.c: we might see
@@ -1976,17 +1915,79 @@
 				if (!qflag)
 					fprintf(stderr, "Process %d attached\n",
 						pid);
-			}
-			else
+			} else {
 				/* This can happen if a clone call used
 				   CLONE_PTRACE itself.  */
-			{
 				if (WIFSTOPPED(status))
 					ptrace(PTRACE_CONT, pid, (char *) 0, 0);
 				error_msg_and_die("Unknown pid: %u", pid);
 			}
 		}
 
+		/* Under Linux, execve changes pid to thread leader's pid,
+		 * and we see this changed pid on EVENT_EXEC and later,
+		 * execve sysexit. Leader "disappears" without exit
+		 * notification. Let user know that, drop leader's tcb,
+		 * and fix up pid in execve thread's tcb.
+		 * Effectively, execve thread's tcb replaces leader's tcb.
+		 *
+		 * BTW, leader is 'stuck undead' (doesn't report WIFEXITED
+		 * on exit syscall) in multithreaded programs exactly
+		 * in order to handle this case.
+		 *
+		 * PTRACE_GETEVENTMSG returns old pid starting from Linux 3.0.
+		 * On 2.6 and earlier, it can return garbage.
+		 */
+		if (event == PTRACE_EVENT_EXEC && os_release >= KERNEL_VERSION(3,0,0)) {
+			struct tcb *execve_thread;
+			long old_pid = 0;
+
+			if (ptrace(PTRACE_GETEVENTMSG, pid, NULL, (long) &old_pid) < 0)
+				goto dont_switch_tcbs;
+			if (old_pid <= 0 || old_pid == pid)
+				goto dont_switch_tcbs;
+			execve_thread = pid2tcb(old_pid);
+			/* It should be !NULL, but I feel paranoid */
+			if (!execve_thread)
+				goto dont_switch_tcbs;
+
+			outf = tcp->outf;
+			curcol = tcp->curcol;
+			if (execve_thread->curcol != 0) {
+				/*
+				 * One case we are here is -ff:
+				 * try "strace -oLOG -ff test/threaded_execve"
+				 */
+				fprintf(execve_thread->outf, " <pid changed to %d ...>\n", pid);
+				execve_thread->curcol = 0;
+			}
+			/* Swap output FILEs (needed for -ff) */
+			tcp->outf = execve_thread->outf;
+			tcp->curcol = execve_thread->curcol;
+			/* And their column positions */
+			execve_thread->outf = outf;
+			execve_thread->curcol = curcol;
+			/* Drop leader, but close execve'd thread outfile (if -ff) */
+			droptcb(tcp);
+			/* Switch to the thread, reusing leader's outfile and pid */
+			tcp = execve_thread;
+			tcp->pid = pid;
+			if (cflag != CFLAG_ONLY_STATS) {
+				printleader(tcp);
+				tprintf("+++ superseded by execve in pid %lu +++\n", old_pid);
+				line_ended();
+				tcp->flags |= TCB_REPRINT;
+			}
+		}
+ dont_switch_tcbs:
+
+		if (event == PTRACE_EVENT_EXEC && detach_on_execve) {
+			if (!skip_startup_execve)
+				detach(tcp);
+			/* This was initial execve for "strace PROG". Skip. */
+			skip_startup_execve = 0;
+		}
+
 		/* Set current output file */
 		outf = tcp->outf;
 		curcol = tcp->curcol;