binutils-gdb/gdb/async-event.c

373 lines
11 KiB
C
Raw Normal View History

/* Async events for the GDB event loop.
Copyright (C) 1999-2023 Free Software Foundation, Inc.
This file is part of GDB.
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 3 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, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "async-event.h"
#include "ser-event.h"
#include "top.h"
#include "ui.h"
/* PROC is a function to be invoked when the READY flag is set. This
happens when there has been a signal and the corresponding signal
handler has 'triggered' this async_signal_handler for execution.
The actual work to be done in response to a signal will be carried
out by PROC at a later time, within process_event. This provides a
deferred execution of signal handlers.
Async_init_signals takes care of setting up such an
async_signal_handler for each interesting signal. */
struct async_signal_handler
{
/* If ready, call this handler from the main event loop, using
invoke_async_handler. */
int ready;
/* Pointer to next handler. */
struct async_signal_handler *next_handler;
/* Function to call to do the work. */
sig_handler_func *proc;
/* Argument to PROC. */
gdb_client_data client_data;
/* User-friendly name of this handler. */
const char *name;
};
/* PROC is a function to be invoked when the READY flag is set. This
happens when the event has been marked with
MARK_ASYNC_EVENT_HANDLER. The actual work to be done in response
to an event will be carried out by PROC at a later time, within
process_event. This provides a deferred execution of event
handlers. */
struct async_event_handler
{
/* If ready, call this handler from the main event loop, using
invoke_event_handler. */
int ready;
/* Pointer to next handler. */
struct async_event_handler *next_handler;
/* Function to call to do the work. */
async_event_handler_func *proc;
/* Argument to PROC. */
gdb_client_data client_data;
/* User-friendly name of this handler. */
const char *name;
};
/* All the async_signal_handlers gdb is interested in are kept onto
this list. */
static struct
{
/* Pointer to first in handler list. */
async_signal_handler *first_handler;
/* Pointer to last in handler list. */
async_signal_handler *last_handler;
}
sighandler_list;
/* All the async_event_handlers gdb is interested in are kept onto
this list. */
static struct
{
/* Pointer to first in handler list. */
async_event_handler *first_handler;
/* Pointer to last in handler list. */
async_event_handler *last_handler;
}
async_event_handler_list;
/* This event is signalled whenever an asynchronous handler needs to
defer an action to the event loop. */
static struct serial_event *async_signal_handlers_serial_event;
/* Callback registered with ASYNC_SIGNAL_HANDLERS_SERIAL_EVENT. */
static void
async_signals_handler (int error, gdb_client_data client_data)
{
/* Do nothing. Handlers are run by invoke_async_signal_handlers
from instead. */
}
void
initialize_async_signal_handlers (void)
{
async_signal_handlers_serial_event = make_serial_event ();
add_file_handler (serial_event_fd (async_signal_handlers_serial_event),
gdb: give names to event loop file handlers Assign names to event loop file handlers. They will be used in debug messages when file handlers are invoked. In GDB, each UI used to get its own unique number, until commit cbe256847e19 ("Remove ui::num"). Re-introduce this field, and use it to make a unique name for the handler. I'm not too sure what goes on in ser-base.c, all I know is that it's what is used when debugging remotely. I've just named the main handler "serial". It would be good to have unique names there too. For instance when debugging with two different remote connections, we'd ideally want the handlers to have unique names. I didn't do it in this patch though. gdb/ChangeLog: * async-event.c (initialize_async_signal_handlers): Pass name to add_file_handler * event-top.c (ui_register_input_event_handler): Likewise. * linux-nat.c (linux_nat_target::async): Likewise. * run-on-main-thread.c (_initialize_run_on_main_thread): Likewise * ser-base.c (reschedule): Likewise. (ser_base_async): Likewise. * tui/tui-io.c: Likewise. * top.h (struct ui) <num>: New field. * top.c (highest_ui_num): New variable. (ui::ui): Initialize num. gdbserver/ChangeLog: * linux-low.cc (linux_process_target::async): Pass name to add_file_handler. * remote-utils.cc (handle_accept_event): Likewise. (remote_open): Likewise. gdbsupport/ChangeLog: * event-loop.h (add_file_handler): Add "name" parameter. * event-loop.cc (struct file_handler) <name>: New field. (create_file_handler): Add "name" parameter, assign it to file handler. (add_file_handler): Add "name" parameter. Change-Id: I9f1545f73888ebb6778eb653a618ca44d105f92c
2020-10-03 02:45:52 +08:00
async_signals_handler, NULL, "async-signals");
}
/* Create an asynchronous handler, allocating memory for it.
Return a pointer to the newly created handler.
This pointer will be used to invoke the handler by
invoke_async_signal_handler.
PROC is the function to call with CLIENT_DATA argument
whenever the handler is invoked. */
async_signal_handler *
create_async_signal_handler (sig_handler_func * proc,
gdb_client_data client_data,
const char *name)
{
async_signal_handler *async_handler_ptr;
async_handler_ptr = XNEW (async_signal_handler);
async_handler_ptr->ready = 0;
async_handler_ptr->next_handler = NULL;
async_handler_ptr->proc = proc;
async_handler_ptr->client_data = client_data;
async_handler_ptr->name = name;
if (sighandler_list.first_handler == NULL)
sighandler_list.first_handler = async_handler_ptr;
else
sighandler_list.last_handler->next_handler = async_handler_ptr;
sighandler_list.last_handler = async_handler_ptr;
return async_handler_ptr;
}
/* Mark the handler (ASYNC_HANDLER_PTR) as ready. This information
will be used when the handlers are invoked, after we have waited
for some event. The caller of this function is the interrupt
handler associated with a signal. */
void
mark_async_signal_handler (async_signal_handler *async_handler_ptr)
{
if (debug_event_loop != debug_event_loop_kind::OFF)
{
/* This is called by signal handlers, so we print it "by hand" using
the async-signal-safe methods. */
const char head[] = ("[event-loop] mark_async_signal_handler: marking"
"async signal handler `");
gdb_stdlog->write_async_safe (head, strlen (head));
gdb_stdlog->write_async_safe (async_handler_ptr->name,
strlen (async_handler_ptr->name));
const char tail[] = "`\n";
gdb_stdlog->write_async_safe (tail, strlen (tail));
}
async_handler_ptr->ready = 1;
serial_event_set (async_signal_handlers_serial_event);
}
/* See event-loop.h. */
void
clear_async_signal_handler (async_signal_handler *async_handler_ptr)
{
event_loop_debug_printf ("clearing async signal handler `%s`",
async_handler_ptr->name);
async_handler_ptr->ready = 0;
}
/* See event-loop.h. */
int
async_signal_handler_is_marked (async_signal_handler *async_handler_ptr)
{
return async_handler_ptr->ready;
}
/* Call all the handlers that are ready. Returns true if any was
indeed ready. */
int
invoke_async_signal_handlers (void)
{
async_signal_handler *async_handler_ptr;
int any_ready = 0;
/* We're going to handle all pending signals, so no need to wake up
the event loop again the next time around. Note this must be
cleared _before_ calling the callbacks, to avoid races. */
serial_event_clear (async_signal_handlers_serial_event);
/* Invoke all ready handlers. */
while (1)
{
for (async_handler_ptr = sighandler_list.first_handler;
async_handler_ptr != NULL;
async_handler_ptr = async_handler_ptr->next_handler)
{
if (async_handler_ptr->ready)
break;
}
if (async_handler_ptr == NULL)
break;
any_ready = 1;
async_handler_ptr->ready = 0;
/* Async signal handlers have no connection to whichever was the
current UI, and thus always run on the main one. */
current_ui = main_ui;
event_loop_debug_printf ("invoking async signal handler `%s`",
async_handler_ptr->name);
(*async_handler_ptr->proc) (async_handler_ptr->client_data);
}
return any_ready;
}
/* Delete an asynchronous handler (ASYNC_HANDLER_PTR).
Free the space allocated for it. */
void
delete_async_signal_handler (async_signal_handler ** async_handler_ptr)
{
async_signal_handler *prev_ptr;
if (sighandler_list.first_handler == (*async_handler_ptr))
{
sighandler_list.first_handler = (*async_handler_ptr)->next_handler;
if (sighandler_list.first_handler == NULL)
sighandler_list.last_handler = NULL;
}
else
{
prev_ptr = sighandler_list.first_handler;
while (prev_ptr && prev_ptr->next_handler != (*async_handler_ptr))
prev_ptr = prev_ptr->next_handler;
gdb_assert (prev_ptr);
prev_ptr->next_handler = (*async_handler_ptr)->next_handler;
if (sighandler_list.last_handler == (*async_handler_ptr))
sighandler_list.last_handler = prev_ptr;
}
xfree ((*async_handler_ptr));
(*async_handler_ptr) = NULL;
}
/* See async-event.h. */
async_event_handler *
create_async_event_handler (async_event_handler_func *proc,
gdb_client_data client_data,
const char *name)
{
async_event_handler *h;
h = XNEW (struct async_event_handler);
h->ready = 0;
h->next_handler = NULL;
h->proc = proc;
h->client_data = client_data;
h->name = name;
if (async_event_handler_list.first_handler == NULL)
async_event_handler_list.first_handler = h;
else
async_event_handler_list.last_handler->next_handler = h;
async_event_handler_list.last_handler = h;
return h;
}
/* Mark the handler (ASYNC_HANDLER_PTR) as ready. This information
will be used by gdb_do_one_event. The caller will be whoever
created the event source, and wants to signal that the event is
ready to be handled. */
void
mark_async_event_handler (async_event_handler *async_handler_ptr)
{
event_loop_debug_printf ("marking async event handler `%s` "
"(previous state was %d)",
async_handler_ptr->name,
async_handler_ptr->ready);
async_handler_ptr->ready = 1;
}
/* See event-loop.h. */
void
clear_async_event_handler (async_event_handler *async_handler_ptr)
{
event_loop_debug_printf ("clearing async event handler `%s`",
async_handler_ptr->name);
async_handler_ptr->ready = 0;
}
gdb: defer commit resume until all available events are consumed Rationale --------- Let's say you have multiple threads hitting a conditional breakpoint at the same time, and all of these are going to evaluate to false. All these threads will need to be resumed. Currently, GDB fetches one target event (one SIGTRAP representing the breakpoint hit) and decides that the thread should be resumed. It calls resume and commit_resume immediately. It then fetches the second target event, and does the same, until it went through all threads. The result is therefore something like: - consume event for thread A - resume thread A - commit resume (affects thread A) - consume event for thread B - resume thread B - commit resume (affects thread B) - consume event for thread C - resume thread C - commit resume (affects thread C) For targets where it's beneficial to group resumptions requests (most likely those that implement target_ops::commit_resume), it would be much better to have: - consume event for thread A - resume thread A - consume event for thread B - resume thread B - consume event for thread C - resume thread C - commit resume (affects threads A, B and C) Implementation details ---------------------- To achieve this, this patch adds another check in maybe_set_commit_resumed_all_targets to avoid setting the commit-resumed flag of targets that readily have events to provide to infrun. To determine if a target has events readily available to report, this patch adds an `has_pending_events` target_ops method. The method returns a simple bool to say whether or not it has pending events to report. Testing ======= To test this, I start GDBserver with a program that spawns multiple threads: $ ../gdbserver/gdbserver --once :1234 ~/src/many-threads-stepping-over-breakpoints/many-threads-stepping-over-breakpoints I then connect with GDB and install a conditional breakpoint that always evaluates to false (and force the evaluation to be done by GDB): $ ./gdb -nx --data-directory=data-directory \ /home/simark/src/many-threads-stepping-over-breakpoints/many-threads-stepping-over-breakpoints \ -ex "set breakpoint condition-evaluation host" \ -ex "set pag off" \ -ex "set confirm off" \ -ex "maint set target-non-stop on" \ -ex "tar rem :1234" \ -ex "tb main" \ -ex "b 13 if 0" \ -ex c \ -ex "set debug infrun" \ -ex "set debug remote 1" \ -ex "set debug displaced" I then do "continue" and look at the log. The remote target receives a bunch of stop notifications for all threads that have hit the breakpoint. infrun consumes and processes one event, decides it should not cause a stop, prepares a displaced step, after which we should see: [infrun] maybe_set_commit_resumed_all_process_targets: not requesting commit-resumed for target remote, target has pending events Same for a second thread (since we have 2 displaced step buffers). For the following threads, their displaced step is deferred since there are no more buffers available. After consuming the last event the remote target has to offer, we get: [infrun] maybe_set_commit_resumed_all_process_targets: enabling commit-resumed for target remote [infrun] maybe_call_commit_resumed_all_process_targets: calling commit_resumed for target remote [remote] Sending packet: $vCont;s:p14d16b.14d1b1;s:p14d16b.14d1b2#55 [remote] Packet received: OK Without the patch, there would have been one vCont;s just after each prepared displaced step. gdb/ChangeLog: yyyy-mm-dd Simon Marchi <simon.marchi@efficios.com> Pedro Alves <pedro@palves.net> * async-event.c (async_event_handler_marked): New. * async-event.h (async_event_handler_marked): Declare. * infrun.c (maybe_set_commit_resumed_all_targets): Switch to inferior before calling target method. Don't commit-resumed if target_has_pending_events is true. * remote.c (remote_target::has_pending_events): New. * target-delegates.c: Regenerate. * target.c (target_has_pending_events): New. * target.h (target_ops::has_pending_events): New target method. (target_has_pending_events): New. Change-Id: I18112ba19a1ff4986530c660f530d847bb4a1f1d
2020-07-07 03:53:28 +08:00
/* See event-loop.h. */
bool
async_event_handler_marked (async_event_handler *handler)
{
return handler->ready;
}
/* Check if asynchronous event handlers are ready, and call the
handler function for one that is. */
int
check_async_event_handlers ()
{
async_event_handler *async_handler_ptr;
for (async_handler_ptr = async_event_handler_list.first_handler;
async_handler_ptr != NULL;
async_handler_ptr = async_handler_ptr->next_handler)
{
if (async_handler_ptr->ready)
{
event_loop_debug_printf ("invoking async event handler `%s`",
async_handler_ptr->name);
(*async_handler_ptr->proc) (async_handler_ptr->client_data);
return 1;
}
}
return 0;
}
/* Delete an asynchronous handler (ASYNC_HANDLER_PTR).
Free the space allocated for it. */
void
delete_async_event_handler (async_event_handler **async_handler_ptr)
{
async_event_handler *prev_ptr;
if (async_event_handler_list.first_handler == *async_handler_ptr)
{
async_event_handler_list.first_handler
= (*async_handler_ptr)->next_handler;
if (async_event_handler_list.first_handler == NULL)
async_event_handler_list.last_handler = NULL;
}
else
{
prev_ptr = async_event_handler_list.first_handler;
while (prev_ptr && prev_ptr->next_handler != *async_handler_ptr)
prev_ptr = prev_ptr->next_handler;
gdb_assert (prev_ptr);
prev_ptr->next_handler = (*async_handler_ptr)->next_handler;
if (async_event_handler_list.last_handler == (*async_handler_ptr))
async_event_handler_list.last_handler = prev_ptr;
}
xfree (*async_handler_ptr);
*async_handler_ptr = NULL;
}