2001-06-12 Michael Snyder <msnyder@redhat.com>

* lin-lwp.c: Prevent thread starvation by using a monte carlo
	method to choose which of several event threads to handle next.

	(stop_wait_callback): Defer pushback of breakpoint events until
	later; add SIGTRAP events to the queue of unhandled events.
	Keep calling waitpid until SIGSTOP retrieved.  If more than one
	non-SIGSTOP event is retrieved, push them back onto the process
	queue using kill.
	(count_events_callback, select_singlestep_lwp_callback,
	select_event_lwp_callback, cancel_breakpoints_callback,
	select_event_lwp): New functions.  Implement monte carlo method
	for selecting which of several SIGTRAP threads to handle next.
	Push back the breakpoint event for all threads other than the
	selected one.
	(lin_lwp_wait): Call select_event_lwp to decide which of several
	sigtrapped lwps to handle next.
	(resume_callback): Disable code that attempts to handle
	step_resume breakpoints.  Let core gdb handle this.
This commit is contained in:
Michael Snyder 2001-07-06 19:06:24 +00:00
parent ee3a7b7f0d
commit b1aeb4c5a3
2 changed files with 235 additions and 41 deletions

View File

@ -1,3 +1,24 @@
2001-06-12 Michael Snyder <msnyder@redhat.com>
* lin-lwp.c: Prevent thread starvation by using a monte carlo
method to choose which of several event threads to handle next.
(stop_wait_callback): Defer pushback of breakpoint events until
later; add SIGTRAP events to the queue of unhandled events.
Keep calling waitpid until SIGSTOP retrieved. If more than one
non-SIGSTOP event is retrieved, push them back onto the process
queue using kill.
(count_events_callback, select_singlestep_lwp_callback,
select_event_lwp_callback, cancel_breakpoints_callback,
select_event_lwp): New functions. Implement monte carlo method
for selecting which of several SIGTRAP threads to handle next.
Push back the breakpoint event for all threads other than the
selected one.
(lin_lwp_wait): Call select_event_lwp to decide which of several
sigtrapped lwps to handle next.
(resume_callback): Disable code that attempts to handle
step_resume breakpoints. Let core gdb handle this.
2001-07-06 Jim Blandy <jimb@redhat.com>
* gdbtypes.h (builtin_type_void_func_ptr): New builtin type.

View File

@ -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
{
if (debug_lin_lwp)
fprintf_unfiltered (gdb_stdlog,
"Received %s in LWP %d while waiting for SIGSTOP.\n",
strsignal (WSTOPSIG (status)), pid);
/* The thread was stopped with a signal other than
SIGSTOP, and didn't accidentally trip a breakpoint.
Record the wait status. */
lp->status = status;
SIGSTOP, and didn't accidentally trip a breakpoint. */
if (debug_lin_lwp)
{
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);
/* 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;