]> granicus.if.org Git - strace/commitdiff
detach(): Reorganize code. Logic is the same.
authorDenys Vlasenko <dvlasenk@redhat.com>
Fri, 21 Jun 2013 14:11:10 +0000 (16:11 +0200)
committerDenys Vlasenko <dvlasenk@redhat.com>
Fri, 21 Jun 2013 14:11:10 +0000 (16:11 +0200)
* strace.c (detach): Use goto's instead of excessive nesting.
Drop sigstop_expected and interrupt_done variables.

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
strace.c

index 55089bf3be744b755110a67dc5e8eb417200910d..6e9ea91afb1b73fb7c343b6d6cd5e3da41da0d71 100644 (file)
--- 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 <stopped_process>".
                 */
-               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 <stopped_process>".
-                                */
-                               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);