From b1aeb4c5a3e17984e4333b45608293ba06ee8085 Mon Sep 17 00:00:00 2001 From: Michael Snyder Date: Fri, 6 Jul 2001 19:06:24 +0000 Subject: [PATCH] 2001-06-12 Michael Snyder * 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. --- gdb/ChangeLog | 21 +++++ gdb/lin-lwp.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 234 insertions(+), 40 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 59ffe44..f187415 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,24 @@ +2001-06-12 Michael Snyder + + * 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 * gdbtypes.h (builtin_type_void_func_ptr): New builtin type. 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; -- 2.7.4