2001-04-26 Michael Snyder <msnyder@redhat.com>
[deliverable/binutils-gdb.git] / gdb / lin-lwp.c
index 3b0812eae0a41c1b5d7b0419272595cb25dd355a..56e7bb238718e1d102f88e6297865c2310d7afd3 100644 (file)
@@ -1,5 +1,5 @@
 /* Multi-threaded debugging support for Linux (LWP layer).
-   Copyright 2000 Free Software Foundation, Inc.
+   Copyright 2000, 2001 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -29,6 +29,7 @@
 #include "gdbthread.h"
 #include "inferior.h"
 #include "target.h"
+#include "regcache.h"
 
 #define DEBUG 1
 
@@ -55,12 +56,14 @@ extern const char *strsignal (int sig);
    code:
 
    - In general one should specify the __WCLONE flag to waitpid in
-     order to make it report events for any of the cloned processes.
-     However, if a cloned process has exited the exit status is only
-     reported if the __WCLONE flag is absent.
+     order to make it report events for any of the cloned processes
+     (and leave it out for the initial process).  However, if a cloned
+     process has exited the exit status is only reported if the
+     __WCLONE flag is absent.  Linux 2.4 has a __WALL flag, but we
+     cannot use it since GDB must work on older systems too.
 
    - When a traced, cloned process exits and is waited for by the
-     debugger, the kernel reassigns it to the origional parent and
+     debugger, the kernel reassigns it to the original parent and
      keeps it around as a "zombie".  Somehow, the LinuxThreads library
      doesn't notice this, which leads to the "zombie problem": When
      debugged a multi-threaded process that spawns a lot of threads
@@ -126,9 +129,26 @@ static struct target_ops lin_lwp_ops;
 /* The standard child operations.  */
 extern struct target_ops child_ops;
 
+/* Since we cannot wait (in lin_lwp_wait) for the initial process and
+   any cloned processes with a single call to waitpid, we have to use
+   the WNOHANG flag and call waitpid in a loop.  To optimize
+   things a bit we use `sigsuspend' to wake us up when a process has
+   something to report (it will send us a SIGCHLD if it has).  To make
+   this work we have to juggle with the signal mask.  We save the
+   original signal mask such that we can restore it before creating a
+   new process in order to avoid blocking certain signals in the
+   inferior.  We then block SIGCHLD during the waitpid/sigsuspend
+   loop.  */
+
+/* Original signal mask.  */
+static sigset_t normal_mask;
+
 /* Signal mask for use with sigsuspend in lin_lwp_wait, initialized in
    _initialize_lin_lwp.  */
 static sigset_t suspend_mask;
+
+/* Signals to block to make that sigsuspend work.  */
+static sigset_t blocked_mask;
 \f
 
 /* Prototypes for local functions.  */
@@ -145,7 +165,7 @@ init_lwp_list (void)
   for (lp = lwp_list; lp; lp = lpnext)
     {
       lpnext = lp->next;
-      free (lp);
+      xfree (lp);
     }
 
   lwp_list = NULL;
@@ -203,7 +223,7 @@ delete_lwp (int pid)
   else
     lwp_list = lp->next;
 
-  free (lp);
+  xfree (lp);
 }
 
 /* Return a pointer to the structure describing the LWP corresponding
@@ -249,7 +269,7 @@ restore_inferior_pid (void *arg)
 {
   int *saved_pid_ptr = arg;
   inferior_pid = *saved_pid_ptr;
-  free (arg);
+  xfree (arg);
 }
 
 static struct cleanup *
@@ -263,7 +283,11 @@ save_inferior_pid (void)
 }
 \f
 
-/* Implementation of the PREPARE_TO_PROCEED hook for the Linux LWP layer.  */
+/* Implementation of the PREPARE_TO_PROCEED hook for the Linux LWP
+   layer.
+
+   Note that this implementation is potentially redundant now that
+   default_prepare_to_proceed() has been added.  */
 
 int
 lin_lwp_prepare_to_proceed (void)
@@ -419,10 +443,6 @@ lin_lwp_resume (int pid, int step, enum target_signal signo)
     {
       pid = GET_LWP (lp->pid);
 
-      /* Mark LWP as not stopped to prevent it from being continued by
-        resume_callback.  */
-      lp->stopped = 0;
-
       /* Remember if we're stepping.  */
       lp->step = step;
 
@@ -435,6 +455,10 @@ lin_lwp_resume (int pid, int step, enum target_signal signo)
          gdb_assert (signo == TARGET_SIGNAL_0);
          return;
        }
+
+      /* Mark LWP as not stopped to prevent it from being continued by
+        resume_callback.  */
+      lp->stopped = 0;
     }
 
   if (resume_all)
@@ -479,7 +503,7 @@ stop_wait_callback (struct lwp_info *lp, void *data)
                     is_cloned (lp->pid) ? __WCLONE : 0);
       if (pid == -1 && errno == ECHILD)
        /* OK, the proccess has disappeared.  We'll catch the actual
-           exit event in lin_lwp_wait.  */
+          exit event in lin_lwp_wait.  */
        return 0;
 
       gdb_assert (pid == GET_LWP (lp->pid));
