static int debug_lin_lwp;
extern char *strsignal (int sig);
+#include "linux-nat.h"
+
/* On GNU/Linux there are no real LWP's. The closest thing to LWP's
are processes sharing the same VM space. A multi-threaded process
is basically a group of such processes. However, such a grouping
threads will run out of processes, even if the threads exit,
because the "zombies" stay around. */
-/* Structure describing a LWP. */
-struct lwp_info
-{
- /* The process id of the LWP. This is a combination of the LWP id
- and overall process id. */
- ptid_t ptid;
-
- /* Non-zero if this LWP is cloned. In this context "cloned" means
- that the LWP is reporting to its parent using a signal other than
- SIGCHLD. */
- int cloned;
-
- /* Non-zero if we sent this LWP a SIGSTOP (but the LWP didn't report
- it back yet). */
- int signalled;
-
- /* Non-zero if this LWP is stopped. */
- int stopped;
-
- /* Non-zero if this LWP will be/has been resumed. Note that an LWP
- can be marked both as stopped and resumed at the same time. This
- happens if we try to resume an LWP that has a wait status
- pending. We shouldn't let the LWP run until that wait status has
- been processed, but we should not report that wait status if GDB
- didn't try to let the LWP run. */
- int resumed;
-
- /* If non-zero, a pending wait status. */
- int status;
-
- /* Non-zero if we were stepping this LWP. */
- int step;
-
- /* Next LWP in list. */
- struct lwp_info *next;
-};
-
/* List of known LWPs. */
static struct lwp_info *lwp_list;
}
\f
-/* Implementation of the PREPARE_TO_PROCEED hook for the GNU/Linux LWP
- layer.
-
- Note that this implementation is potentially redundant now that
- default_prepare_to_proceed() has been added.
-
- FIXME This may not support switching threads after Ctrl-C
- correctly. The default implementation does support this. */
-
-int
-lin_lwp_prepare_to_proceed (void)
-{
- if (!ptid_equal (trap_ptid, null_ptid)
- && !ptid_equal (inferior_ptid, trap_ptid))
- {
- /* Switched over from TRAP_PID. */
- CORE_ADDR stop_pc = read_pc ();
- CORE_ADDR trap_pc;
-
- /* Avoid switching where it wouldn't do any good, i.e. if both
- threads are at the same breakpoint. */
- trap_pc = read_pc_pid (trap_ptid);
- if (trap_pc != stop_pc && breakpoint_here_p (trap_pc))
- {
- /* User hasn't deleted the breakpoint. Return non-zero, and
- switch back to TRAP_PID. */
- inferior_ptid = trap_ptid;
-
- /* FIXME: Is this stuff really necessary? */
- flush_cached_frames ();
- registers_changed ();
-
- return 1;
- }
- }
-
- return 0;
-}
-\f
-
#if 0
static void
lin_lwp_open (char *args, int from_tty)
gdb_assert (pid == GET_LWP (ptid)
&& WIFSTOPPED (status) && WSTOPSIG (status));
+ child_post_attach (pid);
+
lp->stopped = 1;
if (debug_lin_lwp)
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));
}
return kill (lwpid, signo);
}
+/* Wait for LP to stop. Returns the wait status, or 0 if the LWP has
+ exited. */
+
+static int
+wait_lwp (struct lwp_info *lp)
+{
+ pid_t pid;
+ int status;
+ int thread_dead = 0;
+
+ gdb_assert (!lp->stopped);
+ gdb_assert (lp->status == 0);
+
+ pid = waitpid (GET_LWP (lp->ptid), &status, 0);
+ if (pid == -1 && errno == ECHILD)
+ {
+ pid = waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
+ if (pid == -1 && errno == ECHILD)
+ {
+ /* The thread has previously exited. We need to delete it now
+ because in the case of NPTL threads, there won't be an
+ exit event unless it is the main thread. */
+ thread_dead = 1;
+ if (debug_lin_lwp)
+ fprintf_unfiltered (gdb_stdlog, "WL: %s vanished.\n",
+ target_pid_to_str (lp->ptid));
+ }
+ }
+
+ if (!thread_dead)
+ {
+ gdb_assert (pid == GET_LWP (lp->ptid));
+
+ if (debug_lin_lwp)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "WL: waitpid %s received %s\n",
+ target_pid_to_str (lp->ptid),
+ status_to_str (status));
+ }
+ }
+
+ /* Check if the thread has exited. */
+ if (WIFEXITED (status) || WIFSIGNALED (status))
+ {
+ thread_dead = 1;
+ if (debug_lin_lwp)
+ fprintf_unfiltered (gdb_stdlog, "WL: %s exited.\n",
+ target_pid_to_str (lp->ptid));
+ }
+
+ if (thread_dead)
+ {
+ if (in_thread_list (lp->ptid))
+ {
+ /* Core GDB cannot deal with us deleting the current thread. */
+ if (!ptid_equal (lp->ptid, inferior_ptid))
+ delete_thread (lp->ptid);
+ printf_unfiltered ("[%s exited]\n",
+ target_pid_to_str (lp->ptid));
+ }
+
+ delete_lwp (lp->ptid);
+ return 0;
+ }
+
+ gdb_assert (WIFSTOPPED (status));
+
+ return status;
+}
+
/* Send a SIGSTOP to LP. */
static int
{
sigset_t *flush_mask = data;
- if (!lp->stopped && lp->signalled)
+ if (!lp->stopped)
{
- pid_t pid;
int status;
- gdb_assert (lp->status == 0);
+ status = wait_lwp (lp);
+ if (status == 0)
+ return 0;
- pid = waitpid (GET_LWP (lp->ptid), &status, 0);
- if (pid == -1 && errno == ECHILD)
+ /* Ignore any signals in FLUSH_MASK. */
+ if (flush_mask && sigismember (flush_mask, WSTOPSIG (status)))
{
- pid = waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
- if (pid == -1 && errno == ECHILD)
+ if (!lp->signalled)
{
- /* The thread has previously exited. We need to delete it now
- because in the case of nptl threads, there won't be an
- exit event unless it is the main thread. */
- if (debug_lin_lwp)
- fprintf_unfiltered (gdb_stdlog,
- "SWC: %s exited.\n",
- target_pid_to_str (lp->ptid));
- delete_lwp (lp->ptid);
+ lp->stopped = 1;
return 0;
}
- }
- gdb_assert (pid == GET_LWP (lp->ptid));
-
- if (debug_lin_lwp)
- {
- fprintf_unfiltered (gdb_stdlog,
- "SWC: waitpid %s received %s\n",
- target_pid_to_str (lp->ptid),
- status_to_str (status));
- }
-
- /* Check if the thread has exited. */
- if (WIFEXITED (status) || WIFSIGNALED (status))
- {
- gdb_assert (num_lwps > 1);
-
- if (in_thread_list (lp->ptid))
- {
- /* Core GDB cannot deal with us deleting the current
- thread. */
- if (!ptid_equal (lp->ptid, inferior_ptid))
- delete_thread (lp->ptid);
- printf_unfiltered ("[%s exited]\n",
- target_pid_to_str (lp->ptid));
- }
- if (debug_lin_lwp)
- fprintf_unfiltered (gdb_stdlog,
- "SWC: %s exited.\n",
- target_pid_to_str (lp->ptid));
-
- delete_lwp (lp->ptid);
- return 0;
- }
-
- /* Check if the current LWP has previously exited. For nptl threads,
- there is no exit signal issued for LWPs that are not the
- main thread so we should check whenever the thread is stopped. */
- if (!lin_lwp_thread_alive (lp->ptid))
- {
- if (in_thread_list (lp->ptid))
- {
- /* Core GDB cannot deal with us deleting the current
- thread. */
- if (!ptid_equal (lp->ptid, inferior_ptid))
- delete_thread (lp->ptid);
- printf_unfiltered ("[%s exited]\n",
- target_pid_to_str (lp->ptid));
- }
- if (debug_lin_lwp)
- fprintf_unfiltered (gdb_stdlog,
- "SWC: %s already exited.\n",
- target_pid_to_str (lp->ptid));
-
- delete_lwp (lp->ptid);
- return 0;
- }
-
- gdb_assert (WIFSTOPPED (status));
-
- /* Ignore any signals in FLUSH_MASK. */
- if (flush_mask && sigismember (flush_mask, WSTOPSIG (status)))
- {
errno = 0;
ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
if (debug_lin_lwp)
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
save_errno = EINTR;
}
+ /* Check for stop events reported by a process we didn't already
+ know about - in this case, anything other than inferior_ptid.
+
+ If we're expecting to receive stopped processes after fork,
+ vfork, and clone events, then we'll just add the new one to
+ our list and go back to waiting for the event to be reported
+ - the stopped process might be returned from waitpid before
+ or after the event is. If we want to handle debugging of
+ CLONE_PTRACE processes we need to do more here, i.e. switch
+ to multi-threaded mode. */
+ if (pid != -1 && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP
+ && pid != GET_PID (inferior_ptid))
+ {
+ linux_record_stopped_pid (pid);
+ pid = -1;
+ save_errno = EINTR;
+ }
+
clear_sigio_trap ();
clear_sigint_trap ();
}
return minus_one_ptid;
}
+ /* Handle GNU/Linux's extended waitstatus for trace events. */
+ if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ return linux_handle_extended_wait (pid, status, ourstatus);
+
store_waitstatus (ourstatus, status);
return pid_to_ptid (pid);
}
lp = find_lwp_pid (pid_to_ptid (lwpid));
+ /* Check for stop events reported by a process we didn't
+ already know about - anything not already in our LWP
+ list.
+
+ If we're expecting to receive stopped processes after
+ fork, vfork, and clone events, then we'll just add the
+ new one to our list and go back to waiting for the event
+ to be reported - the stopped process might be returned
+ from waitpid before or after the event is. */
+ if (WIFSTOPPED (status) && !lp)
+ {
+ linux_record_stopped_pid (lwpid);
+ status = 0;
+ continue;
+ }
+
/* Make sure we don't report an event for the exit of an LWP not in
our list, i.e. not part of the current process. This can happen
if we detach from a program we original forked and then it
continue;
}
+ /* NOTE drow/2003-06-17: This code seems to be meant for debugging
+ CLONE_PTRACE processes which do not use the thread library -
+ otherwise we wouldn't find the new LWP this way. That doesn't
+ currently work, and the following code is currently unreachable
+ due to the two blocks above. If it's fixed some day, this code
+ should be broken out into a function so that we can also pick up
+ LWPs from the new interface. */
if (!lp)
{
lp = add_lwp (BUILD_LWP (lwpid, GET_PID (inferior_ptid)));
/* ... 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
else
trap_ptid = null_ptid;
+ /* Handle GNU/Linux's extended waitstatus for trace events. */
+ if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ {
+ linux_handle_extended_wait (ptid_get_pid (trap_ptid),
+ status, ourstatus);
+ return trap_ptid;
+ }
+
store_waitstatus (ourstatus, status);
return (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid)));
}
lin_lwp_ops.to_mourn_inferior = lin_lwp_mourn_inferior;
lin_lwp_ops.to_thread_alive = lin_lwp_thread_alive;
lin_lwp_ops.to_pid_to_str = lin_lwp_pid_to_str;
+ lin_lwp_ops.to_post_startup_inferior = child_post_startup_inferior;
+ lin_lwp_ops.to_post_attach = child_post_attach;
+ lin_lwp_ops.to_insert_fork_catchpoint = child_insert_fork_catchpoint;
+ lin_lwp_ops.to_insert_vfork_catchpoint = child_insert_vfork_catchpoint;
+ lin_lwp_ops.to_insert_exec_catchpoint = child_insert_exec_catchpoint;
+
lin_lwp_ops.to_stratum = thread_stratum;
lin_lwp_ops.to_has_thread_control = tc_schedlock;
lin_lwp_ops.to_magic = OPS_MAGIC;