* linux native async mode support
@ 2008-03-14 8:11 Pedro Alves
2008-03-14 21:17 ` Daniel Jacobowitz
2008-03-14 23:10 ` Nick Roberts
0 siblings, 2 replies; 37+ messages in thread
From: Pedro Alves @ 2008-03-14 8:11 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 1963 bytes --]
This patch adds linux native async mode support.
It is based on the same idea as Nick's patch. The basic idea is that
listening for SIGCHLD signals, call waitpid in the handler until we
get all the pending waits out, and stuff the
results in a pipe. This pipe is registered as the waitable file
in the event loop at the appropriate times.
I had to make a couple of target stack changes. I had to replace
the target_async_mask_value target variable by a proper target
method, which unfortunatelly makes me have to change remote.c
as well.
There are several target_can_async_p calls that are made *before*
the running target is pushed on the stack. That problem wasn't
visible in "target async", because there you're testing against
a target that doesn't know how to run or attach anyway.
I fixed it by making the dummy target look for the default run
target like we do for the run and attach commands.
This is the part that I think I could split out of this patch,
but I though that showing it without showing this linux patch
wouldn't make much sense.
The patch still maintains support for non-async mode, which is
why we see caring to enable/disable in async handling in several
places.
With this patch on top of the pending posted async patches,
I only have regressions in define.exp. Those are not caused
by this patch. They are generic async problems (that have to
be fixed, but independently) that are visible in "target async" as well.
To enable/disable async mode, there's a new set/show command
"set linux-async 1" enables it. I left it on in this patch, but
if/when this goes in, it should be turned off by default until
we fix at least the defines.exp problems.
To enable it from the command line, you can use
gdb -ex "set linux-async 1", of course.
(remote.c could take the same treatment and get rid
of "target async").
There are no regressions if async mode is disabled.
Comments ? I'll be happy to clarify more.
--
Pedro Alves
[-- Attachment #2: linux_async.diff --]
[-- Type: text/x-diff, Size: 44155 bytes --]
2008-03-14 Pedro Alves <pedro@codesourcery.com>
* target.h (struct target_ops): Delete to_async_mask_value and add
to_async_mask.
(target_is_async_p, target_async): Formatting.
(target_async_mask_value): Delete.
(target_async_mask): Delete function declaration, and add new
target macro with the same name.
* target.c (update_current_target): Replace to_async_mask_value by
to_async_mask. Default to_async_mask to return_one.
(target_async_mask): Delete.
(find_default_can_async_p, find_default_is_async_p): New.
(init_dummy_target): register find_default_can_async_p and
find_default_is_async_p on the dummy target.
* linux-nat.c: Include inf-loop.h, event-loop.h and event-top.h.
(debug_linux_nat_async): New global.
(show_debug_linux_nat_async): New function.
(linux_nat_async_permitted, linux_nat_async_permitted_1): New
globals.
(set_linux_nat_async_permitted, show_linux_nat_async_permitted):
New functions.
(linux_nat_async_enabled, linux_nat_async_mask_value)
(linux_nat_event_pipe, linux_nat_num_queued_events)
(linux_nat_async_events_enabled): New globals.
(struct linux_queued_event): New struct.
(cached_waitpid_queue): New global.
(cached_waitpid, push_cached_waitpid, clear_cached_waitpid_queue):
New.
(my_waitpid): Call cached_waitpid.
(linux_child_post_startup_inferior): Enable async mode.
(linux_child_follow_fork): Renable async mode if needed.
(sigchld_action, async_mask, async_old_mask, async_old_action):
New global.
(lin_lwp_attach_lwp): No need to mess with signal blocking in
async mode.
(linux_nat_attach): Implement async attaching.
(linux_nat_detach): Disable async mode if it is enabled.
(linux_nat_resume): If in async mode, mask async events on entry.
If short circuiting, force event loop wake up. If resuming, set
target_executing, and register target events in the event loop.
(pipe_to_local_event_queue): New.
(linux_nat_wait): In async mode, get events from the
cached_waitpid_queue. If only discarded events are available,
return to event loop. If the inferior stopped with an interesting
event, clear target_executing, and register target events in the
event loop.
(linux_nat_mourn_inferior): Disable async mode if there are no
more forks available, otherwise leave it on.
(sigchld_handler): If in async mode, call internal_error.
(linux_nat_is_async_p, linux_nat_can_async_p)
(linux_nat_async_mask): New.
(linux_nat_event_pipe_pop, linux_nat_event_pipe_push): New.
(async_sigchld_handler): New.
(linux_nat_async_events): New.
(async_terminal_is_ours): New global.
(linux_nat_terminal_inferior, linux_nat_terminal_ours): New.
(async_client_callback, async_client_context): New.
(linux_nat_async_file_handler): New.
(linux_nat_async, linux_nat_disable_async)
(linux_nat_enable_async): New.
(linux_nat_add_target): Register linux_nat_can_async_p,
linux_nat_is_async_p, linux_nat_async, linux_nat_async_mask,
linux_nat_terminal_inferior and linux_nat_terminal_ours.
(_initialize_linux_nat): Remove local action variable, and
update code that used it to use sigchld_action.
Add new "lin-lwp-async" debug set/show command.
Add new "linux-async" set/show command.
Setup async_mask and async_action.
* linux-thread-db.c (thread_db_can_async_p,thread_db_is_async_p)
(thread_db_async, thread_db_async_mask): New.
(clear_lwpid_callback): Handle TARGET_WAITKIND_IGNORE.
(init_thread_db_ops): Register thread_db_can_async_p,
thread_db_is_async_p, thread_db_async and thread_db_async_mask.
* remote.c (remote_async_mask_value): New.
(remote_return_zero): New.
(init_remote_ops): Register remote_return_zero as callbacks of
to_can_async_p and to_is_async_p.
(remote_can_async_p, remote_is_async_p, remote_async): Update to
use remote_async_mask_value.
(remote_async_mask): New.
(init_remote_async_ops): Remove to_async_mask_value setting and
register remote_async_mask as to_async_mask callback in
remote_async_ops.
* Makefile.in (linux-nat.o): Update.
---
gdb/Makefile.in | 2
gdb/linux-nat.c | 814 +++++++++++++++++++++++++++++++++++++++++++++++---
gdb/linux-thread-db.c | 32 +
gdb/remote.c | 26 +
gdb/target.c | 33 +-
gdb/target.h | 15
6 files changed, 855 insertions(+), 67 deletions(-)
Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in 2008-03-14 05:51:40.000000000 +0000
+++ src/gdb/Makefile.in 2008-03-14 06:06:24.000000000 +0000
@@ -2355,7 +2355,7 @@ linux-nat.o: linux-nat.c $(defs_h) $(inf
$(gdb_wait_h) $(gdb_assert_h) $(linux_nat_h) $(gdbthread_h) \
$(gdbcmd_h) $(regcache_h) $(regset_h) $(inf_ptrace_h) $(auxv_h) \
$(elf_bfd_h) $(gregset_h) $(gdbcore_h) $(gdbthread_h) $(gdb_stat_h) \
- $(linux_fork_h)
+ $(linux_fork_h) $(inf_loop_h) $(event_loop_h) $(event_top_h)
linux-thread-db.o: linux-thread-db.c $(defs_h) $(gdb_assert_h) \
$(gdb_proc_service_h) $(gdb_thread_db_h) $(bfd_h) $(exceptions_h) \
$(gdbthread_h) $(inferior_h) $(symfile_h) $(objfiles_h) $(target_h) \
Index: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c 2008-03-14 06:04:45.000000000 +0000
+++ src/gdb/linux-nat.c 2008-03-14 06:39:42.000000000 +0000
@@ -46,6 +46,10 @@
#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"
+#include <poll.h>
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
@@ -113,6 +117,44 @@ show_debug_linux_nat (struct ui_file *fi
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);
+}
+
+
+/* Controls if async mode is permitted. */
+static int linux_nat_async_permitted = 1;
+
+/* The set command writes to this variable. Only if the inferior is
+ executing linux_nat_async_permitted is *not* updated. */
+static int linux_nat_async_permitted_1 = 1;
+
+static void
+set_linux_nat_async_permitted (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ if (target_has_execution)
+ {
+ linux_nat_async_permitted_1 = linux_nat_async_permitted;
+ error (_("Cannot change this setting while the target is running."));
+ }
+ linux_nat_async_permitted = linux_nat_async_permitted_1;
+}
+
+static void
+show_linux_nat_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);
+}
+
+
static int linux_parent_pid;
struct simple_pid_list
@@ -133,6 +175,142 @@ static int linux_supports_tracefork_flag
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. */
+
+static int linux_nat_async_enabled;
+static int linux_nat_async_mask_value = 1;
+
+static int linux_nat_event_pipe[2] = { -1, -1 };
+static volatile int linux_nat_num_queued_events;
+static volatile int linux_nat_async_events_enabled;
+
+static void linux_nat_async_events (int enable);
+static void pipe_to_local_event_queue (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_enable_async (void);
+static void linux_nat_disable_async (void);
+
+struct linux_queued_event
+{
+ int pid;
+ int options;
+ int status;
+ struct linux_queued_event *next;
+};
+
+static struct linux_queued_event *cached_waitpid_queue = NULL;
+
+static int
+cached_waitpid (int pid, int *status, int flags)
+{
+ struct linux_queued_event *msg = cached_waitpid_queue;
+ struct linux_queued_event *prev = NULL;
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog,
+ "CWPID: 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
+ cached_waitpid_queue = msg->next;
+
+ msg->next = NULL;
+ if (status)
+ *status = msg->status;
+ pid = msg->pid;
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "CWPID: pid(%d), status(%x)\n",
+ pid, msg->status);
+ xfree (msg);
+
+ return pid;
+ }
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "CWPID: miss\n");
+
+ if (status)
+ *status = 0;
+ return -1;
+}
+
+static void
+push_cached_waitpid (int pid, int status, int options)
+{
+ struct linux_queued_event *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 (cached_waitpid_queue)
+ {
+ for (event = cached_waitpid_queue;
+ event && event->next;
+ event = event->next)
+ ;
+
+ event->next = new_event;
+ }
+ else
+ cached_waitpid_queue = new_event;
+}
+
+static void
+clear_cached_waitpid_queue (void)
+{
+ struct linux_queued_event *event = cached_waitpid_queue;
+ while (event)
+ {
+ struct linux_queued_event *next = event->next;
+ xfree (event);
+ event = next;
+ }
+ cached_waitpid_queue = NULL;
+}
+
\f
/* Trivial list manipulation functions to keep track of a list of
new stopped processes. */
@@ -183,12 +361,18 @@ linux_tracefork_child (void)
_exit (0);
}
-/* Wrapper function for waitpid which handles EINTR. */
+/* Wrapper function for waitpid which handles EINTR, and checks for
+ cached events. */
static int
my_waitpid (int pid, int *status, int flags)
{
int ret;
+
+ ret = cached_waitpid (pid, status, flags);
+ if (ret != -1)
+ return ret;
+
do
{
ret = waitpid (pid, status, flags);
@@ -351,6 +535,12 @@ static void
linux_child_post_startup_inferior (ptid_t ptid)
{
linux_enable_event_reporting (ptid);
+
+ /* Checking for thread_db may throw a non-fatal error, so do this
+ before. */
+ if (target_can_async_p ())
+ linux_nat_enable_async ();
+
check_for_thread_db ();
}
@@ -361,6 +551,7 @@ linux_child_follow_fork (struct target_o
struct target_waitstatus last_status;
int has_vforked;
int parent_pid, child_pid;
+ int async_was_enabled = linux_nat_async_enabled;
get_last_target_status (&last_ptid, &last_status);
has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
@@ -516,6 +707,12 @@ linux_child_follow_fork (struct target_o
target_detach (which does other necessary cleanup). */
push_target (ops);
+
+ if (async_was_enabled && !linux_nat_async_enabled)
+ /* target_detach may disable async depending on multi-threaded
+ enabled or not. Reenable it if needed. */
+ linux_nat_enable_async ();
+
linux_nat_switch_fork (inferior_ptid);
check_for_thread_db ();
@@ -616,8 +813,15 @@ static sigset_t normal_mask;
_initialize_linux_nat. */
static sigset_t suspend_mask;
+/* SIGCHLD action for synchronous mode. */
+struct sigaction sigchld_action;
+
/* Signals to block to make that sigsuspend work. */
static sigset_t blocked_mask;
+
+/* Signal mask and handler for async mode. */
+static sigset_t async_mask, async_old_mask;
+static struct sigaction async_action, async_old_action;
\f
/* Prototypes for local functions. */
@@ -877,12 +1081,15 @@ lin_lwp_attach_lwp (ptid_t ptid)
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))
+ if (!linux_nat_async_enabled)
{
- sigaddset (&blocked_mask, SIGCHLD);
- sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
+ /* 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);
+ }
}
lp = find_lwp_pid (ptid);
@@ -966,6 +1173,17 @@ linux_nat_attach (char *args, int from_t
pid_t pid;
int status;
int cloned = 0;
+ sigset_t mask, prev_mask;
+
+ /* Make sure SIGCHLD is blocked. The sync mode SIGCHLD handler
+ accesses the current_target, which is pushed by
+ linux_ops->to_attach below. This stops the race. It also
+ prevents the async mode to lose any SIGCHLD events, as the async
+ handler is only registered at function exit. */
+ sigemptyset (&mask);
+ sigaddset (&mask, SIGCHLD);
+ sigprocmask (SIG_SETMASK, NULL, &prev_mask);
+ sigprocmask (SIG_BLOCK, &mask, NULL);
/* FIXME: We should probably accept a list of process id's, and
attach all of them. */
@@ -994,9 +1212,36 @@ linux_nat_attach (char *args, int from_t
lp->stopped = 1;
- /* Fake the SIGSTOP that core GDB expects. */
- lp->status = W_STOPCODE (SIGSTOP);
- lp->resumed = 1;
+ if (target_can_async_p ())
+ /* This needs to before the above, so the current target is
+ already set when the user didn't specify an exec file, and the
+ async SIGCHLD handler doesn't mess with the waitpid calls
+ above. */
+ linux_nat_enable_async ();
+
+ if (target_can_async_p () && !sync_execution)
+ /* No need to fake the SIGSTOP and stop all threads in WFI, we're
+ going to resume right away. */
+ ;
+ else
+ {
+ /* Fake the SIGSTOP that core GDB expects. */
+ lp->status = W_STOPCODE (SIGSTOP);
+ lp->resumed = 1;
+ if (target_can_async_p ())
+ /* Wake event loop with special token, to get to WFI. */
+ linux_nat_event_pipe_push (-1, -1, -1);
+
+ if (debug_linux_nat)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "LLA: waitpid %ld, faking SIGSTOP\n", (long) pid);
+ }
+
+ /* Restore the original signal mask. */
+ sigprocmask (SIG_SETMASK, NULL, &prev_mask);
+ }
+
if (debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
@@ -1076,12 +1321,17 @@ linux_nat_detach (char *args, int from_t
/* 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));
linux_ops->to_detach (args, from_tty);
+
+ if (target_can_async_p () && linux_nat_async_enabled)
+ linux_nat_disable_async ();
+ else
+ {
+ /* Restore the original signal mask. */
+ sigprocmask (SIG_SETMASK, &normal_mask, NULL);
+ sigemptyset (&blocked_mask);
+ }
}
/* Resume LP. */
@@ -1135,6 +1385,13 @@ linux_nat_resume (ptid_t ptid, int step,
prune_lwps ();
+ if (linux_nat_async_events_enabled)
+ {
+ /* Mask events while we're here. */
+ linux_nat_async_events (0);
+ pipe_to_local_event_queue ();
+ }
+
/* A specific PTID means `step only this process id'. */
resume_all = (PIDGET (ptid) == -1);
@@ -1199,6 +1456,13 @@ linux_nat_resume (ptid_t ptid, int step,
"LLR: Short circuiting for status 0x%x\n",
lp->status);
+ if (target_can_async_p () && linux_nat_async_enabled)
+ {
+ target_async (inferior_event_handler, 0);
+
+ /* Wake event loop with special token, to get to WFI. */
+ linux_nat_event_pipe_push (-1, -1, -1);
+ }
return;
}
@@ -1212,12 +1476,17 @@ linux_nat_resume (ptid_t ptid, int step,
linux_ops->to_resume (ptid, step, signo);
memset (&lp->siginfo, 0, sizeof (lp->siginfo));
+ target_executing = 1;
+
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLR: %s %s, %s (resume event thread)\n",
step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT",
target_pid_to_str (ptid),
signo ? strsignal (signo) : "0");
+
+ if (target_can_async_p () && linux_nat_async_enabled)
+ target_async (inferior_event_handler, 0);
}
/* Issue kill to specified lwp. */
@@ -2069,6 +2338,27 @@ linux_nat_filter_event (int lwpid, int s
return lp;
}
+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_cached_waitpid (lwpid, status, options);
+ }
+}
+
static ptid_t
linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
{
@@ -2077,6 +2367,15 @@ linux_nat_wait (ptid_t ptid, struct targ
int status = 0;
pid_t pid = PIDGET (ptid);
sigset_t flush_mask;
+ struct sigaction entry_action;
+
+ /* Handle switching the SIGCHLD handler between async/sync modes.
+ Sync mode is currently used even when async is enabled, to
+ implement inferior function calls. */
+ sigaction (SIGCHLD, NULL, &entry_action);
+
+ 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
@@ -2093,11 +2392,29 @@ linux_nat_wait (ptid_t ptid, struct targ
sigemptyset (&flush_mask);
- /* Make sure SIGCHLD is blocked. */
- if (!sigismember (&blocked_mask, SIGCHLD))
+ if (linux_nat_async_events_enabled)
{
- sigaddset (&blocked_mask, SIGCHLD);
- sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
+ /* Block events while we're here. They'll be enabled on linux_nat_resume. */
+ linux_nat_async_events (0);
+ /* Get events out of queue, and make them available to
+ cached_waitpid / my_waitpid. */
+ pipe_to_local_event_queue ();
+ }
+ else
+ {
+ /* Make sure SIGCHLD is blocked. */
+ if (!sigismember (&blocked_mask, SIGCHLD))
+ {
+ sigaddset (&blocked_mask, SIGCHLD);
+ sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
+ }
+
+ /* We're in sync mode. Make sure SIGCHLD isn't handled by
+ async_sigchld_handler, which would call waitpid and put the
+ results in the event pipe. When we wake up from sigsuspend
+ below, we wan't to call waitpid ourselves. It is just easier
+ and more efficient to toggle the handler. */
+ sigaction (SIGCHLD, &sigchld_action, NULL);
}
retry:
@@ -2122,7 +2439,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;
@@ -2189,7 +2506,13 @@ retry:
{
pid_t lwpid;
- lwpid = my_waitpid (pid, &status, options);
+ if (target_can_async_p () && linux_nat_async_enabled)
+ /* In async mode, don't ever block. Only look at the locally
+ queued events. */
+ lwpid = cached_waitpid (pid, &status, options);
+ else
+ lwpid = my_waitpid (pid, &status, options);
+
if (lwpid > 0)
{
gdb_assert (pid == -1 || lwpid == pid);
@@ -2217,9 +2540,27 @@ 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 () && linux_nat_async_enabled)
+ {
+ /* 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;
+ }
+ else
+ sigsuspend (&suspend_mask);
+ }
}
/* We shouldn't end up here unless we want to try again. */
@@ -2324,6 +2665,17 @@ retry:
else
store_waitstatus (ourstatus, status);
+ target_executing = 0;
+
+ sigaction (SIGCHLD, &entry_action, NULL);
+
+ /* Get ready for the next event. */
+ if (target_can_async_p () && linux_nat_async_enabled)
+ target_async (inferior_event_handler, 0);
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "LLW: exit\n");
+
return lp->ptid;
}
@@ -2438,18 +2790,27 @@ 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 ();
+ {
+ if (target_can_async_p () && linux_nat_async_enabled)
+ linux_nat_disable_async ();
+ else
+ {
+ /* Restore the original signal mask. */
+ sigprocmask (SIG_SETMASK, &normal_mask, NULL);
+ sigemptyset (&blocked_mask);
+ }
+
+ /* Normal case, no other forks available. */
+ 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
- one and context-switch to the first available. */
- linux_fork_mourn_inferior ();
+ {
+ /* Multi-fork case. The current inferior_ptid has exited, but
+ there are other viable forks to debug. Delete the exiting
+ one and context-switch to the first available. */
+ linux_fork_mourn_inferior ();
+ }
}
static LONGEST
@@ -2512,6 +2873,11 @@ linux_nat_pid_to_str (ptid_t ptid)
static void
sigchld_handler (int signo)
{
+ if (target_can_async_p () && linux_nat_async_enabled && signo == SIGCHLD)
+ /* It is *always* a bug to hit this. */
+ internal_error (__FILE__, __LINE__,
+ "sigchld_handler called when async is 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. */
@@ -3270,6 +3636,331 @@ linux_trad_target (CORE_ADDR (*register_
return t;
}
+/* target_is_async_p implementation. */
+
+static int
+linux_nat_is_async_p (void)
+{
+ /* NOTE: palves 2008-03-11: We're only async when the user requests
+ it explicitly with the "set linux-async" command. Someday, linux
+ will always be async. */
+ if (!linux_nat_async_permitted)
+ return 0;
+
+ return 1;
+}
+
+/* target_can_async_p implementation. */
+
+static int
+linux_nat_can_async_p (void)
+{
+ /* NOTE: palves 2008-03-11: We're only async when the user requests
+ it explicitly with the "set linux-async" command. Someday, linux
+ will always be async. */
+ if (!linux_nat_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 = linux_nat_async_mask_value;
+ linux_nat_async_mask_value = mask;
+ return current_state;
+}
+
+/* Pop an event from the event pipe. */
+
+static int
+linux_nat_event_pipe_pop (int* ptr_status, int* ptr_options)
+{
+ struct linux_queued_event 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 linux_queued_event 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++;
+}
+
+/* SIGCHLD handler for async mode. */
+
+static void
+async_sigchld_handler (int signo)
+{
+ int status, options, pid;
+
+ if (!linux_nat_async_enabled || !linux_nat_async_events_enabled)
+ internal_error (__FILE__, __LINE__,
+ "async_sigchld_handler 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 status == 0, the lwp is exiting. */
+
+ if (debug_linux_nat_async)
+ fprintf_unfiltered (gdb_stdlog, "\
+async_sigchld_handler: 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,
+ "async_sigchld_handler: linux_nat_num_queued_events(%d)\n",
+ linux_nat_num_queued_events);
+}
+
+/* Enable or disable async SIGCHLD handling. */
+
+static void
+linux_nat_async_events (int enable)
+{
+ 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 (enable)
+ {
+ gdb_assert (!linux_nat_async_events_enabled);
+
+ /* Unblock target events. */
+ linux_nat_async_events_enabled = 1;
+ sigprocmask (SIG_SETMASK, &async_mask, &async_old_mask);
+ }
+ else
+ {
+ gdb_assert (linux_nat_async_events_enabled);
+
+ /* Block target events. */
+ sigprocmask (SIG_SETMASK, &async_old_mask, NULL);
+ linux_nat_async_events_enabled = 0;
+ }
+}
+
+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;
+}
+
+/* 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;
+
+ 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 (! (target_can_async_p () && 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);
+
+ if (!linux_nat_async_events_enabled)
+ linux_nat_async_events (1);
+ }
+ else
+ {
+ async_client_callback = callback;
+ async_client_context = context;
+
+ if (linux_nat_async_events_enabled)
+ linux_nat_async_events (0);
+ delete_file_handler (linux_nat_event_pipe[0]);
+ }
+ return;
+}
+
+/* Disable async mode. */
+
+static void
+linux_nat_disable_async (void)
+{
+ /* Unregister from event loop. Don't go through the target stack
+ (target_async), as linux-nat may have been poped off already. */
+ linux_nat_async (NULL, 0);
+
+ linux_nat_async_enabled = 0;
+
+ sigaction (SIGCHLD, &async_old_action, NULL);
+
+ /* Restore the original signal mask. */
+ sigprocmask (SIG_SETMASK, &normal_mask, NULL);
+ sigemptyset (&blocked_mask);
+
+ clear_cached_waitpid_queue ();
+ linux_nat_async_enabled = 0;
+ 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;
+}
+
+/* Enable async mode. */
+
+static void
+linux_nat_enable_async (void)
+{
+ sigset_t mask;
+
+ gdb_assert (linux_nat_async_enabled == 0);
+ gdb_assert (cached_waitpid_queue == NULL);
+
+ /* Order matters. Disable SIGCHLD first so we don't hit the
+ assertion in sigchld. */
+
+ /* Make sure SIGCHLD is only delivered when we want. */
+ sigprocmask (SIG_SETMASK, NULL, &mask);
+ if (!sigismember (&mask, SIGCHLD))
+ {
+ sigaddset (&mask, SIGCHLD);
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+ }
+ sigaction (SIGCHLD, &async_action, &async_old_action);
+
+ 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);
+
+ linux_nat_async_enabled = 1;
+
+ /* Register in the event loop. Don't go through the target stack
+ (target_async), as we're not sure linux-nat is pushed on the
+ stack yet. We may be called as a result of of a
+ find_default_run_target. */
+ linux_nat_async (inferior_event_handler, 0);
+}
+
void
linux_nat_add_target (struct target_ops *t)
{
@@ -3292,6 +3983,13 @@ linux_nat_add_target (struct target_ops
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
@@ -3329,8 +4027,6 @@ linux_nat_get_siginfo (ptid_t ptid)
void
_initialize_linux_nat (void)
{
- struct sigaction action;
-
add_info ("proc", linux_nat_info_proc_cmd, _("\
Show /proc process information about any running process.\n\
Specify any process id, or use the program being debugged by default.\n\
@@ -3340,13 +4036,39 @@ Specify any of the following keywords fo
status -- list a different bunch of random process info.\n\
all -- list all available /proc info."));
+ 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);
+
+ add_setshow_zinteger_cmd ("lin-lwp-async", no_class,
+ &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_obscure,
+ &linux_nat_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_linux_nat_async_permitted,
+ show_linux_nat_async_permitted,
+ &setlist, &showlist);
+
/* Save the original signal mask. */
sigprocmask (SIG_SETMASK, NULL, &normal_mask);
- action.sa_handler = sigchld_handler;
- sigemptyset (&action.sa_mask);
- action.sa_flags = SA_RESTART;
- sigaction (SIGCHLD, &action, NULL);
+ sigchld_action.sa_handler = sigchld_handler;
+ sigemptyset (&sigchld_action.sa_mask);
+ sigchld_action.sa_flags = SA_RESTART;
+ sigaction (SIGCHLD, &sigchld_action, NULL);
/* Make sure we don't block SIGCHLD during a sigsuspend. */
sigprocmask (SIG_SETMASK, NULL, &suspend_mask);
@@ -3354,13 +4076,17 @@ Specify any of the following keywords fo
sigemptyset (&blocked_mask);
- 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);
+ /* Set up signal handling for asynchronous mode. */
+
+ /* Make sure we don't block SIGCHLD during a poll/select. */
+ sigprocmask (SIG_SETMASK, NULL, &async_mask);
+ sigdelset (&async_mask, SIGCHLD);
+
+ /* SIGCHLD handler for async mode. */
+ async_action.sa_handler = async_sigchld_handler;
+ sigemptyset (&async_action.sa_mask);
+ async_action.sa_flags = SA_RESTART;
+ sigaddset (&async_action.sa_mask, SIGCHLD);
}
\f
Index: src/gdb/linux-thread-db.c
===================================================================
--- src.orig/gdb/linux-thread-db.c 2008-03-14 05:51:31.000000000 +0000
+++ src/gdb/linux-thread-db.c 2008-03-14 06:06:24.000000000 +0000
@@ -754,6 +754,31 @@ thread_db_detach (char *args, int from_t
}
static int
+thread_db_can_async_p (void)
+{
+ return target_beneath->to_can_async_p ();
+}
+
+static int
+thread_db_is_async_p (void)
+{
+ return target_beneath->to_is_async_p ();
+}
+
+static void
+thread_db_async (void (*callback) (enum inferior_event_type event_type,
+ void *context), void *context)
+{
+ return target_beneath->to_async (callback, context);
+}
+
+static int
+thread_db_async_mask (int mask)
+{
+ return target_beneath->to_async_mask (mask);
+}
+
+static int
clear_lwpid_callback (struct thread_info *thread, void *dummy)
{
/* If we know that our thread implementation is 1-to-1, we could save
@@ -871,6 +896,9 @@ thread_db_wait (ptid_t ptid, struct targ
ptid = target_beneath->to_wait (ptid, ourstatus);
+ if (ourstatus->kind == TARGET_WAITKIND_IGNORE)
+ return ptid;
+
if (ourstatus->kind == TARGET_WAITKIND_EXITED
|| ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
return pid_to_ptid (-1);
@@ -1134,6 +1162,10 @@ init_thread_db_ops (void)
thread_db_ops.to_get_thread_local_address
= thread_db_get_thread_local_address;
thread_db_ops.to_extra_thread_info = thread_db_extra_thread_info;
+ thread_db_ops.to_can_async_p = thread_db_can_async_p;
+ thread_db_ops.to_is_async_p = thread_db_is_async_p;
+ thread_db_ops.to_async = thread_db_async;
+ thread_db_ops.to_async_mask = thread_db_async_mask;
thread_db_ops.to_magic = OPS_MAGIC;
}
Index: src/gdb/remote.c
===================================================================
--- src.orig/gdb/remote.c 2008-03-14 05:51:31.000000000 +0000
+++ src/gdb/remote.c 2008-03-14 06:06:24.000000000 +0000
@@ -481,6 +481,8 @@ static struct target_ops extended_remote
extended_remote_ops, but with asynchronous support. */
static struct target_ops remote_async_ops;
+static int remote_async_mask_value = 1;
+
static struct target_ops extended_async_remote_ops;
/* FIXME: cagney/1999-09-23: Even though getpkt was called with
@@ -7211,6 +7213,12 @@ remote_command (char *args, int from_tty
help_list (remote_cmdlist, "remote ", -1, gdb_stdout);
}
+static int
+remote_return_zero (void)
+{
+ return 0;
+}
+
static void
init_remote_ops (void)
{
@@ -7264,6 +7272,8 @@ Specify the serial device it is connecte
remote_ops.to_flash_erase = remote_flash_erase;
remote_ops.to_flash_done = remote_flash_done;
remote_ops.to_read_description = remote_read_description;
+ remote_ops.to_can_async_p = remote_return_zero;
+ remote_ops.to_is_async_p = remote_return_zero;
}
/* Set up the extended remote vector by making a copy of the standard
@@ -7291,14 +7301,14 @@ static int
remote_can_async_p (void)
{
/* We're async whenever the serial device is. */
- return (current_target.to_async_mask_value) && serial_can_async_p (remote_desc);
+ return remote_async_mask_value && serial_can_async_p (remote_desc);
}
static int
remote_is_async_p (void)
{
/* We're async whenever the serial device is. */
- return (current_target.to_async_mask_value) && serial_is_async_p (remote_desc);
+ return remote_async_mask_value && serial_is_async_p (remote_desc);
}
/* Pass the SERIAL event on and up to the client. One day this code
@@ -7322,7 +7332,7 @@ static void
remote_async (void (*callback) (enum inferior_event_type event_type,
void *context), void *context)
{
- if (current_target.to_async_mask_value == 0)
+ if (remote_async_mask_value == 0)
internal_error (__FILE__, __LINE__,
_("Calling remote_async when async is masked"));
@@ -7336,6 +7346,14 @@ remote_async (void (*callback) (enum inf
serial_async (remote_desc, NULL, NULL);
}
+static int
+remote_async_mask (int new_mask)
+{
+ int curr_mask = remote_async_mask_value;
+ remote_async_mask_value = new_mask;
+ return curr_mask;
+}
+
/* Target async and target extended-async.
This are temporary targets, until it is all tested. Eventually
@@ -7395,7 +7413,7 @@ Specify the serial device it is connecte
remote_async_ops.to_can_async_p = remote_can_async_p;
remote_async_ops.to_is_async_p = remote_is_async_p;
remote_async_ops.to_async = remote_async;
- remote_async_ops.to_async_mask_value = 1;
+ remote_async_ops.to_async_mask = remote_async_mask;
remote_async_ops.to_magic = OPS_MAGIC;
remote_async_ops.to_memory_map = remote_memory_map;
remote_async_ops.to_flash_erase = remote_flash_erase;
Index: src/gdb/target.c
===================================================================
--- src.orig/gdb/target.c 2008-03-14 05:51:31.000000000 +0000
+++ src/gdb/target.c 2008-03-14 06:11:07.000000000 +0000
@@ -472,7 +472,7 @@ update_current_target (void)
INHERIT (to_can_async_p, t);
INHERIT (to_is_async_p, t);
INHERIT (to_async, t);
- INHERIT (to_async_mask_value, t);
+ INHERIT (to_async_mask, t);
INHERIT (to_find_memory_regions, t);
INHERIT (to_make_corefile_notes, t);
INHERIT (to_get_thread_local_address, t);
@@ -648,6 +648,9 @@ update_current_target (void)
de_fault (to_async,
(void (*) (void (*) (enum inferior_event_type, void*), void*))
tcomplain);
+ de_fault (to_async_mask,
+ (int (*) (int))
+ return_one);
current_target.to_read_description = NULL;
#undef de_fault
@@ -1713,14 +1716,6 @@ target_disconnect (char *args, int from_
tcomplain ();
}
-int
-target_async_mask (int mask)
-{
- int saved_async_masked_status = target_async_mask_value;
- target_async_mask_value = mask;
- return saved_async_masked_status;
-}
-
/* Look through the list of possible targets for a target that can
follow forks. */
@@ -1819,6 +1814,24 @@ find_default_create_inferior (char *exec
return;
}
+int
+find_default_can_async_p (void)
+{
+ struct target_ops *t;
+
+ t = find_default_run_target ("async");
+ return (t->to_can_async_p) ();
+}
+
+int
+find_default_is_async_p (void)
+{
+ struct target_ops *t;
+
+ t = find_default_run_target ("async");
+ return (t->to_is_async_p) ();
+}
+
static int
default_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
{
@@ -2083,6 +2096,8 @@ init_dummy_target (void)
dummy_target.to_doc = "";
dummy_target.to_attach = find_default_attach;
dummy_target.to_create_inferior = find_default_create_inferior;
+ dummy_target.to_can_async_p = find_default_can_async_p;
+ dummy_target.to_is_async_p = find_default_is_async_p;
dummy_target.to_pid_to_str = normal_pid_to_str;
dummy_target.to_stratum = dummy_stratum;
dummy_target.to_find_memory_regions = dummy_find_memory_regions;
Index: src/gdb/target.h
===================================================================
--- src.orig/gdb/target.h 2008-03-14 05:51:31.000000000 +0000
+++ src/gdb/target.h 2008-03-14 06:06:24.000000000 +0000
@@ -415,9 +415,8 @@ struct target_ops
/* ASYNC target controls */
int (*to_can_async_p) (void);
int (*to_is_async_p) (void);
- void (*to_async) (void (*cb) (enum inferior_event_type, void *context),
- void *context);
- int to_async_mask_value;
+ void (*to_async) (void (*) (enum inferior_event_type, void *), void *);
+ int (*to_async_mask) (int);
int (*to_find_memory_regions) (int (*) (CORE_ADDR,
unsigned long,
int, int, int,
@@ -946,11 +945,11 @@ int target_follow_fork (int follow_child
#define target_can_async_p() (current_target.to_can_async_p ())
/* Is the target in asynchronous execution mode? */
-#define target_is_async_p() (current_target.to_is_async_p())
+#define target_is_async_p() (current_target.to_is_async_p ())
/* Put the target in async mode with the specified callback function. */
#define target_async(CALLBACK,CONTEXT) \
- (current_target.to_async((CALLBACK), (CONTEXT)))
+ (current_target.to_async ((CALLBACK), (CONTEXT)))
/* This is to be used ONLY within call_function_by_hand(). It provides
a workaround, to have inferior function calls done in sychronous
@@ -966,10 +965,8 @@ int target_follow_fork (int follow_child
the turning async on and off to the single execution commands,
from where it is done currently, in remote_resume(). */
-#define target_async_mask_value \
- (current_target.to_async_mask_value)
-
-extern int target_async_mask (int mask);
+#define target_async_mask(MASK) \
+ (current_target.to_async_mask (MASK))
/* Converts a process id to a string. Usually, the string just contains
`process xyz', but on some systems it may contain
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-14 8:11 linux native async mode support Pedro Alves
@ 2008-03-14 21:17 ` Daniel Jacobowitz
2008-03-17 16:05 ` Pedro Alves
2008-03-14 23:10 ` Nick Roberts
1 sibling, 1 reply; 37+ messages in thread
From: Daniel Jacobowitz @ 2008-03-14 21:17 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
Nick, would you like to comment on this before it goes in?
On Fri, Mar 14, 2008 at 08:10:22AM +0000, Pedro Alves wrote:
> To enable/disable async mode, there's a new set/show command
> "set linux-async 1" enables it. I left it on in this patch, but
> if/when this goes in, it should be turned off by default until
> we fix at least the defines.exp problems.
Could you move this to "maint set"? Users shouldn't go twiddling it.
When everything is together, I hope we can remove it again.
Until then, it has to go in the manual; Eli went to a lot of trouble
to fix all the undocumented commands :-) The debug command needs
to also, unless you want to lump it in with debug_linux_nat.
> (remote.c could take the same treatment and get rid
> of "target async").
Yeah. If you want to do that, we could call it "maint set async".
> @@ -516,6 +707,12 @@ linux_child_follow_fork (struct target_o
> target_detach (which does other necessary cleanup). */
>
> push_target (ops);
> +
> + if (async_was_enabled && !linux_nat_async_enabled)
> + /* target_detach may disable async depending on multi-threaded
> + enabled or not. Reenable it if needed. */
> + linux_nat_enable_async ();
> +
> linux_nat_switch_fork (inferior_ptid);
> check_for_thread_db ();
>
This will drain the queue, losing any pending events. In non-stop
mode I can imagine that causing trouble. Maybe we need to avoid
target_detach now.
> @@ -966,6 +1173,17 @@ linux_nat_attach (char *args, int from_t
> pid_t pid;
> int status;
> int cloned = 0;
> + sigset_t mask, prev_mask;
> +
> + /* Make sure SIGCHLD is blocked. The sync mode SIGCHLD handler
> + accesses the current_target, which is pushed by
> + linux_ops->to_attach below. This stops the race. It also
> + prevents the async mode to lose any SIGCHLD events, as the async
> + handler is only registered at function exit. */
> + sigemptyset (&mask);
> + sigaddset (&mask, SIGCHLD);
> + sigprocmask (SIG_SETMASK, NULL, &prev_mask);
> + sigprocmask (SIG_BLOCK, &mask, NULL);
That's the same as sigprocmask (SIG_BLOCK, &mask, &prev_mask).
We can get a SIGCHLD pretty much any time, even when we think
the target is stopped (e.g. if we detached from something
that ran on the same terminal, and it finally exited). Do we
reach common code that might change the target stack while
SIGCHLD is unblocked? If it's just for the new assert, adding
the assert may not be worth the trouble.
>
> /* FIXME: We should probably accept a list of process id's, and
> attach all of them. */
> @@ -994,9 +1212,36 @@ linux_nat_attach (char *args, int from_t
>
> lp->stopped = 1;
>
> - /* Fake the SIGSTOP that core GDB expects. */
> - lp->status = W_STOPCODE (SIGSTOP);
> - lp->resumed = 1;
> + if (target_can_async_p ())
> + /* This needs to before the above, so the current target is
> + already set when the user didn't specify an exec file, and the
> + async SIGCHLD handler doesn't mess with the waitpid calls
> + above. */
> + linux_nat_enable_async ();
"This needs to before the above" has wrong grammar, and I don't know
what it means; do you mean after?
> @@ -1199,6 +1456,13 @@ linux_nat_resume (ptid_t ptid, int step,
> "LLR: Short circuiting for status 0x%x\n",
> lp->status);
>
> + if (target_can_async_p () && linux_nat_async_enabled)
> + {
> + target_async (inferior_event_handler, 0);
> +
> + /* Wake event loop with special token, to get to WFI. */
> + linux_nat_event_pipe_push (-1, -1, -1);
> + }
> return;
> }
>
If target_can_async_p, doesn't that imply linux_nat_async_enabled?
> + /* We're in sync mode. Make sure SIGCHLD isn't handled by
> + async_sigchld_handler, which would call waitpid and put the
> + results in the event pipe. When we wake up from sigsuspend
> + below, we wan't to call waitpid ourselves. It is just easier
> + and more efficient to toggle the handler. */
wan't -> want
> if (options & __WCLONE)
> - sigsuspend (&suspend_mask);
> + {
> + if (target_can_async_p () && linux_nat_async_enabled)
> + {
> + /* 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;
If we just return from here, we miss clear_sigio_trap and
clear_sigint_trap. But we keep calling set_sigint_trap on each
entrance. I think we'll eventually end up with them saving and
restoring the wrong signal handlers.
> +/* target_can_async_p implementation. */
> +
> +static int
> +linux_nat_can_async_p (void)
> +{
> + /* NOTE: palves 2008-03-11: We're only async when the user requests
> + it explicitly with the "set linux-async" command. Someday, linux
> + will always be async. */
> + if (!linux_nat_async_permitted)
> + return 0;
> +
> + /* See target.h/target_async_mask. */
> + return linux_nat_async_mask_value;
> +}
I hope we can get rid of target_async_mask someday. Would just a
wait call suffice? if (target_executing) reenter the event
loop or something like that?
> +/* Disable async mode. */
> +
> +static void
> +linux_nat_disable_async (void)
> +{
> + /* Unregister from event loop. Don't go through the target stack
> + (target_async), as linux-nat may have been poped off already. */
popped
> + linux_nat_async (NULL, 0);
> +
> + linux_nat_async_enabled = 0;
> +
> + sigaction (SIGCHLD, &async_old_action, NULL);
> +
> + /* Restore the original signal mask. */
> + sigprocmask (SIG_SETMASK, &normal_mask, NULL);
> + sigemptyset (&blocked_mask);
> +
> + clear_cached_waitpid_queue ();
> + linux_nat_async_enabled = 0;
> + linux_nat_num_queued_events = 0;
Sets linux_nat_async_enabled twice.
Other than that, I don't see any problems.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-14 8:11 linux native async mode support Pedro Alves
2008-03-14 21:17 ` Daniel Jacobowitz
@ 2008-03-14 23:10 ` Nick Roberts
2008-03-15 1:58 ` Pedro Alves
1 sibling, 1 reply; 37+ messages in thread
From: Nick Roberts @ 2008-03-14 23:10 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
> This patch adds linux native async mode support.
Wow! This is an awesome patch especially as your previous changes seem to be
for different parts of Gdb. I think my patch only really worked if only one
SIGCHLD signal came in for each round trip in the event loop. I was starting
to see that that the signal handler shouldn't be wrapped around select and that
I would need to queue signals but I never began to understand all the waitpids
in linux_nat_wait or how to get back to the event loop when all the SIGCHLD
signals had been processed.
I have some initial comments but bear in mind it's from a position of lesser
understanding. I can see that I really need to up my game a bit!
It seems unfortunate that you had to move async_file_handler into linux_nat.c
as much of this seems generic and will presumably make it harder to port the
async mode to other platforms. I put it in inf-ptrace.c (*) which creates
problems if the target doesn't actually support async mode. More recently I
thought maybe not setting t->to_async_mask_value to 1 in inf_ptrace_target
might solve that.
(*) I copied this from Apple whose handler was in macosx-nat-inferior.c
but their build is only for one architecture (maybe two now but source
appears to be no longer available from their repository)
---
+ /* SIGCHLD handler for async mode. */
+ async_action.sa_handler = async_sigchld_handler;
+ sigemptyset (&async_action.sa_mask);
+ async_action.sa_flags = SA_RESTART;
+ sigaddset (&async_action.sa_mask, SIGCHLD);
I'm not sure that last line is necessary (I had it in my patch, I know) as the
libc manual says:
When a handler function is invoked on a signal, that signal is
automatically blocked (in addition to any other signals that are already
in the process's signal mask) during the time the handler is running.
---
Daniel talks about async mode being the default but I don't really understand
this. In order for Gdb to be able to accept input while the inferior is
running and possibly accepting it's own input, seems to require separate
terminals. This isn't a problem for a front end but, in general, will be
when Gdb is run from the command line.
--
Nick http://www.inet.net.nz/~nickrob
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-14 23:10 ` Nick Roberts
@ 2008-03-15 1:58 ` Pedro Alves
2008-03-15 3:11 ` Daniel Jacobowitz
2008-03-17 23:41 ` Nick Roberts
0 siblings, 2 replies; 37+ messages in thread
From: Pedro Alves @ 2008-03-15 1:58 UTC (permalink / raw)
To: Nick Roberts; +Cc: gdb-patches
A Friday 14 March 2008 23:09:51, Nick wrote:
> Wow!
:-)
> It seems unfortunate that you had to move async_file_handler into
> linux_nat.c as much of this seems generic and will presumably make it
> harder to port the async mode to other platforms. I put it in inf-ptrace.c
> (*) which creates problems if the target doesn't actually support async
> mode. More recently I thought maybe not setting t->to_async_mask_value to
> 1 in inf_ptrace_target might solve that.
>
On the contraire. Not assuming abstractions upfront allows us to
enhance and test the linux port, without worrying about the other
inf-ptrace.c clients breaking. We'll add the non-stop support soon, and
that will need further tweaking. We can worry about abstractions and
common code when someone wants to add async support to another target.
I'd advise to hold on a bit until we stabilize this port a bit more.
> Daniel talks about async mode being the default but I don't really
> understand this. In order for Gdb to be able to accept input while the
> inferior is running and possibly accepting it's own input, seems to require
> separate terminals. This isn't a problem for a front end but, in general,
> will be when Gdb is run from the command line.
This is a confusion of terms. The async mode he was refering to, is
the event loop code path, in which we (ideally) never block in
wait_for_inferior.
I'm sure you know most of this already, but let me repeat it for
clarity.
If the target supports async event loop mode, the user can still run
the inferior in synchronous mode, with the usual commands, and
gdb can put the inferior in the foreground. That's currently
controlled by the sync_execution global variable. The sync_execution
mode is implemented on top of the async/event loop code path, and can
(ideally) be ignored by the target code (as long as it supports
async/event loop mode). It is up to gdb common code to disable
stdin to simulate synchronous execution, which enables passing
the terminal to the inferior. From a users perpective, target
in event loop mode with sync_execution set is the same as
what we have had until now.
In addition to that mode, by having the target in async/event loop
mode, the user/frontend can use the background execution commands
(run&, continue&, step&, etc.), which makes gdb accept input
from the user/frontend while the inferior is running. This is
essential for non-stop mode, which we'll be adding soon.
In a nutshell, having the target in "async mode" has no
drawbacks, only advantages. It's only that it isn't perfect yet. When
we get to the point where the non-async code path is not needed anymore,
we can drop a bunch of support code. That's doesn't seem likelly to
happen anytime soon in the common code, but it can be done target by
target. Linux native and target (extended-)remote are the first
candidates.
(I'll address the patch comments later)
Thanks!
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-15 1:58 ` Pedro Alves
@ 2008-03-15 3:11 ` Daniel Jacobowitz
2008-03-17 23:41 ` Nick Roberts
1 sibling, 0 replies; 37+ messages in thread
From: Daniel Jacobowitz @ 2008-03-15 3:11 UTC (permalink / raw)
To: gdb-patches
On Sat, Mar 15, 2008 at 01:57:47AM +0000, Pedro Alves wrote:
> we get to the point where the non-async code path is not needed anymore,
> we can drop a bunch of support code. That's doesn't seem likelly to
> happen anytime soon in the common code, but it can be done target by
> target. Linux native and target (extended-)remote are the first
> candidates.
Exactly. Pedro and I both know how to do this on native Windows, and
can test there. A similar version to the GNU/Linux code will work on
other ptrace targets (not exactly the same, I think, but pretty
close). Ttrace may need something different, but I'm sure it's
possible. I suspect that procfs will be easy, for someone more
familiar with it. And the various remote targets are all similar
in this regard.
In short, I don't think it will be very difficult to get rid of
synchronous target_wait. It will probably take a year from when
we get GNU/Linux and remote working, while we round up people familiar
with all the various supported interfaces, and some of them we'll
probably have to guess. But I think we can do it.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-14 21:17 ` Daniel Jacobowitz
@ 2008-03-17 16:05 ` Pedro Alves
2008-03-17 22:05 ` Daniel Jacobowitz
` (2 more replies)
0 siblings, 3 replies; 37+ messages in thread
From: Pedro Alves @ 2008-03-17 16:05 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 8491 bytes --]
A Friday 14 March 2008 21:16:46, Daniel Jacobowitz wrote:
> On Fri, Mar 14, 2008 at 08:10:22AM +0000, Pedro Alves wrote:
> > To enable/disable async mode, there's a new set/show command
> > "set linux-async 1" enables it. I left it on in this patch, but
> > if/when this goes in, it should be turned off by default until
> > we fix at least the defines.exp problems.
>
> Could you move this to "maint set"? Users shouldn't go twiddling it.
> When everything is together, I hope we can remove it again.
>
Done.
> Until then, it has to go in the manual; Eli went to a lot of trouble
> to fix all the undocumented commands :-) The debug command needs
> to also, unless you want to lump it in with debug_linux_nat.
>
The debug output is too noisy to be merged yet. But is is invaluable to
catch problems. Added docs to the patch.
> > @@ -516,6 +707,12 @@ linux_child_follow_fork (struct target_o
> > target_detach (which does other necessary cleanup). */
> >
> > push_target (ops);
> > +
> > + if (async_was_enabled && !linux_nat_async_enabled)
> > + /* target_detach may disable async depending on multi-threaded
> > + enabled or not. Reenable it if needed. */
> > + linux_nat_enable_async ();
> > +
> > linux_nat_switch_fork (inferior_ptid);
> > check_for_thread_db ();
>
> This will drain the queue, losing any pending events. In non-stop
> mode I can imagine that causing trouble. Maybe we need to avoid
> target_detach now.
>
Yeah. Here's how I fixed it: target_detach, when the target is
multi-threaded, would call thread_db_detach, which calls
target_mourn_inferior. The mourn inferior call (although most targets
don't mourn on detach), is there AFAICT mostly to reset
using_thread_db. There's nothing in linux_nat_mourn_inferior that
we'd miss. Notice that linux_nat_detach doesn't mourn, so a
single-threaded inferior doesn't get mourned.
So, I split linux_nat_detach in two (linux_nat_detach_inferior, and
linux_nat_detach proper which calls linux_nat_detach_inferior), and
added a re_check_for_thread_db, to be used after following the child,
so thread_db knows about the new current inferior.
> > @@ -966,6 +1173,17 @@ linux_nat_attach (char *args, int from_t
> > pid_t pid;
> > int status;
> > int cloned = 0;
> > + sigset_t mask, prev_mask;
> > +
> > + /* Make sure SIGCHLD is blocked. The sync mode SIGCHLD handler
> > + accesses the current_target, which is pushed by
> > + linux_ops->to_attach below. This stops the race. It also
> > + prevents the async mode to lose any SIGCHLD events, as the async
> > + handler is only registered at function exit. */
> > + sigemptyset (&mask);
> > + sigaddset (&mask, SIGCHLD);
> > + sigprocmask (SIG_SETMASK, NULL, &prev_mask);
> > + sigprocmask (SIG_BLOCK, &mask, NULL);
>
> That's the same as sigprocmask (SIG_BLOCK, &mask, &prev_mask).
>
Indeeed. I've reworked and cleaned the patch, so these
sigprocmask handlings are mostly unneeded. I think this version
looks much better.
> We can get a SIGCHLD pretty much any time, even when we think
> the target is stopped (e.g. if we detached from something
> that ran on the same terminal, and it finally exited). Do we
> reach common code that might change the target stack while
> SIGCHLD is unblocked? If it's just for the new assert, adding
> the assert may not be worth the trouble.
>
Well, the assert helped me a lot of times, I'd like to
leave it in. I've just removed it's dependency on the
target_can_async_p call, so it's always safe now.
> > /* FIXME: We should probably accept a list of process id's, and
> > attach all of them. */
> > @@ -994,9 +1212,36 @@ linux_nat_attach (char *args, int from_t
> >
> > lp->stopped = 1;
> >
> > - /* Fake the SIGSTOP that core GDB expects. */
> > - lp->status = W_STOPCODE (SIGSTOP);
> > - lp->resumed = 1;
> > + if (target_can_async_p ())
> > + /* This needs to before the above, so the current target is
> > + already set when the user didn't specify an exec file, and the
> > + async SIGCHLD handler doesn't mess with the waitpid calls
> > + above. */
> > + linux_nat_enable_async ();
>
> "This needs to before the above" has wrong grammar, and I don't know
> what it means; do you mean after?
>
Ah, stalled comments. Would be "to go after the above". The current
patch has this much better abstracted, so the comment is gone.
> > @@ -1199,6 +1456,13 @@ linux_nat_resume (ptid_t ptid, int step,
> > "LLR: Short circuiting for status 0x%x\n",
> > lp->status);
> >
> > + if (target_can_async_p () && linux_nat_async_enabled)
> > + {
> > + target_async (inferior_event_handler, 0);
> > +
> > + /* Wake event loop with special token, to get to WFI. */
> > + linux_nat_event_pipe_push (-1, -1, -1);
> > + }
> > return;
> > }
>
> If target_can_async_p, doesn't that imply linux_nat_async_enabled?
In that version it didn't. target_can_async_p needed to return true,
even before async mode was really kicked in. That's because the
fork_child path is still synchronous. We need to make that async,
probably by inventing a new stop_reason (similarly to how we do with
attaching). I was only enabling async in post_create_inferior,
so I needed those checks. This version of the patch does it the other
way around. It enables async in linux_nat_create_inferior,
but immediatelly masks it. This way, I don't need those
linux_nat_async_enabled spread in several places.
Another change, is that I've found that an exception would unmask
the SIGCHLD blocking if an error was thrown while we start the
inferior. I've fixed this by blocking SIGCHLD very early, in
_initialize_linux_nat.
I've also added comments to those state variables, and a few other
places.
>
> > if (options & __WCLONE)
> > - sigsuspend (&suspend_mask);
> > + {
> > + if (target_can_async_p () && linux_nat_async_enabled)
> > + {
> > + /* 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;
>
> If we just return from here, we miss clear_sigio_trap and
> clear_sigint_trap. But we keep calling set_sigint_trap on each
> entrance. I think we'll eventually end up with them saving and
> restoring the wrong signal handlers.
>
Fixed.
> > +/* target_can_async_p implementation. */
> > +
> > +static int
> > +linux_nat_can_async_p (void)
> > +{
> > + /* NOTE: palves 2008-03-11: We're only async when the user requests
> > + it explicitly with the "set linux-async" command. Someday, linux
> > + will always be async. */
> > + if (!linux_nat_async_permitted)
> > + return 0;
> > +
> > + /* See target.h/target_async_mask. */
> > + return linux_nat_async_mask_value;
> > +}
>
> I hope we can get rid of target_async_mask someday. Would just a
> wait call suffice? if (target_executing) reenter the event
> loop or something like that?
>
I had talked about it with Vlad a bit, but I'm afraid someone will
have to think about it some more. The infcall.c code that needs
asynching is marked with a funny "SNIP - SNIP - SNIP - " for
everyone to see. :-)
> > +/* Disable async mode. */
> > +
> > +static void
> > +linux_nat_disable_async (void)
> > +{
> > + /* Unregister from event loop. Don't go through the target stack
> > + (target_async), as linux-nat may have been poped off already. */
>
> popped
>
I've removed this call from linux_nat_{enabled|disable} async, so the
comment is gone. But thanks, I was making that error a lot. :-)
> > + linux_nat_async (NULL, 0);
> > +
> > + linux_nat_async_enabled = 0;
> > +
> > + sigaction (SIGCHLD, &async_old_action, NULL);
> > +
> > + /* Restore the original signal mask. */
> > + sigprocmask (SIG_SETMASK, &normal_mask, NULL);
> > + sigemptyset (&blocked_mask);
> > +
> > + clear_cached_waitpid_queue ();
> > + linux_nat_async_enabled = 0;
> > + linux_nat_num_queued_events = 0;
>
> Sets linux_nat_async_enabled twice.
>
Fixed.
Nick, I've also removed that extra unneeded line you pointed out.
New patch attached. No regressions in async mode other than
the same defines.exp regressions, and no regressions when async
mode is disabled. --- tested on x86-64-unknown-linux-gnu.
--
Pedro Alves
[-- Attachment #2: linux_async.diff --]
[-- Type: text/x-diff, Size: 48505 bytes --]
2008-03-17 Pedro Alves <pedro@codesourcery.com>
* target.h (struct target_ops): Delete to_async_mask_value and add
to_async_mask.
(target_is_async_p, target_async): Formatting.
(target_async_mask_value): Delete.
(target_async_mask): Delete function declaration, and add new
target macro with the same name.
* target.c (update_current_target): Replace to_async_mask_value by
to_async_mask. Default to_async_mask to return_one.
(target_async_mask): Delete.
(find_default_can_async_p, find_default_is_async_p): New.
(init_dummy_target): register find_default_can_async_p and
find_default_is_async_p on the dummy target.
* linux-nat.h (re_check_for_thread_db): Declare.
* linux-nat.c: Include inf-loop.h, event-loop.h and event-top.h.
(debug_linux_nat_async): New global.
(show_debug_linux_nat_async): New function.
(linux_async_permitted, linux_async_permitted_1): New globals.
(set_maintenance_linux_async_permitted)
(show_maintenance_linux_async_permitted): New functions.
(linux_nat_async_enabled, linux_nat_async_mask_value)
(linux_nat_event_pipe, linux_nat_num_queued_events)
(linux_nat_async_events_enabled): New globals.
(struct waitpid_result): New struct.
(waitpid_queue): New global.
(queued_waitpid, push_waitpid, clear_waitpid_queue): New.
(my_waitpid): Call queued_waitpid.
(linux_child_follow_fork): Mask async events during the call.
Call linux_nat_detach_inferior instead of target_detach. Call
re_check_for_thread_db instead of check_for_thread_db.
(sync_sigchld_action, async_sigchld_action): New globals.
(lin_lwp_attach_lwp): In sync mode, don't reblock SIGCHLD. In
async mode, block events during the call.
(linux_nat_create_inferior): New.
(linux_nat_attach): Implement async attaching.
(linux_nat_detach): Disable async mode if it is enabled.
(linux_nat_detach_inferior): New, refactored from
linux_nat_detach.
(linux_nat_detach): Call linux_nat_detach_inferior, and disable
async mode.
(linux_nat_resume): If in async mode, mask async events during the
call. If short circuiting, force event loop wake up. If
resuming, set target_executing, and register target events in the
event loop.
(pipe_to_local_event_queue, local_event_queue_to_pipe): New.
(linux_nat_wait): In async mode, block events during the call.
Get events from local waitpid queue. If no interesting events was
found, return to events loop. Reregister target events in the
event loop on exit. In sync mode, no need to reblock SIGCHLD.
(linux_nat_kill): Disable events on entry.
(linux_nat_mourn_inferior): Disable async mode if there are no
more forks available, otherwise leave it on.
(sigchld_handler): Assure this is called only in sync mode.
(linux_nat_is_async_p, linux_nat_can_async_p)
(linux_nat_async_mask): New.
(linux_nat_event_pipe_pop, linux_nat_event_pipe_push): New.
(get_pending_events, async_sigchld_handler): New.
(linux_nat_async_events): New.
(async_terminal_is_ours): New global.
(linux_nat_terminal_inferior, linux_nat_terminal_ours): New.
(async_client_callback, async_client_context): New.
(linux_nat_async_file_handler, linux_nat_async)
(linux_nat_disable_async, linux_nat_enable_async): New.
(linux_nat_add_target): Register linux_nat_create_inferior,
linux_nat_can_async_p, linux_nat_is_async_p, linux_nat_async,
linux_nat_async_mask, linux_nat_terminal_inferior and
linux_nat_terminal_ours.
(_initialize_linux_nat): Remove local action variable, and update
code that used it to use sync_sigchld_action. Add new
"lin-lwp-async" debug set/show command. Add new "linux-async"
maintenance set/show command. Block SIGCHLD by default. Setup
async_sichld_action, and sync_sigchld_action.
* linux-thread-db.c (re_check_for_thread_db): New.
(clear_lwpid_callback): Handle TARGET_WAITKIND_IGNORE.
(thread_db_can_async_p, thread_db_is_async_p, thread_db_async)
(thread_db_async_mask): New.
(init_thread_db_ops): Register thread_db_can_async_p,
thread_db_is_async_p, thread_db_async and thread_db_async_mask.
* remote.c (remote_async_mask_value): New.
(remote_return_zero): New.
(init_remote_ops): Register remote_return_zero as callbacks of
to_can_async_p and to_is_async_p.
(remote_can_async_p, remote_is_async_p, remote_async): Update to
use remote_async_mask_value.
(remote_async_mask): New.
(init_remote_async_ops): Remove to_async_mask_value setting and
register remote_async_mask as to_async_mask callback in
remote_async_ops.
* Makefile.in (linux-nat.o): Update.
doc/
* gdb.texinfo (Debugging Output): Document "set/show debug lin-lwp".
(Maintenance Commands): Document "maint set/show linux-async".
---
gdb/Makefile.in | 2
gdb/doc/gdb.texinfo | 16
gdb/linux-nat.c | 880 +++++++++++++++++++++++++++++++++++++++++++++++---
gdb/linux-nat.h | 4
gdb/linux-thread-db.c | 39 ++
gdb/remote.c | 26 +
gdb/target.c | 37 +-
gdb/target.h | 15
8 files changed, 947 insertions(+), 72 deletions(-)
Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in 2008-03-17 15:54:44.000000000 +0000
+++ src/gdb/Makefile.in 2008-03-17 15:55:51.000000000 +0000
@@ -2351,7 +2351,7 @@ linux-nat.o: linux-nat.c $(defs_h) $(inf
$(gdb_wait_h) $(gdb_assert_h) $(linux_nat_h) $(gdbthread_h) \
$(gdbcmd_h) $(regcache_h) $(regset_h) $(inf_ptrace_h) $(auxv_h) \
$(elf_bfd_h) $(gregset_h) $(gdbcore_h) $(gdbthread_h) $(gdb_stat_h) \
- $(linux_fork_h)
+ $(linux_fork_h) $(inf_loop_h) $(event_loop_h) $(event_top_h)
linux-thread-db.o: linux-thread-db.c $(defs_h) $(gdb_assert_h) \
$(gdb_proc_service_h) $(gdb_thread_db_h) $(bfd_h) $(exceptions_h) \
$(gdbthread_h) $(inferior_h) $(symfile_h) $(objfiles_h) $(target_h) \
Index: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c 2008-03-17 15:54:44.000000000 +0000
+++ src/gdb/linux-nat.c 2008-03-17 16:03:49.000000000 +0000
@@ -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,44 @@ show_debug_linux_nat (struct ui_file *fi
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);
+}
+
+
+/* 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;
+}
+
+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);
+}
+
static int linux_parent_pid;
struct simple_pid_list
@@ -133,6 +174,165 @@ static int linux_supports_tracefork_flag
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_enable_async (void);
+static void linux_nat_disable_async (void);
+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 void linux_nat_detach_inferior (char *args, int from_tty);
+
+/* 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;
+}
+
+static void
+clear_waitpid_queue (void)
+{
+ struct waitpid_result *event = waitpid_queue;
+ while (event)
+ {
+ struct waitpid_result *next = event->next;
+ xfree (event);
+ event = next;
+ }
+ waitpid_queue = NULL;
+}
+
\f
/* Trivial list manipulation functions to keep track of a list of
new stopped processes. */
@@ -183,12 +383,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);
@@ -361,6 +570,10 @@ linux_child_follow_fork (struct target_o
struct target_waitstatus last_status;
int has_vforked;
int parent_pid, child_pid;
+ int async_events_were_enabled = 0;
+
+ if (target_can_async_p ())
+ async_events_were_enabled = linux_nat_async_events (0);
get_last_target_status (&last_ptid, &last_status);
has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
@@ -506,9 +719,7 @@ linux_child_follow_fork (struct target_o
fork_save_infrun_state (fp, 0);
}
else
- {
- target_detach (NULL, 0);
- }
+ linux_nat_detach_inferior (NULL, 0);
inferior_ptid = ptid_build (child_pid, child_pid, 0);
@@ -517,12 +728,15 @@ linux_child_follow_fork (struct target_o
push_target (ops);
linux_nat_switch_fork (inferior_ptid);
- check_for_thread_db ();
+ re_check_for_thread_db ();
/* Reset breakpoints in the child as appropriate. */
follow_inferior_reset_breakpoints ();
}
+ if (async_events_were_enabled)
+ linux_nat_async_events (1);
+
return 0;
}
@@ -616,8 +830,14 @@ static sigset_t normal_mask;
_initialize_linux_nat. */
static sigset_t suspend_mask;
+/* SIGCHLD action for synchronous mode. */
+struct sigaction sync_sigchld_action;
+
/* Signals to block to make that sigsuspend work. */
static sigset_t blocked_mask;
+
+/* SIGCHLD action for asynchronous mode. */
+static struct sigaction async_sigchld_action;
\f
/* Prototypes for local functions. */
@@ -874,16 +1094,11 @@ 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);
@@ -956,10 +1171,34 @@ 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 ())
+ {
+ linux_nat_enable_async ();
+ saved_async = linux_nat_async_mask (0);
+ }
+
+ linux_ops->to_create_inferior (exec_file, allargs, env, from_tty);
+
+ if (saved_async)
+ saved_async = linux_nat_async_mask (saved_async);
+}
+
+static void
linux_nat_attach (char *args, int from_tty)
{
struct lwp_info *lp;
@@ -971,6 +1210,12 @@ linux_nat_attach (char *args, int from_t
attach all of them. */
linux_ops->to_attach (args, from_tty);
+ if (target_can_async_p ())
+ {
+ linux_nat_enable_async ();
+ linux_nat_async (NULL, 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. */
@@ -998,9 +1243,14 @@ linux_nat_attach (char *args, int from_t
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);
}
}
@@ -1063,8 +1313,10 @@ detach_callback (struct lwp_info *lp, vo
return 0;
}
+/* Detaches inferior_ptid, but doesn't end the debugging session. */
+
static void
-linux_nat_detach (char *args, int from_tty)
+linux_nat_detach_inferior (char *args, int from_tty)
{
iterate_over_lwps (detach_callback, NULL);
@@ -1076,14 +1328,30 @@ linux_nat_detach (char *args, int from_t
/* 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));
linux_ops->to_detach (args, from_tty);
}
+/* Detaches inferior_ptid, and ends the debugging session. */
+
+static void
+linux_nat_detach (char *args, int from_tty)
+{
+ linux_nat_detach_inferior (args, from_tty);
+
+ if (target_can_async_p ())
+ {
+ linux_nat_async (NULL, 0);
+ linux_nat_disable_async ();
+ }
+ else
+ {
+ /* Restore the original signal mask. */
+ sigprocmask (SIG_SETMASK, &normal_mask, NULL);
+ sigemptyset (&blocked_mask);
+ }
+}
+
/* Resume LP. */
static int
@@ -1135,6 +1403,10 @@ linux_nat_resume (ptid_t ptid, int step,
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);
@@ -1199,6 +1471,13 @@ linux_nat_resume (ptid_t ptid, int step,
"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;
}
@@ -1218,6 +1497,12 @@ linux_nat_resume (ptid_t ptid, int step,
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. */
@@ -2069,6 +2354,57 @@ linux_nat_filter_event (int lwpid, int s
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)
{
@@ -2078,6 +2414,9 @@ linux_nat_wait (ptid_t ptid, struct targ
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. */
@@ -2093,12 +2432,9 @@ linux_nat_wait (ptid_t ptid, struct targ
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:
@@ -2122,7 +2458,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;
@@ -2189,7 +2525,13 @@ retry:
{
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);
@@ -2217,9 +2559,30 @@ 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");
+
+ clear_sigio_trap ();
+ clear_sigint_trap ();
+
+ return minus_one_ptid;
+ }
+
+ sigsuspend (&suspend_mask);
+ }
}
/* We shouldn't end up here unless we want to try again. */
@@ -2324,6 +2687,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;
}
@@ -2403,6 +2773,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. */
@@ -2438,13 +2811,24 @@ 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_nat_disable_async ();
+ }
+ else
+ {
+ /* Restore the original signal mask. */
+ sigprocmask (SIG_SETMASK, &normal_mask, NULL);
+ sigemptyset (&blocked_mask);
+ }
+
+ 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
@@ -2512,6 +2896,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. */
@@ -3270,6 +3661,354 @@ linux_trad_target (CORE_ADDR (*register_
return t;
}
+/* target_is_async_p implementation. */
+
+static int
+linux_nat_is_async_p (void)
+{
+ /* NOTE: palves 2008-03-17: 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-17: 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;
+
+ /* While in masked async, we may have not collected all the
+ pending events. Get them out now. */
+ get_pending_events ();
+ local_event_queue_to_pipe ();
+ 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;
+}
+
+/* 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;
+
+ 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;
+}
+
+/* Disable async mode. */
+
+static void
+linux_nat_disable_async (void)
+{
+ if (linux_nat_async_enabled)
+ {
+ sigaction (SIGCHLD, &sync_sigchld_action, NULL);
+
+ /* Restore the original signal mask. */
+ sigprocmask (SIG_SETMASK, &normal_mask, NULL);
+ sigemptyset (&blocked_mask);
+
+ clear_waitpid_queue ();
+
+ 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 = 0;
+ }
+}
+
+/* Enable async mode. */
+
+static void
+linux_nat_enable_async (void)
+{
+ if (!linux_nat_async_enabled)
+ {
+ 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);
+
+ linux_nat_async_enabled = 1;
+ }
+}
+
void
linux_nat_add_target (struct target_ops *t)
{
@@ -3281,6 +4020,7 @@ linux_nat_add_target (struct target_ops
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;
@@ -3292,6 +4032,13 @@ linux_nat_add_target (struct target_ops
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
@@ -3329,7 +4076,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\
@@ -3340,27 +4087,62 @@ Specify any of the following keywords fo
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", 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);
+
+ add_setshow_zinteger_cmd ("lin-lwp-async", no_class,
+ &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);
+ /* Signals to block in adition to SIGCHLD. */
sigemptyset (&blocked_mask);
- 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);
+ /* 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;
}
\f
Index: src/gdb/linux-thread-db.c
===================================================================
--- src.orig/gdb/linux-thread-db.c 2008-03-17 15:54:44.000000000 +0000
+++ src/gdb/linux-thread-db.c 2008-03-17 15:55:51.000000000 +0000
@@ -657,6 +657,13 @@ check_for_thread_db (void)
}
}
+void
+re_check_for_thread_db (void)
+{
+ using_thread_db = 0;
+ check_for_thread_db ();
+}
+
static void
thread_db_new_objfile (struct objfile *objfile)
{
@@ -870,6 +877,9 @@ thread_db_wait (ptid_t ptid, struct targ
ptid = target_beneath->to_wait (ptid, ourstatus);
+ if (ourstatus->kind == TARGET_WAITKIND_IGNORE)
+ return ptid;
+
if (ourstatus->kind == TARGET_WAITKIND_EXITED
|| ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
return pid_to_ptid (-1);
@@ -940,6 +950,31 @@ thread_db_mourn_inferior (void)
}
static int
+thread_db_can_async_p (void)
+{
+ return target_beneath->to_can_async_p ();
+}
+
+static int
+thread_db_is_async_p (void)
+{
+ return target_beneath->to_is_async_p ();
+}
+
+static void
+thread_db_async (void (*callback) (enum inferior_event_type event_type,
+ void *context), void *context)
+{
+ return target_beneath->to_async (callback, context);
+}
+
+static int
+thread_db_async_mask (int mask)
+{
+ return target_beneath->to_async_mask (mask);
+}
+
+static int
find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
{
td_thrinfo_t ti;
@@ -1133,6 +1168,10 @@ init_thread_db_ops (void)
thread_db_ops.to_get_thread_local_address
= thread_db_get_thread_local_address;
thread_db_ops.to_extra_thread_info = thread_db_extra_thread_info;
+ thread_db_ops.to_can_async_p = thread_db_can_async_p;
+ thread_db_ops.to_is_async_p = thread_db_is_async_p;
+ thread_db_ops.to_async = thread_db_async;
+ thread_db_ops.to_async_mask = thread_db_async_mask;
thread_db_ops.to_magic = OPS_MAGIC;
}
Index: src/gdb/remote.c
===================================================================
--- src.orig/gdb/remote.c 2008-03-17 15:54:44.000000000 +0000
+++ src/gdb/remote.c 2008-03-17 15:55:51.000000000 +0000
@@ -481,6 +481,8 @@ static struct target_ops extended_remote
extended_remote_ops, but with asynchronous support. */
static struct target_ops remote_async_ops;
+static int remote_async_mask_value = 1;
+
static struct target_ops extended_async_remote_ops;
/* FIXME: cagney/1999-09-23: Even though getpkt was called with
@@ -7176,6 +7178,12 @@ remote_command (char *args, int from_tty
help_list (remote_cmdlist, "remote ", -1, gdb_stdout);
}
+static int
+remote_return_zero (void)
+{
+ return 0;
+}
+
static void
init_remote_ops (void)
{
@@ -7229,6 +7237,8 @@ Specify the serial device it is connecte
remote_ops.to_flash_erase = remote_flash_erase;
remote_ops.to_flash_done = remote_flash_done;
remote_ops.to_read_description = remote_read_description;
+ remote_ops.to_can_async_p = remote_return_zero;
+ remote_ops.to_is_async_p = remote_return_zero;
}
/* Set up the extended remote vector by making a copy of the standard
@@ -7256,14 +7266,14 @@ static int
remote_can_async_p (void)
{
/* We're async whenever the serial device is. */
- return (current_target.to_async_mask_value) && serial_can_async_p (remote_desc);
+ return remote_async_mask_value && serial_can_async_p (remote_desc);
}
static int
remote_is_async_p (void)
{
/* We're async whenever the serial device is. */
- return (current_target.to_async_mask_value) && serial_is_async_p (remote_desc);
+ return remote_async_mask_value && serial_is_async_p (remote_desc);
}
/* Pass the SERIAL event on and up to the client. One day this code
@@ -7287,7 +7297,7 @@ static void
remote_async (void (*callback) (enum inferior_event_type event_type,
void *context), void *context)
{
- if (current_target.to_async_mask_value == 0)
+ if (remote_async_mask_value == 0)
internal_error (__FILE__, __LINE__,
_("Calling remote_async when async is masked"));
@@ -7301,6 +7311,14 @@ remote_async (void (*callback) (enum inf
serial_async (remote_desc, NULL, NULL);
}
+static int
+remote_async_mask (int new_mask)
+{
+ int curr_mask = remote_async_mask_value;
+ remote_async_mask_value = new_mask;
+ return curr_mask;
+}
+
/* Target async and target extended-async.
This are temporary targets, until it is all tested. Eventually
@@ -7360,7 +7378,7 @@ Specify the serial device it is connecte
remote_async_ops.to_can_async_p = remote_can_async_p;
remote_async_ops.to_is_async_p = remote_is_async_p;
remote_async_ops.to_async = remote_async;
- remote_async_ops.to_async_mask_value = 1;
+ remote_async_ops.to_async_mask = remote_async_mask;
remote_async_ops.to_magic = OPS_MAGIC;
remote_async_ops.to_memory_map = remote_memory_map;
remote_async_ops.to_flash_erase = remote_flash_erase;
Index: src/gdb/target.c
===================================================================
--- src.orig/gdb/target.c 2008-03-17 15:54:44.000000000 +0000
+++ src/gdb/target.c 2008-03-17 15:55:51.000000000 +0000
@@ -464,7 +464,7 @@ update_current_target (void)
INHERIT (to_can_async_p, t);
INHERIT (to_is_async_p, t);
INHERIT (to_async, t);
- INHERIT (to_async_mask_value, t);
+ INHERIT (to_async_mask, t);
INHERIT (to_find_memory_regions, t);
INHERIT (to_make_corefile_notes, t);
INHERIT (to_get_thread_local_address, t);
@@ -637,6 +637,9 @@ update_current_target (void)
de_fault (to_async,
(void (*) (void (*) (enum inferior_event_type, void*), void*))
tcomplain);
+ de_fault (to_async_mask,
+ (int (*) (int))
+ return_one);
current_target.to_read_description = NULL;
#undef de_fault
@@ -1694,14 +1697,6 @@ target_disconnect (char *args, int from_
tcomplain ();
}
-int
-target_async_mask (int mask)
-{
- int saved_async_masked_status = target_async_mask_value;
- target_async_mask_value = mask;
- return saved_async_masked_status;
-}
-
/* Look through the list of possible targets for a target that can
follow forks. */
@@ -1835,6 +1830,28 @@ find_default_create_inferior (char *exec
return;
}
+int
+find_default_can_async_p (void)
+{
+ struct target_ops *t;
+
+ t = find_default_run_target ("async");
+ if (t->to_can_async_p)
+ return (t->to_can_async_p) ();
+ return 0;
+}
+
+int
+find_default_is_async_p (void)
+{
+ struct target_ops *t;
+
+ t = find_default_run_target ("async");
+ if (t->to_is_async_p)
+ return (t->to_is_async_p) ();
+ return 0;
+}
+
static int
default_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
{
@@ -2099,6 +2116,8 @@ init_dummy_target (void)
dummy_target.to_doc = "";
dummy_target.to_attach = find_default_attach;
dummy_target.to_create_inferior = find_default_create_inferior;
+ dummy_target.to_can_async_p = find_default_can_async_p;
+ dummy_target.to_is_async_p = find_default_is_async_p;
dummy_target.to_pid_to_str = normal_pid_to_str;
dummy_target.to_stratum = dummy_stratum;
dummy_target.to_find_memory_regions = dummy_find_memory_regions;
Index: src/gdb/target.h
===================================================================
--- src.orig/gdb/target.h 2008-03-17 15:54:44.000000000 +0000
+++ src/gdb/target.h 2008-03-17 15:55:51.000000000 +0000
@@ -414,9 +414,8 @@ struct target_ops
/* ASYNC target controls */
int (*to_can_async_p) (void);
int (*to_is_async_p) (void);
- void (*to_async) (void (*cb) (enum inferior_event_type, void *context),
- void *context);
- int to_async_mask_value;
+ void (*to_async) (void (*) (enum inferior_event_type, void *), void *);
+ int (*to_async_mask) (int);
int (*to_find_memory_regions) (int (*) (CORE_ADDR,
unsigned long,
int, int, int,
@@ -938,11 +937,11 @@ int target_follow_fork (int follow_child
#define target_can_async_p() (current_target.to_can_async_p ())
/* Is the target in asynchronous execution mode? */
-#define target_is_async_p() (current_target.to_is_async_p())
+#define target_is_async_p() (current_target.to_is_async_p ())
/* Put the target in async mode with the specified callback function. */
#define target_async(CALLBACK,CONTEXT) \
- (current_target.to_async((CALLBACK), (CONTEXT)))
+ (current_target.to_async ((CALLBACK), (CONTEXT)))
/* This is to be used ONLY within call_function_by_hand(). It provides
a workaround, to have inferior function calls done in sychronous
@@ -958,10 +957,8 @@ int target_follow_fork (int follow_child
the turning async on and off to the single execution commands,
from where it is done currently, in remote_resume(). */
-#define target_async_mask_value \
- (current_target.to_async_mask_value)
-
-extern int target_async_mask (int mask);
+#define target_async_mask(MASK) \
+ (current_target.to_async_mask (MASK))
/* Converts a process id to a string. Usually, the string just contains
`process xyz', but on some systems it may contain
Index: src/gdb/linux-nat.h
===================================================================
--- src.orig/gdb/linux-nat.h 2008-03-17 15:54:44.000000000 +0000
+++ src/gdb/linux-nat.h 2008-03-17 15:55:51.000000000 +0000
@@ -86,6 +86,10 @@ extern struct lwp_info *lwp_list;
/* Attempt to initialize libthread_db. */
void check_for_thread_db (void);
+/* Re-attempt to initialize libthread_db, even if has already been
+ tried before. */
+void re_check_for_thread_db (void);
+
/* Tell the thread_db layer what native target operations to use. */
void thread_db_init (struct target_ops *);
Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo 2008-03-17 15:54:44.000000000 +0000
+++ src/gdb/doc/gdb.texinfo 2008-03-17 15:55:51.000000000 +0000
@@ -16405,6 +16405,12 @@ Displays the current state of @value{GDB
Turns on or off debugging messages from the Linux LWP debug support.
@item show debug lin-lwp
Show the current state of Linux LWP debugging messages.
+@item set debug lin-lwp-async
+@cindex @sc{gnu}/Linux LWP async debug messages
+@cindex Linux lightweight processes
+Turns on or off debugging messages from the Linux LWP async debug support.
+@item show debug lin-lwp-async
+Show the current state of Linux LWP async debugging messages.
@item set debug observer
@cindex observer debugging info
Turns on or off display of @value{GDBN} observer debugging. This
@@ -23240,6 +23246,16 @@ data in a @file{gmon.out} file, be sure
Configuring with @samp{--enable-profiling} arranges for @value{GDBN} to be
compiled with the @samp{-pg} compiler option.
+@kindex maint set linux-async
+@kindex maint show linux-async
+@cindex asynchronous support
+@item maint set linux-async
+@itemx maint show linux-async
+Control the GNU/Linux native asynchronous support of @value{GDBN}.
+
+GNU/Linux native asynchronous support will be disabled until you use
+the @samp{maint set linux-async} command to enable it.
+
@kindex maint show-debug-regs
@cindex x86 hardware debug registers
@item maint show-debug-regs
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-17 16:05 ` Pedro Alves
@ 2008-03-17 22:05 ` Daniel Jacobowitz
2008-03-18 23:27 ` Pedro Alves
2008-03-18 0:06 ` Nick Roberts
2008-03-18 2:47 ` Nick Roberts
2 siblings, 1 reply; 37+ messages in thread
From: Daniel Jacobowitz @ 2008-03-17 22:05 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
On Mon, Mar 17, 2008 at 04:05:24PM +0000, Pedro Alves wrote:
> > > @@ -516,6 +707,12 @@ linux_child_follow_fork (struct target_o
> > > target_detach (which does other necessary cleanup). */
> > >
> > > push_target (ops);
> > > +
> > > + if (async_was_enabled && !linux_nat_async_enabled)
> > > + /* target_detach may disable async depending on multi-threaded
> > > + enabled or not. Reenable it if needed. */
> > > + linux_nat_enable_async ();
> > > +
> > > linux_nat_switch_fork (inferior_ptid);
> > > check_for_thread_db ();
> >
> > This will drain the queue, losing any pending events. In non-stop
> > mode I can imagine that causing trouble. Maybe we need to avoid
> > target_detach now.
> >
>
> Yeah. Here's how I fixed it: target_detach, when the target is
> multi-threaded, would call thread_db_detach, which calls
> target_mourn_inferior. The mourn inferior call (although most targets
> don't mourn on detach), is there AFAICT mostly to reset
> using_thread_db. There's nothing in linux_nat_mourn_inferior that
> we'd miss. Notice that linux_nat_detach doesn't mourn, so a
> single-threaded inferior doesn't get mourned.
>
> So, I split linux_nat_detach in two (linux_nat_detach_inferior, and
> linux_nat_detach proper which calls linux_nat_detach_inferior), and
> added a re_check_for_thread_db, to be used after following the child,
> so thread_db knows about the new current inferior.
I don't like this :-( There's more that thread_db_attach does. If we
don't disable thread event reporting, then it's likely to stop and
wait for the debugger the next time it creates a thread. Yes, this
was already broken when following a vfork. And it may be broken if
the child creates threads when we follow the parent of a fork. But
if we can avoid breaking it further, that would be nice.
Do we really have to disable async when detaching? Maybe we could do
so only at attach and create, if it doesn't matter how async is set
the rest of the time. Remove the normal_mask / blocked_mask bits
from linux_nat_disable_async and call it in else blocks after the
two calls to linux_nat_enable_async. What do you think?
Other than this one bit, the new patch looks good to me.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-15 1:58 ` Pedro Alves
2008-03-15 3:11 ` Daniel Jacobowitz
@ 2008-03-17 23:41 ` Nick Roberts
1 sibling, 0 replies; 37+ messages in thread
From: Nick Roberts @ 2008-03-17 23:41 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
> > Daniel talks about async mode being the default but I don't really
> > understand this. In order for Gdb to be able to accept input while the
> > inferior is running and possibly accepting it's own input, seems to require
> > separate terminals. This isn't a problem for a front end but, in general,
> > will be when Gdb is run from the command line.
>
> This is a confusion of terms. The async mode he was refering to, is
> the event loop code path, in which we (ideally) never block in
> wait_for_inferior.
Maybe it will be useful in non-stop mode but it seems to me that there will
always be a problem when the inferior tries to read from/write to the terminal.
In any case, async mode is immediately useful for a frontend so, to begin with,
it might be a good idea to turn it on by default just for MI.
--
Nick http://www.inet.net.nz/~nickrob
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-17 16:05 ` Pedro Alves
2008-03-17 22:05 ` Daniel Jacobowitz
@ 2008-03-18 0:06 ` Nick Roberts
2008-03-18 23:28 ` Pedro Alves
2008-03-21 15:49 ` Daniel Jacobowitz
2008-03-18 2:47 ` Nick Roberts
2 siblings, 2 replies; 37+ messages in thread
From: Nick Roberts @ 2008-03-18 0:06 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
> New patch attached. No regressions in async mode other than
> the same defines.exp regressions, and no regressions when async
> mode is disabled. --- tested on x86-64-unknown-linux-gnu.
I've tested it (evidently on i686-linux-gnu) with
set GDBFLAGS "-ex \"maint set linux-async on\""
in site.exp, ignoring commands.exp and just got a few extra exceptions:
FAIL: gdb.mi/mi-var-child-f.exp: mi runto MAIN__ (timeout)
FAIL: gdb.mi/mi-var-child-f.exp: create local variable array
FAIL: gdb.mi/mi2-simplerun.exp: continue to end (1)
FAIL: gdb.threads/pthreads.exp: check backtrace from main thread
Are you going to add any new tests?
My original test file mi-async.exp (attached below) works with your patch when
GDBFLAGS is toggled as above. It would be be good to have a test for
-exec-interrupt too.
--
Nick http://www.inet.net.nz/~nickrob
---------------------------------
# Copyright 2008 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Test asynchronous Machine Interface (MI) operations
#
load_lib mi-support.exp
set saved_gdbflags $GDBFLAGS
set GDBFLAGS "-ex \"maint set linux-async on\""
gdb_exit
if [mi_gdb_start] {
continue
}
set testfile "basics"
set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile}
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DFAKEARGV}] != "" } {
untested mi-async.exp
return -1
}
mi_delete_breakpoints
mi_gdb_reinitialize_dir $srcdir/$subdir
mi_gdb_load ${binfile}
# mi_gdb_test cannot be used for asynchronous commands because there are
# two prompts involved and this can lead to a race condition.
proc linux_async_tests {} {
global mi_gdb_prompt
global hex
set line_main_head [gdb_get_line_number "main ("]
set line_main_body [expr $line_main_head + 2]
set line_main_next [expr $line_main_head + 3]
send_gdb "start\n"
gdb_expect {
-re ".*\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
gdb_expect {
-re "\\*stopped,thread-id=\"0\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_body\"\}\r\n$mi_gdb_prompt$" {
pass "Asynchronous response after start command"
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after start command (2)"
}
timeout {
fail "Asynchronous response after start command (timeout 2)"
}
}
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after start command (1)"
}
timeout {fail "Asynchronous response after start command (timeout 1)"}
}
send_gdb "next\n"
gdb_expect {
-re "\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
gdb_expect {
-re "\\*stopped,reason=\"end-stepping-range\",thread-id=\"0\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_next\"\}\r\n$mi_gdb_prompt$" {
pass "Asynchronous response after next command"
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after next command (2)"
}
timeout {
fail "Asynchronous response after next command (timeout 2)"
}
}
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after next command (1)"
}
timeout {fail "Asynchronous response after next command (timeout 1)"}
}
send_gdb "start\n"
gdb_expect {
-re ".*\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
gdb_expect {
-re "\\*stopped,thread-id=\"0\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_body\"\}\r\n$mi_gdb_prompt$" {
pass "Asynchronous response after (re) start"
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after (re) start (2)"
}
timeout {
fail "Asynchronous response after (re) start (timeout 2)"
}
}
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after (re) start (1)"
}
timeout {fail "Asynchronous response after (re) start (timeout 1)"}
}
}
#if [istarget "i386-*-linux-gnu"] then {
linux_async_tests
#}
mi_gdb_exit
set GDBFLAGS $saved_gdbflags
return 0
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-17 16:05 ` Pedro Alves
2008-03-17 22:05 ` Daniel Jacobowitz
2008-03-18 0:06 ` Nick Roberts
@ 2008-03-18 2:47 ` Nick Roberts
2 siblings, 0 replies; 37+ messages in thread
From: Nick Roberts @ 2008-03-18 2:47 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
I've really gone through your patch to try to understand it, not review it
but you might as well have my thoughts.
> * linux-nat.h (re_check_for_thread_db): Declare.
recheck_for_thread_db (assuming it means "check again").
The word "re" on it's own is an abbreviation for "regarding" and
in GDB it's generally used for "regular expression".
>...
> +static void
> +clear_waitpid_queue (void)
> +{
> + struct waitpid_result *event = waitpid_queue;
> + while (event)
> + {
> + struct waitpid_result *next = event->next;
> + xfree (event);
> + event = next;
> + }
> + waitpid_queue = NULL;
> +}
struct waitpid_result *next, *event = waitpid_queue;
while (event)
{
next = event->next;
xfree (event);
event = next;
I guess the compiler will optimise it anyway but shouldn't
the declaration be outside the loop?
>...
> +/* Disable async mode. */
> +
> +static void
> +linux_nat_disable_async (void)
and
> +/* Enable async mode. */
> +
> +static void
> +linux_nat_enable_async (void)
Previously you've added linux_nat_async_events (int enable). So should
this be:
linux_nat_toggle_async (int enable)
for consistency? (or linux_nat_async_events -> linux_nat_async_disable_events
linux_nat_async_enable_events)
As a rule what does GDB do elsewhere in these situations?
>...
> + add_setshow_zinteger_cmd ("lin-lwp-async", no_class,
> + &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 ("lin-lwp-async", class_maintenance,
I guess you copied from "lin-lwp" but I think this should be
boolean too (only a few currently are: timestamp and xml)
The other "set debug" commands are also in class_maintenance, although
I'm not sure what effect that has here.
--
Nick http://www.inet.net.nz/~nickrob
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-17 22:05 ` Daniel Jacobowitz
@ 2008-03-18 23:27 ` Pedro Alves
2008-03-18 23:58 ` Daniel Jacobowitz
2008-03-21 15:55 ` Daniel Jacobowitz
0 siblings, 2 replies; 37+ messages in thread
From: Pedro Alves @ 2008-03-18 23:27 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 5803 bytes --]
Answearing to both Daniel and Nick at the same time.
A Monday 17 March 2008 22:05:15, Daniel Jacobowitz wrote:
> >
> > So, I split linux_nat_detach in two (linux_nat_detach_inferior, and
> > linux_nat_detach proper which calls linux_nat_detach_inferior), and
> > added a re_check_for_thread_db, to be used after following the child,
> > so thread_db knows about the new current inferior.
>
> I don't like this :-( There's more that thread_db_attach does. If we
> don't disable thread event reporting, then it's likely to stop and
> wait for the debugger the next time it creates a thread. Yes, this
> was already broken when following a vfork. And it may be broken if
> the child creates threads when we follow the parent of a fork. But
> if we can avoid breaking it further, that would be nice.
>
Ok. Although we could do the rest from a forget_about_thread_db function,
after tinkering about several other possibilities (including
passing magic args to target_detach ((char*) -1, -1) :-), ...
> Do we really have to disable async when detaching? Maybe we could do
> so only at attach and create, if it doesn't matter how async is set
> the rest of the time. Remove the normal_mask / blocked_mask bits
> from linux_nat_disable_async and call it in else blocks after the
> two calls to linux_nat_enable_async. What do you think?
>
... I've settled for this. In doing that, I added a way to
drain the local event queue of all the pending events we
already waited on of lwp's we're detaching from, so they
don't linger around always waiking up the event loop. linux_nat_kill
already drains the events, in the non-multifork case. The multifork
case doesn't kill all lwp's explicitly, only the main processes,
so I drain the events in that case. When we get to mourn by the
normal exit path, by definition there should be no
pending events left.
The other change I made, was in linux_nat_terminal_{inferior|ours},
and linux_nat_wait, regarding the passing SIGINT to the inferior.
I noticed that interrupting the inferior with ^C wasn't working
in the attach case in the previous patches. To fix it, I call
set_siging_trap in linux_nat_terminal_inferior, and clear it in
linux_nat_terminal_ours, but only in the sync_execution case.
In linux_nat_wait, only do the SIGINT dance if in sync mode.
This is similar to what remote.c does, although remote.c uses
an async_signal_handler for that. I had made a patch that
moves that code from remote.c into event-loop.c for targets other
than remote to share the functionality/implementation, but ended
up not using it, because native unix targets don't need
the complexity. Windows will, though.
----
A Tuesday 18 March 2008 02:46:58, Nick Roberts wrote:
> I've really gone through your patch to try to understand it, not review it
> but you might as well have my thoughts.
>
> > * linux-nat.h (re_check_for_thread_db): Declare.
>
> recheck_for_thread_db (assuming it means "check again").
>
> The word "re" on it's own is an abbreviation for "regarding" and
> in GDB it's generally used for "regular expression".
>
I see. Well, Daniel was against that change, so that problem
is gone.
> >...
> > +static void
> > +clear_waitpid_queue (void)
> > +{
> > + struct waitpid_result *event = waitpid_queue;
> > + while (event)
> > + {
> > + struct waitpid_result *next = event->next;
> > + xfree (event);
> > + event = next;
> > + }
> > + waitpid_queue = NULL;
> > +}
>
> struct waitpid_result *next, *event = waitpid_queue;
> while (event)
> {
> next = event->next;
> xfree (event);
> event = next;
>
> I guess the compiler will optimise it anyway but shouldn't
> the declaration be outside the loop?
>
I don't understand why you find that an issue. That variable
is only used inside the while scope. Anyway, this function
can now replaced by a call to the new drain_queued_events.
> >...
> > +/* Disable async mode. */
> > +
> > +static void
> > +linux_nat_disable_async (void)
>
> and
>
> > +/* Enable async mode. */
> > +
> > +static void
> > +linux_nat_enable_async (void)
>
> Previously you've added linux_nat_async_events (int enable). So should
> this be:
>
> linux_nat_toggle_async (int enable)
>
> for consistency? (or linux_nat_async_events ->
> linux_nat_async_disable_events linux_nat_async_enable_events)
>
Indeed. See new patch. The form that takes an int is better suited
for the three cases introduced, so I kept that form. Thanks for
the suggestion, it looks nicer now.
> >...
> > + add_setshow_zinteger_cmd ("lin-lwp-async", no_class,
> > + &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 ("lin-lwp-async", class_maintenance,
>
> I guess you copied from "lin-lwp" but I think this should be
> boolean too (only a few currently are: timestamp and xml)
>
> The other "set debug" commands are also in class_maintenance, although
> I'm not sure what effect that has here.
I've moved them in the class_maintenance. I suspect that
the debug commands are integers not booleans so we can have
several debug levels. I count approximatelly the same amount of
boolean vs integer debug mode vars.
New patch attached. Again no regressions on x86_64-unknown-linux-gnu,
if async mode is off (default), and only regressions on define.exp (*)
if async is on, provided this patch is in:
http://sourceware.org/ml/gdb-patches/2008-03/msg00233.html
(fixes commands.exp)
(*) - again, not related to this patch, it's a general async problem.
--
Pedro Alves
[-- Attachment #2: linux_async.diff --]
[-- Type: text/x-diff, Size: 49188 bytes --]
2008-03-18 Pedro Alves <pedro@codesourcery.com>
gdb/
* target.h (struct target_ops): Delete to_async_mask_value and add
to_async_mask.
(target_is_async_p, target_async): Formatting.
(target_async_mask_value): Delete.
(target_async_mask): Delete function declaration, and add new
target macro with the same name.
* target.c (update_current_target): Replace to_async_mask_value by
to_async_mask. Default to_async_mask to return_one.
(target_async_mask): Delete.
(find_default_can_async_p, find_default_is_async_p): New.
(init_dummy_target): register find_default_can_async_p and
find_default_is_async_p on the dummy target.
* linux-nat.h (re_check_for_thread_db): Declare.
* linux-nat.c: Include inf-loop.h, event-loop.h and event-top.h.
(debug_linux_nat_async): New global.
(show_debug_linux_nat_async): New function.
(linux_nat_async_enabled, linux_nat_async_mask_value)
(linux_nat_event_pipe, linux_nat_num_queued_events)
(linux_nat_async_events_enabled): New globals.
(struct waitpid_result): New struct.
(waitpid_queue): New global.
(queued_waitpid, push_waitpid, drain_queued_events): New.
(my_waitpid): Call queued_waitpid.
(linux_child_follow_fork): Disable async events during the call.
(blocked_mask): Delete.
(sync_sigchld_action, async_sigchld_action): New globals.
(lin_lwp_attach_lwp): In sync mode, don't reblock SIGCHLD. In
async mode, block events during the call.
(linux_nat_create_inferior): New.
(linux_nat_attach): In sync mode, restore the mask states. In
async mode, wake the event loop immediatelly.
(detach_callback): Drain all queued events of the lwp we're
detaching from.
(linux_nat_detach): Block async mode, and drain events of the main
process.
(linux_nat_resume): If in async mode, mask async events during the
call. If short circuiting, force event loop to wake up. If
resuming, set target_executing, and register target events in the
event loop.
(pipe_to_local_event_queue, local_event_queue_to_pipe): New.
(linux_nat_wait): In async mode, block events during the call.
Only enable/disable passing SIGINT to the inferior in sync mode.
Get events from local waitpid queue. If no interesting events was
found, return to events loop. Reregister target events in the
event loop on exit. In sync mode, no need to reblock SIGCHLD.
(linux_nat_kill): Disable events on entry.
(linux_nat_mourn_inferior): In sync mode, don't restore the masks
here. Detach async mode from the event loop if there are no more
forks available, otherwise leave it on.
(sigchld_handler): Assure this is called only in sync mode.
(linux_async_permitted, linux_async_permitted_1): New globals.
(set_maintenance_linux_async_permitted)
(show_maintenance_linux_async_permitted): New functions.
(linux_nat_is_async_p, linux_nat_can_async_p)
(linux_nat_async_mask): New.
(linux_nat_event_pipe_pop, linux_nat_event_pipe_push): New.
(get_pending_events, async_sigchld_handler): New.
(linux_nat_async_events): New.
(async_terminal_is_ours): New global.
(linux_nat_terminal_inferior, linux_nat_terminal_ours): New.
(async_client_callback, async_client_context): New.
(linux_nat_async_file_handler, linux_nat_async)
(linux_nat_disable_async, linux_nat_enable_async): New.
(linux_nat_add_target): Register linux_nat_create_inferior,
linux_nat_can_async_p, linux_nat_is_async_p, linux_nat_async,
linux_nat_async_mask, linux_nat_terminal_inferior and
linux_nat_terminal_ours.
(_initialize_linux_nat): Remove local action variable, and update
code that used it to use sync_sigchld_action. Add new
"lin-lwp-async" debug set/show command. Put the "lin-lwp" debug
set/show command in the maintenance class. Add new "linux-async"
maintenance set/show command. Block SIGCHLD by default. Setup
async_sichld_action, and sync_sigchld_action. Install the default
async mode.
(lin_thread_get_thread_signals): Use a local sigset_t for blocking
the cancel signals.
* linux-thread-db.c (re_check_for_thread_db): New.
(clear_lwpid_callback): Handle TARGET_WAITKIND_IGNORE.
(thread_db_can_async_p, thread_db_is_async_p, thread_db_async)
(thread_db_async_mask): New.
(init_thread_db_ops): Register thread_db_can_async_p,
thread_db_is_async_p, thread_db_async and thread_db_async_mask.
* remote.c (remote_async_mask_value): New.
(remote_return_zero): New.
(init_remote_ops): Register remote_return_zero as callbacks of
to_can_async_p and to_is_async_p.
(remote_can_async_p, remote_is_async_p, remote_async): Update to
use remote_async_mask_value.
(remote_async_mask): New.
(init_remote_async_ops): Remove to_async_mask_value setting and
register remote_async_mask as to_async_mask callback in
remote_async_ops.
* Makefile.in (linux-nat.o): Update.
2008-03-18 Pedro Alves <pedro@codesourcery.com>
gdb/doc/
* gdb.texinfo (Debugging Output): Document
"set/show debug lin-lwp-async".
(Maintenance Commands): Document "maint set/show linux-async".
---
gdb/Makefile.in | 2
gdb/doc/gdb.texinfo | 16
gdb/linux-nat.c | 872 ++++++++++++++++++++++++++++++++++++++++++++++----
gdb/linux-nat.h | 4
gdb/linux-thread-db.c | 32 +
gdb/remote.c | 26 +
gdb/target.c | 37 +-
gdb/target.h | 15
8 files changed, 924 insertions(+), 80 deletions(-)
Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in 2008-03-18 20:25:22.000000000 +0000
+++ src/gdb/Makefile.in 2008-03-18 20:49:01.000000000 +0000
@@ -2351,7 +2351,7 @@ linux-nat.o: linux-nat.c $(defs_h) $(inf
$(gdb_wait_h) $(gdb_assert_h) $(linux_nat_h) $(gdbthread_h) \
$(gdbcmd_h) $(regcache_h) $(regset_h) $(inf_ptrace_h) $(auxv_h) \
$(elf_bfd_h) $(gregset_h) $(gdbcore_h) $(gdbthread_h) $(gdb_stat_h) \
- $(linux_fork_h)
+ $(linux_fork_h) $(inf_loop_h) $(event_loop_h) $(event_top_h)
linux-thread-db.o: linux-thread-db.c $(defs_h) $(gdb_assert_h) \
$(gdb_proc_service_h) $(gdb_thread_db_h) $(bfd_h) $(exceptions_h) \
$(gdbthread_h) $(inferior_h) $(symfile_h) $(objfiles_h) $(target_h) \
Index: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c 2008-03-18 20:26:04.000000000 +0000
+++ src/gdb/linux-nat.c 2008-03-18 23:10:00.000000000 +0000
@@ -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
@@ -115,6 +118,15 @@ show_debug_linux_nat (struct ui_file *fi
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
@@ -135,6 +147,158 @@ static int linux_supports_tracefork_flag
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)
+ ;
+}
+
\f
/* Trivial list manipulation functions to keep track of a list of
new stopped processes. */
@@ -185,12 +349,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);
@@ -363,6 +536,10 @@ linux_child_follow_fork (struct target_o
struct target_waitstatus last_status;
int has_vforked;
int parent_pid, child_pid;
+ int async_events_were_enabled = 0;
+
+ 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);
@@ -509,9 +686,7 @@ linux_child_follow_fork (struct target_o
fork_save_infrun_state (fp, 0);
}
else
- {
- target_detach (NULL, 0);
- }
+ target_detach (NULL, 0);
inferior_ptid = ptid_build (child_pid, child_pid, 0);
@@ -532,6 +707,9 @@ linux_child_follow_fork (struct target_o
follow_inferior_reset_breakpoints ();
}
+ if (target_can_async_p ())
+ target_async (inferior_event_handler, 0);
+
return 0;
}
@@ -625,8 +803,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;
\f
/* Prototypes for local functions. */
@@ -883,16 +1064,11 @@ 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);
@@ -965,10 +1141,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;
@@ -980,6 +1185,15 @@ linux_nat_attach (char *args, int from_t
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. */
@@ -1007,9 +1221,14 @@ linux_nat_attach (char *args, int from_t
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);
}
}
@@ -1066,6 +1285,7 @@ detach_callback (struct lwp_info *lp, vo
target_pid_to_str (lp->ptid),
strsignal (WSTOPSIG (lp->status)));
+ drain_queued_events (GET_LWP (lp->ptid));
delete_lwp (lp->ptid);
}
@@ -1075,6 +1295,10 @@ detach_callback (struct lwp_info *lp, vo
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. */
@@ -1085,12 +1309,12 @@ linux_nat_detach (char *args, int from_t
/* 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. */
@@ -1144,6 +1368,10 @@ linux_nat_resume (ptid_t ptid, int step,
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);
@@ -1208,6 +1436,13 @@ linux_nat_resume (ptid_t ptid, int step,
"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;
}
@@ -1227,6 +1462,12 @@ linux_nat_resume (ptid_t ptid, int step,
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. */
@@ -2078,6 +2319,57 @@ linux_nat_filter_event (int lwpid, int s
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)
{
@@ -2087,6 +2379,9 @@ linux_nat_wait (ptid_t ptid, struct targ
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. */
@@ -2102,12 +2397,9 @@ linux_nat_wait (ptid_t ptid, struct targ
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:
@@ -2131,7 +2423,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;
@@ -2190,15 +2482,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);
@@ -2226,17 +2527,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);
@@ -2333,6 +2655,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;
}
@@ -2412,6 +2741,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. */
@@ -2426,7 +2758,10 @@ linux_nat_kill (void)
}
if (forks_exist_p ())
- linux_fork_killall ();
+ {
+ linux_fork_killall ();
+ drain_queued_events (-1);
+ }
else
{
/* Kill all LWP's ... */
@@ -2447,13 +2782,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
@@ -2521,6 +2856,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. */
@@ -3279,6 +3621,376 @@ linux_trad_target (CORE_ADDR (*register_
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-17: 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-17: 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)
{
@@ -3290,6 +4002,7 @@ linux_nat_add_target (struct target_ops
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;
@@ -3301,6 +4014,13 @@ linux_nat_add_target (struct target_ops
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
@@ -3338,7 +4058,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\
@@ -3349,27 +4069,63 @@ Specify any of the following keywords fo
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);
}
\f
@@ -3405,7 +4161,9 @@ lin_thread_get_thread_signals (sigset_t
{
struct sigaction action;
int restart, cancel;
+ sigset_t blocked_mask;
+ sigemptyset (&blocked_mask);
sigemptyset (set);
restart = get_signo ("__pthread_sig_restart");
Index: src/gdb/linux-thread-db.c
===================================================================
--- src.orig/gdb/linux-thread-db.c 2008-03-18 20:25:22.000000000 +0000
+++ src/gdb/linux-thread-db.c 2008-03-18 21:44:25.000000000 +0000
@@ -870,6 +870,9 @@ thread_db_wait (ptid_t ptid, struct targ
ptid = target_beneath->to_wait (ptid, ourstatus);
+ if (ourstatus->kind == TARGET_WAITKIND_IGNORE)
+ return ptid;
+
if (ourstatus->kind == TARGET_WAITKIND_EXITED
|| ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
return pid_to_ptid (-1);
@@ -940,6 +943,31 @@ thread_db_mourn_inferior (void)
}
static int
+thread_db_can_async_p (void)
+{
+ return target_beneath->to_can_async_p ();
+}
+
+static int
+thread_db_is_async_p (void)
+{
+ return target_beneath->to_is_async_p ();
+}
+
+static void
+thread_db_async (void (*callback) (enum inferior_event_type event_type,
+ void *context), void *context)
+{
+ return target_beneath->to_async (callback, context);
+}
+
+static int
+thread_db_async_mask (int mask)
+{
+ return target_beneath->to_async_mask (mask);
+}
+
+static int
find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
{
td_thrinfo_t ti;
@@ -1133,6 +1161,10 @@ init_thread_db_ops (void)
thread_db_ops.to_get_thread_local_address
= thread_db_get_thread_local_address;
thread_db_ops.to_extra_thread_info = thread_db_extra_thread_info;
+ thread_db_ops.to_can_async_p = thread_db_can_async_p;
+ thread_db_ops.to_is_async_p = thread_db_is_async_p;
+ thread_db_ops.to_async = thread_db_async;
+ thread_db_ops.to_async_mask = thread_db_async_mask;
thread_db_ops.to_magic = OPS_MAGIC;
}
Index: src/gdb/remote.c
===================================================================
--- src.orig/gdb/remote.c 2008-03-18 20:25:22.000000000 +0000
+++ src/gdb/remote.c 2008-03-18 20:49:01.000000000 +0000
@@ -481,6 +481,8 @@ static struct target_ops extended_remote
extended_remote_ops, but with asynchronous support. */
static struct target_ops remote_async_ops;
+static int remote_async_mask_value = 1;
+
static struct target_ops extended_async_remote_ops;
/* FIXME: cagney/1999-09-23: Even though getpkt was called with
@@ -7176,6 +7178,12 @@ remote_command (char *args, int from_tty
help_list (remote_cmdlist, "remote ", -1, gdb_stdout);
}
+static int
+remote_return_zero (void)
+{
+ return 0;
+}
+
static void
init_remote_ops (void)
{
@@ -7229,6 +7237,8 @@ Specify the serial device it is connecte
remote_ops.to_flash_erase = remote_flash_erase;
remote_ops.to_flash_done = remote_flash_done;
remote_ops.to_read_description = remote_read_description;
+ remote_ops.to_can_async_p = remote_return_zero;
+ remote_ops.to_is_async_p = remote_return_zero;
}
/* Set up the extended remote vector by making a copy of the standard
@@ -7256,14 +7266,14 @@ static int
remote_can_async_p (void)
{
/* We're async whenever the serial device is. */
- return (current_target.to_async_mask_value) && serial_can_async_p (remote_desc);
+ return remote_async_mask_value && serial_can_async_p (remote_desc);
}
static int
remote_is_async_p (void)
{
/* We're async whenever the serial device is. */
- return (current_target.to_async_mask_value) && serial_is_async_p (remote_desc);
+ return remote_async_mask_value && serial_is_async_p (remote_desc);
}
/* Pass the SERIAL event on and up to the client. One day this code
@@ -7287,7 +7297,7 @@ static void
remote_async (void (*callback) (enum inferior_event_type event_type,
void *context), void *context)
{
- if (current_target.to_async_mask_value == 0)
+ if (remote_async_mask_value == 0)
internal_error (__FILE__, __LINE__,
_("Calling remote_async when async is masked"));
@@ -7301,6 +7311,14 @@ remote_async (void (*callback) (enum inf
serial_async (remote_desc, NULL, NULL);
}
+static int
+remote_async_mask (int new_mask)
+{
+ int curr_mask = remote_async_mask_value;
+ remote_async_mask_value = new_mask;
+ return curr_mask;
+}
+
/* Target async and target extended-async.
This are temporary targets, until it is all tested. Eventually
@@ -7360,7 +7378,7 @@ Specify the serial device it is connecte
remote_async_ops.to_can_async_p = remote_can_async_p;
remote_async_ops.to_is_async_p = remote_is_async_p;
remote_async_ops.to_async = remote_async;
- remote_async_ops.to_async_mask_value = 1;
+ remote_async_ops.to_async_mask = remote_async_mask;
remote_async_ops.to_magic = OPS_MAGIC;
remote_async_ops.to_memory_map = remote_memory_map;
remote_async_ops.to_flash_erase = remote_flash_erase;
Index: src/gdb/target.c
===================================================================
--- src.orig/gdb/target.c 2008-03-18 20:25:22.000000000 +0000
+++ src/gdb/target.c 2008-03-18 20:49:01.000000000 +0000
@@ -464,7 +464,7 @@ update_current_target (void)
INHERIT (to_can_async_p, t);
INHERIT (to_is_async_p, t);
INHERIT (to_async, t);
- INHERIT (to_async_mask_value, t);
+ INHERIT (to_async_mask, t);
INHERIT (to_find_memory_regions, t);
INHERIT (to_make_corefile_notes, t);
INHERIT (to_get_thread_local_address, t);
@@ -637,6 +637,9 @@ update_current_target (void)
de_fault (to_async,
(void (*) (void (*) (enum inferior_event_type, void*), void*))
tcomplain);
+ de_fault (to_async_mask,
+ (int (*) (int))
+ return_one);
current_target.to_read_description = NULL;
#undef de_fault
@@ -1694,14 +1697,6 @@ target_disconnect (char *args, int from_
tcomplain ();
}
-int
-target_async_mask (int mask)
-{
- int saved_async_masked_status = target_async_mask_value;
- target_async_mask_value = mask;
- return saved_async_masked_status;
-}
-
/* Look through the list of possible targets for a target that can
follow forks. */
@@ -1835,6 +1830,28 @@ find_default_create_inferior (char *exec
return;
}
+int
+find_default_can_async_p (void)
+{
+ struct target_ops *t;
+
+ t = find_default_run_target ("async");
+ if (t->to_can_async_p)
+ return (t->to_can_async_p) ();
+ return 0;
+}
+
+int
+find_default_is_async_p (void)
+{
+ struct target_ops *t;
+
+ t = find_default_run_target ("async");
+ if (t->to_is_async_p)
+ return (t->to_is_async_p) ();
+ return 0;
+}
+
static int
default_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
{
@@ -2099,6 +2116,8 @@ init_dummy_target (void)
dummy_target.to_doc = "";
dummy_target.to_attach = find_default_attach;
dummy_target.to_create_inferior = find_default_create_inferior;
+ dummy_target.to_can_async_p = find_default_can_async_p;
+ dummy_target.to_is_async_p = find_default_is_async_p;
dummy_target.to_pid_to_str = normal_pid_to_str;
dummy_target.to_stratum = dummy_stratum;
dummy_target.to_find_memory_regions = dummy_find_memory_regions;
Index: src/gdb/target.h
===================================================================
--- src.orig/gdb/target.h 2008-03-18 20:25:22.000000000 +0000
+++ src/gdb/target.h 2008-03-18 20:49:01.000000000 +0000
@@ -414,9 +414,8 @@ struct target_ops
/* ASYNC target controls */
int (*to_can_async_p) (void);
int (*to_is_async_p) (void);
- void (*to_async) (void (*cb) (enum inferior_event_type, void *context),
- void *context);
- int to_async_mask_value;
+ void (*to_async) (void (*) (enum inferior_event_type, void *), void *);
+ int (*to_async_mask) (int);
int (*to_find_memory_regions) (int (*) (CORE_ADDR,
unsigned long,
int, int, int,
@@ -938,11 +937,11 @@ int target_follow_fork (int follow_child
#define target_can_async_p() (current_target.to_can_async_p ())
/* Is the target in asynchronous execution mode? */
-#define target_is_async_p() (current_target.to_is_async_p())
+#define target_is_async_p() (current_target.to_is_async_p ())
/* Put the target in async mode with the specified callback function. */
#define target_async(CALLBACK,CONTEXT) \
- (current_target.to_async((CALLBACK), (CONTEXT)))
+ (current_target.to_async ((CALLBACK), (CONTEXT)))
/* This is to be used ONLY within call_function_by_hand(). It provides
a workaround, to have inferior function calls done in sychronous
@@ -958,10 +957,8 @@ int target_follow_fork (int follow_child
the turning async on and off to the single execution commands,
from where it is done currently, in remote_resume(). */
-#define target_async_mask_value \
- (current_target.to_async_mask_value)
-
-extern int target_async_mask (int mask);
+#define target_async_mask(MASK) \
+ (current_target.to_async_mask (MASK))
/* Converts a process id to a string. Usually, the string just contains
`process xyz', but on some systems it may contain
Index: src/gdb/linux-nat.h
===================================================================
--- src.orig/gdb/linux-nat.h 2008-03-18 20:25:22.000000000 +0000
+++ src/gdb/linux-nat.h 2008-03-18 20:49:01.000000000 +0000
@@ -86,6 +86,10 @@ extern struct lwp_info *lwp_list;
/* Attempt to initialize libthread_db. */
void check_for_thread_db (void);
+/* Re-attempt to initialize libthread_db, even if has already been
+ tried before. */
+void re_check_for_thread_db (void);
+
/* Tell the thread_db layer what native target operations to use. */
void thread_db_init (struct target_ops *);
Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo 2008-03-18 20:25:22.000000000 +0000
+++ src/gdb/doc/gdb.texinfo 2008-03-18 20:49:01.000000000 +0000
@@ -16405,6 +16405,12 @@ Displays the current state of @value{GDB
Turns on or off debugging messages from the Linux LWP debug support.
@item show debug lin-lwp
Show the current state of Linux LWP debugging messages.
+@item set debug lin-lwp-async
+@cindex @sc{gnu}/Linux LWP async debug messages
+@cindex Linux lightweight processes
+Turns on or off debugging messages from the Linux LWP async debug support.
+@item show debug lin-lwp-async
+Show the current state of Linux LWP async debugging messages.
@item set debug observer
@cindex observer debugging info
Turns on or off display of @value{GDBN} observer debugging. This
@@ -23240,6 +23246,16 @@ data in a @file{gmon.out} file, be sure
Configuring with @samp{--enable-profiling} arranges for @value{GDBN} to be
compiled with the @samp{-pg} compiler option.
+@kindex maint set linux-async
+@kindex maint show linux-async
+@cindex asynchronous support
+@item maint set linux-async
+@itemx maint show linux-async
+Control the GNU/Linux native asynchronous support of @value{GDBN}.
+
+GNU/Linux native asynchronous support will be disabled until you use
+the @samp{maint set linux-async} command to enable it.
+
@kindex maint show-debug-regs
@cindex x86 hardware debug registers
@item maint show-debug-regs
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-18 0:06 ` Nick Roberts
@ 2008-03-18 23:28 ` Pedro Alves
2008-03-19 3:59 ` Nick Roberts
2008-03-21 15:49 ` Daniel Jacobowitz
1 sibling, 1 reply; 37+ messages in thread
From: Pedro Alves @ 2008-03-18 23:28 UTC (permalink / raw)
To: gdb-patches
A Tuesday 18 March 2008 00:05:36, Nick Roberts wrote:
> > New patch attached. No regressions in async mode other than
> > the same defines.exp regressions, and no regressions when async
> > mode is disabled. --- tested on x86-64-unknown-linux-gnu.
>
> I've tested it (evidently on i686-linux-gnu) with
>
> set GDBFLAGS "-ex \"maint set linux-async on\""
>
> in site.exp, ignoring commands.exp and just got a few extra exceptions:
>
> FAIL: gdb.mi/mi-var-child-f.exp: mi runto MAIN__ (timeout)
> FAIL: gdb.mi/mi-var-child-f.exp: create local variable array
>
> FAIL: gdb.mi/mi2-simplerun.exp: continue to end (1)
>
> FAIL: gdb.threads/pthreads.exp: check backtrace from main thread
>
Strange, I don't get any of these. Could you try to see what's
different from a non-async run? The first failure may be gdb
losing an event, although I haven't seen those in a while now.
> Are you going to add any new tests?
It seems you are!
Actually, passing all the testsuite in async (with simulated syncronous
mode) is already a good test coverage. I feel like I hit
every possible problem already while writting this :-) You're
right in that we're going to need tests for the new functionaly, ...
> My original test file mi-async.exp (attached below) works with your patch
> when GDBFLAGS is toggled as above. It would be be good to have a test for
> -exec-interrupt too.
Yep.
... but please let's not prevent the not having many tests from putting
this in so more people can try it.
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-18 23:27 ` Pedro Alves
@ 2008-03-18 23:58 ` Daniel Jacobowitz
2008-03-21 15:55 ` Daniel Jacobowitz
1 sibling, 0 replies; 37+ messages in thread
From: Daniel Jacobowitz @ 2008-03-18 23:58 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
On Tue, Mar 18, 2008 at 11:26:37PM +0000, Pedro Alves wrote:
> I've moved them in the class_maintenance. I suspect that
> the debug commands are integers not booleans so we can have
> several debug levels. I count approximatelly the same amount of
> boolean vs integer debug mode vars.
I believe they're integers because we invented booleans later,
actually.
(Just commenting; will look at the patch properly tomorrow.)
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-18 23:28 ` Pedro Alves
@ 2008-03-19 3:59 ` Nick Roberts
2008-03-19 16:25 ` Luis Machado
0 siblings, 1 reply; 37+ messages in thread
From: Nick Roberts @ 2008-03-19 3:59 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=us-ascii, Size: 1244 bytes --]
I've rebuilt with your new patch.
> > FAIL: gdb.mi/mi-var-child-f.exp: mi runto MAIN__ (timeout)
This one has gone.
> > FAIL: gdb.mi/mi-var-child-f.exp: create local variable array
Looks like a buffering problem:
000-exec-run
000^running
(gdb)
000*stopped,thread-id="0",frame={addr="0x0804859a",func="MAIN__",args=[],file="../../../gdb/testsuite/gdb.mi/array.f",fullname="/extra/src-prus/gdb/testsuite/gdb.mi/array.f",line="18"}
(gdb)
PASS: gdb.mi/mi-var-child-f.exp: mi runto MAIN__
-var-create array * array
~"Current language: auto; currently fortran\n"
^done,name="array",numchild="3",value="[3]",type="integer (2,-1:1)"
(gdb)
FAIL: gdb.mi/mi-var-child-f.exp: create local variable array
> > FAIL: gdb.mi/mi2-simplerun.exp: continue to end (1)
Ditto.
999-exec-continue
999^running
Hello, World!callme
callme
(gdb)
999*stopped,reason="exited-normally"
(gdb)
FAIL: gdb.mi/mi2-simplerun.exp: continue to end (1)
> > FAIL: gdb.threads/pthreads.exp: check backtrace from main thread
The backtrace was looking at the wrong thread but this has gone now.
--
Nick http://www.inet.net.nz/~nickrob\x16º&Öéj×!zÊÞ¶êçç¶îX¬µªÜ\a[¥«\
ë
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-19 3:59 ` Nick Roberts
@ 2008-03-19 16:25 ` Luis Machado
2008-03-19 23:19 ` Pedro Alves
0 siblings, 1 reply; 37+ messages in thread
From: Luis Machado @ 2008-03-19 16:25 UTC (permalink / raw)
To: Nick Roberts; +Cc: Pedro Alves, gdb-patches
Hi folks,
I've ran the testsuite on PPC to check for regressions due to the async
patch. The results look good, just the expected regressions and one
unexpected (mi2-simplerun.exp).
define.exp:
FAIL: gdb.base/define.exp: use user command: nextwhere
FAIL: gdb.base/define.exp: use hook-stop command
commands.exp:
FAIL: gdb.base/commands.exp: running to factorial in runto
FAIL: gdb.base/commands.exp: if test in test_command_prompt_position
FAIL: gdb.base/commands.exp: run factorial until breakpoint
mi2-simplerun.exp:
FAIL: gdb.mi/mi2-simplerun.exp: continue to end (1)
Nick, i still can reproduce the last one constantly.
Best regards,
On Wed, 2008-03-19 at 15:58 +1200, Nick Roberts wrote:
> I've rebuilt with your new patch.
>
> > > FAIL: gdb.mi/mi-var-child-f.exp: mi runto MAIN__ (timeout)
>
> This one has gone.
>
> > > FAIL: gdb.mi/mi-var-child-f.exp: create local variable array
>
> Looks like a buffering problem:
>
> 000-exec-run
> 000^running
> (gdb)
> 000*stopped,thread-id="0",frame={addr="0x0804859a",func="MAIN__",args=[],file="../../../gdb/testsuite/gdb.mi/array.f",fullname="/extra/src-prus/gdb/testsuite/gdb.mi/array.f",line="18"}
> (gdb)
> PASS: gdb.mi/mi-var-child-f.exp: mi runto MAIN__
> -var-create array * array
> ~"Current language: auto; currently fortran\n"
> ^done,name="array",numchild="3",value="[3]",type="integer (2,-1:1)"
> (gdb)
> FAIL: gdb.mi/mi-var-child-f.exp: create local variable array
>
> > > FAIL: gdb.mi/mi2-simplerun.exp: continue to end (1)
>
> Ditto.
>
> 999-exec-continue
> 999^running
> Hello, World!callme
> callme
> (gdb)
> 999*stopped,reason="exited-normally"
> (gdb)
> FAIL: gdb.mi/mi2-simplerun.exp: continue to end (1)
>
> > > FAIL: gdb.threads/pthreads.exp: check backtrace from main thread
>
> The backtrace was looking at the wrong thread but this has gone now.
>
> --
> Nick http://www.inet.net.nz/~nickrob
--
Luis Machado
Software Engineer
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-19 16:25 ` Luis Machado
@ 2008-03-19 23:19 ` Pedro Alves
2008-03-19 23:26 ` Pedro Alves
2008-03-20 1:58 ` Nick Roberts
0 siblings, 2 replies; 37+ messages in thread
From: Pedro Alves @ 2008-03-19 23:19 UTC (permalink / raw)
To: luisgpm; +Cc: Nick Roberts, gdb-patches
A Wednesday 19 March 2008 16:24:35, Luis Machado wrote:
> Hi folks,
>
> I've ran the testsuite on PPC to check for regressions due to the async
> patch. The results look good, just the expected regressions and one
> unexpected (mi2-simplerun.exp).
>
Thanks both for testing, and thanks Luis for sending me the full logs!
> mi2-simplerun.exp:
> FAIL: gdb.mi/mi2-simplerun.exp: continue to end (1)
>
> Nick, i still can reproduce the last one constantly.
> >
> > 999-exec-continue
> > 999^running
> > Hello, World!callme
> > callme
> > (gdb)
> > 999*stopped,reason="exited-normally"
> > (gdb)
> > FAIL: gdb.mi/mi2-simplerun.exp: continue to end (1)
> >
I've looked at this failure, and what happens is that
in sync mode, gdb prompts "(gdb)" immediatelly
after printing "^running", while in async mode,
"^running" is output first, and only when "run" cli command
actually completes, the "(gdb) " is output. Like so:
mi_execute_command
captured_mi_execute_command
mi_execute_async_cli_command
mi_cmd_execute
mi_cmd_exec_run
mi_execute_async_cli_command
sets up continuation for "*stopped", if async mode.
execute_command
(...)
run_command_1
target_create_inferior
proceed
prints ("gdb") before exiting.
later, when inferior exits, the mi_exec_async_cli_cmd_continuation is called,
which prints that part:
> > 999*stopped,reason="exited-normally"
> > (gdb)
Here's a bit of current code:
enum mi_cmd_result
mi_execute_async_cli_command (char *mi, char *args, int from_tty)
{
struct cleanup *old_cleanups;
char *run;
char *async_args;
if (target_can_async_p ())
{
async_args = (char *) xmalloc (strlen (args) + 2);
make_exec_cleanup (free, async_args);
strcpy (async_args, args);
strcat (async_args, "&");
run = xstrprintf ("%s %s", mi, async_args);
make_exec_cleanup (free, run);
add_continuation (mi_exec_async_cli_cmd_continuation, NULL);
old_cleanups = NULL;
}
else
{
run = xstrprintf ("%s %s", mi, args);
old_cleanups = make_cleanup (xfree, run);
}
if (!target_can_async_p ())
{
/* NOTE: For synchronous targets asynchronous behavour is faked by
printing out the GDB prompt before we even try to execute the
command. */
if (last_async_command)
fputs_unfiltered (last_async_command, raw_stdout);
fputs_unfiltered ("^running\n", raw_stdout);
fputs_unfiltered ("(gdb) \n", raw_stdout); <<<<< sync mode
prints it early.
gdb_flush (raw_stdout);
}
else
{
/* FIXME: cagney/1999-11-29: Printing this message before
calling execute_command is wrong. It should only be printed
once gdb has confirmed that it really has managed to send a
run command to the target. */
if (last_async_command)
fputs_unfiltered (last_async_command, raw_stdout);
fputs_unfiltered ("^running\n", raw_stdout); <<<<< async mode
defers the "(gdb)" printing
}
execute_command ( /*ui */ run, 0 /*from_tty */ );
(...)
}
So, -exec-run, in async mode, is turned into CLI's "run &", which ends
up in run_command_1. At the end of it, after calling target_create_inferior
and friends, we do proceed. So at this point the inferior as already started
running, and we haven't printed "(gdb)" yet. We'll only print *after*
creating the inferior and proceed'ing. There you have the difference to sync
mode. It's really a timing issue, and that's why I can't reproduce -- it
is timing/host sensitive. So this means this problem isn't related to my
patch, but it's a general async issue, that may be visible too in
"target async" connected to a gdbserver for example. Although
since we're running over a slower link there, nobody was probably seeing
it. I don't know enough MI to say if this is according to the spec
or not, but it feels like a testsuite deficiency, is it not?
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-19 23:19 ` Pedro Alves
@ 2008-03-19 23:26 ` Pedro Alves
2008-03-20 1:58 ` Nick Roberts
1 sibling, 0 replies; 37+ messages in thread
From: Pedro Alves @ 2008-03-19 23:26 UTC (permalink / raw)
To: gdb-patches; +Cc: luisgpm, Nick Roberts
Sorry, forgot to post the sync run without the failure for comparision.
A Wednesday 19 March 2008 23:18:45, Pedro Alves wrote:
> A Wednesday 19 March 2008 16:24:35, Luis Machado wrote:
> > Hi folks,
> >
> > I've ran the testsuite on PPC to check for regressions due to the async
> > patch. The results look good, just the expected regressions and one
> > unexpected (mi2-simplerun.exp).
>
> Thanks both for testing, and thanks Luis for sending me the full logs!
>
> > mi2-simplerun.exp:
> > FAIL: gdb.mi/mi2-simplerun.exp: continue to end (1)
> >
> > Nick, i still can reproduce the last one constantly.
> >
> > > 999-exec-continue
> > > 999^running
> > > Hello, World!callme
> > > callme
> > > (gdb)
> > > 999*stopped,reason="exited-normally"
> > > (gdb)
> > > FAIL: gdb.mi/mi2-simplerun.exp: continue to end (1)
>
In sync mode:
999-exec-continue
999^running
(gdb) << immediatelly after "^running". Keep reading.
Hello, World!callme
callme
999*stopped,reason="exited-normally"
(gdb)
> I've looked at this failure, and what happens is that
> in sync mode, gdb prompts "(gdb)" immediatelly
> after printing "^running", while in async mode,
> "^running" is output first, and only when "run" cli command
> actually completes, the "(gdb) " is output. Like so:
>
> mi_execute_command
> captured_mi_execute_command
> mi_execute_async_cli_command
> mi_cmd_execute
> mi_cmd_exec_run
> mi_execute_async_cli_command
> sets up continuation for "*stopped", if async mode.
> execute_command
> (...)
> run_command_1
> target_create_inferior
> proceed
> prints ("gdb") before exiting.
>
> later, when inferior exits, the mi_exec_async_cli_cmd_continuation is
> called,
>
> which prints that part:
> > > 999*stopped,reason="exited-normally"
> > > (gdb)
>
> Here's a bit of current code:
>
> enum mi_cmd_result
> mi_execute_async_cli_command (char *mi, char *args, int from_tty)
> {
> struct cleanup *old_cleanups;
> char *run;
> char *async_args;
>
> if (target_can_async_p ())
> {
> async_args = (char *) xmalloc (strlen (args) + 2);
> make_exec_cleanup (free, async_args);
> strcpy (async_args, args);
> strcat (async_args, "&");
> run = xstrprintf ("%s %s", mi, async_args);
> make_exec_cleanup (free, run);
> add_continuation (mi_exec_async_cli_cmd_continuation, NULL);
> old_cleanups = NULL;
> }
> else
> {
> run = xstrprintf ("%s %s", mi, args);
> old_cleanups = make_cleanup (xfree, run);
> }
>
> if (!target_can_async_p ())
> {
> /* NOTE: For synchronous targets asynchronous behavour is faked by
> printing out the GDB prompt before we even try to execute the
> command. */
> if (last_async_command)
> fputs_unfiltered (last_async_command, raw_stdout);
> fputs_unfiltered ("^running\n", raw_stdout);
> fputs_unfiltered ("(gdb) \n", raw_stdout); <<<<< sync mode
> prints it early.
> gdb_flush (raw_stdout);
> }
> else
> {
> /* FIXME: cagney/1999-11-29: Printing this message before
> calling execute_command is wrong. It should only be printed
> once gdb has confirmed that it really has managed to send a
> run command to the target. */
> if (last_async_command)
> fputs_unfiltered (last_async_command, raw_stdout);
> fputs_unfiltered ("^running\n", raw_stdout); <<<<< async mode
> defers the "(gdb)"
> printing }
>
> execute_command ( /*ui */ run, 0 /*from_tty */ );
>
> (...)
> }
>
> So, -exec-run, in async mode, is turned into CLI's "run &", which ends
> up in run_command_1. At the end of it, after calling
> target_create_inferior and friends, we do proceed. So at this point the
> inferior as already started running, and we haven't printed "(gdb)" yet.
> We'll only print *after* creating the inferior and proceed'ing. There you
> have the difference to sync mode. It's really a timing issue, and that's
> why I can't reproduce -- it is timing/host sensitive. So this means this
> problem isn't related to my patch, but it's a general async issue, that may
> be visible too in
> "target async" connected to a gdbserver for example. Although
> since we're running over a slower link there, nobody was probably seeing
> it. I don't know enough MI to say if this is according to the spec
> or not, but it feels like a testsuite deficiency, is it not?
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-19 23:19 ` Pedro Alves
2008-03-19 23:26 ` Pedro Alves
@ 2008-03-20 1:58 ` Nick Roberts
2008-03-21 15:47 ` Daniel Jacobowitz
1 sibling, 1 reply; 37+ messages in thread
From: Nick Roberts @ 2008-03-20 1:58 UTC (permalink / raw)
To: Pedro Alves; +Cc: luisgpm, gdb-patches
> It's really a timing issue, and that's why I can't reproduce -- it is
> timing/host sensitive. So this means this problem isn't related to my
> patch, but it's a general async issue, that may be visible too in "target
> async" connected to a gdbserver for example. Although since we're running
> over a slower link there, nobody was probably seeing it. I don't know
> enough MI to say if this is according to the spec or not, but it feels like
> a testsuite deficiency, is it not?
I don't think it's a testsuite deficiency but, coincidentally, Vladimir has
just been talking about removing the "(gdb) \n" after ^running. This may be
a good reason to do it.
The patch below fixes the fails that I was seeing in mi-var-child-f.exp.
--
Nick http://www.inet.net.nz/~nickrob
*** inf-loop.c 15 Mar 2008 22:34:07 +1300 1.11
--- inf-loop.c 20 Mar 2008 13:55:12 +1200
*************** inferior_event_handler (enum inferior_ev
*** 91,98 ****
was_sync = sync_execution;
async_enable_stdin ();
- do_all_continuations ();
-
if (current_language != expected_language)
{
if (language_mode == language_mode_auto)
--- 91,96 ----
*************** inferior_event_handler (enum inferior_ev
*** 101,106 ****
--- 99,106 ----
}
}
+ do_all_continuations ();
+
/* If the continuation did not start the target again,
prepare for interation with the user. */
if (!target_executing)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-20 1:58 ` Nick Roberts
@ 2008-03-21 15:47 ` Daniel Jacobowitz
0 siblings, 0 replies; 37+ messages in thread
From: Daniel Jacobowitz @ 2008-03-21 15:47 UTC (permalink / raw)
To: Nick Roberts; +Cc: Pedro Alves, luisgpm, gdb-patches
On Thu, Mar 20, 2008 at 01:57:30PM +1200, Nick Roberts wrote:
> > It's really a timing issue, and that's why I can't reproduce -- it is
> > timing/host sensitive. So this means this problem isn't related to my
> > patch, but it's a general async issue, that may be visible too in "target
> > async" connected to a gdbserver for example. Although since we're running
> > over a slower link there, nobody was probably seeing it. I don't know
> > enough MI to say if this is according to the spec or not, but it feels like
> > a testsuite deficiency, is it not?
>
> I don't think it's a testsuite deficiency but, coincidentally, Vladimir has
> just been talking about removing the "(gdb) \n" after ^running. This may be
> a good reason to do it.
>
> The patch below fixes the fails that I was seeing in mi-var-child-f.exp.
This seems to be a general async issue, not related specifically to
Linux. So let's address it separately. If you think this patch is
right, could you post it in a new thread with an explanation, please?
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-18 0:06 ` Nick Roberts
2008-03-18 23:28 ` Pedro Alves
@ 2008-03-21 15:49 ` Daniel Jacobowitz
2008-03-21 23:02 ` Nick Roberts
1 sibling, 1 reply; 37+ messages in thread
From: Daniel Jacobowitz @ 2008-03-21 15:49 UTC (permalink / raw)
To: Nick Roberts; +Cc: Pedro Alves, gdb-patches
On Tue, Mar 18, 2008 at 12:05:36PM +1200, Nick Roberts wrote:
> My original test file mi-async.exp (attached below) works with your patch when
> GDBFLAGS is toggled as above.
Could you post a patch to add this? You can use -gdb-set to avoid
adjusting GDBFLAGS, and [isnative] && [istarget *-linux*] to limit
it to platforms with the new command for now.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-18 23:27 ` Pedro Alves
2008-03-18 23:58 ` Daniel Jacobowitz
@ 2008-03-21 15:55 ` Daniel Jacobowitz
2008-03-21 17:19 ` Pedro Alves
1 sibling, 1 reply; 37+ messages in thread
From: Daniel Jacobowitz @ 2008-03-21 15:55 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
On Tue, Mar 18, 2008 at 11:26:37PM +0000, Pedro Alves wrote:
> 2008-03-18 Pedro Alves <pedro@codesourcery.com>
>
> gdb/
> * target.h (struct target_ops): Delete to_async_mask_value and add
> to_async_mask.
...
This is OK to commit now. I'm sure we'll shake some more bugs out of
it in the next few weeks :-)
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-21 15:55 ` Daniel Jacobowitz
@ 2008-03-21 17:19 ` Pedro Alves
2008-03-28 14:48 ` Maciej W. Rozycki
0 siblings, 1 reply; 37+ messages in thread
From: Pedro Alves @ 2008-03-21 17:19 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 300 bytes --]
A Friday 21 March 2008 15:54:37, Daniel Jacobowitz wrote:
>
> This is OK to commit now. I'm sure we'll shake some more bugs out of
> it in the next few weeks :-)
Thanks! I'm sure we will. :-)
This is what I checked in after another round of testing
on x86_64-unknown-linux-gnu.
--
Pedro Alves
[-- Attachment #2: linux_async.diff --]
[-- Type: text/x-diff, Size: 49468 bytes --]
Index: ChangeLog
===================================================================
RCS file: /cvs/src/src/gdb/ChangeLog,v
retrieving revision 1.9241
diff -u -p -r1.9241 ChangeLog
--- ChangeLog 21 Mar 2008 15:44:53 -0000 1.9241
+++ ChangeLog 21 Mar 2008 17:05:49 -0000
@@ -1,3 +1,105 @@
+2008-03-21 Pedro Alves <pedro@codesourcery.com>
+
+ Linux native async support.
+
+ * target.h (struct target_ops): Delete to_async_mask_value and add
+ to_async_mask.
+ (target_is_async_p, target_async): Formatting.
+ (target_async_mask_value): Delete.
+ (target_async_mask): Delete function declaration, and add new
+ target macro with the same name.
+
+ * target.c (update_current_target): Replace to_async_mask_value by
+ to_async_mask. Default to_async_mask to return_one.
+ (target_async_mask): Delete.
+ (find_default_can_async_p, find_default_is_async_p): New.
+ (init_dummy_target): register find_default_can_async_p and
+ find_default_is_async_p on the dummy target.
+
+ * linux-nat.c: Include inf-loop.h, event-loop.h and event-top.h.
+ (debug_linux_nat_async): New global.
+ (show_debug_linux_nat_async): New function.
+ (linux_nat_async_enabled, linux_nat_async_mask_value)
+ (linux_nat_event_pipe, linux_nat_num_queued_events)
+ (linux_nat_async_events_enabled): New globals.
+ (struct waitpid_result): New struct.
+ (waitpid_queue): New global.
+ (queued_waitpid, push_waitpid, drain_queued_events): New.
+ (my_waitpid): Call queued_waitpid.
+ (linux_child_follow_fork): Disable async events during the call.
+ (blocked_mask): Delete.
+ (sync_sigchld_action, async_sigchld_action): New globals.
+ (lin_lwp_attach_lwp): In sync mode, don't reblock SIGCHLD. In
+ async mode, block events during the call.
+ (linux_nat_create_inferior): New.
+ (linux_nat_attach): In sync mode, restore the mask states. In
+ async mode, wake the event loop immediatelly.
+ (detach_callback): Drain all queued events of the lwp we're
+ detaching from.
+ (linux_nat_detach): Block async mode, and drain events of the main
+ process.
+ (linux_nat_resume): If in async mode, mask async events during the
+ call. If short circuiting, force event loop to wake up. If
+ resuming, set target_executing, and register target events in the
+ event loop.
+ (pipe_to_local_event_queue, local_event_queue_to_pipe): New.
+ (linux_nat_wait): In async mode, block events during the call.
+ Only enable/disable passing SIGINT to the inferior in sync mode.
+ Get events from local waitpid queue. If no interesting events was
+ found, return to events loop. Reregister target events in the
+ event loop on exit. In sync mode, no need to reblock SIGCHLD.
+ (linux_nat_kill): Disable events on entry.
+ (linux_nat_mourn_inferior): In sync mode, don't restore the masks
+ here. Detach async mode from the event loop if there are no more
+ forks available, otherwise leave it on.
+ (sigchld_handler): Assure this is called only in sync mode.
+ (linux_async_permitted, linux_async_permitted_1): New globals.
+ (set_maintenance_linux_async_permitted)
+ (show_maintenance_linux_async_permitted): New functions.
+ (linux_nat_is_async_p, linux_nat_can_async_p)
+ (linux_nat_async_mask): New.
+ (linux_nat_event_pipe_pop, linux_nat_event_pipe_push): New.
+ (get_pending_events, async_sigchld_handler): New.
+ (linux_nat_async_events): New.
+ (async_terminal_is_ours): New global.
+ (linux_nat_terminal_inferior, linux_nat_terminal_ours): New.
+ (async_client_callback, async_client_context): New.
+ (linux_nat_async_file_handler, linux_nat_async)
+ (linux_nat_disable_async, linux_nat_enable_async): New.
+ (linux_nat_add_target): Register linux_nat_create_inferior,
+ linux_nat_can_async_p, linux_nat_is_async_p, linux_nat_async,
+ linux_nat_async_mask, linux_nat_terminal_inferior and
+ linux_nat_terminal_ours.
+ (_initialize_linux_nat): Remove local action variable, and update
+ code that used it to use sync_sigchld_action. Add new
+ "lin-lwp-async" debug set/show command. Put the "lin-lwp" debug
+ set/show command in the maintenance class. Add new "linux-async"
+ maintenance set/show command. Block SIGCHLD by default. Setup
+ async_sichld_action, and sync_sigchld_action. Install the default
+ async mode.
+ (lin_thread_get_thread_signals): Use a local sigset_t for blocking
+ the cancel signals.
+
+ * linux-thread-db.c (re_check_for_thread_db): New.
+ (clear_lwpid_callback): Handle TARGET_WAITKIND_IGNORE.
+ (thread_db_can_async_p, thread_db_is_async_p, thread_db_async)
+ (thread_db_async_mask): New.
+ (init_thread_db_ops): Register thread_db_can_async_p,
+ thread_db_is_async_p, thread_db_async and thread_db_async_mask.
+
+ * remote.c (remote_async_mask_value): New.
+ (remote_return_zero): New.
+ (init_remote_ops): Register remote_return_zero as callbacks of
+ to_can_async_p and to_is_async_p.
+ (remote_can_async_p, remote_is_async_p, remote_async): Update to
+ use remote_async_mask_value.
+ (remote_async_mask): New.
+ (init_remote_async_ops): Remove to_async_mask_value setting and
+ register remote_async_mask as to_async_mask callback in
+ remote_async_ops.
+
+ * Makefile.in (linux-nat.o): Update.
+
2008-03-21 Daniel Jacobowitz <dan@codesourcery.com>
* gdbthread.h (add_thread_with_info): New.
Index: target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.113
diff -u -p -r1.113 target.h
--- target.h 15 Mar 2008 14:55:21 -0000 1.113
+++ target.h 21 Mar 2008 17:05:49 -0000
@@ -414,9 +414,8 @@ struct target_ops
/* ASYNC target controls */
int (*to_can_async_p) (void);
int (*to_is_async_p) (void);
- void (*to_async) (void (*cb) (enum inferior_event_type, void *context),
- void *context);
- int to_async_mask_value;
+ void (*to_async) (void (*) (enum inferior_event_type, void *), void *);
+ int (*to_async_mask) (int);
int (*to_find_memory_regions) (int (*) (CORE_ADDR,
unsigned long,
int, int, int,
@@ -938,11 +937,11 @@ int target_follow_fork (int follow_child
#define target_can_async_p() (current_target.to_can_async_p ())
/* Is the target in asynchronous execution mode? */
-#define target_is_async_p() (current_target.to_is_async_p())
+#define target_is_async_p() (current_target.to_is_async_p ())
/* Put the target in async mode with the specified callback function. */
#define target_async(CALLBACK,CONTEXT) \
- (current_target.to_async((CALLBACK), (CONTEXT)))
+ (current_target.to_async ((CALLBACK), (CONTEXT)))
/* This is to be used ONLY within call_function_by_hand(). It provides
a workaround, to have inferior function calls done in sychronous
@@ -958,10 +957,8 @@ int target_follow_fork (int follow_child
the turning async on and off to the single execution commands,
from where it is done currently, in remote_resume(). */
-#define target_async_mask_value \
- (current_target.to_async_mask_value)
-
-extern int target_async_mask (int mask);
+#define target_async_mask(MASK) \
+ (current_target.to_async_mask (MASK))
/* Converts a process id to a string. Usually, the string just contains
`process xyz', but on some systems it may contain
Index: target.c
===================================================================
RCS file: /cvs/src/src/gdb/target.c,v
retrieving revision 1.157
diff -u -p -r1.157 target.c
--- target.c 15 Mar 2008 14:55:21 -0000 1.157
+++ target.c 21 Mar 2008 17:05:49 -0000
@@ -464,7 +464,7 @@ update_current_target (void)
INHERIT (to_can_async_p, t);
INHERIT (to_is_async_p, t);
INHERIT (to_async, t);
- INHERIT (to_async_mask_value, t);
+ INHERIT (to_async_mask, t);
INHERIT (to_find_memory_regions, t);
INHERIT (to_make_corefile_notes, t);
INHERIT (to_get_thread_local_address, t);
@@ -637,6 +637,9 @@ update_current_target (void)
de_fault (to_async,
(void (*) (void (*) (enum inferior_event_type, void*), void*))
tcomplain);
+ de_fault (to_async_mask,
+ (int (*) (int))
+ return_one);
current_target.to_read_description = NULL;
#undef de_fault
@@ -1694,14 +1697,6 @@ target_disconnect (char *args, int from_
tcomplain ();
}
-int
-target_async_mask (int mask)
-{
- int saved_async_masked_status = target_async_mask_value;
- target_async_mask_value = mask;
- return saved_async_masked_status;
-}
-
/* Look through the list of possible targets for a target that can
follow forks. */
@@ -1835,6 +1830,28 @@ find_default_create_inferior (char *exec
return;
}
+int
+find_default_can_async_p (void)
+{
+ struct target_ops *t;
+
+ t = find_default_run_target ("async");
+ if (t->to_can_async_p)
+ return (t->to_can_async_p) ();
+ return 0;
+}
+
+int
+find_default_is_async_p (void)
+{
+ struct target_ops *t;
+
+ t = find_default_run_target ("async");
+ if (t->to_is_async_p)
+ return (t->to_is_async_p) ();
+ return 0;
+}
+
static int
default_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
{
@@ -2099,6 +2116,8 @@ init_dummy_target (void)
dummy_target.to_doc = "";
dummy_target.to_attach = find_default_attach;
dummy_target.to_create_inferior = find_default_create_inferior;
+ dummy_target.to_can_async_p = find_default_can_async_p;
+ dummy_target.to_is_async_p = find_default_is_async_p;
dummy_target.to_pid_to_str = normal_pid_to_str;
dummy_target.to_stratum = dummy_stratum;
dummy_target.to_find_memory_regions = dummy_find_memory_regions;
Index: linux-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/linux-nat.c,v
retrieving revision 1.77
diff -u -p -r1.77 linux-nat.c
--- linux-nat.c 21 Mar 2008 15:44:53 -0000 1.77
+++ linux-nat.c 21 Mar 2008 17:05:52 -0000
@@ -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 *fi
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
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)
+ ;
+}
+
\f
/* 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_o
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_o
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_o
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;
\f
/* 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_t
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_t
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, vo
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, vo
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_t
/* 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,
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,
"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,
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 s
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 targ
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 targ
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_
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
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->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 fo
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);
}
\f
@@ -3359,7 +4115,9 @@ lin_thread_get_thread_signals (sigset_t
{
struct sigaction action;
int restart, cancel;
+ sigset_t blocked_mask;
+ sigemptyset (&blocked_mask);
sigemptyset (set);
restart = get_signo ("__pthread_sig_restart");
Index: linux-thread-db.c
===================================================================
RCS file: /cvs/src/src/gdb/linux-thread-db.c,v
retrieving revision 1.38
diff -u -p -r1.38 linux-thread-db.c
--- linux-thread-db.c 21 Mar 2008 15:44:53 -0000 1.38
+++ linux-thread-db.c 21 Mar 2008 17:05:52 -0000
@@ -824,6 +824,9 @@ thread_db_wait (ptid_t ptid, struct targ
ptid = target_beneath->to_wait (ptid, ourstatus);
+ if (ourstatus->kind == TARGET_WAITKIND_IGNORE)
+ return ptid;
+
if (ourstatus->kind == TARGET_WAITKIND_EXITED
|| ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
return pid_to_ptid (-1);
@@ -885,6 +888,31 @@ thread_db_mourn_inferior (void)
}
static int
+thread_db_can_async_p (void)
+{
+ return target_beneath->to_can_async_p ();
+}
+
+static int
+thread_db_is_async_p (void)
+{
+ return target_beneath->to_is_async_p ();
+}
+
+static void
+thread_db_async (void (*callback) (enum inferior_event_type event_type,
+ void *context), void *context)
+{
+ return target_beneath->to_async (callback, context);
+}
+
+static int
+thread_db_async_mask (int mask)
+{
+ return target_beneath->to_async_mask (mask);
+}
+
+static int
find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
{
td_thrinfo_t ti;
@@ -1056,6 +1084,10 @@ init_thread_db_ops (void)
thread_db_ops.to_get_thread_local_address
= thread_db_get_thread_local_address;
thread_db_ops.to_extra_thread_info = thread_db_extra_thread_info;
+ thread_db_ops.to_can_async_p = thread_db_can_async_p;
+ thread_db_ops.to_is_async_p = thread_db_is_async_p;
+ thread_db_ops.to_async = thread_db_async;
+ thread_db_ops.to_async_mask = thread_db_async_mask;
thread_db_ops.to_magic = OPS_MAGIC;
}
Index: remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.285
diff -u -p -r1.285 remote.c
--- remote.c 14 Mar 2008 18:57:43 -0000 1.285
+++ remote.c 21 Mar 2008 17:05:53 -0000
@@ -481,6 +481,8 @@ static struct target_ops extended_remote
extended_remote_ops, but with asynchronous support. */
static struct target_ops remote_async_ops;
+static int remote_async_mask_value = 1;
+
static struct target_ops extended_async_remote_ops;
/* FIXME: cagney/1999-09-23: Even though getpkt was called with
@@ -7176,6 +7178,12 @@ remote_command (char *args, int from_tty
help_list (remote_cmdlist, "remote ", -1, gdb_stdout);
}
+static int
+remote_return_zero (void)
+{
+ return 0;
+}
+
static void
init_remote_ops (void)
{
@@ -7229,6 +7237,8 @@ Specify the serial device it is connecte
remote_ops.to_flash_erase = remote_flash_erase;
remote_ops.to_flash_done = remote_flash_done;
remote_ops.to_read_description = remote_read_description;
+ remote_ops.to_can_async_p = remote_return_zero;
+ remote_ops.to_is_async_p = remote_return_zero;
}
/* Set up the extended remote vector by making a copy of the standard
@@ -7256,14 +7266,14 @@ static int
remote_can_async_p (void)
{
/* We're async whenever the serial device is. */
- return (current_target.to_async_mask_value) && serial_can_async_p (remote_desc);
+ return remote_async_mask_value && serial_can_async_p (remote_desc);
}
static int
remote_is_async_p (void)
{
/* We're async whenever the serial device is. */
- return (current_target.to_async_mask_value) && serial_is_async_p (remote_desc);
+ return remote_async_mask_value && serial_is_async_p (remote_desc);
}
/* Pass the SERIAL event on and up to the client. One day this code
@@ -7287,7 +7297,7 @@ static void
remote_async (void (*callback) (enum inferior_event_type event_type,
void *context), void *context)
{
- if (current_target.to_async_mask_value == 0)
+ if (remote_async_mask_value == 0)
internal_error (__FILE__, __LINE__,
_("Calling remote_async when async is masked"));
@@ -7301,6 +7311,14 @@ remote_async (void (*callback) (enum inf
serial_async (remote_desc, NULL, NULL);
}
+static int
+remote_async_mask (int new_mask)
+{
+ int curr_mask = remote_async_mask_value;
+ remote_async_mask_value = new_mask;
+ return curr_mask;
+}
+
/* Target async and target extended-async.
This are temporary targets, until it is all tested. Eventually
@@ -7360,7 +7378,7 @@ Specify the serial device it is connecte
remote_async_ops.to_can_async_p = remote_can_async_p;
remote_async_ops.to_is_async_p = remote_is_async_p;
remote_async_ops.to_async = remote_async;
- remote_async_ops.to_async_mask_value = 1;
+ remote_async_ops.to_async_mask = remote_async_mask;
remote_async_ops.to_magic = OPS_MAGIC;
remote_async_ops.to_memory_map = remote_memory_map;
remote_async_ops.to_flash_erase = remote_flash_erase;
Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.993
diff -u -p -r1.993 Makefile.in
--- Makefile.in 16 Mar 2008 08:50:43 -0000 1.993
+++ Makefile.in 21 Mar 2008 17:05:54 -0000
@@ -2351,7 +2351,7 @@ linux-nat.o: linux-nat.c $(defs_h) $(inf
$(gdb_wait_h) $(gdb_assert_h) $(linux_nat_h) $(gdbthread_h) \
$(gdbcmd_h) $(regcache_h) $(regset_h) $(inf_ptrace_h) $(auxv_h) \
$(elf_bfd_h) $(gregset_h) $(gdbcore_h) $(gdbthread_h) $(gdb_stat_h) \
- $(linux_fork_h)
+ $(linux_fork_h) $(inf_loop_h) $(event_loop_h) $(event_top_h)
linux-thread-db.o: linux-thread-db.c $(defs_h) $(gdb_assert_h) \
$(gdb_proc_service_h) $(gdb_thread_db_h) $(bfd_h) $(exceptions_h) \
$(gdbthread_h) $(inferior_h) $(symfile_h) $(objfiles_h) $(target_h) \
Index: doc/ChangeLog
===================================================================
RCS file: /cvs/src/src/gdb/doc/ChangeLog,v
retrieving revision 1.757
diff -u -p -r1.757 ChangeLog
--- doc/ChangeLog 21 Mar 2008 15:02:37 -0000 1.757
+++ doc/ChangeLog 21 Mar 2008 17:05:55 -0000
@@ -1,3 +1,9 @@
+2008-03-21 Pedro Alves <pedro@codesourcery.com>
+
+ * gdb.texinfo (Debugging Output): Document
+ "set/show debug lin-lwp-async".
+ (Maintenance Commands): Document "maint set/show linux-async".
+
2008-03-21 Daniel Jacobowitz <dan@codesourcery.com>
* gdb.texinfo (Expressions): Update description of malloced arrays.
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.476
diff -u -p -r1.476 gdb.texinfo
--- doc/gdb.texinfo 21 Mar 2008 15:02:37 -0000 1.476
+++ doc/gdb.texinfo 21 Mar 2008 17:06:01 -0000
@@ -16407,6 +16407,12 @@ Displays the current state of @value{GDB
Turns on or off debugging messages from the Linux LWP debug support.
@item show debug lin-lwp
Show the current state of Linux LWP debugging messages.
+@item set debug lin-lwp-async
+@cindex @sc{gnu}/Linux LWP async debug messages
+@cindex Linux lightweight processes
+Turns on or off debugging messages from the Linux LWP async debug support.
+@item show debug lin-lwp-async
+Show the current state of Linux LWP async debugging messages.
@item set debug observer
@cindex observer debugging info
Turns on or off display of @value{GDBN} observer debugging. This
@@ -23242,6 +23248,16 @@ data in a @file{gmon.out} file, be sure
Configuring with @samp{--enable-profiling} arranges for @value{GDBN} to be
compiled with the @samp{-pg} compiler option.
+@kindex maint set linux-async
+@kindex maint show linux-async
+@cindex asynchronous support
+@item maint set linux-async
+@itemx maint show linux-async
+Control the GNU/Linux native asynchronous support of @value{GDBN}.
+
+GNU/Linux native asynchronous support will be disabled until you use
+the @samp{maint set linux-async} command to enable it.
+
@kindex maint show-debug-regs
@cindex x86 hardware debug registers
@item maint show-debug-regs
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-21 15:49 ` Daniel Jacobowitz
@ 2008-03-21 23:02 ` Nick Roberts
2008-03-22 1:25 ` Daniel Jacobowitz
0 siblings, 1 reply; 37+ messages in thread
From: Nick Roberts @ 2008-03-21 23:02 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: Pedro Alves, gdb-patches
> > My original test file mi-async.exp (attached below) works with your patch
> > when GDBFLAGS is toggled as above.
>
> Could you post a patch to add this? You can use -gdb-set to avoid
> adjusting GDBFLAGS,
I don't think so:
maintenance set linux-async on
^^^^^^^^^^^
and
(gdb) start
Breakpoint 1 at 0x804862c: file myprog.c, line 95.
Starting program: /home/nickrob/myprog
main (argc=1, argv=0xbfe3b904) at myprog.c:95
95 main (int argc, char **argv) {
(gdb) maintenance set linux-async on
Cannot change this setting while the inferior is running.
> and [isnative] && [istarget *-linux*] to limit
> it to platforms with the new command for now.
I will do this.
--
Nick http://www.inet.net.nz/~nickrob
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-21 23:02 ` Nick Roberts
@ 2008-03-22 1:25 ` Daniel Jacobowitz
2008-03-22 22:06 ` Nick Roberts
0 siblings, 1 reply; 37+ messages in thread
From: Daniel Jacobowitz @ 2008-03-22 1:25 UTC (permalink / raw)
To: Nick Roberts; +Cc: Pedro Alves, gdb-patches
On Sat, Mar 22, 2008 at 11:01:28AM +1200, Nick Roberts wrote:
> > > My original test file mi-async.exp (attached below) works with your patch
> > > when GDBFLAGS is toggled as above.
> >
> > Could you post a patch to add this? You can use -gdb-set to avoid
> > adjusting GDBFLAGS,
>
> I don't think so:
>
> maintenance set linux-async on
> ^^^^^^^^^^^
Whoops, I didn't think that one through. You're right.
> (gdb) start
> Breakpoint 1 at 0x804862c: file myprog.c, line 95.
> Starting program: /home/nickrob/myprog
> main (argc=1, argv=0xbfe3b904) at myprog.c:95
> 95 main (int argc, char **argv) {
> (gdb) maintenance set linux-async on
> Cannot change this setting while the inferior is running.
Right, gotta do it after gdb_start but before anything else.
> > and [isnative] && [istarget *-linux*] to limit
> > it to platforms with the new command for now.
>
> I will do this.
Thank you!
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-22 1:25 ` Daniel Jacobowitz
@ 2008-03-22 22:06 ` Nick Roberts
2008-04-01 14:00 ` Daniel Jacobowitz
2008-04-01 15:17 ` Vladimir Prus
0 siblings, 2 replies; 37+ messages in thread
From: Nick Roberts @ 2008-03-22 22:06 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: Pedro Alves, gdb-patches
> > (gdb) start
> > Breakpoint 1 at 0x804862c: file myprog.c, line 95.
> > Starting program: /home/nickrob/myprog
> > main (argc=1, argv=0xbfe3b904) at myprog.c:95
> > 95 main (int argc, char **argv) {
> > (gdb) maintenance set linux-async on
> > Cannot change this setting while the inferior is running.
>
> Right, gotta do it after gdb_start but before anything else.
OK. For some reason I was (mistakenly) thinking mi_gdb_start
started the inferior.
> > > and [isnative] && [istarget *-linux*] to limit
> > > it to platforms with the new command for now.
> >
> > I will do this.
>
> Thank you!
Attached below. I've added [board_info gdb_protocol] == "async" since
"target async" works after Vladimir's changes.
The tests for async output of CLI commands are really motivated by my interest
in such commands being entered from the GUD buffer in Emacs. There really
should be tests which have a general relevance to async mode, e.g., interrupt
(not documented in the manual) or -exec-interrupt but this would require a
different executable for the tests.
--
Nick http://www.inet.net.nz/~nickrob
# Copyright 2008 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Test asynchronous Machine Interface (MI) operations
#
# This currently only works with native linux and the async remote protocol.
if { !([isnative] && [istarget *-linux*]) \
&& [board_info gdb_protocol] != "async" } then {
return
}
# The plan is for async mode to become the default but toggle for now.
set saved_gdbflags $GDBFLAGS
set GDBFLAGS "-ex \"maint set linux-async on\""
load_lib mi-support.exp
gdb_exit
if [mi_gdb_start] {
continue
}
set testfile "basics"
set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile}
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DFAKEARGV}] != "" } {
untested mi-async.exp
return -1
}
mi_delete_breakpoints
mi_gdb_reinitialize_dir $srcdir/$subdir
mi_gdb_load ${binfile}
# mi_gdb_test cannot be used for asynchronous commands because there are
# two prompts involved and this can lead to a race condition.
proc linux_async_tests {} {
global mi_gdb_prompt
global hex
set line_main_head [gdb_get_line_number "main ("]
set line_main_body [expr $line_main_head + 2]
set line_main_next [expr $line_main_head + 3]
send_gdb "start\n"
gdb_expect {
-re ".*\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
gdb_expect {
-re "\\*stopped,thread-id=\"0\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_body\"\}\r\n$mi_gdb_prompt$" {
pass "Asynchronous response after start command"
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after start command (2)"
}
timeout {
fail "Asynchronous response after start command (timeout 2)"
}
}
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after start command (1)"
}
timeout {fail "Asynchronous response after start command (timeout 1)"}
}
send_gdb "next\n"
gdb_expect {
-re "\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
gdb_expect {
-re "\\*stopped,reason=\"end-stepping-range\",thread-id=\"0\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_next\"\}\r\n$mi_gdb_prompt$" {
pass "Asynchronous response after next command"
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after next command (2)"
}
timeout {
fail "Asynchronous response after next command (timeout 2)"
}
}
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after next command (1)"
}
timeout {fail "Asynchronous response after next command (timeout 1)"}
}
mi_gdb_test "-exec-interrupt" \
"" \
""
send_gdb "start\n"
gdb_expect {
-re ".*\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
gdb_expect {
-re "\\*stopped,thread-id=\"0\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_body\"\}\r\n$mi_gdb_prompt$" {
pass "Asynchronous response after (re) start"
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after (re) start (2)"
}
timeout {
fail "Asynchronous response after (re) start (timeout 2)"
}
}
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after (re) start (1)"
}
timeout {fail "Asynchronous response after (re) start (timeout 1)"}
}
}
#if [istarget "i386-*-linux-gnu"] then {
linux_async_tests
#}
mi_gdb_exit
set GDBFLAGS $saved_gdbflags
return 0
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-21 17:19 ` Pedro Alves
@ 2008-03-28 14:48 ` Maciej W. Rozycki
2008-03-28 16:07 ` Pedro Alves
0 siblings, 1 reply; 37+ messages in thread
From: Maciej W. Rozycki @ 2008-03-28 14:48 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
On Fri, 21 Mar 2008, Pedro Alves wrote:
> This is what I checked in after another round of testing
> on x86_64-unknown-linux-gnu.
Hmm, I have narrowed it down to be the reason of breakage when the
current head of the tree is built for the mipsisa32-sde-elf target. The
symptom is when you run GBD interactively (no arguments) and enter any
line for processing (a lone new-line character is enough), then the
program crashes with SIGSEGV (an infinite recursion, it would seem).
Reverting this commit makes GDB run as previously.
Just a heads-up, given it is trivial to reproduce and I fear I may have
no resources to debug it myself (sorry).
Maciej
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-28 14:48 ` Maciej W. Rozycki
@ 2008-03-28 16:07 ` Pedro Alves
2008-03-28 16:13 ` Daniel Jacobowitz
0 siblings, 1 reply; 37+ messages in thread
From: Pedro Alves @ 2008-03-28 16:07 UTC (permalink / raw)
To: gdb-patches; +Cc: Maciej W. Rozycki
[-- Attachment #1: Type: text/plain, Size: 1667 bytes --]
A Friday 28 March 2008 14:47:54, Maciej W. Rozycki wrote:
> On Fri, 21 Mar 2008, Pedro Alves wrote:
> > This is what I checked in after another round of testing
> > on x86_64-unknown-linux-gnu.
>
> Hmm, I have narrowed it down to be the reason of breakage when the
> current head of the tree is built for the mipsisa32-sde-elf target. The
> symptom is when you run GBD interactively (no arguments) and enter any
> line for processing (a lone new-line character is enough), then the
> program crashes with SIGSEGV (an infinite recursion, it would seem).
Yes, it's infinite recursion. Thanks for spotting that, and sorry for
giving you trouble.
The problem comes from the way we cope to the fact that
target_can_async_p is called in several places before the
runnable target is pushed on the stack. This particular case was
happening in execute_command.
Currently, if no target is yet pushed, we default
to look for the default run target, and check if that can async.
On a cross-configured gdb, without a native debugger, there's no
default run target, so find_default_run_target calls error. As if
this wasn't a problematic enough, throw_exception ends up calling
target_can_async_p, which leads to infinite recursion. I know
that Vladimir has a two patches that remove these two problematic
target_can_async_p calls (one that removes the
command_list_handler_continuation, the other that removes the
target_can_async_p call from throw_exception), but that still
leaves a lot of call in infcmd.c, that I'm not sure we'll be able
to remove.
The attached fixes the problem for me. We're not really
interested in calling error in this case.
--
Pedro Alves
[-- Attachment #2: fix_inf_recursion.diff --]
[-- Type: text/x-diff, Size: 2383 bytes --]
2008-03-28 Pedro Alves <pedro@codesourcery.com>
* target.c (find_default_run_target): All a NULL `do_mesg'
parameter. If it is NULL, don't call error.
(find_default_can_async_p, find_default_is_async_p): Pass NULL as
`do_mesg'parameter to find_default_run_target. If no target was
found, return 0.
---
gdb/target.c | 26 ++++++++++++++++++++------
1 file changed, 20 insertions(+), 6 deletions(-)
Index: src/gdb/target.c
===================================================================
--- src.orig/gdb/target.c 2008-03-28 15:16:23.000000000 +0000
+++ src/gdb/target.c 2008-03-28 15:50:54.000000000 +0000
@@ -1782,7 +1782,8 @@ The \"%s\" target does not support \"run
execute a run or attach command without any other data. This is
used to locate the default process stratum.
- Result is always valid (error() is called for errors). */
+ If DO_MESG is not NULL, the result is always valid (error() is
+ called for errors); else, return NULL on error. */
static struct target_ops *
find_default_run_target (char *do_mesg)
@@ -1804,7 +1805,12 @@ find_default_run_target (char *do_mesg)
}
if (count != 1)
- error (_("Don't know how to %s. Try \"help target\"."), do_mesg);
+ {
+ if (do_mesg)
+ error (_("Don't know how to %s. Try \"help target\"."), do_mesg);
+ else
+ return NULL;
+ }
return runable;
}
@@ -1835,8 +1841,12 @@ find_default_can_async_p (void)
{
struct target_ops *t;
- t = find_default_run_target ("async");
- if (t->to_can_async_p)
+ /* This may be called before the target is pushed on the stack;
+ look for the default process stratum. If there's none, gdb isn't
+ configured with a native debugger, and target remote isn't
+ connected yet. */
+ t = find_default_run_target (NULL);
+ if (t && t->to_can_async_p)
return (t->to_can_async_p) ();
return 0;
}
@@ -1846,8 +1856,12 @@ find_default_is_async_p (void)
{
struct target_ops *t;
- t = find_default_run_target ("async");
- if (t->to_is_async_p)
+ /* This may be called before the target is pushed on the stack;
+ look for the default process stratum. If there's none, gdb isn't
+ configured with a native debugger, and target remote isn't
+ connected yet. */
+ t = find_default_run_target (NULL);
+ if (t && t->to_is_async_p)
return (t->to_is_async_p) ();
return 0;
}
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-28 16:07 ` Pedro Alves
@ 2008-03-28 16:13 ` Daniel Jacobowitz
2008-03-28 16:40 ` Pedro Alves
0 siblings, 1 reply; 37+ messages in thread
From: Daniel Jacobowitz @ 2008-03-28 16:13 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches, Maciej W. Rozycki
On Fri, Mar 28, 2008 at 04:06:58PM +0000, Pedro Alves wrote:
> 2008-03-28 Pedro Alves <pedro@codesourcery.com>
>
> * target.c (find_default_run_target): All a NULL `do_mesg'
> parameter. If it is NULL, don't call error.
> (find_default_can_async_p, find_default_is_async_p): Pass NULL as
> `do_mesg'parameter to find_default_run_target. If no target was
> found, return 0.
OK.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-28 16:13 ` Daniel Jacobowitz
@ 2008-03-28 16:40 ` Pedro Alves
0 siblings, 0 replies; 37+ messages in thread
From: Pedro Alves @ 2008-03-28 16:40 UTC (permalink / raw)
To: gdb-patches; +Cc: Maciej W. Rozycki
A Friday 28 March 2008 16:13:06, Daniel Jacobowitz wrote:
> OK.
Thanks. It's in now with the following untypoed ChangeLog entry.
2008-03-28 Pedro Alves <pedro@codesourcery.com>
* target.c (find_default_run_target): Allow a NULL `do_mesg'
parameter. If it is NULL, don't call error.
(find_default_can_async_p, find_default_is_async_p): Pass NULL as
`do_mesg' parameter to find_default_run_target. If no target was
found, return 0.
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-22 22:06 ` Nick Roberts
@ 2008-04-01 14:00 ` Daniel Jacobowitz
2008-04-01 15:17 ` Vladimir Prus
1 sibling, 0 replies; 37+ messages in thread
From: Daniel Jacobowitz @ 2008-04-01 14:00 UTC (permalink / raw)
To: Nick Roberts; +Cc: Pedro Alves, gdb-patches
On Sun, Mar 23, 2008 at 10:05:42AM +1200, Nick Roberts wrote:
> Attached below. I've added [board_info gdb_protocol] == "async" since
> "target async" works after Vladimir's changes.
> # This currently only works with native linux and the async remote protocol.
> if { !([isnative] && [istarget *-linux*]) \
> && [board_info gdb_protocol] != "async" } then {
> return
> }
It has to be [board_info target gdb_protocol] or [target_info
gdb_protocol]. Actually, this:
if { !([isnative] && [istarget *-linux*]) \
&& (![target_info exists gdb_protocol] \
|| [target_info gdb_protocol] != "async") } then {
> # The plan is for async mode to become the default but toggle for now.
> set saved_gdbflags $GDBFLAGS
> set GDBFLAGS "-ex \"maint set linux-async on\""
Please append to GDBFLAGS instead of setting it; otherwise you'll lose
-nx and it will read $HOME/.gdbinit.
> #if [istarget "i386-*-linux-gnu"] then {
> linux_async_tests
> #}
Remove the commented out lines.
Otherwise OK. Thanks for posting this.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-03-22 22:06 ` Nick Roberts
2008-04-01 14:00 ` Daniel Jacobowitz
@ 2008-04-01 15:17 ` Vladimir Prus
2008-04-01 20:09 ` Nick Roberts
1 sibling, 1 reply; 37+ messages in thread
From: Vladimir Prus @ 2008-04-01 15:17 UTC (permalink / raw)
To: gdb-patches
Nick Roberts wrote:
> #
> # Test asynchronous Machine Interface (MI) operations
> #
>
> # This currently only works with native linux and the async remote protocol.
> if { !([isnative] && [istarget *-linux*]) \
> Â Â Â && [board_info gdb_protocol] != "async" } then {
> Â return
> }
>
> # The plan is for async mode to become the default but toggle for now.
> set saved_gdbflags $GDBFLAGS
> set GDBFLAGS "-ex \"maint set linux-async on\""
>
> load_lib mi-support.exp
>
> gdb_exit
> if [mi_gdb_start] {
> Â Â continue
> }
>
> set testfile "basics"
> set srcfile ${testfile}.c
> set binfile ${objdir}/${subdir}/${testfile}
> if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug
additional_flags=-DFAKEARGV}] != "" } {
> Â Â Â untested mi-async.exp
> Â Â Â return -1
> }
>
> mi_delete_breakpoints
> mi_gdb_reinitialize_dir $srcdir/$subdir
> mi_gdb_load ${binfile}
>
> # mi_gdb_test cannot be used for asynchronous commands because there are
> # two prompts involved and this can lead to a race condition.
>
> proc linux_async_tests {} {
> Â Â global mi_gdb_prompt
> Â Â global hex
>
>   set line_main_head   [gdb_get_line_number "main ("]
>   set line_main_body   [expr $line_main_head + 2]
>   set line_main_next   [expr $line_main_head + 3]
>
> Â Â send_gdb "start\n"
> Â Â gdb_expect {
> Â Â Â Â Â Â Â Â -re ".*\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
I don't think this is right. MI is (supposed to be) extensible. In particular, new async
notification may be added. The above will break if any notification will be emitted
between ^running and ^done and the prompt.
> Â Â Â Â Â Â Â Â Â Â gdb_expect {
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â -re "\\*stopped,thread-id=\"0\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_body\"\}\r\n$mi_gdb_prompt$"
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â {
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â pass "Asynchronous response after start command"
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â }
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â -re ".*$mi_gdb_prompt$" {
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â fail "Asynchronous response after start command (2)"
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â }
>                 timeout {
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â fail "Asynchronous response after start command (timeout 2)"
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â }
> Â Â Â Â Â Â Â Â Â Â }
> Â Â Â Â Â Â Â Â }
> Â Â Â Â Â Â Â Â -re ".*$mi_gdb_prompt$" {
> Â Â Â Â Â Â Â Â Â Â fail "Asynchronous response after start command (1)"
> Â Â Â Â Â Â Â Â }
> Â Â Â Â Â Â Â Â timeout {fail "Asynchronous response after start command (timeout 1)"}
> Â Â }
>
> Â Â send_gdb "next\n"
> Â Â gdb_expect {
> Â Â Â Â Â Â Â Â -re "\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
> Â Â Â Â Â Â Â Â Â Â gdb_expect {
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â -re "\\*stopped,reason=\"end-stepping-range\",thread-id=\"0\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_next\"\}\r\n$mi_gdb_prompt$"
{
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â pass "Asynchronous response after next command"
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â }
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â -re ".*$mi_gdb_prompt$" {
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â fail "Asynchronous response after next command (2)"
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â }
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â timeout {
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â fail "Asynchronous response after next command (timeout 2)"
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â }
> Â Â Â Â Â Â Â Â Â Â }
> Â Â Â Â Â Â Â Â }
It appears there's a lot of duplicated logic above. Can we please have factor it out into a helper
function? This is not for the sake of abstract clarify -- I've already spend hours updating the
current MI testsuite for non-stop mode.
- Volodya
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-04-01 15:17 ` Vladimir Prus
@ 2008-04-01 20:09 ` Nick Roberts
2008-04-04 12:34 ` Vladimir Prus
0 siblings, 1 reply; 37+ messages in thread
From: Nick Roberts @ 2008-04-01 20:09 UTC (permalink / raw)
To: Vladimir Prus; +Cc: gdb-patches
> > send_gdb "start\n"
> > gdb_expect {
> > -re ".*\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
>
> I don't think this is right. MI is (supposed to be) extensible. In particular, new async
> notification may be added. The above will break if any notification will be emitted
> between ^running and ^done and the prompt.
I try to make Emacs (the frontend) as tolerant as possible to changes in MI
output, in the hope that future changes, e.g., new fields don't break released
versions of Emacs. This is a test, however, and it's purpose *is* to detect
changes in Gdb output. If they are undesirable changes then it's done its job
and detected a regression. If the changes are deliberate, we update the test.
There's no way to know, in advance, which type the change is and the danger of
making the regexp too general is that we miss a real failure.
> > ...
> > send_gdb "next\n"
> > gdb_expect {
> > -re "\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
> > gdb_expect {
> > -re "\\*stopped,reason=\"end-stepping-range\",thread-id=\"0\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_next\"\}\r\n$mi_gdb_prompt$"
> {
> > pass "Asynchronous response after next command"
> > }
> > -re ".*$mi_gdb_prompt$" {
> > fail "Asynchronous response after next command (2)"
> > }
> > timeout {
> > fail "Asynchronous response after next command (timeout 2)"
> > }
> > }
> > }
>
> It appears there's a lot of duplicated logic above. Can we please have
> factor it out into a helper function? This is not for the sake of abstract
> clarify -- I've already spend hours updating the current MI testsuite for
> non-stop mode.
The helper functions that you have written for common sequences are a good
idea as they need only be updated in one place but the three here are the
only ones of their kind (they output an extra "^done" over MI commands like
-exec-next). Not really worth a dedicated helper function do you agree?
--
Nick http://www.inet.net.nz/~nickrob
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-04-01 20:09 ` Nick Roberts
@ 2008-04-04 12:34 ` Vladimir Prus
2008-04-05 17:20 ` Nick Roberts
0 siblings, 1 reply; 37+ messages in thread
From: Vladimir Prus @ 2008-04-04 12:34 UTC (permalink / raw)
To: Nick Roberts; +Cc: gdb-patches
On Tuesday 01 April 2008 23:40:22 you wrote:
> > > send_gdb "start\n"
> > > gdb_expect {
> > > -re ".*\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
> >
> > I don't think this is right. MI is (supposed to be) extensible. In particular, new async
> > notification may be added. The above will break if any notification will be emitted
> > between ^running and ^done and the prompt.
>
> I try to make Emacs (the frontend) as tolerant as possible to changes in MI
> output, in the hope that future changes, e.g., new fields don't break released
> versions of Emacs. This is a test, however, and it's purpose *is* to detect
> changes in Gdb output. If they are undesirable changes then it's done its job
> and detected a regression.
By definition, MI is extensible, and a test for feature X should only fail if feature
X got broken, not when new fields or notification are added by feature Y. We already
had "but how can we update all MI testsuite for new field" discussions here.
> If the changes are deliberate, we update the test.
> There's no way to know, in advance, which type the change is and the danger of
> making the regexp too general is that we miss a real failure.
>
> > > ...
> > > send_gdb "next\n"
> > > gdb_expect {
> > > -re "\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
> > > gdb_expect {
> > > -re "\\*stopped,reason=\"end-stepping-range\",thread-id=\"0\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_next\"\}\r\n$mi_gdb_prompt$"
> > {
> > > pass "Asynchronous response after next command"
> > > }
> > > -re ".*$mi_gdb_prompt$" {
> > > fail "Asynchronous response after next command (2)"
> > > }
> > > timeout {
> > > fail "Asynchronous response after next command (timeout 2)"
> > > }
> > > }
> > > }
> >
> > It appears there's a lot of duplicated logic above. Can we please have
> > factor it out into a helper function? This is not for the sake of abstract
> > clarify -- I've already spend hours updating the current MI testsuite for
> > non-stop mode.
>
> The helper functions that you have written for common sequences are a good
> idea as they need only be updated in one place but the three here are the
> only ones of their kind (they output an extra "^done" over MI commands like
> -exec-next). Not really worth a dedicated helper function do you agree?
Why there's extra "^done"? Presently, each command is supposed to have
either "^running" or "^done", not both. I realize that fixing this requires
introducing "*running", so that we still know that the target is resumed.
I have the patch for *running basically done, so I suggest that I submit it,
and then we'll have just ^done in this example.
BTW, I'm not even sure that "^done" vs. "^running" + "^done" is so big
difference that a helper function cannot be introduced.
- Volodya
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-04-04 12:34 ` Vladimir Prus
@ 2008-04-05 17:20 ` Nick Roberts
2008-04-05 22:07 ` Vladimir Prus
0 siblings, 1 reply; 37+ messages in thread
From: Nick Roberts @ 2008-04-05 17:20 UTC (permalink / raw)
To: Vladimir Prus; +Cc: gdb-patches
> > The helper functions that you have written for common sequences are a good
> > idea as they need only be updated in one place but the three here are the
> > only ones of their kind (they output an extra "^done" over MI commands like
> > -exec-next). Not really worth a dedicated helper function do you agree?
>
> Why there's extra "^done"? Presently, each command is supposed to have
> either "^running" or "^done", not both.
There's an extra "^done" because this is a CLI command entered in MI (and
therfore case CLI_COMMAND: of captured_mi_execute_command). There are many
issues here like should we diallow immediate use of CLI commands now and
require explicit use of "-interpreter-exec console"? Then "-interpreter-exec
console next" doesn't emit "(gdb)/n" after "^running" when in asynchronous
mode, so should we remove it from synchronous mode too (as you have suggested)?
But these are all MI issues and this test is meant to just be a mark in the
sand for asynchronous mode, and one that I had lying around. For the moment, I
don't really want to work on it further
> I realize that fixing this requires
> introducing "*running", so that we still know that the target is resumed.
> I have the patch for *running basically done, so I suggest that I submit it,
> and then we'll have just ^done in this example.
I'm not sure what the output will look like after your change but it may
still not be like normal MI output.
> BTW, I'm not even sure that "^done" vs. "^running" + "^done" is so big
> difference that a helper function cannot be introduced.
The comment suggests that mi_gdb_test will not work but I have not revisited
that since writing the test 18 months or so ago. If true, that could also
make helper functions difficult.
--
Nick http://www.inet.net.nz/~nickrob
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-04-05 17:20 ` Nick Roberts
@ 2008-04-05 22:07 ` Vladimir Prus
2008-04-07 0:06 ` Nick Roberts
2008-04-07 2:33 ` Nick Roberts
0 siblings, 2 replies; 37+ messages in thread
From: Vladimir Prus @ 2008-04-05 22:07 UTC (permalink / raw)
To: Nick Roberts; +Cc: gdb-patches
On Saturday 05 April 2008 13:10:53 Nick Roberts wrote:
> > > The helper functions that you have written for common sequences are a good
> > > idea as they need only be updated in one place but the three here are the
> > > only ones of their kind (they output an extra "^done" over MI commands like
> > > -exec-next). Not really worth a dedicated helper function do you agree?
> >
> > Why there's extra "^done"? Presently, each command is supposed to have
> > either "^running" or "^done", not both.
>
> There's an extra "^done" because this is a CLI command entered in MI (and
> therfore case CLI_COMMAND: of captured_mi_execute_command). There are many
> issues here like should we diallow immediate use of CLI commands now and
> require explicit use of "-interpreter-exec console"?
Probably not -- I don't see any immediate advantage from that.
> Then "-interpreter-exec
> console next" doesn't emit "(gdb)/n" after "^running" when in asynchronous
> mode, so should we remove it from synchronous mode too (as you have suggested)?
This prompting is fairly confusing, so it could be that I've confused myself
as well, but I think "-interpreter-exec console next" actually *should* print
the prompt if the target is async-capable. The reason is that for MI mode,
we don't really care about terminal ownership issues, and then should not
care about sync_execution, and then this bit of code:
else if (sync_execution)
{
/* Don't print the prompt. We are executing the target in
synchronous mode. */
args->action = EXECUTE_COMMAND_SUPPRESS_PROMPT;
return;
}
in captured_mi_execute_command should be removed. In fact, I have a patch-in-progress
to do exactly that, and hopefully will be posting it later today.
> But these are all MI issues and this test is meant to just be a mark in the
> sand for asynchronous mode, and one that I had lying around. For the moment, I
> don't really want to work on it further
Okay :-) Then, will you mind if I apply this patch either when I'm done with
another round of MI testsuite cleanups I'm doing today, or after the ^done is gone,
whichever proves more convenient?
> > BTW, I'm not even sure that "^done" vs. "^running" + "^done" is so big
> > difference that a helper function cannot be introduced.
>
> The comment suggests that mi_gdb_test will not work but I have not revisited
> that since writing the test 18 months or so ago. If true, that could also
> make helper functions difficult.
Do you happen to remember what race condition the comment mentions?
- Volodya
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-04-05 22:07 ` Vladimir Prus
@ 2008-04-07 0:06 ` Nick Roberts
2008-04-07 2:33 ` Nick Roberts
1 sibling, 0 replies; 37+ messages in thread
From: Nick Roberts @ 2008-04-07 0:06 UTC (permalink / raw)
To: Vladimir Prus; +Cc: gdb-patches
> > There's an extra "^done" because this is a CLI command entered in MI (and
> > therfore case CLI_COMMAND: of captured_mi_execute_command). There are many
> > issues here like should we diallow immediate use of CLI commands now and
> > require explicit use of "-interpreter-exec console"?
>
> Probably not -- I don't see any immediate advantage from that.
The node "GDB/MI Compatibility with CLI" explains why.
> > Then "-interpreter-exec
> > console next" doesn't emit "(gdb)/n" after "^running" when in asynchronous
> > mode, so should we remove it from synchronous mode too (as you have suggested)?
>
> This prompting is fairly confusing, so it could be that I've confused myself
> as well, but I think "-interpreter-exec console next" actually *should* print
> the prompt if the target is async-capable. The reason is that for MI mode,
> we don't really care about terminal ownership issues, and then should not
> care about sync_execution, and then this bit of code:
>
> else if (sync_execution)
> {
> /* Don't print the prompt. We are executing the target in
> synchronous mode. */
> args->action = EXECUTE_COMMAND_SUPPRESS_PROMPT;
> return;
> }
>
> in captured_mi_execute_command should be removed. In fact, I have a
> patch-in-progress to do exactly that, and hopefully will be posting it later
> today.
OK.
> > But these are all MI issues and this test is meant to just be a mark in the
> > sand for asynchronous mode, and one that I had lying around. For the moment, I
> > don't really want to work on it further
>
> Okay :-) Then, will you mind if I apply this patch either when I'm done with
> another round of MI testsuite cleanups I'm doing today, or after the ^done
> is gone, whichever proves more convenient?
I'll commit this as is then. Then, of course, you can decide what changes you
wish to make. Meanwhile I'll try to come up with some tests for floating
variable objects which will be more efficient use of my time.
> > > BTW, I'm not even sure that "^done" vs. "^running" + "^done" is so big
> > > difference that a helper function cannot be introduced.
> >
> > The comment suggests that mi_gdb_test will not work but I have not revisited
> > that since writing the test 18 months or so ago. If true, that could also
> > make helper functions difficult.
>
> Do you happen to remember what race condition the comment mentions?
I must have copied the comment from mi-simplerun.exp and I think I found it
necessary with my asynchronous branch, although I can't be sure, and it may
not be be necessary with Pedro's version (.i.e. mainline).
--
Nick http://www.inet.net.nz/~nickrob
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: linux native async mode support
2008-04-05 22:07 ` Vladimir Prus
2008-04-07 0:06 ` Nick Roberts
@ 2008-04-07 2:33 ` Nick Roberts
1 sibling, 0 replies; 37+ messages in thread
From: Nick Roberts @ 2008-04-07 2:33 UTC (permalink / raw)
To: Vladimir Prus; +Cc: gdb-patches
> > But these are all MI issues and this test is meant to just be a mark in
> > the sand for asynchronous mode, and one that I had lying around. For the
> > moment, I don't really want to work on it further
>
> Okay :-)
This is what I've checked in.
--
Nick http://www.inet.net.nz/~nickrob
2008-04-07 Nick Roberts <nickrob@snap.net.nz>
* gdb.mi/mi-async.exp: New test for asynchronous Machine
Interface (MI) responses.
# Copyright 2008 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Test asynchronous Machine Interface (MI) responses.
#
# This currently only works with native linux and the async remote protocol.
if { !([isnative] && [istarget *-linux*]) \
&& (![target_info exists gdb_protocol] \
|| [target_info gdb_protocol] != "async") } then {
return
}
# The plan is for async mode to become the default but toggle for now.
set saved_gdbflags $GDBFLAGS
set GDBFLAGS [concat $GDBFLAGS " -ex \"maint set linux-async on\""]
load_lib mi-support.exp
gdb_exit
if [mi_gdb_start] {
continue
}
set testfile "basics"
set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile}
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DFAKEARGV}] != "" } {
untested mi-async.exp
return -1
}
mi_delete_breakpoints
mi_gdb_reinitialize_dir $srcdir/$subdir
mi_gdb_load ${binfile}
# mi_gdb_test cannot be used for asynchronous commands because there are
# two prompts involved and this can lead to a race condition.
proc linux_async_tests {} {
global mi_gdb_prompt
global hex
set line_main_head [gdb_get_line_number "main ("]
set line_main_body [expr $line_main_head + 2]
set line_main_next [expr $line_main_head + 3]
send_gdb "start\n"
gdb_expect {
-re ".*\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
gdb_expect {
-re "\\*stopped,thread-id=\"0|1\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_body\"\}\r\n$mi_gdb_prompt$" {
pass "Asynchronous response after start command"
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after start command (2)"
}
timeout {
fail "Asynchronous response after start command (timeout 2)"
}
}
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after start command (1)"
}
timeout {fail "Asynchronous response after start command (timeout 1)"}
}
send_gdb "next\n"
gdb_expect {
-re "\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
gdb_expect {
-re "\\*stopped,reason=\"end-stepping-range\",thread-id=\"0|1\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_next\"\}\r\n$mi_gdb_prompt$" {
pass "Asynchronous response after next command"
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after next command (2)"
}
timeout {
fail "Asynchronous response after next command (timeout 2)"
}
}
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after next command (1)"
}
timeout {fail "Asynchronous response after next command (timeout 1)"}
}
mi_gdb_test "-exec-interrupt" \
"" \
""
send_gdb "start\n"
gdb_expect {
-re ".*\\^running\r\n\\^done\r\n$mi_gdb_prompt" {
gdb_expect {
-re "\\*stopped,thread-id=\"0|1\",frame=\{addr=\"$hex\",func=\"main\",args=\\\[\\\],file=\".*basics.c\",line=\"$line_main_body\"\}\r\n$mi_gdb_prompt$" {
pass "Asynchronous response after (re) start"
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after (re) start (2)"
}
timeout {
fail "Asynchronous response after (re) start (timeout 2)"
}
}
}
-re ".*$mi_gdb_prompt$" {
fail "Asynchronous response after (re) start (1)"
}
timeout {fail "Asynchronous response after (re) start (timeout 1)"}
}
}
linux_async_tests
mi_gdb_exit
set GDBFLAGS $saved_gdbflags
return 0
^ permalink raw reply [flat|nested] 37+ messages in thread
end of thread, other threads:[~2008-04-06 22:46 UTC | newest]
Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-03-14 8:11 linux native async mode support Pedro Alves
2008-03-14 21:17 ` Daniel Jacobowitz
2008-03-17 16:05 ` Pedro Alves
2008-03-17 22:05 ` Daniel Jacobowitz
2008-03-18 23:27 ` Pedro Alves
2008-03-18 23:58 ` Daniel Jacobowitz
2008-03-21 15:55 ` Daniel Jacobowitz
2008-03-21 17:19 ` Pedro Alves
2008-03-28 14:48 ` Maciej W. Rozycki
2008-03-28 16:07 ` Pedro Alves
2008-03-28 16:13 ` Daniel Jacobowitz
2008-03-28 16:40 ` Pedro Alves
2008-03-18 0:06 ` Nick Roberts
2008-03-18 23:28 ` Pedro Alves
2008-03-19 3:59 ` Nick Roberts
2008-03-19 16:25 ` Luis Machado
2008-03-19 23:19 ` Pedro Alves
2008-03-19 23:26 ` Pedro Alves
2008-03-20 1:58 ` Nick Roberts
2008-03-21 15:47 ` Daniel Jacobowitz
2008-03-21 15:49 ` Daniel Jacobowitz
2008-03-21 23:02 ` Nick Roberts
2008-03-22 1:25 ` Daniel Jacobowitz
2008-03-22 22:06 ` Nick Roberts
2008-04-01 14:00 ` Daniel Jacobowitz
2008-04-01 15:17 ` Vladimir Prus
2008-04-01 20:09 ` Nick Roberts
2008-04-04 12:34 ` Vladimir Prus
2008-04-05 17:20 ` Nick Roberts
2008-04-05 22:07 ` Vladimir Prus
2008-04-07 0:06 ` Nick Roberts
2008-04-07 2:33 ` Nick Roberts
2008-03-18 2:47 ` Nick Roberts
2008-03-14 23:10 ` Nick Roberts
2008-03-15 1:58 ` Pedro Alves
2008-03-15 3:11 ` Daniel Jacobowitz
2008-03-17 23:41 ` Nick Roberts
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox