2012-03-02 Pedro Alves <palves@redhat.com>
[deliverable/binutils-gdb.git] / gdb / gdbserver / linux-low.c
index ab34d849131713294027d413e8ce5a84169ab3c7..7638ca334d45ba2fd0c85a5700e242f395cd7671 100644 (file)
 
 struct inferior_list all_lwps;
 
-/* A list of all unknown processes which receive stop signals.  Some other
-   process will presumably claim each of these as forked children
-   momentarily.  */
+/* A list of all unknown processes which receive stop signals.  Some
+   other process will presumably claim each of these as forked
+   children momentarily.  */
 
-struct inferior_list stopped_pids;
+struct simple_pid_list
+{
+  /* The process ID.  */
+  int pid;
+
+  /* The status as reported by waitpid.  */
+  int status;
+
+  /* Next in chain.  */
+  struct simple_pid_list *next;
+};
+struct simple_pid_list *stopped_pids;
+
+/* Trivial list manipulation functions to keep track of a list of new
+   stopped processes.  */
+
+static void
+add_to_pid_list (struct simple_pid_list **listp, int pid, int status)
+{
+  struct simple_pid_list *new_pid = xmalloc (sizeof (struct simple_pid_list));
+
+  new_pid->pid = pid;
+  new_pid->status = status;
+  new_pid->next = *listp;
+  *listp = new_pid;
+}
+
+static int
+pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
+{
+  struct simple_pid_list **p;
+
+  for (p = listp; *p != NULL; p = &(*p)->next)
+    if ((*p)->pid == pid)
+      {
+       struct simple_pid_list *next = (*p)->next;
+
+       *statusp = (*p)->status;
+       xfree (*p);
+       *p = next;
+       return 1;
+      }
+  return 0;
+}
 
 /* FIXME this is a bit of a hack, and could be removed.  */
 int stopping_threads;
@@ -353,12 +396,12 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
     {
       ptid_t ptid;
       unsigned long new_pid;
-      int ret, status = W_STOPCODE (SIGSTOP);
+      int ret, status;
 
       ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_child), 0, &new_pid);
 
       /* If we haven't already seen the new PID stop, wait for it now.  */
-      if (! pull_pid_from_list (&stopped_pids, new_pid))
+      if (!pull_pid_from_list (&stopped_pids, new_pid, &status))
        {
          /* The new child has a pending SIGSTOP.  We can't affect it until it
             hits the SIGSTOP, but we're already attached.  */
@@ -643,6 +686,33 @@ linux_attach_lwp_1 (unsigned long lwpid, int initial)
      ptrace call on this LWP.  */
   new_lwp->must_set_ptrace_flags = 1;
 
+  if (linux_proc_pid_is_stopped (lwpid))
+    {
+      if (debug_threads)
+       fprintf (stderr,
+                "Attached to a stopped process\n");
+
+      /* The process is definitely stopped.  It is in a job control
+        stop, unless the kernel predates the TASK_STOPPED /
+        TASK_TRACED distinction, in which case it might be in a
+        ptrace stop.  Make sure it is in a ptrace stop; from there we
+        can kill it, signal it, et cetera.
+
+        First make sure there is a pending SIGSTOP.  Since we are
+        already attached, the process can not transition from stopped
+        to running without a PTRACE_CONT; so we know this signal will
+        go into the queue.  The SIGSTOP generated by PTRACE_ATTACH is
+        probably already in the queue (unless this kernel is old
+        enough to use TASK_STOPPED for ptrace stops); but since
+        SIGSTOP is not an RT signal, it can only be queued once.  */
+      kill_lwp (lwpid, SIGSTOP);
+
+      /* Finally, resume the stopped process.  This will deliver the
+        SIGSTOP (or a higher priority signal, just like normal
+        PTRACE_ATTACH), which we'll catch later on.  */
+      ptrace (PTRACE_CONT, lwpid, 0, 0);
+    }
+
   /* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH
      brings it to a halt.
 
@@ -1143,7 +1213,7 @@ retry:
      was reported to us by the kernel.  Save its PID.  */
   if (child == NULL && WIFSTOPPED (*wstatp))
     {
-      add_pid_to_list (&stopped_pids, ret);
+      add_to_pid_list (&stopped_pids, ret, *wstatp);
       goto retry;
     }
   else if (child == NULL)
@@ -2398,7 +2468,8 @@ Check if we're already there.\n",
                   || event_child->stopped_by_watchpoint
                   || (!step_over_finished
                       && !bp_explains_trap && !trace_event)
-                  || gdb_breakpoint_here (event_child->stop_pc));
+                  || (gdb_breakpoint_here (event_child->stop_pc)
+                  && gdb_condition_true_at_breakpoint (event_child->stop_pc)));
 
   /* We found no reason GDB would want us to stop.  We either hit one
      of our own breakpoints, or finished an internal step GDB
@@ -2481,6 +2552,15 @@ Check if we're already there.\n",
         why.  */
       find_inferior (&all_lwps, cancel_breakpoints_callback, event_child);
 
