This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 4/5] Teach gdbserver's linux backend about no unwaited-for children (TARGET_WAITDKIND_NO_RESUMED).
- From: Pedro Alves <palves at redhat dot com>
- To: gdb-patches at sourceware dot org
- Cc: teawater at gmail dot com, luis_gustavo at mentor dot com
- Date: Thu, 23 Jan 2014 14:10:08 +0000
- Subject: [PATCH 4/5] Teach gdbserver's linux backend about no unwaited-for children (TARGET_WAITDKIND_NO_RESUMED).
- Authentication-results: sourceware.org; auth=none
- References: <52E12075 dot 3040601 at redhat dot com> <1390486209-8167-1-git-send-email-palves at redhat dot com>
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