/* Prototypes for local functions. */
static int stop_wait_callback (struct lwp_info *lp, void *data);
\f
+/* Convert wait status STATUS to a string. Used for printing debug
+ messages only. */
+static char *
+status_to_str (int status)
+{
+ static char buf[64];
+
+ if (WIFSTOPPED (status))
+ snprintf (buf, sizeof (buf), "%s (stopped)",
+ strsignal (WSTOPSIG (status)));
+ else if (WIFSIGNALED (status))
+ snprintf (buf, sizeof (buf), "%s (terminated)",
+ strsignal (WSTOPSIG (status)));
+ else
+ snprintf (buf, sizeof (buf), "%d (exited)",
+ WEXITSTATUS (status));
+
+ return buf;
+}
+\f
/* Initialize the list of LWPs. Note that this module, contrary to
what GDB's generic threads layer does for its thread list,
re-initializes the LWP lists whenever we mourn or detach (which
{
struct thread_info *tp;
-#if 1
+#if 0
/* FIXME: kettenis/2000-08-26: This should really be handled
properly by core GDB. */
pid_t pid;
int status;
- get_another_event:
gdb_assert (lp->status == 0);
pid = waitpid (GET_LWP (lp->ptid), &status,
}
gdb_assert (WIFSTOPPED (status));
- lp->stopped = 1;
if (WSTOPSIG (status) != SIGSTOP)
{
- if (WSTOPSIG (status) == SIGTRAP
- && breakpoint_inserted_here_p (read_pc_pid (pid_to_ptid (pid))
- - DECR_PC_AFTER_BREAK))
+ if (WSTOPSIG (status) == SIGTRAP)
{
/* If a LWP other than the LWP that we're reporting an
event for has hit a GDB breakpoint (as opposed to
user will delete or disable the breakpoint, but the
thread will have already tripped on it. */
- if (debug_lin_lwp)
- fprintf_unfiltered (gdb_stdlog,
- "Tripped breakpoint at %lx in LWP %d"
- " while waiting for SIGSTOP.\n",
- (long) read_pc_pid (lp->ptid), pid);
-
- /* Set the PC to before the trap. */
- if (DECR_PC_AFTER_BREAK)
- write_pc_pid (read_pc_pid (pid_to_ptid (pid))
- - DECR_PC_AFTER_BREAK,
- pid_to_ptid (pid));
-
/* Now resume this LWP and get the SIGSTOP event. */
ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
- goto get_another_event;
+ if (debug_lin_lwp)
+ {
+ fprintf_unfiltered (gdb_stderr,
+ "SWC: Candidate SIGTRAP event in %ld\n",
+ GET_LWP (lp->ptid));
+ }
+ /* Hold the SIGTRAP for handling by lin_lwp_wait. */
+ stop_wait_callback (lp, data);
+ /* If there's another event, throw it back into the queue. */
+ if (lp->status)
+ kill (GET_LWP (lp->ptid), WSTOPSIG (lp->status));
+ /* Save the sigtrap event. */
+ lp->status = status;
+ return 0;
}
else if (WSTOPSIG (status) == SIGINT &&
signal_pass_state (SIGINT) == 0)
just ignore all SIGINT events from all lwp's except for
the one that was caught by lin_lwp_wait. */
- /* Now resume this LWP and get the SIGSTP event. */
+ /* Now resume this LWP and get the SIGSTOP event. */
ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
- goto get_another_event;
+ return stop_wait_callback (lp, data);
}
else
{
+ /* The thread was stopped with a signal other than
+ SIGSTOP, and didn't accidentally trip a breakpoint. */
+
if (debug_lin_lwp)
- fprintf_unfiltered (gdb_stdlog,
- "Received %s in LWP %d while waiting for SIGSTOP.\n",
- strsignal (WSTOPSIG (status)), pid);
+ {
+ fprintf_unfiltered (gdb_stderr,
+ "SWC: Pending event %d in %ld\n",
+ WSTOPSIG (status), GET_LWP (lp->ptid));
+ }
+ /* Now resume this LWP and get the SIGSTOP event. */
+ ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
- /* The thread was stopped with a signal other than
- SIGSTOP, and didn't accidentally trip a breakpoint.
- Record the wait status. */
- lp->status = status;
+ /* Hold this event/waitstatus while we check to see if
+ there are any more (we still want to get that SIGSTOP). */
+ stop_wait_callback (lp, data);
+ /* If the lp->status field is still empty, use it to hold
+ this event. If not, then this event must be returned
+ to the event queue of the LWP. */
+ if (lp->status == 0)
+ lp->status = status;
+ else
+ kill (GET_LWP (lp->ptid), WSTOPSIG (status));
+ return 0;
}
}
else
{
/* We caught the SIGSTOP that we intended to catch, so
there's no SIGSTOP pending. */
+ lp->stopped = 1;
lp->signalled = 0;
}
}
return (lp->stopped == 0);
}
+/* Count the LWP's that have had events. */
+
+static int
+count_events_callback (struct lwp_info *lp, void *data)
+{
+ int *count = data;
+
+ gdb_assert (count != NULL);
+
+ /* Count only LWPs that have a SIGTRAP event pending. */
+ if (lp->status != 0
+ && WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP)
+ (*count)++;
+
+ return 0;
+}
+
+/* Select the LWP (if any) that is currently being single-stepped. */
+
+static int
+select_singlestep_lwp_callback (struct lwp_info *lp, void *data)
+{
+ if (lp->step && lp->status != 0)
+ return 1;
+ else
+ return 0;
+}
+
+/* Select the Nth LWP that has had a SIGTRAP event. */
+
+static int
+select_event_lwp_callback (struct lwp_info *lp, void *data)
+{
+ int *selector = data;
+
+ gdb_assert (selector != NULL);
+
+ /* Select only LWPs that have a SIGTRAP event pending. */
+ if (lp->status != 0
+ && WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP)
+ if ((*selector)-- == 0)
+ return 1;
+
+ return 0;
+}
+
+static int
+cancel_breakpoints_callback (struct lwp_info *lp, void *data)
+{
+ struct lwp_info *event_lp = data;
+
+ /* Leave the LWP that has been elected to receive a SIGTRAP alone. */
+ if (lp == event_lp)
+ return 0;
+
+ /* If a LWP other than the LWP that we're reporting an event for has
+ hit a GDB breakpoint (as opposed to some random trap signal),
+ then just arrange for it to hit it again later. We don't keep
+ the SIGTRAP status and don't forward the SIGTRAP signal to the
+ LWP. We will handle the current event, eventually we will resume
+ all LWPs, and this one will get its breakpoint trap again.
+
+ If we do not do this, then we run the risk that the user will
+ delete or disable the breakpoint, but the LWP will have already
+ tripped on it. */
+
+ if (lp->status != 0
+ && WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP
+ && breakpoint_inserted_here_p (read_pc_pid (lp->ptid) -
+ DECR_PC_AFTER_BREAK))
+ {
+ if (debug_lin_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "Push back breakpoint for LWP %ld\n",
+ GET_LWP (lp->ptid));
+
+ /* Back up the PC if necessary. */
+ if (DECR_PC_AFTER_BREAK)
+ write_pc_pid (read_pc_pid (lp->ptid) - DECR_PC_AFTER_BREAK, lp->ptid);
+
+ /* Throw away the SIGTRAP. */
+ lp->status = 0;
+ }
+
+ return 0;
+}
+
+/* Select one LWP out of those that have events pending. */
+
+static void
+select_event_lwp (struct lwp_info **orig_lp, int *status)
+{
+ int num_events = 0;
+ int random_selector;
+ struct lwp_info *event_lp;
+
+ /* Record the wait status for the origional LWP. */
+ (*orig_lp)->status = *status;
+
+ /* Give preference to any LWP that is being single-stepped. */
+ event_lp = iterate_over_lwps (select_singlestep_lwp_callback, NULL);
+ if (event_lp != NULL)
+ {
+ if (debug_lin_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "Select single-step LWP %ld\n",
+ GET_LWP (event_lp->ptid));
+ }
+ else
+ {
+ /* No single-stepping LWP. Select one at random, out of those
+ which have had SIGTRAP events. */
+
+ /* First see how many SIGTRAP events we have. */
+ iterate_over_lwps (count_events_callback, &num_events);
+
+ /* Now randomly pick a LWP out of those that have had a SIGTRAP. */
+ random_selector = (int)
+ ((num_events * (double) rand ()) / (RAND_MAX + 1.0));
+
+ if (debug_lin_lwp && num_events > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "Found %d SIGTRAP events, selecting #%d\n",
+ num_events, random_selector);
+
+ event_lp = iterate_over_lwps (select_event_lwp_callback,
+ &random_selector);
+ }
+
+ if (event_lp != NULL)
+ {
+ /* Switch the event LWP. */
+ *orig_lp = event_lp;
+ *status = event_lp->status;
+ }
+
+ /* Flush the wait status for the event LWP. */
+ (*orig_lp)->status = 0;
+}
+
/* Return non-zero if LP has been resumed. */
static int
/* First check if there is a LWP with a wait status pending. */
if (pid == -1)
{
- /* Any LWP will do. */
+ /* Any LWP that's been resumed will do. */
lp = iterate_over_lwps (status_callback, NULL);
if (lp)
{
- if (debug_lin_lwp)
- fprintf_unfiltered (gdb_stdlog,
- "Using pending wait status for LWP %ld.\n",
- GET_LWP (lp->ptid));
-
status = lp->status;
lp->status = 0;
+
+ if (debug_lin_lwp && status)
+ fprintf_unfiltered (gdb_stdlog,
+ "Using pending wait status %s for LWP %ld.\n",
+ status_to_str (status), GET_LWP (lp->ptid));
}
/* But if we don't fine one, we'll have to wait, and check both
status = lp->status;
lp->status = 0;
- if (debug_lin_lwp)
- if (status)
- fprintf_unfiltered (gdb_stdlog,
- "Using pending wait status for LWP %ld.\n",
- GET_LWP (lp->ptid));
+ if (debug_lin_lwp && status)
+ fprintf_unfiltered (gdb_stdlog,
+ "Using pending wait status %s for LWP %ld.\n",
+ status_to_str (status), GET_LWP (lp->ptid));
/* If we have to wait, take into account whether PID is a cloned
process or not. And we have to convert it to something that
/* This LWP is stopped now. */
lp->stopped = 1;
+ if (debug_lin_lwp)
+ fprintf_unfiltered (gdb_stdlog, "Candidate event %s in LWP %ld.\n",
+ status_to_str (status), GET_LWP (lp->ptid));
+
/* Now stop all other LWP's ... */
iterate_over_lwps (stop_callback, NULL);
longer running. */
iterate_over_lwps (stop_wait_callback, NULL);
+ /* If we're not waiting for a specific LWP, choose an event LWP from
+ among those that have had events. Giving equal priority to all
+ LWPs that have had events helps prevent starvation. */
+ if (pid == -1)
+ select_event_lwp (&lp, &status);
+
+ /* Now that we've selected our final event LWP, cancel any
+ breakpoints in other LWPs that have hit a GDB breakpoint. See
+ the comment in cancel_breakpoints_callback to find out why. */
+ iterate_over_lwps (cancel_breakpoints_callback, lp);
+
/* If we're not running in "threaded" mode, we'll report the bare
process id. */
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP)
- trap_ptid = (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid)));
+ {
+ trap_ptid = (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid)));
+ if (debug_lin_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLW: trap_ptid is %ld\n",
+ GET_LWP (trap_ptid));
+ }
else
trap_ptid = null_ptid;