*** empty log message ***
[deliverable/binutils-gdb.git] / gdb / linux-nat.c
index 8f12587950092623a9cb7c915633a6914a20dd94..1308844bf214cadc4a3c33dc385c5513b8f788f6 100644 (file)
 #include "gdb_dirent.h"
 #include "xml-support.h"
 #include "terminal.h"
+#include <sys/vfs.h>
+
+#ifndef SPUFS_MAGIC
+#define SPUFS_MAGIC 0x23c9b64e
+#endif
 
 #ifdef HAVE_PERSONALITY
 # include <sys/personality.h>
@@ -338,6 +343,12 @@ static int stop_callback (struct lwp_info *lp, void *data);
 
 static void block_child_signals (sigset_t *prev_mask);
 static void restore_child_signals_mask (sigset_t *prev_mask);
+
+struct lwp_info;
+static struct lwp_info *add_lwp (ptid_t ptid);
+static void purge_lwp_list (int pid);
+static struct lwp_info *find_lwp_pid (ptid_t ptid);
+
 \f
 /* Trivial list manipulation functions to keep track of a list of
    new stopped processes.  */
@@ -575,19 +586,20 @@ static int
 linux_child_follow_fork (struct target_ops *ops, int follow_child)
 {
   sigset_t prev_mask;
-  ptid_t last_ptid;
-  struct target_waitstatus last_status;
   int has_vforked;
   int parent_pid, child_pid;
 
   block_child_signals (&prev_mask);
 
-  get_last_target_status (&last_ptid, &last_status);
-  has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
-  parent_pid = ptid_get_lwp (last_ptid);
+  has_vforked = (inferior_thread ()->pending_follow.kind
+                == TARGET_WAITKIND_VFORKED);
+  parent_pid = ptid_get_lwp (inferior_ptid);
   if (parent_pid == 0)
-    parent_pid = ptid_get_pid (last_ptid);
-  child_pid = PIDGET (last_status.value.related_pid);
+    parent_pid = ptid_get_pid (inferior_ptid);
+  child_pid = PIDGET (inferior_thread ()->pending_follow.value.related_pid);
+
+  if (!detach_fork)
+    linux_enable_event_reporting (pid_to_ptid (child_pid));
 
   if (! follow_child)
     {
@@ -619,21 +631,27 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
        }
       else
        {
-         struct fork_info *fp;
          struct inferior *parent_inf, *child_inf;
+         struct lwp_info *lp;
+         struct cleanup *old_chain;
 
          /* Add process to GDB's tables.  */
          child_inf = add_inferior (child_pid);
 
-         parent_inf = find_inferior_pid (GET_PID (last_ptid));
+         parent_inf = current_inferior ();
          child_inf->attach_flag = parent_inf->attach_flag;
          copy_terminal_info (child_inf, parent_inf);
 
-         /* 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);
+         old_chain = save_inferior_ptid ();
+
+         inferior_ptid = ptid_build (child_pid, child_pid, 0);
+         add_thread (inferior_ptid);
+         lp = add_lwp (inferior_ptid);
+         lp->stopped = 1;
+
+         check_for_thread_db ();
+
+         do_cleanups (old_chain);
        }
 
       if (has_vforked)
@@ -692,20 +710,9 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
     }
   else
     {
-      struct thread_info *last_tp = find_thread_pid (last_ptid);
       struct thread_info *tp;
-      char child_pid_spelling[40];
       struct inferior *parent_inf, *child_inf;
-
-      /* Copy user stepping state to the new inferior thread.  */
-      struct breakpoint *step_resume_breakpoint = last_tp->step_resume_breakpoint;
-      CORE_ADDR step_range_start = last_tp->step_range_start;
-      CORE_ADDR step_range_end = last_tp->step_range_end;
-      struct frame_id step_frame_id = last_tp->step_frame_id;
-
-      /* Otherwise, deleting the parent would get rid of this
-        breakpoint.  */
-      last_tp->step_resume_breakpoint = NULL;
+      struct lwp_info *lp;
 
       /* Before detaching from the parent, remove all breakpoints from it. */
       remove_breakpoints ();
@@ -723,7 +730,7 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
 
       child_inf = add_inferior (child_pid);
 
-      parent_inf = find_inferior_pid (GET_PID (last_ptid));
+      parent_inf = current_inferior ();
       child_inf->attach_flag = parent_inf->attach_flag;
       copy_terminal_info (child_inf, parent_inf);
 
@@ -747,40 +754,34 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
 
       if (has_vforked)
        {
+         struct lwp_info *parent_lwp;
+
          linux_parent_pid = parent_pid;
+
+         /* Get rid of the inferior on the core side as well.  */
+         inferior_ptid = null_ptid;
          detach_inferior (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);
 
-         /* Also add an entry for the child fork.  */
-         fp = find_fork_pid (child_pid);
-         if (!fp)
-           fp = add_fork (child_pid);
-         fork_save_infrun_state (fp, 0);
+         /* Also get rid of all its lwps.  We will detach from this
+            inferior soon-ish, but, we will still get an exit event
+            reported through waitpid when it exits.  If we didn't get
+            rid of the lwps from our list, we would end up reporting
+            the inferior exit to the core, which would then try to
+            mourn a non-existing (from the core's perspective)
+            inferior.  */
+         parent_lwp = find_lwp_pid (pid_to_ptid (parent_pid));
+         purge_lwp_list (GET_PID (parent_lwp->ptid));
+         linux_parent_pid = parent_pid;
        }
-      else
+      else if (detach_fork)
        target_detach (NULL, 0);
 
       inferior_ptid = ptid_build (child_pid, child_pid, 0);
+      add_thread (inferior_ptid);
+      lp = add_lwp (inferior_ptid);
+      lp->stopped = 1;
 
-      linux_nat_switch_fork (inferior_ptid);
       check_for_thread_db ();
-
-      tp = inferior_thread ();
-      tp->step_resume_breakpoint = step_resume_breakpoint;
-      tp->step_range_start = step_range_start;
-      tp->step_range_end = step_range_end;
-      tp->step_frame_id = step_frame_id;
-
-      /* Reset breakpoints in the child as appropriate.  */
-      follow_inferior_reset_breakpoints ();
     }
 
   restore_child_signals_mask (&prev_mask);
@@ -1096,22 +1097,30 @@ iterate_over_lwps (ptid_t filter,
   return NULL;
 }
 
-/* Update our internal state when changing from one fork (checkpoint,
-   et cetera) to another indicated by NEW_PTID.  We can only switch
-   single-threaded applications, so we only create one new LWP, and
-   the previous list is discarded.  */
+/* Update our internal state when changing from one checkpoint to
+   another indicated by NEW_PTID.  We can only switch single-threaded
+   applications, so we only create one new LWP, and the previous list
+   is discarded.  */
 
 void
 linux_nat_switch_fork (ptid_t new_ptid)
 {
   struct lwp_info *lp;
 
-  init_lwp_list ();
+  purge_lwp_list (GET_PID (inferior_ptid));
+
   lp = add_lwp (new_ptid);
   lp->stopped = 1;
 
-  init_thread_list ();
-  add_thread_silent (new_ptid);
+  /* This changes the thread's ptid while preserving the gdb thread
+     num.  Also changes the inferior pid, while preserving the
+     inferior num.  */
+  thread_change_ptid (inferior_ptid, new_ptid);
+
+  /* We've just told GDB core that the thread changed target id, but,
+     in fact, it really is a different thread, with different register
+     contents.  */
+  registers_changed ();
 }
 
 /* Handle the exit of a single thread LP.  */
@@ -1119,7 +1128,7 @@ linux_nat_switch_fork (ptid_t new_ptid)
 static void
 exit_lwp (struct lwp_info *lp)
 {
-  struct thread_info *th = find_thread_pid (lp->ptid);
+  struct thread_info *th = find_thread_ptid (lp->ptid);
 
   if (th)
     {
@@ -1449,7 +1458,7 @@ get_pending_status (struct lwp_info *lp, int *status)
             have the last signal recorded in
             thread_info->stop_signal.  */
 
-         struct thread_info *tp = find_thread_pid (lp->ptid);
+         struct thread_info *tp = find_thread_ptid (lp->ptid);
          signo = tp->stop_signal;
        }
 
@@ -1478,7 +1487,7 @@ GPT: lwp %s had signal %s, but it is in no pass state\n",
     {
       if (GET_LWP (lp->ptid) == GET_LWP (last_ptid))
        {
-         struct thread_info *tp = find_thread_pid (lp->ptid);
+         struct thread_info *tp = find_thread_ptid (lp->ptid);
          if (tp->stop_signal != TARGET_SIGNAL_0
              && signal_pass_state (tp->stop_signal))
            *status = W_STOPCODE (target_signal_to_host (tp->stop_signal));
@@ -1838,6 +1847,34 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
 
       ourstatus->value.related_pid = ptid_build (new_pid, new_pid, 0);
 
+      if (event == PTRACE_EVENT_FORK
+         && linux_fork_checkpointing_p (GET_PID (lp->ptid)))
+       {
+         struct fork_info *fp;
+
+         /* Handle checkpointing by linux-fork.c here as a special
+            case.  We don't want the follow-fork-mode or 'catch fork'
+            to interfere with this.  */
+
+         /* This won't actually modify the breakpoint list, but will
+            physically remove the breakpoints from the child.  */
+         detach_breakpoints (new_pid);
+
+         /* Retain child fork in ptrace (stopped) state.  */
+         fp = find_fork_pid (new_pid);
+         if (!fp)
+           fp = add_fork (new_pid);
+
+         /* Report as spurious, so that infrun doesn't want to follow
+            this fork.  We're actually doing an infcall in
+            linux-fork.c.  */
+         ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+         linux_enable_event_reporting (pid_to_ptid (new_pid));
+
+         /* Report the stop to the core.  */
+         return 0;
+       }
+
       if (event == PTRACE_EVENT_FORK)
        ourstatus->kind = TARGET_WAITKIND_FORKED;
       else if (event == PTRACE_EVENT_VFORK)
@@ -1915,6 +1952,11 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
 
   if (event == PTRACE_EVENT_EXEC)
     {
+      if (debug_linux_nat)
+       fprintf_unfiltered (gdb_stdlog,
+                           "LHEW: Got exec event from LWP %ld\n",
+                           GET_LWP (lp->ptid));
+
       ourstatus->kind = TARGET_WAITKIND_EXECD;
       ourstatus->value.execd_pathname
        = xstrdup (linux_child_pid_to_exec_file (pid));
@@ -3308,6 +3350,12 @@ linux_nat_xfer_partial (struct target_ops *ops, enum target_object object,
     return linux_xfer_siginfo (ops, object, annex, readbuf, writebuf,
                               offset, len);
 
+  /* The target is connected but no live inferior is selected.  Pass
+     this request down to a lower stratum (e.g., the executable
+     file).  */
+  if (object == TARGET_OBJECT_MEMORY && ptid_equal (inferior_ptid, null_ptid))
+    return 0;
+
   old_chain = save_inferior_ptid ();
 
   if (is_lwp (inferior_ptid))
@@ -3457,8 +3505,8 @@ linux_nat_find_memory_regions (int (*func) (CORE_ADDR,
       if (info_verbose)
        {
          fprintf_filtered (gdb_stdout,
-                           "Save segment, %lld bytes at 0x%s (%c%c%c)",
-                           size, paddr_nz (addr),
+                           "Save segment, %lld bytes at %s (%c%c%c)",
+                           size, paddress (target_gdbarch, addr),
                            read ? 'r' : ' ',
                            write ? 'w' : ' ', exec ? 'x' : ' ');
          if (filename[0])
@@ -3507,8 +3555,8 @@ linux_nat_do_thread_registers (bfd *obfd, ptid_t ptid,
   gdb_gregset_t gregs;
   gdb_fpregset_t fpregs;
   unsigned long lwp = ptid_get_lwp (ptid);
-  struct regcache *regcache = get_thread_regcache (ptid);
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct gdbarch *gdbarch = target_gdbarch;
+  struct regcache *regcache = get_thread_arch_regcache (ptid, gdbarch);
   const struct regset *regset;
   int core_regset_p;
   struct cleanup *old_chain;
@@ -3618,6 +3666,119 @@ linux_nat_corefile_thread_callback (struct lwp_info *ti, void *data)
   return 0;
 }
 
+/* Enumerate spufs IDs for process PID.  */
+
+static void
+iterate_over_spus (int pid, void (*callback) (void *, int), void *data)
+{
+  char path[128];
+  DIR *dir;
+  struct dirent *entry;
+
+  xsnprintf (path, sizeof path, "/proc/%d/fd", pid);
+  dir = opendir (path);
+  if (!dir)
+    return;
+
+  rewinddir (dir);
+  while ((entry = readdir (dir)) != NULL)
+    {
+      struct stat st;
+      struct statfs stfs;
+      int fd;
+
+      fd = atoi (entry->d_name);
+      if (!fd)
+       continue;
+
+      xsnprintf (path, sizeof path, "/proc/%d/fd/%d", pid, fd);
+      if (stat (path, &st) != 0)
+       continue;
+      if (!S_ISDIR (st.st_mode))
+       continue;
+
+      if (statfs (path, &stfs) != 0)
+       continue;
+      if (stfs.f_type != SPUFS_MAGIC)
+       continue;
+
+      callback (data, fd);
+    }
+
+  closedir (dir);
+}
+
+/* Generate corefile notes for SPU contexts.  */
+
+struct linux_spu_corefile_data
+{
+  bfd *obfd;
+  char *note_data;
+  int *note_size;
+};
+
+static void
+linux_spu_corefile_callback (void *data, int fd)
+{
+  struct linux_spu_corefile_data *args = data;
+  int i;
+
+  static const char *spu_files[] =
+    {
+      "object-id",
+      "mem",
+      "regs",
+      "fpcr",
+      "lslr",
+      "decr",
+      "decr_status",
+      "signal1",
+      "signal1_type",
+      "signal2",
+      "signal2_type",
+      "event_mask",
+      "event_status",
+      "mbox_info",
+      "ibox_info",
+      "wbox_info",
+      "dma_info",
+      "proxydma_info",
+   };
+
+  for (i = 0; i < sizeof (spu_files) / sizeof (spu_files[0]); i++)
+    {
+      char annex[32], note_name[32];
+      gdb_byte *spu_data;
+      LONGEST spu_len;
+
+      xsnprintf (annex, sizeof annex, "%d/%s", fd, spu_files[i]);
+      spu_len = target_read_alloc (&current_target, TARGET_OBJECT_SPU,
+                                  annex, &spu_data);
+      if (spu_len > 0)
+       {
+         xsnprintf (note_name, sizeof note_name, "SPU/%s", annex);
+         args->note_data = elfcore_write_note (args->obfd, args->note_data,
+                                               args->note_size, note_name,
+                                               NT_SPU, spu_data, spu_len);
+         xfree (spu_data);
+       }
+    }
+}
+
+static char *
+linux_spu_make_corefile_notes (bfd *obfd, char *note_data, int *note_size)
+{
+  struct linux_spu_corefile_data args;
+  args.obfd = obfd;
+  args.note_data = note_data;
+  args.note_size = note_size;
+
+  iterate_over_spus (PIDGET (inferior_ptid),
+                    linux_spu_corefile_callback, &args);
+
+  return args.note_data;
+}
+
 /* Fills the "to_make_corefile_note" target vector.  Builds the note
    section for a corefile, and returns it in a malloc buffer.  */
 
@@ -3679,6 +3840,8 @@ linux_nat_make_corefile_notes (bfd *obfd, int *note_size)
       xfree (auxv);
     }
 
+  note_data = linux_spu_make_corefile_notes (obfd, note_data, note_size);
+
   make_cleanup (xfree, note_data);
   return note_data;
 }
@@ -3803,7 +3966,7 @@ linux_nat_info_proc_cmd (char *args, int from_tty)
 
          cleanup = make_cleanup_fclose (procfile);
          printf_filtered (_("Mapped address spaces:\n\n"));
-         if (gdbarch_addr_bit (current_gdbarch) == 32)
+         if (gdbarch_addr_bit (target_gdbarch) == 32)
            {
              printf_filtered ("\t%10s %10s %10s %10s %7s\n",
                           "Start Addr",
@@ -3829,7 +3992,7 @@ linux_nat_info_proc_cmd (char *args, int from_tty)
                 a generic local_address_string instead to print out
                 the addresses; that makes sense to me, too.  */
 
-             if (gdbarch_addr_bit (current_gdbarch) == 32)
+             if (gdbarch_addr_bit (target_gdbarch) == 32)
                {
                  printf_filtered ("\t%#10lx %#10lx %#10x %#10x %7s\n",
                               (unsigned long) addr,    /* FIXME: pr_addr */
@@ -4012,6 +4175,100 @@ linux_proc_xfer_partial (struct target_ops *ops, enum target_object object,
   return ret;
 }
 
+
+/* Enumerate spufs IDs for process PID.  */
+static LONGEST
+spu_enumerate_spu_ids (int pid, gdb_byte *buf, ULONGEST offset, LONGEST len)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+  LONGEST pos = 0;
+  LONGEST written = 0;
+  char path[128];
+  DIR *dir;
+  struct dirent *entry;
+
+  xsnprintf (path, sizeof path, "/proc/%d/fd", pid);
+  dir = opendir (path);
+  if (!dir)
+    return -1;
+
+  rewinddir (dir);
+  while ((entry = readdir (dir)) != NULL)
+    {
+      struct stat st;
+      struct statfs stfs;
+      int fd;
+
+      fd = atoi (entry->d_name);
+      if (!fd)
+       continue;
+
+      xsnprintf (path, sizeof path, "/proc/%d/fd/%d", pid, fd);
+      if (stat (path, &st) != 0)
+       continue;
+      if (!S_ISDIR (st.st_mode))
+       continue;
+
+      if (statfs (path, &stfs) != 0)
+       continue;
+      if (stfs.f_type != SPUFS_MAGIC)
+       continue;
+
+      if (pos >= offset && pos + 4 <= offset + len)
+       {
+         store_unsigned_integer (buf + pos - offset, 4, byte_order, fd);
+         written += 4;
+       }
+      pos += 4;
+    }
+
+  closedir (dir);
+  return written;
+}
+
+/* Implement the to_xfer_partial interface for the TARGET_OBJECT_SPU
+   object type, using the /proc file system.  */
+static LONGEST
+linux_proc_xfer_spu (struct target_ops *ops, enum target_object object,
+                    const char *annex, gdb_byte *readbuf,
+                    const gdb_byte *writebuf,
+                    ULONGEST offset, LONGEST len)
+{
+  char buf[128];
+  int fd = 0;
+  int ret = -1;
+  int pid = PIDGET (inferior_ptid);
+
+  if (!annex)
+    {
+      if (!readbuf)
+       return -1;
+      else
+       return spu_enumerate_spu_ids (pid, readbuf, offset, len);
+    }
+
+  xsnprintf (buf, sizeof buf, "/proc/%d/fd/%s", pid, annex);
+  fd = open (buf, writebuf? O_WRONLY : O_RDONLY);
+  if (fd <= 0)
+    return -1;
+
+  if (offset != 0
+      && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
+    {
+      close (fd);
+      return 0;
+    }
+
+  if (writebuf)
+    ret = write (fd, writebuf, (size_t) len);
+  else if (readbuf)
+    ret = read (fd, readbuf, (size_t) len);
+
+  close (fd);
+  return ret;
+}
+
+
 /* Parse LINE as a signal set and add its set bits to SIGS.  */
 
 static void
@@ -4217,6 +4474,24 @@ linux_xfer_partial (struct target_ops *ops, enum target_object object,
     return linux_nat_xfer_osdata (ops, object, annex, readbuf, writebuf,
                                offset, len);
 
+  if (object == TARGET_OBJECT_SPU)
+    return linux_proc_xfer_spu (ops, object, annex, readbuf, writebuf,
+                               offset, len);
+
+  /* GDB calculates all the addresses in possibly larget width of the address.
+     Address width needs to be masked before its final use - either by
+     linux_proc_xfer_partial or inf_ptrace_xfer_partial.
+
+     Compare ADDR_BIT first to avoid a compiler warning on shift overflow.  */
+
+  if (object == TARGET_OBJECT_MEMORY)
+    {
+      int addr_bit = gdbarch_addr_bit (target_gdbarch);
+
+      if (addr_bit < (sizeof (ULONGEST) * HOST_CHAR_BIT))
+       offset &= ((ULONGEST) 1 << addr_bit) - 1;
+    }
+
   xfer = linux_proc_xfer_partial (ops, object, annex, readbuf, writebuf,
                                  offset, len);
   if (xfer != 0)
@@ -4307,7 +4582,7 @@ linux_nat_supports_non_stop (void)
 /* True if we want to support multi-process.  To be removed when GDB
    supports multi-exec.  */
 
-int linux_multi_process = 0;
+int linux_multi_process = 1;
 
 static int
 linux_nat_supports_multi_process (void)
@@ -4360,14 +4635,9 @@ linux_nat_terminal_inferior (void)
       return;
     }
 
-  /* GDB should never give the terminal to the inferior, if the
-     inferior is running in the background (run&, continue&, etc.).
-     This check can be removed when the common code is fixed.  */
-  if (!sync_execution)
-    return;
-
   terminal_inferior ();
 
+  /* Calls to target_terminal_*() are meant to be idempotent.  */
   if (!async_terminal_is_ours)
     return;
 
@@ -4393,9 +4663,6 @@ linux_nat_terminal_ours (void)
      but claiming it sure should.  */
   terminal_ours ();
 
-  if (!sync_execution)
-    return;
-
   if (async_terminal_is_ours)
     return;
 
@@ -4546,7 +4813,7 @@ linux_nat_stop_lwp (struct lwp_info *lwp, void *data)
 
       if (debug_linux_nat)
        {
-         if (find_thread_pid (lwp->ptid)->stop_requested)
+         if (find_thread_ptid (lwp->ptid)->stop_requested)
            fprintf_unfiltered (gdb_stdlog, "\
 LNSL: already stopped/stop_requested %s\n",
                                target_pid_to_str (lwp->ptid));
This page took 0.030224 seconds and 4 git commands to generate.