2008-05-01 Daniel Jacobowitz <dan@codesourcery.com>

Pedro Alves  <pedro@codesourcery.com>

	Based on work by Jan Kratochvil <jan.kratochvil@redhat.com> and Jeff
 	Johnston <jjohnstn@redhat.com>.

	* NEWS: Mention attach to stopped process fix.
	* infcmd.c (detach_command, disconnect_command): Discard the thread
	list.
	* infrun.c (handle_inferior_event): Do not ignore non-SIGSTOP while
	attaching.  Use signal_stop_state.
	(signal_stop_state): Check stop_soon.
	* linux-nat.c (kill_lwp): Declare earlier.
	(pid_is_stopped, linux_nat_post_attach_wait): New.
	(lin_lwp_attach_lwp): Use linux_nat_post_attach_wait.  Update
	comments.
	(linux_nat_attach): Use linux_nat_post_attach_wait.
	(detach_callback, linux_nat_detach): Improve handling for signalled
	processes.
	(linux_nat_pid_to_str): Always print out the LWP ID if it differs
	from the process ID.
	* Makefile.in (infcmd.o): Update.

2008-05-01  Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Daniel Jacobowitz  <dan@codesourcery.com>

	* gdb.threads/attach-into-signal.c, gdb.threads/attach-into-signal.exp,
	gdb.threads/attach-stopped.c, gdb.threads/attach-stopped.exp,
	gdb.threads/attachstop-mt.c, gdb.threads/attachstop-mt.exp: New.
This commit is contained in:
Daniel Jacobowitz
2008-05-01 18:50:14 +00:00
parent ca38c58efa
commit a0ef42744d
11 changed files with 985 additions and 87 deletions

View File

