This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 4/5] Teach gdbserver's linux backend about no unwaited-for children (TARGET_WAITDKIND_NO_RESUMED).


WIP.

gdb.threads/leader-exit.exp now passes.

gdb.threads/no-unwaited-for-left.exp now at least errors out instead of hanging:

 continue
 Continuing.
 warning: Remote failure reply: E.No unwaited-for children left.

 [Thread 15454] #1 stopped.
 0x00000034cf408e60 in pthread_join (threadid=140737353922368, thread_return=0x0) at pthread_join.c:93
 93          lll_wait_tid (pd->tid);
 (gdb) FAIL: gdb.threads/no-unwaited-for-left.exp: continue stops when the main thread exits

(BTW, in case of error from vCont, it would be better to query the
target for the current thread, or re-select one, instead of assuming
current inferior_ptid.)
---
 gdb/gdbserver/inferiors.h |  22 ++
 gdb/gdbserver/linux-low.c | 844 +++++++++++++++++++++++++++-------------------
 gdb/gdbserver/server.c    |  18 +-
 3 files changed, 537 insertions(+), 347 deletions(-)

diff --git a/gdb/gdbserver/inferiors.h b/gdb/gdbserver/inferiors.h
index 5f99fbc..6ba40b1 100644
--- a/gdb/gdbserver/inferiors.h
+++ b/gdb/gdbserver/inferiors.h
@@ -84,6 +84,28 @@ void add_inferior_to_list (struct inferior_list *list,
 void for_each_inferior (struct inferior_list *list,
 			void (*action) (struct inferior_list_entry *));
 
+/* Helper for ALL_INFERIORS_TYPE.  Gets the next element starting at
+   CUR, if CUR is not NULL.  */
+#define A_I_NEXT(type, list, cur)					\
+  ((cur) != NULL							\
+   ? (type *) ((struct inferior_list_entry *) cur)->next		\
+   : NULL)
+
+/* Iterate over all inferiors of type TYPE in LIST, open loop
+   style.  */
+#define ALL_INFERIORS_TYPE(type, list, cur, tmp)				\
+  for ((cur) = (type *) (list)->head, (tmp) = A_I_NEXT (type, list, cur); \
+       (cur) != NULL;							\
+       (cur) = (tmp), (tmp) = A_I_NEXT (type, list, cur))
+
+/* Iterate over all inferiors in LIST, open loop style.  */
+#define ALL_INFERIORS(list, cur, tmp)				\
+  ALL_INFERIORS_TYPE (struct inferior_list_entry, list, cur, tmp)
+
+/* Iterate over all processes, open loop style.  */
+#define ALL_PROCESSES(cur, tmp)					\
+  ALL_INFERIORS_TYPE (struct process_info, &all_processes, cur, tmp)
+
 extern struct thread_info *current_inferior;
 void remove_inferior (struct inferior_list *list,
 		      struct inferior_list_entry *entry);
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index bac6134..3593dbc 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -225,6 +225,8 @@ static void linux_resume_one_lwp (struct lwp_info *lwp,
 static void linux_resume (struct thread_resume *resume_info, size_t n);
 static void stop_all_lwps (int suspend, struct lwp_info *except);
 static void unstop_all_lwps (int unsuspend, struct lwp_info *except);
+static int linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
+					  int *wstat, int options);
 static int linux_wait_for_event (ptid_t ptid, int *wstat, int options);
 static void *add_lwp (ptid_t ptid);
 static int linux_stopped_by_watchpoint (void);
@@ -287,7 +289,7 @@ static int linux_event_pipe[2] = { -1, -1 };
 #define target_is_async_p() (linux_event_pipe[0] != -1)
 
 static void send_sigstop (struct lwp_info *lwp);
-static void wait_for_sigstop (struct inferior_list_entry *entry);
+static void wait_for_sigstop (void);
 
 /* Return non-zero if HEADER is a 64-bit ELF file.  */
 
@@ -346,6 +348,9 @@ linux_pid_exe_is_elf_64_file (int pid, unsigned int *machine)
 static void
 delete_lwp (struct lwp_info *lwp)
 {
+  if (debug_threads)
+    debug_printf ("deleting %ld\n", lwpid_of (lwp));
+
   remove_thread (get_lwp_thread (lwp));
   remove_inferior (&all_lwps, &lwp->head);
   free (lwp->arch_private);
@@ -407,6 +412,11 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
 	    warning ("wait returned unexpected status 0x%x", status);
 	}
 
+      if (debug_threads)
+	debug_printf ("HEW: Got clone event "
+		      "from LWP %ld, new child is LWP %ld\n",
+		      lwpid_of (event_child), new_pid);
+
       ptid = ptid_build (pid_of (event_child), new_pid, 0);
       new_lwp = (struct lwp_info *) add_lwp (ptid);
       add_thread (ptid, new_lwp);
@@ -863,10 +873,8 @@ second_thread_of_pid_p (struct inferior_list_entry *entry, void *args)
 }
 
 static int
