From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 4835 invoked by alias); 1 Dec 2011 21:26:52 -0000 Received: (qmail 4823 invoked by uid 22791); 1 Dec 2011 21:26:49 -0000 X-SWARE-Spam-Status: No, hits=-2.8 required=5.0 tests=AWL,BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,RCVD_IN_DNSWL_LOW,RP_MATCHES_RCVD,TW_DB X-Spam-Check-By: sourceware.org Received: from mail-gx0-f201.google.com (HELO mail-gx0-f201.google.com) (209.85.161.201) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 01 Dec 2011 21:26:35 +0000 Received: by ggnv5 with SMTP id v5so296498ggn.0 for ; Thu, 01 Dec 2011 13:26:34 -0800 (PST) Received: by 10.100.50.13 with SMTP id x13mr2443687anx.10.1322774794533; Thu, 01 Dec 2011 13:26:34 -0800 (PST) Received: by 10.100.50.13 with SMTP id x13mr2443681anx.10.1322774794400; Thu, 01 Dec 2011 13:26:34 -0800 (PST) Received: from wpzn3.hot.corp.google.com (216-239-44-65.google.com [216.239.44.65]) by gmr-mx.google.com with ESMTPS id v44si2497262yhd.1.2011.12.01.13.26.34 (version=TLSv1/SSLv3 cipher=AES128-SHA); Thu, 01 Dec 2011 13:26:34 -0800 (PST) Received: from wpaz1.hot.corp.google.com (wpaz1.hot.corp.google.com [172.24.198.65]) by wpzn3.hot.corp.google.com (Postfix) with ESMTPS id 540B610004D for ; Thu, 1 Dec 2011 13:26:34 -0800 (PST) Received: from ruffy.mtv.corp.google.com (ruffy.mtv.corp.google.com [172.18.110.50]) by wpaz1.hot.corp.google.com with ESMTP id pB1LQXjk029829 for ; Thu, 1 Dec 2011 13:26:33 -0800 Received: by ruffy.mtv.corp.google.com (Postfix, from userid 67641) id E6F892461D0; Thu, 1 Dec 2011 13:26:32 -0800 (PST) To: gdb-patches@sourceware.org Subject: [RFA] stdio gdbserver Message-Id: <20111201212632.E6F892461D0@ruffy.mtv.corp.google.com> Date: Thu, 01 Dec 2011 21:26:00 -0000 From: dje@google.com (Doug Evans) X-System-Of-Record: true X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2011-12/txt/msg00022.txt.bz2 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 * 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 : 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 +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