* remote.c: Add an enumeration for configurable remote
[deliverable/binutils-gdb.git] / gdb / linux-nat.c
index 61c0effa52c9ce9134c71ca1ae46e254bc4508dc..b08943298b64b68794f47f321f2b79fc7c3c7342 100644 (file)
@@ -1,6 +1,7 @@
 /* GNU/Linux native-dependent code common to multiple platforms.
 
-   Copyright 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -16,8 +17,8 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
 
 #include "defs.h"
 #include "inferior.h"
@@ -31,6 +32,7 @@
 #endif
 #include <sys/ptrace.h>
 #include "linux-nat.h"
+#include "linux-fork.h"
 #include "gdbthread.h"
 #include "gdbcmd.h"
 #include "regcache.h"
    the use of the multi-threaded target.  */
 static struct target_ops *linux_ops;
 
-/* The saved to_xfer_partial method, inherited from inf-ptrace.c.  Called
-   by our to_xfer_partial.   */
-static LONGEST (*super_xfer_partial) (struct target_ops *, enum target_object,
-                                     const char *, gdb_byte *, const gdb_byte *,
+/* The saved to_xfer_partial method, inherited from inf-ptrace.c.
+   Called by our to_xfer_partial.  */
+static LONGEST (*super_xfer_partial) (struct target_ops *, 
+                                     enum target_object,
+                                     const char *, gdb_byte *, 
+                                     const gdb_byte *,
                                      ULONGEST, LONGEST);
 
+/* The saved to_mourn_inferior method, inherited from inf-ptrace.c.
+   Called by our to_mourn_inferior.  */
+static void (*super_mourn_inferior) (void);
+
 static int debug_linux_nat;
 static void
 show_debug_linux_nat (struct ui_file *file, int from_tty,
@@ -363,15 +371,28 @@ child_follow_fork (struct target_ops *ops, int follow_child)
         also, but they'll be reinserted below.  */
       detach_breakpoints (child_pid);
 
-      if (debug_linux_nat)
+      /* Detach new forked process?  */
+      if (detach_fork)
        {
-         target_terminal_ours ();
-         fprintf_unfiltered (gdb_stdlog,
-                             "Detaching after fork from child process %d.\n",
-                             child_pid);
-       }
+         if (debug_linux_nat)
+           {
+             target_terminal_ours ();
+             fprintf_filtered (gdb_stdlog,
+                               "Detaching after fork from child process %d.\n",
+                               child_pid);
+           }
 
-      ptrace (PTRACE_DETACH, child_pid, 0, 0);
+         ptrace (PTRACE_DETACH, child_pid, 0, 0);
+       }
+      else
+       {
+         struct fork_info *fp;
+         /* Retain child fork in ptrace (stopped) state.  */
+         fp = find_fork_pid (child_pid);
+         if (!fp)
+           fp = add_fork (child_pid);
+         fork_save_infrun_state (fp, 0);
+       }
 
       if (has_vforked)
        {
@@ -441,9 +462,9 @@ child_follow_fork (struct target_ops *ops, int follow_child)
       if (debug_linux_nat)
        {
          target_terminal_ours ();
-         fprintf_unfiltered (gdb_stdlog,
-                             "Attaching after fork to child process %d.\n",
-                             child_pid);
+         fprintf_filtered (gdb_stdlog,
+                           "Attaching after fork to child process %d.\n",
+                           child_pid);
        }
 
       /* If we're vforking, we may want to hold on to the parent until
@@ -466,13 +487,25 @@ child_follow_fork (struct target_ops *ops, int follow_child)
 
       if (has_vforked)
        linux_parent_pid = parent_pid;
+      else if (!detach_fork)
+       {
+         struct fork_info *fp;
+         /* Retain parent fork in ptrace (stopped) state.  */
+         fp = find_fork_pid (parent_pid);
+         if (!fp)
+           fp = add_fork (parent_pid);
+         fork_save_infrun_state (fp, 0);
+       }
       else
-       target_detach (NULL, 0);
+       {
+         target_detach (NULL, 0);
+       }
 
       inferior_ptid = pid_to_ptid (child_pid);
 
       /* Reinstall ourselves, since we might have been removed in
         target_detach (which does other necessary cleanup).  */
+
       push_target (ops);
 
       /* Reset breakpoints in the child as appropriate.  */
@@ -579,33 +612,42 @@ kill_inferior (void)
   if (pid == 0)
     return;
 
-  /* If we're stopped while forking and we haven't followed yet, kill the
-     other task.  We need to do this first because the parent will be
-     sleeping if this is a vfork.  */
-
-  get_last_target_status (&last_ptid, &last);
-
-  if (last.kind == TARGET_WAITKIND_FORKED
-      || last.kind == TARGET_WAITKIND_VFORKED)
+  /* First cut -- let's crudely do everything inline.  */
+  if (forks_exist_p ())
     {
-      ptrace (PT_KILL, last.value.related_pid, 0, 0);
-      wait (&status);
+      linux_fork_killall ();
+      pop_target ();
+      generic_mourn_inferior ();
     }
+  else
+    {
+      /* If we're stopped while forking and we haven't followed yet,
+        kill the other task.  We need to do this first because the
+        parent will be sleeping if this is a vfork.  */
 
-  /* Kill the current process.  */
-  ptrace (PT_KILL, pid, 0, 0);
-  ret = wait (&status);
+      get_last_target_status (&last_ptid, &last);
 
-  /* We might get a SIGCHLD instead of an exit status.  This is
-     aggravated by the first kill above - a child has just died.  */
+      if (last.kind == TARGET_WAITKIND_FORKED
+         || last.kind == TARGET_WAITKIND_VFORKED)
+       {
+         ptrace (PT_KILL, last.value.related_pid, 0, 0);
+         wait (&status);
+       }
 
-  while (ret == pid && WIFSTOPPED (status))
-    {
+      /* Kill the current process.  */
       ptrace (PT_KILL, pid, 0, 0);
       ret = wait (&status);
-    }
 
-  target_mourn_inferior ();
+      /* We might get a SIGCHLD instead of an exit status.  This is
+        aggravated by the first kill above - a child has just died.  */
+
+      while (ret == pid && WIFSTOPPED (status))
+       {
+         ptrace (PT_KILL, pid, 0, 0);
+         ret = wait (&status);
+       }
+      target_mourn_inferior ();
+    }
 }
 
 /* On GNU/Linux there are no real LWP's.  The closest thing to LWP's
@@ -827,6 +869,92 @@ iterate_over_lwps (int (*callback) (struct lwp_info *, void *), void *data)
   return NULL;
 }
 
+/* Record a PTID for later deletion.  */
+
+struct saved_ptids
+{
+  ptid_t ptid;
+  struct saved_ptids *next;
+};
+static struct saved_ptids *threads_to_delete;
+
+static void
+record_dead_thread (ptid_t ptid)
+{
+  struct saved_ptids *p = xmalloc (sizeof (struct saved_ptids));
+  p->ptid = ptid;
+  p->next = threads_to_delete;
+  threads_to_delete = p;
+}
+
+/* Delete any dead threads which are not the current thread.  */
+
+static void
+prune_lwps (void)
+{
+  struct saved_ptids **p = &threads_to_delete;
+
+  while (*p)
+    if (! ptid_equal ((*p)->ptid, inferior_ptid))
+      {
+       struct saved_ptids *tmp = *p;
+       delete_thread (tmp->ptid);
+       *p = tmp->next;
+       xfree (tmp);
+      }
+    else
+      p = &(*p)->next;
+}
+
+/* Callback for iterate_over_threads that finds a thread corresponding
+   to the given LWP.  */
+
+static int
+find_thread_from_lwp (struct thread_info *thr, void *dummy)
+{
+  ptid_t *ptid_p = dummy;
+
+  if (GET_LWP (thr->ptid) && GET_LWP (thr->ptid) == GET_LWP (*ptid_p))
+    return 1;
+  else
+    return 0;
+}
+
+/* Handle the exit of a single thread LP.  */
+
+static void
+exit_lwp (struct lwp_info *lp)
+{
+  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);
+      else
+       record_dead_thread (lp->ptid);
+      printf_unfiltered (_("[%s exited]\n"),
+                        target_pid_to_str (lp->ptid));
+    }
+  else
+    {
+      /* Even if LP->PTID is not in the global GDB thread list, the
+        LWP may be - with an additional thread ID.  We don't need
+        to print anything in this case; thread_db is in use and
+        already took care of that.  But it didn't delete the thread
+        in order to handle zombies correctly.  */
+
+      struct thread_info *thr;
+
+      thr = iterate_over_threads (find_thread_from_lwp, &lp->ptid);
+      if (thr && !ptid_equal (thr->ptid, inferior_ptid))
+       delete_thread (thr->ptid);
+      else
+       record_dead_thread (thr->ptid);
+    }
+
+  delete_lwp (lp->ptid);
+}
+
 /* Attach to the LWP specified by PID.  If VERBOSE is non-zero, print
    a message telling the user that a new LWP has been added to the
    process.  */
