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 connection


Hi.

This patch lets gdbserver communicate via stdio.

E.g.

(gdb) target remote | ssh -T myhost gdbserver stdio hello

This patch also enhances testsuite support so that one can test it
(e.g. using the appended board description file).

One outstanding issue is what to do with inferior stdio.
stderr is ok, it'll just get propagated back to gdb which will display it.
But we don't want inferior stdio to interfere with the gdb connection.
The board description file uses exec-wrapper to redirect stdin/out to /dev/null.
I think gdbserver should do that itself if the connection is stdio.
If the user wants something else for the inferior's stdio (named pipes
or whatever), they can still use exec-wrapper.
Comments?

2010-05-25  Doug Evans  <dje@google.com>

	* NEWS: Mention new stdio gdbserver connection type.

	gdbserver/
	* remote-utils.c (STDIN_FILENO, STDOUT_FILENO): New macros.
	(remote_connection_is_stdio): New function.
	(remote_open): Handle "stdio" as a connection address.
	(write_prim, read_prim): New functions.
	(putpkt_binary_1, input_interrupt): Call them instead of read, write.
	(readchar, getpkt): Ditto.
	* server.c (main): Terminate programs started by gdbserver
	when stdio connection closes.
	* server.h (remote_connection_is_stdio): Declare.

	testsuite/
	* lib/gdbserver-support.exp (gdb_target_cmd): Process
	gdb,pass_exec_to_target if specified.
	(gdbserver_start): Only add :portnum if specified.
	(gdbserver_spawn): Record downloaded program in case user specifies
	gdb,pass_exec_to_target.
	(gdbserver_start_extended): Set gdbserver_target_exec.

	doc/
	* gdb.texinfo (Server): Add docs for stdio connection.

Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.380
diff -u -p -r1.380 NEWS
--- NEWS	25 May 2010 15:27:16 -0000	1.380
+++ NEWS	25 May 2010 17:06:09 -0000
@@ -29,6 +29,9 @@ qGetTIBAddr
 
   - GDBserver now supports x86_64 Windows 64-bit debugging.
 
+  - GDBserver now supports stdio connections.
+    E.g. (gdb) target remote | ssh -T myhost gdbserver stdio hello
+
 * GDB now sends xmlRegisters= in qSupported packet to indicate that
   it understands register description.
 
