aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog10
-rw-r--r--gdb/lin-lwp.c98
-rw-r--r--gdb/linux-nat.h3
-rw-r--r--gdb/linux-proc.c83
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 <drow@mvista.com>
+ * 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 <drow@mvista.com>
+
From Nick Kelsey <nickk@ubicom.com>:
* 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 <signal.h>
+
#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);
+}