@@ -1072,6 +1200,16 @@ linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
   struct lwp_info *lp;
   int resume_all;
 
+  if (debug_linux_nat)
+    fprintf_unfiltered (gdb_stdlog,
+                       "LLR: Preparing to %s %s, %s, inferior_ptid %s\n",
+                       step ? "step" : "resume",
+                       target_pid_to_str (ptid),
+                       signo ? strsignal (signo) : "0",
+                       target_pid_to_str (inferior_ptid));
+
+  prune_lwps ();
+
   /* A specific PTID means `step only this process id'.  */
   resume_all = (PIDGET (ptid) == -1);
 
@@ -1097,12 +1235,45 @@ linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
       lp->resumed = 1;
 
       /* If we have a pending wait status for this thread, there is no
-         point in resuming the process.  */
+        point in resuming the process.  But first make sure that
+        linux_nat_wait won't preemptively handle the event - we
+        should never take this short-circuit if we are going to
+        leave LP running, since we have skipped resuming all the
+        other threads.  This bit of code needs to be synchronized
+        with linux_nat_wait.  */
+
+      if (lp->status && WIFSTOPPED (lp->status))
+       {
+         int saved_signo = target_signal_from_host (WSTOPSIG (lp->status));
+
+         if (signal_stop_state (saved_signo) == 0
+             && signal_print_state (saved_signo) == 0
+             && signal_pass_state (saved_signo) == 1)
+           {
+             if (debug_linux_nat)
+               fprintf_unfiltered (gdb_stdlog,
+                                   "LLR: Not short circuiting for ignored "
+                                   "status 0x%x\n", lp->status);
+
+             /* FIXME: What should we do if we are supposed to continue
+                this thread with a signal?  */
+             gdb_assert (signo == TARGET_SIGNAL_0);
+             signo = saved_signo;
+             lp->status = 0;
+           }
+       }
+
       if (lp->status)
        {
          /* FIXME: What should we do if we are supposed to continue
             this thread with a signal?  */
          gdb_assert (signo == TARGET_SIGNAL_0);
+
+         if (debug_linux_nat)
+           fprintf_unfiltered (gdb_stdlog,
+                               "LLR: Short circuiting for status 0x%x\n",
+                               lp->status);
+
          return;
        }
 