Index: gdbserver/remote-utils.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/remote-utils.c,v
retrieving revision 1.76
diff -u -p -r1.76 remote-utils.c
--- gdbserver/remote-utils.c	3 May 2010 20:53:21 -0000	1.76
+++ gdbserver/remote-utils.c	25 May 2010 16:48:02 -0000
@@ -80,6 +80,9 @@ typedef int socklen_t;
 # define INVALID_DESCRIPTOR -1
 #endif
 
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+
 /* Extra value for readchar_callback.  */
 enum {
   /* The callback is currently not scheduled.  */
@@ -128,6 +131,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_desc == STDIN_FILENO;
+}
+
 static void
 enable_async_notification (int fd)
 {
@@ -207,7 +218,21 @@ remote_open (char *name)
   char *port_str;
 
   port_str = strchr (name, ':');
-  if (port_str == NULL)
+
+  if (strcmp (name, "stdio") == 0)
+    {
+      fprintf (stderr, "Remote debugging using %s\n", name);
+
+      /* Use stdin as the handle of the connection.
+	 We only select on reads, for example.  */
+      remote_desc = 0;
+      transport_is_reliable = 1;
+      enable_async_notification (remote_desc);
+
+      /* Register the event loop handler.  */
+      add_file_handler (remote_desc, handle_serial_event, NULL);
+    }
+  else if (port_str == NULL)
     {
 #ifdef USE_WIN32API
       error ("Only <host>:<port> is supported on this platform.");
@@ -697,6 +722,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 (STDOUT_FILENO, 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 (STDIN_FILENO, 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.  */
@@ -734,7 +785,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);
@@ -829,7 +880,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)
 	{
@@ -957,7 +1008,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)
 	{
@@ -1077,7 +1128,7 @@ getpkt (char *buf)
 
       fprintf (stderr, "Bad checksum, sentsum=0x%x, csum=0x%x, buf=%s\n",
 	       (c1 << 4) + c2, csum, buf);
-      write (remote_desc, "-", 1);
+      write_prim ("-", 1);
     }
 
   if (!noack_mode)
@@ -1088,7 +1139,7 @@ getpkt (char *buf)
 	  fflush (stderr);
 	}
 
-      write (remote_desc, "+", 1);
+      write_prim ("+", 1);
 
       if (remote_debug)
 	{
Index: gdbserver/server.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/server.c,v
retrieving revision 1.120
diff -u -p -r1.120 server.c
--- gdbserver/server.c	3 May 2010 04:02:20 -0000	1.120
+++ gdbserver/server.c	25 May 2010 16:48:02 -0000
@@ -2502,11 +2502,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)
+      if (exit_requested
+	  || remote_connection_is_stdio ())
 	{
+	  if (debug_threads
+	      && remote_connection_is_stdio ())
+	    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.68
diff -u -p -r1.68 server.h
--- gdbserver/server.h	3 May 2010 20:53:21 -0000	1.68
+++ gdbserver/server.h	25 May 2010 16:48:02 -0000
@@ -363,6 +363,7 @@ extern int noack_mode;
 extern int transport_is_reliable;
 
 int gdb_connected (void);
+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.17
diff -u -p -r1.17 gdbserver-support.exp
--- testsuite/lib/gdbserver-support.exp	12 Jan 2010 00:50:26 -0000	1.17
+++ testsuite/lib/gdbserver-support.exp	25 May 2010 16:48:03 -0000
@@ -36,6 +36,10 @@
 #	Port id to use for socket connection.  If not set explicitly,
 #	it will start at "2345" and increment for each use.
 #
+#   set_board_info gdb,pass_exec_to_target
+#       Set to 1 if the inferior and optional arguments need to be passed
+#       to gdb's "target" command".  This is used by "target remote |".
+#
 
 #
 # gdb_target_cmd
@@ -44,9 +48,19 @@
 proc gdb_target_cmd { targetname serialport } {
     global gdb_prompt
 
+    set pass_exec_to_target 0
+    if [target_info exists gdb,pass_exec_to_target] {
+	set pass_exec_to_target [target_info gdb,pass_exec_to_target]
+    }
+
     set serialport_re [string_to_regexp $serialport]
     for {set i 1} {$i <= 3} {incr i} {
-	send_gdb "target $targetname $serialport\n"
+	if $pass_exec_to_target {
+	    global gdbserver_target_exec
+	    send_gdb "target $targetname $serialport $gdbserver_target_exec\n"
+	} else {
+	    send_gdb "target $targetname $serialport\n"
+	}
 	gdb_expect 60 {
 	    -re "A program is being debugged already.*ill it.*y or n. $" {
 		send_gdb "y\n"
@@ -221,7 +235,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"
 	}
@@ -268,12 +284,13 @@ proc gdbserver_start { options arguments
 # Returns the target protocol and socket to connect to.
 
 proc gdbserver_spawn { child_args } {
-    set target_exec [gdbserver_download_current_prog]
+    global gdbserver_target_exec
+    set gdbserver_target_exec [gdbserver_download_current_prog]
 
     # Fire off the debug agent.  This flavour of gdbserver takes as
     # arguments the port information, the name of the executable file to
     # be debugged, and any arguments.
-    set arguments "$target_exec"
+    set arguments "$gdbserver_target_exec"
     if { $child_args != "" } {
 	append arguments " $child_args"
     }
@@ -324,5 +341,8 @@ proc gdbserver_start_extended { } {
     set gdbserver_protocol "extended-[lindex $res 0]"
     set gdbserver_gdbport [lindex $res 1]
 
+    global gdbserver_target_exec
+    set gdbserver_target_exec ""
+
     return [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport]
 }
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.717
diff -u -p -r1.717 gdb.texinfo
--- doc/gdb.texinfo	25 May 2010 15:27:16 -0000	1.717
+++ doc/gdb.texinfo	25 May 2010 17:07:53 -0000
@@ -15516,8 +15516,9 @@ 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{stdio} (to use gdbserver's stdin/stdout).
+For example, to debug Emacs with the argument
 @samp{foo.txt} and communicate with @value{GDBN} over the serial port
 @file{/dev/com1}:
 
@@ -15546,6 +15547,16 @@ 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 gdbserver stdio emacs foo.txt
+@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.
+
 @subsubsection Attaching to a Running Program
 
 On some targets, @code{gdbserver} can also attach to running programs.




Here's a sample board description file.

--- snip --- native-stdio-gdbserver.exp
# gdbserver running native w/ comms over stdio
# Based on: http://sourceware.org/gdb/wiki/Native_gdbserver_testing
# Usage: make check-gdb RUNTESTFLAGS="--target_board native-stdio-gdbserver"

verbose -log "native-stdio-gdbserver.exp: board is $board"

load_generic_config "gdbserver"
process_multilib_options ""

# The default compiler for this target.
set_board_info compiler "[find_gcc]"

# This gdbserver can only run a process once per session.
set_board_info gdb,do_reload_on_run 1

# There's no support for argument-passing (yet).
set_board_info noargs 1

# Can't do input (or output) in the current gdbserver.
set_board_info gdb,noinferiorio 1

# gdbserver does not intercept target file operations and perform them
# on the host.
set_board_info gdb,nofileio 1

# x86 linux gdbserver supports h/w breakpoints (hbreak)
if { [istarget i?86-*-*] || [istarget x86_64-*-* ] } {
    set_board_info gdb,hardware_breakpoints 1
}

# Can't do hardware watchpoints, in general.
# But can for i386.
if { [istarget i?86-*-*] || [istarget x86_64-*-* ] } {
    ;# leave unset, testcases check for whether it is defined
} else {
    set_board_info gdb,no_hardware_watchpoints 1
}
#set_board_info gdb,no_hardware_watchpoints 1

set gdb_server_prog "[pwd]/../gdbserver/gdbserver"
set_board_info sockethost "| $gdb_server_prog --wrapper /bin/sh -c 'exec \"$@\" >/dev/null </dev/null' exec -- stdio"
set_board_info gdb,socketport ""
set_board_info gdb,pass_exec_to_target 1
set_board_info use_gdb_stub 1

# We will be using the standard GDB remote protocol.
set_board_info gdb_protocol "remote"
# Test the copy of gdbserver in the build directory.
set_board_info gdb_server_prog "$gdb_server_prog"

proc ${board}_spawn { board cmd } {
    global board_info

    verbose -log "${board}_spawn: $board $cmd"

    set baseboard [lindex [split $board "/"] 0]

    set board_info($baseboard,isremote) 0
    #set result [remote_spawn $board $cmd]
    # Provide test harness with what it's waiting for.
    set result [remote_spawn $board "echo Listening on"]
    set board_info($baseboard,isremote) 1

    return $result
}

proc ${board}_download { board host dest } {
    return $host
}

proc ${board}_file { dest op args } {
    if { $op == "delete" } {
        return 0
    }
    return [eval [list standard_file $dest $op] $args]
}


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