2004-06-07 Randolph Chung <tausq@debian.org>
[deliverable/binutils-gdb.git] / gdb / lin-lwp.c
index c36394e87f3223745e0db9579165808f49fca9dc..4d04d62167e7661dc9db192383a2f554145563ef 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.
 
 #include "gdb_string.h"
 #include <errno.h>
 #include <signal.h>
+#ifdef HAVE_TKILL_SYSCALL
+#include <unistd.h>
+#include <sys/syscall.h>
+#endif
 #include <sys/ptrace.h>
 #include "gdb_wait.h"
 
@@ -36,6 +40,8 @@
 static int debug_lin_lwp;
 extern char *strsignal (int sig);
 
+#include "linux-nat.h"
+
 /* On GNU/Linux there are no real LWP's.  The closest thing to LWP's
    are processes sharing the same VM space.  A multi-threaded process
    is basically a group of such processes.  However, such a grouping
@@ -69,43 +75,6 @@ extern char *strsignal (int sig);
      threads will run out of processes, even if the threads exit,
      because the "zombies" stay around.  */
 
-/* Structure describing a LWP.  */
-struct lwp_info
-{
-  /* The process id of the LWP.  This is a combination of the LWP id
-     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;
-
-  /* Non-zero if this LWP is stopped.  */
-  int stopped;
-
-  /* Non-zero if this LWP will be/has been resumed.  Note that an LWP
-     can be marked both as stopped and resumed at the same time.  This
-     happens if we try to resume an LWP that has a wait status
-     pending.  We shouldn't let the LWP run until that wait status has
-     been processed, but we should not report that wait status if GDB
-     didn't try to let the LWP run.  */
-  int resumed;
-
-  /* If non-zero, a pending wait status.  */
-  int status;
-
-  /* Non-zero if we were stepping this LWP.  */
-  int step;
-
-  /* Next LWP in list.  */
-  struct lwp_info *next;
-};
-
 /* List of known LWPs.  */
 static struct lwp_info *lwp_list;
 
@@ -156,6 +125,7 @@ static sigset_t blocked_mask;
 
 /* Prototypes for local functions.  */
 static int stop_wait_callback (struct lwp_info *lp, void *data);
+static int lin_lwp_thread_alive (ptid_t ptid);
 \f
 /* Convert wait status STATUS to a string.  Used for printing debug
    messages only.  */
@@ -213,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;
@@ -293,46 +265,6 @@ iterate_over_lwps (int (*callback) (struct lwp_info *, void *), void *data)
 }
 \f
 
-/* Implementation of the PREPARE_TO_PROCEED hook for the GNU/Linux LWP
-   layer.
-
-   Note that this implementation is potentially redundant now that
-   default_prepare_to_proceed() has been added.
-
-   FIXME This may not support switching threads after Ctrl-C
-   correctly. The default implementation does support this. */
-
-int
-lin_lwp_prepare_to_proceed (void)
-{
-  if (!ptid_equal (trap_ptid, null_ptid)
-      && !ptid_equal (inferior_ptid, trap_ptid))
-    {
-      /* Switched over from TRAP_PID.  */
-      CORE_ADDR stop_pc = read_pc ();
-      CORE_ADDR trap_pc;
-
-      /* Avoid switching where it wouldn't do any good, i.e. if both
-         threads are at the same breakpoint.  */
-      trap_pc = read_pc_pid (trap_ptid);
-      if (trap_pc != stop_pc && breakpoint_here_p (trap_pc))
-       {
-         /* User hasn't deleted the breakpoint.  Return non-zero, and
-            switch back to TRAP_PID.  */
-         inferior_ptid = trap_ptid;
-
-         /* FIXME: Is this stuff really necessary?  */
-         flush_cached_frames ();
-         registers_changed ();
-
-         return 1;
-       }
-    }
-
-  return 0;
-}
-\f
-
 #if 0
 static void
 lin_lwp_open (char *args, int from_tty)
@@ -348,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));
 
@@ -363,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;
@@ -394,6 +330,8 @@ lin_lwp_attach_lwp (ptid_t ptid, int verbose)
       gdb_assert (pid == GET_LWP (ptid)
                  && WIFSTOPPED (status) && WSTOPSIG (status));
 
+      child_post_attach (pid);
+
       lp->stopped = 1;
 
       if (debug_lin_lwp)
@@ -485,7 +423,12 @@ detach_callback (struct lwp_info *lp, void *data)
       lp->stopped = 0;
       lp->signalled = 0;
       lp->status = 0;