-last_thread_of_process_p (struct thread_info *thread)
+last_thread_of_process_p (int pid)
 {
-  ptid_t ptid = ((struct inferior_list_entry *)thread)->id;
-  int pid = ptid_get_pid (ptid);
   struct counter counter = { pid , 0 };
 
   return (find_inferior (&all_threads,
@@ -941,7 +949,7 @@ kill_one_lwp_callback (struct inferior_list_entry *entry, void *args)
       linux_kill_one_lwp (lwp);
 
       /* Make sure it died.  The loop is most likely unnecessary.  */
-      pid = linux_wait_for_event (lwp->head.id, &wstat, __WALL);
+      pid = linux_wait_for_event (ptid_of (lwp), &wstat, __WALL);
     } while (pid > 0 && WIFSTOPPED (wstat));
 
   return 0;
@@ -986,7 +994,7 @@ linux_kill (int pid)
 	  linux_kill_one_lwp (lwp);
 
 	  /* Make sure it died.  The loop is most likely unnecessary.  */
-	  lwpid = linux_wait_for_event (lwp->head.id, &wstat, __WALL);
+	  lwpid = linux_wait_for_event (ptid_of (lwp), &wstat, __WALL);
 	} while (lwpid > 0 && WIFSTOPPED (wstat));
     }
 
@@ -1260,147 +1268,105 @@ find_lwp_pid (ptid_t ptid)
   return (struct lwp_info*) find_inferior (&all_lwps, same_lwp, &ptid);
 }
 
