]> granicus.if.org Git - strace/commitdiff
Do post-attach initialization earlier; fix "we ignore SIGSTOP on NOMMU" bug
authorDenys Vlasenko <dvlasenk@redhat.com>
Mon, 5 Sep 2011 12:05:46 +0000 (14:05 +0200)
committerDenys Vlasenko <dvlasenk@redhat.com>
Mon, 5 Sep 2011 12:05:46 +0000 (14:05 +0200)
We set ptrace options when we see post-attach SIGSTOP.
This is wrong: it's better to set them right away on the very first
stop (whichever it will be). It also will make adding SEIZE support easier,
since SEIZE has no post-attach SIGSTOP.

We do it by adding a new bit, TCB_IGNORE_ONE_SIGSTOP, and treating
TCB_STARTUP and TCB_IGNORE_ONE_SIGSTOP as two slightly different things.

* defs.h: Add a new flag bit, TCB_IGNORE_ONE_SIGSTOP.
* process.c (internal_fork): Set TCB_IGNORE_ONE_SIGSTOP on a newly added child.
* strace.c (startup_attach): Set TCB_IGNORE_ONE_SIGSTOP after attach.
Fix a case when "strace -p PID" found PID dead but sone other of its threads
still alive.
(startup_child): Set TCB_IGNORE_ONE_SIGSTOP after attach, _if needed_.
This fixes a bogus case where we can ignore a _real_ SIGSTOP on NOMMU.
(detach): Perform anti-SIGSTOP dance only if TCB_IGNORE_ONE_SIGSTOP is set,
not if TCB_STARTUP is set.
(trace): Set TCB_IGNORE_ONE_SIGSTOP after attach.
Clear TCB_STARTUP and initialize tracee on the very first tracee stop.
Clear TCB_IGNORE_ONE_SIGSTOP when SIGSTOP is seen.

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

diff --git a/defs.h b/defs.h
index 9c0d340a2674cd115755a88b120c2594330e9af5..98c6a0b3e613adeb0cd17142c24ca66b467a283d 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -436,8 +436,15 @@ struct tcb {
 };
 
 /* TCB flags */
-#define TCB_STARTUP    00001   /* We have just begun ptracing this process */
-#define TCB_INUSE      00002   /* This table entry is in use */
+#define TCB_INUSE              00001   /* This table entry is in use */
+/* We have attached to this process, but did not see it stopping yet.
+ * (If this bit is not set, we either didn't attach yet,
+ * or we did attach to it, already saw it stopping at least once,
+ * did some init work on it and cleared this bit. TODO: maybe it makes sense
+ * to split these two states?)
+ */
+#define TCB_STARTUP            00002
+#define TCB_IGNORE_ONE_SIGSTOP 00004   /* Next SIGSTOP is to be ignored */
 /*
  * Are we in system call entry or in syscall exit?
  *
@@ -456,8 +463,8 @@ struct tcb {
  *
  * Use entering(tcp) / exiting(tcp) to check this bit to make code more readable.
  */
-#define TCB_INSYSCALL  00004
-#define TCB_ATTACHED   00010   /* Process is not our own child */
+#define TCB_INSYSCALL  00010
+#define TCB_ATTACHED   00020   /* Process is not our own child */
 #define TCB_BPTSET     00100   /* "Breakpoint" set after fork(2) */
 #define TCB_SIGTRAPPED 00200   /* Process wanted to block SIGTRAP */
 #define TCB_REPRINT    01000   /* We should reprint this syscall on exit */
index 45d71c422248e26388e01f573962c25c22cc5c03..33033a4360e7802b547db37dfdb380fa2cd0f3bb 100644 (file)
--- a/process.c
+++ b/process.c
@@ -859,7 +859,7 @@ internal_fork(struct tcb *tcp)
                }
 #endif /* !oldway */
 #endif /* SUNOS4 */
-               tcpchild->flags |= TCB_ATTACHED | TCB_STARTUP;
+               tcpchild->flags |= TCB_ATTACHED | TCB_STARTUP | TCB_IGNORE_ONE_SIGSTOP;
                /* Child has BPT too, must be removed on first occasion */
                if (bpt) {
                        tcpchild->flags |= TCB_BPTSET;
index c911ae412ed97ff5593a07e08f33233dc0282791..84c59b7b573cf572d035ecc339c916836bf9bdf5 100644 (file)
--- a/strace.c
+++ b/strace.c
@@ -491,7 +491,7 @@ startup_attach(void)
                                        cur_tcp = tcp;
                                        if (tid != tcp->pid)
                                                cur_tcp = alloctcb(tid);
-                                       cur_tcp->flags |= TCB_ATTACHED | TCB_STARTUP;
+                                       cur_tcp->flags |= TCB_ATTACHED | TCB_STARTUP | TCB_IGNORE_ONE_SIGSTOP;
                                }
                                closedir(dir);
                                if (interactive) {
@@ -512,6 +512,13 @@ startup_attach(void)
 : "Process %u attached - interrupt to quit\n",
                                                tcp->pid, ntid);
                                }
