diff options
-rw-r--r-- | gdb/event-loop.c | 741 | ||||
-rw-r--r-- | gdb/event-loop.h | 241 | ||||
-rw-r--r-- | gdb/event-top.c | 950 |
3 files changed, 1932 insertions, 0 deletions
diff --git a/gdb/event-loop.c b/gdb/event-loop.c new file mode 100644 index 0000000..fe1d0f5 --- /dev/null +++ b/gdb/event-loop.c @@ -0,0 +1,741 @@ +/* Event loop machinery for GDB, the GNU debugger. + Copyright 1999 Free Software Foundation, Inc. + Written by Elena Zannoni <ezannoni@cygnus.com> of Cygnus Solutions. + + 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 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. */ + +#include "event-loop.h" +#include <readline/readline.h> +#include <setjmp.h> +#include "top.h" + +/* For config.h which may define HAVE_POLL */ +#include "defs.h" + +#ifdef HAVE_POLL +#include <sys/poll.h> +#endif +#include <errno.h> + +/* Event queue: + - the first event in the queue is the head of the queue. + It will be the next to be serviced. + - the last event in the queue + + Events can be inserted at the front of the queue or at the end of + the queue. Events will be extracted from the queue for processing + starting from the head. Therefore, events inserted at the head of + the queue will be processed in a last in first out fashoin, while + those inserted at the tail of the queue will be processed in a first + in first out manner. All the fields are NULL if the queue is + empty. */ + +static struct + { + gdb_event *first_event; /* First pending event */ + gdb_event *last_event; /* Last pending event */ + } +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. We have two flavors of the notifier, one for platforms + that have the POLL function, the other for those that don't, and + only support SELECT. 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. */ + +/* As of 4/30/99 only the input file descriptor is registered with the + event loop. */ + +#ifdef HAVE_POLL +/* Poll based implementation of the notifier. */ + +static struct + { + /* Ptr to head of file handler list. */ + file_handler *first_file_handler; + + /* Ptr to array of pollfd structures. */ + struct pollfd *poll_fds; + + /* Number of file descriptors to monitor. */ + int num_fds; + + } +gdb_notifier; + +#else /* ! HAVE_POLL */ + +/* Select based implementation of the notifier. */ + +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_mask check_masks[3 * MASK_SIZE]; + + /* What file descriptors were found ready by select. */ + fd_mask ready_masks[3 * MASK_SIZE]; + + /* Number of valid bits (highest fd value + 1). */ + int num_fds; + + } +gdb_notifier; + +#endif /* HAVE_POLL */ + +/* 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; + +/* Is any of the handlers ready? Check this variable using + check_async_ready. This is used by process_event, to determine + whether or not to invoke the invoke_async_signal_handler + function. */ +static int async_handler_ready = 0; + +static void invoke_async_signal_handler PARAMS ((void)); +static int gdb_wait_for_event PARAMS ((void)); +static int check_async_ready PARAMS ((void)); +extern display_gdb_prompt PARAMS ((char *)); + + +/* Insert an event object into the gdb event queue at + the specified position. + POSITION can be head or tail, with values TAIL, HEAD. + EVENT_PTR points to the event to be inserted into the queue. + The caller must allocate memory for the event. It is freed + after the event has ben handled. + Events in the queue will be processed head to tail, therefore, + events inserted at the head of the queue will be processed + as last in first out. Event appended at the tail of the queue + will be processed first in first out. */ +static void +async_queue_event (event_ptr, position) + gdb_event *event_ptr; + queue_position position; +{ + if (position == TAIL) + { + /* The event will become the new last_event. */ + + event_ptr->next_event = NULL; + if (event_queue.first_event == NULL) + event_queue.first_event = event_ptr; + else + event_queue.last_event->next_event = event_ptr; + event_queue.last_event = event_ptr; + } + else if (position == HEAD) + { + /* The event becomes the new first_event. */ + + event_ptr->next_event = event_queue.first_event; + if (event_queue.first_event == NULL) + event_queue.last_event = event_ptr; + event_queue.first_event = event_ptr; + } +} + +/* Process one event. + The event can be the next one to be serviced in the event queue, + or an asynchronous event handler can be invoked in response to + the reception of a signal. + If an event was processed (either way), 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 () +{ + gdb_event *event_ptr, *prev_ptr; + event_handler_func *proc; + int fd; + + /* First let's see if there are any asynchronous event handlers that + are ready. These would be the result of invoking any of the + signal handlers. */ + + if (check_async_ready ()) + { + invoke_async_signal_handler (); + return 1; + } + + /* Look in the event queue to find an event that is ready + to be processed. */ + + for (event_ptr = event_queue.first_event; event_ptr != NULL; + event_ptr = event_ptr->next_event) + { + /* Call the handler for the event. */ + + proc = event_ptr->proc; + fd = event_ptr->fd; + + /* Let's get rid of the event from the event queue. We need to + do this now because while processing the event, the proc + function could end up calling 'error' and therefore jump out + to the caller of this function, gdb_do_one_event. In that + case, we would have on the event queue an event wich has been + processed, but not deleted. */ + + if (event_queue.first_event == event_ptr) + { + event_queue.first_event = event_ptr->next_event; + if (event_ptr->next_event == NULL) + event_queue.last_event = NULL; + } + else + { + prev_ptr = event_queue.first_event; + while (prev_ptr->next_event != event_ptr) + prev_ptr = prev_ptr->next_event; + + prev_ptr->next_event = event_ptr->next_event; + if (event_ptr->next_event == NULL) + event_queue.last_event = prev_ptr; + } + free ((char *) event_ptr); + + /* Now call the procedure associted with the event. */ + (*proc) (fd); + return 1; + } + + /* this is the case if there are no event on the event queue. */ + return 0; +} + +/* Process one high level event. If nothing is ready at this time, + wait for something to happen (via gdb_wait_for_event), then process + it. Returns 1 if something was done otherwise returns 0 (this can + happen if there are no event sources to wait for). */ +int +gdb_do_one_event () +{ + int result = 0; + + while (1) + { + if (!SET_TOP_LEVEL ()) + { + /* Any events already waiting in the queue? */ + if (process_event ()) + { + result = 1; + break; + } + + /* Wait for a new event. If gdb_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. */ + + result = gdb_wait_for_event (); + if (result < 0) + { + result = 0; + break; + } + + /* Handle any new events occurred while waiting. */ + if (process_event ()) + { + result = 1; + break; + } + + /* If gdb_wait_for_event has returned 1, it means that one + event has been handled. We break out of the loop. */ + if (result) + break; + } /* end of if !set_top_level */ + else + { + display_gdb_prompt (0); + /* Maybe better to set a flag to be checked somewhere as to + whether display the prompt or not. */ + } + } + return result; +} + + +/* 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. + For the poll case, MASK is a combination (OR) of + POLLIN, POLLRDNORM, POLLRDBAND, POLLPRI, POLLOUT, POLLWRNORM, + POLLWRBAND: these are the events we are interested in. If any of them + occurs, proc should be called. + For the select case, 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. */ +void +create_file_handler (fd, mask, proc, client_data) + int fd; + int mask; + file_handler_func *proc; + gdb_client_data client_data; +{ + file_handler *file_ptr; + +#ifndef HAVE_POLL + int index, bit; +#endif + + /* 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. */ + if (file_ptr == NULL) + { + file_ptr = (file_handler *) xmalloc (sizeof (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; + } + file_ptr->proc = proc; + file_ptr->client_data = client_data; + file_ptr->mask = mask; + +#ifdef HAVE_POLL + + gdb_notifier.num_fds++; + gdb_notifier.poll_fds = + (struct pollfd *) realloc (gdb_notifier.poll_fds, + (gdb_notifier.num_fds) * sizeof (struct pollfd)); + (gdb_notifier.poll_fds + gdb_notifier.num_fds - 1)->fd = fd; + (gdb_notifier.poll_fds + gdb_notifier.num_fds - 1)->events = mask; + (gdb_notifier.poll_fds + gdb_notifier.num_fds - 1)->revents = 0; + +#else /* ! HAVE_POLL */ + + index = fd / (NBBY * sizeof (fd_mask)); + bit = 1 << (fd % (NBBY * sizeof (fd_mask))); + + if (mask & GDB_READABLE) + gdb_notifier.check_masks[index] |= bit; + else + gdb_notifier.check_masks[index] &= ~bit; + + if (mask & GDB_WRITABLE) + (gdb_notifier.check_masks + MASK_SIZE)[index] |= bit; + else + (gdb_notifier.check_masks + MASK_SIZE)[index] &= ~bit; + + if (mask & GDB_EXCEPTION) + (gdb_notifier.check_masks + 2 * (MASK_SIZE))[index] |= bit; + else + (gdb_notifier.check_masks + 2 * (MASK_SIZE))[index] &= ~bit; + + if (gdb_notifier.num_fds <= fd) + gdb_notifier.num_fds = fd + 1; + +#endif /* HAVE_POLL */ +} + +/* 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 (fd) + int fd; +{ + file_handler *file_ptr, *prev_ptr = NULL; + int i, j; + struct pollfd *new_poll_fds; +#ifndef HAVE_POLL + int index, bit; + unsigned long flags; +#endif + + /* 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; + + /* Deactivate the file descriptor, by clearing its mask, + so that it will not fire again. */ + + file_ptr->mask = 0; + +#ifdef HAVE_POLL + /* Create a new poll_fds array by copying every fd's information but the + one we want to get rid of. */ + + new_poll_fds = + (struct pollfd *) xmalloc ((gdb_notifier.num_fds - 1) * sizeof (struct pollfd)); + + for (i = 0, j = 0; i < gdb_notifier.num_fds; i++) + { + if ((gdb_notifier.poll_fds + i)->fd != fd) + { + (new_poll_fds + j)->fd = (gdb_notifier.poll_fds + i)->fd; + (new_poll_fds + j)->events = (gdb_notifier.poll_fds + i)->events; + (new_poll_fds + j)->revents = (gdb_notifier.poll_fds + i)->revents; + j++; + } + } + free (gdb_notifier.poll_fds); + gdb_notifier.poll_fds = new_poll_fds; + gdb_notifier.num_fds--; + +#else /* ! HAVE_POLL */ + + index = fd / (NBBY * sizeof (fd_mask)); + bit = 1 << (fd % (NBBY * sizeof (fd_mask))); + + if (file_ptr->mask & GDB_READABLE) + gdb_notifier.check_masks[index] &= ~bit; + if (file_ptr->mask & GDB_WRITABLE) + (gdb_notifier.check_masks + MASK_SIZE)[index] &= ~bit; + if (file_ptr->mask & GDB_EXCEPTION) + (gdb_notifier.check_masks + 2 * (MASK_SIZE))[index] &= ~bit; + + /* Find current max fd. */ + + if ((fd + 1) == gdb_notifier.num_fds) + { + for (gdb_notifier.num_fds = 0; index >= 0; index--) + { + flags = gdb_notifier.check_masks[index] + | (gdb_notifier.check_masks + MASK_SIZE)[index] + | (gdb_notifier.check_masks + 2 * (MASK_SIZE))[index]; + if (flags) + { + for (i = (NBBY * sizeof (fd_mask)); i > 0; i--) + { + if (flags & (((unsigned long) 1) << (i - 1))) + break; + } + gdb_notifier.num_fds = index * (NBBY * sizeof (fd_mask)) + i; + break; + } + } + } +#endif /* HAVE_POLL */ + + /* 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 ((char *) 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 void +handle_file_event (event_file_desc) + int event_file_desc; +{ + file_handler *file_ptr; + int mask, error_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) + { + /* With poll, the ready_mask could have any of three events + set to 1: POLLHUP, POLLERR, POLLNVAL. These events cannot + be used in the requested event mask (events), but they + can be returned in the return mask (revents). We need to + check for those event too, and add them to the mask which + will be passed to the handler. */ + + /* See if the desired events (mask) match the received + events (ready_mask). */ + +#ifdef HAVE_POLL + error_mask = POLLHUP | POLLERR | POLLNVAL; + mask = (file_ptr->ready_mask & file_ptr->mask) | + (file_ptr->ready_mask & error_mask); + +#else /* ! HAVE_POLL */ + mask = file_ptr->ready_mask & file_ptr->mask; +#endif /* HAVE_POLL */ + + /* 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) + (*file_ptr->proc) (file_ptr->client_data, mask); + break; + } + } +} + +/* Called by gdb_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 poll. + Return -1 if there are no files descriptors to monitor, + otherwise return 0. */ +static int +gdb_wait_for_event () +{ + file_handler *file_ptr; + gdb_event *file_event_ptr; + int num_found, i; + +#ifndef HAVE_POLL + int mask, bit, index; +#endif + + if (gdb_notifier.num_fds == 0) + return -1; + +#ifdef HAVE_POLL + num_found = + poll (gdb_notifier.poll_fds, (unsigned long) gdb_notifier.num_fds, -1); + +#else /* ! HAVE_POLL */ + memcpy (gdb_notifier.ready_masks, + gdb_notifier.check_masks, + 3 * MASK_SIZE * sizeof (fd_mask)); + num_found = select (gdb_notifier.num_fds, + (SELECT_MASK *) & gdb_notifier.ready_masks[0], + (SELECT_MASK *) & gdb_notifier.ready_masks[MASK_SIZE], + (SELECT_MASK *) & gdb_notifier.ready_masks[2 * MASK_SIZE], + NULL); + + /* Clear the masks after an error from select. */ + if (num_found == -1) + memset (gdb_notifier.ready_masks, + 0, 3 * MASK_SIZE * sizeof (fd_mask)); + +#endif /* HAVE_POLL */ + + /* Enqueue all detected file events. */ + +#ifdef HAVE_POLL + + for (i = 0; (i < gdb_notifier.num_fds) && (num_found > 0); i++) + { + if ((gdb_notifier.poll_fds + i)->revents) + num_found--; + else + continue; + + for (file_ptr = gdb_notifier.first_file_handler; + file_ptr != NULL; + file_ptr = file_ptr->next_file) + { + if (file_ptr->fd == (gdb_notifier.poll_fds + i)->fd) + break; + } + + if (file_ptr) + { + /* Enqueue an event only if this is still a new event for + this fd. */ + if (file_ptr->ready_mask == 0) + { + file_event_ptr = + (gdb_event *) xmalloc (sizeof (gdb_event)); + file_event_ptr->proc = handle_file_event; + file_event_ptr->fd = file_ptr->fd; + async_queue_event (file_event_ptr, TAIL); + } + } + + file_ptr->ready_mask = (gdb_notifier.poll_fds + i)->revents; + } + +#else /* ! HAVE_POLL */ + for (file_ptr = gdb_notifier.first_file_handler; + (file_ptr != NULL) && (num_found > 0); + file_ptr = file_ptr->next_file) + { + index = file_ptr->fd / (NBBY * sizeof (fd_mask)); + bit = 1 << (file_ptr->fd % (NBBY * sizeof (fd_mask))); + mask = 0; + + if (gdb_notifier.ready_masks[index] & bit) + mask |= GDB_READABLE; + if ((gdb_notifier.ready_masks + MASK_SIZE)[index] & bit) + mask |= GDB_WRITABLE; + if ((gdb_notifier.ready_masks + 2 * (MASK_SIZE))[index] & bit) + 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) + { + file_event_ptr = + (gdb_event *) xmalloc (sizeof (gdb_event)); + file_event_ptr->proc = handle_file_event; + file_event_ptr->fd = file_ptr->fd; + async_queue_event (file_event_ptr, TAIL); + } + file_ptr->ready_mask = mask; + } +#endif /* HAVE_POLL */ + + return 0; +} + + +/* 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 (proc, client_data) + async_handler_func *proc; + gdb_client_data client_data; +{ + async_signal_handler *async_handler_ptr; + + async_handler_ptr = + (async_signal_handler *) xmalloc (sizeof (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; + 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_handler_ptr) + async_signal_handler *async_handler_ptr; +{ + ((async_signal_handler *) async_handler_ptr)->ready = 1; + async_handler_ready = 1; +} + +/* Call all the handlers that are ready. */ +static void +invoke_async_signal_handler () +{ + async_signal_handler *async_handler_ptr; + + if (async_handler_ready == 0) + return; + async_handler_ready = 0; + + /* Invoke 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; + async_handler_ptr->ready = 0; + (*async_handler_ptr->proc) (async_handler_ptr->client_data); + } + + return; +} + +/* Delete an asynchronous handler (ASYNC_HANDLER_PTR). + Free the space allocated for it. */ +void +delete_async_signal_handler (async_handler_ptr) + 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->next_handler != async_handler_ptr) + prev_ptr = prev_ptr->next_handler; + prev_ptr->next_handler = async_handler_ptr->next_handler; + if (sighandler_list.last_handler == async_handler_ptr) + sighandler_list.last_handler = prev_ptr; + } + free ((char *) async_handler_ptr); +} + +/* Is it necessary to call invoke_async_signal_handler? */ +static int +check_async_ready () +{ + return async_handler_ready; +} diff --git a/gdb/event-loop.h b/gdb/event-loop.h new file mode 100644 index 0000000..6076254 --- /dev/null +++ b/gdb/event-loop.h @@ -0,0 +1,241 @@ +/* Definitions used by the GDB event loop. + Copyright 1999 Free Software Foundation, Inc. + Written by Elena Zannoni <ezannoni@cygnus.com> of Cygnus Solutions. + + 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 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. */ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/time.h> +#include <signal.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/wait.h> +#include "defs.h" + +/* An event loop listens for events from multiple event sources. When + an event arrives, it is queued and processed by calling the + appropriate event handler. The event loop then continues to listen + for more events. An event loop completes when there are no event + sources to listen on. External event sources can be plugged into + the loop. + + There are 3 main components: + - a list of file descriptors to be monitored, GDB_NOTIFIER. + - a list of events that have occurred, EVENT_QUEUE. + - a list of signal handling functions, SIGHANDLER_LIST. + + GDB_NOTIFIER keeps track of the event sources. Event sources for + gdb are currently the UI and the target. Gdb communicates with the + command line user interface via the readline library and usually + communicates with remote targets via a serial port. Serial ports + are represented in GDB as file descriptors and select/poll calls. + For native targets instead, the communication consists of calls to + ptrace and waits (via signals) or calls to poll/select (via file + descriptors). In the current gdb, the code handling events related + to the target resides in the wait_for_inferior function and in + various target specific files (*-tdep.c). + + EVENT_QUEUE keeps track of the events that have happened during the + last iteration of the event loop, and need to be processed. An + event is represented by a procedure to be invoked in order to + process the event. The queue is scanned head to tail. If the + event of interest is a change of state in a file descriptor, then a + call to poll or select will be made to detect it. + + If the events generate signals, they are also queued by special + functions that are invoked through traditional signal handlers. + The actions to be taken is response to such events will be executed + when the SIGHANDLER_LIST is scanned, the next time through the + infinite loop. + + Corollary tasks are the creation and deletion of event sources. */ + +typedef PTR gdb_client_data; +typedef struct gdb_event gdb_event; + +typedef void (file_handler_func) PARAMS ((gdb_client_data, int mask)); +typedef void (async_handler_func) PARAMS ((gdb_client_data)); +typedef void (event_handler_func) PARAMS ((int)); + +/* Event for the GDB event system. Events are queued by calling + async_queue_event and serviced later on by gdb_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 adn such. */ + +struct gdb_event + { + event_handler_func *proc; /* Procedure to call to service this event. */ + int fd; /* File descriptor that is ready. */ + struct gdb_event *next_event; /* Next in list of events or NULL. */ + }; + +/* Information about each file descriptor we register with the event + loop. */ + +typedef struct file_handler + { + int fd; /* File descriptor. */ + int mask; /* Events we want to monitor: POLLIN, etc. */ + int ready_mask; /* Events that have been seen since + the last time. */ + file_handler_func *proc; /* Procedure to call when fd is ready. */ + gdb_client_data client_data; /* Argument to pass to proc. */ + struct file_handler *next_file; /* Next registered file descriptor. */ + } +file_handler; + +/* 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 + asyn_signal_handler for each interesting signal. */ + +typedef struct async_signal_handler + { + int ready; /* If ready, call this handler from the main event loop, + using invoke_async_handler. */ + struct async_signal_handler *next_handler; /* Ptr to next handler */ + async_handler_func *proc; /* Function to call to do the work */ + gdb_client_data client_data; /* Argument to async_handler_func */ + } +async_signal_handler; + +/* Where to add an event onto the event queue, by queue_event. */ +typedef enum + { + /* Add at tail of queue. It will be processed in first in first + out order. */ + TAIL, + /* Add at head of queue. It will be processed in last in first out + order. */ + HEAD + } +queue_position; + +/* Tell create_file_handler what events we are interested in. + This is used by the select version of the event loop. */ + +#define GDB_READABLE (1<<1) +#define GDB_WRITABLE (1<<2) +#define GDB_EXCEPTION (1<<3) + +/* Type of the mask arguments to select. */ + +#ifndef NO_FD_SET +#define SELECT_MASK fd_set +#else +#ifndef _AIX +typedef long fd_mask; +#endif +#if defined(_IBMR2) +#define SELECT_MASK void +#else +#define SELECT_MASK int +#endif +#endif + +/* Define "NBBY" (number of bits per byte) if it's not already defined. */ + +#ifndef NBBY +#define NBBY 8 +#endif + + +/* Define the number of fd_masks in an fd_set */ + +#ifndef FD_SETSIZE +#ifdef OPEN_MAX +#define FD_SETSIZE OPEN_MAX +#else +#define FD_SETSIZE 256 +#endif +#endif +#if !defined(howmany) +#define howmany(x, y) (((x)+((y)-1))/(y)) +#endif +#ifndef NFDBITS +#define NFDBITS NBBY*sizeof(fd_mask) +#endif +#define MASK_SIZE howmany(FD_SETSIZE, NFDBITS) + + +/* Stack for prompts. Each prompt is composed as a prefix, a prompt + and a suffix. The prompt to be displayed at any given time is the + one on top of the stack. A stack is necessary because of cases in + which the execution of a gdb command requires further input from + the user, like for instance 'commands' for breakpoints and + 'actions' for tracepoints. In these cases, the prompt is '>' and + gdb should process input using the asynchronous readline interface + and the event loop. In order to achieve this, we need to save + somewhere the state of GDB, i.e. that it is processing user input + as part of a command and not as part of the top level command loop. + The prompt stack represents part of the saved state. Another part + would be the function that readline would invoke after a whole line + of input has ben entered. This second piece would be something + like, for instance, where to return within the code for the actions + commands after a line has been read. This latter portion has not + beeen implemented yet. The need for a 3-part prompt arises from + the annotation level. When this is set to 2, the prompt is actually + composed of a prefix, the prompt itself and a suffix. */ + +/* At any particular time there will be always at least one prompt on + the stack, the one being currently displayed by gdb. If gdb is + using annotation level equal 2, there will be 2 prompts on the + stack: the usual one, w/o prefix and suffix (at top - 1), and the + 'composite' one with prefix and suffix added (at top). At this + time, this is the only use of the prompt stack. Resetting annotate + to 0 or 1, pops the top of the stack, resetting its size to one + element. The MAXPROMPTS limit is safe, for now. Once other cases + are dealt with (like the different prompts used for 'commands' or + 'actions') this array implementation of the prompt stack may have + to change. */ + +#define MAXPROMPTS 10 +struct prompts + { + struct + { + char *prefix; + char *prompt; + char *suffix; + } + prompt_stack[MAXPROMPTS]; + int top; + }; + +#define PROMPT(X) the_prompts.prompt_stack[the_prompts.top + X].prompt +#define PREFIX(X) the_prompts.prompt_stack[the_prompts.top + X].prefix +#define SUFFIX(X) the_prompts.prompt_stack[the_prompts.top + X].suffix + +extern void delete_file_handler PARAMS ((int)); +extern void + create_file_handler PARAMS ((int, int, file_handler_func, gdb_client_data)); +extern int gdb_do_one_event PARAMS ((void)); +extern void mark_async_signal_handler PARAMS ((async_signal_handler *)); +extern async_signal_handler * + create_async_signal_handler PARAMS ((async_handler_func *, gdb_client_data)); + diff --git a/gdb/event-top.c b/gdb/event-top.c new file mode 100644 index 0000000..5851fd2 --- /dev/null +++ b/gdb/event-top.c @@ -0,0 +1,950 @@ +/* Top level stuff for GDB, the GNU debugger. + Copyright 1999 Free Software Foundation, Inc. + Written by Elena Zannoni <ezannoni@cygnus.com> of Cygnus Solutions. + + 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 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. */ + +#include "defs.h" +#include "event-loop.h" +#ifdef HAVE_POLL +#include <sys/poll.h> +#endif +#include "inferior.h" + +/* readline include files */ +#include <readline/readline.h> +#include <readline/history.h> + +/* readline defines this. */ +#undef savestring + +extern FILE *instream; + +static void command_line_handler PARAMS ((char *)); +static void gdb_readline2 PARAMS ((void)); +static void pop_prompt PARAMS ((void)); +static void push_prompt PARAMS ((char *, char *, char *)); + +/* Signal handlers. */ +void handle_sigint PARAMS ((int)); +void handle_sigquit PARAMS ((int)); +void handle_sighup PARAMS ((int)); +void handle_sigfpe PARAMS ((int)); +void handle_sigwinch PARAMS ((int)); + +/* Functions to be invoked by the event loop in response to + signals. */ +void async_request_quit PARAMS ((void)); +void async_do_nothing PARAMS ((void)); +void async_disconnect PARAMS ((void)); +void async_float_handler PARAMS ((void)); + +/* Functions from top.c. */ +extern void command_loop_marker PARAMS ((int)); +extern int quit_cover PARAMS ((PTR)); +extern void quit_command PARAMS ((char *, int)); +extern void execute_command PARAMS ((char *, int)); + +/* Variables from top.c. */ +extern int source_line_number; +extern char *source_file_name; +extern char *source_error; +extern char *source_pre_error; +extern int history_expansion_p; +extern int server_command; + +/* If this definition isn't overridden by the header files, assume + that isatty and fileno exist on this system. */ +#ifndef ISATTY +#define ISATTY(FP) (isatty (fileno (FP))) +#endif + +/* Hook for alternate command interface. */ +void (*async_hook) PARAMS ((void)); + +/* Readline offers an alternate interface, via callback + functions. These are all included in the file callback.c in the + readline distribution. This file provides (mainly) a function, which + the event loop uses as callback (i.e. event handler) whenever an event + is detected on the standard input file descriptor. + readline_callback_read_char is called (by the GDB event loop) whenever + there is a new character ready on the input stream. This function + incrementally builds a buffer internal to readline where it + accumulates the line read up to the point of invocation. In the + special case in which the character read is newline, the function + invokes a GDB supplied callback routine, which does the processing of + a full command line. This latter routine is the asynchronous analog + of the old command_line_input in gdb. Instead of invoking (and waiting + for) readline to read the command line and pass it back to + command_loop for processing, the new command_line_handler function has + the command line already available as its parameter. INPUT_HANDLER is + to be set to the function that readline will invoke when a complete + line of input is ready. CALL_READLINE is to be set to the function + that readline offers as callback to the event_loop. */ + +void (*input_handler) PARAMS ((char *)); +void (*call_readline) PARAMS ((void)); + +/* Important variables for the event loop. */ + +/* This is used to determine if GDB is using the readline library or + its own simplified form of readline. It is used by the asynchronous + form of the set editing command. + ezannoni: as of 4/29/99 I expect that this + variable will not be used after gdb is changed to use the event + loop as default engine, and event-top.c is merged into top.c. */ +int async_command_editing_p; + +/* This variable contains the new prompt that the user sets with the + set prompt command. */ +char *new_async_prompt; + +/* This is the annotation suffix that will be used when the + annotation_level is 2. */ +char *async_annotation_suffix; + +/* This is the file descriptor for the input stream that GDB uses to + read commands from. */ +int input_fd; + +/* This is the prompt stack. Prompts will be pushed on the stack as + needed by the different 'kinds' of user inputs GDB is asking + for. See event-loop.h. */ +struct prompts the_prompts; + +/* signal handling variables */ +/* Each of these is a pointer to a function that the event loop will + invoke if the corresponding signal has received. The real signal + handlers mark these functions as ready to be executed and the event + loop, in a later iteration, calls them. See the function + invoke_async_signal_handler. */ +async_signal_handler *sigint_token; +#ifdef SIGHUP +async_signal_handler *sighup_token; +#endif +async_signal_handler *sigquit_token; +async_signal_handler *sigfpe_token; +#if defined(SIGWINCH) && defined(SIGWINCH_HANDLER) +async_signal_handler *sigwinch_token; +#endif + +/* Structure to save a partially entered command. This is used when + the user types '\' at the end of a command line. This is necessary + because each line of input is handled by a different call to + command_line_handler, and normally there is no state retained + between different calls. */ +int more_to_come = 0; + +struct readline_input_state + { + char *linebuffer; + char *linebuffer_ptr; + } +readline_input_state; + + +/* Initialize all the necessary variables, start the event loop, + register readline, and stdin. */ +void +setup_event_loop () +{ + int length = strlen (PREFIX (0)) + strlen (PROMPT (0)) + strlen (SUFFIX (0)) + 1; + char *a_prompt = (char *) xmalloc (length); + + /* Set things up for readline to be invoked via the alternate + interface, i.e. via a callback function (rl_callback_read_char). */ + call_readline = rl_callback_read_char; + + /* When readline has read an end-of-line character, it passes the + complete line to gdb for processing. command_line_handler is the + function that does this. */ + input_handler = command_line_handler; + + /* Tell readline what the prompt to display is and what function it + will need to call after a whole line is read. */ + strcpy (a_prompt, PREFIX (0)); + strcat (a_prompt, PROMPT (0)); + strcat (a_prompt, SUFFIX (0)); + rl_callback_handler_install (a_prompt, input_handler); + + /* Tell readline to use the same input stream that gdb uses. */ + rl_instream = instream; + /* Get a file descriptor for the input stream, so that we can + register it with the event loop. */ + input_fd = fileno (instream); + + /* Now we need to create the event sources for the input file descriptor. */ + /* At this point in time, this is the only event source that we + register with the even loop. Another source is going to be the + target program (inferior), but that must be registered only when + it actually exists (I.e. after we say 'run' or after we connect + to a remote target. */ +#ifdef HAVE_POLL + create_file_handler (input_fd, POLLIN, + (file_handler_func *) call_readline, 0); +#else + create_file_handler (input_fd, GDB_READABLE, + (file_handler_func *) call_readline, 0); +#endif + + /* Loop until there is something to do. This is the entry point to + the event loop engine. gdb_do_one_event will process one event + for each invocation. It always returns 1, unless there are no + more event sources registered. In this case it returns 0. */ + while (gdb_do_one_event () != 0) + ; + + /* We are done with the event loop. There are no more event sources + to listen to. So we exit GDB. */ + return; +} + +/* Change the function to be invoked every time there is a character + ready on stdin. This is used when the user sets the editing off, + therefore bypassing readline, and letting gdb handle the input + itself, via gdb_readline2. Also it is used in the opposite case in + which the user sets editing on again, by restoring readline + handling of the input. */ +void +change_line_handler () +{ + if (async_command_editing_p) + { + /* Turn on editing by using readline. */ + call_readline = rl_callback_read_char; + } + else + { + /* Turn off editing by using gdb_readline2. */ + rl_callback_handler_remove (); + call_readline = gdb_readline2; + } + + /* To tell the event loop to change the handler associated with the + input file descriptor, we need to create a new event source, + corresponding to the same fd, but with a new event handler + function. */ + delete_file_handler (input_fd); +#ifdef HAVE_POLL + create_file_handler (input_fd, POLLIN, + (file_handler_func *) call_readline, 0); +#else + create_file_handler (input_fd, GDB_READABLE, + (file_handler_func *) call_readline, 0); +#endif +} + +/* Displays the prompt. The prompt that is displayed is the current + top of the prompt stack, if the argument NEW_PROMPT is + 0. Otherwise, it displays whatever NEW_PROMPT is. This is used + after each gdb command has completed, and in the following cases: + 1. when the user enters a command line which is ended by '\' + indicating that the command will continue on the next line. + In that case the prompt that is displayed is the empty string. + 2. When the user is entering 'commands' for a breakpoint, or + actions for a tracepoint. In this case the prompt will be '>' + 3. Other???? + FIXME: 2. & 3. not implemented yet for async. */ +void +display_gdb_prompt (new_prompt) + char *new_prompt; +{ + int prompt_length = 0; + + if (!new_prompt) + { + /* Just use the top of the prompt stack. */ + prompt_length = strlen (PREFIX (0)) + + strlen (SUFFIX (0)) + + strlen (PROMPT (0)) + 1; + + new_prompt = (char *) alloca (prompt_length); + + /* Prefix needs to have new line at end. */ + strcpy (new_prompt, PREFIX (0)); + strcat (new_prompt, PROMPT (0)); + /* Suffix needs to have a new line at end and \032 \032 at + beginning. */ + strcat (new_prompt, SUFFIX (0)); + } + + if (async_command_editing_p) + { + rl_callback_handler_remove (); + rl_callback_handler_install (new_prompt, input_handler); + } + else if (new_prompt) + { + /* Don't use a _filtered function here. It causes the assumed + character position to be off, since the newline we read from + the user is not accounted for. */ + fputs_unfiltered (new_prompt, gdb_stdout); + +#ifdef MPW + /* Move to a new line so the entered line doesn't have a prompt + on the front of it. */ + fputs_unfiltered ("\n", gdb_stdout); +#endif /* MPW */ + gdb_flush (gdb_stdout); + } +} + +/* Used when the user requests a different annotation level, with + 'set annotate'. It pushes a new prompt (with prefix and suffix) on top + of the prompt stack, if the annotation level desired is 2, otherwise + it pops the top of the prompt stack when we want the annotation level + to be the normal ones (1 or 2). */ +void +change_annotation_level () +{ + char *prefix, *suffix; + + if (!PREFIX (0) || !PROMPT (0) || !SUFFIX (0)) + { + /* The prompt stack has not been initialized to "", we are + using gdb w/o the --async switch */ + warning ("Command has same effect as set annotate"); + return; + } + + if (annotation_level > 1) + { + if (!strcmp (PREFIX (0), "") && !strcmp (SUFFIX (0), "")) + { + /* Push a new prompt if the previous annotation_level was not >1. */ + prefix = (char *) alloca (strlen (async_annotation_suffix) + 10); + strcpy (prefix, "\n\032\032pre-"); + strcat (prefix, async_annotation_suffix); + strcat (prefix, "\n"); + + suffix = (char *) alloca (strlen (async_annotation_suffix) + 6); + strcpy (suffix, "\n\032\032"); + strcat (suffix, async_annotation_suffix); + strcat (suffix, "\n"); + + push_prompt (prefix, (char *) 0, suffix); + } + } + else + { + if (strcmp (PREFIX (0), "") && strcmp (SUFFIX (0), "")) + { + /* Pop the top of the stack, we are going back to annotation < 1. */ + pop_prompt (); + } + } +} + +/* Pushes a new prompt on the prompt stack. Each prompt has three + parts: prefix, prompt, suffix. Usually prefix and suffix are empty + strings, except when the annotation level is 2. Memory is allocated + within savestring for the new prompt. */ +static void +push_prompt (prefix, prompt, suffix) + char *prefix; + char *prompt; + char *suffix; +{ + the_prompts.top++; + PREFIX (0) = savestring (prefix, strlen (prefix)); + + if (prompt) + PROMPT (0) = savestring (prompt, strlen (prompt)); + else + PROMPT (0) = savestring (PROMPT (-1), strlen (PROMPT (-1))); + + SUFFIX (0) = savestring (suffix, strlen (suffix)); +} + +/* Pops the top of the prompt stack, and frees the memory allocated for it. */ +static void +pop_prompt () +{ + if (strcmp (PROMPT (0), PROMPT (-1))) + { + free (PROMPT (-1)); + PROMPT (-1) = savestring (PROMPT (0), strlen (PROMPT (0))); + } + + free (PREFIX (0)); + free (PROMPT (0)); + free (SUFFIX (0)); + the_prompts.top--; +} + +/* Handles a gdb command. This function is called by + command_line_handler, which has processed one or more input lines + into COMMAND. */ +/* NOTE: 4/30/99 This is the asynchronous version of the command_loop + function. The command_loop function will be obsolete when we + switch to use the event loop at every execution of gdb. */ +void +command_handler (command) + char *command; +{ + struct cleanup *old_chain; + int stdin_is_tty = ISATTY (stdin); + long time_at_cmd_start; +#ifdef HAVE_SBRK + long space_at_cmd_start = 0; +#endif + extern int display_time; + extern int display_space; + +#if defined(TUI) + extern int insert_mode; +#endif + + quit_flag = 0; + if (instream == stdin && stdin_is_tty) + reinitialize_more_filter (); + old_chain = make_cleanup ((make_cleanup_func) command_loop_marker, 0); + +#if defined(TUI) + insert_mode = 0; +#endif + /* If readline returned a NULL command, it means that the + connection with the terminal is gone. This happens at the + end of a testsuite run, after Expect has hung up + but GDB is still alive. In such a case, we just quit gdb + killing the inferior program too. */ + if (command == 0) + quit_command ((char *) 0, stdin == instream); + + time_at_cmd_start = get_run_time (); + + if (display_space) + { +#ifdef HAVE_SBRK + extern char **environ; + char *lim = (char *) sbrk (0); + + space_at_cmd_start = (long) (lim - (char *) &environ); +#endif + } + + execute_command (command, instream == stdin); + + /* Do any commands attached to breakpoint we stopped at. */ + bpstat_do_actions (&stop_bpstat); + do_cleanups (old_chain); + + if (display_time) + { + long cmd_time = get_run_time () - time_at_cmd_start; + + printf_unfiltered ("Command execution time: %ld.%06ld\n", + cmd_time / 1000000, cmd_time % 1000000); + } + + if (display_space) + { +#ifdef HAVE_SBRK + extern char **environ; + char *lim = (char *) sbrk (0); + long space_now = lim - (char *) &environ; + long space_diff = space_now - space_at_cmd_start; + + printf_unfiltered ("Space used: %ld (%c%ld for this command)\n", + space_now, + (space_diff >= 0 ? '+' : '-'), + space_diff); +#endif + } +} + +/* Handle a complete line of input. This is called by the callback + mechanism within the readline library. Deal with incomplete commands + as well, by saving the partial input in a global buffer. */ + +/* NOTE: 4/30/99 This is the asynchronous version of the + command_line_input function. command_line_input will become + obsolete once we use the event loop as the default mechanism in + GDB. */ +static void +command_line_handler (rl) + char *rl; +{ + static char *linebuffer = 0; + static unsigned linelength = 0; + register char *p; + char *p1; + int change_prompt = 0; + extern char *line; + extern int linesize; + char *nline; + char got_eof = 0; + + + int repeat = (instream == stdin); + + if (annotation_level > 1 && instream == stdin) + { + printf_unfiltered ("\n\032\032post-"); + printf_unfiltered (async_annotation_suffix); + printf_unfiltered ("\n"); + } + + if (linebuffer == 0) + { + linelength = 80; + linebuffer = (char *) xmalloc (linelength); + } + + p = linebuffer; + + if (more_to_come) + { + strcpy (linebuffer, readline_input_state.linebuffer); + p = readline_input_state.linebuffer_ptr; + free (readline_input_state.linebuffer); + more_to_come = 0; + change_prompt = 1; + } + +#ifdef STOP_SIGNAL + if (job_control) + signal (STOP_SIGNAL, stop_sig); +#endif + + /* Make sure that all output has been output. Some machines may let + you get away with leaving out some of the gdb_flush, but not all. */ + wrap_here (""); + gdb_flush (gdb_stdout); + gdb_flush (gdb_stderr); + + if (source_file_name != NULL) + { + ++source_line_number; + sprintf (source_error, + "%s%s:%d: Error in sourced command file:\n", + source_pre_error, + source_file_name, + source_line_number); + error_pre_print = source_error; + } + + /* If we are in this case, then command_handler will call quit + and exit from gdb. */ + if (!rl || rl == (char *) EOF) + { + got_eof = 1; + command_handler (0); + } + if (strlen (rl) + 1 + (p - linebuffer) > linelength) + { + linelength = strlen (rl) + 1 + (p - linebuffer); + nline = (char *) xrealloc (linebuffer, linelength); + p += nline - linebuffer; + linebuffer = nline; + } + p1 = rl; + /* Copy line. Don't copy null at end. (Leaves line alone + if this was just a newline) */ + while (*p1) + *p++ = *p1++; + + free (rl); /* Allocated in readline. */ + + if (p == linebuffer || *(p - 1) == '\\') + { + /* We come here also if the line entered is empty (just a 'return') */ + p--; /* Put on top of '\'. */ + + if (*p == '\\') + { + readline_input_state.linebuffer = savestring (linebuffer, + strlen (linebuffer)); + readline_input_state.linebuffer_ptr = p; + + /* We will not invoke a execute_command if there is more + input expected to complete the command. So, we need to + print an empty prompt here. */ + display_gdb_prompt (""); + more_to_come = 1; + } + } + +#ifdef STOP_SIGNAL + if (job_control) + signal (STOP_SIGNAL, SIG_DFL); +#endif + +#define SERVER_COMMAND_LENGTH 7 + server_command = + (p - linebuffer > SERVER_COMMAND_LENGTH) + && STREQN (linebuffer, "server ", SERVER_COMMAND_LENGTH); + if (server_command) + { + /* Note that we don't set `line'. Between this and the check in + dont_repeat, this insures that repeating will still do the + right thing. */ + *p = '\0'; + command_handler (linebuffer + SERVER_COMMAND_LENGTH); + display_gdb_prompt (0); + return; + } + + /* Do history expansion if that is wished. */ + if (history_expansion_p && instream == stdin + && ISATTY (instream)) + { + char *history_value; + int expanded; + + *p = '\0'; /* Insert null now. */ + expanded = history_expand (linebuffer, &history_value); + if (expanded) + { + /* Print the changes. */ + printf_unfiltered ("%s\n", history_value); + + /* If there was an error, call this function again. */ + if (expanded < 0) + { + free (history_value); + return; + } + if (strlen (history_value) > linelength) + { + linelength = strlen (history_value) + 1; + linebuffer = (char *) xrealloc (linebuffer, linelength); + } + strcpy (linebuffer, history_value); + p = linebuffer + strlen (linebuffer); + free (history_value); + } + } + + /* If we just got an empty line, and that is supposed + to repeat the previous command, return the value in the + global buffer. */ + if (repeat && p == linebuffer && *p != '\\') + { + command_handler (line); + display_gdb_prompt (0); + return; + } + + for (p1 = linebuffer; *p1 == ' ' || *p1 == '\t'; p1++); + if (repeat && !*p1) + { + command_handler (line); + display_gdb_prompt (0); + return; + } + + *p = 0; + + /* Add line to history if appropriate. */ + if (instream == stdin + && ISATTY (stdin) && *linebuffer) + add_history (linebuffer); + + /* Note: lines consisting solely of comments are added to the command + history. This is useful when you type a command, and then + realize you don't want to execute it quite yet. You can comment + out the command and then later fetch it from the value history + and remove the '#'. The kill ring is probably better, but some + people are in the habit of commenting things out. */ + if (*p1 == '#') + *p1 = '\0'; /* Found a comment. */ + + /* Save into global buffer if appropriate. */ + if (repeat) + { + if (linelength > linesize) + { + line = xrealloc (line, linelength); + linesize = linelength; + } + strcpy (line, linebuffer); + if (!more_to_come) + { + command_handler (line); + display_gdb_prompt (0); + } + return; + } + + command_handler (linebuffer); + display_gdb_prompt (0); + return; +} + +/* Does reading of input from terminal w/o the editing features + provided by the readline library. */ + +/* NOTE: 4/30/99 Asynchronous version of gdb_readline. gdb_readline + will become obsolete when the event loop is made the default + execution for gdb. */ +static void +gdb_readline2 () +{ + int c; + char *result; + int input_index = 0; + int result_size = 80; + + result = (char *) xmalloc (result_size); + + /* We still need the while loop here, even though it would seem + obvious to invoke gdb_readline2 at every character entered. If + not using the readline library, the terminal is in cooked mode, + which sends the characters all at once. Poll will notice that the + input fd has changed state only after enter is pressed. At this + point we still need to fetch all the chars entered. */ + + while (1) + { + /* Read from stdin if we are executing a user defined command. + This is the right thing for prompt_for_continue, at least. */ + c = fgetc (instream ? instream : stdin); + + if (c == EOF) + { + if (input_index > 0) + /* The last line does not end with a newline. Return it, and + if we are called again fgetc will still return EOF and + we'll return NULL then. */ + break; + free (result); + command_line_handler (0); + } + + if (c == '\n') +#ifndef CRLF_SOURCE_FILES + break; +#else + { + if (input_index > 0 && result[input_index - 1] == '\r') + input_index--; + break; + } +#endif + + result[input_index++] = c; + while (input_index >= result_size) + { + result_size *= 2; + result = (char *) xrealloc (result, result_size); + } + } + + result[input_index++] = '\0'; + command_line_handler (result); +} + + +/* Initialization of signal handlers and tokens. There is a function + handle_sig* for each of the signals GDB cares about. Specifically: + SIGINT, SIGFPE, SIGQUIT, SIGTSTP, SIGHUP, SIGWINCH. These + functions are the actual signal handlers associated to the signals + via calls to signal(). The only job for these functions is to + enqueue the appropriate event/procedure with the event loop. Such + procedures are the old signal handlers. The event loop will take + care of invoking the queued procedures to perform the usual tasks + associated with the reception of the signal. */ +/* NOTE: 4/30/99 This is the asynchronous version of init_signals. + init_signals will become obsolete as we move to have to event loop + as the default for gdb. */ +void +async_init_signals () +{ + signal (SIGINT, handle_sigint); + sigint_token = + create_async_signal_handler ((async_handler_func *) async_request_quit, NULL); + + /* If SIGTRAP was set to SIG_IGN, then the SIG_IGN will get passed + to the inferior and breakpoints will be ignored. */ +#ifdef SIGTRAP + signal (SIGTRAP, SIG_DFL); +#endif + + /* If we initialize SIGQUIT to SIG_IGN, then the SIG_IGN will get + passed to the inferior, which we don't want. It would be + possible to do a "signal (SIGQUIT, SIG_DFL)" after we fork, but + on BSD4.3 systems using vfork, that can affect the + GDB process as well as the inferior (the signal handling tables + might be in memory, shared between the two). Since we establish + a handler for SIGQUIT, when we call exec it will set the signal + to SIG_DFL for us. */ + signal (SIGQUIT, handle_sigquit); + sigquit_token = + create_async_signal_handler ((async_handler_func *) async_do_nothing, NULL); +#ifdef SIGHUP + if (signal (SIGHUP, handle_sighup) != SIG_IGN) + sighup_token = + create_async_signal_handler ((async_handler_func *) async_disconnect, NULL); + else + sighup_token = + create_async_signal_handler ((async_handler_func *) async_do_nothing, NULL); +#endif + signal (SIGFPE, handle_sigfpe); + sigfpe_token = + create_async_signal_handler ((async_handler_func *) async_float_handler, NULL); + +#if defined(SIGWINCH) && defined(SIGWINCH_HANDLER) + signal (SIGWINCH, handle_sigwinch); + sigwinch_token = + create_async_signal_handler ((async_handler_func *) SIGWINCH_HANDLER, NULL); +#endif +} + +/* Tell the event loop what to do if SIGINT is received. + See event-signal.c. */ +void +handle_sigint (sig) + int sig; +{ + signal (sig, handle_sigint); + + /* If immediate_quit is set, we go ahead and process the SIGINT right + away, even if we usually would defer this to the event loop. The + assumption here is that it is safe to process ^C immediately if + immediate_quit is set. If we didn't, SIGINT would be really + processed only the next time through the event loop. To get to + that point, though, the command that we want to interrupt needs to + finish first, which is unacceptable. */ + if (immediate_quit) + async_request_quit (); + else + /* If immediate quit is not set, we process SIGINT the next time + through the loop, which is fine. */ + mark_async_signal_handler (sigint_token); +} + +/* Do the quit. All the checks have been done by the caller. */ +void +async_request_quit () +{ + quit_flag = 1; +#ifdef REQUEST_QUIT + REQUEST_QUIT; +#else + quit (); +#endif +} + +/* Tell the event loop what to do if SIGQUIT is received. + See event-signal.c. */ +void +handle_sigquit (sig) + int sig; +{ + mark_async_signal_handler (sigquit_token); + signal (sig, handle_sigquit); +} + +/* Called by the event loop in response to a SIGQUIT. */ +void +async_do_nothing () +{ + /* Empty function body. */ +} + +#ifdef SIGHUP +/* Tell the event loop what to do if SIGHUP is received. + See event-signal.c. */ +void +handle_sighup (sig) + int sig; +{ + mark_async_signal_handler (sighup_token); + signal (sig, handle_sighup); +} + +/* Called by the event loop to process a SIGHUP. */ +void +async_disconnect () +{ + catch_errors (quit_cover, NULL, + "Could not kill the program being debugged", + RETURN_MASK_ALL); + signal (SIGHUP, SIG_DFL); /*FIXME: ??????????? */ + kill (getpid (), SIGHUP); +} +#endif + +/* Tell the event loop what to do if SIGFPE is received. + See event-signal.c. */ +void +handle_sigfpe (sig) + int sig; +{ + mark_async_signal_handler (sigfpe_token); + signal (sig, handle_sigfpe); +} + +/* Event loop will call this functin to process a SIGFPE. */ +void +async_float_handler () +{ + /* This message is based on ANSI C, section 4.7. Note that integer + divide by zero causes this, so "float" is a misnomer. */ + error ("Erroneous arithmetic operation."); +} + +/* Tell the event loop what to do if SIGWINCH is received. + See event-signal.c. */ +#if defined(SIGWINCH) && defined(SIGWINCH_HANDLER) +void +handle_sigwinch (sig) + int sig; +{ + mark_async_signal_handler (sigwinch_token); + signal (sig, handle_sigwinch); +} +#endif + + +/* Called by do_setshow_command. */ +/* ARGSUSED */ +void +set_async_editing_command (args, from_tty, c) + char *args; + int from_tty; + struct cmd_list_element *c; +{ + change_line_handler (); +} + +/* Called by do_setshow_command. */ +/* ARGSUSED */ +void +set_async_annotation_level (args, from_tty, c) + char *args; + int from_tty; + struct cmd_list_element *c; +{ + change_annotation_level (); +} + +/* Called by do_setshow_command. */ +/* ARGSUSED */ +void +set_async_prompt (args, from_tty, c) + char *args; + int from_tty; + struct cmd_list_element *c; +{ + PROMPT (0) = savestring (new_async_prompt, strlen (new_async_prompt)); +} + + + + |