-static struct lwp_info *
-linux_wait_for_lwp (ptid_t ptid, int *wstatp, int options)
-{
-  int ret;
-  int to_wait_for = -1;
-  struct lwp_info *child = NULL;
-
-  if (debug_threads)
-    debug_printf ("linux_wait_for_lwp: %s\n", target_pid_to_str (ptid));
-
-  if (ptid_equal (ptid, minus_one_ptid))
-    to_wait_for = -1;			/* any child */
-  else
-    to_wait_for = ptid_get_lwp (ptid);	/* this lwp only */
-
-  options |= __WALL;
+/* Return the number of known LWPs in the tgid given by PID.  */
 
-retry:
-
-  ret = my_waitpid (to_wait_for, wstatp, options);
-  if (ret == 0 || (ret == -1 && errno == ECHILD && (options & WNOHANG)))
-    return NULL;
-  else if (ret == -1)
-    perror_with_name ("waitpid");
-
-  if (debug_threads
-      && (!WIFSTOPPED (*wstatp)
-	  || (WSTOPSIG (*wstatp) != 32
-	      && WSTOPSIG (*wstatp) != 33)))
-    debug_printf ("Got an event from %d (%x)\n", ret, *wstatp);
-
-  child = find_lwp_pid (pid_to_ptid (ret));
+static int
+num_lwps (int pid)
+{
+  struct inferior_list_entry *inf, *tmp;
+  int count = 0;
 
-  /* If we didn't find a process, one of two things presumably happened:
-     - A process we started and then detached from has exited.  Ignore it.
-     - A process we are controlling has forked and the new child's stop
-     was reported to us by the kernel.  Save its PID.  */
-  if (child == NULL && WIFSTOPPED (*wstatp))
+  ALL_INFERIORS (&all_lwps, inf, tmp)
     {
-      add_to_pid_list (&stopped_pids, ret, *wstatp);
-      goto retry;
+      if (ptid_get_pid (inf->id) == pid)
+	count++;
     }
-  else if (child == NULL)
-    goto retry;
-
-  child->stopped = 1;
-
-  child->last_status = *wstatp;
-
-  if (WIFSTOPPED (*wstatp))
-    {
-      struct process_info *proc;
-
-      /* Architecture-specific setup after inferior is running.  This
-	 needs to happen after we have attached to the inferior and it
-	 is stopped for the first time, but before we access any
-	 inferior registers.  */
-      proc = find_process_pid (pid_of (child));
-      if (proc->private->new_inferior)
-	{
-	  struct thread_info *saved_inferior;
-
-	  saved_inferior = current_inferior;
-	  current_inferior = get_lwp_thread (child);
-
-	  the_low_target.arch_setup ();
-
-	  current_inferior = saved_inferior;
 
-	  proc->private->new_inferior = 0;
-	}
-    }
+  return count;
+}
 
-  /* Fetch the possibly triggered data watchpoint info and store it in
-     CHILD.
+/* Detect zombie thread group leaders, and "exit" them.  We can't reap
+   their exits until all other threads in the group have exited.  */
 
-     On some archs, like x86, that use debug registers to set
-     watchpoints, it's possible that the way to know which watched
-     address trapped, is to check the register that is used to select
-     which address to watch.  Problem is, between setting the
-     watchpoint and reading back which data address trapped, the user
-     may change the set of watchpoints, and, as a consequence, GDB
-     changes the debug registers in the inferior.  To avoid reading
-     back a stale stopped-data-address when that happens, we cache in
-     LP the fact that a watchpoint trapped, and the corresponding data
-     address, as soon as we see CHILD stop with a SIGTRAP.  If GDB
-     changes the debug registers meanwhile, we have the cached data we
-     can rely on.  */
+static void
+check_zombie_leaders (void)
+{
+  struct process_info *proc, *tmp;
 
-  if (WIFSTOPPED (*wstatp) && WSTOPSIG (*wstatp) == SIGTRAP)
+  ALL_PROCESSES (proc, tmp)
     {
-      if (the_low_target.stopped_by_watchpoint == NULL)
-	{
-	  child->stopped_by_watchpoint = 0;
-	}
-      else
-	{
-	  struct thread_info *saved_inferior;
+      pid_t leader_pid = pid_of (proc);
+      struct lwp_info *leader_lp;
 
-	  saved_inferior = current_inferior;
-	  current_inferior = get_lwp_thread (child);
+      leader_lp = find_lwp_pid (pid_to_ptid (leader_pid));
 
-	  child->stopped_by_watchpoint
-	    = the_low_target.stopped_by_watchpoint ();
+      if (debug_threads)
+	debug_printf ("leader_pid=%d, leader_lp!=NULL=%d, "
+		      "num_lwps=%d, zombie=%d\n",
+		      leader_pid, leader_lp!= NULL, num_lwps (leader_pid),
+		      linux_proc_pid_is_zombie (leader_pid));
+
+      if (leader_lp != NULL
+	  /* Check if there are other threads in the group, as we may
+	     have raced with the inferior simply exiting.  */
+	  && !last_thread_of_process_p (leader_pid)
+	  && linux_proc_pid_is_zombie (leader_pid))
+	{
+	  /* A leader zombie can mean one of two things:
+
+	     - It exited, and there's an exit status pending
+	     available, or only the leader exited (not the whole
+	     program).  In the latter case, we can't waitpid the
+	     leader's exit status until all other threads are gone.
+
+	     - There are 3 or more threads in the group, and a thread
+	     other than the leader exec'd.  On an exec, the Linux
+	     kernel destroys all other threads (except the execing
+	     one) in the thread group, and resets the execing thread's
+	     tid to the tgid.  No exit notification is sent for the
+	     execing thread -- from the ptracer's perspective, it
+	     appears as though the execing thread just vanishes.
+	     Until we reap all other threads except the leader and the
+	     execing thread, the leader will be zombie, and the
+	     execing thread will be in `D (disc sleep)'.  As soon as
+	     all other threads are reaped, the execing thread changes
+	     it's tid to the tgid, and the previous (zombie) leader
+	     vanishes, giving place to the "new" leader.  We could try
+	     distinguishing the exit and exec cases, by waiting once
+	     more, and seeing if something comes out, but it doesn't
+	     sound useful.  The previous leader _does_ go away, and
+	     we'll re-add the new one once we see the exec event
+	     (which is just the same as what would happen if the
+	     previous leader did exit voluntarily before some other
+	     thread execs).  */
 
-	  if (child->stopped_by_watchpoint)
-	    {
-	      if (the_low_target.stopped_data_address != NULL)
-		child->stopped_data_address
-		  = the_low_target.stopped_data_address ();
-	      else
-		child->stopped_data_address = 0;
-	    }
+	  if (debug_threads)
+	    fprintf (stderr,
+		     "CZL: Thread group leader %d zombie "
+		     "(it exited, or another thread execd).\n",
+		     leader_pid);
 
-	  current_inferior = saved_inferior;
+	  delete_lwp (leader_lp);
 	}
     }
+}
 
-  /* Store the STOP_PC, with adjustment applied.  This depends on the
-     architecture being defined already (so that CHILD has a valid
-     regcache), and on LAST_STATUS being set (to check for SIGTRAP or
-     not).  */
-  if (WIFSTOPPED (*wstatp))
-    child->stop_pc = get_stop_pc (child);
+/* Callback for `find_inferior'.  Returns the first LWP that is not
+   stopped.  ARG is a PTID filter.  */
 
-  if (debug_threads
-      && WIFSTOPPED (*wstatp)
-      && the_low_target.get_pc != NULL)
-    {
-      struct thread_info *saved_inferior = current_inferior;
-      struct regcache *regcache;
-      CORE_ADDR pc;
+static int
+not_stopped_callback (struct inferior_list_entry *entry, void *arg)
+{
+  struct lwp_info *lp = (struct lwp_info *) entry;
+  ptid_t filter = *(ptid_t *) arg;
 
-      current_inferior = get_lwp_thread (child);
-      regcache = get_thread_regcache (current_inferior, 1);
-      pc = (*the_low_target.get_pc) (regcache);
-      debug_printf ("linux_wait_for_lwp: pc is 0x%lx\n", (long) pc);
-      current_inferior = saved_inferior;
-    }
+  if (!ptid_match (ptid_of (lp), filter))
+    return 0;
 
-  return child;
+  if (!lp->stopped)
+    return 1;
+
+  return 0;
 }
 
 /* This function should only be called if the LWP got a SIGTRAP.
@@ -1748,37 +1714,275 @@ cancel_breakpoint (struct lwp_info *lwp)
   return 0;
 }
 
+/* Do low-level handling of the event, and check if we should go on
+   and pass it to caller code.  Return the affected lwp if we are, or
+   NULL otherwise.  */
+
+static struct lwp_info *
+linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
+{
+  struct lwp_info *child;
+
+  child = find_lwp_pid (pid_to_ptid (lwpid));
+
+  /* If we didn't find a process, one of two things presumably happened:
+     - A process we started and then detached from has exited.  Ignore it.
+     - A process we are controlling has forked and the new child's stop
+     was reported to us by the kernel.  Save its PID.  */
+  if (child == NULL && WIFSTOPPED (wstat))
+    {
+      add_to_pid_list (&stopped_pids, lwpid, wstat);
+      return NULL;
+    }
+  else if (child == NULL)
+    return NULL;
+
+  child->stopped = 1;
+
+  child->last_status = wstat;
+
+  if (WIFSTOPPED (wstat))
+    {
+      struct process_info *proc;
+
+      /* Architecture-specific setup after inferior is running.  This
+	 needs to happen after we have attached to the inferior and it
+	 is stopped for the first time, but before we access any
+	 inferior registers.  */
+      proc = find_process_pid (pid_of (child));
+      if (proc->private->new_inferior)
+	{
+	  struct thread_info *saved_inferior;
+
+	  saved_inferior = current_inferior;
+	  current_inferior = get_lwp_thread (child);
+
+	  the_low_target.arch_setup ();
+
+	  current_inferior = saved_inferior;
+
+	  proc->private->new_inferior = 0;
+	}
+    }
+
+  /* Store the STOP_PC, with adjustment applied.  This depends on the
+     architecture being defined already (so that CHILD has a valid
+     regcache), and on LAST_STATUS being set (to check for SIGTRAP or
+     not).  */
+  if (WIFSTOPPED (wstat))
+    {
+      if (debug_threads
+	  && the_low_target.get_pc != NULL)
+	{
+	  struct thread_info *saved_inferior = current_inferior;
+	  struct regcache *regcache;
+	  CORE_ADDR pc;
+
+	  current_inferior = get_lwp_thread (child);
+	  regcache = get_thread_regcache (current_inferior, 1);
+	  pc = (*the_low_target.get_pc) (regcache);
+	  debug_printf ("linux_low_filter_event: pc is 0x%lx\n", (long) pc);
+	  current_inferior = saved_inferior;
+	}
+
+      child->stop_pc = get_stop_pc (child);
+    }
+
+  /* Fetch the possibly triggered data watchpoint info and store it in
+     CHILD.
+
+     On some archs, like x86, that use debug registers to set
+     watchpoints, it's possible that the way to know which watched
+     address trapped, is to check the register that is used to select
+     which address to watch.  Problem is, between setting the
+     watchpoint and reading back which data address trapped, the user
+     may change the set of watchpoints, and, as a consequence, GDB
+     changes the debug registers in the inferior.  To avoid reading
+     back a stale stopped-data-address when that happens, we cache in
+     LP the fact that a watchpoint trapped, and the corresponding data
+     address, as soon as we see CHILD stop with a SIGTRAP.  If GDB
+     changes the debug registers meanwhile, we have the cached data we
+     can rely on.  */
+
+  if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP)
+    {
+      if (the_low_target.stopped_by_watchpoint == NULL)
+	{
+	  child->stopped_by_watchpoint = 0;
+	}
+      else
+	{
+	  struct thread_info *saved_inferior;
+
+	  saved_inferior = current_inferior;
+	  current_inferior = get_lwp_thread (child);
+
+	  child->stopped_by_watchpoint
+	    = the_low_target.stopped_by_watchpoint ();
+
+	  if (child->stopped_by_watchpoint)
+	    {
+	      if (the_low_target.stopped_data_address != NULL)
+		child->stopped_data_address
+		  = the_low_target.stopped_data_address ();
+	      else
+		child->stopped_data_address = 0;
+	    }
+
+	  current_inferior = saved_inferior;
+	}
+    }
+
+  if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
+    {
+      linux_enable_event_reporting (lwpid_of (child));
+      child->must_set_ptrace_flags = 0;
+    }
+
+  if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
+      && wstat >> 16 != 0)
+    {
+      handle_extended_wait (child, wstat);
+      return NULL;
+    }
+
+  if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGSTOP
+      && child->stop_expected)
+    {
+      struct thread_info *thread;
+
+      if (debug_threads)
+	debug_printf ("Expected stop.\n");
+      child->stop_expected = 0;
+
+      thread = get_lwp_thread (child);
+
+      if (thread->last_resume_kind == resume_stop)
+	{
+	  /* We want to report the stop to the core.  Treat the
+	     SIGSTOP as a normal event.  */
+	}
+      else if (stopping_threads != NOT_STOPPING_THREADS)
+	{
+	  /* Stopping threads.  We don't want this SIGSTOP to end up
+	     pending in the FILTER_PTID handling below.  */
+	  return NULL;
+	}
+      else
+	{
+	  /* Filter out the event.  */
+	  linux_resume_one_lwp (child, child->stepping, 0, NULL);
+	  return NULL;
+	}
+    }
+
+    /* Check if the thread has exited.  */
+  if ((WIFEXITED (wstat) || WIFSIGNALED (wstat))
+      && num_lwps (pid_of (child)) > 1)
+    {
+      if (debug_threads)
+	debug_printf ("LLW: %ld exited.\n", lwpid_of (child));
+
+	 /* If there is at least one more LWP, then the exit signal
+	    was not the end of the debugged application and should be
+	    ignored.  */
+      delete_lwp (child);
+      return NULL;
+    }
+
+  if (!ptid_match (ptid_of (child), filter_ptid))
+    {
+      if (debug_threads)
+	debug_printf ("LWP %ld got an event %06x, leaving pending.\n",
+		      lwpid_of (child), wstat);
+
+      if (WIFSTOPPED (wstat))
+	{
+	  child->status_pending_p = 1;
+	  child->status_pending = wstat;
+
+	  if (WSTOPSIG (wstat) != SIGSTOP)
+	    {
+	      /* Cancel breakpoint hits.  The breakpoint may be
+		 removed before we fetch events from this process to
+		 report to the core.  It is best not to assume the
+		 moribund breakpoints heuristic always handles these
+		 cases --- it could be too many events go through to
+		 the core before this one is handled.  All-stop always
+		 cancels breakpoint hits in all threads.  */
+	      if (non_stop
+		  && WSTOPSIG (wstat) == SIGTRAP
+		  && cancel_breakpoint (child))
+		{
+		  /* Throw away the SIGTRAP.  */
+		  child->status_pending_p = 0;
+
+		  if (debug_threads)
+		    debug_printf ("LLW: LWP %ld hit a breakpoint while"
+				  " waiting for another process;"
+				  " cancelled it\n",
+				  lwpid_of (child));
+		}
+	    }
+	}
+      else if (WIFEXITED (wstat) || WIFSIGNALED (wstat))
+	{
+	  if (debug_threads)
+	    debug_printf ("LLWE: process %ld exited while fetching "
+			  "event from another LWP\n",
+			  lwpid_of (child));
+
+	  /* This was the last lwp in the process.  Since events are
+	     serialized to GDB core, and we can't report this one
+	     right now, but GDB core and the other target layers will
+	     want to be notified about the exit code/signal, leave the
+	     status pending for the next time we're able to report
+	     it.  */
+	  mark_lwp_dead (child, wstat);
+	}
+
+      return NULL;
+    }
+
+  return child;
+}
+
 /* When the event-loop is doing a step-over, this points at the thread
    being stepped.  */
 ptid_t step_over_bkpt;
 