+      /* If we were going a step-over, all other threads but the stepping one
+        had been paused in start_step_over, with their suspend counts
+        incremented.  We don't want to do a full unstop/unpause, because we're
+        in all-stop mode (so we want threads stopped), but we still need to
+        unsuspend the other threads, to decrement their `suspended' count
+        back.  */
+      if (step_over_finished)
+       unsuspend_all_lwps (event_child);
+
       /* Stabilize threads (move out of jump pads).  */
       stabilize_threads ();
     }
@@ -3261,8 +3341,10 @@ need_step_over_p (struct inferior_list_entry *entry, void *dummy)
   if (breakpoint_here (pc) || fast_tracepoint_jump_here (pc))
     {
       /* Don't step over a breakpoint that GDB expects to hit
-        though.  */
-      if (gdb_breakpoint_here (pc))
+        though.  If the condition is being evaluated on the target's side
+        and it evaluate to false, step over this breakpoint as well.  */
+      if (gdb_breakpoint_here (pc)
+         && gdb_condition_true_at_breakpoint (pc))
        {
          if (debug_threads)
            fprintf (stderr,
@@ -3742,145 +3824,11 @@ unstop_all_lwps (int unsuspend, struct lwp_info *except)
     find_inferior (&all_lwps, proceed_one_lwp, except);
 }
 
-#ifdef HAVE_LINUX_USRREGS
-
-int
-register_addr (int regnum)
-{
-  int addr;
-
-  if (regnum < 0 || regnum >= the_low_target.num_regs)
-    error ("Invalid register number %d.", regnum);
-
-  addr = the_low_target.regmap[regnum];
-
-  return addr;
-}
-
-/* Fetch one register.  */
-static void
-fetch_register (struct regcache *regcache, int regno)
-{
-  CORE_ADDR regaddr;
-  int i, size;
-  char *buf;
-  int pid;
-
-  if (regno >= the_low_target.num_regs)
-    return;
-  if ((*the_low_target.cannot_fetch_register) (regno))
-    return;
-
-  regaddr = register_addr (regno);
-  if (regaddr == -1)
-    return;
-
-  size = ((register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
-         & -sizeof (PTRACE_XFER_TYPE));
-  buf = alloca (size);
-
-  pid = lwpid_of (get_thread_lwp (current_inferior));
-  for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
-    {
-      errno = 0;
-      *(PTRACE_XFER_TYPE *) (buf + i) =
-       ptrace (PTRACE_PEEKUSER, pid,
-               /* Coerce to a uintptr_t first to avoid potential gcc warning
-                  of coercing an 8 byte integer to a 4 byte pointer.  */
-               (PTRACE_ARG3_TYPE) (uintptr_t) regaddr, 0);
-      regaddr += sizeof (PTRACE_XFER_TYPE);
-      if (errno != 0)
-       error ("reading register %d: %s", regno, strerror (errno));
-    }
-
-  if (the_low_target.supply_ptrace_register)
-    the_low_target.supply_ptrace_register (regcache, regno, buf);
-  else
-    supply_register (regcache, regno, buf);
-}
-
-/* Store one register.  */
-static void
-store_register (struct regcache *regcache, int regno)
-{
-  CORE_ADDR regaddr;
-  int i, size;
-  char *buf;
-  int pid;
-
-  if (regno >= the_low_target.num_regs)
-    return;
-  if ((*the_low_target.cannot_store_register) (regno))
-    return;
-
-  regaddr = register_addr (regno);
-  if (regaddr == -1)
-    return;
-
-  size = ((register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
-         & -sizeof (PTRACE_XFER_TYPE));
-  buf = alloca (size);
-  memset (buf, 0, size);
-
-  if (the_low_target.collect_ptrace_register)
-    the_low_target.collect_ptrace_register (regcache, regno, buf);
-  else
-    collect_register (regcache, regno, buf);
-
-  pid = lwpid_of (get_thread_lwp (current_inferior));
-  for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
-    {
-      errno = 0;
-      ptrace (PTRACE_POKEUSER, pid,
-           /* Coerce to a uintptr_t first to avoid potential gcc warning
-              about coercing an 8 byte integer to a 4 byte pointer.  */
-             (PTRACE_ARG3_TYPE) (uintptr_t) regaddr,
-             (PTRACE_ARG4_TYPE) *(PTRACE_XFER_TYPE *) (buf + i));
-      if (errno != 0)
-       {
-         /* At this point, ESRCH should mean the process is
-            already gone, in which case we simply ignore attempts
-            to change its registers.  See also the related
-            comment in linux_resume_one_lwp.  */
-         if (errno == ESRCH)
-           return;
-
-         if ((*the_low_target.cannot_store_register) (regno) == 0)
-           error ("writing register %d: %s", regno, strerror (errno));
-       }
-      regaddr += sizeof (PTRACE_XFER_TYPE);
-    }
-}
-
-/* Fetch all registers, or just one, from the child process.  */
-static void
-usr_fetch_inferior_registers (struct regcache *regcache, int regno)
-{
-  if (regno == -1)
-    for (regno = 0; regno < the_low_target.num_regs; regno++)
-      fetch_register (regcache, regno);
-  else
-    fetch_register (regcache, regno);
-}
-
-/* Store our register values back into the inferior.
-   If REGNO is -1, do this for all registers.
-   Otherwise, REGNO specifies which register (so we can save time).  */
-static void
-usr_store_inferior_registers (struct regcache *regcache, int regno)
-{
-  if (regno == -1)
-    for (regno = 0; regno < the_low_target.num_regs; regno++)
-      store_register (regcache, regno);
-  else
-    store_register (regcache, regno);
-}
-#endif /* HAVE_LINUX_USRREGS */
-
-
 
 #ifdef HAVE_LINUX_REGSETS
 
+#define use_linux_regsets 1
+
 static int
 regsets_fetch_inferior_registers (struct regcache *regcache)
 {
@@ -4040,34 +3988,224 @@ regsets_store_inferior_registers (struct regcache *regcache)
     return 0;
   else
     return 1;
-  return 0;
 }
 
-#endif /* HAVE_LINUX_REGSETS */
+#else /* !HAVE_LINUX_REGSETS */
 
+#define use_linux_regsets 0
+#define regsets_fetch_inferior_registers(regcache) 1
+#define regsets_store_inferior_registers(regcache) 1
 
-void
-linux_fetch_registers (struct regcache *regcache, int regno)
-{
-#ifdef HAVE_LINUX_REGSETS
-  if (regsets_fetch_inferior_registers (regcache) == 0)
-    return;
 #endif
+
+/* Return 1 if register REGNO is supported by one of the regset ptrace
+   calls or 0 if it has to be transferred individually.  */
+
+static int
+linux_register_in_regsets (int regno)
+{
+  unsigned char mask = 1 << (regno % 8);
+  size_t index = regno / 8;
+
+  return (use_linux_regsets
+         && (the_low_target.regset_bitmap == NULL
+             || (the_low_target.regset_bitmap[index] & mask) != 0));
+}
+
 #ifdef HAVE_LINUX_USRREGS
-  usr_fetch_inferior_registers (regcache, regno);
+
+int
+register_addr (int regnum)
+{
+  int addr;
+
+  if (regnum < 0 || regnum >= the_low_target.num_regs)
+    error ("Invalid register number %d.", regnum);
+
+  addr = the_low_target.regmap[regnum];
+
+  return addr;
+}
+
+/* Fetch one register.  */
+static void
+fetch_register (struct regcache *regcache, int regno)
+{
+  CORE_ADDR regaddr;
+  int i, size;
+  char *buf;
+  int pid;
+
+  if (regno >= the_low_target.num_regs)
+    return;
+  if ((*the_low_target.cannot_fetch_register) (regno))
+    return;
+
+  regaddr = register_addr (regno);
+  if (regaddr == -1)
+    return;
+
+  size = ((register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
+         & -sizeof (PTRACE_XFER_TYPE));
+  buf = alloca (size);
+
+  pid = lwpid_of (get_thread_lwp (current_inferior));
+  for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
+    {
+      errno = 0;
+      *(PTRACE_XFER_TYPE *) (buf + i) =
+       ptrace (PTRACE_PEEKUSER, pid,
+               /* Coerce to a uintptr_t first to avoid potential gcc warning
+                  of coercing an 8 byte integer to a 4 byte pointer.  */
+               (PTRACE_ARG3_TYPE) (uintptr_t) regaddr, 0);
+      regaddr += sizeof (PTRACE_XFER_TYPE);
+      if (errno != 0)
+       error ("reading register %d: %s", regno, strerror (errno));
+    }
+
+  if (the_low_target.supply_ptrace_register)
+    the_low_target.supply_ptrace_register (regcache, regno, buf);
+  else
+    supply_register (regcache, regno, buf);
+}
+
+/* Store one register.  */
+static void
+store_register (struct regcache *regcache, int regno)
+{
+  CORE_ADDR regaddr;
+  int i, size;
+  char *buf;
+  int pid;
+
+  if (regno >= the_low_target.num_regs)
+    return;
+  if ((*the_low_target.cannot_store_register) (regno))
+    return;
+
+  regaddr = register_addr (regno);
+  if (regaddr == -1)
+    return;
+
+  size = ((register_size (regno) + sizeof (PTRACE_XFER_TYPE) - 1)
+         & -sizeof (PTRACE_XFER_TYPE));
+  buf = alloca (size);
+  memset (buf, 0, size);
+
+  if (the_low_target.collect_ptrace_register)
+    the_low_target.collect_ptrace_register (regcache, regno, buf);
+  else
+    collect_register (regcache, regno, buf);
+
+  pid = lwpid_of (get_thread_lwp (current_inferior));
+  for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
+    {
+      errno = 0;
+      ptrace (PTRACE_POKEUSER, pid,
+           /* Coerce to a uintptr_t first to avoid potential gcc warning
+              about coercing an 8 byte integer to a 4 byte pointer.  */
+             (PTRACE_ARG3_TYPE) (uintptr_t) regaddr,
+             (PTRACE_ARG4_TYPE) *(PTRACE_XFER_TYPE *) (buf + i));
+      if (errno != 0)
+       {
+         /* At this point, ESRCH should mean the process is
+            already gone, in which case we simply ignore attempts
+            to change its registers.  See also the related
+            comment in linux_resume_one_lwp.  */
+         if (errno == ESRCH)
+           return;
+
+         if ((*the_low_target.cannot_store_register) (regno) == 0)
+           error ("writing register %d: %s", regno, strerror (errno));
+       }
+      regaddr += sizeof (PTRACE_XFER_TYPE);
+    }
+}
+
+/* Fetch all registers, or just one, from the child process.
+   If REGNO is -1, do this for all registers, skipping any that are
+   assumed to have been retrieved by regsets_fetch_inferior_registers,
+   unless ALL is non-zero.
+   Otherwise, REGNO specifies which register (so we can save time).  */
+static void
+usr_fetch_inferior_registers (struct regcache *regcache, int regno, int all)
+{
+  if (regno == -1)
+    {
+      for (regno = 0; regno < the_low_target.num_regs; regno++)
+       if (all || !linux_register_in_regsets (regno))
+         fetch_register (regcache, regno);
+    }
+  else
+    fetch_register (regcache, regno);
+}
+
+/* Store our register values back into the inferior.
+   If REGNO is -1, do this for all registers, skipping any that are
+   assumed to have been saved by regsets_store_inferior_registers,
+   unless ALL is non-zero.
+   Otherwise, REGNO specifies which register (so we can save time).  */
+static void
+usr_store_inferior_registers (struct regcache *regcache, int regno, int all)
+{
+  if (regno == -1)
+    {
+      for (regno = 0; regno < the_low_target.num_regs; regno++)
+       if (all || !linux_register_in_regsets (regno))
+         store_register (regcache, regno);
+    }
+  else
+    store_register (regcache, regno);
+}
+
+#else /* !HAVE_LINUX_USRREGS */
+
+#define usr_fetch_inferior_registers(regcache, regno, all) do {} while (0)
+#define usr_store_inferior_registers(regcache, regno, all) do {} while (0)
+
 #endif
+
+
+void
+linux_fetch_registers (struct regcache *regcache, int regno)
+{
+  int use_regsets;
+  int all = 0;
+
+  if (regno == -1)
+    {
+      all = regsets_fetch_inferior_registers (regcache);
+      usr_fetch_inferior_registers (regcache, regno, all);
+    }
+  else
+    {
+      use_regsets = linux_register_in_regsets (regno);
+      if (use_regsets)
+       all = regsets_fetch_inferior_registers (regcache);
+      if (!use_regsets || all)
+       usr_fetch_inferior_registers (regcache, regno, 1);
+    }
 }
 
 void
 linux_store_registers (struct regcache *regcache, int regno)
 {
-#ifdef HAVE_LINUX_REGSETS
-  if (regsets_store_inferior_registers (regcache) == 0)
-    return;
-#endif
-#ifdef HAVE_LINUX_USRREGS
-  usr_store_inferior_registers (regcache, regno);
-#endif
+  int use_regsets;
+  int all = 0;
+
+  if (regno == -1)
+    {
+      all = regsets_store_inferior_registers (regcache);
+      usr_store_inferior_registers (regcache, regno, all);
+    }
+  else
+    {
+      use_regsets = linux_register_in_regsets (regno);
+      if (use_regsets)
+       all = regsets_store_inferior_registers (regcache);
+      if (!use_regsets || all)
+       usr_store_inferior_registers (regcache, regno, 1);
+    }
 }
 
 
This page took 0.037006 seconds and 4 git commands to generate.