fbsd-nat: Implement async target support.

This is a fairly simple version of async target support.

Synchronous mode still uses blocking waitpid() calls in
inf_ptrace::wait() unlike the Linux native target which always uses
WNOHANG and uses sigsuspend() for synchronous operation.

Asynchronous mode registers an event pipe with the core as a file
handle and writes to the pipe when SIGCHLD is raised.  TARGET_WNOHANG
is handled by inf_ptrace::wait().
This commit is contained in:
John Baldwin 2022-02-22 11:22:14 -08:00
parent ca81b5334e
commit 9385df2a58
2 changed files with 164 additions and 2 deletions

View file

@ -18,7 +18,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "gdbsupport/block-signals.h"
#include "gdbsupport/byte-vector.h"
#include "gdbsupport/event-loop.h"
#include "gdbsupport/event-pipe.h"
#include "gdbcore.h"
#include "inferior.h"
#include "regcache.h"
@ -28,6 +31,7 @@
#include "gdbthread.h"
#include "gdbsupport/buildargv.h"
#include "gdbsupport/gdb_wait.h"
#include "inf-loop.h"
#include "inf-ptrace.h"
#include <sys/types.h>
#ifdef HAVE_SYS_PROCCTL_H
@ -926,6 +930,114 @@ fbsd_nat_target::update_thread_list ()
#endif
}
/* Async mode support. */
static event_pipe fbsd_nat_event_pipe;
/* Implement the "can_async_p" target method. */
bool
fbsd_nat_target::can_async_p ()
{
/* This flag should be checked in the common target.c code. */
gdb_assert (target_async_permitted);
/* Otherwise, this targets is always able to support async mode. */
return true;
}
/* Implement the "is_async_p" target method. */
bool
fbsd_nat_target::is_async_p ()
{
return fbsd_nat_event_pipe.is_open ();
}
/* Implement the "async_wait_fd" target method. */
int
fbsd_nat_target::async_wait_fd ()
{
return fbsd_nat_event_pipe.event_fd ();
}
/* SIGCHLD handler notifies the event-loop in async mode. */
static void
sigchld_handler (int signo)
{
int old_errno = errno;
if (fbsd_nat_event_pipe.is_open ())
fbsd_nat_event_pipe.mark ();
errno = old_errno;
}
/* Callback registered with the target events file descriptor. */
static void
handle_target_event (int error, gdb_client_data client_data)
{
inferior_event_handler (INF_REG_EVENT);
}
/* Implement the "async" target method. */
void
fbsd_nat_target::async (int enable)
{
if ((enable != 0) == is_async_p ())
return;
/* Block SIGCHILD while we create/destroy the pipe, as the handler
writes to it. */
gdb::block_signals blocker;
if (enable)
{
if (!fbsd_nat_event_pipe.open ())
internal_error (__FILE__, __LINE__, "failed to create event pipe.");
add_file_handler (fbsd_nat_event_pipe.event_fd (),
handle_target_event, NULL, "fbsd-nat");
/* Trigger a poll in case there are pending events to
handle. */
fbsd_nat_event_pipe.mark ();
}
else
{
delete_file_handler (fbsd_nat_event_pipe.event_fd ());
fbsd_nat_event_pipe.close ();
}
}
/* Implement the "close" target method. */
void
fbsd_nat_target::close ()
{
if (is_async_p ())
async (0);
inf_ptrace_target::close ();
}
/* Implement the "attach" target method. */
void
fbsd_nat_target::attach (const char *args, int from_tty)
{
inf_ptrace_target::attach (args, from_tty);
/* Curiously, the core does not do this automatically. */
if (target_can_async_p ())
target_async (1);
}
#ifdef TDP_RFPPWAIT
/*
To catch fork events, PT_FOLLOW_FORK is set on every traced process
@ -997,6 +1109,11 @@ static void
fbsd_add_vfork_done (ptid_t pid)
{
fbsd_pending_vfork_done.push_front (pid);
/* If we're in async mode, need to tell the event loop there's
something here to process. */
if (target_is_async_p ())
fbsd_nat_event_pipe.mark ();
}
/* Check for a pending vfork done event for a specific PID. */
@ -1165,8 +1282,8 @@ fbsd_handle_debug_trap (fbsd_nat_target *target, ptid_t ptid,
the status in *OURSTATUS. */
ptid_t
fbsd_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
target_wait_flags target_options)
fbsd_nat_target::wait_1 (ptid_t ptid, struct target_waitstatus *ourstatus,
target_wait_flags target_options)
{
ptid_t wptid;
@ -1381,6 +1498,36 @@ fbsd_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
}
}
ptid_t
fbsd_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
target_wait_flags target_options)
{
ptid_t wptid;
fbsd_nat_debug_printf ("[%s], [%s]", target_pid_to_str (ptid).c_str (),
target_options_to_string (target_options).c_str ());
/* Ensure any subsequent events trigger a new event in the loop. */
if (is_async_p ())
fbsd_nat_event_pipe.flush ();
wptid = wait_1 (ptid, ourstatus, target_options);
/* If we are in async mode and found an event, there may still be
another event pending. Trigger the event pipe so that that the
event loop keeps polling until no event is returned. */
if (is_async_p ()
&& ((ourstatus->kind () != TARGET_WAITKIND_IGNORE
&& ourstatus->kind() != TARGET_WAITKIND_NO_RESUMED)
|| ptid != minus_one_ptid))
fbsd_nat_event_pipe.mark ();
fbsd_nat_debug_printf ("returning [%s], [%s]",
target_pid_to_str (wptid).c_str (),
ourstatus->to_string ().c_str ());
return wptid;
}
#ifdef USE_SIGTRAP_SIGINFO
/* Implement the "stopped_by_sw_breakpoint" target_ops method. */
@ -1679,4 +1826,7 @@ Enables printf debugging output."),
NULL,
&show_fbsd_nat_debug,
&setdebuglist, &showdebuglist);
/* Install a SIGCHLD handler. */
signal (SIGCHLD, sigchld_handler);
}

View file

@ -66,9 +66,19 @@ public:
void update_thread_list () override;
bool can_async_p () override;
bool is_async_p () override;
int async_wait_fd () override;
void async (int) override;
void close () override;
thread_control_capabilities get_thread_control_capabilities () override
{ return tc_schedlock; }
void attach (const char *, int) override;
void create_inferior (const char *, const std::string &,
char **, int) override;
@ -110,6 +120,8 @@ protected:
void post_startup_inferior (ptid_t) override;
private:
ptid_t wait_1 (ptid_t, struct target_waitstatus *, target_wait_flags);
/* Helper routines for use in fetch_registers and store_registers in
subclasses. These routines fetch and store a single set of
registers described by REGSET. The REGSET's 'regmap' field must