aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
Diffstat (limited to 'gdb')
-rw-r--r--gdb/gdbserver/ChangeLog28
-rw-r--r--gdb/gdbserver/inferiors.h22
-rw-r--r--gdb/gdbserver/linux-low.c852
-rw-r--r--gdb/gdbserver/server.c18
4 files changed, 568 insertions, 352 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 8eb3e28..eab7e8d 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,31 @@
+2014-02-27 Pedro Alves <palves@redhat.com>
+
+ PR 12702
+ * inferiors.h (A_I_NEXT, ALL_INFERIORS_TYPE, ALL_PROCESSES): New
+ macros.
+ * linux-low.c (delete_lwp, handle_extended_wait): Add debug
+ output.
+ (last_thread_of_process_p): Take a PID argument instead of a
+ thread pointer.
+ (linux_wait_for_lwp): Delete.
+ (num_lwps, check_zombie_leaders, not_stopped_callback): New
+ functions.
+ (linux_low_filter_event): New function, party factored out from
+ linux_wait_for_event.
+ (linux_wait_for_event): Rename to ...
+ (linux_wait_for_event_filtered): ... this. Add new filter ptid
+ argument. Partly rewrite. Always use waitpid(-1, WNOHANG) and
+ sigsuspend. Check for zombie leaders.
+ (linux_wait_for_event): Reimplement as wrapper around
+ linux_wait_for_event_filtered.
+ (linux_wait_1): Handle TARGET_WAITKIND_NO_RESUMED. Assume that if
+ a normal or signal exit is seen, it's the whole process exiting.
+ (wait_for_sigstop): No longer a for_each_inferior callback.
+ Rewrite on top of linux_wait_for_event_filtered.
+ (stop_all_lwps): Call wait_for_sigstop directly.
+ * server.c (resume, handle_target_event): Handle
+ TARGET_WAITKIND_NO_RESUMED.
+
2014-02-26 Joel Brobecker <brobecker@adacore.com>
* win32-low.c (psapi_get_dll_name,
diff --git a/gdb/gdbserver/inferiors.h b/gdb/gdbserver/inferiors.h
index 9ea55df..f584339 100644
--- a/gdb/gdbserver/inferiors.h
+++ b/gdb/gdbserver/inferiors.h
@@ -99,6 +99,28 @@ void clear_inferior_list (struct inferior_list *list);
int one_inferior_p (struct inferior_list *list);
+/* 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 923ee14..2d8d5f5 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -214,6 +214,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 struct lwp_info *add_lwp (ptid_t ptid);
static int linux_stopped_by_watchpoint (void);
@@ -276,7 +278,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. */
@@ -335,7 +337,12 @@ linux_pid_exe_is_elf_64_file (int pid, unsigned int *machine)
static void
delete_lwp (struct lwp_info *lwp)
{
- remove_thread (get_lwp_thread (lwp));
+ struct thread_info *thr = get_lwp_thread (lwp);
+
+ if (debug_threads)
+ debug_printf ("deleting %ld\n", lwpid_of (thr));
+
+ remove_thread (thr);
free (lwp->arch_private);
free (lwp);
}
@@ -396,6 +403,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_thr), new_pid);
+
ptid = ptid_build (pid_of (event_thr), new_pid, 0);
new_lwp = add_lwp (ptid);
@@ -847,10 +859,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 = thread->entry.id;
- int pid = ptid_get_pid (ptid);
struct counter counter = { pid , 0 };
return (find_inferior (&all_threads,
@@ -1252,152 +1262,107 @@ find_lwp_pid (ptid_t ptid)
return get_thread_lwp ((struct thread_info *) thread);
}
-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;
- struct thread_info *thread;
-
- 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;
-
-retry:
+/* Return the number of known LWPs in the tgid given by PID. */
- 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));
- if (child != NULL)
- thread = get_lwp_thread (child);
- else
- thread = NULL;
+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_threads, 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 (thread));
- if (proc->private->new_inferior)
- {
- struct thread_info *saved_inferior;
-
- saved_inferior = current_inferior;
- current_inferior = thread;
-
- 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 thread_info *thr = (struct thread_info *) entry;
+ struct lwp_info *lwp;
+ 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 (thr), filter))
+ return 0;
- return child;
+ lwp = get_thread_lwp (thr);
+ if (!lwp->stopped)
+ return 1;
+
+ return 0;
}
/* This function should only be called if the LWP got a SIGTRAP.
@@ -1749,23 +1714,259 @@ 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;
+ struct thread_info *thread;
+
+ 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;
+
+ thread = get_lwp_thread (child);
+
+ 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 (thread));
+ if (proc->private->new_inferior)
+ {
+ struct thread_info *saved_inferior;
+
+ saved_inferior = current_inferior;
+ current_inferior = thread;
+
+ 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;
+ struct regcache *regcache;
+ CORE_ADDR pc;
+
+ saved_inferior = current_inferior;
+ current_inferior = thread;
+ 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 = thread;
+
+ 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);
+ 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)
+ {
+ if (debug_threads)
+ debug_printf ("Expected stop.\n");
+ child->stop_expected = 0;
+
+ 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 (thread)) > 1)
+ {
+ if (debug_threads)
+ debug_printf ("LLW: %d exited.\n", lwpid);
+
+ /* 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 (thread), filter_ptid))
+ {
+ if (debug_threads)
+ debug_printf ("LWP %d got an event %06x, leaving pending.\n",
+ lwpid, 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 %d hit a breakpoint while"
+ " waiting for another process;"
+ " cancelled it\n", lwpid);
+ }
+ }
+ }
+ else if (WIFEXITED (wstat) || WIFSIGNALED (wstat))
+ {
+ if (debug_threads)
+ debug_printf ("LLWE: process %d exited while fetching "
+ "event from another LWP\n", lwpid);
+
+ /* 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 and update current_inferior 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 thread_info *event_thread;
struct lwp_info *event_child, *requested_child;
- ptid_t wait_ptid;
+ sigset_t block_mask, prev_mask;
+ retry:
/* N.B. event_thread points to the thread_info struct that contains
event_child. Keep them in sync. */
event_thread = NULL;
@@ -1774,18 +1975,18 @@ linux_wait_for_event (ptid_t ptid, int *wstat, int options)
/* 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_thread = (struct thread_info *)
- find_inferior (&all_threads, status_pending_p_callback, &ptid);
+ find_inferior (&all_threads, status_pending_p_callback, &filter_ptid);
if (event_thread != NULL)
event_child = get_thread_lwp (event_thread);
if (debug_threads && event_thread)
debug_printf ("Got a pending child %ld\n", lwpid_of (event_thread));
}
- 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
@@ -1814,146 +2015,142 @@ 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_thread), event_child->status_pending);
- *wstat = event_child->status_pending;
+ *wstatp = event_child->status_pending;
event_child->status_pending_p = 0;
event_child->status_pending = 0;
current_inferior = event_thread;
return lwpid_of (event_thread);
}
- 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.
- event_thread = get_lwp_thread (event_child);
- if (ptid_is_pid (ptid)
- && ptid_get_pid (ptid) != ptid_get_pid (ptid_of (event_thread)))
- {
- 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 = event_thread;
+ 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_thread));
-
- /* 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_thread));
- return lwpid_of (event_thread);
- }
-
- if (!non_stop)
- {
- current_inferior = get_first_thread ();
- if (debug_threads)
- debug_printf ("Current inferior is now %ld\n",
- lwpid_of (current_inferior));
- }
- else
{
- current_inferior = NULL;
- if (debug_threads)
- debug_printf ("Current inferior is now <NULL>\n");
+ debug_printf ("LLW: waitpid %ld received %s\n",
+ (long) ret, status_to_str (*wstatp));
}
- /* If we were waiting for this particular child to do something...
- well, it did something. */
- if (requested_child != NULL)
+ event_child = linux_low_filter_event (filter_ptid,
+ ret, *wstatp);
+ if (event_child != NULL)
{
- int lwpid = lwpid_of (event_thread);
-
- /* 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. */
+ event_thread = get_lwp_thread (event_child);
+ 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_threads,
+ not_stopped_callback,
+ &wait_ptid) == NULL))
{
- linux_enable_event_reporting (lwpid_of (event_thread));
- 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 = event_thread;
+
+ /* Check for thread exit. */
+ if (! WIFSTOPPED (*wstatp))
+ {
+ gdb_assert (last_thread_of_process_p (pid_of (event_thread)));
+ if (debug_threads)
+ debug_printf ("LWP %d is the last lwp of process. "
+ "Process %ld exiting.\n",
+ pid_of (event_thread), lwpid_of (event_thread));
return lwpid_of (event_thread);
}
- /* NOTREACHED */
- return 0;
+ return lwpid_of (event_thread);
+}
+
+/* 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. */
@@ -2322,70 +2519,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 (current_inferior)),
- 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 (current_inferior)),
- WTERMSIG (w));
- debug_exit ();
- }
+ debug_printf ("linux_wait_1 ret = %s, exited with "
+ "retcode %d\n",
+ target_pid_to_str (ptid_of (current_inferior)),
+ WEXITSTATUS (w));
+ debug_exit ();
}
+ }
+ else
+ {
+ ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+ ourstatus->value.sig = gdb_signal_from_host (WTERMSIG (w));
- return ptid_of (current_inferior);
+ if (debug_threads)
+ {
+ debug_printf ("linux_wait_1 ret = %s, terminated with "
+ "signal %d\n",
+ target_pid_to_str (ptid_of (current_inferior)),
+ WTERMSIG (w));
+ debug_exit ();
+ }
}
- }
- else
- {
- if (!WIFSTOPPED (w))
- goto retry;
+
+ return ptid_of (current_inferior);
}
/* If this event was not handled before, and is not a SIGTRAP, we
@@ -2929,24 +3125,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 thread_info *thread = (struct thread_info *) entry;
- struct lwp_info *lwp = get_thread_lwp (thread);
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 (thread));
- return;
- }
+ int wstat;
+ int ret;
saved_inferior = current_inferior;
if (saved_inferior != NULL)
@@ -2954,50 +3141,15 @@ wait_for_sigstop (struct inferior_list_entry *entry)
else
saved_tid = null_ptid; /* avoid bogus unused warning */
- ptid = thread->entry.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 (thread), WSTOPSIG (wstat));
-
- if (WSTOPSIG (wstat) != SIGSTOP)
- {
- if (debug_threads)
- debug_printf ("LWP %ld stopped with non-sigstop status %06x\n",
- lwpid_of (thread), 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;
@@ -3124,7 +3276,7 @@ stop_all_lwps (int suspend, struct lwp_info *except)
find_inferior (&all_threads, suspend_and_send_sigstop_callback, except);
else
find_inferior (&all_threads, send_sigstop_callback, except);
- for_each_inferior (&all_threads, 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 115aca4..9868af6 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2367,8 +2367,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
@@ -3897,7 +3907,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);