2004-06-07 Randolph Chung <tausq@debian.org>
[deliverable/binutils-gdb.git] / gdb / lin-lwp.c
index a82adb7ea2a4dee39dd047bf4e210011635fe7be..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.
 
@@ -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;
@@ -324,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)
@@ -415,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));
     }
@@ -583,6 +596,125 @@ 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.  */
+
+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
@@ -623,92 +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);
+      status = wait_lwp (lp);
+      if (status == 0)
+       return 0;
 
-      pid = waitpid (GET_LWP (lp->ptid), &status, 0);
-      if (pid == -1 && errno == ECHILD)
+      /* Ignore any signals in FLUSH_MASK.  */
+      if (flush_mask && sigismember (flush_mask, WSTOPSIG (status)))
        {
-         pid = waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
-         if (pid == -1 && errno == ECHILD)
+         if (!lp->signalled)
            {
-             /* 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.  */
-             if (debug_lin_lwp)
-               fprintf_unfiltered (gdb_stdlog,
-                                   "SWC: %s exited.\n",
-                                   target_pid_to_str (lp->ptid));
-             delete_lwp (lp->ptid);
+             lp->stopped = 1;
              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));
-       }
-
-      /* Check if the thread has exited.  */
-      if (WIFEXITED (status) || WIFSIGNALED (status))
-       {
-         gdb_assert (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 (debug_lin_lwp)
-           fprintf_unfiltered (gdb_stdlog,
-                               "SWC: %s exited.\n",
-                               target_pid_to_str (lp->ptid));
-
-         delete_lwp (lp->ptid);
-         return 0;
-       }
-
-      /* Check if the current LWP has previously exited.  For nptl threads,
-         there is no exit signal issued for LWPs that are not the
-         main thread so we should check whenever the thread is stopped.  */
-      if (!lin_lwp_thread_alive (lp->ptid))
-       {
-         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 (debug_lin_lwp)
-           fprintf_unfiltered (gdb_stdlog,
-                               "SWC: %s already 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)
@@ -824,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
@@ -839,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.  */
@@ -1006,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
@@ -1047,10 +1194,30 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
       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 ();
     }
@@ -1067,7 +1234,9 @@ 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);
 }
 
@@ -1087,7 +1256,10 @@ stop_and_resume_callback (struct lwp_info *lp, void *data)
       /* Resume if the lwp still exists.  */
       for (ptr = lwp_list; ptr; ptr = ptr->next)
        if (lp == ptr)
-         resume_callback (lp, NULL);
+         {
+           resume_callback (lp, NULL);
+           resume_set_callback (lp, NULL);
+         }
     }
   return 0;
 }
@@ -1272,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)
            {
@@ -1462,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
@@ -1488,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)));
 }
 
@@ -1564,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
@@ -1657,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.029224 seconds and 4 git commands to generate.