-/* Wait for an event from child PID.  If PID is -1, wait for any
-   child.  Store the stop status through the status pointer WSTAT.
-   OPTIONS is passed to the waitpid call.  Return 0 if no child stop
-   event was found and OPTIONS contains WNOHANG.  Return the PID of
-   the stopped child otherwise.  */
+/* Wait for an event from child(ren) WAIT_PTID, and return any that
+   match FILTER_PTID (leaving others pending).  The PTIDs can be:
+   minus_one_ptid, to specify any child; a pid PTID, specifying all
+   lwps of a thread group; or a PTID representing a single lwp.  Store
+   the stop status through the status pointer WSTAT.  OPTIONS is
+   passed to the waitpid call.  Return 0 if no event was found and
+   OPTIONS contains WNOHANG.  Return -1 if no unwaited-for children
+   was found.  Return the PID of the stopped child otherwise.  */
 
 static int
-linux_wait_for_event (ptid_t ptid, int *wstat, int options)
+linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
+			       int *wstatp, int options)
 {
   struct lwp_info *event_child, *requested_child;
-  ptid_t wait_ptid;
+  sigset_t block_mask, prev_mask;
 
+ retry:
   event_child = NULL;
   requested_child = NULL;
 
   /* Check for a lwp with a pending status.  */
 
-  if (ptid_equal (ptid, minus_one_ptid) || ptid_is_pid (ptid))
+  if (ptid_equal (filter_ptid, minus_one_ptid) || ptid_is_pid (filter_ptid))
     {
       event_child = (struct lwp_info *)
-	find_inferior (&all_lwps, status_pending_p_callback, &ptid);
+	find_inferior (&all_lwps, status_pending_p_callback, &filter_ptid);
       if (debug_threads && event_child)
 	debug_printf ("Got a pending child %ld\n", lwpid_of (event_child));
     }
-  else
+  else if (!ptid_equal (filter_ptid, null_ptid))
     {
-      requested_child = find_lwp_pid (ptid);
+      requested_child = find_lwp_pid (filter_ptid);
 
       if (stopping_threads == NOT_STOPPING_THREADS
 	  && requested_child->status_pending_p
@@ -1803,146 +2007,140 @@ linux_wait_for_event (ptid_t ptid, int *wstat, int options)
     {
       if (debug_threads)
 	debug_printf ("Got an event from pending child %ld (%04x)\n",
-		      lwpid_of (event_child), event_child->status_pending);
-      *wstat = event_child->status_pending;
+		 lwpid_of (event_child), event_child->status_pending);
+      *wstatp = event_child->status_pending;
       event_child->status_pending_p = 0;
       event_child->status_pending = 0;
       current_inferior = get_lwp_thread (event_child);
       return lwpid_of (event_child);
     }
 
-  if (ptid_is_pid (ptid))
-    {
-      /* A request to wait for a specific tgid.  This is not possible
-	 with waitpid, so instead, we wait for any child, and leave
-	 children we're not interested in right now with a pending
-	 status to report later.  */
-      wait_ptid = minus_one_ptid;
-    }
-  else
-    wait_ptid = ptid;
+  /* But if we don't find a pending event, we'll have to wait.
+
+     We only enter this loop if no process has a pending wait status.
+     Thus any action taken in response to a wait status inside this
+     loop is responding as soon as we detect the status, not after any
+     pending events.  */
+
+  /* Make sure SIGCHLD is blocked until the sigsuspend below.  Block
+     all signals while here.  */
+  sigfillset (&block_mask);
+  sigprocmask (SIG_BLOCK, &block_mask, &prev_mask);
 
-  /* We only enter this loop if no process has a pending wait status.  Thus
-     any action taken in response to a wait status inside this loop is
-     responding as soon as we detect the status, not after any pending
-     events.  */
-  while (1)
+  while (event_child == NULL)
     {
-      event_child = linux_wait_for_lwp (wait_ptid, wstat, options);
+      pid_t ret = 0;
 
-      if ((options & WNOHANG) && event_child == NULL)
-	{
-	  if (debug_threads)
-	    debug_printf ("WNOHANG set, no event found\n");
-	  return 0;
-	}
+      /* Always use -1 and WNOHANG, due to couple of a kernel/ptrace
+	 quirks:
 
-      if (event_child == NULL)
-	error ("event from unknown child");
+	 - If the thread group leader exits while other threads in the
+	   thread group still exist, waitpid(TGID, ...) hangs.  That
+	   waitpid won't return an exit status until the other threads
+	   in the group are reaped.
 
-      if (ptid_is_pid (ptid)
-	  && ptid_get_pid (ptid) != ptid_get_pid (ptid_of (event_child)))
-	{
-	  if (! WIFSTOPPED (*wstat))
-	    mark_lwp_dead (event_child, *wstat);
-	  else
-	    {
-	      event_child->status_pending_p = 1;
-	      event_child->status_pending = *wstat;
-	    }
-	  continue;
-	}
+	   - When a non-leader thread execs, that thread just vanishes
+	     without reporting an exit (so we'd hang if we waited for
+	     it explicitly in that case).  The exec event is reported
+	     to the TGID pid (although we don't currently enable exec
+	     events).  */
+      errno = 0;
+      ret = my_waitpid (-1, wstatp, options | WNOHANG);
 
-      current_inferior = get_lwp_thread (event_child);
+      if (debug_threads)
+	debug_printf ("LWFE: waitpid(-1, ...) returned %d, %s\n",
+		      ret, errno ? strerror (errno) : "ERRNO-OK");
 
-      /* Check for thread exit.  */
-      if (! WIFSTOPPED (*wstat))
+      if (ret > 0)
 	{
 	  if (debug_threads)
-	    debug_printf ("LWP %ld exiting\n", lwpid_of (event_child));
-
-	  /* If the last thread is exiting, just return.  */
-	  if (last_thread_of_process_p (current_inferior))
 	    {
-	      if (debug_threads)
-		debug_printf ("LWP %ld is last lwp of process\n",
-			      lwpid_of (event_child));
-	      return lwpid_of (event_child);
+	      debug_printf ("LLW: waitpid %ld received %s\n",
+			    (long) ret, status_to_str (*wstatp));
 	    }
 
-	  if (!non_stop)
+	  event_child = linux_low_filter_event (filter_ptid,
+						ret, *wstatp);
+	  if (event_child != NULL)
 	    {
-	      current_inferior = (struct thread_info *) all_threads.head;
-	      if (debug_threads)
-		debug_printf ("Current inferior is now %ld\n",
-			      lwpid_of (get_thread_lwp (current_inferior)));
-	    }
-	  else
-	    {
-	      current_inferior = NULL;
-	      if (debug_threads)
-		debug_printf ("Current inferior is now <NULL>\n");
-	    }
-
-	  /* If we were waiting for this particular child to do something...
-	     well, it did something.  */
-	  if (requested_child != NULL)
-	    {
-	      int lwpid = lwpid_of (event_child);
-
-	      /* Cancel the step-over operation --- the thread that
-		 started it is gone.  */
-	      if (finish_step_over (event_child))
-		unstop_all_lwps (1, event_child);
-	      delete_lwp (event_child);
-	      return lwpid;
+	      /* We got an event to report to the core.  */
+	      break;
 	    }
 
-	  delete_lwp (event_child);
-
-	  /* Wait for a more interesting event.  */
+	  /* Retry until nothing comes out of waitpid.  A single
+	     SIGCHLD can indicate more than one child stopped.  */
 	  continue;
 	}
 
-      if (event_child->must_set_ptrace_flags)
+      /* Check for zombie thread group leaders.  Those can't be reaped
+	 until all other threads in the thread group are.  */
+      check_zombie_leaders ();
+
+      /* If there are no resumed children left in the set of LWPs we
+	 want to wait for, bail.  We can't just block in
+	 waitpid/sigsuspend, because lwps might have been left stopped
+	 in trace-stop state, and we'd be stuck forever waiting for
+	 their status to change (which would only happen if we resumed
+	 them).  Even if WNOHANG is set, this return code is preferred
+	 over 0 (below), as it is more detailed.  */
+      if ((find_inferior (&all_lwps, not_stopped_callback, &wait_ptid) == NULL))
 	{
-	  linux_enable_event_reporting (lwpid_of (event_child));
-	  event_child->must_set_ptrace_flags = 0;
+	  if (debug_threads)
+	    debug_printf ("LLW: exit (no unwaited-for LWP)\n");
+	  sigprocmask (SIG_SETMASK, &prev_mask, NULL);
+	  return -1;
 	}
 
-      if (WIFSTOPPED (*wstat) && WSTOPSIG (*wstat) == SIGTRAP
-	  && *wstat >> 16 != 0)
+      /* No interesting event to report to the caller.  */
+      if ((options & WNOHANG))
 	{
-	  handle_extended_wait (event_child, *wstat);
-	  continue;
+	  if (debug_threads)
+	    debug_printf ("WNOHANG set, no event found\n");
+
+	  sigprocmask (SIG_SETMASK, &prev_mask, NULL);
+	  return 0;
 	}
 
-      if (WIFSTOPPED (*wstat)
-	  && WSTOPSIG (*wstat) == SIGSTOP
-	  && event_child->stop_expected)
-	{
-	  int should_stop;
+      /* Block until we get an event reported with SIGCHLD.  */
+      if (debug_threads)
+	debug_printf ("sigsuspend'ing\n");
 
-	  if (debug_threads)
-	    debug_printf ("Expected stop.\n");
-	  event_child->stop_expected = 0;
+      sigsuspend (&prev_mask);
+      sigprocmask (SIG_SETMASK, &prev_mask, NULL);
+      goto retry;
+    }
 
-	  should_stop = (current_inferior->last_resume_kind == resume_stop
-			 || stopping_threads != NOT_STOPPING_THREADS);
+  sigprocmask (SIG_SETMASK, &prev_mask, NULL);
 
-	  if (!should_stop)
-	    {
-	      linux_resume_one_lwp (event_child,
-				    event_child->stepping, 0, NULL);
-	      continue;
-	    }
-	}
+  current_inferior = get_lwp_thread (event_child);
 
+  /* Check for thread exit.  */
+  if (! WIFSTOPPED (*wstatp))
+    {
+      gdb_assert (last_thread_of_process_p (pid_of (event_child)));
+
+      if (debug_threads)
+	debug_printf ("LWP %d is the last lwp of process.  "
+		      "Process %ld exiting.\n",
+		      pid_of (event_child), lwpid_of (event_child));
       return lwpid_of (event_child);
     }
 
-  /* NOTREACHED */
-  return 0;
+  return lwpid_of (event_child);
+}
+
+/* Wait for an event from child(ren) PTID.  PTIDs can be:
+   minus_one_ptid, to specify any child; a pid PTID, specifying all
+   lwps of a thread group; or a PTID representing a single lwp.  Store
+   the stop status through the status pointer WSTAT.  OPTIONS is
+   passed to the waitpid call.  Return 0 if no event was found and
+   OPTIONS contains WNOHANG.  Return -1 if no unwaited-for children
+   was found.  Return the PID of the stopped child otherwise.  */
+
+static int
+linux_wait_for_event (ptid_t ptid, int *wstatp, int options)
+{
+  return linux_wait_for_event_filtered (ptid, ptid, wstatp, options);
 }
 
 /* Count the LWP's that have had events.  */
@@ -2304,70 +2502,69 @@ retry:
       pid = linux_wait_for_event (step_over_bkpt, &w, options & ~WNOHANG);
     }
 
