[PATCH 1/3] Add gdb remote protocol handling to strace

Stan Cox scox at redhat.com
Fri Feb 10 20:21:29 UTC 2017


Requesting comments on this patch, which adds support for the gdb
remote serial protocol, which is described further in "info gdb
'Remote Protocol' 'Packets'" This protocol connects to gdbserver,
which catches the syscalls and returns information about them, as well
as register and memory info, via the protocol.  The strace -d option
will display the packets going over the protocol.  Most of the
implementation is in the gdbserver backend.  The strace level support
is primarily of two types: option handling and gdb remote protocol
interception.

This is currently located at github.com:stanfordcox/strace.git on the
gdbserver0 branch.

Brief summary of changes:

Makefile.am:  Also consider gdbserver when searching for header
files. Build gdbserver.c and protocol.c

defs.h:  Add TCB_GDB_CONT_PID_TID which determines what type of
continue packet to use..  Make pid2tcb available for use by the
gdbserver backend.

strace.c:  droptcb:  gdb_detach a connection.
starup_attach: gdb_starup_attach a connection.
startup_child:  gdb_startup_child a child.
test_ptrace_seize:  gdbserver handles this itself.
init:  Add -G option.  The connection syntax is further described in "info
gdb 'Remote Debugging' 'Connecting'"  Initialize via gdb_init and
gdb_finalize_init.
cleanup:  gdb_cleanup to cleanup.
trace: gdb_trace handles gdb remote protocol tracing

syscall.c:  update_personality called by gdbserver backend.
get_regs: gdbserver returns the register values.
get_scno:  gdbserver returns the syscall number.

upeek.c:  upeek:  gdbserver reads memory.

util.c:  umoven/umovestr:  gdbserver reads memory.

pathtrace.c:  getfdpath:  gdbserver gets the path.




diff a/Makefile.am b/Makefile.am
--- ../src/Makefile.am	2017-01-25 10:43:07.586827022 -0500
+++ Makefile.am	2017-02-02 10:30:27.834558340 -0500
@@ -47,5 +47,5 @@ ARCH		= @arch@
  ACLOCAL_AMFLAGS = -I m4
  AM_CFLAGS = $(WARN_CFLAGS)
-AM_CPPFLAGS = -I$(builddir)/$(OS)/$(ARCH) \
+OS_CPPFLAGS = -I$(builddir)/$(OS)/$(ARCH) \
  	      -I$(srcdir)/$(OS)/$(ARCH) \
  	      -I$(builddir)/$(OS) \