-      stop_wait_callback (lp, NULL);
+      /* FIXME drow/2003-08-26: There was a call to stop_wait_callback
+        here.  But since lp->signalled was cleared above,
+        stop_wait_callback didn't do anything; the process was left
+        running.  Shouldn't we be waiting for it to stop?
+        I've removed the call, since stop_wait_callback now does do
+        something when called with lp->signalled == 0.  */
 
       gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status));
     }
@@ -627,6 +570,151 @@ lin_lwp_resume (ptid_t ptid, int step, enum target_signal signo)
 }
 \f
 
+/* Issue kill to specified lwp.  */
+
+static int tkill_failed;
+
+static int
+kill_lwp (int lwpid, int signo)
+{
+  errno = 0;
+
+/* Use tkill, if possible, in case we are using nptl threads.  If tkill
+   fails, then we are not using nptl threads and we should be using kill.  */
+
+#ifdef HAVE_TKILL_SYSCALL
+  if (!tkill_failed)
+    {
+      int ret = syscall (__NR_tkill, lwpid, signo);
+      if (errno != ENOSYS)
+       return ret;
+      errno = 0;
+      tkill_failed = 1;
+    }
+#endif
+
+  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.  */
+
+static int
+wait_lwp (struct lwp_info *lp)
+{
+  pid_t pid;
+  int status;
+  int thread_dead = 0;
+
+  gdb_assert (!lp->stopped);
+  gdb_assert (lp->status == 0);
+
+  pid = waitpid (GET_LWP (lp->ptid), &status, 0);
+  if (pid == -1 && errno == ECHILD)
+    {
+      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, 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",
+                               target_pid_to_str (lp->ptid));
+       }
+    }
+
+  if (!thread_dead)
+    {
+      gdb_assert (pid == GET_LWP (lp->ptid));
+
+      if (debug_lin_lwp)
+       {
+         fprintf_unfiltered (gdb_stdlog,
+                             "WL: waitpid %s received %s\n",
+                             target_pid_to_str (lp->ptid),
+                             status_to_str (status));
+       }
+    }
+
+  /* Check if the thread has exited.  */
+  if (WIFEXITED (status) || WIFSIGNALED (status))
+    {
+      thread_dead = 1;
+      if (debug_lin_lwp)
+       fprintf_unfiltered (gdb_stdlog, "WL: %s exited.\n",
+                           target_pid_to_str (lp->ptid));
+    }
+
+  if (thread_dead)
+    {
+      if (in_thread_list (lp->ptid))
+       {
+         /* Core GDB cannot deal with us deleting the current thread.  */
+         if (!ptid_equal (lp->ptid, inferior_ptid))
+           delete_thread (lp->ptid);
+         printf_unfiltered ("[%s exited]\n",
+                            target_pid_to_str (lp->ptid));
+       }
+
+      delete_lwp (lp->ptid);
+      return 0;
+    }
+
+  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;
+}
+
 /* Send a SIGSTOP to LP.  */
 
 static int
@@ -642,8 +730,15 @@ stop_callback (struct lwp_info *lp, void *data)
                              "SC:  kill %s **<SIGSTOP>**\n",
                              target_pid_to_str (lp->ptid));
        }
-      ret = kill (GET_LWP (lp->ptid), SIGSTOP);
-      gdb_assert (ret == 0);
+      errno = 0;
+      ret = kill_lwp (GET_LWP (lp->ptid), SIGSTOP);
+      if (debug_lin_lwp)
+       {
+         fprintf_unfiltered (gdb_stdlog,
+                             "SC:  lwp kill %d %s\n",
+                             ret,
+                             errno ? safe_strerror (errno) : "ERRNO-OK");
+       }
 
       lp->signalled = 1;
       gdb_assert (lp->status == 0);
