Index: ChangeLog
[deliverable/binutils-gdb.git] / gdb / lin-lwp.c
index 25c06eb4a10928327510c1fe063513ae9b9ee19b..df91aa7696333b46d6cf7721cf1768bf235a0379 100644 (file)
@@ -40,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
@@ -73,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;
 
@@ -298,46 +263,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)
@@ -399,6 +324,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)
@@ -490,7 +417,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));
     }
@@ -658,6 +590,77 @@ kill_lwp (int lwpid, int signo)
   return kill (lwpid, signo);
 }
 
+/* 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 in the case of NPTL threads, there won't be an
+            exit event unless it is the main thread.  */
+         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));
+
+  return status;
+}
+
 /* Send a SIGSTOP to LP.  */
 
 static int
@@ -698,92 +701,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)
@@ -899,6 +833,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
@@ -914,7 +930,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.  */
@@ -1122,6 +1138,7 @@ 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;
        }
@@ -1142,6 +1159,10 @@ 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);
+
   store_waitstatus (ourstatus, status);
   return pid_to_ptid (pid);
 }
@@ -1162,7 +1183,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;
 }
@@ -1537,6 +1561,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
@@ -1563,6 +1588,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)
+    {
+      linux_handle_extended_wait (ptid_get_pid (trap_ptid),
+                                 status, ourstatus);
+      return trap_ptid;
+    }
+
   store_waitstatus (ourstatus, status);
   return (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid)));
 }
@@ -1732,6 +1765,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.029014 seconds and 4 git commands to generate.