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