@@ -660,55 +755,23 @@ stop_wait_callback (struct lwp_info *lp, void *data)
 {
   sigset_t *flush_mask = data;
 
-  if (!lp->stopped && lp->signalled)
+  if (!lp->stopped)
     {
-      pid_t pid;
       int status;
 
-      gdb_assert (lp->status == 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.  */
+      status = wait_lwp (lp);
+      if (status == 0)
        return 0;
 
-      gdb_assert (pid == GET_LWP (lp->ptid));
-
-      if (debug_lin_lwp)
-       {
-         fprintf_unfiltered (gdb_stdlog,
-                             "SWC: waitpid %s received %s\n",
-                             target_pid_to_str (lp->ptid),
-                             status_to_str (status));
-       }
-
-      if (WIFEXITED (status) || WIFSIGNALED (status))
+      /* Ignore any signals in FLUSH_MASK.  */
+      if (flush_mask && sigismember (flush_mask, WSTOPSIG (status)))
        {
-         gdb_assert (num_lwps > 1);
-
-         if (in_thread_list (lp->ptid))
+         if (!lp->signalled)
            {
-             /* Core GDB cannot deal with us deleting the current
-                thread.  */
-             if (!ptid_equal (lp->ptid, inferior_ptid))
-               delete_thread (lp->ptid);
-             printf_unfiltered ("[%s exited]\n",
-                                target_pid_to_str (lp->ptid));
+             lp->stopped = 1;
+             return 0;
            }
-         if (debug_lin_lwp)
-           fprintf_unfiltered (gdb_stdlog, "SWC: %s exited.\n",
-                               target_pid_to_str (lp->ptid));
-
-         delete_lwp (lp->ptid);
-         return 0;
-       }
 
-      gdb_assert (WIFSTOPPED (status));
-
-      /* Ignore any signals in FLUSH_MASK.  */
-      if (flush_mask && sigismember (flush_mask, WSTOPSIG (status)))
-       {
          errno = 0;
          ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
          if (debug_lin_lwp)
@@ -756,7 +819,14 @@ stop_wait_callback (struct lwp_info *lp, void *data)
              /* If there's another event, throw it back into the queue. */
              if (lp->status)
                {
-                 kill (GET_LWP (lp->ptid), WSTOPSIG (lp->status));
+                 if (debug_lin_lwp)
+                   {
+                     fprintf_unfiltered (gdb_stdlog,
+                                         "SWC: kill %s, %s\n",
+                                         target_pid_to_str (lp->ptid),
+                                         status_to_str ((int) status));
+                   }
+                 kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status));
                }
              /* Save the sigtrap event. */
              lp->status = status;
@@ -800,7 +870,7 @@ stop_wait_callback (struct lwp_info *lp, void *data)
                                          target_pid_to_str (lp->ptid),
                                          status_to_str ((int) status));
                    }
-                 kill (GET_LWP (lp->ptid), WSTOPSIG (status));
+                 kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (status));
                }
              return 0;
            }
@@ -817,6 +887,88 @@ stop_wait_callback (struct lwp_info *lp, void *data)
   return 0;
 }
 
+/* Check whether PID has any pending signals in FLUSH_MASK.  If so set
+   the appropriate bits in PENDING, and return 1 - otherwise return 0.  */
+
+static int
+lin_lwp_has_pending (int pid, sigset_t *pending, sigset_t *flush_mask)
+{
+  sigset_t blocked, ignored;
+  int i;
+
+  linux_proc_pending_signals (pid, pending, &blocked, &ignored);
+
+  if (!flush_mask)
+    return 0;
+
+  for (i = 1; i < NSIG; i++)
+    if (sigismember (pending, i))
+      if (!sigismember (flush_mask, i)
+         || sigismember (&blocked, i)
+         || sigismember (&ignored, i))
+       sigdelset (pending, i);
+
+  if (sigisemptyset (pending))
+    return 0;
+
+  return 1;
+}
+
+/* DATA is interpreted as a mask of signals to flush.  If LP has
+   signals pending, and they are all in the flush mask, then arrange
+   to flush them.  LP should be stopped, as should all other threads
+   it might share a signal queue with.  */
+
+static int
+flush_callback (struct lwp_info *lp, void *data)
+{
+  sigset_t *flush_mask = data;
+  sigset_t pending, intersection, blocked, ignored;
+  int pid, status;
+
+  /* Normally, when an LWP exits, it is removed from the LWP list.  The
+     last LWP isn't removed till later, however.  So if there is only
+     one LWP on the list, make sure it's alive.  */
+  if (lwp_list == lp && lp->next == NULL)
+    if (!lin_lwp_thread_alive (lp->ptid))
+      return 0;
+
+  /* Just because the LWP is stopped doesn't mean that new signals
+     can't arrive from outside, so this function must be careful of
+     race conditions.  However, because all threads are stopped, we
+     can assume that the pending mask will not shrink unless we resume
+     the LWP, and that it will then get another signal.  We can't
+     control which one, however.  */
+
+  if (lp->status)
+    {
+      if (debug_lin_lwp)
+       printf_unfiltered ("FC: LP has pending status %06x\n", lp->status);
+      if (WIFSTOPPED (lp->status) && sigismember (flush_mask, WSTOPSIG (lp->status)))
+       lp->status = 0;
+    }
+
+  while (lin_lwp_has_pending (GET_LWP (lp->ptid), &pending, flush_mask))
+    {
+      int ret;
+      
+      errno = 0;
+      ret = ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
+      if (debug_lin_lwp)
+       fprintf_unfiltered (gdb_stderr,
+                           "FC: Sent PTRACE_CONT, ret %d %d\n", ret, errno);
+
+      lp->stopped = 0;
+      stop_wait_callback (lp, flush_mask);
+      if (debug_lin_lwp)
+       fprintf_unfiltered (gdb_stderr,
+                           "FC: Wait finished; saved status is %d\n",
+                           lp->status);
+    }
+
+  return 0;
+}
+
 /* Return non-zero if LP has a wait status pending.  */
 
 static int
