From a2f23071c92ee8e0ad72cef70e82af6192b48ba9 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Mon, 29 Mar 2004 18:07:14 +0000 Subject: [PATCH] * Makefile.in (linux_nat_h): Update dependencies. * configure.in: Check for . * configure: Regenerate. * config.in: Regenerate. * linux-nat.h: Include "target.h". Add waitstatus field to struct lwp_info. * lin-lwp.c (add_lwp): Initialize waitstatus.kind. (lin_lwp_attach_lwp): Don't attach to LWPs we have already attached to. (lin_lwp_handle_extended): New function. Handle clone events. (wait_lwp): Use lin_lwp_handle_extended. Update comment about thread exit events. (child_wait): Handle clone events. (lin_lwp_wait: Use lin_lwp_handle_extended and handle clone events. * linux-nat.c (linux_enable_event_reporting): Turn on PTRACE_O_TRACECLONE. (linux_handle_extended_wait): Handle clone events. * thread-db.c: Include . (struct private_thread_info): Add dying flag. (enable_thread_event_reporting): Enable TD_DEATH for glibc 2.2 and higher. (attach_thread): Update comments. Handle dying threads. (detach_thread): Set the dying flag. (check_event): Always call attach_thread. --- gdb/ChangeLog | 27 +++++++++++ gdb/Makefile.in | 2 +- gdb/config.in | 3 ++ gdb/configure | 2 +- gdb/configure.in | 2 +- gdb/lin-lwp.c | 123 ++++++++++++++++++++++++++++++++++++++++------- gdb/linux-nat.c | 29 +++++------ gdb/linux-nat.h | 10 +++- gdb/thread-db.c | 69 ++++++++++++++++++++++---- 9 files changed, 221 insertions(+), 46 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1713275f33..cf32cf6023 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,30 @@ +2004-03-29 Daniel Jacobowitz + + * Makefile.in (linux_nat_h): Update dependencies. + * configure.in: Check for . + * configure: Regenerate. + * config.in: Regenerate. + * linux-nat.h: Include "target.h". Add waitstatus field to + struct lwp_info. + * lin-lwp.c (add_lwp): Initialize waitstatus.kind. + (lin_lwp_attach_lwp): Don't attach to LWPs we have already attached + to. + (lin_lwp_handle_extended): New function. Handle clone events. + (wait_lwp): Use lin_lwp_handle_extended. Update comment about + thread exit events. + (child_wait): Handle clone events. + (lin_lwp_wait: Use lin_lwp_handle_extended and handle clone events. + * linux-nat.c (linux_enable_event_reporting): Turn on + PTRACE_O_TRACECLONE. + (linux_handle_extended_wait): Handle clone events. + * thread-db.c: Include . + (struct private_thread_info): Add dying flag. + (enable_thread_event_reporting): Enable TD_DEATH for glibc 2.2 and + higher. + (attach_thread): Update comments. Handle dying threads. + (detach_thread): Set the dying flag. + (check_event): Always call attach_thread. + 2004-03-29 Daniel Jacobowitz * mips-tdep.c (mips_pdr_data): New. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index b78d46cd7f..555badcb24 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -699,7 +699,7 @@ kod_h = kod.h language_h = language.h libunwind_frame_h = libunwind-frame.h $(libunwind_h) linespec_h = linespec.h -linux_nat_h = linux-nat.h +linux_nat_h = linux-nat.h $(target_h) m2_lang_h = m2-lang.h m68k_tdep_h = m68k-tdep.h macroexp_h = macroexp.h diff --git a/gdb/config.in b/gdb/config.in index 69982fd6b9..a1c2b0fc5c 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -266,6 +266,9 @@ /* Define if you have the header file. */ #undef HAVE_DIRENT_H +/* Define if you have the header file. */ +#undef HAVE_GNU_LIBC_VERSION_H + /* Define if you have the header file. */ #undef HAVE_LIBUNWIND_IA64_H diff --git a/gdb/configure b/gdb/configure index e4de4c9408..bc8ccbb794 100755 --- a/gdb/configure +++ b/gdb/configure @@ -4756,7 +4756,7 @@ else fi done -for ac_hdr in proc_service.h thread_db.h +for ac_hdr in proc_service.h thread_db.h gnu/libc-version.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 diff --git a/gdb/configure.in b/gdb/configure.in index 07a289fb00..2781b41f73 100644 --- a/gdb/configure.in +++ b/gdb/configure.in @@ -342,7 +342,7 @@ AC_CHECK_HEADERS(link.h) AC_CHECK_HEADERS(machine/reg.h) AC_CHECK_HEADERS(nlist.h) AC_CHECK_HEADERS(poll.h sys/poll.h) -AC_CHECK_HEADERS(proc_service.h thread_db.h) +AC_CHECK_HEADERS(proc_service.h thread_db.h gnu/libc-version.h) AC_CHECK_HEADERS(stddef.h) AC_CHECK_HEADERS(stdlib.h) AC_CHECK_HEADERS(stdint.h) diff --git a/gdb/lin-lwp.c b/gdb/lin-lwp.c index 0d8d1ff6a0..23cf2c4a9b 100644 --- a/gdb/lin-lwp.c +++ b/gdb/lin-lwp.c @@ -1,5 +1,5 @@ /* Multi-threaded debugging support for GNU/Linux (LWP layer). - Copyright 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + Copyright 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of GDB. @@ -183,6 +183,8 @@ add_lwp (ptid_t ptid) memset (lp, 0, sizeof (struct lwp_info)); + lp->waitstatus.kind = TARGET_WAITKIND_IGNORE; + lp->ptid = ptid; lp->next = lwp_list; @@ -278,7 +280,7 @@ lin_lwp_open (char *args, int from_tty) void lin_lwp_attach_lwp (ptid_t ptid, int verbose) { - struct lwp_info *lp; + struct lwp_info *lp, *found_lp; gdb_assert (is_lwp (ptid)); @@ -293,13 +295,17 @@ lin_lwp_attach_lwp (ptid_t ptid, int verbose) if (verbose) printf_filtered ("[New %s]\n", target_pid_to_str (ptid)); - lp = find_lwp_pid (ptid); + found_lp = lp = find_lwp_pid (ptid); if (lp == NULL) lp = add_lwp (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)) + /* We assume that we're already attached to any LWP that has an id + equal to the overall process id, and to any LWP that is already + in our list of LWPs. If we're not seeing exit events from threads + and we've had PID wraparound since we last tried to stop all threads, + this assumption might be wrong; fortunately, this is very unlikely + to happen. */ + if (GET_LWP (ptid) != GET_PID (ptid) && found_lp == NULL) { pid_t pid; int status; @@ -590,6 +596,41 @@ kill_lwp (int lwpid, int signo) return kill (lwpid, signo); } +/* Handle a GNU/Linux extended wait response. Most of the work we + just pass off to linux_handle_extended_wait, but if it reports a + clone event we need to add the new LWP to our list (and not report + the trap to higher layers). This function returns non-zero if + the event should be ignored and we should wait again. */ + +static int +lin_lwp_handle_extended (struct lwp_info *lp, int status) +{ + linux_handle_extended_wait (GET_LWP (lp->ptid), status, + &lp->waitstatus); + + /* TARGET_WAITKIND_SPURIOUS is used to indicate clone events. */ + if (lp->waitstatus.kind == TARGET_WAITKIND_SPURIOUS) + { + struct lwp_info *new_lp; + new_lp = add_lwp (BUILD_LWP (lp->waitstatus.value.related_pid, + GET_PID (inferior_ptid))); + new_lp->cloned = 1; + new_lp->stopped = 1; + + lp->waitstatus.kind = TARGET_WAITKIND_IGNORE; + + if (debug_lin_lwp) + fprintf_unfiltered (gdb_stdlog, + "LLHE: Got clone event from LWP %ld, resuming\n", + GET_LWP (lp->ptid)); + ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0); + + return 1; + } + + return 0; +} + /* Wait for LP to stop. Returns the wait status, or 0 if the LWP has exited. */ @@ -609,9 +650,11 @@ wait_lwp (struct lwp_info *lp) pid = waitpid (GET_LWP (lp->ptid), &status, __WCLONE); if (pid == -1 && errno == ECHILD) { - /* The thread has previously exited. We need to delete it now - because in the case of NPTL threads, there won't be an - exit event unless it is the main thread. */ + /* The thread has previously exited. We need to delete it + now because, for some vendor 2.4 kernels with NPTL + support backported, there won't be an exit event unless + it is the main thread. 2.6 kernels will report an exit + event for each thread that exits, as expected. */ thread_dead = 1; if (debug_lin_lwp) fprintf_unfiltered (gdb_stdlog, "WL: %s vanished.\n", @@ -658,6 +701,17 @@ wait_lwp (struct lwp_info *lp) gdb_assert (WIFSTOPPED (status)); + /* Handle GNU/Linux's extended waitstatus for trace events. */ + if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0) + { + if (debug_lin_lwp) + fprintf_unfiltered (gdb_stdlog, + "WL: Handling extended status 0x%06x\n", + status); + if (lin_lwp_handle_extended (lp, status)) + return wait_lwp (lp); + } + return status; } @@ -1097,6 +1151,8 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus) int status; pid_t pid; + ourstatus->kind = TARGET_WAITKIND_IGNORE; + do { set_sigint_trap (); /* Causes SIGINT to be passed on to the @@ -1143,6 +1199,25 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus) save_errno = EINTR; } + /* Handle GNU/Linux's extended waitstatus for trace events. */ + if (pid != -1 && WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP + && status >> 16 != 0) + { + linux_handle_extended_wait (pid, status, ourstatus); + + /* If we see a clone event, detach the child, and don't + report the event. It would be nice to offer some way to + switch into a non-thread-db based threaded mode at this + point. */ + if (ourstatus->kind == TARGET_WAITKIND_SPURIOUS) + { + ptrace (PTRACE_DETACH, ourstatus->value.related_pid, 0, 0); + ourstatus->kind = TARGET_WAITKIND_IGNORE; + pid = -1; + save_errno = EINTR; + } + } + clear_sigio_trap (); clear_sigint_trap (); } @@ -1159,11 +1234,9 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus) return minus_one_ptid; } - /* Handle GNU/Linux's extended waitstatus for trace events. */ - if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0) - return linux_handle_extended_wait (pid, status, ourstatus); + if (ourstatus->kind == TARGET_WAITKIND_IGNORE) + store_waitstatus (ourstatus, status); - store_waitstatus (ourstatus, status); return pid_to_ptid (pid); } @@ -1371,6 +1444,20 @@ retry: } } + /* Handle GNU/Linux's extended waitstatus for trace events. */ + if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0) + { + if (debug_lin_lwp) + fprintf_unfiltered (gdb_stdlog, + "LLW: Handling extended status 0x%06x\n", + status); + if (lin_lwp_handle_extended (lp, status)) + { + status = 0; + continue; + } + } + /* Check if the thread has exited. */ if ((WIFEXITED (status) || WIFSIGNALED (status)) && num_lwps > 1) { @@ -1588,14 +1675,14 @@ retry: else trap_ptid = null_ptid; - /* Handle GNU/Linux's extended waitstatus for trace events. */ - if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0) + if (lp->waitstatus.kind != TARGET_WAITKIND_IGNORE) { - linux_handle_extended_wait (GET_LWP (lp->ptid), status, ourstatus); - return trap_ptid; + *ourstatus = lp->waitstatus; + lp->waitstatus.kind = TARGET_WAITKIND_IGNORE; } + else + store_waitstatus (ourstatus, status); - store_waitstatus (ourstatus, status); return (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid))); } diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 2680422cd5..e421c9ca22 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -1,5 +1,5 @@ /* GNU/Linux native-dependent code common to multiple platforms. - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. This file is part of GDB. @@ -224,7 +224,8 @@ linux_enable_event_reporting (ptid_t ptid) if (! linux_supports_tracefork ()) return; - options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC; + options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC + | PTRACE_O_TRACECLONE; if (linux_supports_tracevforkdone ()) options |= PTRACE_O_TRACEVFORKDONE; @@ -391,11 +392,8 @@ linux_handle_extended_wait (int pid, int status, { int event = status >> 16; - if (event == PTRACE_EVENT_CLONE) - internal_error (__FILE__, __LINE__, - "unexpected clone event"); - - if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK) + if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK + || event == PTRACE_EVENT_CLONE) { unsigned long new_pid; int ret; @@ -406,12 +404,10 @@ linux_handle_extended_wait (int pid, int status, if (! pull_pid_from_list (&stopped_pids, new_pid)) { /* The new child has a pending SIGSTOP. We can't affect it until it - hits the SIGSTOP, but we're already attached. - - It won't be a clone (we didn't ask for clones in the event mask) - so we can just call waitpid and wait for the SIGSTOP. */ + hits the SIGSTOP, but we're already attached. */ do { - ret = waitpid (new_pid, &status, 0); + ret = waitpid (new_pid, &status, + (event == PTRACE_EVENT_CLONE) ? __WCLONE : 0); } while (ret == -1 && errno == EINTR); if (ret == -1) perror_with_name ("waiting for new child"); @@ -423,8 +419,13 @@ linux_handle_extended_wait (int pid, int status, "wait returned unexpected status 0x%x", status); } - ourstatus->kind = (event == PTRACE_EVENT_FORK) - ? TARGET_WAITKIND_FORKED : TARGET_WAITKIND_VFORKED; + if (event == PTRACE_EVENT_FORK) + ourstatus->kind = TARGET_WAITKIND_FORKED; + else if (event == PTRACE_EVENT_VFORK) + ourstatus->kind = TARGET_WAITKIND_VFORKED; + else + ourstatus->kind = TARGET_WAITKIND_SPURIOUS; + ourstatus->value.related_pid = new_pid; return inferior_ptid; } diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h index 23730bb949..74a8286b35 100644 --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -1,5 +1,5 @@ /* Native debugging support for GNU/Linux (LWP layer). - Copyright 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + Copyright 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. This file is part of GDB. @@ -18,6 +18,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include "target.h" + /* Structure describing an LWP. */ struct lwp_info @@ -52,6 +54,11 @@ struct lwp_info /* Non-zero if we were stepping this LWP. */ int step; + /* If WAITSTATUS->KIND != TARGET_WAITKIND_SPURIOUS, the waitstatus + for this LWP's last event. This may correspond to STATUS above, + or to a local variable in lin_lwp_wait. */ + struct target_waitstatus waitstatus; + /* Next LWP in list. */ struct lwp_info *next; }; @@ -60,7 +67,6 @@ struct lwp_info system". */ struct mem_attrib; struct target_ops; -struct target_waitstatus; extern int linux_proc_xfer_memory (CORE_ADDR addr, char *myaddr, int len, int write, struct mem_attrib *attrib, diff --git a/gdb/thread-db.c b/gdb/thread-db.c index 804f48a807..46838474e3 100644 --- a/gdb/thread-db.c +++ b/gdb/thread-db.c @@ -1,6 +1,6 @@ /* libthread_db assisted debugging support, generic parts. - Copyright 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + Copyright 1999, 2000, 2001, 2003, 2004 Free Software Foundation, Inc. This file is part of GDB. @@ -35,6 +35,10 @@ #include "regcache.h" #include "solib-svr4.h" +#ifdef HAVE_GNU_LIBC_VERSION_H +#include +#endif + #ifndef LIBTHREAD_DB_SO #define LIBTHREAD_DB_SO "libthread_db.so.1" #endif @@ -130,6 +134,7 @@ static CORE_ADDR td_death_bp_addr; static void thread_db_find_new_threads (void); static void attach_thread (ptid_t ptid, const td_thrhandle_t *th_p, const td_thrinfo_t *ti_p, int verbose); +static void detach_thread (ptid_t ptid, int verbose); /* Building process ids. */ @@ -150,6 +155,9 @@ static void attach_thread (ptid_t ptid, const td_thrhandle_t *th_p, struct private_thread_info { + /* Flag set when we see a TD_DEATH event for this thread. */ + unsigned int dying:1; + /* Cached thread state. */ unsigned int th_valid:1; unsigned int ti_valid:1; @@ -491,6 +499,10 @@ enable_thread_event_reporting (void) td_thr_events_t events; td_notify_t notify; td_err_e err; +#ifdef HAVE_GNU_LIBC_VERSION_H + const char *libc_version; + int libc_major, libc_minor; +#endif /* We cannot use the thread event reporting facility if these functions aren't available. */ @@ -501,12 +513,16 @@ enable_thread_event_reporting (void) /* Set the process wide mask saying which events we're interested in. */ td_event_emptyset (&events); td_event_addset (&events, TD_CREATE); -#if 0 + +#ifdef HAVE_GNU_LIBC_VERSION_H /* FIXME: kettenis/2000-04-23: The event reporting facility is broken for TD_DEATH events in glibc 2.1.3, so don't enable it for now. */ - td_event_addset (&events, TD_DEATH); + libc_version = gnu_get_libc_version (); + if (sscanf (libc_version, "%d.%d", &libc_major, &libc_minor) == 2 + && (libc_major > 2 || (libc_major == 2 && libc_minor > 1))) #endif + td_event_addset (&events, TD_DEATH); err = td_ta_set_event_p (thread_agent, &events); if (err != TD_OK) @@ -689,6 +705,10 @@ quit: target_new_objfile_chain (objfile); } +/* Attach to a new thread. This function is called when we receive a + TD_CREATE event or when we iterate over all threads and find one + that wasn't already in our list. */ + static void attach_thread (ptid_t ptid, const td_thrhandle_t *th_p, const td_thrinfo_t *ti_p, int verbose) @@ -696,6 +716,27 @@ attach_thread (ptid_t ptid, const td_thrhandle_t *th_p, struct thread_info *tp; td_err_e err; + /* If we're being called after a TD_CREATE event, we may already + know about this thread. There are two ways this can happen. We + may have iterated over all threads between the thread creation + and the TD_CREATE event, for instance when the user has issued + the `info threads' command before the SIGTRAP for hitting the + thread creation breakpoint was reported. Alternatively, the + thread may have exited and a new one been created with the same + thread ID. In the first case we don't need to do anything; in + the second case we should discard information about the dead + thread and attach to the new one. */ + if (in_thread_list (ptid)) + { + tp = find_thread_pid (ptid); + gdb_assert (tp != NULL); + + if (!tp->private->dying) + return; + + delete_thread (ptid); + } + check_thread_signals (); /* Add the thread to GDB's thread list. */ @@ -741,8 +782,21 @@ thread_db_attach (char *args, int from_tty) static void detach_thread (ptid_t ptid, int verbose) { + struct thread_info *thread_info; + if (verbose) printf_unfiltered ("[%s exited]\n", target_pid_to_str (ptid)); + + /* Don't delete the thread now, because it still reports as active + until it has executed a few instructions after the event + breakpoint - if we deleted it now, "info threads" would cause us + to re-attach to it. Just mark it as having had a TD_DEATH + event. This means that we won't delete it from our thread list + until we notice that it's dead (via prune_threads), or until + something re-uses its thread ID. */ + thread_info = find_thread_pid (ptid); + gdb_assert (thread_info != NULL); + thread_info->private->dying = 1; } static void @@ -847,12 +901,9 @@ check_event (ptid_t ptid) switch (msg.event) { case TD_CREATE: - - /* We may already know about this thread, for instance when the - user has issued the `info threads' command before the SIGTRAP - for hitting the thread creation breakpoint was reported. */ - if (!in_thread_list (ptid)) - attach_thread (ptid, msg.th_p, &ti, 1); + /* Call attach_thread whether or not we already know about a + thread with this thread ID. */ + attach_thread (ptid, msg.th_p, &ti, 1); break;