This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFA] stdio gdbserver


Hi.

This patch adds stdio support to gdbserver.
It's a revamp of a patch I submitted a fair while ago.

E.g. target remote | ssh hostname gdbserver - hello

Ok to check in?

I also have a board description file akin to native-gdbserver
that lets me run the testsuite with it.
I'll add that to the wiki alongside the one for native-gdbserver
after this goes in.


2011-12-01  Doug Evans  <dje@google.com>

	* NEWS: Add entry for stdio gdbserver.

	gdbserver/
	* linux-low.c (linux_create_inferior): If stdio connection,
	redirect stdin from /dev/null, stdout to stderr.
	* remote-utils.c (remote_is_stdio): New static global.
	(remote_connection_is_stdio): New function.
	(remote_prepare): Handle stdio connection.
	(remote_open): Ditto.
	(remote_close): Don't close stdin for stdio connections.
	(read_prim,write_prim): New functions.  Replace all calls to
	read/write to these.
	* server.c (main): Watch for "-" argument.  Move call to
	remote_prepare before start_inferior.  If client closes connection,
	exit if stdio connection.  Add debugging printf.
	* server.h (STDIO_CONNECTION_NAME): New macro.
	(remote_connection_is_stdio): Declare.

	doc/
	* gdb.texinfo (Server): Document -/stdio argument to gdbserver.

	testsuite/
	* lib/gdbserver-support.exp (gdbserver_default_get_remote_address): New
	function.
	(gdbserver_start): Call gdb,get_remote_address to compute argument
	to "target remote" command.

Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.469
diff -u -p -r1.469 NEWS
--- NEWS	20 Nov 2011 23:59:47 -0000	1.469
+++ NEWS	1 Dec 2011 21:14:00 -0000
@@ -3,6 +3,9 @@
 
 *** Changes since GDB 7.3.1
 
+* GDBserver now supports stdio connections.
+  E.g. (gdb) target remote | ssh myhost gdbserver - hello
+
 * GDB now allows you to skip uninteresting functions and files when
   stepping with the "skip function" and "skip file" commands.
 
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.901
diff -u -p -r1.901 gdb.texinfo
--- doc/gdb.texinfo	22 Nov 2011 21:25:18 -0000	1.901
+++ doc/gdb.texinfo	1 Dec 2011 21:14:00 -0000
@@ -16767,8 +16767,10 @@ syntax is:
 target> gdbserver @var{comm} @var{program} [ @var{args} @dots{} ]
 @end smallexample
 
-@var{comm} is either a device name (to use a serial line) or a TCP
-hostname and portnumber.  For example, to debug Emacs with the argument
+@var{comm} is either a device name (to use a serial line), or a TCP
+hostname and portnumber, or @code{-} or @code{stdio} to use
+stdin/stdout of @code{gdbserver}.
+For example, to debug Emacs with the argument
 @samp{foo.txt} and communicate with @value{GDBN} over the serial port
 @file{/dev/com1}:
 
@@ -16797,6 +16799,23 @@ conflicts with another service, @code{gd
 and exits.}  You must use the same port number with the host @value{GDBN}
 @code{target remote} command.
 
+The @code{stdio} connection is useful when starting @code{gdbserver}
+with ssh:
+
+@smallexample
+(gdb) target remote | ssh -T hostname gdbserver - hello
+@end smallexample
+
+The @samp{-T} option to ssh is provided because we don't need a remote pty,
+and we don't want escape-character handling.  Ssh does this by default when
+a command is provided, the flag is provided to make it explicit.
+It can be omitted if you want to.
+
+Programs started with stdio-connected gdbserver have @file{/dev/null} for
+@code{stdin}, and @code{stdout} is redirected to @code{stderr}.
+This is done so that inferior I/O doesn't interfere with
+@value{GDBN}/gdbserver communication.
+
 @subsubsection Attaching to a Running Program
 @cindex attach to a program, @code{gdbserver}
 @cindex @option{--attach}, @code{gdbserver} option
