From: Denys Vlasenko Date: Fri, 21 Jun 2013 14:11:10 +0000 (+0200) Subject: detach(): Reorganize code. Logic is the same. X-Git-Tag: v4.9~229 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e2567d54dd57438d8ebe0167748cde7b661103e8;p=strace detach(): Reorganize code. Logic is the same. * strace.c (detach): Use goto's instead of excessive nesting. Drop sigstop_expected and interrupt_done variables. Signed-off-by: Denys Vlasenko --- diff --git a/strace.c b/strace.c index 55089bf3..6e9ea91a 100644 --- a/strace.c +++ b/strace.c @@ -733,7 +733,7 @@ static void detach(struct tcb *tcp) { int error; - int status, sigstop_expected, interrupt_done; + int status; if (tcp->flags & TCB_BPTSET) clearbpt(tcp); @@ -748,142 +748,145 @@ detach(struct tcb *tcp) # define PTRACE_DETACH PTRACE_SUNDETACH #endif - error = 0; - sigstop_expected = 0; - interrupt_done = 0; - if (tcp->flags & TCB_ATTACHED) { + if (!(tcp->flags & TCB_ATTACHED)) + goto drop; + + /* We attached but possibly didn't see the expected SIGSTOP. + * We must catch exactly one as otherwise the detached process + * would be left stopped (process state T). + */ + if (tcp->flags & TCB_IGNORE_ONE_SIGSTOP) + goto wait_loop; + + error = ptrace(PTRACE_DETACH, tcp->pid, 0, 0); + if (!error) { + /* On a clear day, you can see forever. */ + goto drop; + } + if (errno != ESRCH) { + /* Shouldn't happen. */ + perror_msg("detach: ptrace(PTRACE_DETACH,%u)", tcp->pid); + goto drop; + } + /* ESRCH: process is either not stopped or doesn't exist. */ + if (my_tkill(tcp->pid, 0) < 0) { + if (errno != ESRCH) + /* Shouldn't happen. */ + perror_msg("detach: tkill(%u,0)", tcp->pid); + /* else: process doesn't exist. */ + goto drop; + } + /* Process is not stopped, need to stop it. */ + if (use_seize) { /* - * We attached but possibly didn't see the expected SIGSTOP. - * We must catch exactly one as otherwise the detached process - * would be left stopped (process state T). + * With SEIZE, tracee can be in group-stop already. + * In this state sending it another SIGSTOP does nothing. + * Need to use INTERRUPT. + * Testcase: trying to ^C a "strace -p ". */ - sigstop_expected = (tcp->flags & TCB_IGNORE_ONE_SIGSTOP); - if (sigstop_expected) + error = ptrace(PTRACE_INTERRUPT, tcp->pid, 0, 0); + if (!error) goto wait_loop; - error = ptrace(PTRACE_DETACH, tcp->pid, 0, 0); - if (error == 0) { - /* On a clear day, you can see forever. */ - } - else if (errno != ESRCH) { - /* Shouldn't happen. */ - perror_msg("detach: ptrace(PTRACE_DETACH,%u)", tcp->pid); - } - else - /* ESRCH: process is either not stopped or doesn't exist. */ - if (my_tkill(tcp->pid, 0) < 0) { - if (errno != ESRCH) - /* Shouldn't happen. */ - perror_msg("detach: tkill(%u,0)", tcp->pid); - /* else: process doesn't exist. */ - } - else - /* Process is not stopped. */ - if (!sigstop_expected) { - /* We need to stop it. */ - if (use_seize) { - /* - * With SEIZE, tracee can be in group-stop already. - * In this state sending it another SIGSTOP does nothing. - * Need to use INTERRUPT. - * Testcase: trying to ^C a "strace -p ". - */ - error = ptrace(PTRACE_INTERRUPT, tcp->pid, 0, 0); - if (!error) - interrupt_done = 1; - else if (errno != ESRCH) - perror_msg("detach: ptrace(PTRACE_INTERRUPT,%u)", tcp->pid); - } - else { - error = my_tkill(tcp->pid, SIGSTOP); - if (!error) - sigstop_expected = 1; - else if (errno != ESRCH) - perror_msg("detach: tkill(%u,SIGSTOP)", tcp->pid); - } - } + if (errno != ESRCH) + perror_msg("detach: ptrace(PTRACE_INTERRUPT,%u)", tcp->pid); } + else { + error = my_tkill(tcp->pid, SIGSTOP); + if (!error) + goto wait_loop; + if (errno != ESRCH) + perror_msg("detach: tkill(%u,SIGSTOP)", tcp->pid); + } + /* Either process doesn't exist, or some weird error. */ + goto drop; - if (sigstop_expected || interrupt_done) { wait_loop: - for (;;) { - int sig; - if (waitpid(tcp->pid, &status, __WALL) < 0) { - if (errno == EINTR) - continue; - /* - * if (errno == ECHILD) break; - * ^^^ WRONG! We expect this PID to exist, - * and want to emit a message otherwise: - */ - perror_msg("detach: waitpid(%u)", tcp->pid); - break; - } - if (!WIFSTOPPED(status)) { - /* - * Tracee exited or was killed by signal. - * We shouldn't normally reach this place: - * we don't want to consume exit status. - * Consider "strace -p PID" being ^C-ed: - * we want merely to detach from PID. - * - * However, we _can_ end up here if tracee - * was SIGKILLed. - */ - break; - } - sig = WSTOPSIG(status); - if (debug_flag) - fprintf(stderr, "detach wait: event:%d sig:%d\n", - (unsigned)status >> 16, sig); - if (sigstop_expected && sig == SIGSTOP) { - /* Detach, suppressing SIGSTOP */ - ptrace_restart(PTRACE_DETACH, tcp, 0); - break; - } - if (interrupt_done) { - unsigned event = (unsigned)status >> 16; - if (event == PTRACE_EVENT_STOP /*&& sig == SIGTRAP*/) { - /* - * sig == SIGTRAP: PTRACE_INTERRUPT stop. - * sig == other: process was already stopped - * with this stopping sig (see tests/detach-stopped). - * Looks like re-injecting this sig is not necessary - * in DETACH for the tracee to remain stopped. - */ - sig = 0; - } + /* We end up here in three cases: + * 1. We sent PTRACE_INTERRUPT (use_seize case) + * 2. We sent SIGSTOP (!use_seize) + * 3. Attach SIGSTOP was already pending (TCB_IGNORE_ONE_SIGSTOP set) + */ + for (;;) { + int sig; + if (waitpid(tcp->pid, &status, __WALL) < 0) { + if (errno == EINTR) + continue; + /* + * if (errno == ECHILD) break; + * ^^^ WRONG! We expect this PID to exist, + * and want to emit a message otherwise: + */ + perror_msg("detach: waitpid(%u)", tcp->pid); + break; + } + if (!WIFSTOPPED(status)) { + /* + * Tracee exited or was killed by signal. + * We shouldn't normally reach this place: + * we don't want to consume exit status. + * Consider "strace -p PID" being ^C-ed: + * we want merely to detach from PID. + * + * However, we _can_ end up here if tracee + * was SIGKILLed. + */ + break; + } + sig = WSTOPSIG(status); + if (debug_flag) + fprintf(stderr, "detach wait: event:%d sig:%d\n", + (unsigned)status >> 16, sig); + if (use_seize) { + unsigned event = (unsigned)status >> 16; + if (event == PTRACE_EVENT_STOP /*&& sig == SIGTRAP*/) { /* - * PTRACE_INTERRUPT is not guaranteed to produce - * the above event if other ptrace-stop is pending. - * See tests/detach-sleeping testcase: - * strace got SIGINT while tracee is sleeping. - * We sent PTRACE_INTERRUPT. - * We see syscall exit, not PTRACE_INTERRUPT stop. - * We won't get PTRACE_INTERRUPT stop - * if we would CONT now. Need to DETACH. - */ - if (sig == syscall_trap_sig) - sig = 0; - /* else: not sure in which case we can be here. - * Signal stop? Inject it while detaching. + * sig == SIGTRAP: PTRACE_INTERRUPT stop. + * sig == other: process was already stopped + * with this stopping sig (see tests/detach-stopped). + * Looks like re-injecting this sig is not necessary + * in DETACH for the tracee to remain stopped. */ - ptrace_restart(PTRACE_DETACH, tcp, sig); - break; + sig = 0; } + /* + * PTRACE_INTERRUPT is not guaranteed to produce + * the above event if other ptrace-stop is pending. + * See tests/detach-sleeping testcase: + * strace got SIGINT while tracee is sleeping. + * We sent PTRACE_INTERRUPT. + * We see syscall exit, not PTRACE_INTERRUPT stop. + * We won't get PTRACE_INTERRUPT stop + * if we would CONT now. Need to DETACH. + */ if (sig == syscall_trap_sig) sig = 0; - /* Can't detach just yet, may need to wait for SIGSTOP */ - error = ptrace_restart(PTRACE_CONT, tcp, sig); - if (error < 0) { - /* Should not happen. - * Note: ptrace_restart returns 0 on ESRCH, so it's not it. - * ptrace_restart already emitted error message. - */ - break; - } + /* else: not sure in which case we can be here. + * Signal stop? Inject it while detaching. + */ + ptrace_restart(PTRACE_DETACH, tcp, sig); + break; + } + /* Note: this check has to be after use_seize check */ + /* (else, in use_seize case SIGSTOP will be mistreated) */ + if (sig == SIGSTOP) { + /* Detach, suppressing SIGSTOP */ + ptrace_restart(PTRACE_DETACH, tcp, 0); + break; + } + if (sig == syscall_trap_sig) + sig = 0; + /* Can't detach just yet, may need to wait for SIGSTOP */ + error = ptrace_restart(PTRACE_CONT, tcp, sig); + if (error < 0) { + /* Should not happen. + * Note: ptrace_restart returns 0 on ESRCH, so it's not it. + * ptrace_restart already emitted error message. + */ + break; } } + drop: if (!qflag && (tcp->flags & TCB_ATTACHED)) fprintf(stderr, "Process %u detached\n", tcp->pid);