[PATCH] gdb: Don't let SIGWINCH interrupt waiting for remote target

Andrew Burgess andrew.burgess@embecosm.com
Thu Jul 9 15:02:28 GMT 2020


When connecting to a remote target a SIGWINCH will cause GDB to stop
waiting an complain about an interrupted system call, so:

  (gdb) target remote :1234
  # User resizes termainal...
  :1234: Interrupted system call.
  (gdb)

While investigating I also discovered that hitting Ctrl-C looks like
this:

  (gdb) target remote :1234
  # User hits Ctrl-C
  ^C:1234: Interrupted system call.
  (gdb) Quit
  (gdb)

Which doesn't seem great.

In this commit I wrap the code that waits for a connection in a loop
that will swallow any EINTR errors.  As a result SIGWINCH no longer
causes the connection loop to break, but nor does Ctrl-C.  So I add a
check of the QUIT flag, and now we get:

  (gdb) target remote :1234
  # User resizes termainal, and GDB continues to wait.

And also:

  (gdb) target remote :1234
  # User hits Ctrl-C
  ^CQuit
  (gdb)

Which seems better.

There is a test for this, which I've tried to make resilient against
timing issues, but I don't know how good it will actually turn out to
be.

gdb/ChangeLog:

	PR remote/26221
	* ser-tcp.c (wait_for_connection): Update comment.  Ignore EINTR
	errors, and handle QUIT from user.

gdb/testsuite/ChangeLog:

	PR remote/26221
	* gdb.server/connect-resize-quit.exp: New file.
---
 gdb/ChangeLog                                 |  6 ++
 gdb/ser-tcp.c                                 | 57 ++++++------
 gdb/testsuite/ChangeLog                       |  5 ++
 .../gdb.server/connect-resize-quit.exp        | 88 +++++++++++++++++++
 4 files changed, 131 insertions(+), 25 deletions(-)
 create mode 100644 gdb/testsuite/gdb.server/connect-resize-quit.exp

diff --git a/gdb/ser-tcp.c b/gdb/ser-tcp.c
index 7dd903dfaad..8fc19b959fc 100644
--- a/gdb/ser-tcp.c
+++ b/gdb/ser-tcp.c
@@ -82,10 +82,12 @@ static unsigned int tcp_retry_limit = 15;
 
 #define POLL_INTERVAL 5
 
-/* Helper function to wait a while.  If SOCK is not -1, wait on its
-   file descriptor.  Otherwise just wait on a timeout, updating
-   *POLLS.  Returns -1 on timeout or interrupt, otherwise the value of
-   select.  */
+/* Helper function to wait a while.  If SOCK is not -1, wait on its file
+   descriptor.  Otherwise just wait on a timeout, updating *POLLS.
+
+   Returns -1 on timeout, otherwise the value of select.  Interrupts
+   (EINTR) are swallowed within this function, however, this function will
+   throw an error if the user interrupts the wait.  */
 
 static int
 wait_for_connect (int sock, unsigned int *polls)
@@ -122,29 +124,34 @@ wait_for_connect (int sock, unsigned int *polls)
       t.tv_usec = 0;
     }
 
-  if (sock >= 0)
+  do
     {
-      fd_set rset, wset, eset;
-
-      FD_ZERO (&rset);
-      FD_SET (sock, &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 (sock + 1, &rset, &wset, &eset, &t);
+      QUIT;
+      if (sock >= 0)
+	{
+	  fd_set rset, wset, eset;
+
+	  FD_ZERO (&rset);
+	  FD_SET (sock, &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 (sock + 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);
     }
-  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);
+  while (n == -1 && errno == EINTR);
 
   /* If we didn't time out, only count it as one poll.  */
   if (n > 0 || *polls < POLL_INTERVAL)
diff --git a/gdb/testsuite/gdb.server/connect-resize-quit.exp b/gdb/testsuite/gdb.server/connect-resize-quit.exp
new file mode 100644
index 00000000000..cd5db4a3675
--- /dev/null
+++ b/gdb/testsuite/gdb.server/connect-resize-quit.exp
@@ -0,0 +1,88 @@
+# Copyright 2020 Free Software Foundation, Inc.
+
+# 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/>.  */
+
+# Check that a terminal resize while waiting to connect to a target
+# doesn't cause the wait to be abandoned.  Then check that the wait
+# can be interrupted with SIGINT.
+
+proc test_signal { sig } {
+    global gdb_prompt
+
+    with_test_prefix "SIG${sig}" {
+
+	set testname "waiting on target remote"
+
+	gdb_start
+
+	# Open a connection to a remote target, but use a port number that is
+	# unlikely to actually be in use.  We want this connection to block
+	# waiting for a remote so we can test GDB's behaviour in this blocked
+	# state.
+	gdb_test_no_output "set tcp connect-timeout 2"
+
+	set timeout_count 0
+	while { $timeout_count < 5} {
+	    # Try connecting.  This should block waiting for a remote to appear.
+	    send_gdb "target remote :0\n"
+
+	    # Now send a signal to GDB and follow up by sending some other command.
+	    set gdb_pid [exp_pid -i [board_info host fileid]]
+	    remote_exec host "kill -${sig} $gdb_pid"
+	    send_gdb "echo xxyyzz\\n\n"
+
+	    # Now try to figure out what GDB did.
+	    set got_timeout false
+	    gdb_test_multiple "" "$testname" {
+		-re "^target remote :0\r\n:0: Connection timed out\\..*$gdb_prompt $" {
+		    # It's possible that we didn't send the signal quickly enough.
+		    # Maybe the testing machine is heavily loaded?
+		    set got_timeout true
+		}
+		-re "^target remote :0\r\n:0: Interrupted system call\\.\r\n$gdb_prompt.*xxyyzz.*$gdb_prompt $" {
+		    fail "$gdb_test_name (interrupted by $sig)"
+		    return
+		}
+		-re "^target remote :0\r\necho xxyyzz\\\\n\r.:0: Connection timed out.\r\n$gdb_prompt.*xxyyzz.*$gdb_prompt $" {
+		    if { $sig == "WINCH" } {
+			pass $gdb_test_name
+		    } else {
+			fail "$gdb_test_name (unexpected timeout)"
+		    }
+		    return
+		}
+		-re "^target remote :0\r\nQuit\r\n$gdb_prompt.*xxyyzz.*$gdb_prompt $" {
+		    if { $sig == "INT" } {
+			pass $gdb_test_name
+		    } else {
+			fail "$gdb_test_name (unexpected QUIT)"
+		    }
+		    return
+		}
+	    }
+
+	    if { ! $got_timeout } {
+		# Some other unknown error.
+		return
+	    }
+
+	    incr timeout_count
+	}
+
+	unresolved "$testname (too many timeouts)"
+    }
+}
+
+test_signal WINCH
+test_signal INT
-- 
2.25.4



More information about the Gdb-patches mailing list