Index: gdbserver/linux-low.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v
retrieving revision 1.179
diff -u -p -r1.179 linux-low.c
--- gdbserver/linux-low.c	14 Nov 2011 20:07:24 -0000	1.179
+++ gdbserver/linux-low.c	1 Dec 2011 21:14:00 -0000
@@ -569,6 +569,18 @@ linux_create_inferior (char *program, ch
 
       setpgid (0, 0);
 
+      /* If gdbserver is connected to gdb via stdio, redirect the inferior's
+	 stdout to stderr so that inferior i/o doesn't corrupt the connection.
+	 Also, redirect stdin to /dev/null.  */
+      if (remote_connection_is_stdio ())
+	{
+	  close (0);
+	  open ("/dev/null", O_RDONLY);
+	  dup2 (2, 1);
+	  write (2, "stdin/stdout redirected\n",
+		 sizeof ("stdin/stdout redirected\n") - 1);
+	}
+
       execv (program, allargs);
       if (errno == ENOENT)
 	execvp (program, allargs);
Index: gdbserver/remote-utils.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/remote-utils.c,v
retrieving revision 1.88
diff -u -p -r1.88 remote-utils.c
--- gdbserver/remote-utils.c	9 Nov 2011 02:32:42 -0000	1.88
+++ gdbserver/remote-utils.c	1 Dec 2011 21:14:00 -0000
@@ -107,6 +107,8 @@ struct sym_cache
 int remote_debug = 0;
 struct ui_file *gdb_stdlog;
 
+static int remote_is_stdio = 0;
+
 static gdb_fildes_t remote_desc = INVALID_DESCRIPTOR;
 static gdb_fildes_t listen_desc = INVALID_DESCRIPTOR;
 
@@ -130,6 +132,14 @@ gdb_connected (void)
   return remote_desc != INVALID_DESCRIPTOR;
 }
 
