+++ /dev/null
-/* Event loop machinery for the remote server for GDB.
- Copyright (C) 1999-2020 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/>. */
-
-/* Based on src/gdb/event-loop.c. */
-
-#include "server.h"
-
-#include <sys/types.h>
-#include "gdbsupport/gdb_sys_time.h"
-
-#ifdef USE_WIN32API
-#include <windows.h>
-#include <io.h>
-#endif
-
-#include <unistd.h>
-#include <queue>
-
-typedef int (event_handler_func) (gdb_fildes_t);
-
-/* Tell create_file_handler what events we are interested in. */
-
-#define GDB_READABLE (1<<1)
-#define GDB_WRITABLE (1<<2)
-#define GDB_EXCEPTION (1<<3)
-
-/* Events are queued by on the event_queue and serviced later
- on by do_one_event. An event can be, for instance, a file
- descriptor becoming ready to be read. Servicing an event simply
- means that the procedure PROC will be called. We have 2 queues,
- one for file handlers that we listen to in the event loop, and one
- for the file handlers+events that are ready. The procedure PROC
- associated with each event is always the same (handle_file_event).
- Its duty is to invoke the handler associated with the file
- descriptor whose state change generated the event, plus doing other
- cleanups and such. */
-
-struct gdb_event
- {
- /* Procedure to call to service this event. */
- event_handler_func *proc;
-
- /* File descriptor that is ready. */
- gdb_fildes_t fd;
- };
-
-/* Information about each file descriptor we register with the event
- loop. */
-
-typedef struct file_handler
- {
- /* File descriptor. */
- gdb_fildes_t fd;
-
- /* Events we want to monitor. */
- int mask;
-
- /* Events that have been seen since the last time. */
- int ready_mask;
-
- /* Procedure to call when fd is ready. */
- handler_func *proc;
-
- /* Argument to pass to proc. */
- gdb_client_data client_data;
-
- /* Was an error detected on this fd? */
- int error;
-
- /* Next registered file descriptor. */
- struct file_handler *next_file;
- }
-file_handler;
-
-typedef gdb::unique_xmalloc_ptr<gdb_event> gdb_event_up;
-
-static std::queue<gdb_event_up, std::list<gdb_event_up>> event_queue;
-
-/* Gdb_notifier is just a list of file descriptors gdb is interested
- in. These are the input file descriptor, and the target file
- descriptor. Each of the elements in the gdb_notifier list is
- basically a description of what kind of events gdb is interested
- in, for each fd. */
-
-static struct
- {
- /* Ptr to head of file handler list. */
- file_handler *first_file_handler;
-
- /* Masks to be used in the next call to select. Bits are set in
- response to calls to create_file_handler. */
- fd_set check_masks[3];
-
- /* What file descriptors were found ready by select. */
- fd_set ready_masks[3];
-
- /* Number of valid bits (highest fd value + 1). (for select) */
- int num_fds;
- }
-gdb_notifier;
-
-/* Callbacks are just routines that are executed before waiting for the
- next event. In GDB this is struct gdb_timer. We don't need timers
- so rather than copy all that complexity in gdbserver, we provide what
- we need, but we do so in a way that if/when the day comes that we need
- that complexity, it'll be easier to add - replace callbacks with timers
- and use a delta of zero (which is all gdb currently uses timers for anyway).
-
- PROC will be executed before gdbserver goes to sleep to wait for the
- next event. */
-
-struct callback_event
- {
- int id;
- callback_handler_func *proc;
- gdb_client_data data;
- struct callback_event *next;
- };
-
-/* Table of registered callbacks. */
-
-static struct
- {
- struct callback_event *first;
- struct callback_event *last;
-
- /* Id of the last callback created. */
- int num_callbacks;
- }
-callback_list;
-
-void
-initialize_event_loop (void)
-{
-}
-
-/* Process one event. If an event was processed, 1 is returned
- otherwise 0 is returned. Scan the queue from head to tail,
- processing therefore the high priority events first, by invoking
- the associated event handler procedure. */
-
-static int
-process_event (void)
-{
- /* Let's get rid of the event from the event queue. We need to
- do this now because while processing the event, since the
- proc function could end up jumping out to the caller of this
- function. In that case, we would have on the event queue an
- event which has been processed, but not deleted. */
- if (!event_queue.empty ())
- {
- gdb_event_up event_ptr = std::move (event_queue.front ());
- event_queue.pop ();
-
- event_handler_func *proc = event_ptr->proc;
- gdb_fildes_t fd = event_ptr->fd;
-
- /* Now call the procedure associated with the event. */
- if ((*proc) (fd))
- return -1;
- return 1;
- }
-
- /* This is the case if there are no event on the event queue. */
- return 0;
-}
-
-/* Append PROC to the callback list.
- The result is the "id" of the callback that can be passed back to
- delete_callback_event. */
-
-int
-append_callback_event (callback_handler_func *proc, gdb_client_data data)
-{
- struct callback_event *event_ptr = XNEW (struct callback_event);
-
- event_ptr->id = callback_list.num_callbacks++;
- event_ptr->proc = proc;
- event_ptr->data = data;
- event_ptr->next = NULL;
- if (callback_list.first == NULL)
- callback_list.first = event_ptr;
- if (callback_list.last != NULL)
- callback_list.last->next = event_ptr;
- callback_list.last = event_ptr;
- return event_ptr->id;
-}
-
-/* Delete callback ID.
- It is not an error callback ID doesn't exist. */
-
-void
-delete_callback_event (int id)
-{
- struct callback_event **p;
-
- for (p = &callback_list.first; *p != NULL; p = &(*p)->next)
- {
- struct callback_event *event_ptr = *p;
-
- if (event_ptr->id == id)
- {
- *p = event_ptr->next;
- if (event_ptr == callback_list.last)
- callback_list.last = NULL;
- free (event_ptr);
- break;
- }
- }
-}
-
-/* Run the next callback.
- The result is 1 if a callback was called and event processing
- should continue, -1 if the callback wants the event loop to exit,
- and 0 if there are no more callbacks. */
-
-static int
-process_callback (void)
-{
- struct callback_event *event_ptr;
-
- event_ptr = callback_list.first;
- if (event_ptr != NULL)
- {
- callback_handler_func *proc = event_ptr->proc;
- gdb_client_data data = event_ptr->data;
-
- /* Remove the event before calling PROC,
- more events may get added by PROC. */
- callback_list.first = event_ptr->next;
- if (callback_list.first == NULL)
- callback_list.last = NULL;
- free (event_ptr);
- if ((*proc) (data))
- return -1;
- return 1;
- }
-
- return 0;
-}
-
-/* Add a file handler/descriptor to the list of descriptors we are
- interested in. FD is the file descriptor for the file/stream to be
- listened to. MASK is a combination of READABLE, WRITABLE,
- EXCEPTION. PROC is the procedure that will be called when an event
- occurs for FD. CLIENT_DATA is the argument to pass to PROC. */
-
-static void
-create_file_handler (gdb_fildes_t fd, int mask, handler_func *proc,
- gdb_client_data client_data)
-{
- file_handler *file_ptr;
-
- /* Do we already have a file handler for this file? (We may be
- changing its associated procedure). */
- for (file_ptr = gdb_notifier.first_file_handler;
- file_ptr != NULL;
- file_ptr = file_ptr->next_file)
- if (file_ptr->fd == fd)
- break;
-
- /* It is a new file descriptor. Add it to the list. Otherwise,
- just change the data associated with it. */
- if (file_ptr == NULL)
- {
- file_ptr = XNEW (struct file_handler);
- file_ptr->fd = fd;
- file_ptr->ready_mask = 0;
- file_ptr->next_file = gdb_notifier.first_file_handler;
- gdb_notifier.first_file_handler = file_ptr;
-
- if (mask & GDB_READABLE)
- FD_SET (fd, &gdb_notifier.check_masks[0]);
- else
- FD_CLR (fd, &gdb_notifier.check_masks[0]);
-
- if (mask & GDB_WRITABLE)
- FD_SET (fd, &gdb_notifier.check_masks[1]);
- else
- FD_CLR (fd, &gdb_notifier.check_masks[1]);
-
- if (mask & GDB_EXCEPTION)
- FD_SET (fd, &gdb_notifier.check_masks[2]);
- else
- FD_CLR (fd, &gdb_notifier.check_masks[2]);
-
- if (gdb_notifier.num_fds <= fd)
- gdb_notifier.num_fds = fd + 1;
- }
-
- file_ptr->proc = proc;
- file_ptr->client_data = client_data;
- file_ptr->mask = mask;
-}
-
-/* Wrapper function for create_file_handler. */
-
-void
-add_file_handler (gdb_fildes_t fd,
- handler_func *proc, gdb_client_data client_data)
-{
- create_file_handler (fd, GDB_READABLE | GDB_EXCEPTION, proc, client_data);
-}
-
-/* Remove the file descriptor FD from the list of monitored fd's:
- i.e. we don't care anymore about events on the FD. */
-
-void
-delete_file_handler (gdb_fildes_t fd)
-{
- file_handler *file_ptr, *prev_ptr = NULL;
- int i;
-
- /* Find the entry for the given file. */
-
- for (file_ptr = gdb_notifier.first_file_handler;
- file_ptr != NULL;
- file_ptr = file_ptr->next_file)
- if (file_ptr->fd == fd)
- break;
-
- if (file_ptr == NULL)
- return;
-
- if (file_ptr->mask & GDB_READABLE)
- FD_CLR (fd, &gdb_notifier.check_masks[0]);
- if (file_ptr->mask & GDB_WRITABLE)
- FD_CLR (fd, &gdb_notifier.check_masks[1]);
- if (file_ptr->mask & GDB_EXCEPTION)
- FD_CLR (fd, &gdb_notifier.check_masks[2]);
-
- /* Find current max fd. */
-
- if ((fd + 1) == gdb_notifier.num_fds)
- {
- gdb_notifier.num_fds--;
- for (i = gdb_notifier.num_fds; i; i--)
- {
- if (FD_ISSET (i - 1, &gdb_notifier.check_masks[0])
- || FD_ISSET (i - 1, &gdb_notifier.check_masks[1])
- || FD_ISSET (i - 1, &gdb_notifier.check_masks[2]))
- break;
- }
- gdb_notifier.num_fds = i;
- }
-
- /* Deactivate the file descriptor, by clearing its mask, so that it
- will not fire again. */
-
- file_ptr->mask = 0;
-
- /* Get rid of the file handler in the file handler list. */
- if (file_ptr == gdb_notifier.first_file_handler)
- gdb_notifier.first_file_handler = file_ptr->next_file;
- else
- {
- for (prev_ptr = gdb_notifier.first_file_handler;
- prev_ptr->next_file != file_ptr;
- prev_ptr = prev_ptr->next_file)
- ;
- prev_ptr->next_file = file_ptr->next_file;
- }
- free (file_ptr);
-}
-
-/* Handle the given event by calling the procedure associated to the
- corresponding file handler. Called by process_event indirectly,
- through event_ptr->proc. EVENT_FILE_DESC is file descriptor of the
- event in the front of the event queue. */
-
-static int
-handle_file_event (gdb_fildes_t event_file_desc)
-{
- file_handler *file_ptr;
- int mask;
-
- /* Search the file handler list to find one that matches the fd in
- the event. */
- for (file_ptr = gdb_notifier.first_file_handler; file_ptr != NULL;
- file_ptr = file_ptr->next_file)
- {
- if (file_ptr->fd == event_file_desc)
- {
- /* See if the desired events (mask) match the received
- events (ready_mask). */
-
- if (file_ptr->ready_mask & GDB_EXCEPTION)
- {
- warning ("Exception condition detected on fd %s",
- pfildes (file_ptr->fd));
- file_ptr->error = 1;
- }
- else
- file_ptr->error = 0;
- mask = file_ptr->ready_mask & file_ptr->mask;
-
- /* Clear the received events for next time around. */
- file_ptr->ready_mask = 0;
-
- /* If there was a match, then call the handler. */
- if (mask != 0)
- {
- if ((*file_ptr->proc) (file_ptr->error,
- file_ptr->client_data) < 0)
- return -1;
- }
- break;
- }
- }
-
- return 0;
-}
-
-/* Create a file event, to be enqueued in the event queue for
- processing. The procedure associated to this event is always
- handle_file_event, which will in turn invoke the one that was
- associated to FD when it was registered with the event loop. */
-
-static gdb_event *
-create_file_event (gdb_fildes_t fd)
-{
- gdb_event *file_event_ptr;
-
- file_event_ptr = XNEW (gdb_event);
- file_event_ptr->proc = handle_file_event;
- file_event_ptr->fd = fd;
-
- return file_event_ptr;
-}
-
-/* Called by do_one_event to wait for new events on the monitored file
- descriptors. Queue file events as they are detected by the poll.
- If there are no events, this function will block in the call to
- select. Return -1 if there are no files descriptors to monitor,
- otherwise return 0. */
-
-static int
-wait_for_event (void)
-{
- file_handler *file_ptr;
- int num_found = 0;
-
- /* Make sure all output is done before getting another event. */
- fflush (stdout);
- fflush (stderr);
-
- if (gdb_notifier.num_fds == 0)
- return -1;
-
- gdb_notifier.ready_masks[0] = gdb_notifier.check_masks[0];
- gdb_notifier.ready_masks[1] = gdb_notifier.check_masks[1];
- gdb_notifier.ready_masks[2] = gdb_notifier.check_masks[2];
- num_found = select (gdb_notifier.num_fds,
- &gdb_notifier.ready_masks[0],
- &gdb_notifier.ready_masks[1],
- &gdb_notifier.ready_masks[2],
- NULL);
-
- /* Clear the masks after an error from select. */
- if (num_found == -1)
- {
- FD_ZERO (&gdb_notifier.ready_masks[0]);
- FD_ZERO (&gdb_notifier.ready_masks[1]);
- FD_ZERO (&gdb_notifier.ready_masks[2]);
-#ifdef EINTR
- /* Dont print anything if we got a signal, let gdb handle
- it. */
- if (errno != EINTR)
- perror_with_name ("select");
-#endif
- }
-
- /* Enqueue all detected file events. */
-
- for (file_ptr = gdb_notifier.first_file_handler;
- file_ptr != NULL && num_found > 0;
- file_ptr = file_ptr->next_file)
- {
- int mask = 0;
-
- if (FD_ISSET (file_ptr->fd, &gdb_notifier.ready_masks[0]))
- mask |= GDB_READABLE;
- if (FD_ISSET (file_ptr->fd, &gdb_notifier.ready_masks[1]))
- mask |= GDB_WRITABLE;
- if (FD_ISSET (file_ptr->fd, &gdb_notifier.ready_masks[2]))
- mask |= GDB_EXCEPTION;
-
- if (!mask)
- continue;
- else
- num_found--;
-
- /* Enqueue an event only if this is still a new event for this
- fd. */
-
- if (file_ptr->ready_mask == 0)
- {
- gdb_event *file_event_ptr = create_file_event (file_ptr->fd);
-
- event_queue.emplace (file_event_ptr);
- }
- file_ptr->ready_mask = mask;
- }
-
- return 0;
-}
-
-/* Start up the event loop. This is the entry point to the event
- loop. */
-
-void
-start_event_loop (void)
-{
- /* Loop until there is nothing to do. This is the entry point to
- the event loop engine. If nothing is ready at this time, wait
- for something to happen (via wait_for_event), then process it.
- Return when there are no longer event sources to wait for. */
-
- while (1)
- {
- /* Any events already waiting in the queue? */
- int res = process_event ();
-
- /* Did the event handler want the event loop to stop? */
- if (res == -1)
- return;
-
- if (res)
- continue;
-
- /* Process any queued callbacks before we go to sleep. */
- res = process_callback ();
-
- /* Did the callback want the event loop to stop? */
- if (res == -1)
- return;
-
- if (res)
- continue;
-
- /* Wait for a new event. If wait_for_event returns -1, we
- should get out because this means that there are no event
- sources left. This will make the event loop stop, and the
- application exit. */
-
- if (wait_for_event () < 0)
- return;
- }
-
- /* We are done with the event loop. There are no more event sources
- to listen to. So we exit gdbserver. */
-}