@@ -487,14 +511,19 @@ stop_wait_callback (struct lwp_info *lp, void *data)
       if (WIFEXITED (status) || WIFSIGNALED (status))
        {
          gdb_assert (num_lwps > 1);
-         gdb_assert (! is_cloned (lp->pid));
-
-         gdb_assert (in_thread_list (lp->pid));
-         if (lp->pid != inferior_pid)
-           delete_thread (lp->pid);
-         printf_unfiltered ("[%s exited]\n",
-                            target_pid_to_str (lp->pid));
 
+         if (in_thread_list (lp->pid))
+           {
+             /* Core GDB cannot deal with us deleting the current
+                thread.  */
+             if (lp->pid != inferior_pid)
+               delete_thread (lp->pid);
+             printf_unfiltered ("[%s exited]\n",
+                                target_pid_to_str (lp->pid));
+           }
+#if DEBUG
+         printf ("%s exited.\n", target_pid_to_str (lp->pid));
+#endif
          delete_lwp (lp->pid);
          return 0;
        }
@@ -575,6 +604,13 @@ lin_lwp_wait (int pid, struct target_waitstatus *ourstatus)
   int options = 0;
   int status = 0;
 
+  /* Make sure SIGCHLD is blocked.  */
+  if (! sigismember (&blocked_mask, SIGCHLD))
+    {
+      sigaddset (&blocked_mask, SIGCHLD);
+      sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
+    }
+
  retry:
 
   /* First check if there is a LWP with a wait status pending.  */
@@ -659,7 +695,8 @@ lin_lwp_wait (int pid, struct target_waitstatus *ourstatus)
              lp = add_lwp (BUILD_LWP (lwpid, inferior_pid));
              if (threaded)
                {
-                 gdb_assert (WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
+                 gdb_assert (WIFSTOPPED (status)
+                             && WSTOPSIG (status) == SIGSTOP);
                  lp->signalled = 1;
 
                  if (! in_thread_list (inferior_pid))
@@ -681,7 +718,7 @@ lin_lwp_wait (int pid, struct target_waitstatus *ourstatus)
            {
              if (in_thread_list (lp->pid))
                {
-                 /* Core GDB cannot deal with us deeting the current
+                 /* Core GDB cannot deal with us deleting the current
                      thread.  */
                  if (lp->pid != inferior_pid)
                    delete_thread (lp->pid);
@@ -863,6 +900,10 @@ lin_lwp_mourn_inferior (void)
 
   trap_pid = 0;
 
+  /* Restore the original signal mask.  */
+  sigprocmask (SIG_SETMASK, &normal_mask, NULL);
+  sigemptyset (&blocked_mask);
+
 #if 0
   target_beneath = find_target_beneath (&lin_lwp_ops);
 #else
@@ -899,6 +940,7 @@ lin_lwp_store_registers (int regno)
 
 static int
 lin_lwp_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write,
+                    struct mem_attrib *attrib,
                     struct target_ops *target)
 {
   struct cleanup *old_chain = save_inferior_pid ();
@@ -907,7 +949,7 @@ lin_lwp_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write,
   if (is_lwp (inferior_pid))
     inferior_pid = GET_LWP (inferior_pid);
 
-  xfer = child_xfer_memory (memaddr, myaddr, len, write, target);
+  xfer = child_xfer_memory (memaddr, myaddr, len, write, attrib, target);
 
   do_cleanups (old_chain);
   return xfer;
@@ -978,7 +1020,6 @@ void
 _initialize_lin_lwp (void)
 {
   struct sigaction action;
-  sigset_t mask;
 
   extern void thread_db_init (struct target_ops *);
 
@@ -986,18 +1027,19 @@ _initialize_lin_lwp (void)
   add_target (&lin_lwp_ops);
   thread_db_init (&lin_lwp_ops);
 
+  /* Save the original signal mask.  */
+  sigprocmask (SIG_SETMASK, NULL, &normal_mask);
+
   action.sa_handler = sigchld_handler;
   sigemptyset (&action.sa_mask);
   action.sa_flags = 0;
   sigaction (SIGCHLD, &action, NULL);
 
-  /* We block SIGCHLD throughout this code ...  */
-  sigemptyset (&mask);
-  sigaddset (&mask, SIGCHLD);
-  sigprocmask (SIG_BLOCK, &mask, &suspend_mask);
-
-  /* ... except during a sigsuspend.  */
+  /* Make sure we don't block SIGCHLD during a sigsuspend.  */
+  sigprocmask (SIG_SETMASK, NULL, &suspend_mask);
   sigdelset (&suspend_mask, SIGCHLD);
+
+  sigemptyset (&blocked_mask);
 }
 \f
 
@@ -1030,8 +1072,8 @@ get_signo (const char *name)
 void
 lin_thread_get_thread_signals (sigset_t *set)
 {
-  int restart;
-  int cancel;
+  struct sigaction action;
+  int restart, cancel;
 
   sigemptyset (set);
 
@@ -1045,4 +1087,21 @@ lin_thread_get_thread_signals (sigset_t *set)
 
   sigaddset (set, restart);
   sigaddset (set, cancel);
+
+  /* The LinuxThreads library makes terminating threads send a special
+     "cancel" signal instead of SIGCHLD.  Make sure we catch those (to
+     prevent them from terminating GDB itself, which is likely to be
+     their default action) and treat them the same way as SIGCHLD.  */
+
+  action.sa_handler = sigchld_handler;
+  sigemptyset (&action.sa_mask);
+  action.sa_flags = 0;
+  sigaction (cancel, &action, NULL);
+
+  /* We block the "cancel" signal throughout this code ...  */
+  sigaddset (&blocked_mask, cancel);
+  sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
+
+  /* ... except during a sigsuspend.  */
+  sigdelset (&suspend_mask, cancel);
 }
This page took 0.027285 seconds and 4 git commands to generate.