From: Denys Vlasenko Date: Mon, 5 Sep 2011 12:05:46 +0000 (+0200) Subject: Do post-attach initialization earlier; fix "we ignore SIGSTOP on NOMMU" bug X-Git-Tag: v4.7~256 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f88837a666a716aecd9974f7ef3fcf006e0afce1;p=strace Do post-attach initialization earlier; fix "we ignore SIGSTOP on NOMMU" bug 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 --- diff --git a/defs.h b/defs.h index 9c0d340a..98c6a0b3 100644 --- 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 */ diff --git a/process.c b/process.c index 45d71c42..33033a43 100644 --- 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; diff --git a/strace.c b/strace.c index c911ae41..84c59b7b 100644 --- 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; }