* 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.
This commit is contained in:
Daniel Jacobowitz 2003-09-07 18:49:44 +00:00
parent 161afb2472
commit bfb39158ef
4 changed files with 192 additions and 2 deletions

View File

@ -1,3 +1,13 @@
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>:

View File

@ -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

View File

@ -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);

View File

@ -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);
}