@@ -832,7 +984,7 @@ status_callback (struct lwp_info *lp, void *data)
 static int
 running_callback (struct lwp_info *lp, void *data)
 {
-  return (lp->stopped == 0);
+  return (lp->stopped == 0 || (lp->status != 0 && lp->resumed));
 }
 
 /* Count the LWP's that have had events.  */
@@ -999,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
@@ -1027,6 +1181,43 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
          save_errno = EINTR;
        }
 
+      /* Check for stop events reported by a process we didn't already
+        know about - in this case, anything other than inferior_ptid.
+
+        If we're expecting to receive stopped processes after fork,
+        vfork, and clone events, then we'll just add the new one to
+        our list and go back to waiting for the event to be reported
+        - the stopped process might be returned from waitpid before
+        or after the event is.  If we want to handle debugging of
+        CLONE_PTRACE processes we need to do more here, i.e. switch
+        to multi-threaded mode.  */
+      if (pid != -1 && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP
+         && pid != GET_PID (inferior_ptid))
+       {
+         linux_record_stopped_pid (pid);
+         pid = -1;
+         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 ();
     }
@@ -1043,12 +1234,36 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
       return minus_one_ptid;
     }
 
-  store_waitstatus (ourstatus, status);
+  if (ourstatus->kind == TARGET_WAITKIND_IGNORE)
+    store_waitstatus (ourstatus, status);
+
   return pid_to_ptid (pid);
 }
 
 #endif
 
+/* Stop an active thread, verify it still exists, then resume it.  */
+
+static int
+stop_and_resume_callback (struct lwp_info *lp, void *data)
+{
+  struct lwp_info *ptr;
+
+  if (!lp->stopped && !lp->signalled)
+    {
+      stop_callback (lp, NULL);
+      stop_wait_callback (lp, NULL);
+      /* Resume if the lwp still exists.  */
+      for (ptr = lwp_list; ptr; ptr = ptr->next)
+       if (lp == ptr)
+         {
+           resume_callback (lp, NULL);
+           resume_set_callback (lp, NULL);
+         }
+    }
+  return 0;
+}
+
 static ptid_t
 lin_lwp_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
 {
@@ -1171,6 +1386,22 @@ retry:
 
          lp = find_lwp_pid (pid_to_ptid (lwpid));
 
+         /* Check for stop events reported by a process we didn't
+            already know about - anything not already in our LWP
+            list.
+
+            If we're expecting to receive stopped processes after
+            fork, vfork, and clone events, then we'll just add the
+            new one to our list and go back to waiting for the event
+            to be reported - the stopped process might be returned
+            from waitpid before or after the event is.  */
+         if (WIFSTOPPED (status) && !lp)
+           {
+             linux_record_stopped_pid (lwpid);
+             status = 0;
+             continue;
+           }
+
          /* Make sure we don't report an event for the exit of an LWP not in
             our list, i.e.  not part of the current process.  This can happen
             if we detach from a program we original forked and then it
@@ -1181,6 +1412,13 @@ retry:
              continue;
            }
 
+         /* NOTE drow/2003-06-17: This code seems to be meant for debugging
+            CLONE_PTRACE processes which do not use the thread library -
+            otherwise we wouldn't find the new LWP this way.  That doesn't
+            currently work, and the following code is currently unreachable
+            due to the two blocks above.  If it's fixed some day, this code
+            should be broken out into a function so that we can also pick up
+            LWPs from the new interface.  */
          if (!lp)
            {
              lp = add_lwp (BUILD_LWP (lwpid, GET_PID (inferior_ptid)));
@@ -1206,10 +1444,75 @@ retry:
                }
            }
 
