From bfb39158efdb1ce5a0d0cdba1ddfaef6600a9e7a Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Sun, 7 Sep 2003 18:49:44 +0000 Subject: [PATCH] * lin-lwp.c (detach_callback): Don't call stop_wait_callback. (stop_wait_callback): Handle !lp->signalled also. (lin_lwp_has_pending, flush_callback): New functions. (lin_lwp_wait): Call flush_callback. * linux-proc.c (linux_proc_add_line_to_sigset): New function. (linux_proc_pending_signals): New function. * linux-nat.h (linux_proc_pending_signals): Add prototype. --- gdb/ChangeLog | 10 ++++++ gdb/lin-lwp.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- gdb/linux-nat.h | 3 ++ gdb/linux-proc.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 192 insertions(+), 2 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 54b3829..e73ef1c 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,15 @@ 2003-09-07 Daniel Jacobowitz + * lin-lwp.c (detach_callback): Don't call stop_wait_callback. + (stop_wait_callback): Handle !lp->signalled also. + (lin_lwp_has_pending, flush_callback): New functions. + (lin_lwp_wait): Call flush_callback. + * linux-proc.c (linux_proc_add_line_to_sigset): New function. + (linux_proc_pending_signals): New function. + * linux-nat.h (linux_proc_pending_signals): Add prototype. + +2003-09-07 Daniel Jacobowitz + From Nick Kelsey : * infrun.c (handle_inferior_event): Check IN_SOLIB_RETURN_TRAMPOLINE when the stop PC is at the beginning of a function also. diff --git a/gdb/lin-lwp.c b/gdb/lin-lwp.c index 446b76f..fbf9a09 100644 --- a/gdb/lin-lwp.c +++ b/gdb/lin-lwp.c @@ -417,7 +417,12 @@ detach_callback (struct lwp_info *lp, void *data) lp->stopped = 0; lp->signalled = 0; lp->status = 0; - stop_wait_callback (lp, NULL); + /* FIXME drow/2003-08-26: There was a call to stop_wait_callback + here. But since lp->signalled was cleared above, + stop_wait_callback didn't do anything; the process was left + running. Shouldn't we be waiting for it to stop? + I've removed the call, since stop_wait_callback now does do + something when called with lp->signalled == 0. */ gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status)); } @@ -696,7 +701,7 @@ stop_wait_callback (struct lwp_info *lp, void *data) { sigset_t *flush_mask = data; - if (!lp->stopped && lp->signalled) + if (!lp->stopped) { int status; @@ -707,6 +712,12 @@ stop_wait_callback (struct lwp_info *lp, void *data) /* Ignore any signals in FLUSH_MASK. */ if (flush_mask && sigismember (flush_mask, WSTOPSIG (status))) { + if (!lp->signalled) + { + lp->stopped = 1; + return 0; + } + errno = 0; ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0); if (debug_lin_lwp) @@ -822,6 +833,88 @@ stop_wait_callback (struct lwp_info *lp, void *data) return 0; } +/* Check whether PID has any pending signals in FLUSH_MASK. If so set + the appropriate bits in PENDING, and return 1 - otherwise return 0. */ + +static int +lin_lwp_has_pending (int pid, sigset_t *pending, sigset_t *flush_mask) +{ + sigset_t blocked, ignored; + int i; + + linux_proc_pending_signals (pid, pending, &blocked, &ignored); + + if (!flush_mask) + return 0; + + for (i = 1; i < NSIG; i++) + if (sigismember (pending, i)) + if (!sigismember (flush_mask, i) + || sigismember (&blocked, i) + || sigismember (&ignored, i)) + sigdelset (pending, i); + + if (sigisemptyset (pending)) + return 0; + + return 1; +} + +/* DATA is interpreted as a mask of signals to flush. If LP has + signals pending, and they are all in the flush mask, then arrange + to flush them. LP should be stopped, as should all other threads + it might share a signal queue with. */ + +static int +flush_callback (struct lwp_info *lp, void *data) +{ + sigset_t *flush_mask = data; + sigset_t pending, intersection, blocked, ignored; + int pid, status; + + /* Normally, when an LWP exits, it is removed from the LWP list. The + last LWP isn't removed till later, however. So if there is only + one LWP on the list, make sure it's alive. */ + if (lwp_list == lp && lp->next == NULL) + if (!lin_lwp_thread_alive (lp->ptid)) + return 0; + + /* Just because the LWP is stopped doesn't mean that new signals + can't arrive from outside, so this function must be careful of + race conditions. However, because all threads are stopped, we + can assume that the pending mask will not shrink unless we resume + the LWP, and that it will then get another signal. We can't + control which one, however. */ + + if (lp->status) + { + if (debug_lin_lwp) + printf_unfiltered ("FC: LP has pending status %06x\n", lp->status); + if (WIFSTOPPED (lp->status) && sigismember (flush_mask, WSTOPSIG (lp->status))) + lp->status = 0; + } + + while (lin_lwp_has_pending (GET_LWP (lp->ptid), &pending, flush_mask)) + { + int ret; + + errno = 0; + ret = ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0); + if (debug_lin_lwp) + fprintf_unfiltered (gdb_stderr, + "FC: Sent PTRACE_CONT, ret %d %d\n", ret, errno); + + lp->stopped = 0; + stop_wait_callback (lp, flush_mask); + if (debug_lin_lwp) + fprintf_unfiltered (gdb_stderr, + "FC: Wait finished; saved status is %d\n", + lp->status); + } + + return 0; +} + /* Return non-zero if LP has a wait status pending. */ static int @@ -1465,6 +1558,7 @@ retry: /* ... and wait until all of them have reported back that they're no longer running. */ iterate_over_lwps (stop_wait_callback, &flush_mask); + iterate_over_lwps (flush_callback, &flush_mask); /* 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 diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h index 90652aa..da62c76 100644 --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -65,6 +65,9 @@ extern int linux_proc_xfer_memory (CORE_ADDR addr, char *myaddr, int len, int write, struct mem_attrib *attrib, struct target_ops *target); +/* Find process PID's pending signal set from /proc/pid/status. */ +void linux_proc_pending_signals (int pid, sigset_t *pending, sigset_t *blocked, sigset_t *ignored); + /* linux-nat functions for handling fork events. */ extern void linux_record_stopped_pid (int pid); extern void linux_enable_event_reporting (ptid_t ptid); diff --git a/gdb/linux-proc.c b/gdb/linux-proc.c index 6d9be05..2f290c4 100644 --- a/gdb/linux-proc.c +++ b/gdb/linux-proc.c @@ -35,6 +35,8 @@ #include "cli/cli-decode.h" /* for add_info */ #include "gdb_string.h" +#include + #include "linux-nat.h" #ifndef O_LARGEFILE @@ -622,3 +624,84 @@ linux_proc_xfer_memory (CORE_ADDR addr, char *myaddr, int len, int write, close (fd); return ret; } + +/* Parse LINE as a signal set and add its set bits to SIGS. */ + +static void +linux_proc_add_line_to_sigset (const char *line, sigset_t *sigs) +{ + int len = strlen (line) - 1; + const char *p; + int signum; + + if (line[len] != '\n') + error ("Could not parse signal set: %s", line); + + p = line; + signum = len * 4; + while (len-- > 0) + { + int digit; + + if (*p >= '0' && *p <= '9') + digit = *p - '0'; + else if (*p >= 'a' && *p <= 'f') + digit = *p - 'a' + 10; + else + error ("Could not parse signal set: %s", line); + + signum -= 4; + + if (digit & 1) + sigaddset (sigs, signum + 1); + if (digit & 2) + sigaddset (sigs, signum + 2); + if (digit & 4) + sigaddset (sigs, signum + 3); + if (digit & 8) + sigaddset (sigs, signum + 4); + + p++; + } +} + +/* Find process PID's pending signals from /proc/pid/status and set SIGS + to match. */ + +void +linux_proc_pending_signals (int pid, sigset_t *pending, sigset_t *blocked, sigset_t *ignored) +{ + FILE *procfile; + char buffer[MAXPATHLEN], fname[MAXPATHLEN]; + int signum; + + sigemptyset (pending); + sigemptyset (blocked); + sigemptyset (ignored); + sprintf (fname, "/proc/%d/status", pid); + procfile = fopen (fname, "r"); + if (procfile == NULL) + error ("Could not open %s", fname); + + while (fgets (buffer, MAXPATHLEN, procfile) != NULL) + { + /* Normal queued signals are on the SigPnd line in the status + file. However, 2.6 kernels also have a "shared" pending queue + for delivering signals to a thread group, so check for a ShdPnd + line also. + + Unfortunately some Red Hat kernels include the shared pending queue + but not the ShdPnd status field. */ + + if (strncmp (buffer, "SigPnd:\t", 8) == 0) + linux_proc_add_line_to_sigset (buffer + 8, pending); + else if (strncmp (buffer, "ShdPnd:\t", 8) == 0) + linux_proc_add_line_to_sigset (buffer + 8, pending); + else if (strncmp (buffer, "SigBlk:\t", 8) == 0) + linux_proc_add_line_to_sigset (buffer + 8, blocked); + else if (strncmp (buffer, "SigIgn:\t", 8) == 0) + linux_proc_add_line_to_sigset (buffer + 8, ignored); + } + + fclose (procfile); +} -- 2.7.4