2011-12-16 Doug Evans * 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. * 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 (gdb_target_cmd): Recognize stdio gdbserver output. (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.475 diff -u -p -r1.475 NEWS --- NEWS 16 Dec 2011 15:55:38 -0000 1.475 +++ NEWS 16 Dec 2011 18:33:35 -0000 @@ -3,6 +3,9 @@ *** Changes since GDB 7.4 +* GDBserver now supports stdio connections. + E.g. (gdb) target remote | ssh myhost gdbserver - hello + *** Changes in GDB 7.4 * GDB now handles ambiguous linespecs more consistently; the existing Index: doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.906 diff -u -p -r1.906 gdb.texinfo --- doc/gdb.texinfo 16 Dec 2011 15:55:38 -0000 1.906 +++ doc/gdb.texinfo 16 Dec 2011 18:33:36 -0000 @@ -16766,8 +16766,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}: @@ -16796,6 +16798,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. +You could elide it if you want to. + +Programs started with stdio-connected gdbserver have @file{/dev/null} for +@code{stdin}, and @code{stdout},@code{stderr} are sent back to gdb for +display through a pipe connected to gdbserver. +Both @code{stdout} and @code{stderr} use the same pipe. + @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.184 diff -u -p -r1.184 linux-low.c --- gdbserver/linux-low.c 14 Dec 2011 17:33:24 -0000 1.184 +++ gdbserver/linux-low.c 16 Dec 2011 18:33:36 -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 16 Dec 2011 18:33:36 -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,17 @@ remote_prepare (char *name) socklen_t tmp; char *port_end; + remote_is_stdio = 0; + 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 +293,27 @@ remote_open (char *name) char *port_str; port_str = strchr (name, ':'); +#ifdef USE_WIN32API if (port_str == NULL) + error ("Only : 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 : is supported on this platform."); -#else struct stat statbuf; if (stat (name, &statbuf) == 0 @@ -341,8 +378,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 +409,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 +769,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 +832,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 +927,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 +1055,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 +1177,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 +1189,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.152 diff -u -p -r1.152 server.c --- gdbserver/server.c 2 Dec 2011 22:26:53 -0000 1.152 +++ gdbserver/server.c 16 Dec 2011 18:33:36 -0000 @@ -2606,6 +2606,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) @@ -2636,6 +2643,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; @@ -2723,8 +2736,6 @@ main (int argc, char *argv[]) exit (1); } - remote_prepare (port); - while (1) { noack_mode = 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 16 Dec 2011 18:33:36 -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.21 diff -u -p -r1.21 gdbserver-support.exp --- testsuite/lib/gdbserver-support.exp 3 Dec 2011 20:20:29 -0000 1.21 +++ testsuite/lib/gdbserver-support.exp 16 Dec 2011 18:33:36 -0000 @@ -70,6 +70,10 @@ proc gdb_target_cmd { targetname serialp verbose "Set target to $targetname" return 0 } + -re "Remote debugging using stdio.*$gdb_prompt $" { + verbose "Set target to $targetname" + return 0 + } -re "Remote target $targetname connected to.*$gdb_prompt $" { verbose "Set target to $targetname" return 0 @@ -183,6 +187,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. # @@ -206,6 +216,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] @@ -217,9 +236,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" @@ -235,9 +251,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" } @@ -275,7 +291,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