aboutsummaryrefslogtreecommitdiff
path: root/gdb/linux-nat.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/linux-nat.c')
-rw-r--r--gdb/linux-nat.c870
1 files changed, 814 insertions, 56 deletions
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 770cf31..b097408 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -46,6 +46,9 @@
#include "gdbthread.h" /* for struct thread_info etc. */
#include "gdb_stat.h" /* for struct stat */
#include <fcntl.h> /* for O_RDONLY */
+#include "inf-loop.h"
+#include "event-loop.h"
+#include "event-top.h"
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
@@ -113,6 +116,15 @@ show_debug_linux_nat (struct ui_file *file, int from_tty,
value);
}
+static int debug_linux_nat_async = 0;
+static void
+show_debug_linux_nat_async (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("Debugging of GNU/Linux async lwp module is %s.\n"),
+ value);
+}
+
static int linux_parent_pid;
struct simple_pid_list
@@ -133,6 +145,158 @@ static int linux_supports_tracefork_flag = -1;
static int linux_supports_tracevforkdone_flag = -1;
+/* Async mode support */
+
+/* To listen to target events asynchronously, we install a SIGCHLD
+ handler whose duty is to call waitpid (-1, ..., WNOHANG) to get all
+ the pending events into a pipe. Whenever we're ready to handle
+ events asynchronously, this pipe is registered as the waitable file
+ handle in the event loop. When we get to entry target points
+ coming out of the common code (target_wait, target_resume, ...),
+ that are going to call waitpid, we block SIGCHLD signals, and
+ remove all the events placed in the pipe into a local queue. All
+ the subsequent calls to my_waitpid (a waitpid wrapper) check this
+ local queue first. */
+
+/* True if async mode is currently on. */
+static int linux_nat_async_enabled;
+
+/* Zero if the async mode, although enabled, is masked, which means
+ linux_nat_wait should behave as if async mode was off. */
+static int linux_nat_async_mask_value = 1;
+
+/* The read/write ends of the pipe registered as waitable file in the
+ event loop. */
+static int linux_nat_event_pipe[2] = { -1, -1 };
+
+/* Number of queued events in the pipe. */
+static volatile int linux_nat_num_queued_events;
+
+/* If async mode is on, true if we're listening for events; false if
+ target events are blocked. */
+static int linux_nat_async_events_enabled;
+
+static int linux_nat_async_events (int enable);
+static void pipe_to_local_event_queue (void);
+static void local_event_queue_to_pipe (void);
+static void linux_nat_event_pipe_push (int pid, int status, int options);
+static int linux_nat_event_pipe_pop (int* ptr_status, int* ptr_options);
+static void linux_nat_set_async_mode (int on);
+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);
+
+/* Captures the result of a successful waitpid call, along with the
+ options used in that call. */
+struct waitpid_result
+{
+ int pid;
+ int status;
+ int options;
+ struct waitpid_result *next;
+};
+
+/* A singly-linked list of the results of the waitpid calls performed
+ in the async SIGCHLD handler. */
+static struct waitpid_result *waitpid_queue = NULL;
+
+static int
+queued_waitpid (int pid, int *status, int flags)
+{
+ struct waitpid_result *msg = waitpid_queue, *prev = NULL;
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog,
+ "\
+QWPID: linux_nat_async_events_enabled(%d), linux_nat_num_queued_events(%d)\n",
+ linux_nat_async_events_enabled,
+ linux_nat_num_queued_events);
+
+ if (flags & __WALL)
+ {
+ for (; msg; prev = msg, msg = msg->next)
+ if (pid == -1 || pid == msg->pid)
+ break;
+ }
+ else if (flags & __WCLONE)
+ {
+ for (; msg; prev = msg, msg = msg->next)
+ if (msg->options & __WCLONE
+ && (pid == -1 || pid == msg->pid))
+ break;
+ }
+ else
+ {
+ for (; msg; prev = msg, msg = msg->next)
+ if ((msg->options & __WCLONE) == 0
+ && (pid == -1 || pid == msg->pid))
+ break;
+ }
+
+ if (msg)
+ {
+ int pid;
+
+ if (prev)
+ prev->next = msg->next;
+ else
+ waitpid_queue = msg->next;
+
+ msg->next = NULL;
+ if (status)
+ *status = msg->status;
+ pid = msg->pid;
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "QWPID: pid(%d), status(%x)\n",
+ pid, msg->status);
+ xfree (msg);
+
+ return pid;
+ }
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "QWPID: miss\n");
+
+ if (status)
+ *status = 0;
+ return -1;
+}
+
+static void
+push_waitpid (int pid, int status, int options)
+{
+ struct waitpid_result *event, *new_event;
+
+ new_event = xmalloc (sizeof (*new_event));
+ new_event->pid = pid;
+ new_event->status = status;
+ new_event->options = options;
+ new_event->next = NULL;
+
+ if (waitpid_queue)
+ {
+ for (event = waitpid_queue;
+ event && event->next;
+ event = event->next)
+ ;
+
+ event->next = new_event;
+ }
+ else
+ waitpid_queue = new_event;
+}
+
+/* Drain all queued event of PID. If PID is -1, the effect is of
+ draining all events. */
+static void
+drain_queued_events (int pid)
+{
+ while (queued_waitpid (pid, NULL, __WALL) != -1)
+ ;
+}
+
/* Trivial list manipulation functions to keep track of a list of
new stopped processes. */
@@ -183,12 +347,21 @@ linux_tracefork_child (void)
_exit (0);
}
-/* Wrapper function for waitpid which handles EINTR. */
+/* Wrapper function for waitpid which handles EINTR, and checks for
+ locally queued events. */
static int
my_waitpid (int pid, int *status, int flags)
{
int ret;
+
+ /* There should be no concurrent calls to waitpid. */
+ gdb_assert (!linux_nat_async_events_enabled);
+
+ ret = queued_waitpid (pid, status, flags);
+ if (ret != -1)
+ return ret;
+
do
{
ret = waitpid (pid, status, flags);
@@ -362,6 +535,9 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
int has_vforked;
int parent_pid, child_pid;
+ if (target_can_async_p ())
+ target_async (NULL, 0);
+
get_last_target_status (&last_ptid, &last_status);
has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
parent_pid = ptid_get_lwp (last_ptid);
@@ -506,9 +682,7 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
fork_save_infrun_state (fp, 0);
}
else
- {
- target_detach (NULL, 0);
- }
+ target_detach (NULL, 0);
inferior_ptid = ptid_build (child_pid, child_pid, 0);
@@ -523,6 +697,9 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
follow_inferior_reset_breakpoints ();
}
+ if (target_can_async_p ())
+ target_async (inferior_event_handler, 0);
+
return 0;
}
@@ -611,8 +788,11 @@ static sigset_t normal_mask;
_initialize_linux_nat. */
static sigset_t suspend_mask;
-/* Signals to block to make that sigsuspend work. */
-static sigset_t blocked_mask;
+/* SIGCHLD action for synchronous mode. */
+struct sigaction sync_sigchld_action;
+
+/* SIGCHLD action for asynchronous mode. */
+static struct sigaction async_sigchld_action;
/* Prototypes for local functions. */
@@ -837,16 +1017,12 @@ int
lin_lwp_attach_lwp (ptid_t ptid)
{
struct lwp_info *lp;
+ int async_events_were_enabled = 0;
gdb_assert (is_lwp (ptid));
- /* Make sure SIGCHLD is blocked. We don't want SIGCHLD events
- to interrupt either the ptrace() or waitpid() calls below. */
- if (!sigismember (&blocked_mask, SIGCHLD))
- {
- sigaddset (&blocked_mask, SIGCHLD);
- sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
- }
+ if (target_can_async_p ())
+ async_events_were_enabled = linux_nat_async_events (0);
lp = find_lwp_pid (ptid);
@@ -919,10 +1095,39 @@ lin_lwp_attach_lwp (ptid_t ptid)
lp->stopped = 1;
}
+ if (async_events_were_enabled)
+ linux_nat_async_events (1);
+
return 0;
}
static void
+linux_nat_create_inferior (char *exec_file, char *allargs, char **env,
+ int from_tty)
+{
+ int saved_async = 0;
+
+ /* The fork_child mechanism is synchronous and calls target_wait, so
+ we have to mask the async mode. */
+
+ if (target_can_async_p ())
+ saved_async = linux_nat_async_mask (0);
+ else
+ {
+ /* Restore the original signal mask. */
+ sigprocmask (SIG_SETMASK, &normal_mask, NULL);
+ /* Make sure we don't block SIGCHLD during a sigsuspend. */
+ suspend_mask = normal_mask;
+ sigdelset (&suspend_mask, SIGCHLD);
+ }
+
+ linux_ops->to_create_inferior (exec_file, allargs, env, from_tty);
+
+ if (saved_async)
+ linux_nat_async_mask (saved_async);
+}
+
+static void
linux_nat_attach (char *args, int from_tty)
{
struct lwp_info *lp;
@@ -934,6 +1139,15 @@ linux_nat_attach (char *args, int from_tty)
attach all of them. */
linux_ops->to_attach (args, from_tty);
+ if (!target_can_async_p ())
+ {
+ /* Restore the original signal mask. */
+ sigprocmask (SIG_SETMASK, &normal_mask, NULL);
+ /* Make sure we don't block SIGCHLD during a sigsuspend. */
+ suspend_mask = normal_mask;
+ 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. */
@@ -961,9 +1175,14 @@ linux_nat_attach (char *args, int from_tty)
lp->status = W_STOPCODE (SIGSTOP);
lp->resumed = 1;
if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LNA: waitpid %ld, faking SIGSTOP\n", (long) pid);
+ if (target_can_async_p ())
{
- fprintf_unfiltered (gdb_stdlog,
- "LLA: waitpid %ld, faking SIGSTOP\n", (long) pid);
+ /* Wake event loop with special token, to get to WFI. */
+ linux_nat_event_pipe_push (-1, -1, -1);
+ /* Register in the event loop. */
+ target_async (inferior_event_handler, 0);
}
}
@@ -1020,6 +1239,7 @@ 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);
}
@@ -1029,6 +1249,10 @@ detach_callback (struct lwp_info *lp, void *data)
static void
linux_nat_detach (char *args, int from_tty)
{
+ int pid;
+ if (target_can_async_p ())
+ linux_nat_async (NULL, 0);
+
iterate_over_lwps (detach_callback, NULL);
/* Only the initial process should be left right now. */
@@ -1039,12 +1263,12 @@ linux_nat_detach (char *args, int from_tty)
/* Destroy LWP info; it's no longer valid. */
init_lwp_list ();
- /* Restore the original signal mask. */
- sigprocmask (SIG_SETMASK, &normal_mask, NULL);
- sigemptyset (&blocked_mask);
-
- inferior_ptid = pid_to_ptid (GET_PID (inferior_ptid));
+ pid = GET_PID (inferior_ptid);
+ inferior_ptid = pid_to_ptid (pid);
linux_ops->to_detach (args, from_tty);
+
+ if (target_can_async_p ())
+ drain_queued_events (pid);
}
/* Resume LP. */
@@ -1098,6 +1322,10 @@ linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
prune_lwps ();
+ if (target_can_async_p ())
+ /* Block events while we're here. */
+ linux_nat_async_events (0);
+
/* A specific PTID means `step only this process id'. */
resume_all = (PIDGET (ptid) == -1);
@@ -1162,6 +1390,13 @@ linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
"LLR: Short circuiting for status 0x%x\n",
lp->status);
+ if (target_can_async_p ())
+ {
+ /* Wake event loop with special token, to get to WFI. */
+ linux_nat_event_pipe_push (-1, -1, -1);
+
+ target_async (inferior_event_handler, 0);
+ }
return;
}
@@ -1181,6 +1416,12 @@ linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT",
target_pid_to_str (ptid),
signo ? strsignal (signo) : "0");
+
+ if (target_can_async_p ())
+ {
+ target_executing = 1;
+ target_async (inferior_event_handler, 0);
+ }
}
/* Issue kill to specified lwp. */
@@ -2032,6 +2273,57 @@ linux_nat_filter_event (int lwpid, int status, int options)
return lp;
}
+/* Get the events stored in the pipe into the local queue, so they are
+ accessible to queued_waitpid. We need to do this, since it is not
+ always the case that the event at the head of the pipe is the event
+ we want. */
+
+static void
+pipe_to_local_event_queue (void)
+{
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog,
+ "PTLEQ: linux_nat_num_queued_events(%d)\n",
+ linux_nat_num_queued_events);
+ while (linux_nat_num_queued_events)
+ {
+ int lwpid, status, options;
+
+ lwpid = linux_nat_event_pipe_pop (&status, &options);
+ if (lwpid == -1 && status == -1 && options == -1)
+ /* Special wake up event loop token. */
+ continue;
+
+ gdb_assert (lwpid > 0);
+ push_waitpid (lwpid, status, options);
+ }
+}
+
+/* Get the unprocessed events stored in the local queue back into the
+ pipe, so the event loop realizes there's something else to
+ process. */
+
+static void
+local_event_queue_to_pipe (void)
+{
+ struct waitpid_result *w = waitpid_queue;
+ while (w)
+ {
+ struct waitpid_result *next = w->next;
+ linux_nat_event_pipe_push (w->pid,
+ w->status,
+ w->options);
+ xfree (w);
+ w = next;
+ }
+ waitpid_queue = NULL;
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog,
+ "LEQTP: linux_nat_num_queued_events(%d)\n",
+ linux_nat_num_queued_events);
+}
+
static ptid_t
linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
{
@@ -2041,6 +2333,9 @@ linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
pid_t pid = PIDGET (ptid);
sigset_t flush_mask;
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "LLW: enter\n");
+
/* The first time we get here after starting a new inferior, we may
not have added it to the LWP list yet - this is the earliest
moment at which we know its PID. */
@@ -2056,12 +2351,9 @@ linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
sigemptyset (&flush_mask);
- /* Make sure SIGCHLD is blocked. */
- if (!sigismember (&blocked_mask, SIGCHLD))
- {
- sigaddset (&blocked_mask, SIGCHLD);
- sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
- }
+ if (target_can_async_p ())
+ /* Block events while we're here. */
+ target_async (NULL, 0);
retry:
@@ -2085,7 +2377,7 @@ retry:
target_pid_to_str (lp->ptid));
}
- /* But if we don't fine one, we'll have to wait, and check both
+ /* But if we don't find one, we'll have to wait, and check both
cloned and uncloned processes. We start with the cloned
processes. */
options = __WCLONE | WNOHANG;
@@ -2144,15 +2436,24 @@ retry:
stop_wait_callback (lp, NULL);
}
- set_sigint_trap (); /* Causes SIGINT to be passed on to the
- attached process. */
- set_sigio_trap ();
+ if (!target_can_async_p ())
+ {
+ /* Causes SIGINT to be passed on to the attached process. */
+ set_sigint_trap ();
+ set_sigio_trap ();
+ }
while (status == 0)
{
pid_t lwpid;
- lwpid = my_waitpid (pid, &status, options);
+ if (target_can_async_p ())
+ /* In async mode, don't ever block. Only look at the locally
+ queued events. */
+ lwpid = queued_waitpid (pid, &status, options);
+ else
+ lwpid = my_waitpid (pid, &status, options);
+
if (lwpid > 0)
{
gdb_assert (pid == -1 || lwpid == pid);
@@ -2180,17 +2481,38 @@ retry:
/* Alternate between checking cloned and uncloned processes. */
options ^= __WCLONE;
- /* And suspend every time we have checked both. */
+ /* And every time we have checked both:
+ In async mode, return to event loop;
+ In sync mode, suspend waiting for a SIGCHLD signal. */
if (options & __WCLONE)
- sigsuspend (&suspend_mask);
+ {
+ if (target_can_async_p ())
+ {
+ /* No interesting event. */
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+
+ /* Get ready for the next event. */
+ target_async (inferior_event_handler, 0);
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "LLW: exit (ignore)\n");
+
+ return minus_one_ptid;
+ }
+
+ sigsuspend (&suspend_mask);
+ }
}
/* We shouldn't end up here unless we want to try again. */
gdb_assert (status == 0);
}
- clear_sigio_trap ();
- clear_sigint_trap ();
+ if (!target_can_async_p ())
+ {
+ clear_sigio_trap ();
+ clear_sigint_trap ();
+ }
gdb_assert (lp);
@@ -2287,6 +2609,13 @@ retry:
else
store_waitstatus (ourstatus, status);
+ /* Get ready for the next event. */
+ if (target_can_async_p ())
+ target_async (inferior_event_handler, 0);
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "LLW: exit\n");
+
return lp->ptid;
}
@@ -2366,6 +2695,9 @@ linux_nat_kill (void)
ptid_t last_ptid;
int status;
+ if (target_can_async_p ())
+ target_async (NULL, 0);
+
/* If we're stopped while forking and we haven't followed yet,
kill the other task. We need to do this first because the
parent will be sleeping if this is a vfork. */
@@ -2380,7 +2712,10 @@ linux_nat_kill (void)
}
if (forks_exist_p ())
- linux_fork_killall ();
+ {
+ linux_fork_killall ();
+ drain_queued_events (-1);
+ }
else
{
/* Kill all LWP's ... */
@@ -2401,13 +2736,13 @@ linux_nat_mourn_inferior (void)
/* Destroy LWP info; it's no longer valid. */
init_lwp_list ();
- /* Restore the original signal mask. */
- sigprocmask (SIG_SETMASK, &normal_mask, NULL);
- sigemptyset (&blocked_mask);
-
if (! forks_exist_p ())
- /* Normal case, no other forks available. */
- linux_ops->to_mourn_inferior ();
+ {
+ /* Normal case, no other forks available. */
+ if (target_can_async_p ())
+ linux_nat_async (NULL, 0);
+ linux_ops->to_mourn_inferior ();
+ }
else
/* Multi-fork case. The current inferior_ptid has exited, but
there are other viable forks to debug. Delete the exiting
@@ -2475,6 +2810,13 @@ linux_nat_pid_to_str (ptid_t ptid)
static void
sigchld_handler (int signo)
{
+ if (linux_nat_async_enabled
+ && linux_nat_async_events_enabled
+ && signo == SIGCHLD)
+ /* It is *always* a bug to hit this. */
+ internal_error (__FILE__, __LINE__,
+ "sigchld_handler called when async events are enabled");
+
/* Do nothing. The only reason for this handler is that it allows
us to use sigsuspend in linux_nat_wait above to wait for the
arrival of a SIGCHLD. */
@@ -3233,6 +3575,376 @@ linux_trad_target (CORE_ADDR (*register_u_offset)(struct gdbarch *, int, int))
return t;
}
+/* Controls if async mode is permitted. */
+static int linux_async_permitted = 0;
+
+/* The set command writes to this variable. If the inferior is
+ executing, linux_nat_async_permitted is *not* updated. */
+static int linux_async_permitted_1 = 0;
+
+static void
+set_maintenance_linux_async_permitted (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ if (target_has_execution)
+ {
+ linux_async_permitted_1 = linux_async_permitted;
+ error (_("Cannot change this setting while the inferior is running."));
+ }
+
+ linux_async_permitted = linux_async_permitted_1;
+ linux_nat_set_async_mode (linux_async_permitted);
+}
+
+static void
+show_maintenance_linux_async_permitted (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("\
+Controlling the GNU/Linux inferior in asynchronous mode is %s.\n"),
+ value);
+}
+
+/* target_is_async_p implementation. */
+
+static int
+linux_nat_is_async_p (void)
+{
+ /* NOTE: palves 2008-03-21: We're only async when the user requests
+ it explicitly with the "maintenance set linux-async" command.
+ Someday, linux will always be async. */
+ if (!linux_async_permitted)
+ return 0;
+
+ return 1;
+}
+
+/* target_can_async_p implementation. */
+
+static int
+linux_nat_can_async_p (void)
+{
+ /* NOTE: palves 2008-03-21: We're only async when the user requests
+ it explicitly with the "maintenance set linux-async" command.
+ Someday, linux will always be async. */
+ if (!linux_async_permitted)
+ return 0;
+
+ /* See target.h/target_async_mask. */
+ return linux_nat_async_mask_value;
+}
+
+/* target_async_mask implementation. */
+
+static int
+linux_nat_async_mask (int mask)
+{
+ int current_state;
+ current_state = linux_nat_async_mask_value;
+
+ if (current_state != mask)
+ {
+ if (mask == 0)
+ {
+ linux_nat_async (NULL, 0);
+ linux_nat_async_mask_value = mask;
+ /* We're in sync mode. Make sure SIGCHLD isn't handled by
+ async_sigchld_handler when we come out of sigsuspend in
+ linux_nat_wait. */
+ sigaction (SIGCHLD, &sync_sigchld_action, NULL);
+ }
+ else
+ {
+ /* Restore the async handler. */
+ sigaction (SIGCHLD, &async_sigchld_action, NULL);
+ linux_nat_async_mask_value = mask;
+ linux_nat_async (inferior_event_handler, 0);
+ }
+ }
+
+ return current_state;
+}
+
+/* Pop an event from the event pipe. */
+
+static int
+linux_nat_event_pipe_pop (int* ptr_status, int* ptr_options)
+{
+ struct waitpid_result event = {0};
+ int ret;
+
+ do
+ {
+ ret = read (linux_nat_event_pipe[0], &event, sizeof (event));
+ }
+ while (ret == -1 && errno == EINTR);
+
+ gdb_assert (ret == sizeof (event));
+
+ *ptr_status = event.status;
+ *ptr_options = event.options;
+
+ linux_nat_num_queued_events--;
+
+ return event.pid;
+}
+
+/* Push an event into the event pipe. */
+
+static void
+linux_nat_event_pipe_push (int pid, int status, int options)
+{
+ int ret;
+ struct waitpid_result event = {0};
+ event.pid = pid;
+ event.status = status;
+ event.options = options;
+
+ do
+ {
+ ret = write (linux_nat_event_pipe[1], &event, sizeof (event));
+ gdb_assert ((ret == -1 && errno == EINTR) || ret == sizeof (event));
+ } while (ret == -1 && errno == EINTR);
+
+ linux_nat_num_queued_events++;
+}
+
+static void
+get_pending_events (void)
+{
+ int status, options, pid;
+
+ if (!linux_nat_async_enabled || !linux_nat_async_events_enabled)
+ internal_error (__FILE__, __LINE__,
+ "get_pending_events called with async masked");
+
+ while (1)
+ {
+ status = 0;
+ options = __WCLONE | WNOHANG;
+
+ do
+ {
+ pid = waitpid (-1, &status, options);
+ }
+ while (pid == -1 && errno == EINTR);
+
+ if (pid <= 0)
+ {
+ options = WNOHANG;
+ do
+ {
+ pid = waitpid (-1, &status, options);
+ }
+ while (pid == -1 && errno == EINTR);
+ }
+
+ if (pid <= 0)
+ /* No more children reporting events. */
+ break;
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "\
+get_pending_events: pid(%d), status(%x), options (%x)\n",
+ pid, status, options);
+
+ linux_nat_event_pipe_push (pid, status, options);
+ }
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "\
+get_pending_events: linux_nat_num_queued_events(%d)\n",
+ linux_nat_num_queued_events);
+}
+
+/* SIGCHLD handler for async mode. */
+
+static void
+async_sigchld_handler (int signo)
+{
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "async_sigchld_handler\n");
+
+ get_pending_events ();
+}
+
+/* Enable or disable async SIGCHLD handling. */
+
+static int
+linux_nat_async_events (int enable)
+{
+ int current_state = linux_nat_async_events_enabled;
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog,
+ "LNAE: enable(%d): linux_nat_async_events_enabled(%d), "
+ "linux_nat_num_queued_events(%d)\n",
+ enable, linux_nat_async_events_enabled,
+ linux_nat_num_queued_events);
+
+ if (current_state != enable)
+ {
+ sigset_t mask;
+ sigemptyset (&mask);
+ sigaddset (&mask, SIGCHLD);
+ if (enable)
+ {
+ /* Unblock target events. */
+ linux_nat_async_events_enabled = 1;
+
+ local_event_queue_to_pipe ();
+ /* While in masked async, we may have not collected all the
+ pending events. Get them out now. */
+ get_pending_events ();
+ sigprocmask (SIG_UNBLOCK, &mask, NULL);
+ }
+ else
+ {
+ /* Block target events. */
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+ linux_nat_async_events_enabled = 0;
+ /* Get events out of queue, and make them available to
+ queued_waitpid / my_waitpid. */
+ pipe_to_local_event_queue ();
+ }
+ }
+
+ return current_state;
+}
+
+static int async_terminal_is_ours = 1;
+
+/* target_terminal_inferior implementation. */
+
+static void
+linux_nat_terminal_inferior (void)
+{
+ if (!target_is_async_p ())
+ {
+ /* Async mode is disabled. */
+ terminal_inferior ();
+ return;
+ }
+
+ /* GDB should never give the terminal to the inferior, if the
+ inferior is running in the background (run&, continue&, etc.).
+ This check can be removed when the common code is fixed. */
+ if (!sync_execution)
+ return;
+
+ terminal_inferior ();
+
+ if (!async_terminal_is_ours)
+ return;
+
+ delete_file_handler (input_fd);
+ async_terminal_is_ours = 0;
+ set_sigint_trap ();
+}
+
+/* target_terminal_ours implementation. */
+
+void
+linux_nat_terminal_ours (void)
+{
+ if (!target_is_async_p ())
+ {
+ /* Async mode is disabled. */
+ terminal_ours ();
+ return;
+ }
+
+ /* GDB should never give the terminal to the inferior if the
+ inferior is running in the background (run&, continue&, etc.),
+ but claiming it sure should. */
+ terminal_ours ();
+
+ if (!sync_execution)
+ return;
+
+ if (async_terminal_is_ours)
+ return;
+
+ clear_sigint_trap ();
+ add_file_handler (input_fd, stdin_event_handler, 0);
+ async_terminal_is_ours = 1;
+}
+
+static void (*async_client_callback) (enum inferior_event_type event_type,
+ void *context);
+static void *async_client_context;
+
+static void
+linux_nat_async_file_handler (int error, gdb_client_data client_data)
+{
+ async_client_callback (INF_REG_EVENT, async_client_context);
+}
+
+/* target_async implementation. */
+
+static void
+linux_nat_async (void (*callback) (enum inferior_event_type event_type,
+ void *context), void *context)
+{
+ if (linux_nat_async_mask_value == 0 || !linux_nat_async_enabled)
+ internal_error (__FILE__, __LINE__,
+ "Calling target_async when async is masked");
+
+ if (callback != NULL)
+ {
+ async_client_callback = callback;
+ async_client_context = context;
+ add_file_handler (linux_nat_event_pipe[0],
+ linux_nat_async_file_handler, NULL);
+
+ linux_nat_async_events (1);
+ }
+ else
+ {
+ async_client_callback = callback;
+ async_client_context = context;
+
+ linux_nat_async_events (0);
+ delete_file_handler (linux_nat_event_pipe[0]);
+ }
+ return;
+}
+
+/* Enable/Disable async mode. */
+
+static void
+linux_nat_set_async_mode (int on)
+{
+ if (linux_nat_async_enabled != on)
+ {
+ if (on)
+ {
+ gdb_assert (waitpid_queue == NULL);
+ sigaction (SIGCHLD, &async_sigchld_action, NULL);
+
+ if (pipe (linux_nat_event_pipe) == -1)
+ internal_error (__FILE__, __LINE__,
+ "creating event pipe failed.");
+
+ fcntl (linux_nat_event_pipe[0], F_SETFL, O_NONBLOCK);
+ fcntl (linux_nat_event_pipe[1], F_SETFL, O_NONBLOCK);
+ }
+ else
+ {
+ sigaction (SIGCHLD, &sync_sigchld_action, NULL);
+
+ drain_queued_events (-1);
+
+ linux_nat_num_queued_events = 0;
+ close (linux_nat_event_pipe[0]);
+ close (linux_nat_event_pipe[1]);
+ linux_nat_event_pipe[0] = linux_nat_event_pipe[1] = -1;
+
+ }
+ }
+ linux_nat_async_enabled = on;
+}
+
void
linux_nat_add_target (struct target_ops *t)
{
@@ -3244,6 +3956,7 @@ linux_nat_add_target (struct target_ops *t)
linux_ops = &linux_ops_saved;
/* Override some methods for multithreading. */
+ t->to_create_inferior = linux_nat_create_inferior;
t->to_attach = linux_nat_attach;
t->to_detach = linux_nat_detach;
t->to_resume = linux_nat_resume;
@@ -3255,6 +3968,13 @@ linux_nat_add_target (struct target_ops *t)
t->to_pid_to_str = linux_nat_pid_to_str;
t->to_has_thread_control = tc_schedlock;
+ t->to_can_async_p = linux_nat_can_async_p;
+ t->to_is_async_p = linux_nat_is_async_p;
+ t->to_async = linux_nat_async;
+ t->to_async_mask = linux_nat_async_mask;
+ t->to_terminal_inferior = linux_nat_terminal_inferior;
+ t->to_terminal_ours = linux_nat_terminal_ours;
+
/* We don't change the stratum; this target will sit at
process_stratum and thread_db will set at thread_stratum. This
is a little strange, since this is a multi-threaded-capable
@@ -3292,7 +4012,7 @@ linux_nat_get_siginfo (ptid_t ptid)
void
_initialize_linux_nat (void)
{
- struct sigaction action;
+ sigset_t mask;
add_info ("proc", linux_nat_info_proc_cmd, _("\
Show /proc process information about any running process.\n\
@@ -3303,27 +4023,63 @@ Specify any of the following keywords for detailed info:\n\
status -- list a different bunch of random process info.\n\
all -- list all available /proc info."));
- /* Save the original signal mask. */
+ add_setshow_zinteger_cmd ("lin-lwp", class_maintenance,
+ &debug_linux_nat, _("\
+Set debugging of GNU/Linux lwp module."), _("\
+Show debugging of GNU/Linux lwp module."), _("\
+Enables printf debugging output."),
+ NULL,
+ show_debug_linux_nat,
+ &setdebuglist, &showdebuglist);
+
+ add_setshow_zinteger_cmd ("lin-lwp-async", class_maintenance,
+ &debug_linux_nat_async, _("\
+Set debugging of GNU/Linux async lwp module."), _("\
+Show debugging of GNU/Linux async lwp module."), _("\
+Enables printf debugging output."),
+ NULL,
+ show_debug_linux_nat_async,
+ &setdebuglist, &showdebuglist);
+
+ add_setshow_boolean_cmd ("linux-async", class_maintenance,
+ &linux_async_permitted_1, _("\
+Set whether gdb controls the GNU/Linux inferior in asynchronous mode."), _("\
+Show whether gdb controls the GNU/Linux inferior in asynchronous mode."), _("\
+Tells gdb whether to control the GNU/Linux inferior in asynchronous mode."),
+ set_maintenance_linux_async_permitted,
+ show_maintenance_linux_async_permitted,
+ &maintenance_set_cmdlist,
+ &maintenance_show_cmdlist);
+
+ /* Block SIGCHLD by default. Doing this early prevents it getting
+ unblocked if an exception is thrown due to an error while the
+ inferior is starting (sigsetjmp/siglongjmp). */
+ sigemptyset (&mask);
+ sigaddset (&mask, SIGCHLD);
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+
+ /* Save this mask as the default. */
sigprocmask (SIG_SETMASK, NULL, &normal_mask);
- action.sa_handler = sigchld_handler;
- sigemptyset (&action.sa_mask);
- action.sa_flags = SA_RESTART;
- sigaction (SIGCHLD, &action, NULL);
+ /* The synchronous SIGCHLD handler. */
+ sync_sigchld_action.sa_handler = sigchld_handler;
+ sigemptyset (&sync_sigchld_action.sa_mask);
+ sync_sigchld_action.sa_flags = SA_RESTART;
+
+ /* Make it the default. */
+ sigaction (SIGCHLD, &sync_sigchld_action, NULL);
/* Make sure we don't block SIGCHLD during a sigsuspend. */
sigprocmask (SIG_SETMASK, NULL, &suspend_mask);
sigdelset (&suspend_mask, SIGCHLD);
- sigemptyset (&blocked_mask);
+ /* SIGCHLD handler for async mode. */
+ async_sigchld_action.sa_handler = async_sigchld_handler;
+ sigemptyset (&async_sigchld_action.sa_mask);
+ async_sigchld_action.sa_flags = SA_RESTART;
- add_setshow_zinteger_cmd ("lin-lwp", no_class, &debug_linux_nat, _("\
-Set debugging of GNU/Linux lwp module."), _("\
-Show debugging of GNU/Linux lwp module."), _("\
-Enables printf debugging output."),
- NULL,
- show_debug_linux_nat,
- &setdebuglist, &showdebuglist);
+ /* Install the default mode. */
+ linux_nat_set_async_mode (linux_async_permitted);
}
@@ -3359,7 +4115,9 @@ lin_thread_get_thread_signals (sigset_t *set)
{
struct sigaction action;
int restart, cancel;
+ sigset_t blocked_mask;
+ sigemptyset (&blocked_mask);
sigemptyset (set);
restart = get_signo ("__pthread_sig_restart");