From 84603566b73e9ad18d094da3b7510ab480db8170 Mon Sep 17 00:00:00 2001 From: Sandra Loosemore Date: Tue, 6 Jan 2009 17:07:08 +0000 Subject: [PATCH] 2009-01-06 Sandra Loosemore gdb/ * ser-tcp.c: Adjust includes. (tcp_set_cmdlist, tcp_show_cmdlist): Declare. (tcp_auto_retry, tcp_retry_limit): Declare. (TIMEOUT): Remove, in favor of tcp_retry_limit. (POLL_INTERVAL): Increase to 5, in favor of backoff logic. (wait_for_connect): New function. (net_open): Use it. Add auto-retry logic. (set_tcp_cmd, show_tcp_cmd): New functions. (_initialize_ser_tcp): Initialize new "set/show tcp auto-retry" and "set/show tcp connect-timeout" commands. * NEWS: Document new commands. gdb/doc/ * gdb.texinfo (Remote Configuration): Document new "set/show tcp auto-retry" and "set/show tcp connect-timeout" commands. --- gdb/ChangeLog | 14 +++ gdb/NEWS | 8 ++ gdb/doc/ChangeLog | 6 ++ gdb/doc/gdb.texinfo | 30 ++++++ gdb/ser-tcp.c | 244 +++++++++++++++++++++++++++++++++----------- 5 files changed, 242 insertions(+), 60 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index b959e9aec2a..e848895fcf7 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,17 @@ +2009-01-06 Sandra Loosemore + + * ser-tcp.c: Adjust includes. + (tcp_set_cmdlist, tcp_show_cmdlist): Declare. + (tcp_auto_retry, tcp_retry_limit): Declare. + (TIMEOUT): Remove, in favor of tcp_retry_limit. + (POLL_INTERVAL): Increase to 5, in favor of backoff logic. + (wait_for_connect): New function. + (net_open): Use it. Add auto-retry logic. + (set_tcp_cmd, show_tcp_cmd): New functions. + (_initialize_ser_tcp): Initialize new "set/show tcp auto-retry" + and "set/show tcp connect-timeout" commands. + * NEWS: Document new commands. + 2009-01-05 Tom Tromey * python/python-internal.h (Py_ssize_t): Define as int. diff --git a/gdb/NEWS b/gdb/NEWS index 415a4bed14c..4532f9e0bdb 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -169,6 +169,14 @@ set target-async with GDB while the target is running. "show target-async" displays the current state of asynchronous execution of the target. +set tcp auto-retry (on|off) +show tcp auto-retry +set tcp connect-timeout +show tcp connect-timeout + These commands allow GDB to retry failed TCP connections to a remote stub + with a specified timeout period; this is useful if the stub is launched + in parallel with GDB but may not be ready to accept connections immediately. + macro define macro list macro undef diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index b79252cfe4b..dfe9526761c 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,9 @@ +2009-01-06 Sandra Loosemore + + * gdb.texinfo (Remote Configuration): Document new + "set/show tcp auto-retry" and "set/show tcp connect-timeout" + commands. + 2008-12-28 Pedro Alves * gdbint.texinfo (gdbarch_cannot_fetch_register): Don't mention diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 6ef124c24e7..f6b7a1cb20d 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -14206,6 +14206,36 @@ Select the file used for @code{run} with @code{target extended-remote}. This should be set to a filename valid on the target system. If it is not set, the target will use a default filename (e.g.@: the last program run). + +@kindex set tcp +@kindex show tcp +@item set tcp auto-retry on +@cindex auto-retry, for remote TCP target +Enable auto-retry for remote TCP connections. This is useful if the remote +debugging agent is launched in parallel with @value{GDBN}; there is a race +condition because the agent may not become ready to accept the connection +before @value{GDBN} attempts to connect. When auto-retry is +enabled, if the initial attempt to connect fails, @value{GDBN} reattempts +to establish the connection using the timeout specified by +@code{set tcp connect-timeout}. + +@item set tcp auto-retry off +Do not auto-retry failed TCP connections. + +@item show tcp auto-retry +Show the current auto-retry setting. + +@item set tcp connect-timeout @var{seconds} +@cindex connection timeout, for remote TCP target +@cindex timeout, for remote target connection +Set the timeout for establishing a TCP connection to the remote target to +@var{seconds}. The timeout affects both polling to retry failed connections +(enabled by @code{set tcp auto-retry on}) and waiting for connections +that are merely slow to complete, and represents an approximate cumulative +value. + +@item show tcp connect-timeout +Show the current connection timeout setting. @end table @cindex remote packets, enabling and disabling diff --git a/gdb/ser-tcp.c b/gdb/ser-tcp.c index e3cf9704a4d..7792133dc2a 100644 --- a/gdb/ser-tcp.c +++ b/gdb/ser-tcp.c @@ -22,6 +22,9 @@ #include "serial.h" #include "ser-base.h" #include "ser-tcp.h" +#include "gdbcmd.h" +#include "cli/cli-decode.h" +#include "cli/cli-setshow.h" #include @@ -49,6 +52,7 @@ #include #include "gdb_string.h" +#include "gdb_select.h" #ifndef HAVE_SOCKLEN_T typedef int socklen_t; @@ -56,10 +60,93 @@ typedef int socklen_t; void _initialize_ser_tcp (void); -/* seconds to wait for connect */ -#define TIMEOUT 15 +/* For "set tcp" and "show tcp". */ + +static struct cmd_list_element *tcp_set_cmdlist; +static struct cmd_list_element *tcp_show_cmdlist; + +/* Whether to auto-retry refused connections. */ + +static int tcp_auto_retry = 1; + +/* Timeout period for connections, in seconds. */ + +static int tcp_retry_limit = 15; + /* how many times per second to poll deprecated_ui_loop_hook */ -#define POLL_INTERVAL 2 + +#define POLL_INTERVAL 5 + +/* Helper function to wait a while. If SCB is non-null, wait on its + file descriptor. Otherwise just wait on a timeout, updating *POLLS. + Returns -1 on timeout or interrupt, otherwise the value of select. */ + +static int +wait_for_connect (struct serial *scb, int *polls) +{ + struct timeval t; + int n; + + /* While we wait for the connect to complete, + poll the UI so it can update or the user can + interrupt. */ + if (deprecated_ui_loop_hook && deprecated_ui_loop_hook (0)) + { + errno = EINTR; + return -1; + } + + /* Check for timeout. */ + if (*polls > tcp_retry_limit * POLL_INTERVAL) + { + errno = ETIMEDOUT; + return -1; + } + + /* Back off to polling once per second after the first POLL_INTERVAL + polls. */ + if (*polls < POLL_INTERVAL) + { + t.tv_sec = 0; + t.tv_usec = 1000000 / POLL_INTERVAL; + } + else + { + t.tv_sec = 1; + t.tv_usec = 0; + } + + if (scb) + { + fd_set rset, wset, eset; + FD_ZERO (&rset); + FD_SET (scb->fd, &rset); + wset = rset; + eset = rset; + + /* POSIX systems return connection success or failure by signalling + wset. Windows systems return success in wset and failure in + eset. + + We must call select here, rather than gdb_select, because + the serial structure has not yet been initialized - the + MinGW select wrapper will not know that this FD refers + to a socket. */ + n = select (scb->fd + 1, &rset, &wset, &eset, &t); + } + else + /* Use gdb_select here, since we have no file descriptors, and on + Windows, plain select doesn't work in that case. */ + n = gdb_select (0, NULL, NULL, NULL, &t); + + /* If we didn't time out, only count it as one poll. */ + if (n > 0 || *polls < POLL_INTERVAL) + (*polls)++; + else + (*polls) += POLL_INTERVAL; + + return n; +} /* Open a tcp socket */ @@ -76,6 +163,7 @@ net_open (struct serial *scb, const char *name) #else int ioarg; #endif + int polls = 0; use_udp = 0; if (strncmp (name, "udp:", 4) == 0) @@ -108,6 +196,13 @@ net_open (struct serial *scb, const char *name) return -1; } + sockaddr.sin_family = PF_INET; + sockaddr.sin_port = htons (port); + memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr, + sizeof (struct in_addr)); + + retry: + if (use_udp) scb->fd = socket (PF_INET, SOCK_DGRAM, 0); else @@ -116,11 +211,6 @@ net_open (struct serial *scb, const char *name) if (scb->fd < 0) return -1; - sockaddr.sin_family = PF_INET; - sockaddr.sin_port = htons (port); - memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr, - sizeof (struct in_addr)); - /* set socket nonblocking */ ioarg = 1; ioctl (scb->fd, FIONBIO, &ioarg); @@ -128,68 +218,51 @@ net_open (struct serial *scb, const char *name) /* Use Non-blocking connect. connect() will return 0 if connected already. */ n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr)); - if (n < 0 + if (n < 0) + { #ifdef USE_WIN32API - /* Under Windows, calling "connect" with a non-blocking socket - results in WSAEWOULDBLOCK, not WSAEINPROGRESS. */ - && WSAGetLastError() != WSAEWOULDBLOCK + int err = WSAGetLastError(); #else - && errno != EINPROGRESS + int err = errno; #endif - ) - { + + /* Maybe we're waiting for the remote target to become ready to + accept connections. */ + if (tcp_auto_retry #ifdef USE_WIN32API - errno = WSAGetLastError(); + && err == WSAECONNREFUSED +#else + && err == ECONNREFUSED #endif - net_close (scb); - return -1; - } + && wait_for_connect (NULL, &polls) >= 0) + { + close (scb->fd); + goto retry; + } - if (n) - { - /* looks like we need to wait for the connect */ - struct timeval t; - fd_set rset, wset, eset; - int polls = 0; - FD_ZERO (&rset); + if ( +#ifdef USE_WIN32API + /* Under Windows, calling "connect" with a non-blocking socket + results in WSAEWOULDBLOCK, not WSAEINPROGRESS. */ + err != WSAEWOULDBLOCK +#else + err != EINPROGRESS +#endif + ) + { + errno = err; + net_close (scb); + return -1; + } + /* looks like we need to wait for the connect */ do { - /* While we wait for the connect to complete, - poll the UI so it can update or the user can - interrupt. */ - if (deprecated_ui_loop_hook) - { - if (deprecated_ui_loop_hook (0)) - { - errno = EINTR; - net_close (scb); - return -1; - } - } - - FD_SET (scb->fd, &rset); - wset = rset; - eset = rset; - t.tv_sec = 0; - t.tv_usec = 1000000 / POLL_INTERVAL; - - /* POSIX systems return connection success or failure by signalling - wset. Windows systems return success in wset and failure in - eset. - - We must call select here, rather than gdb_select, because - the serial structure has not yet been initialized - the - MinGW select wrapper will not know that this FD refers - to a socket. */ - n = select (scb->fd + 1, &rset, &wset, &eset, &t); - polls++; + n = wait_for_connect (scb, &polls); } - while (n == 0 && polls <= TIMEOUT * POLL_INTERVAL); - if (n < 0 || polls > TIMEOUT * POLL_INTERVAL) + while (n == 0); + if (n < 0) { - if (polls > TIMEOUT * POLL_INTERVAL) - errno = ETIMEDOUT; net_close (scb); return -1; } @@ -207,6 +280,18 @@ net_open (struct serial *scb, const char *name) res = getsockopt (scb->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len); if (res < 0 || err) { + /* Maybe the target still isn't ready to accept the connection. */ + if (tcp_auto_retry +#ifdef USE_WIN32API + && err == WSAECONNREFUSED +#else + && err == ECONNREFUSED +#endif + && wait_for_connect (NULL, &polls) >= 0) + { + close (scb->fd); + goto retry; + } if (err) errno = err; net_close (scb); @@ -264,13 +349,27 @@ ser_tcp_send_break (struct serial *scb) return (serial_write (scb, "\377\363", 2)); } +/* Support for "set tcp" and "show tcp" commands. */ + +static void +set_tcp_cmd (char *args, int from_tty) +{ + help_list (tcp_set_cmdlist, "set tcp ", -1, gdb_stdout); +} + +static void +show_tcp_cmd (char *args, int from_tty) +{ + help_list (tcp_show_cmdlist, "show tcp ", -1, gdb_stdout); +} + + void _initialize_ser_tcp (void) { #ifdef USE_WIN32API /* Do nothing; the TCP serial operations will be initialized in ser-mingw.c. */ - return; #else struct serial_ops *ops; ops = XMALLOC (struct serial_ops); @@ -297,4 +396,29 @@ _initialize_ser_tcp (void) ops->write_prim = net_write_prim; serial_add_interface (ops); #endif /* USE_WIN32API */ + + add_prefix_cmd ("tcp", class_maintenance, set_tcp_cmd, _("\ +TCP protocol specific variables\n\ +Configure variables specific to remote TCP connections"), + &tcp_set_cmdlist, "set tcp ", + 0 /* allow-unknown */, &setlist); + add_prefix_cmd ("tcp", class_maintenance, show_tcp_cmd, _("\ +TCP protocol specific variables\n\ +Configure variables specific to remote TCP connections"), + &tcp_show_cmdlist, "show tcp ", + 0 /* allow-unknown */, &showlist); + + add_setshow_boolean_cmd ("auto-retry", class_obscure, + &tcp_auto_retry, _("\ +Set auto-retry on socket connect"), _("\ +Show auto-retry on socket connect"), + NULL, NULL, NULL, + &tcp_set_cmdlist, &tcp_show_cmdlist); + + add_setshow_uinteger_cmd ("connect-timeout", class_obscure, + &tcp_retry_limit, _("\ +Set timeout limit for socket connection"), _("\ +Show timeout limit for socket connection"), + NULL, NULL, NULL, + &tcp_set_cmdlist, &tcp_show_cmdlist); } -- 2.30.2