From: Dmitry V. Levin Date: Wed, 10 Jul 2019 16:12:44 +0000 (+0000) Subject: Refactor maybe_switch_tcbs X-Git-Tag: v5.2~12 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d342f4753a1bba4593b00b8857df290a20e404bf;p=strace Refactor maybe_switch_tcbs Change maybe_switch_tcbs to return NULL when no switching is necessary. Introduce maybe_switch_current_tcp as a thing wrapper around maybe_switch_tcbs. * strace.c (maybe_switch_current_tcp): New function. (dispatch_event): Use it instead of maybe_switch_tcbs, move comments and the os_release check before maybe_switch_tcbs invocation ... (maybe_switch_tcbs): ... here. Change return value to NULL if no switching was performed. --- diff --git a/strace.c b/strace.c index f7dfdaa5..d4c1fa87 100644 --- a/strace.c +++ b/strace.c @@ -2029,33 +2029,48 @@ maybe_allocate_tcb(const int pid, int status) } } +/* + * 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 multi-threaded programs exactly in order to handle this case. + */ static struct tcb * maybe_switch_tcbs(struct tcb *tcp, const int pid) { - FILE *fp; - struct tcb *execve_thread; - long old_pid = tcb_wait_tab[tcp->wait_data_idx].msg; + /* + * PTRACE_GETEVENTMSG returns old pid starting from Linux 3.0. + * On 2.6 and earlier it can return garbage. + */ + if (os_release < KERNEL_VERSION(3, 0, 0)) + return NULL; + + const long old_pid = tcb_wait_tab[tcp->wait_data_idx].msg; /* Avoid truncation in pid2tcb() param passing */ if (old_pid <= 0 || old_pid == pid) - return tcp; + return NULL; if ((unsigned long) old_pid > UINT_MAX) - return tcp; - execve_thread = pid2tcb(old_pid); + return NULL; + struct tcb *execve_thread = pid2tcb(old_pid); /* It should be !NULL, but I feel paranoid */ if (!execve_thread) - return tcp; + return NULL; if (execve_thread->curcol != 0) { /* - * One case we are here is -ff: - * try "strace -oLOG -ff test/threaded_execve" + * One case we are here is -ff, try + * "strace -oLOG -ff test/threaded_execve". */ fprintf(execve_thread->outf, " \n", pid); /*execve_thread->curcol = 0; - no need, see code below */ } /* Swap output FILEs (needed for -ff) */ - fp = execve_thread->outf; + FILE *fp = execve_thread->outf; execve_thread->outf = tcp->outf; tcp->outf = fp; /* And their column positions */ @@ -2076,6 +2091,17 @@ maybe_switch_tcbs(struct tcb *tcp, const int pid) return tcp; } +static struct tcb * +maybe_switch_current_tcp(void) +{ + struct tcb *tcp = maybe_switch_tcbs(current_tcp, current_tcp->pid); + + if (tcp) + set_current_tcp(tcp); + + return tcp; +} + static void print_signalled(struct tcb *tcp, const int pid, int status) { @@ -2636,24 +2662,7 @@ dispatch_event(const struct tcb_wait_data *wd) } } - /* - * 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 (os_release >= KERNEL_VERSION(3, 0, 0)) - set_current_tcp(maybe_switch_tcbs(current_tcp, - current_tcp->pid)); + maybe_switch_current_tcp(); if (detach_on_execve) { if (current_tcp->flags & TCB_SKIP_DETACH_ON_FIRST_EXEC) {