+/* Return true if the remote connection is over stdio.  */
+
+int
+remote_connection_is_stdio (void)
+{
+  return remote_is_stdio;
+}
+
 static void
 enable_async_notification (int fd)
 {
@@ -222,6 +232,16 @@ remote_prepare (char *name)
   socklen_t tmp;
   char *port_end;
 
+  if (strcmp (name, STDIO_CONNECTION_NAME) == 0)
+    {
+      /* We need to record fact that we're using stdio sooner than the
+	 call to remote_open so start_inferior knows the connection is
+	 via stdio.  */
+      remote_is_stdio = 1;
+      transport_is_reliable = 1;
+      return;
+    }
+
   port_str = strchr (name, ':');
   if (port_str == NULL)
     {
@@ -272,11 +292,27 @@ remote_open (char *name)
   char *port_str;
 
   port_str = strchr (name, ':');
+#ifdef USE_WIN32API
   if (port_str == NULL)
+    error ("Only <host>:<port> is supported on this platform.");
+#endif
+
+  if (strcmp (name, STDIO_CONNECTION_NAME) == 0)
+    {
+      fprintf (stderr, "Remote debugging using stdio\n");
+
+      /* Use stdin as the handle of the connection.
+	 We only select on reads, for example.  */
+      remote_desc = fileno (stdin);
+
+      enable_async_notification (remote_desc);
+
+      /* Register the event loop handler.  */
+      add_file_handler (remote_desc, handle_serial_event, NULL);
+    }
+#ifndef USE_WIN32API
+  else if (port_str == NULL)
     {
-#ifdef USE_WIN32API
-      error ("Only <host>:<port> is supported on this platform.");
-#else
       struct stat statbuf;
 
       if (stat (name, &statbuf) == 0
@@ -341,8 +377,8 @@ remote_open (char *name)
 
       /* Register the event loop handler.  */
       add_file_handler (remote_desc, handle_serial_event, NULL);
-#endif /* USE_WIN32API */
     }
+#endif /* ! USE_WIN32API */
   else
     {
       int port;
@@ -372,7 +408,8 @@ remote_close (void)
 #ifdef USE_WIN32API
   closesocket (remote_desc);
 #else
-  close (remote_desc);
+  if (! remote_connection_is_stdio ())
+    close (remote_desc);
 #endif
   remote_desc = INVALID_DESCRIPTOR;
 
@@ -731,6 +768,32 @@ read_ptid (char *buf, char **obuf)
   return ptid_build (pid, tid, 0);
 }
 
+/* Write COUNT bytes in BUF to the client.
+   The result is the number of bytes written or -1 if error.
+   This may return less than COUNT.  */
+
+static int
+write_prim (const void *buf, int count)
+{
+  if (remote_connection_is_stdio ())
+    return write (fileno (stdout), buf, count);
+  else
+    return write (remote_desc, buf, count);
+}
+
+/* Read COUNT bytes from the client and store in BUF.
+   The result is the number of bytes read or -1 if error.
+   This may return less than COUNT.  */
+
+static int
+read_prim (void *buf, int count)
+{
+  if (remote_connection_is_stdio ())
+    return read (fileno (stdin), buf, count);
+  else
+    return read (remote_desc, buf, count);
+}
+
 /* Send a packet to the remote machine, with error checking.
    The data of the packet is in BUF, and the length of the
    packet is in CNT.  Returns >= 0 on success, -1 otherwise.  */
@@ -768,7 +831,7 @@ putpkt_binary_1 (char *buf, int cnt, int
 
   do
     {
-      if (write (remote_desc, buf2, p - buf2) != p - buf2)
+      if (write_prim (buf2, p - buf2) != p - buf2)
 	{
 	  perror ("putpkt(write)");
 	  free (buf2);
@@ -863,7 +926,7 @@ input_interrupt (int unused)
       int cc;
       char c = 0;
 
-      cc = read (remote_desc, &c, 1);
+      cc = read_prim (&c, 1);
 
       if (cc != 1 || c != '\003' || current_inferior == NULL)
 	{
@@ -991,8 +1054,7 @@ readchar (void)
 
   if (readchar_bufcnt == 0)
     {
-      readchar_bufcnt = read (remote_desc, readchar_buf,
-			      sizeof (readchar_buf));
+      readchar_bufcnt = read_prim (readchar_buf, sizeof (readchar_buf));
 
       if (readchar_bufcnt <= 0)
 	{
@@ -1114,7 +1176,7 @@ getpkt (char *buf)
 
       fprintf (stderr, "Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n",
 	       (c1 << 4) + c2, csum, buf);
-      if (write (remote_desc, "-", 1) != 1)
+      if (write_prim ("-", 1) != 1)
 	return -1;
     }
 
@@ -1126,7 +1188,7 @@ getpkt (char *buf)
 	  fflush (stderr);
 	}
 
-      if (write (remote_desc, "+", 1) != 1)
+      if (write_prim ("+", 1) != 1)
 	return -1;
 
       if (remote_debug)
Index: gdbserver/server.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/server.c,v
retrieving revision 1.151
diff -u -p -r1.151 server.c
--- gdbserver/server.c	14 Nov 2011 15:18:51 -0000	1.151
+++ gdbserver/server.c	1 Dec 2011 21:14:00 -0000
@@ -2579,6 +2579,13 @@ main (int argc, char *argv[])
 		}
 	    }
 	}
+      else if (strcmp (*next_arg, "-") == 0)
+	{
+	  /* "-" specifies a stdio connection and is a form of port
+	     specification.  */
+	  *next_arg = STDIO_CONNECTION_NAME;
+	  break;
+	}
       else if (strcmp (*next_arg, "--disable-randomization") == 0)
 	disable_randomization = 1;
       else if (strcmp (*next_arg, "--no-disable-randomization") == 0)
@@ -2609,6 +2616,12 @@ main (int argc, char *argv[])
       exit (1);
     }
 
+  /* We need to know whether the remote connection is stdio before
+     starting the inferior.  Inferiors created in this scenario have
+     stdin,stdout redirected.  So do this here before we call
+     start_inferior.  */
+  remote_prepare (port);
+
   bad_attach = 0;
   pid = 0;
 
@@ -2696,8 +2709,6 @@ main (int argc, char *argv[])
       exit (1);
     }
 
-  remote_prepare (port);
-
   while (1)
     {
       noack_mode = 0;
@@ -2723,11 +2734,17 @@ main (int argc, char *argv[])
 
       /* If an exit was requested (using the "monitor exit" command),
 	 terminate now.  The only other way to get here is for
-	 getpkt to fail; close the connection and reopen it at the
+	 getpkt to fail; if the connection is via stdio terminate now,
+	 otherwise close the connection and reopen it at the
 	 top of the loop.  */
 
-      if (exit_requested || run_once)
-	{
+      if (exit_requested
+	  || run_once
+	  || remote_connection_is_stdio ())
+	{
+	  if (debug_threads)
+	    fprintf (stderr, "Remote side has terminated connection.  "
+		     "Shutting down.\n");
 	  detach_or_kill_for_exit ();
 	  exit (0);
 	}
Index: gdbserver/server.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/server.h,v
retrieving revision 1.88
diff -u -p -r1.88 server.h
--- gdbserver/server.h	14 Nov 2011 20:07:24 -0000	1.88
+++ gdbserver/server.h	1 Dec 2011 21:14:00 -0000
@@ -342,6 +342,9 @@ extern int transport_is_reliable;
 
 int gdb_connected (void);
 
+#define STDIO_CONNECTION_NAME "stdio"
+int remote_connection_is_stdio (void);
+
 ptid_t read_ptid (char *buf, char **obuf);
 char *write_ptid (char *buf, ptid_t ptid);
 
Index: testsuite/lib/gdbserver-support.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/lib/gdbserver-support.exp,v
retrieving revision 1.20
diff -u -p -r1.20 gdbserver-support.exp
--- testsuite/lib/gdbserver-support.exp	20 Nov 2011 23:59:49 -0000	1.20
+++ testsuite/lib/gdbserver-support.exp	1 Dec 2011 21:14:00 -0000
@@ -179,6 +179,12 @@ proc gdbserver_download_current_prog { }
     return $gdbserver_server_exec
 }
 
+# Default routine to compute the argument to "target remote".
+
+proc gdbserver_default_get_remote_address { host port } {
+    return "$host$port"
+}
+
 # Start a gdbserver process with initial OPTIONS and trailing ARGUMENTS.
 # The port will be filled in between them automatically.
 #
@@ -202,6 +208,15 @@ proc gdbserver_start { options arguments
 	set debughost "localhost:"
     }
 
+    # Some boards use a different value for the port that is passed to
+    # gdbserver and the port that is passed to the "target remote" command.
+    # One example is the stdio gdbserver support.
+    if [target_info exists gdb,get_remote_address] {
+	set get_remote_address [target_info gdb,get_remote_address]
+    } else {
+	set get_remote_address gdbserver_default_get_remote_address
+    }
+
     # Extract the protocol
     if [target_info exists gdb_protocol] {
 	set protocol [target_info gdb_protocol]
@@ -213,9 +228,6 @@ proc gdbserver_start { options arguments
 
     # Loop till we find a free port.
     while 1 {
-	# Export the host:port pair.
-	set gdbport $debughost$portnum
-
 	# Fire off the debug agent.
 	set gdbserver_command "$gdbserver"
 
@@ -231,9 +243,9 @@ proc gdbserver_start { options arguments
 	if { $options != "" } {
 	    append gdbserver_command " $options"
 	}
-
-	append gdbserver_command " :$portnum"
-
+	if { $portnum != "" } {
+	    append gdbserver_command " :$portnum"
+	}
 	if { $arguments != "" } {
 	    append gdbserver_command " $arguments"
 	}
@@ -271,7 +283,7 @@ proc gdbserver_start { options arguments
 	}
     }
 
-    return [list $protocol $gdbport]
+    return [list $protocol [$get_remote_address $debughost $portnum]]
 }
 
 # Start a gdbserver process running SERVER_EXEC, and connect GDB


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]