diff options
-rw-r--r-- | gdb/ChangeLog | 14 | ||||
-rw-r--r-- | gdb/Makefile.in | 6 | ||||
-rw-r--r-- | gdb/ser-event.c | 220 | ||||
-rw-r--r-- | gdb/ser-event.h | 51 | ||||
-rw-r--r-- | gdb/serial.c | 61 | ||||
-rw-r--r-- | gdb/serial.h | 7 |
6 files changed, 334 insertions, 25 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index b77e5e4..55fd901 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,19 @@ 2016-04-12 Pedro Alves <palves@redhat.com> + * Makefile.in (SFILES): Add ser-event.c. + (HFILES_NO_SRCDIR): Add ser-event.h. + (COMMON_OBS): Add ser-event.o. + * ser-event.c, ser-event.h: New files. + * serial.c (new_serial): New function, factored out from + (serial_fdopen_ops): ... this. + (serial_open_ops_1): New function, factored out from + (serial_open): ... this. + (serial_open_ops): New function. + * serial.h (struct serial): Forware declare. + (serial_open_ops): New declaration. + +2016-04-12 Pedro Alves <palves@redhat.com> + * serial.c (serial_open, serial_fdopen_ops, do_serial_close): Remove references to name. * serial.h (struct serial) <name>: Delete. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index ccd5c23..2af78a5 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -870,7 +870,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ prologue-value.c psymtab.c \ regcache.c reggroups.c remote.c remote-fileio.c remote-notif.c reverse.c \ sentinel-frame.c \ - serial.c ser-base.c ser-unix.c skip.c \ + serial.c ser-base.c ser-unix.c ser-event.c skip.c \ solib.c solib-target.c source.c \ stabsread.c stack.c probe.c stap-probe.c std-regs.c \ symfile.c symfile-debug.c symfile-mem.c symmisc.c symtab.c \ @@ -987,7 +987,7 @@ common/common-exceptions.h target/target.h common/symbol.h \ common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \ common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h nat/amd64-linux-siginfo.h\ nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h arch/aarch64-insn.h \ -tid-parse.h +tid-parse.h ser-event.h # Header files that already have srcdir in them, or which are in objdir. @@ -1065,7 +1065,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ ada-typeprint.o c-typeprint.o f-typeprint.o m2-typeprint.o \ ada-valprint.o c-valprint.o cp-valprint.o d-valprint.o f-valprint.o \ m2-valprint.o \ - serial.o mdebugread.o top.o utils.o \ + ser-event.o serial.o mdebugread.o top.o utils.o \ ui-file.o \ user-regs.o \ frame.o frame-unwind.o doublest.o \ diff --git a/gdb/ser-event.c b/gdb/ser-event.c new file mode 100644 index 0000000..4851672 --- /dev/null +++ b/gdb/ser-event.c @@ -0,0 +1,220 @@ +/* Serial interface for a selectable event. + Copyright (C) 2016 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 "ser-event.h" +#include "serial.h" +#include "common/filestuff.h" + +/* On POSIX hosts, a serial_event is basically an abstraction for the + classical self-pipe trick. + + On Windows, a serial_event is a wrapper around a native Windows + event object. Because we want to interface with gdb_select, which + takes file descriptors, we need to wrap that Windows event object + in a file descriptor. As _open_osfhandle can not be used with + event objects, we instead create a dummy file wrap that in a file + descriptor with _open_osfhandle, and pass that as selectable + descriptor to callers. As Windows' gdb_select converts file + descriptors back to Windows handles by calling serial->wait_handle, + nothing ever actually waits on that file descriptor. */ + +struct serial_event_state + { +#ifdef USE_WIN32API + /* The Windows event object, created with CreateEvent. */ + HANDLE event; +#else + /* The write side of the pipe. The read side is in + serial->fd. */ + int write_fd; +#endif + }; + +/* Open a new serial event. */ + +static int +serial_event_open (struct serial *scb, const char *name) +{ + struct serial_event_state *state; + + state = XNEW (struct serial_event_state); + scb->state = state; + +#ifndef USE_WIN32API + { + int fds[2]; + + if (gdb_pipe_cloexec (fds) == -1) + internal_error (__FILE__, __LINE__, + "creating serial event pipe failed."); + + fcntl (fds[0], F_SETFL, O_NONBLOCK); + fcntl (fds[1], F_SETFL, O_NONBLOCK); + + scb->fd = fds[0]; + state->write_fd = fds[1]; + } +#else + { + /* A dummy file object that can be wrapped in a file descriptor. + We don't need to store this handle because closing the file + descriptor automatically closes this. */ + HANDLE dummy_file; + + /* A manual-reset event. */ + state->event = CreateEvent (0, TRUE, FALSE, 0); + + /* The dummy file handle. Created just so we have something + wrappable in a file descriptor. */ + dummy_file = CreateFile ("nul", 0, 0, NULL, OPEN_EXISTING, 0, NULL); + scb->fd = _open_osfhandle ((intptr_t) dummy_file, 0); + } +#endif + + return 0; +} + +static void +serial_event_close (struct serial *scb) +{ + struct serial_event_state *state = (struct serial_event_state *) scb->state; + + close (scb->fd); +#ifndef USE_WIN32API + close (state->write_fd); +#else + CloseHandle (state->event); +#endif + + scb->fd = -1; + + xfree (state); + scb->state = NULL; +} + +#ifdef USE_WIN32API + +/* Implementation of the wait_handle method. Returns the native + Windows event object handle. */ + +static void +serial_event_wait_handle (struct serial *scb, HANDLE *read, HANDLE *except) +{ + struct serial_event_state *state = (struct serial_event_state *) scb->state; + + *read = state->event; +} + +#endif + +/* The serial_ops for struct serial_event objects. Note we never + register this serial type with serial_add_interface, because this + is internal implementation detail never to be used by remote + targets for protocol transport. */ + +static const struct serial_ops serial_event_ops = +{ + "event", + serial_event_open, + serial_event_close, + NULL, /* fdopen */ + NULL, /* readchar */ + NULL, /* write */ + NULL, /* flush_output */ + NULL, /* flush_input */ + NULL, /* send_break */ + NULL, /* go_raw */ + NULL, /* get_tty_state */ + NULL, /* copy_tty_state */ + NULL, /* set_tty_state */ + NULL, /* print_tty_state */ + NULL, /* noflush_set_tty_state */ + NULL, /* setbaudrate */ + NULL, /* setstopbits */ + NULL, /* setparity */ + NULL, /* drain_output */ + NULL, /* async */ + NULL, /* read_prim */ + NULL, /* write_prim */ + NULL, /* avail */ +#ifdef USE_WIN32API + serial_event_wait_handle, +#endif +}; + +/* See ser-event.h. */ + +struct serial_event * +make_serial_event (void) +{ + return (struct serial_event *) serial_open_ops (&serial_event_ops); +} + +/* See ser-event.h. */ + +int +serial_event_fd (struct serial_event *event) +{ + struct serial *ser = (struct serial *) event; + + return ser->fd; +} + +/* See ser-event.h. */ + +void +serial_event_set (struct serial_event *event) +{ + struct serial *ser = (struct serial *) event; + struct serial_event_state *state = (struct serial_event_state *) ser->state; +#ifndef USE_WIN32API + int r; + char c = '+'; /* Anything. */ + + do + { + r = write (state->write_fd, &c, 1); + } + while (r < 0 && errno == EINTR); +#else + SetEvent (state->event); +#endif +} + +/* See ser-event.h. */ + +void +serial_event_clear (struct serial_event *event) +{ + struct serial *ser = (struct serial *) event; + struct serial_event_state *state = (struct serial_event_state *) ser->state; +#ifndef USE_WIN32API + int r; + + do + { + char c; + + r = read (ser->fd, &c, 1); + } + while (r > 0 || (r < 0 && errno == EINTR)); +#else + ResetEvent (state->event); +#endif +} diff --git a/gdb/ser-event.h b/gdb/ser-event.h new file mode 100644 index 0000000..b6654c4 --- /dev/null +++ b/gdb/ser-event.h @@ -0,0 +1,51 @@ +/* Serial interface for a selectable event. + Copyright (C) 2016 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/>. */ + +#ifndef SER_EVENT_H +#define SER_EVENT_H + +/* This is used to be able to signal the event loop (or any other + select/poll) of events, in a race-free manner. + + For example, a signal handler can defer non-async-signal-safe work + to the event loop, by having the signal handler set a struct + serial_event object, and having the event loop wait for that same + object to the readable. Once readable, the event loop breaks out + of select/poll and calls a registered callback that does the + deferred work. */ + +struct serial_event; + +/* Make a new serial_event object. */ +struct serial_event *make_serial_event (void); + +/* Return the FD that can be used by select/poll to wait for the + event. The only valid operation on this object is to wait until it + is readable. */ +extern int serial_event_fd (struct serial_event *event); + +/* Set the event. This signals the file descriptor returned by + serial_event_fd as readable. */ +extern void serial_event_set (struct serial_event *event); + +/* Clear the event. The file descriptor returned by serial_event_fd + is not longer readable after this, until a new serial_event_set + call is made. */ +extern void serial_event_clear (struct serial_event *event); + +#endif diff --git a/gdb/serial.c b/gdb/serial.c index 5d242b9..a95f85e 100644 --- a/gdb/serial.c +++ b/gdb/serial.c @@ -179,6 +179,27 @@ serial_for_fd (int fd) return NULL; } +/* Create a new serial for OPS. */ + +static struct serial * +new_serial (const struct serial_ops *ops) +{ + struct serial *scb; + + scb = XCNEW (struct serial); + + scb->ops = ops; + + scb->bufp = scb->buf; + scb->error_fd = -1; + scb->refcnt = 1; + + return scb; +} + +static struct serial *serial_open_ops_1 (const struct serial_ops *ops, + const char *open_name); + /* Open up a device or a network socket, depending upon the syntax of NAME. */ struct serial * @@ -210,14 +231,17 @@ serial_open (const char *name) if (!ops) return NULL; - scb = XNEW (struct serial); + return serial_open_ops_1 (ops, open_name); +} - scb->ops = ops; +/* Open up a serial for OPS, passing OPEN_NAME to the open method. */ - scb->bufcnt = 0; - scb->bufp = scb->buf; - scb->error_fd = -1; - scb->refcnt = 1; +static struct serial * +serial_open_ops_1 (const struct serial_ops *ops, const char *open_name) +{ + struct serial *scb; + + scb = new_serial (ops); /* `...->open (...)' would get expanded by the open(2) syscall macro. */ if ((*scb->ops->open) (scb, open_name)) @@ -227,10 +251,6 @@ serial_open (const char *name) } scb->next = scb_base; - scb->debug_p = 0; - scb->async_state = 0; - scb->async_handler = NULL; - scb->async_context = NULL; scb_base = scb; if (serial_logfile != NULL) @@ -243,6 +263,14 @@ serial_open (const char *name) return scb; } +/* See serial.h. */ + +struct serial * +serial_open_ops (const struct serial_ops *ops) +{ + return serial_open_ops_1 (ops, NULL); +} + /* Open a new serial stream using a file handle, using serial interface ops OPS. */ @@ -261,20 +289,9 @@ serial_fdopen_ops (const int fd, const struct serial_ops *ops) if (!ops) return NULL; - scb = XCNEW (struct serial); - - scb->ops = ops; - - scb->bufcnt = 0; - scb->bufp = scb->buf; - scb->error_fd = -1; - scb->refcnt = 1; + scb = new_serial (ops); scb->next = scb_base; - scb->debug_p = 0; - scb->async_state = 0; - scb->async_handler = NULL; - scb->async_context = NULL; scb_base = scb; if ((ops->fdopen) != NULL) diff --git a/gdb/serial.h b/gdb/serial.h index b339f66..10b0643 100644 --- a/gdb/serial.h +++ b/gdb/serial.h @@ -34,6 +34,9 @@ struct ui_file; typedef void *serial_ttystate; struct serial; +struct serial_ops; + +/* Create a new serial for OPS. The new serial is not opened. */ /* Try to open NAME. Returns a new `struct serial *' on success, NULL on failure. The new serial object has a reference count of 1. @@ -44,6 +47,10 @@ struct serial; extern struct serial *serial_open (const char *name); +/* Open a new serial stream using OPS. */ + +extern struct serial *serial_open_ops (const struct serial_ops *ops); + /* Returns true if SCB is open. */ extern int serial_is_open (struct serial *scb); |