-         /* Make sure we don't report a TARGET_WAITKIND_EXITED or
-            TARGET_WAITKIND_SIGNALLED event if there are still LWP's
-            left in the process.  */
+         /* 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)
+           {
+             if (in_thread_list (lp->ptid))
+               {
+                 /* Core GDB cannot deal with us deleting the current
+                    thread.  */
+                 if (!ptid_equal (lp->ptid, inferior_ptid))
+                   delete_thread (lp->ptid);
+                 printf_unfiltered ("[%s exited]\n",
+                                    target_pid_to_str (lp->ptid));
+               }
+
+             /* If this is the main thread, we must stop all threads and
+                verify if they are still alive.  This is because in the nptl
+                thread model, there is no signal issued for exiting LWPs
+                other than the main thread.  We only get the main thread
+                exit signal once all child threads have already exited.
+                If we stop all the threads and use the stop_wait_callback
+                to check if they have exited we can determine whether this
+                signal should be ignored or whether it means the end of the
+                debugged application, regardless of which threading model
+                is being used.  */
+             if (GET_PID (lp->ptid) == GET_LWP (lp->ptid))
+               {
+                 lp->stopped = 1;
+                 iterate_over_lwps (stop_and_resume_callback, NULL);
+               }
+
+             if (debug_lin_lwp)
+               fprintf_unfiltered (gdb_stdlog,
+                                   "LLW: %s exited.\n",
+                                   target_pid_to_str (lp->ptid));
+
+             delete_lwp (lp->ptid);
+
+             /* If there is at least one more LWP, then the exit signal
+                was not the end of the debugged application and should be
+                ignored.  */
+             if (num_lwps > 0)
+               {
+                 /* Make sure there is at least one thread running.  */
+                 gdb_assert (iterate_over_lwps (running_callback, NULL));
+
+                 /* Discard the event.  */
+                 status = 0;
+                 continue;
+               }
+           }
+
+         /* Check if the current LWP has previously exited.  In the nptl
+            thread model, LWPs other than the main thread do not issue
+            signals when they exit so we must check whenever the thread
+            has stopped.  A similar check is made in stop_wait_callback().  */
+         if (num_lwps > 1 && !lin_lwp_thread_alive (lp->ptid))
            {
              if (in_thread_list (lp->ptid))
                {
@@ -1345,6 +1648,7 @@ retry:
   /* ... and wait until all of them have reported back that they're no
      longer running.  */
   iterate_over_lwps (stop_wait_callback, &flush_mask);
+  iterate_over_lwps (flush_callback, &flush_mask);
 
   /* If we're not waiting for a specific LWP, choose an event LWP from
      among those that have had events.  Giving equal priority to all
@@ -1371,7 +1675,14 @@ retry:
   else
     trap_ptid = null_ptid;
 
-  store_waitstatus (ourstatus, status);
+  if (lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
+    {
+      *ourstatus = lp->waitstatus;
+      lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
+    }
+  else
+    store_waitstatus (ourstatus, status);
+
   return (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid)));
 }
 
@@ -1447,9 +1758,10 @@ lin_lwp_kill (void)
 }
 
 static void
-lin_lwp_create_inferior (char *exec_file, char *allargs, char **env)
+lin_lwp_create_inferior (char *exec_file, char *allargs, char **env,
+                        int from_tty)
 {
-  child_ops.to_create_inferior (exec_file, allargs, env);
+  child_ops.to_create_inferior (exec_file, allargs, env, from_tty);
 }
 
 static void
@@ -1540,6 +1852,12 @@ init_lin_lwp_ops (void)
   lin_lwp_ops.to_mourn_inferior = lin_lwp_mourn_inferior;
   lin_lwp_ops.to_thread_alive = lin_lwp_thread_alive;
   lin_lwp_ops.to_pid_to_str = lin_lwp_pid_to_str;
+  lin_lwp_ops.to_post_startup_inferior = child_post_startup_inferior;
+  lin_lwp_ops.to_post_attach = child_post_attach;
+  lin_lwp_ops.to_insert_fork_catchpoint = child_insert_fork_catchpoint;
+  lin_lwp_ops.to_insert_vfork_catchpoint = child_insert_vfork_catchpoint;
+  lin_lwp_ops.to_insert_exec_catchpoint = child_insert_exec_catchpoint;
+
   lin_lwp_ops.to_stratum = thread_stratum;
   lin_lwp_ops.to_has_thread_control = tc_schedlock;
   lin_lwp_ops.to_magic = OPS_MAGIC;
This page took 0.031696 seconds and 4 git commands to generate.