From: Mark Kettenis Date: Sun, 14 Oct 2001 11:30:37 +0000 (+0000) Subject: Fix attaching to cloned processes. This fixes PR gdb/61. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=cacab7c47722fd1c63c95d4c4a5b761ba6823632;p=binutils-gdb.git Fix attaching to cloned processes. This fixes PR gdb/61. * lin-lwp.c (struct lwp_info): Add new member `cloned'. (is_cloned) Removed. (lin_lwp_attach_lwp): Don't call stop_wait_callback. Instead call waitpid explicitly. Mark the LWP as cloned if waitpid fails and retry with __WCLONE flag. (lin_lwp_attach): Likewise. Warn if attaching to a cloned process. (detach_callback): Replace use of is_cloned with explicit check on LWP id and process id. (stop_wait_callback): Replace use of is_cloned with check if LWP is marked as cloned. [CHILD_WAIT] (child_wait): New function. (lin_lwp_wait): Replace use of is_cloned with check if LWP is marked as cloned. Mark newly detected LWPs as cloned if detected by waitpid with __WCLONE flag. (kill_wait_callback): Replace use of is_cloned with check if LWP is marked as cloned. * config/i386/nm-linux.h (struct target_waitstatus): Add forward declaration. (child_wait): Add prototype. (CHILD_WAIT): Define. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 40e45e93d6a..6cefdb22e79 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,27 @@ +2001-10-14 Mark Kettenis + + Fix attaching to cloned processes. This fixes PR gdb/61. + * lin-lwp.c (struct lwp_info): Add new member `cloned'. + (is_cloned) Removed. + (lin_lwp_attach_lwp): Don't call stop_wait_callback. Instead call + waitpid explicitly. Mark the LWP as cloned if waitpid fails and + retry with __WCLONE flag. + (lin_lwp_attach): Likewise. Warn if attaching to a cloned process. + (detach_callback): Replace use of is_cloned with explicit check on + LWP id and process id. + (stop_wait_callback): Replace use of is_cloned with check if LWP + is marked as cloned. + [CHILD_WAIT] (child_wait): New function. + (lin_lwp_wait): Replace use of is_cloned with check if LWP is + marked as cloned. Mark newly detected LWPs as cloned if detected + by waitpid with __WCLONE flag. + (kill_wait_callback): Replace use of is_cloned with check if LWP + is marked as cloned. + * config/i386/nm-linux.h (struct target_waitstatus): Add forward + declaration. + (child_wait): Add prototype. + (CHILD_WAIT): Define. + 2001-10-13 Andrew Cagney S/390 31 & 64 bit target and GNU/Linux native support. diff --git a/gdb/config/i386/nm-linux.h b/gdb/config/i386/nm-linux.h index ab42ee59e56..089cb1ba47c 100644 --- a/gdb/config/i386/nm-linux.h +++ b/gdb/config/i386/nm-linux.h @@ -87,6 +87,11 @@ extern int cannot_store_register (int regno); /* Override child_resume in `infptrace.c'. */ #define CHILD_RESUME +/* Override child_wait in `inftarg.c'. */ +struct target_waitstatus; +extern ptid_t child_wait (ptid_t ptid, struct target_waitstatus *ourstatus); +#define CHILD_WAIT + /* FIXME: kettenis/2000-09-03: This should be moved to ../nm-linux.h once we have converted all Linux targets to use the new threads stuff (without the #undef of course). */ diff --git a/gdb/lin-lwp.c b/gdb/lin-lwp.c index 385b235f37e..22d9d74796c 100644 --- a/gdb/lin-lwp.c +++ b/gdb/lin-lwp.c @@ -75,6 +75,11 @@ struct lwp_info and overall process id. */ ptid_t ptid; + /* Non-zero if this LWP is cloned. In this context "cloned" means + that the LWP is reporting to its parent using a signal other than + SIGCHLD. */ + int cloned; + /* Non-zero if we sent this LWP a SIGSTOP (but the LWP didn't report it back yet). */ int signalled; @@ -115,8 +120,6 @@ static int threaded; #define is_lwp(ptid) (GET_LWP (ptid) != 0) #define BUILD_LWP(lwp, pid) ptid_build (pid, lwp, 0) -#define is_cloned(pid) (GET_LWP (pid) != GET_PID (pid)) - /* If the last reported event was a SIGTRAP, this variable is set to the process id of the LWP/thread that got it. */ ptid_t trap_ptid; @@ -352,18 +355,33 @@ lin_lwp_attach_lwp (ptid_t ptid, int verbose) if (verbose) printf_filtered ("[New %s]\n", target_pid_to_str (ptid)); - /* We assume that we're already tracing the initial process. */ - if (is_cloned (ptid) && ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0) - error ("Can't attach %s: %s", target_pid_to_str (ptid), strerror (errno)); - lp = find_lwp_pid (ptid); if (lp == NULL) lp = add_lwp (ptid); - if (is_cloned (ptid)) + /* We assume that we're already attached to any LWP that has an + id equal to the overall process id. */ + if (GET_LWP (ptid) != GET_PID (ptid)) { - lp->signalled = 1; - stop_wait_callback (lp, NULL); + pid_t pid; + int status; + + if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0) + error ("Can't attach %s: %s", target_pid_to_str (ptid), + strerror (errno)); + + pid = waitpid (GET_LWP (ptid), &status, 0); + if (pid == -1 && errno == ECHILD) + { + /* Try again with __WCLONE to check cloned processes. */ + pid = waitpid (GET_LWP (ptid), &status, __WCLONE); + lp->cloned = 1; + } + + gdb_assert (pid == GET_LWP (ptid) + && WIFSTOPPED (status) && WSTOPSIG (status)); + + lp->stopped = 1; } } @@ -371,20 +389,33 @@ static void lin_lwp_attach (char *args, int from_tty) { struct lwp_info *lp; + pid_t pid; + int status; /* FIXME: We should probably accept a list of process id's, and attach all of them. */ child_ops.to_attach (args, from_tty); /* Add the initial process as the first LWP to the list. */ - lp = add_lwp (BUILD_LWP (PIDGET (inferior_ptid), PIDGET (inferior_ptid))); + lp = add_lwp (BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid))); /* Make sure the initial process is stopped. The user-level threads layer might want to poke around in the inferior, and that won't work if things haven't stabilized yet. */ - lp->signalled = 1; - stop_wait_callback (lp, NULL); - gdb_assert (lp->status == 0); + pid = waitpid (GET_PID (inferior_ptid), &status, 0); + if (pid == -1 && errno == ECHILD) + { + warning ("%s is a cloned process", target_pid_to_str (inferior_ptid)); + + /* Try again with __WCLONE to check cloned processes. */ + pid = waitpid (GET_PID (inferior_ptid), &status, __WCLONE); + lp->cloned = 1; + } + + gdb_assert (pid == GET_PID (inferior_ptid) + && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP); + + lp->stopped = 1; /* Fake the SIGSTOP that core GDB expects. */ lp->status = W_STOPCODE (SIGSTOP); @@ -415,7 +446,9 @@ detach_callback (struct lwp_info *lp, void *data) gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status)); } - if (is_cloned (lp->ptid)) + /* We don't actually detach from the LWP that has an id equal to the + overall process id just yet. */ + if (GET_LWP (lp->ptid) != GET_PID (lp->ptid)) { if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0, WSTOPSIG (lp->status)) < 0) @@ -433,7 +466,7 @@ lin_lwp_detach (char *args, int from_tty) { iterate_over_lwps (detach_callback, NULL); - /* Only the initial (uncloned) process should be left right now. */ + /* Only the initial process should be left right now. */ gdb_assert (num_lwps == 1); trap_ptid = null_ptid; @@ -610,8 +643,7 @@ stop_wait_callback (struct lwp_info *lp, void *data) gdb_assert (lp->status == 0); - pid = waitpid (GET_LWP (lp->ptid), &status, - is_cloned (lp->ptid) ? __WCLONE : 0); + pid = waitpid (GET_LWP (lp->ptid), &status, lp->cloned ? __WCLONE : 0); if (pid == -1 && errno == ECHILD) /* OK, the proccess has disappeared. We'll catch the actual exit event in lin_lwp_wait. */ @@ -888,6 +920,55 @@ resumed_callback (struct lwp_info *lp, void *data) return lp->resumed; } +#ifdef CHILD_WAIT + +/* We need to override child_wait to support attaching to cloned + processes, since a normal wait (as done by the default version) + ignores those processes. */ + +/* Wait for child PTID to do something. Return id of the child, + minus_one_ptid in case of error; store status into *OURSTATUS. */ + +ptid_t +child_wait (ptid_t ptid, struct target_waitstatus *ourstatus) +{ + int save_errno; + int status; + pid_t pid; + + do + { + set_sigint_trap (); /* Causes SIGINT to be passed on to the + attached process. */ + set_sigio_trap (); + + pid = waitpid (GET_PID (ptid), &status, 0); + if (pid == -1 && errno == ECHILD) + /* Try again with __WCLONE to check cloned processes. */ + pid = waitpid (GET_PID (ptid), &status, __WCLONE); + save_errno = errno; + + clear_sigio_trap (); + clear_sigint_trap (); + } + while (pid == -1 && errno == EINTR); + + if (pid == -1) + { + warning ("Child process unexpectedly missing: %s", strerror (errno)); + + /* Claim it exited with unknown signal. */ + ourstatus->kind = TARGET_WAITKIND_SIGNALLED; + ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; + return minus_one_ptid; + } + + store_waitstatus (ourstatus, status); + return pid_to_ptid (pid); +} + +#endif + static ptid_t lin_lwp_wait (ptid_t ptid, struct target_waitstatus *ourstatus) { @@ -954,7 +1035,7 @@ lin_lwp_wait (ptid_t ptid, struct target_waitstatus *ourstatus) /* If we have to wait, take into account whether PID is a cloned process or not. And we have to convert it to something that the layer beneath us can understand. */ - options = is_cloned (lp->ptid) ? __WCLONE : 0; + options = lp->cloned ? __WCLONE : 0; pid = GET_LWP (ptid); } @@ -997,6 +1078,9 @@ lin_lwp_wait (ptid_t ptid, struct target_waitstatus *ourstatus) if (! lp) { lp = add_lwp (BUILD_LWP (lwpid, GET_PID (inferior_ptid))); + if (options & __WCLONE) + lp->cloned = 1; + if (threaded) { gdb_assert (WIFSTOPPED (status) @@ -1189,7 +1273,7 @@ kill_wait_callback (struct lwp_info *lp, void *data) /* For cloned processes we must check both with __WCLONE and without, since the exit status of a cloned process isn't reported with __WCLONE. */ - if (is_cloned (lp->ptid)) + if (lp->cloned) { do {