@@ -1239,16 +1410,7 @@ wait_lwp (struct lwp_info *lp)
 
   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);
+      exit_lwp (lp);
       return 0;
     }
 
@@ -1636,7 +1798,7 @@ select_event_lwp (struct lwp_info **orig_lp, int *status)
   int random_selector;
   struct lwp_info *event_lp;
 
-  /* Record the wait status for the origional LWP.  */
+  /* Record the wait status for the original LWP.  */
   (*orig_lp)->status = *status;
 
   /* Give preference to any LWP that is being single-stepped.  */
@@ -1688,6 +1850,30 @@ resumed_callback (struct lwp_info *lp, void *data)
   return lp->resumed;
 }
 
+/* Local mourn_inferior -- we need to override mourn_inferior
+   so that we can do something clever if one of several forks
+   has exited.  */
+
+static void
+child_mourn_inferior (void)
+{
+  int status;
+
+  if (! forks_exist_p ())
+    {
+      /* Normal case, no other forks available.  */
+      super_mourn_inferior ();
+      return;
+    }
+  else
+    {
+      /* Multi-fork case.  The current inferior_ptid has exited, but
+        there are other viable forks to debug.  Delete the exiting
+        one and context-switch to the first available.  */
+      linux_fork_mourn_inferior ();
+    }
+}
+
 /* We need to override child_wait to support attaching to cloned
    processes, since a normal wait (as done by the default version)
    ignores those processes.  */
@@ -2011,16 +2197,6 @@ retry:
          /* 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
@@ -2042,7 +2218,7 @@ retry:
                                    "LLW: %s exited.\n",
                                    target_pid_to_str (lp->ptid));
 
-             delete_lwp (lp->ptid);
+             exit_lwp (lp);
 
              /* If there is at least one more LWP, then the exit signal
                 was not the end of the debugged application and should be
@@ -2064,21 +2240,12 @@ retry:
             has stopped.  A similar check is made in stop_wait_callback().  */
          if (num_lwps > 1 && !linux_nat_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_linux_nat)
                fprintf_unfiltered (gdb_stdlog,
                                    "LLW: %s exited.\n",
                                    target_pid_to_str (lp->ptid));
 
-             delete_lwp (lp->ptid);
+             exit_lwp (lp);
 
              /* Make sure there is at least one thread running.  */
              gdb_assert (iterate_over_lwps (running_callback, NULL));
@@ -3160,6 +3327,9 @@ linux_target (void)
   super_xfer_partial = t->to_xfer_partial;
   t->to_xfer_partial = linux_xfer_partial;
 
+  super_mourn_inferior = t->to_mourn_inferior;
+  t->to_mourn_inferior = child_mourn_inferior;
+
   linux_ops = t;
   return t;
 }
@@ -3271,3 +3441,4 @@ lin_thread_get_thread_signals (sigset_t *set)
   /* ... except during a sigsuspend.  */
   sigdelset (&suspend_mask, cancel);
 }
+
This page took 0.031806 seconds and 4 git commands to generate.