@@ -54,4 +54,9 @@ AM_CPPFLAGS = -I$(builddir)/$(OS)/$(ARCH
  	      -I$(srcdir)

+AM_CPPFLAGS = -I$(builddir)/gdbserver/$(ARCH) \
+	      -I$(srcdir)/gdbserver/$(ARCH) \
+	      -I$(builddir)/gdbserver \
+	      -I$(srcdir)/gdbserver $(OS_CPPFLAGS)
+
  AM_CFLAGS_FOR_BUILD = $(WARN_CFLAGS_FOR_BUILD)
  AM_CPPFLAGS_FOR_BUILD = $(AM_CPPFLAGS)
@@ -262,4 +267,9 @@ strace_SOURCES =	\
  	# end of strace_SOURCES

+# jistone
+strace_SOURCES +=		\
+	gdbserver/gdbserver.c	\
+	gdbserver/protocol.c
+
  if USE_LIBUNWIND
  strace_SOURCES += unwind.c

diff a/defs.h b/defs.h
--- ../src/defs.h	2017-02-02 12:40:50.967534599 -0500
+++ defs.h	2017-02-09 10:53:49.474373376 -0500
@@ -269,4 +269,5 @@ struct tcb {
  #define TCB_HIDE_LOG	0x80	/* We should hide everything (until execve) */
  #define TCB_SKIP_DETACH_ON_FIRST_EXEC	0x100	/* -b execve should skip 
detach on first execve */
+#define TCB_GDB_CONT_PID_TID 0x200 /* Use vCont;c:pPID.TID for for gdb 
backend */

  /* qualifier flags */
@@ -431,4 +432,5 @@ extern int set_tcb_priv_data(struct tcb
  			     void (*free_priv_data)(void *));
  extern void free_tcb_priv_data(struct tcb *);
+extern struct tcb *pid2tcb(int pid);

  static inline unsigned long get_tcb_priv_ulong(const struct tcb *tcp)

diff a/strace.c b/strace.c
--- ../src/strace.c	2017-01-25 10:43:07.605827151 -0500
+++ strace.c	2017-02-02 10:15:09.396248528 -0500
@@ -50,4 +50,6 @@
  #include "printsiginfo.h"

+#include "gdbserver.h"
+
  /* In some libc, these aren't declared. Do it ourself: */
  extern char **environ;
@@ -131,5 +133,5 @@ static bool detach_on_execve = 0;

  static int exit_code;
-static int strace_child = 0;
+int strace_child = 0;
  static int strace_tracer_pid = 0;

@@ -147,5 +149,5 @@ static FILE *shared_log;

  struct tcb *printing_tcp = NULL;
-static struct tcb *current_tcp;
+struct tcb *current_tcp;

  static struct tcb **tcbtab;
@@ -692,5 +694,5 @@ tabto(void)
   * may create bogus empty FILE.<nonexistant_pid>, and then die.
   */
-static void
+void
  newoutf(struct tcb *tcp)
  {
@@ -727,5 +729,5 @@ expand_tcbtab(void)
  }

-static struct tcb *
+struct tcb *
  alloctcb(int pid)
  {
@@ -791,5 +793,5 @@ free_tcb_priv_data(struct tcb *tcp)
  }

-static void
+void
  droptcb(struct tcb *tcp)
  {
@@ -845,4 +847,9 @@ detach(struct tcb *tcp)
  	int status;

+	if (gdbserver) {
+		gdb_detach(tcp);
+		goto drop;
+	}
+
  	/*
  	 * Linux wrongly insists the child be stopped
@@ -1133,4 +1140,8 @@ startup_attach(void)
  			continue; /* no, we already attached it */

+		if (gdbserver) {
+			gdb_startup_attach(tcp);
+			continue;
+		}
  		if (tcp->pid == parent_pid || tcp->pid == strace_tracer_pid) {
  			errno = EPERM;
@@ -1305,4 +1316,9 @@ startup_child(char **argv)
  	struct tcb *tcp;

+	if (gdbserver) {
+		gdb_startup_child(argv);
+		return;
+	}
+
  	filename = argv[0];
  	filename_len = strlen(filename);
@@ -1486,4 +1502,8 @@ test_ptrace_seize(void)
  	int pid;

+	if (gdbserver) {
+		return;		/* gdbserver provides syscall info */
+	}
+
  	/* Need fork for test. NOMMU has no forks */
  	if (NOMMU_SYSTEM) {
@@ -1612,4 +1632,5 @@ init(int argc, char *argv[])
  #endif
  		"D"
+		"G:"
  		"a:e:o:O:p:s:S:u:E:P:I:")) != EOF) {
  		switch (c) {
@@ -1644,4 +1665,7 @@ init(int argc, char *argv[])
  			followfork++;
  			break;
+		case 'G':
+			gdbserver = strdup(optarg);
+			break;
  		case 'h':
  			usage();
@@ -1742,5 +1766,6 @@ init(int argc, char *argv[])
  	acolumn_spaces[acolumn] = '\0';

-	if (!argv[0] && !nprocs) {
+	/* under gdbserver, we can reasonably allow having neither to use 
existing targets.  */
+	if (!argv[0] && !nprocs && !gdbserver) {
  		error_msg_and_help("must have PROG [ARGS] or -p PID");
  	}
@@ -1753,4 +1778,19 @@ init(int argc, char *argv[])
  		followfork = optF;

+	if (gdbserver) {
+	   if (username) {
+		   error_msg_and_die("-u and -G are mutually exclusive");
+	   }
+
+	   if (daemonized_tracer) {
+		   error_msg_and_die("-D and -G are mutually exclusive");
+	   }
+
+	   if (!followfork) {
+		   error_msg("-G is always multithreaded, implies -f");
+		   followfork = 1;
+	   }
+	}
+
  	if (followfork >= 2 && cflag) {
  		error_msg_and_help("(-c or -C) and -ff are mutually exclusive");
@@ -1784,4 +1824,9 @@ init(int argc, char *argv[])
  	}

+	if (gdbserver) {
+		if (gdb_init() < 0)
+			error_msg_and_die("-G is not supported on this target.");
+	}
+
  #ifdef USE_LIBUNWIND
  	if (stack_trace_enabled) {
@@ -1918,4 +1963,7 @@ init(int argc, char *argv[])
  		startup_attach();

+	if (gdbserver)
+		gdb_finalize_init();
+
  	/* Do we want pids printed in our -o OUTFILE?
  	 * -ff: no (every pid has its own file); or
@@ -1926,5 +1974,5 @@ init(int argc, char *argv[])
  }

-static struct tcb *
+struct tcb *
  pid2tcb(int pid)
  {
@@ -1969,4 +2017,6 @@ cleanup(void)
  	if (cflag)
  		call_summary(shared_log);
+
+	gdb_cleanup();
  }

@@ -2107,5 +2157,5 @@ maybe_switch_tcbs(struct tcb *tcp, const
  }

-static void
+void
  print_signalled(struct tcb *tcp, const int pid, int status)
  {
@@ -2130,5 +2180,5 @@ print_signalled(struct tcb *tcp, const i
  }

-static void
+void
  print_exited(struct tcb *tcp, const int pid, int status)
  {
@@ -2146,5 +2196,5 @@ print_exited(struct tcb *tcp, const int
  }

-static void
+void
  print_stopped(struct tcb *tcp, const siginfo_t *si, const unsigned int 
sig)
  {
@@ -2237,4 +2287,7 @@ trace(void)
  		return false;

+	if (gdbserver)
+		return gdb_trace();
+
  	/*
  	 * Used to exit simply when nprocs hits zero, but in this testcase:

diff a/syscall.c b/syscall.c
--- ../src/syscall.c	2017-02-02 12:40:50.970534610 -0500
+++ syscall.c	2017-02-01 22:22:53.021936753 -0500
@@ -45,4 +45,5 @@
  #include "regs.h"
  #include "ptrace.h"
+#include "gdbserver.h"

  #if defined(SPARC64)
@@ -318,5 +319,5 @@ set_personality(int personality)
  }

-static void
+void
  update_personality(struct tcb *tcp, unsigned int personality)
  {
@@ -1150,4 +1151,7 @@ void
  get_regs(pid_t pid)
  {
+	if (gdbserver)
+# include "gdb_get_regs.c"
+
  #undef USE_GET_SYSCALL_RESULT_REGS
  #ifdef ptrace_getregset_or_getregs
@@ -1219,8 +1223,13 @@ int
  get_scno(struct tcb *tcp)
  {
+	int rc;
+
  	if (get_regs_error)
  		return -1;

-	int rc = arch_get_scno(tcp);
+	if (gdbserver)		/* syscall number is already filled in */
+		rc = 1;
+	else
+		rc = arch_get_scno(tcp);
  	if (rc != 1)
  		return rc;

diff a/upeek.c b/upeek.c
--- ../src/upeek.c	2017-01-25 10:43:07.620827253 -0500
+++ upeek.c	2017-02-01 22:22:53.019936745 -0500
@@ -34,4 +34,5 @@
  #include "defs.h"
  #include "ptrace.h"
+#include "gdbserver.h"

  int
@@ -40,4 +41,7 @@ upeek(int pid, unsigned long off, kernel
  	long val;

+	if (gdbserver)
+		return gdb_read_mem(pid, off, current_wordsize, false, (char*)res);
+
  	errno = 0;
  	val = ptrace(PTRACE_PEEKUSER, (pid_t) pid, (void *) off, 0);

diff a/util.c b/util.c
--- ../src/util.c	2017-01-25 10:43:07.620827253 -0500
+++ util.c	2017-02-02 10:18:05.222878260 -0500
@@ -45,4 +45,5 @@
  #include "regs.h"
  #include "ptrace.h"
+#include "gdbserver.h"

  int
@@ -1125,4 +1126,7 @@ umoven(struct tcb *const tcp, kernel_ulo
  	} u;

+	if (gdbserver)
+		return gdb_read_mem(pid, addr, len, false, laddr);
+
  #if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
  	if (current_wordsize < sizeof(addr)
@@ -1273,4 +1277,7 @@ umovestr(struct tcb *const tcp, kernel_u
  	} u;

+	if (gdbserver)
+		return gdb_read_mem(pid, addr, len, true, laddr);
+
  #if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
  	if (current_wordsize < sizeof(addr)

diff a/pathtrace.c b/pathtrace.c
--- ../src/pathtrace.c	2017-01-25 10:43:07.602827131 -0500
+++ pathtrace.c	2017-02-01 22:22:53.025936769 -0500
@@ -31,4 +31,5 @@
  #include <poll.h>

+#include "gdbserver.h"
  #include "syscall.h"

@@ -102,4 +103,7 @@ getfdpath(struct tcb *tcp, int fd, char
  	ssize_t n;

+	if (gdbserver)
+		return gdb_getfdpath(tcp->pid, fd, buf, bufsize);
+
  	if (fd < 0)
  		return -1;





More information about the Strace-devel mailing list