aboutsummaryrefslogtreecommitdiff
path: root/gdb/lin-lwp.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/lin-lwp.c')
-rw-r--r--gdb/lin-lwp.c253
1 files changed, 213 insertions, 40 deletions
diff --git a/gdb/lin-lwp.c b/gdb/lin-lwp.c
index 91f3691..c36f523 100644
--- a/gdb/lin-lwp.c
+++ b/gdb/lin-lwp.c
@@ -458,7 +458,7 @@ resume_callback (struct lwp_info *lp, void *data)
{
struct thread_info *tp;
-#if 1
+#if 0
/* FIXME: kettenis/2000-08-26: This should really be handled
properly by core GDB. */
@@ -585,7 +585,6 @@ stop_wait_callback (struct lwp_info *lp, void *data)
pid_t pid;
int status;
- get_another_event:
gdb_assert (lp->status == 0);
pid = waitpid (GET_LWP (lp->ptid), &status,
@@ -619,13 +618,10 @@ stop_wait_callback (struct lwp_info *lp, void *data)
}
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
@@ -640,21 +636,22 @@ stop_wait_callback (struct lwp_info *lp, void *data)
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)
@@ -664,27 +661,42 @@ stop_wait_callback (struct lwp_info *lp, void *data)
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;
}
}
@@ -710,6 +722,141 @@ running_callback (struct lwp_info *lp, void *data)
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;
+
+ /* Count only threads that have a SIGTRAP pending. */
+ if (lp->status != 0 &&
+ WIFSTOPPED (lp->status) &&
+ WSTOPSIG (lp->status) == SIGTRAP &&
+ count != NULL) /* paranoia */
+ (*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;
+
+ /* Select only threads that have a SIGTRAP event pending. */
+ if (lp->status != 0 &&
+ WIFSTOPPED (lp->status) &&
+ WSTOPSIG (lp->status) == SIGTRAP &&
+ selector != NULL) /* paranoia */
+ if ((*selector)-- == 0)
+ return 1;
+
+ return 0;
+}
+
+static int
+cancel_breakpoints_callback (struct lwp_info *lp, void *data)
+{
+ struct lwp_info *event_lp = data;
+
+ if (lp != event_lp &&
+ 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 BP for %ld\n",
+ GET_LWP (lp->ptid));
+ }
+ /* For each lp except the event lp, if there was a trap,
+ set the PC to before the trap. */
+ if (DECR_PC_AFTER_BREAK)
+ {
+ write_pc_pid (read_pc_pid (lp->ptid) - DECR_PC_AFTER_BREAK,
+ lp->ptid);
+ }
+ lp->status = 0;
+ }
+ return 0;
+}
+
+/* Select one LWP out of those that have events to be the event thread. */
+
+static void
+select_event_lwp (struct lwp_info **orig_lp, int *status)
+{
+ int num_events = 0;
+ int random_selector;
+ struct lwp_info *event_lp;
+
+ /* 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 singlestep 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, (void *) &num_events);
+
+ /* OK, now randomly pick the Nth LWP of those that have had SIGTRAP. */
+ random_selector = (int)
+ ((num_events * (double) rand ()) / (RAND_MAX + 1.0));
+
+ if (debug_lin_lwp)
+ {
+ if (num_events > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "Found %d SIGTRAP events, selecting #%d\n",
+ num_events, random_selector);
+ else if (num_events <= 0)
+ fprintf_unfiltered (gdb_stdlog,
+ "ERROR select_event_lwp %d events!\n",
+ num_events);
+ }
+
+ event_lp = iterate_over_lwps (select_event_lwp_callback,
+ (void *) &random_selector);
+ }
+
+ if (event_lp != NULL)
+ {
+ /* "event_lp" is now the selected event thread.
+ If any other threads have had SIGTRAP events, these events
+ must now be cancelled, so that the respective thread will
+ trip the breakpoint again once it is resumed. */
+ iterate_over_lwps (cancel_breakpoints_callback, (void *) event_lp);
+ *orig_lp = event_lp;
+ *status = event_lp->status;
+ event_lp->status = 0;
+ }
+}
+
/* Return non-zero if LP has been resumed. */
static int
@@ -741,17 +888,19 @@ lin_lwp_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
/* 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 %d for LWP %ld.\n",
+ WIFSTOPPED (status) ? WSTOPSIG (status) :
+ WIFSIGNALED (status) ? WTERMSIG (status) :
+ WEXITSTATUS (status), GET_LWP (lp->ptid));
}
/* But if we don't fine one, we'll have to wait, and check both
@@ -772,11 +921,12 @@ lin_lwp_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
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 %d for LWP %ld.\n",
+ WIFSTOPPED (status) ? WSTOPSIG (status) :
+ WIFSIGNALED (status) ? WTERMSIG (status) :
+ WEXITSTATUS (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
@@ -947,6 +1097,17 @@ lin_lwp_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
/* This LWP is stopped now. */
lp->stopped = 1;
+ if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP)
+ {
+ /* Save SIGTRAP event for select_event_lwp. */
+ lp->status = status;
+ }
+
+ if (debug_lin_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLW: Candidate event %d in %ld\n",
+ WSTOPSIG (status), GET_LWP (lp->ptid));
+
/* Now stop all other LWP's ... */
iterate_over_lwps (stop_callback, NULL);
@@ -954,11 +1115,23 @@ lin_lwp_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
longer running. */
iterate_over_lwps (stop_wait_callback, NULL);
+ /* MVS Now choose an event thread from among those that
+ have had events. Giving equal priority to all threads
+ that have had events helps prevent starvation. */
+
+ select_event_lwp (&lp, &status);
+
/* 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;