* Makefile.in (linux_nat_h): Update dependencies.
authorDaniel Jacobowitz <drow@false.org>
Mon, 29 Mar 2004 18:07:14 +0000 (18:07 +0000)
committerDaniel Jacobowitz <drow@false.org>
Mon, 29 Mar 2004 18:07:14 +0000 (18:07 +0000)
* configure.in: Check for <gnu/libc-version.h>.
* 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 <gnu/libc-version.h>.
(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
gdb/Makefile.in
gdb/config.in
gdb/configure
gdb/configure.in
gdb/lin-lwp.c
gdb/linux-nat.c
gdb/linux-nat.h
gdb/thread-db.c

index 1713275f33a774950ae0fc792386588eb7033900..cf32cf60230278a03084ea4569e7aa069eb1d872 100644 (file)
@@ -1,3 +1,30 @@
+2004-03-29  Daniel Jacobowitz  <drow@mvista.com>
+
+       * Makefile.in (linux_nat_h): Update dependencies.
+       * configure.in: Check for <gnu/libc-version.h>.
+       * 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 <gnu/libc-version.h>.
+       (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  <drow@mvista.com>
 
        * mips-tdep.c (mips_pdr_data): New.
index b78d46cd7fb3976209ab2c52ce418d8294b53e57..555badcb24d83d1dae95a74a3c08bf4dc96d9b49 100644 (file)
@@ -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
index 69982fd6b9acb9999364e254745a8594cd2bdb79..a1c2b0fc5cccf09ef4283a86210a24224f4396bb 100644 (file)
 /* Define if you have the <dirent.h> header file.  */
 #undef HAVE_DIRENT_H
 
+/* Define if you have the <gnu/libc-version.h> header file.  */
+#undef HAVE_GNU_LIBC_VERSION_H
+
 /* Define if you have the <libunwind-ia64.h> header file.  */
 #undef HAVE_LIBUNWIND_IA64_H
 
index e4de4c940852ee49c3aeefcce99bc67bb1820202..bc8ccbb7949cb9d446871bb8ab34fc1dbccf283a 100755 (executable)
@@ -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
index 07a289fb00f8876cfe86b2a7b86e9b301fdf7e1f..2781b41f73cac10342eb03e9ec5f8014d2e7d7cb 100644 (file)
@@ -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)
index 0d8d1ff6a01e14aaa2a13f89bce174b51813c570..23cf2c4a9b4a42e79ba59ab87cb6997f7d8e3f78 100644 (file)
@@ -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)));
 }
 
index 2680422cd50ff672d65ab3fa6039b5cd98932208..e421c9ca2214f01d65eb7062a57e9132d18510dd 100644 (file)
@@ -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;
     }
index 23730bb9492f46d9d574fd514664997f3031bed1..74a8286b35cfddffc1b2917df2478d8d8d1e2407 100644 (file)
@@ -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,
index 804f48a807384d5e7d81a4665f424ff3e1997e44..46838474e341cd174ed9b72acf1578a12fd3d188 100644 (file)
@@ -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.
 
 #include "regcache.h"
 #include "solib-svr4.h"
 
+#ifdef HAVE_GNU_LIBC_VERSION_H
+#include <gnu/libc-version.h>
+#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);
 \f
 
 /* 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;