-  if (pid == 0) /* only if TARGET_WNOHANG */
+  if (pid == 0)
     {
+      gdb_assert (target_options & TARGET_WNOHANG);
+
       if (debug_threads)
 	{
-	  debug_printf ("linux_wait_1 ret = null_ptid\n");
+	  debug_printf ("linux_wait_1 ret = null_ptid, "
+			"TARGET_WAITKIND_IGNORE\n");
 	  debug_exit ();
 	}
+
+      ourstatus->kind = TARGET_WAITKIND_IGNORE;
       return null_ptid;
     }
+  else if (pid == -1)
+    {
+      if (debug_threads)
+	{
+	  debug_printf ("linux_wait_1 ret = null_ptid, "
+			"TARGET_WAITKIND_NO_RESUMED\n");
+	  debug_exit ();
+	}
 
-  event_child = get_thread_lwp (current_inferior);
-
-  /* If we are waiting for a particular child, and it exited,
-     linux_wait_for_event will return its exit status.  Similarly if
-     the last child exited.  If this is not the last child, however,
-     do not report it as exited until there is a 'thread exited' response
-     available in the remote protocol.  Instead, just wait for another event.
-     This should be safe, because if the thread crashed we will already
-     have reported the termination signal to GDB; that should stop any
-     in-progress stepping operations, etc.
+      ourstatus->kind = TARGET_WAITKIND_NO_RESUMED;
+      return null_ptid;
+    }
 
-     Report the exit status of the last thread to exit.  This matches
-     LinuxThreads' behavior.  */
+  event_child = get_thread_lwp (current_inferior);
 
-  if (last_thread_of_process_p (current_inferior))
+  /* linux_wait_for_event only returns an exit status for the last
+     child of a process.  Report it.  */
+  if (WIFEXITED (w) || WIFSIGNALED (w))
     {
-      if (WIFEXITED (w) || WIFSIGNALED (w))
+      if (WIFEXITED (w))
 	{
-	  if (WIFEXITED (w))
-	    {
-	      ourstatus->kind = TARGET_WAITKIND_EXITED;
-	      ourstatus->value.integer = WEXITSTATUS (w);
+	  ourstatus->kind = TARGET_WAITKIND_EXITED;
+	  ourstatus->value.integer = WEXITSTATUS (w);
 
-	      if (debug_threads)
-		{
-		  debug_printf ("linux_wait_1 ret = %s, exited with "
-				"retcode %d\n",
-				target_pid_to_str (ptid_of (event_child)),
-				WEXITSTATUS (w));
-		  debug_exit ();
-		}
-	    }
-	  else
+	  if (debug_threads)
 	    {
-	      ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
-	      ourstatus->value.sig = gdb_signal_from_host (WTERMSIG (w));
-
-	      if (debug_threads)
-		{
-		  debug_printf ("linux_wait_1 ret = %s, terminated with "
-				"signal %d\n",
-				target_pid_to_str (ptid_of (event_child)),
-				WTERMSIG (w));
-		  debug_exit ();
-		}
+	      debug_printf ("linux_wait_1 ret = %s, exited with "
+			    "retcode %d\n",
+			    target_pid_to_str (ptid_of (event_child)),
+			    WEXITSTATUS (w));
+	      debug_exit ();
 	    }
+	}
+      else
+	{
+	  ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+	  ourstatus->value.sig = gdb_signal_from_host (WTERMSIG (w));
 
-	  return ptid_of (event_child);
+	  if (debug_threads)
+	    {
+	      debug_printf ("linux_wait_1 ret = %s, terminated with "
+			    "signal %d\n",
+			    target_pid_to_str (ptid_of (event_child)),
+			    WTERMSIG (w));
+	      debug_exit ();
+	    }
 	}
-    }
-  else
-    {
-      if (!WIFSTOPPED (w))
-	goto retry;
+
+      return ptid_of (event_child);
     }
 
   /* If this event was not handled before, and is not a SIGTRAP, we
@@ -2906,23 +3103,15 @@ mark_lwp_dead (struct lwp_info *lwp, int wstat)
   lwp->stop_expected = 0;
 }
 
+/* Wait for all children to stop for the SIGSTOPs we just queued.  */
+
 static void
-wait_for_sigstop (struct inferior_list_entry *entry)
+wait_for_sigstop (void)
 {
-  struct lwp_info *lwp = (struct lwp_info *) entry;
   struct thread_info *saved_inferior;
-  int wstat;
   ptid_t saved_tid;
-  ptid_t ptid;
-  int pid;
-
-  if (lwp->stopped)
-    {
-      if (debug_threads)
-	debug_printf ("wait_for_sigstop: LWP %ld already stopped\n",
-		      lwpid_of (lwp));
-      return;
-    }
+  int wstat;
+  int ret;
 
   saved_inferior = current_inferior;
   if (saved_inferior != NULL)
@@ -2930,50 +3119,15 @@ wait_for_sigstop (struct inferior_list_entry *entry)
   else
     saved_tid = null_ptid; /* avoid bogus unused warning */
 
-  ptid = lwp->head.id;
-
   if (debug_threads)
-    debug_printf ("wait_for_sigstop: pulling one event\n");
+    debug_printf ("wait_for_sigstop: pulling events\n");
 
-  pid = linux_wait_for_event (ptid, &wstat, __WALL);
-
-  /* If we stopped with a non-SIGSTOP signal, save it for later
-     and record the pending SIGSTOP.  If the process exited, just
-     return.  */
-  if (WIFSTOPPED (wstat))
-    {
-      if (debug_threads)
-	debug_printf ("LWP %ld stopped with signal %d\n",
-		      lwpid_of (lwp), WSTOPSIG (wstat));
-
-      if (WSTOPSIG (wstat) != SIGSTOP)
-	{
-	  if (debug_threads)
-	    debug_printf ("LWP %ld stopped with non-sigstop status %06x\n",
-			  lwpid_of (lwp), wstat);
-
-	  lwp->status_pending_p = 1;
-	  lwp->status_pending = wstat;
-	}
-    }
-  else
-    {
-      if (debug_threads)
-	debug_printf ("Process %d exited while stopping LWPs\n", pid);
-
-      lwp = find_lwp_pid (pid_to_ptid (pid));
-      if (lwp)
-	{
-	  /* Leave this status pending for the next time we're able to
-	     report it.  In the mean time, we'll report this lwp as
-	     dead to GDB, so GDB doesn't try to read registers and
-	     memory from it.  This can only happen if this was the
-	     last thread of the process; otherwise, PID is removed
-	     from the thread tables before linux_wait_for_event
-	     returns.  */
-	  mark_lwp_dead (lwp, wstat);
-	}
-    }
+  /* Passing NULL_PTID as filter indicates we want all events to be
+     left pending.  Eventually this returns when there are no
+     unwaited-for children left.  */
+  ret = linux_wait_for_event_filtered (minus_one_ptid, null_ptid,
+				       &wstat, __WALL);
+  gdb_assert (ret == -1);
 
   if (saved_inferior == NULL || linux_thread_alive (saved_tid))
     current_inferior = saved_inferior;
@@ -3099,7 +3253,7 @@ stop_all_lwps (int suspend, struct lwp_info *except)
     find_inferior (&all_lwps, suspend_and_send_sigstop_callback, except);
   else
     find_inferior (&all_lwps, send_sigstop_callback, except);
-  for_each_inferior (&all_lwps, wait_for_sigstop);
+  wait_for_sigstop ();
   stopping_threads = NOT_STOPPING_THREADS;
 
   if (debug_threads)
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 88354be..0f50afe 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2321,8 +2321,18 @@ resume (struct thread_resume *actions, size_t num_actions)
     {
       last_ptid = mywait (minus_one_ptid, &last_status, 0, 1);
 
+      if (last_status.kind == TARGET_WAITKIND_NO_RESUMED)
+	{
+	  /* No proper RSP support for this yet.  At least return
+	     error.  */
+	  sprintf (own_buf, "E.No unwaited-for children left.");
+	  disable_async_io ();
+	  return;
+	}
+
       if (last_status.kind != TARGET_WAITKIND_EXITED
-          && last_status.kind != TARGET_WAITKIND_SIGNALLED)
+          && last_status.kind != TARGET_WAITKIND_SIGNALLED
+	  && last_status.kind != TARGET_WAITKIND_NO_RESUMED)
 	current_inferior->last_status = last_status;
 
       /* From the client's perspective, all-stop mode always stops all
@@ -3848,7 +3858,11 @@ handle_target_event (int err, gdb_client_data client_data)
   last_ptid = mywait (minus_one_ptid, &last_status,
 		      TARGET_WNOHANG, 1);
 
-  if (last_status.kind != TARGET_WAITKIND_IGNORE)
+  if (last_status.kind == TARGET_WAITKIND_NO_RESUMED)
+    {
+      /* No RSP support for this yet.  */
+    }
+  else if (last_status.kind != TARGET_WAITKIND_IGNORE)
     {
       int pid = ptid_get_pid (last_ptid);
       struct process_info *process = find_process_pid (pid);
-- 
1.7.11.7


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]