@@ -50,6 +50,30 @@
#include "event-loop.h"
#include "event-top.h"
/* Note on this file's use of signals:
We stop threads by sending a SIGSTOP. The use of SIGSTOP instead
of another signal is not entirely significant; we just need for a
signal to be delivered, so that we can intercept it. SIGSTOP's
advantage is that it can not be blocked. A disadvantage is that it
is not a real-time signal, so it can only be queued once; we do not
keep track of other sources of SIGSTOP.
Two other signals that can't be blocked are SIGCONT and SIGKILL.
But we can't use them, because they have special behavior when the
signal is generated - not when it is delivered. SIGCONT resumes
the entire thread group and SIGKILL kills the entire thread group.
A delivered SIGSTOP would stop the entire thread group, not just the
thread we tkill'd. But we never let the SIGSTOP deliver; we always
intercept and cancel it (by PTRACE_CONT without passing SIGSTOP).
We could use a real-time signal instead. This would solve those
problems; we could use PTRACE_GETSIGINFO to locate the specific
stop signals sent by GDB. But we would still have to have some
support for SIGSTOP, since PTRACE_ATTACH generates it, and there
are races with trying to find a signal that is not blocked. */
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif
@@ -186,6 +210,7 @@ static void linux_nat_async (void (*callback)
(enum inferior_event_type event_type, void *context),
void *context);
static int linux_nat_async_mask (int mask);
static int kill_lwp (int lwpid, int signo);
/* Captures the result of a successful waitpid call, along with the
options used in that call. */
@@ -1010,10 +1035,103 @@ exit_lwp (struct lwp_info *lp)
delete_lwp (lp->ptid);
}
/* Attach to the LWP specified by PID. If VERBOSE is non-zero, print
a message telling the user that a new LWP has been added to the
process. Return 0 if successful or -1 if the new LWP could not
be attached. */
/* Detect `T (stopped)' in `/proc/PID/status'.
Other states including `T (tracing stop)' are reported as false. */
static int
pid_is_stopped (pid_t pid)
{
FILE *status_file;
char buf[100];
int retval = 0;
snprintf (buf, sizeof (buf), "/proc/%d/status", (int) pid);
status_file = fopen (buf, "r");
if (status_file != NULL)
{
int have_state = 0;
while (fgets (buf, sizeof (buf), status_file))
{
if (strncmp (buf, "State:", 6) == 0)
{
have_state = 1;
break;
}
}
if (have_state && strstr (buf, "T (stopped)") != NULL)
retval = 1;
fclose (status_file);
}
return retval;
}
/* Wait for the LWP specified by LP, which we have just attached to.
Returns a wait status for that LWP, to cache. */
static int
linux_nat_post_attach_wait (ptid_t ptid, int first, int *cloned,
int *signalled)
{
pid_t new_pid, pid = GET_LWP (ptid);
int status;
if (pid_is_stopped (pid))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LNPAW: Attaching to a stopped process\n");
/* The process is definitely stopped. It is in a job control
stop, unless the kernel predates the TASK_STOPPED /
TASK_TRACED distinction, in which case it might be in a
ptrace stop. Make sure it is in a ptrace stop; from there we
can kill it, signal it, et cetera.
First make sure there is a pending SIGSTOP. Since we are
already attached, the process can not transition from stopped
to running without a PTRACE_CONT; so we know this signal will
go into the queue. The SIGSTOP generated by PTRACE_ATTACH is
probably already in the queue (unless this kernel is old
enough to use TASK_STOPPED for ptrace stops); but since SIGSTOP
is not an RT signal, it can only be queued once. */
kill_lwp (pid, SIGSTOP);
/* Finally, resume the stopped process. This will deliver the SIGSTOP
(or a higher priority signal, just like normal PTRACE_ATTACH). */
ptrace (PTRACE_CONT, pid, 0, 0);
}
/* Make sure the initial process is stopped. The user-level threads
layer might want to poke around in the inferior, and that won't
work if things haven't stabilized yet. */
new_pid = my_waitpid (pid, &status, 0);
if (new_pid == -1 && errno == ECHILD)
{
if (first)
warning (_("%s is a cloned process"), target_pid_to_str (ptid));
/* Try again with __WCLONE to check cloned processes. */
new_pid = my_waitpid (pid, &status, __WCLONE);
*cloned = 1;
}
gdb_assert (pid == new_pid && WIFSTOPPED (status));
if (WSTOPSIG (status) != SIGSTOP)
{
*signalled = 1;
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LNPAW: Received %s after attaching\n",
status_to_str (status));
}
return status;
}
/* Attach to the LWP specified by PID. Return 0 if successful or -1
if the new LWP could not be attached. */
int
lin_lwp_attach_lwp (ptid_t ptid)
@@ -1036,9 +1154,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
to happen. */
if (GET_LWP (ptid) != GET_PID (ptid) && lp == NULL)
{
pid_t pid;
int status;
int cloned = 0;
int status, cloned = 0, signalled = 0;
if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
{
@@ -1057,24 +1173,18 @@ lin_lwp_attach_lwp (ptid_t ptid)
"LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
target_pid_to_str (ptid));
pid = my_waitpid (GET_LWP (ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
status = linux_nat_post_attach_wait (ptid, 0, &cloned, &signalled);
lp = add_lwp (ptid);
lp->stopped = 1;
lp->cloned = cloned;
lp->signalled = signalled;
if (WSTOPSIG (status) != SIGSTOP)
{
/* Try again with __WCLONE to check cloned processes. */
pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
cloned = 1;
lp->resumed = 1;
lp->status = status;
}
gdb_assert (pid == GET_LWP (ptid)
&& WIFSTOPPED (status) && WSTOPSIG (status));
if (lp == NULL)
lp = add_lwp (ptid);
lp->cloned = cloned;
target_post_attach (pid);
lp->stopped = 1;
target_post_attach (GET_LWP (lp->ptid));
if (debug_linux_nat)
{
@@ -1133,10 +1243,7 @@ static void
linux_nat_attach (char *args, int from_tty)
{
struct lwp_info *lp;
pid_t pid;
int status;
int cloned = 0;
int options = 0;
/* FIXME: We should probably accept a list of process id's, and
attach all of them. */
@@ -1151,54 +1258,69 @@ linux_nat_attach (char *args, int from_tty)
sigdelset (&suspend_mask, SIGCHLD);
}
/* Make sure the initial process is stopped. The user-level threads
layer might want to poke around in the inferior, and that won't
work if things haven't stabilized yet. */
pid = my_waitpid (GET_PID (inferior_ptid), &status, options);
if (pid == -1 && errno == ECHILD)
{
warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
/* Try again with __WCLONE to check cloned processes. */
options = __WCLONE;
pid = my_waitpid (GET_PID (inferior_ptid), &status, options);
cloned = 1;
}
gdb_assert (pid == GET_PID (inferior_ptid)
&& WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
/* Add the initial process as the first LWP to the list. */
inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
lp = add_lwp (inferior_ptid);
lp->cloned = cloned;
status = linux_nat_post_attach_wait (lp->ptid, 1, &lp->cloned,
&lp->signalled);
lp->stopped = 1;
/* If this process is not using thread_db, then we still don't
detect any other threads, but add at least this one. */
add_thread_silent (lp->ptid);
lp->stopped = 1;
/* Save the wait status to report later. */
lp->resumed = 1;
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LNA: waitpid %ld, saving status %s\n",
(long) GET_PID (lp->ptid), status_to_str (status));
if (!target_can_async_p ())
{
/* Fake the SIGSTOP that core GDB expects. */
lp->status = W_STOPCODE (SIGSTOP);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LNA: waitpid %ld, faking SIGSTOP\n", (long) pid);
}
lp->status = status;
else
{
/* We already waited for this LWP, so put the wait result on the
pipe. The event loop will wake up and gets us to handling
this event. */
linux_nat_event_pipe_push (pid, status, options);
linux_nat_event_pipe_push (GET_PID (lp->ptid), status,
lp->cloned ? __WCLONE : 0);
/* Register in the event loop. */
target_async (inferior_event_handler, 0);
}
}
/* Get pending status of LP. */
static int
get_pending_status (struct lwp_info *lp, int *status)
{
struct target_waitstatus last;
ptid_t last_ptid;
get_last_target_status (&last_ptid, &last);
/* If this lwp is the ptid that GDB is processing an event from, the
signal will be in stop_signal. Otherwise, in all-stop + sync
mode, we may cache pending events in lp->status while trying to
stop all threads (see stop_wait_callback). In async mode, the
events are always cached in waitpid_queue. */
*status = 0;
if (GET_LWP (lp->ptid) == GET_LWP (last_ptid))
{
if (stop_signal != TARGET_SIGNAL_0
&& signal_pass_state (stop_signal))
*status = W_STOPCODE (target_signal_to_host (stop_signal));
}
else if (target_can_async_p ())
queued_waitpid (GET_LWP (lp->ptid), status, __WALL);
else
*status = lp->status;
return 0;
}
static int
detach_callback (struct lwp_info *lp, void *data)
{
@@ -1209,40 +1331,30 @@ detach_callback (struct lwp_info *lp, void *data)
strsignal (WSTOPSIG (lp->status)),
target_pid_to_str (lp->ptid));
while (lp->signalled && lp->stopped)
/* If there is a pending SIGSTOP, get rid of it. */
if (lp->signalled)
{
errno = 0;
if (ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0,
WSTOPSIG (lp->status)) < 0)
error (_("Can't continue %s: %s"), target_pid_to_str (lp->ptid),
safe_strerror (errno));
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"DC: PTRACE_CONTINUE (%s, 0, %s) (OK)\n",
target_pid_to_str (lp->ptid),
status_to_str (lp->status));
"DC: Sending SIGCONT to %s\n",
target_pid_to_str (lp->ptid));
lp->stopped = 0;
kill_lwp (GET_LWP (lp->ptid), SIGCONT);
lp->signalled = 0;
lp->status = 0;
/* 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));
}
/* We don't actually detach from the LWP that has an id equal to the
overall process id just yet. */
if (GET_LWP (lp->ptid) != GET_PID (lp->ptid))
{
int status = 0;
/* Pass on any pending signal for this LWP. */
get_pending_status (lp, &status);
errno = 0;
if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
WSTOPSIG (lp->status)) < 0)
WSTOPSIG (status)) < 0)
error (_("Can't detach %s: %s"), target_pid_to_str (lp->ptid),
safe_strerror (errno));
@@ -1252,7 +1364,6 @@ detach_callback (struct lwp_info *lp, void *data)
target_pid_to_str (lp->ptid),
strsignal (WSTOPSIG (lp->status)));
drain_queued_events (GET_LWP (lp->ptid));
delete_lwp (lp->ptid);
}
@@ -1263,6 +1374,9 @@ static void
linux_nat_detach (char *args, int from_tty)
{
int pid;
int status;
enum target_signal sig;
if (target_can_async_p ())
linux_nat_async (NULL, 0);
@@ -1271,6 +1385,21 @@ linux_nat_detach (char *args, int from_tty)
/* Only the initial process should be left right now. */
gdb_assert (num_lwps == 1);
/* Pass on any pending signal for the last LWP. */
if ((args == NULL || *args == '\0')
&& get_pending_status (lwp_list, &status) != -1
&& WIFSTOPPED (status))
{
/* Put the signal number in ARGS so that inf_ptrace_detach will
pass it along with PTRACE_DETACH. */
args = alloca (8);
sprintf (args, "%d", (int) WSTOPSIG (status));
fprintf_unfiltered (gdb_stdlog,
"LND: Sending signal %s to %s\n",
args,
target_pid_to_str (lwp_list->ptid));
}
trap_ptid = null_ptid;
/* Destroy LWP info; it's no longer valid. */
@@ -2848,7 +2977,9 @@ linux_nat_pid_to_str (ptid_t ptid)
{
static char buf[64];
if (lwp_list && lwp_list->next && is_lwp (ptid))
if (is_lwp (ptid)
&& ((lwp_list && lwp_list->next)
|| GET_PID (ptid) != GET_LWP (ptid)))
{
snprintf (buf, sizeof (buf), "LWP %ld", GET_LWP (ptid));
return buf;
@@ -4205,4 +4336,3 @@ lin_thread_get_thread_signals (sigset_t *set)
/* ... except during a sigsuspend. */
sigdelset (&suspend_mask, cancel);
}