From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mark Kettenis To: gdb-patches@sources.redhat.com Subject: [PATCH] Fix attaching to multi-threaded apps on Linux/x86 Date: Sun, 14 Oct 2001 04:32:00 -0000 Message-id: <200110141132.f9EBW1C04131@delius.kettenis.local> X-SW-Source: 2001-10/msg00198.html I checked in the attached. In principle the change I made to config/i386/nm-linux.h should also be made to the other Linux ports, and really belongs in config/nm-linux.h. Unfortunately sparc and m68k still haven't been converted to using the new threads code. I cannot test those configurations, but I am thinking of converting them anyway. Any opinions on that? Mark Index: ChangeLog from 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. Index: lin-lwp.c =================================================================== RCS file: /cvs/src/src/gdb/lin-lwp.c,v retrieving revision 1.29 diff -u -p -r1.29 lin-lwp.c --- lin-lwp.c 2001/07/13 12:49:31 1.29 +++ lin-lwp.c 2001/10/14 11:26:23 @@ -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 ver 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,21 +389,34 @@ 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); lp->resumed = 1; @@ -415,7 +446,9 @@ detach_callback (struct lwp_info *lp, vo 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, 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, v 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 /* 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 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, /* 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 { Index: config/i386/nm-linux.h =================================================================== RCS file: /cvs/src/src/gdb/config/i386/nm-linux.h,v retrieving revision 1.13 diff -u -p -r1.13 nm-linux.h --- config/i386/nm-linux.h 2001/07/14 11:55:29 1.13 +++ config/i386/nm-linux.h 2001/10/14 11:26:23 @@ -87,6 +87,11 @@ extern int cannot_store_register (int re /* 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). */