+                               if (!(tcp->flags & TCB_STARTUP)) {
+                                       /* -p PID, we failed to attach to PID itself
+                                        * but did attach to some of its sibling threads.
+                                        * Drop PID's tcp.
+                                        */
+                                       droptcb(tcp);
+                               }
                                continue;
                        } /* if (opendir worked) */
                } /* if (-f) */
@@ -521,7 +528,7 @@ startup_attach(void)
                        droptcb(tcp);
                        continue;
                }
-               tcp->flags |= TCB_STARTUP;
+               tcp->flags |= TCB_STARTUP | TCB_IGNORE_ONE_SIGSTOP;
                if (debug)
                        fprintf(stderr, "attach to pid %d (main) succeeded\n", tcp->pid);
 
@@ -707,7 +714,10 @@ startup_child(char **argv)
 
        if (!daemonized_tracer) {
                tcp = alloctcb(pid);
-               tcp->flags |= TCB_STARTUP;
+               if (!strace_vforked)
+                       tcp->flags |= TCB_STARTUP | TCB_IGNORE_ONE_SIGSTOP;
+               else
+                       tcp->flags |= TCB_STARTUP;
        }
        else {
                /* With -D, *we* are child here, IOW: different pid. Fetch it: */
@@ -1677,11 +1687,11 @@ detach(struct tcb *tcp, int sig)
 #define PTRACE_DETACH PTRACE_SUNDETACH
 #endif
        /*
-        * On TCB_STARTUP we did PTRACE_ATTACH but still did not get the
-        * expected SIGSTOP.  We must catch exactly one as otherwise the
-        * detached process would be left stopped (process state T).
+        * We did PTRACE_ATTACH 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).
         */
-       catch_sigstop = (tcp->flags & TCB_STARTUP);
+       catch_sigstop = (tcp->flags & TCB_IGNORE_ONE_SIGSTOP);
        error = ptrace(PTRACE_DETACH, tcp->pid, (char *) 1, sig);
        if (error == 0) {
                /* On a clear day, you can see forever. */
@@ -2411,7 +2421,7 @@ trace()
                                   child so that we know how to do clearbpt
                                   in the child.  */
                                tcp = alloctcb(pid);
-                               tcp->flags |= TCB_ATTACHED | TCB_STARTUP;
+                               tcp->flags |= TCB_ATTACHED | TCB_STARTUP | TCB_IGNORE_ONE_SIGSTOP;
                                if (!qflag)
                                        fprintf(stderr, "Process %d attached\n",
                                                pid);
@@ -2478,28 +2488,10 @@ trace()
                        continue;
                }
 
-               if (status >> 16) {
-                       /* Ptrace event (we ignore all of them for now) */
-                       goto restart_tracee_with_sig_0;
-               }
-
-               sig = WSTOPSIG(status);
-
-               /*
-                * Interestingly, the process may stop
-                * with STOPSIG equal to some other signal
-                * than SIGSTOP if we happend to attach
-                * just before the process takes a signal.
-                * A no-MMU vforked child won't send up a signal,
-                * so skip the first (lost) execve notification.
-                */
-               if ((tcp->flags & TCB_STARTUP) &&
-                   (sig == SIGSTOP || strace_vforked)) {
-                       /*
-                        * This flag is there to keep us in sync.
-                        * Next time this process stops it should
-                        * really be entering a system call.
-                        */
+               /* Is this the very first time we see this tracee stopped? */
+               if (tcp->flags & TCB_STARTUP) {
+                       if (debug)
+                               fprintf(stderr, "pid %d has TCB_STARTUP, initializing it\n", tcp->pid);
                        tcp->flags &= ~TCB_STARTUP;
                        if (tcp->flags & TCB_BPTSET) {
                                /*
@@ -2525,6 +2517,25 @@ trace()
                                }
                        }
 #endif
+               }
+
+               if (((unsigned)status >> 16) != 0) {
+                       /* Ptrace event (we ignore all of them for now) */
+                       goto restart_tracee_with_sig_0;
+               }
+
+               sig = WSTOPSIG(status);
+
+               /* Is this post-attach SIGSTOP?
+                * Interestingly, the process may stop
+                * with STOPSIG equal to some other signal
+                * than SIGSTOP if we happend to attach
+                * just before the process takes a signal.
+                */
+               if (sig == SIGSTOP && (tcp->flags & TCB_IGNORE_ONE_SIGSTOP)) {
+                       if (debug)
+                               fprintf(stderr, "ignored SIGSTOP on pid %d\n", tcp->pid);
+                       tcp->flags &= ~TCB_IGNORE_ONE_SIGSTOP;
                        goto restart_tracee_with_sig_0;
                }