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] gdb-freeplay


Here's a 'bump' for the gdb-freeplay patch, now featuring
support for reverse debugging.

I have some rough-cut testsuites for reverse-step,
reverse-continue, reverse-finish, and watchpoints and
breakpoints in reverse.  They pass with gdb-freeplay and
with Teawater's native linux record/replay.

They (the tests) are not really ready to submit, but
maybe I'll post them, just to show how reverse debugging
is coming along.

2008-09-10  Michael Snyder  <msnyder@specifix.com>

	* gdb-freeplay is an enhanced version of gdbreplay.
	* gdbfreeplay-front.c: New file.  Extended gdbreplay.
	* gdbfreeplay-back.c: New file.
	* gdbfreeplay.h: New file.
	* remote-breakpoint.c: New file.
	* remote-breakpoint.h: New file.
	* Makefile.in: Add rules for gdb-freeplay.
	* configure.srv: Ditto.
	* configure.ac: Ditto.
	* configure: Regenerate.
	* gdbfreeplay-i386.c: New file.  x86 back-end.
	* gdbfreeplay-mips64.c: New file.  Mips64 back-end.
	* gdbfreeplay-x86-64.c: New file.  X86-64 back-end.

2008-09-10  Michael Snyder  <msnyder@vmware.com>
	
	* gdbfreeplay-back.c: Add support for reverse debugging.

Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/Makefile.in,v
retrieving revision 1.63
diff -u -p -r1.63 Makefile.in
--- Makefile.in	24 Aug 2008 18:40:37 -0000	1.63
+++ Makefile.in	11 Sep 2008 00:09:23 -0000
@@ -136,6 +136,8 @@ SFILES=	$(srcdir)/gdbreplay.c $(srcdir)/
 
 DEPFILES = @GDBSERVER_DEPFILES@
 
+FREEPLAY_DEPFILES = @GDBFREEPLAY_DEPFILES@
+
 LIBOBJS = @LIBOBJS@
 
 SOURCES = $(SFILES)
@@ -200,6 +202,12 @@ gdbreplay$(EXEEXT): $(GDBREPLAY_OBS)
 	${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) -o gdbreplay$(EXEEXT) $(GDBREPLAY_OBS) \
 	  $(XM_CLIBS)
 
+gdb-freeplay$(EXEEXT): gdbfreeplay-front.o gdbfreeplay-back.o version.o \
+		       $(FREEPLAY_DEPFILES) remote-breakpoint.o
+	rm -f gdb-freeplay$(EXEEXT)
+	${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) \
+	  -o gdb-freeplay$(EXEEXT) $^ $(XM_CLIBS)
+
 # Put the proper machine-specific files first, so M-. on a machine
 # specific routine gets the one for the correct machine.
 # The xyzzy stuff below deals with empty DEPFILES
@@ -218,7 +226,8 @@ tags: TAGS
 clean:
 	rm -f *.o ${ADD_FILES} *~
 	rm -f version.c
-	rm -f gdbserver$(EXEEXT) gdbreplay$(EXEEXT) core make.log
+	rm -f gdbserver$(EXEEXT) gdbreplay$(EXEEXT) gdb-freeplay$(EXEEXT)
+	rm -f core make.log
 	rm -f reg-arm.c reg-i386.c reg-ia64.c reg-m32r.c reg-m68k.c
 	rm -f reg-sh.c reg-spu.c reg-x86-64.c reg-i386-linux.c
 	rm -f reg-cris.c reg-crisv32.c reg-x86-64-linux.c reg-xtensa.c
@@ -290,6 +299,11 @@ target.o: target.c $(server_h)
 thread-db.o: thread-db.c $(server_h) $(gdb_proc_service_h)
 utils.o: utils.c $(server_h)
 gdbreplay.o: gdbreplay.c config.h
+gdbfreeplay-front.o:  gdbfreeplay-front.c gdbfreeplay.h
+gdbfreeplay-back.o:   gdbfreeplay-back.c  gdbfreeplay.h remote-breakpoint.h
+gdbfreeplay-i386.o:   gdbfreeplay-i386.c  gdbfreeplay.h remote-breakpoint.h
+gdbfreeplay-mips64.o: gdbfreeplay-mips64.c  gdbfreeplay.h remote-breakpoint.h
+remote-breakpoint.o:  remote-breakpoint.c remote-breakpoint.h
 
 signals.o: ../signals/signals.c $(server_h)
 	$(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER
Index: configure
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure,v
retrieving revision 1.37
diff -u -p -r1.37 configure
--- configure	31 Jul 2008 17:46:33 -0000	1.37
+++ configure	11 Sep 2008 00:09:24 -0000
@@ -310,7 +310,7 @@ ac_includes_default="\
 # include <unistd.h>
 #endif"
 
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CPP EGREP LIBOBJS PKGVERSION REPORT_BUGS_TO REPORT_BUGS_TEXI RDYNAMIC GDBSERVER_DEPFILES GDBSERVER_LIBS USE_THREAD_DB srv_xmlbuiltin srv_xmlfiles LTLIBOBJS'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CPP EGREP LIBOBJS PKGVERSION REPORT_BUGS_TO REPORT_BUGS_TEXI RDYNAMIC GDBSERVER_DEPFILES GDBSERVER_LIBS USE_THREAD_DB srv_xmlbuiltin srv_xmlfiles GDBFREEPLAY_DEPFILES LTLIBOBJS'
 ac_subst_files=''
 ac_pwd=`pwd`
 
@@ -4579,6 +4579,8 @@ fi
 
 GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_hostio_err_objs $srv_thread_depfiles"
 GDBSERVER_LIBS="$srv_libs"
+GDBFREEPLAY_DEPFILES="$freeplay_tgtobj"
+
 
 
 
@@ -5238,6 +5240,7 @@ s,@GDBSERVER_LIBS@,$GDBSERVER_LIBS,;t t
 s,@USE_THREAD_DB@,$USE_THREAD_DB,;t t
 s,@srv_xmlbuiltin@,$srv_xmlbuiltin,;t t
 s,@srv_xmlfiles@,$srv_xmlfiles,;t t
+s,@GDBFREEPLAY_DEPFILES@,$GDBFREEPLAY_DEPFILES,;t t
 s,@LTLIBOBJS@,$LTLIBOBJS,;t t
 CEOF
 
Index: configure.ac
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/configure.ac,v
retrieving revision 1.24
diff -u -p -r1.24 configure.ac
--- configure.ac	31 Jul 2008 17:46:33 -0000	1.24
+++ configure.ac	11 Sep 2008 00:09:24 -0000
@@ -183,12 +183,14 @@ fi
 
 GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_hostio_err_objs $srv_thread_depfiles"
 GDBSERVER_LIBS="$srv_libs"
+GDBFREEPLAY_DEPFILES="$freeplay_tgtobj"
 
 AC_SUBST(GDBSERVER_DEPFILES)
 AC_SUBST(GDBSERVER_LIBS)
 AC_SUBST(USE_THREAD_DB)
 AC_SUBST(srv_xmlbuiltin)
 AC_SUBST(srv_xmlfiles)
+AC_SUBST(GDBFREEPLAY_DEPFILES)
 
 AC_OUTPUT(Makefile,
 [case x$CONFIG_HEADERS in
Index: gdbfreeplay-back.c
===================================================================
RCS file: gdbfreeplay-back.c
diff -N gdbfreeplay-back.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdbfreeplay-back.c	11 Sep 2008 00:09:24 -0000
@@ -0,0 +1,1159 @@
+/*
+ * gdbfreeplay-back.c
+ *
+ * Backend for gdbfreeplay.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+typedef struct STOPFRAME {
+  /* frame_id -- a unique identifier per stop frame.  */
+  unsigned int frame_id;
+  /* pc -- address of the next instruction to be "executed".  */
+  unsigned long long pc;
+  /* FIXME remove! predecr_pc -- address that will be reported by a breakpoint.
+     These two are different on some targets, see gdb source code,
+     DECR_PC_AFTER_BREAK.  */
+  unsigned long long predecr_pc;
+  unsigned long eventpos;
+  unsigned long Opos;
+} StopFrame;
+
+StopFrame *stopframe;
+int last_cached_frame = 0;
+int framecache_size = 0;
+int cur_frame = -1;
+
+#include "gdbfreeplay.h"
+
+/*
+ * scan_gdbreplay_file
+ *
+ * Make a pass through the replay log, noting the positions
+ * of the stop events and certain packets within them.
+ *
+ * Returns: FAIL, PASS.
+ */
+
+#define INBUF_SIZE 4096
+static char inbuf[INBUF_SIZE];
+
+#define GDB_LINEBUF_SIZE 4096
+static char gdb_linebuf[GDB_LINEBUF_SIZE];
+
+static enum successcode
+scan_gdbreplay_file (FILE *infile)
+{
+  /* Make a pass over the entire file -- cache the record positions.  */
+  char *line, *p;
+  unsigned long nextpos;
+  unsigned long long GPC;
+
+  /* First skip empty lines.  */
+  do {
+    nextpos = ftell (infile);
+    line = fgets (inbuf, sizeof (inbuf), infile);
+  } while (line != NULL && inbuf[0] == '\n');
+
+  if (line == NULL)
+    return FAIL;		/* End of file, nothing there.  */
+
+  /* Now for the main loop.
+   *
+   * Scan the rest of the file, recording stop events etc.
+   */
+  do {
+    /* 'g' packet message?  */
+    if (strstr (line, "$g#67") != NULL)
+      {
+	/* See if we need to grab the PC from this packet.  */
+	if (stopframe[last_cached_frame].pc == 0 ||
+	    stopframe[last_cached_frame].pc == (unsigned long long) -1)
+	  {
+	    nextpos = ftell (infile);
+	    line = fgets (inbuf, sizeof (inbuf), infile);
+	    stopframe[last_cached_frame].pc = target_pc_from_g (line);
+	  }
+      }
+
+    /* Reset PC after breakpoint?  */
+    else if ((p = strstr (line, "$G")) != NULL)
+      {
+	GPC = target_pc_from_G (p);
+	if (stopframe[last_cached_frame].pc == 0 ||
+	    stopframe[last_cached_frame].pc == (unsigned long long) -1)
+	  {
+	    /* Unlikely, but if we need to, we can just grab this PC.  */
+	    stopframe[last_cached_frame].pc = GPC;
+	  }
+	else if (stopframe[last_cached_frame].pc == GPC + 1)
+	  {
+	    /* FIXME remove!  */
+	    /* OK, this is gdb decrementing the PC after a breakpoint.  */
+	    stopframe[last_cached_frame].predecr_pc =
+	      stopframe[last_cached_frame].pc;
+	    stopframe[last_cached_frame].pc = GPC;
+	  }
+      }
+
+    /* 'O' packet(s)?  Watch out for "$OK", we don't want that.  */
+    else if ((p = strstr (line, "$O")) != NULL &&
+	     strstr (p, "$OK#9a") == NULL)
+      {
+	/* If we know these, we can feed them back to gdb
+	   during 'continue'.  */
+	stopframe[last_cached_frame].Opos = nextpos;
+      }
+
+    /* Stop event message?  */
+    else if ((p = strstr (line, "$T")) != NULL ||
+	     (p = strstr (line, "$S")) != NULL ||
+	     (p = strstr (line, "$W")) != NULL)
+      /* FIXME || (p = strstr (line, "$X")) != NULL) 
+	 (ambiguity with binary write X packet).  */
+      {
+	if (last_cached_frame >= framecache_size - 1)
+	  {
+	    /* Time to grow the frame cache buffer.  */
+	    framecache_size += 2048;
+	    if (verbose)
+	      fprintf (stdout, "Growing frame cache to %d\n", 
+		       framecache_size);
+	    stopframe = realloc (stopframe, 
+				 framecache_size * sizeof (stopframe[0]));
+	  }
+	/* Special case: 0th frame.
+
+	   On the first pass, the FIRST time I see a stop event
+	   for the 0th frame, I will not increment the stopframe
+	   index.  
+
+	   The 0th frame is special, in that its "stop event"
+	   happens not as a result of a real stop event, but
+	   as a result of gdb sending the "?" query.  Therefore
+	   the apparent event falls in the middle of the frame.
+	*/
+
+	if (last_cached_frame != 0 ||
+	    stopframe [last_cached_frame].eventpos != 0)
+	  last_cached_frame++;
+
+	/* Since we now have a known frame, default to using it.  */
+	cur_frame = 0;
+	stopframe[last_cached_frame].eventpos = nextpos + p - line;
+	/* The frame for this event will default to not having 
+	   any 'O' packets, until we recognize one.  */
+	stopframe[last_cached_frame].Opos = 0;
+
+	if (p[1] == 'T')
+	  stopframe[last_cached_frame].pc = target_pc_from_T (p);
+
+	if (verbose)
+	  fprintf (stdout, "Record event pos at %lu ('%c')\n",
+		   stopframe [last_cached_frame].eventpos,
+		   p[1]);
+      }
+    nextpos = ftell (infile);
+    line = fgets (inbuf, sizeof (inbuf), infile);
+  } while (line != NULL);
+    
+  return PASS;
+}
+
+/*
+ * gdbfreeplay_open
+ *
+ * Open the gdbfreeplay "target", which mainly means opening 
+ * the gdbreplay log file.
+ *
+ * Returns: FAIL, PASS.
+ */
+
+static FILE *replay_file;
+
+enum successcode
+gdbfreeplay_open (char *filename)
+{
+  if ((replay_file = fopen (filename, "r")) == NULL)
+    {
+      fprintf (stderr, "GDBFREEPLAY: could not open file %s for input.\n",
+	       filename);
+      return FAIL;
+    }
+
+  return scan_gdbreplay_file (replay_file);
+}
+
+/*
+ * gdbreadline 
+ *
+ * Read a line of input from gdb (from the socket).
+ */
+
+static char *
+gdbreadline (int fd)
+{
+  char *p = gdb_linebuf;
+  int saw_dollar = 0;
+  int saw_pound = 0;
+  int cksum_chars = 0;
+
+  while (p < gdb_linebuf + sizeof (gdb_linebuf) - 1)
+    {
+      read (fd, p, 1);
+      p[1] = '\0';
+      switch (p[0]) {
+      case '+':
+	/* Receive "ack" from gdb.  
+	   Assuming it is first char in buffer, ignore.  */
+	if (p - gdb_linebuf != 0)
+	  fprintf (stdout, "gdbreadline: '+' ack in middle of line?\n");
+	continue;
+	break;
+      case '-':
+	if (p - gdb_linebuf == 0)
+	  {
+	    /* Receive "nack" from gdb.  FIXME what to do?
+
+	       Generally, this isn't going to be because of a 
+	       communication drop-out, but because gdb simply
+	       did not like the last thing that we sent.
+
+	       Sending the same thing again probably isn't going to help...
+	     */
+	    return gdb_linebuf;
+	  }
+	goto default_label;
+	break;
+      case '$':
+	if (saw_dollar)
+	  fprintf (stdout, "gdbreadline: two '$' in one line:\n\t'%s'\n",
+		   gdb_linebuf);
+
+	saw_dollar = 1;
+	break;
+      case '#':
+	if (!saw_dollar)
+	  fprintf (stdout, "gdbreadline: '#' before '$':\n\t'%s'\n",
+		   gdb_linebuf);
+
+	if (saw_pound)
+	  fprintf (stdout, "gdbreadline: two '#' in one line:\n\t'%s'\n",
+		   gdb_linebuf);
+
+	saw_pound = 1;
+	break;
+      default:
+      default_label:
+	if (saw_pound)
+	  cksum_chars++;
+	if (cksum_chars >= 2)
+	  {
+	    /* Got a complete message.  */
+	    return gdb_linebuf;
+	  }
+	break;
+      }
+      ++p;
+    }
+  /* Shouldn't get here except for buffer overflow.  */
+  fprintf (stdout, "gdbreadline: buffer overflow?\n");
+  return gdb_linebuf;
+}
+
+/*
+ * find_reply
+ *
+ * Given a filepos pointing to just after a given gdb request, 
+ * find and return a char * containing the reply to that request.
+ *
+ * Return NULL on failure.
+ */
+
+static char *
+find_reply (FILE *logfile, long filepos)
+{
+  char *line;
+
+  if (filepos == -1)
+    return NULL;
+
+  /* Position the file (it's probably already there...)  */
+  fseek (logfile, filepos, SEEK_SET);
+  /* In principle, the next input line should contain the reply.  */
+  line = fgets (inbuf, sizeof (inbuf), logfile);
+
+  return line;
+}
+
+/*
+ * frame_find_request
+ *
+ * Given a request from gdb, in the form of a string, 
+ * find an identical request within the current frame
+ * of the gdbreplay log file.
+ *
+ * Return a file position immediately following the request.
+ */
+
+static long
+frame_find_request (FILE *logfile, char *request)
+{
+  long curpos;
+  char *line;
+
+  if (cur_frame >= 0)
+    {
+      /* Position the input at the beginning of the frame.  
+         SPECIAL CASE: for frame zero, go to the beginning of the log.  
+	 This is so we can find gdb requests that preceed the first
+	 stop-event message.
+      */
+      if (cur_frame == 0)
+	curpos = 0;
+      else
+	curpos = stopframe[cur_frame].eventpos;
+      fseek (logfile, curpos, SEEK_SET);
+      /* Now search for a matching request.  */
+      while ((line = fgets (inbuf, sizeof (inbuf), logfile)) != NULL)
+	{
+	  /* End of current frame?
+	     If we're the last frame, just read till the end of file.  */
+	  if (cur_frame < last_cached_frame &&
+	      curpos >= stopframe[cur_frame + 1].eventpos)
+	    break;
+	  curpos = ftell (logfile);
+	  if (strstr (line, request) != NULL)
+	    {
+	      /* Matching request found.  Return position.  */
+	      return curpos;
+	    }
+	}
+    }
+
+  /* Not found.  */
+  return -1;
+}
+
+/*
+ * gdb_ack
+ * 
+ * Send an ack to gdb.
+ * Returns void.
+ */
+
+static void
+gdb_ack (int fd)
+{
+  static const char *ack = "+";
+  write (fd, ack, 1);
+}
+
+/*
+ * hex_to_int
+ */
+
+int 
+hex_to_int (int c)
+{
+  if (c >= '0' && c <= '9')
+    return c - '0';
+  else if (c >= 'a' && c <= 'f')
+    return c - 'a' + 10;
+  else if (c >= 'A' && c <= 'F')
+    return c - 'F' + 10;
+  else
+    return 0;
+}
+
+/*
+ * int_to_hex
+ *
+ * (lifted from gdb)
+ */
+
+int 
+int_to_hex (int nib)
+{
+  if (nib < 10)
+    return '0' + nib;
+  else
+    return 'a' + nib - 10;
+}
+
+#define EOI 0xdeadbeef
+
+/*
+ * convert_logchar
+ *
+ * Adapted from gdbreplay.
+ * Some characters in the log are escaped, and
+ * need to be translated before being sent back
+ * to gdb.
+ *
+ * Returns a de-escaped char.
+ */
+
+static int
+convert_logchar (char **logp)
+{
+  int ch, ch2;
+
+  ch = *(*logp)++;
+  switch (ch)
+    {
+    case '\0':
+    case '\n':
+      ch = EOI;	/* End of input (out of band).  */
+      break;
+    case '\\':
+      ch = *(*logp)++;
+      switch (ch)
+	{
+	case '\\':
+	  break;
+	case 'b':
+	  ch = '\b';
+	  break;
+	case 'f':
+	  ch = '\f';
+	  break;
+	case 'n':
+	  ch = '\n';
+	  break;
+	case 'r':
+	  ch = '\r';
+	  break;
+	case 't':
+	  ch = '\t';
+	  break;
+	case 'v':
+	  ch = '\v';
+	  break;
+	case 'x':
+	  ch2 = *(*logp)++;
+	  ch = hex_to_int (ch2) << 4;
+	  ch2 = *(*logp)++;
+	  ch |= hex_to_int (ch2);
+	  break;
+	default:
+	  /* Treat any other char as just itself */
+	  break;
+	}
+    default:
+      break;
+    }
+  return (ch);
+}
+
+/*
+ * gdbwriteline
+ *
+ * Send a line of data to gdb, stripped of any 
+ * prefix or suffix stuff.
+ *
+ * Prefix is anything preceeding a '$'.
+ * Suffix is anything following a "#xx" (including any newlines).
+ *
+ * Returns void.
+ */
+
+static void
+gdbwriteline (int fd, char *line)
+{
+  char *end;
+  int ich;
+  char ch;
+
+  if (line)
+    {
+      /* Strip prefix.  */
+      while (*line && *line != '$')
+	line++;
+      if (*line)
+	{
+	  /* Strip suffix.  */
+	  end = strchr (line, '#');
+	  if (end && *end)
+	    {
+	      /* Got line, send it.  */
+	      end[3] = '\0';
+	      while ((ich = convert_logchar (&line)) != EOI)
+		{
+		  ch = ich;
+		  write (fd, &ch, 1);
+		}
+	    }
+	}
+    }
+}
+
+/*
+ * stopframe_signal
+ *
+ * Return the signal number for which the stopframe stopped.
+ * This is a brute force implementation.  Probably should plan
+ * on caching the value to speed things up.
+ */
+
+static int
+stopframe_signal (FILE *infile, int id)
+{
+  char *line, *p;
+  int sig;
+
+  fseek (infile, stopframe[id].eventpos, SEEK_SET);
+  line = fgets (inbuf, sizeof (inbuf), infile);
+
+  if ((p = strstr (line, "$T")) != NULL ||
+      (p = strstr (line, "$S")) != NULL)
+    {
+      /* Signal value is two ascii/hex bytes following "$S" or "$T".  */
+      sig = (hex_to_int (p[2]) << 4) + hex_to_int (p[3]);
+      return sig;
+    }
+  return 0;
+}
+
+/*
+ * freeplay_play_O_packets
+ *
+ * Send one or more 'O' packets back to gdb.
+ * Returns void.
+ */
+
+static void
+freeplay_play_O_packets (FILE *infile, int fd, unsigned long filepos)
+{
+  char *line, *p;
+
+  fseek (infile, filepos, SEEK_SET);
+
+  while ((line = fgets (inbuf, sizeof (inbuf), infile)) != NULL)
+    {
+      if ((p = strstr (line, "$O")) != NULL)
+	{
+	  /* FIXME: do I need to send an ACK?  */
+	  gdb_ack (fd);
+	  gdbwriteline (fd, p);
+	}
+      /* When we reach the next stop event, we're done.  */
+      else if (strstr (line, "$T") != NULL ||
+	       strstr (line, "$S") != NULL)
+	{
+	  return;	/* done */
+	}
+    }
+}
+
+/*
+ * freeplay_find_event
+ *
+ * Scan (forward or backward) thru the stop events, 
+ * looking for a reason to stop now (assuming gdb has
+ * asked us to continue).
+ *
+ * Returns event frame index, or -1 for failure.
+ */
+
+static int
+freeplay_find_event (FILE *infile, 
+		     int gdb_fd, 
+		     int start, 
+		     enum direction_code direction,
+		     enum direction_code play_O_packets)
+{
+  int i;
+  int signum;
+
+  /* Right here, we have to be conscious of the critical difference
+     between a replayer and a simulator.  
+
+     No matter what the reason for stopping, we MUST advance
+     beyond the current frame.  Which is not the same as 
+     advancing beyond the current PC.  If, say, there is a
+     reason why we can't advance beyond the current PC (like
+     say it's an illegal instruction), then the next frame will
+     contain the same PC and the same signal.
+
+     But we cannot just get stuck at the current stop event.
+     We have to advance (or go backward, as the case may be),
+     until and unles we hit the end of the recording.
+
+     So, force the first step.
+  */
+
+  if (play_O_packets && stopframe[start].Opos != 0)
+    freeplay_play_O_packets (infile, gdb_fd, stopframe[start].Opos);
+  if (direction == DIR_FORWARD && start < last_cached_frame)
+    start++;
+  else if (direction == DIR_BACKWARD && start > 0)
+    start--;
+
+  /* Here's a strange loop for you: goes forward or backward.  */
+  for (i = start; 
+       i >= 0 && i <= last_cached_frame;
+       direction == DIR_FORWARD ? i++ : i--)
+    {
+      signum = stopframe_signal (infile, i);
+      /* FIXME we need some modal handling for SIGTRAP.  */
+      if (signum != 0 && signum != 5)
+	{
+	  /* Here's an event we can stop at, regardless of breakpoints.  */
+	  /* FIXME: there is some signal-ignoring stuff to be looked at.  */
+	  return i;
+	}
+      else if (remote_breakpoint_here_p (SOFTWARE_BP, stopframe[i].pc) == PASS)
+	{
+	  /* Found a breakpoint.
+	     FIXME need some DECR_PC_AFTER_BREAK handling.  */
+	  return i;
+	}
+      if (play_O_packets && stopframe[i].Opos != 0)
+	freeplay_play_O_packets (infile, gdb_fd, stopframe[i].Opos);
+    }
+  /* Found no reason to stop.  */
+  return -1;
+}
+
+/*
+ * add_checksum
+ *
+ * Compute the checksum for a string that will go to gdb, 
+ * and concatenate it onto the string.
+ *
+ * Returns input string modified in place (better have room!)
+ */
+
+char *
+add_checksum (char *inbuf)
+{
+  int cksum = 0;
+  char *p = inbuf;
+
+  /* Sanity check.  */
+  if (inbuf == NULL)
+    return NULL;
+
+  /* If the string doesn't start with a '$', it's broken.  */
+  if (*p++ != '$')
+    return inbuf;
+
+  /* Now add up the cksum.  */
+  while (*p)
+    {
+      /* If there's already a '#', it doesn't count toward the cksum.  
+         Stop there.  */
+      if (*p == '#')
+	break;
+      cksum += *p++;
+    }
+
+  /* Add a '#' at the end, even if it's already there, and
+     overwrite any old checksum that may be there already.  */
+  *p++ = '#';
+  *p++ = int_to_hex ((cksum >> 4) & 0xf);
+  *p++ = int_to_hex (cksum & 0xf);
+  *p++ = '\0';
+
+  /* Done.  */
+  return inbuf;
+}
+
+/*
+ * freeplay_show_next_commands
+ *
+ * Output the gdb commands contained in the current frame.
+ */
+
+static void
+freeplay_show_next_commands (FILE *infile)
+{
+  char *line;
+
+  if (cur_frame >= 0 && cur_frame <= last_cached_frame)
+    {
+      fseek (infile, stopframe[cur_frame].eventpos, SEEK_SET);
+      while ((line = fgets (inbuf, sizeof (inbuf), infile)) != NULL)
+	{
+	  if (cur_frame + 1 <= last_cached_frame &&
+	      ftell (infile) >= stopframe[cur_frame + 1].eventpos)
+	    /* Done with current frame.  */
+	    break;
+
+	  if (line[0] == 'c' && line[1] == ' ')
+	    fputs (line, stdout);
+	}
+    }
+}
+
+/*
+ * handle_special_case
+ *
+ * Handle these requests locally, rather than through the replay buffer.
+ *
+ * Returns: a string reply for gdb.
+ */
+
+static char OK[8]    = "$OK#9a";
+static char EMPTY[8] = "$#00";
+static char STOP[8]  = "$S00#44";
+static char E01[8]   = "$E01#a6";
+
+static char *
+handle_special_case (FILE *infile, int fd, char *request)
+{
+  unsigned long long addr;
+  unsigned long len;
+  int next_event_frame;
+  char *p;
+
+  static char *monitor_verbose_off = "$qRcmd,766572626f7365206f6666#13";
+  static char *monitor_verbose_on  = "$qRcmd,766572626f7365206f6e#d6";
+  static char *monitor_gdbreplay_next = 
+    "$qRcmd,6764627265706c61792d6e657874#83";
+
+  /* Handle 'k' (kill) request by exiting.  */
+  if (strstr (request, "$k#6b") != NULL)
+    {
+      /* Well, to PROPERLY honor the 'kill' request, 
+	 we need to actually shoot ourselves in the head...  */
+      gdb_ack (fd);
+      fprintf (stdout, "gdbfreeplay -- killed by gdb.\n");
+      exit (0);
+    }
+
+  /* Handle 'D' (detach) request by exiting.  
+     FIXME might want to stay alive and offer the socket again.  */
+  if (strstr (request, "$D#44") != NULL)
+    {
+      /* OK, we're not yet set up to close and re-open the socket, 
+	 and a new gdb does not seem able to re-attach, so for now
+	 let's actually quit.  */
+      gdb_ack (fd);
+      gdbwriteline (fd, OK);
+      fprintf (stdout, "gdbfreeplay -- gdb has detached.  Exiting.\n");
+      exit (0);
+    }
+
+  /* Handle "monitor verbose on".  */
+  if (strstr (request, monitor_verbose_on) != NULL)
+    {
+      verbose = 1;
+      return OK;
+    }
+
+  /* Handle "monitor verbose off".  */
+  if (strstr (request, monitor_verbose_off) != NULL)
+    {
+      verbose = 0;
+      return OK;
+    }
+
+  /* Handle "monitor gdbreplay-next".  */
+  if (strstr (request, monitor_gdbreplay_next) != NULL)
+    {
+      /* Show the next gdb commands from the gdbreplay log,
+	 just like gdbreplay would.  */
+      freeplay_show_next_commands (infile);
+      return OK;
+    }
+
+  /* Handle 'R' (restart) request.
+     This is an extended-remote request that is not really used any more,
+     but we can send it out-of-band (using "maint packet") to effectively
+     set the replay buffer back to the beginning.  */
+  if (strstr (request, "$R#52") != NULL)
+    {
+      /* Reset replay buffer to beginning.  */
+      /* NOTE: gdb doesn't know about target state changing, so
+	 if you use this, you must accompany it with "flushregs".  */
+      cur_frame = 0;
+      return OK;
+    }
+
+  /* Handle 'S' (singlestep with a signal) like 's'.  */
+  if (strstr (request, "$S") != NULL)
+    {
+      /* This is gdb trying to step the target, but with a signal.
+
+	 Since in reality we can't have any influence on the execution
+	 trace of the "target", we can just ignore the signal and
+	 singlestep.
+
+	 Whatever happened before, will happen again...  */
+
+      if (verbose)
+	fprintf (stdout, 
+		 "handle_special_case: demoting 'S' to 's'.\n");
+      goto step_label;
+    }
+
+  /* Handle 's' (step) by advancing the cur_frame index.  */
+  if (strstr (request, "$s#73") != NULL)
+    {
+    step_label:
+      if (cur_frame < last_cached_frame)
+	cur_frame++;
+
+      if (stopframe[cur_frame].eventpos != 0)
+	{
+	  return find_reply (infile, 
+			     stopframe[cur_frame].eventpos);
+	}
+      else
+	{
+	  /* Not sure how this could even happen, but... */
+	  return STOP;
+	}
+    }
+
+  /* Handle 'bs' (reverse stepi) by decrementing the cur_frame index.  */
+  if (strstr (request, "$bs#d5") != NULL)
+    {
+      if (cur_frame > 0)
+	cur_frame--;
+
+      if (stopframe[cur_frame].eventpos != 0)
+	{
+	  return find_reply (infile, 
+			     stopframe[cur_frame].eventpos);
+	}
+      else
+	{
+	  /* Not sure how this could even happen, but... */
+	  return STOP;
+	}
+    }
+
+  /* Handle 'C' (continue with a signal) like 'c'.  */
+  if (strstr (request, "$C") != NULL)
+    {
+      /* This is gdb trying to continue the target, but with a signal.
+
+	 Since in reality we can't have any influence on the execution
+	 trace of the "target", we can just ignore the signal and
+	 continue.   
+
+	 Whatever happened before, will happen again...  */
+
+      if (verbose)
+	fprintf (stdout, 
+		 "handle_special_case: demoting 'C' to 'c'.\n");
+      goto continue_label;
+    }
+
+  /* Handle 'c' (continue) by searching the cache for a stop event.  */
+  if (strstr (request, "$c#63") != NULL)
+    {
+    continue_label:
+      next_event_frame = freeplay_find_event (infile, fd, 
+					      cur_frame, 
+					      DIR_FORWARD,
+					      PLAY_O_PACKETS);
+      if (next_event_frame != -1)
+	{
+	  /* Got a stop event.  Make it the current frame, and tell gdb.
+	   */
+	  cur_frame = next_event_frame;
+	}
+      else
+	{
+	  cur_frame = last_cached_frame;
+	}
+
+      /* Find the original event message for this stop event.  */
+      fseek (infile, stopframe[cur_frame].eventpos, SEEK_SET);
+      fgets (inbuf, sizeof (inbuf), infile);
+
+      /* If it's a "$T05" (SIGTRAP), give the target a chance to
+	 re-compose it (possibly allowing for DECR_PC_AFTER_BREAK).  
+      */
+      if ((p = strstr (inbuf, "$T05")) != NULL)
+	return add_checksum (target_compose_T_packet (p,
+						      stopframe[cur_frame].pc,
+						      next_event_frame == -1 ?
+						      0 :
+						      1 /* breakpoint_p */));
+      /* Otherwise, just return it.  */
+      else
+	return &inbuf[0];
+    }
+
+  /* Handle 'bc' (revese continue) by searching the cache for a stop event.  */
+  if (strstr (request, "$bc#c5") != NULL)
+    {
+      next_event_frame = freeplay_find_event (infile, fd, 
+					      cur_frame, 
+					      DIR_BACKWARD,
+					      PLAY_O_PACKETS);
+      if (next_event_frame != -1)
+	{
+	  /* Got a stop event.  Make it the current frame, and tell gdb.
+	   */
+	  cur_frame = next_event_frame;
+	}
+      else
+	{
+	  /* WTF? */
+	  gdb_ack (fd);
+	  strcpy (inbuf, "$O5768617420746865206675636b3f");
+	  if (verbose)
+	    fprintf (stdout, "WTF? %s\n", add_checksum (inbuf));
+	  gdbwriteline (fd, add_checksum (inbuf));
+	  cur_frame = 0;
+	}
+
+      /* Find the original event message for this stop event.  */
+      fseek (infile, stopframe[cur_frame].eventpos, SEEK_SET);
+      fgets (inbuf, sizeof (inbuf), infile);
+
+      /* If it's a "$T05" (SIGTRAP, give the target a chance to
+	  re-compose it (possibly allowing for DECR_PC_AFTER_BREAK).  
+      */
+      if ((p = strstr (inbuf, "$T05")) != NULL)
+	return add_checksum (target_compose_T_packet (p,
+						      stopframe[cur_frame].pc,
+						      next_event_frame == -1 ?
+						      0 :
+						      1 /* breakpoint_p */));
+      /* If it's a "$S", just return it (FIXME?)  */
+      else
+	return &inbuf[0];
+    }
+
+  /* Handle Z0 set breakpoint.  */
+  if ((p = strstr (request, "$Z0")) != NULL)
+    {
+      if (p[3] == ',')
+	{
+	  addr = strtoull (p + 4, &p, 16);
+	  if (p[0] == ',')
+	    {
+	      len = strtoul (p + 1, NULL, 16);
+	      remote_set_breakpoint (SOFTWARE_BP, addr, len);
+	      return OK;
+	    }
+	}
+    }
+
+  /* Handle z0 remove breakpoint.  */
+  if ((p = strstr (request, "$z0")) != NULL)
+    {
+      if (p[3] == ',')
+	{
+	  addr = strtoull (p + 4, &p, 16);
+	  if (p[0] == ',')
+	    {
+	      len = strtoul (p + 1, NULL, 16);
+	      remote_remove_breakpoint (SOFTWARE_BP, addr, len);
+	      return OK;
+	    }
+	}
+    }
+#if 0
+  /* 
+     We can actually ignore QPassSignals, because if the original
+     target that made the recording handled it, it will be transparent 
+     to us, and if not, it will still be transparent.  It's not 
+     important enough for us to want to handle locally.  */
+
+  /* Handle QPassSignals.
+     FIXME we should implement this, but for the meantime, 
+     don't let gdb think we're supporting it if we're actually not.  */
+  if (strstr (request, "$QPassSignals") != NULL)
+    {
+      if (verbose)
+	fprintf (stdout, "handle_special_case: punting QPassSignals.\n");
+      return EMPTY;
+    }
+#endif
+  /* Handle "vCont" by saying we can't do it.
+
+     FIXME we could do it, mimicing 's' and 'c' etc., but 
+     until such time we must intercept it here, lest we should
+     let it be handled by something in the replay buffer.
+
+     That would be bad...   */
+
+  if (strstr (request, "$vCont") != 0)
+    return EMPTY;
+
+  /* TODO: tfind, QPassSignals...  */
+
+  /* Let the replay buffer handle it.  */
+  return NULL;
+}
+
+/*
+ * fallbacks
+ *
+ * Attempt to handle requests that were not handled otherwise.
+ * Returns: char * or NULL.
+ */
+
+static char *
+fallbacks (FILE *infile, int fd, char *request)
+{
+  char *p;
+
+  /* Handle "Hc0" request.  */
+  if (strstr (request, "$Hc0#db") != NULL)
+    {
+      /* Debugger just wants to set the "continue thread" to zero.
+	 Don't know why this isn't in the replay cache, but it's
+	 harmless, just say OK.  */
+      if (verbose)
+	fprintf (stdout, "fallbacks: absorbing 'Hc0'\n");
+      return OK;
+    }
+  /* Handle un-cached memory request (if not handled upstream).  */
+  if (strstr (request, "$m") != NULL)
+    {
+      /* FIXME this need not happen so often, 
+	 once we do a better job of simulating memory.  */
+      if (verbose)
+	fprintf (stdout, "fallbacks: failing memory request '%s'.\n",
+		 request);
+      return E01;
+    }
+  /* Handle P/p requests (if they weren't handled upstream).  */
+  if (strstr (request, "$p") != NULL ||
+      strstr (request, "$p") != NULL)
+    {
+      if (verbose)
+	fprintf (stdout, "fallbacks: absorbing P/p request.\n");
+      return EMPTY;	/* Tell gdb we don't know that one.  */
+    }
+
+  /* Handle 'G' request (if not handled upstream).
+     Just tell gdb "OK", and otherwise ignore it.
+     The debugger now has its own idea of what the registers are...  */
+  if (strstr (request, "$G") != NULL)
+    {
+      if (verbose)
+	fprintf (stdout, "fallbacks: absorbing G request.\n");
+      return OK;
+    }
+
+  /* Handle 'M' or 'X' request (if not handled upstream).
+     There are two ways to go here -- just say "OK", without
+     actually doing anything, or return an error.
+
+     Going to try just saying "OK", for now...  */
+  if (strstr (request, "$M") != NULL ||
+      strstr (request, "$X") != NULL)
+    {
+      if (verbose)
+	fprintf (stdout, "fallbacks: absorbing memory write request.\n");
+      return OK;
+    }
+
+  /* Handle 'g' request (if not handled upstream).  
+     This is usually at a singlestep event.  
+     We need to construct a 'g' packet for gdb from the 'T' packet.  */
+  if (strstr (request, "$g#67") != NULL)
+    {
+      /* Find the original event message for this stop event.  */
+      fseek (infile, stopframe[cur_frame].eventpos, SEEK_SET);
+      fgets (inbuf, sizeof (inbuf), infile);
+      /* If it's a "$T", give the target a chance to compose  a g packet.
+	 (possibly allowing for DECR_PC_AFTER_BREAK).  */
+      if ((p = strstr (inbuf, "$T")) != NULL)
+	{
+	  if (verbose)
+	    fprintf (stdout, "fallbacks: constructing 'g' packet.\n");
+	  return add_checksum (target_compose_g_packet (p));
+	}
+      /* If it's an 'S' packet, there ain't much we can do
+	 (FIXME unles we at least know the PC?  */
+      else
+	{
+	  if (verbose)
+	    fprintf (stdout, "fallbacks: punting on 'g' packet request.\n");
+	  return EMPTY;
+	}
+    }
+
+  /* Any unhandled qRcmd, just echo it to stdout.  */
+  if ((p = strstr (request, "$qRcmd,")) != NULL)
+    {
+      if (verbose)
+	fprintf (stdout, "fallbacks: ignoring %s\n", p);
+      return EMPTY;
+    }
+
+  /* Default for any other un-handled request -- return empty string.  */
+  if (verbose)
+    fprintf (stdout, "fallbacks: absorbing unknown request '%s'.\n",
+	     request);
+  return EMPTY;
+}
+
+/*
+ * gdbfreeplay
+ *
+ * This is the function that manages the interaction with gdb.
+ */
+
+void
+gdbfreeplay (int fd)
+{
+  char *request;
+  char *reply;
+
+  /* Process requests from gdb.  */
+  while (1)
+    {
+      /* Read next request from gdb.  */
+      request = gdbreadline (fd);
+      if (verbose)
+	fprintf (stdout, "gdb request: %s\n", request);
+
+      /* FIXME -- this is where we should handle special cases, including:
+	 -- vCont
+	 -- tfind
+      */
+
+      reply = handle_special_case (replay_file, fd, request);
+
+      if (reply == NULL)
+	{
+	  /* Now see if we have a matching request in the current frame.  */
+	  reply = find_reply (replay_file, 
+			      frame_find_request (replay_file, request));
+	}
+
+      if (reply == NULL)
+	{
+	  /* Found no matching request in the current frame.
+	     Do fall-backs and last resorts.  */
+	  reply = fallbacks (replay_file, fd, request);
+	}
+
+      if (verbose)
+	fprintf (stdout, "freeplay reply: %s\n", 
+		 reply == NULL ? "<not found>" : reply);
+
+      if (reply)
+	{
+	  gdb_ack (fd);
+	  gdbwriteline (fd, reply);
+	}
+      else
+	{
+	  /* FIXME -- gotta say something back... */
+	}
+    }
+}
Index: gdbfreeplay-front.c
===================================================================
RCS file: gdbfreeplay-front.c
diff -N gdbfreeplay-front.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdbfreeplay-front.c	11 Sep 2008 00:09:24 -0000
@@ -0,0 +1,311 @@
+/*
+ * gdb-freeplay
+ *
+ * Freely replay a remote debug session logfile for GDB.
+ *
+ * Parts of gdb-freeplay are adapted from gdbreplay, 
+ * by Stu Grossman and Fred Fish.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#include <ctype.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#if HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#if USE_WIN32API
+#include <winsock.h>
+#endif
+
+#ifndef HAVE_SOCKLEN_T
+typedef int socklen_t;
+#endif
+
+/* Version information, from version.c.  */
+extern const char version[];
+extern const char host_name[];
+
+/* Print the system error message for errno, and also mention STRING
+   as the file name for which the error was encountered.
+   Then return to command level.  */
+
+static void
+perror_with_name (char *string)
+{
+#ifndef STDC_HEADERS
+  extern int errno;
+#endif
+  const char *err;
+  char *combined;
+
+  err = strerror (errno);
+  if (err == NULL)
+    err = "unknown error";
+
+  combined = (char *) alloca (strlen (err) + strlen (string) + 3);
+  strcpy (combined, string);
+  strcat (combined, ": ");
+  strcat (combined, err);
+  fprintf (stderr, "\n%s.\n", combined);
+  fflush (stderr);
+  exit (1);
+}
+
+/*
+ * remote_close
+ *
+ * Close the gdb socket.
+ */
+
+static void
+remote_close (int remote_desc)
+{
+#ifdef USE_WIN32API
+  closesocket (remote_desc);
+#else
+  close (remote_desc);
+#endif
+}
+
+/*
+ * remote_open
+ *
+ * Open a connection to a remote debugger (gdb).
+ * NAME is the filename used for communication.  
+ *
+ * Returns: file descriptor. 
+ * Does not return in case of error.
+ */
+
+static int
+remote_open (char *name)
+{
+  int remote_desc;
+
+  if (!strchr (name, ':'))
+    {
+      fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
+      fflush (stderr);
+      exit (1);
+    }
+  else
+    {
+#ifdef USE_WIN32API
+      static int winsock_initialized;
+#endif
+      char *port_str;
+      int port;
+      struct sockaddr_in sockaddr;
+      socklen_t tmp;
+      int tmp_desc;
+
+      port_str = strchr (name, ':');
+
+      port = atoi (port_str + 1);
+
+#ifdef USE_WIN32API
+      if (!winsock_initialized)
+	{
+	  WSADATA wsad;
+
+	  WSAStartup (MAKEWORD (1, 0), &wsad);
+	  winsock_initialized = 1;
+	}
+#endif
+
+      tmp_desc = socket (PF_INET, SOCK_STREAM, 0);
+      if (tmp_desc < 0)
+	perror_with_name ("Can't open socket");
+
+      /* Allow rapid reuse of this port. */
+      tmp = 1;
+      setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
+		  sizeof (tmp));
+
+      sockaddr.sin_family = PF_INET;
+      sockaddr.sin_port = htons (port);
+      sockaddr.sin_addr.s_addr = INADDR_ANY;
+
+      if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
+	  || listen (tmp_desc, 1))
+	perror_with_name ("Can't bind address");
+
+      tmp = sizeof (sockaddr);
+      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp);
+      if (remote_desc == -1)
+	perror_with_name ("Accept failed");
+
+      /* Enable TCP keep alive process. */
+      tmp = 1;
+      setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE, (char *) &tmp, sizeof (tmp));
+
+      /* Tell TCP not to delay small packets.  This greatly speeds up
+         interactive response. */
+      tmp = 1;
+      setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
+		  (char *) &tmp, sizeof (tmp));
+
+#ifndef USE_WIN32API
+      close (tmp_desc);		/* No longer need this */
+
+      signal (SIGPIPE, SIG_IGN);	/* If we don't do this, then gdbreplay simply
+					   exits when the remote side dies.  */
+#else
+      closesocket (tmp_desc);	/* No longer need this */
+#endif
+    }
+
+#if defined(F_SETFL) && defined (FASYNC)
+  fcntl (remote_desc, F_SETFL, FASYNC);
+#endif
+
+  fprintf (stderr, "Replay logfile using %s\n", name);
+  fflush (stderr);
+
+  return remote_desc;
+}
+
+/*
+ * freeplay_version
+ *
+ * Print version information.
+ */
+
+static void
+freeplay_version (FILE *stream)
+{
+  fprintf (stream, "GNU gdb-freeplay %s%s\n"
+	   "Copyright (C) 2008 Free Software Foundation, Inc.\n"
+	   "gdb-freeplay is free software, covered by the GNU General Public License.\n"
+	   "\n"
+	   "This gdb-freeplay was configured as \"%s\"\n",
+	   PKGVERSION, version, host_name);
+}
+
+/*
+ * freeplay_usage
+ *
+ * Print usage information.
+ */
+
+static void
+freeplay_usage (FILE *stream)
+{
+  fprintf (stream, "Usage:\tgdb-freeplay [--verbose] [--version] <logfile> <host:port>\n");
+  if (REPORT_BUGS_TO[0] && stream == stdout)
+    fprintf (stream, "Report bugs to \"%s\".\n", REPORT_BUGS_TO);
+}
+
+#include "gdbfreeplay.h"
+
+/*
+ * TODO:
+ *
+ * Actually accept packets from gdb, with handshake where appropriate.
+ * Parse tfind requests and handle them in the frame cache.
+ * Try to match everything else from the frame cache and send cached reply.
+ */
+
+int verbose;
+
+extern int
+main (int argc, char **argv)
+{
+  char *default_socket = "localhost:2345";
+  int logfile_arg = 1;
+  int socket_arg = 2;
+  int remote_desc = -1, i;
+
+  for (i = 0; i < argc; i++)
+    {
+      if (strcmp (argv[i], "--version") == 0)
+	{
+	  freeplay_version (stdout);
+	  logfile_arg++;
+	  socket_arg++;
+	}
+      if (strcmp (argv[i], "--verbose") == 0)
+	{
+	  verbose = 1;
+	  logfile_arg++;
+	  socket_arg++;
+	}
+      else if (strcmp (argv[i], "--help") == 0)
+	{
+	  freeplay_usage (stdout);
+	  exit (0);
+	}
+      else if (i == logfile_arg)
+	{
+	  if (gdbfreeplay_open (argv[i]) != PASS)
+	    perror_with_name (argv[1]);
+	  logfile_arg = 0;
+	}
+      else if (i == socket_arg)
+	{
+	  if (verbose)
+	    fprintf (stdout, "Open socket: %s\n", argv[i]);
+	  remote_desc = remote_open (argv[i]);
+	  socket_arg = 0;
+	}
+    }
+
+  if (logfile_arg)
+    {
+      freeplay_usage (stderr);
+      exit (1);
+    }
+
+  if (socket_arg)
+    {
+      if (verbose)
+	fprintf (stdout, "Open default socket: %s\n", default_socket);
+      remote_desc = remote_open (default_socket);
+    }
+
+  if (remote_desc > 0)
+    {
+      gdbfreeplay (remote_desc);
+
+      if (verbose)
+	fprintf (stdout, "Close remote socket.\n");
+      remote_close (remote_desc);
+    }
+
+  exit (0);
+}
+
Index: gdbfreeplay-i386.c
===================================================================
RCS file: gdbfreeplay-i386.c
diff -N gdbfreeplay-i386.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdbfreeplay-i386.c	11 Sep 2008 00:09:24 -0000
@@ -0,0 +1,401 @@
+/*
+ * gdbfreeplay-i386.c
+ *
+ * Target-dependent component of gdbfreeplay for i386.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gdbfreeplay.h"
+
+/*
+ * Utility functions
+ */
+
+/*
+ * ix86_hex_to_unsigned_long
+ *
+ * Convert target-order ascii bytes to host unsigned long.
+ * Returns host unsigned long.
+ */
+
+static unsigned long
+ix86_hex_to_unsigned_long (char *hex)
+{
+  unsigned long ul;
+  int i;
+
+  for (i = 6; i >= 0; i-=2)
+    {
+      ul <<= 8;
+      ul += hex_to_int (hex[i]) << 4;
+      ul += hex_to_int (hex[i+1]);
+    }
+  return ul;
+}
+
+/*
+ * nybble_to_char
+ */
+
+static char
+nybble_to_char (int nybble)
+{
+  if (nybble < 10)
+    return '0' + nybble;
+  else
+    return 'a' + (nybble - 10);
+}
+
+/*
+ * ix86_unsigned_long_to_hex
+ *
+ * Convert host unsigned long to target-order ascii string.
+ * Return char buffer, static, use before calling again.
+ */
+
+static char *
+ix86_unsigned_long_to_hex (unsigned long value)
+{
+  int i;
+  static char buf[16];
+  char *p;
+
+  p = buf;
+  for (i = 0; i < 4; i++)
+    {
+      /* Intel byte order.  */
+      *p++ = nybble_to_char ((value >> 4) & 0xf);
+      *p++ = nybble_to_char (value & 0xf);
+      value >>= 8;
+    }
+  *p = '\0';
+  return buf;
+}
+
+/*
+ * target_pc_from_T
+ *
+ * Extract the PC value from the gdb protocol 'T' packet.
+ * Returns PC as host unsigned long long.
+ */
+
+unsigned long long
+target_pc_from_T (char *tpacket)
+{
+  char *p;
+
+  if (tpacket[0] == '$' && tpacket[1] == 'T' &&
+      (p = strstr (tpacket, ";08:")) != NULL)
+    {
+      return (unsigned long long) ix86_hex_to_unsigned_long (p + 4);
+    }
+
+  /* Fail -- just assume no legitimate PC will ever be -1...  */
+  return (unsigned long long) -1;
+}
+
+/*
+ * ix86_pc_from_registers
+ * 
+ * Extract the PC value from a gdb protocol registers file.
+ * Returns PC as host unsigned long long.
+ */
+
+static unsigned long long
+ix86_pc_from_registers (char *regs)
+{
+  return (unsigned long long) ix86_hex_to_unsigned_long (regs + 64);
+}
+
+/*
+ * target_pc_from_G
+ *
+ * Extract the PC value from the gdb protocol 'G' packet.
+ * Returns PC as host unsigned long long.
+ */
+
+unsigned long long
+target_pc_from_G (char *gpacket)
+{
+  if (gpacket[0] == '$' && gpacket[1] == 'G')
+    {
+      return (unsigned long long) ix86_pc_from_registers (gpacket + 2);
+    }
+
+  /* Fail -- just assume no legitimate PC will ever be -1...  */
+  return (unsigned long long) -1;
+}
+
+/*
+ * expand_rle
+ *
+ * Stubs sometimes send us data with RLE compression.
+ * FIXME this code is generic, move into gdbfreeplay-back.c
+ * The code for this function lifted from gdb.
+ */
+
+static char *
+expand_rle (char *input)
+{
+  static char *buf = NULL;
+  static int sizeof_buf;
+  long bc = 0;
+  int c, repeat;
+
+  /* Allocate buf on first time thru.  */
+  if (buf == NULL)
+    {
+      sizeof_buf = 4096;
+      buf = realloc (buf, sizeof_buf);
+    }
+
+  while (1)
+    {
+      c = *input++;
+      switch (c) {
+      case '#':	/* End of interesting data.  We don't care about checksum.  */
+      case '\0':/* End of string data.  */
+	buf[bc] = '\0';
+	return buf;
+	break;
+      case '$':
+	/* Shouldn't happen, but... */
+	fprintf (stderr, "Warning: expand_rle saw '$' in middle of packet.\n");
+      default:
+	if (bc >= sizeof_buf - 1)
+	  {
+	    /* Make some more room in the buffer.  */
+	    sizeof_buf *= 2;
+	    buf = realloc (buf, sizeof_buf);
+	  }
+	buf[bc++] = c;
+	continue;
+	break;
+      case '*':				/* Run length encoding.  */
+	c = *input++;
+	repeat = c - ' ' + 3;		/* Compute repeat count.  */
+
+	/* The character before '*' is repeated.  */
+	if (bc + repeat - 1 >= sizeof_buf - 1)
+	  {
+	    /* Make some more room in the buffer.  */
+	    sizeof_buf *= 2;
+	    buf = realloc (buf, sizeof_buf);
+	  }
+
+	memset (&buf[bc], buf[bc - 1], repeat);
+	bc += repeat;
+	continue;
+	break;
+      }
+    }
+}
+
+/*
+ * target_pc_from_g
+ *
+ * Extract the PC value from the gdb protocol 'g' packet reply.
+ * 
+ * Unlike the two above, this function accepts a FILE pointer
+ * rather than a char pointer, and must read data from the file.
+ *
+ * Returns PC as host unsigned long long.
+ */
+
+unsigned long long
+target_pc_from_g (char *gpacket)
+{
+  if (gpacket[0] == 'r' && gpacket[1] == ' ')
+    {
+      gpacket += 2;
+      if (gpacket[0] == '+')
+	gpacket++;
+      if (gpacket[0] == '$')
+	gpacket++;
+    }
+
+  return (unsigned long long) ix86_pc_from_registers (expand_rle (gpacket));
+}
+
+/*
+ * target_compose_T_packet
+ *
+ * On targets where DECR_PC_AFTER_BREAK is zero, this is a no-op.
+ * We just send back the T packet that was sent to us.
+ *
+ * On targets like i386, where DECR_PC_AFTER_BREAK is non-zero, 
+ * it gets complicated.  We have two pieces of information:
+ *
+ *  1) The address of the current instruction, and
+ *  2) Whether we arrived at the current instruction
+ *     by virtue of a breakpoint -- ie. whether gdb is
+ *     expecting the PC to be off-by-one.
+ *
+ * Based on that info, we decide whether the T packet that was
+ * sent to us is suitable as it is, or else we compose one and
+ * send it back.
+ *
+ * Returns a string T packet, possibly WITHOUT CHECKSUM.
+ * We leave it to the caller to handle that, if necessary,
+ * because it is target independent and therefore inappropriate
+ * to do here.  However the caller has to check for it, because
+ * if we simply return the T packet that we received, it will
+ * already have a checksum.
+ */
+
+char *
+target_compose_T_packet (char *origTpacket, 
+			 unsigned long long instruction_pc,
+			 int breakpoint_p)
+{
+  unsigned long long origTpacket_pc = target_pc_from_T (origTpacket);
+  static char reply_buf[128];
+  char *p;
+
+  /* There are four possibilities.
+     1) We got here by stepping, and instruction_pc == origTpacket_pc.
+     2) We got here by stepping, and instruction_pc != origTpacket_pc.
+     3) We got here by breakpoint, and instruction_pc == origTpacket_pc.
+     4) We got here by breakpoint, and instruction_pc != origTpacket_pc.
+
+     Actually, there's one more (not well understood):
+     5) instruction_pc and origTpacket_pc bear no relationship to each other.
+
+     In that case, we should bail and let gdb get the original Tpacket.  */
+
+  /* Case #5.  */
+  if (instruction_pc != origTpacket_pc &&
+      instruction_pc != origTpacket_pc - 1)
+    return origTpacket;
+
+  /* For #1 and #4, we don't have to do anything, and can return 
+     the original T packet.  */
+
+  /* Case #1.  */
+  if (!breakpoint_p && (instruction_pc == origTpacket_pc))
+    return origTpacket;
+
+  /* Case #4.  */
+  if (breakpoint_p && (instruction_pc == origTpacket_pc - 1))
+    return origTpacket;
+
+  /* That's it for the easy cases.  For the other two, we have work to do.
+     Start by making a copy of the original T packet.  */
+
+  strcpy (reply_buf, origTpacket);
+  if ((p = strstr (reply_buf, ";08:")) != NULL)
+    {
+#if 0
+      /* Snip off the PC value from the original T packet.  */
+      p[4] = '\0';
+      /* Case #2.  */
+      if (breakpoint_p)
+	{
+	  /* Compose a T packet using instruction_pc + 1.  */
+	  strcat (p, ix86_unsigned_long_to_hex (instruction_pc + 1));
+	}
+      else
+	{
+	  /* Compose a T packet using instruction_pc.  */
+	  strcat (p, ix86_unsigned_long_to_hex (instruction_pc));
+	}
+#else
+      /* Insert the new PC value in place 
+	 (without disturbing whatever follows it).  */
+
+      /* Case #2.  */
+      if (breakpoint_p)
+	{
+	  /* Compose a T packet using instruction_pc + 1.  */
+	  memcpy (p + 4, 
+		  ix86_unsigned_long_to_hex (instruction_pc + 1),
+		  8);
+	}
+      else
+	{
+	  /* Compose a T packet using instruction_pc.  */
+	  memcpy (p + 4, 
+		  ix86_unsigned_long_to_hex (instruction_pc),
+		  8);
+	}
+#endif
+      /* Caller has to recompute checksum.  */
+      return reply_buf;
+    }
+  /* Bail... */
+  return origTpacket;
+}
+
+/*
+ * target_compose_g_packet
+ *
+ * Take the registers from the 'T' packet, and compose them into a 
+ * 'g' packet response.  Registers for which we have no values will
+ * be filled in with 'xxxx', in the manner of tracepoints.
+ *
+ * Returns: string, g packet reply.
+ */
+
+#define IX86_TARGET_GBYTES 624
+static char gbuffer[IX86_TARGET_GBYTES + 1];
+#define IX86_BOGUS_NUMREGS (IX86_TARGET_GBYTES / 8)
+
+static unsigned long regval[IX86_BOGUS_NUMREGS];
+static int gotreg[IX86_BOGUS_NUMREGS];
+
+char *
+target_compose_g_packet (char *tpac)
+{
+  int i;
+  int regnum;
+  int signum;
+
+  /* See which regs we can get from the T packet.  Assume none... */
+  for (i = 0; i < IX86_BOGUS_NUMREGS; i++)
+    gotreg[i] = 0;
+
+  /* OK, scan off the prefix -- $T plus signal number.  */
+  if (*tpac++ == '$' && *tpac++ == 'T')
+    {
+      /* We won't actually use signum.  */
+      signum = (hex_to_int (*tpac++) << 4);
+      signum += hex_to_int (*tpac++);
+      while (*tpac)
+	{
+	  regnum = (hex_to_int (*tpac++) << 4);
+	  regnum += hex_to_int (*tpac++);
+	  if (*tpac++ == ':')
+	    {
+	      gotreg[regnum] = 1;
+	      regval[regnum] = ix86_hex_to_unsigned_long (expand_rle (tpac));
+	      tpac = strchr (tpac, ';');
+	    }
+	  else goto gpacket_fail;
+
+	  if (tpac && *tpac == ';')
+	    tpac++;
+	  else goto gpacket_fail;
+
+	  if (strncmp (tpac, "thread", 6) == 0)
+	    break;
+	}
+
+      /* Got values, now get to composin'.  */
+      strcpy (gbuffer, "$");
+      for (i = 0; i < IX86_BOGUS_NUMREGS; i++)
+	if (gotreg[i])
+	  strcat (gbuffer, ix86_unsigned_long_to_hex (regval[i]));
+	else
+	  strcat (gbuffer, "xxxxxxxx");
+
+      /* Return composed g packet reply.
+	 Caller has responsibility of appending checksum.  */
+      return gbuffer;
+    }
+ gpacket_fail:
+  /* Fail.  */
+  return NULL;
+}
Index: gdbfreeplay-mips64.c
===================================================================
RCS file: gdbfreeplay-mips64.c
diff -N gdbfreeplay-mips64.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdbfreeplay-mips64.c	11 Sep 2008 00:09:24 -0000
@@ -0,0 +1,115 @@
+/*
+ * gdbfreeplay-mips64.c
+ *
+ * Target-dependent component of gdbfreeplay for mips64.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gdbfreeplay.h"
+
+/*
+ * target_pc_from_T
+ *
+ * Extract the PC value from the gdb protocol 'T' packet.
+ * Returns PC as host unsigned long long.
+ */
+
+unsigned long long
+target_pc_from_T (char *tpacket)
+{
+  /* Unimplimented -- make caller fall back to using g packet.  */
+  return (unsigned long long) -1;
+}
+
+/*
+ * target_pc_from_G
+ *
+ * Extract the PC value from the gdb protocol 'G' packet.
+ * Returns PC as host unsigned long long.
+ */
+
+unsigned long long
+target_pc_from_G (char *gpacket)
+{
+  char localbuf [24];
+
+  if (gpacket[0] == '$' && gpacket[1] == 'G')
+    {
+      strncpy (localbuf, gpacket + 592, 16);
+      localbuf[16] = '\0';
+      return strtoul (localbuf, NULL, 16);
+    }
+
+  /* Fail -- just assume no legitimate PC will ever be -1...  */
+  return (unsigned long long) -1;
+}
+
+/*
+ * target_pc_from_g
+ *
+ * Extract the PC value from the gdb protocol 'g' packet reply.
+ * 
+ * Unlike the two above, this function accepts a FILE pointer
+ * rather than a char pointer, and must read data from the file.
+ *
+ * Returns PC as host unsigned long long.
+ */
+
+unsigned long long
+target_pc_from_g (char *gpacket)
+{
+  char localbuf [24];
+
+  if (gpacket[0] == 'r' && gpacket[1] == ' ')
+    {
+      gpacket += 2;
+      if (gpacket[0] == '+')
+	gpacket++;
+      if (gpacket[0] == '$')
+	gpacket++;
+    }
+
+  strncpy (localbuf, gpacket + 592, 16);
+  localbuf[16] = '\0';
+  return strtoull (localbuf, NULL, 16);
+}
+
+
+
+/*
+ * target_compose_T_packet
+ *
+ * On targets where DECR_PC_AFTER_BREAK is zero, this is a no-op.
+ * We just send back the T packet that was sent to us.
+ *
+ */
+
+char *
+target_compose_T_packet (char *origTpacket, 
+			 unsigned long long instruction_pc,
+			 int breakpoint_p)
+{
+  return origTpacket;
+}
+
+
+
+/*
+ * target_compose_g_packet
+ *
+ * Take the registers from the 'T' packet, and compose them into a 
+ * 'g' packet response.  Registers for which we have no values will
+ * be filled in with 'xxxx', in the manner of tracepoints.
+ *
+ * Returns: string, g packet reply.
+ */
+
+char *
+target_compose_g_packet (char *tpac)
+{
+  /* stub */
+  return NULL;
+}
Index: gdbfreeplay.h
===================================================================
RCS file: gdbfreeplay.h
diff -N gdbfreeplay.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdbfreeplay.h	11 Sep 2008 00:09:24 -0000
@@ -0,0 +1,26 @@
+/*
+ * gdbfreeplay -- interface
+ */
+
+#ifndef GDBFREEPLAY_H
+#define GDBFREEPLAY_H
+
+#include "remote-breakpoint.h"
+
+extern int verbose;
+
+extern enum successcode gdbfreeplay_open (char *filename);
+extern void gdbfreeplay (int socket_fd);
+
+extern unsigned long long target_pc_from_T (char *tpacket);
+extern unsigned long long target_pc_from_G (char *gpacket);
+extern unsigned long long target_pc_from_g (char *gpacket);
+
+extern char *target_compose_T_packet (char *origTpacket, 
+				      unsigned long long pc,
+				      int breakpoint_p);
+extern char *target_compose_g_packet (char *origTpacket);
+
+extern int hex_to_int (int ch);
+
+#endif
Index: remote-breakpoint.c
===================================================================
RCS file: remote-breakpoint.c
diff -N remote-breakpoint.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ remote-breakpoint.c	11 Sep 2008 00:09:24 -0000
@@ -0,0 +1,159 @@
+/*
+ * remote-breakpoint.c
+ *
+ * A breakpoint list for a remote gdb target.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "remote-breakpoint.h"
+
+static breakpoint *bplist[5];
+
+extern int verbose;
+
+/*
+ * insert_breakpoint
+ *
+ * returns: FAIL/PASS
+ */
+
+static enum successcode
+insert_breakpoint (enum breakpoint_type bptype,
+		   unsigned long long   addr,
+		   unsigned long        len)
+{
+  breakpoint *this_bp;
+
+  switch (bptype) {
+  case ACCESS_BP:
+  case HARDWARE_BP:
+  case READ_BP:
+  case WRITE_BP:
+  default:
+    /* Can't do those.  */
+    return FAIL;
+    break;
+  case SOFTWARE_BP:
+    this_bp = malloc (sizeof (breakpoint));
+    this_bp->addr = addr;
+    this_bp->len  = len;
+    this_bp->next = bplist[bptype];
+    bplist[bptype] = this_bp;
+    return PASS;
+  }
+}
+			 
+/*
+ * unlink_breakpoint
+ *
+ * returns: 0 for fail, 1 for success
+ */
+
+static int
+unlink_breakpoint (enum breakpoint_type bptype,
+		   unsigned long long   addr,
+		   unsigned long        len)
+{
+  breakpoint *this_bp, *tmp;
+
+  switch (bptype) {
+  case ACCESS_BP:
+  case HARDWARE_BP:
+  case READ_BP:
+  case WRITE_BP:
+  default:
+    /* Can't do those.  */
+    return FAIL;
+    break;
+  case SOFTWARE_BP:
+    /* Special case - list is empty.  */
+    if (bplist[bptype] == NULL)
+      return FAIL;
+
+    /* Start from list head.  */
+    this_bp = bplist[bptype];
+    /* Special case -- remove head of list.  */
+    if (this_bp->addr == addr &&
+	this_bp->len  == len)
+      {
+	bplist[bptype] = this_bp->next;
+	return PASS;
+      }
+
+    /* Scan list.  */
+    for (; this_bp && this_bp->next; this_bp = this_bp->next)
+      if (this_bp->next->addr == addr &&
+	  this_bp->next->len  == len)
+	{
+	  /* Remove from middle of list.  */
+	  tmp = this_bp->next->next;
+	  free (this_bp->next);
+	  this_bp->next = tmp;
+	  return PASS;
+	}
+
+    /* Not found.  */
+    return FAIL;
+  }    
+}
+			 
+
+extern enum successcode
+remote_remove_breakpoint (enum breakpoint_type bptype,
+			  unsigned long long   addr,
+			  unsigned long        len)
+{
+  if (verbose)
+    fprintf (stdout, "remote-breakpoint: Remove sw breakpoint type %d\n", 
+	     bptype);
+  if (unlink_breakpoint (bptype, addr, len) == 0)
+    {
+      fprintf (stderr, "       FAILED!\n");
+      return FAIL;
+    }
+  return PASS;
+}
+
+extern enum successcode
+remote_set_breakpoint (enum breakpoint_type bptype,
+		       unsigned long long   addr,
+		       unsigned long        len)
+{
+  if (verbose)
+    fprintf (stdout, "remote-breakpoint: Set sw breakpoint type %d\n", 
+	     bptype);
+  if (insert_breakpoint (bptype, addr, len) == 0)
+    {
+      fprintf (stderr, "       FAILED!\n");
+      return FAIL;
+    }
+  return PASS;
+}
+
+/*
+ * remote_breakpoint_here_p
+ *
+ * Scan the list of breakpoints of type BPTYPE.
+ * Return PASS if there is one that matches ADDR, else FAIL.
+ *
+ * FIXME: do I need to consider the length?
+ */
+
+enum successcode 
+remote_breakpoint_here_p (enum breakpoint_type bptype,
+			  unsigned long long addr)
+{
+  breakpoint *bp = bplist[bptype];
+
+  while (bp != NULL)
+    {
+      if (bp->addr == addr)
+	return PASS;
+      bp = bp->next;
+    }
+  return FAIL;
+}
Index: remote-breakpoint.h
===================================================================
RCS file: remote-breakpoint.h
diff -N remote-breakpoint.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ remote-breakpoint.h	11 Sep 2008 00:09:24 -0000
@@ -0,0 +1,45 @@
+/*
+ * remote-breakpoint -- interface
+ */
+
+#ifndef REMOTE_BREAKPOINT_H
+#define REMOTE_BREAKPOINT_H
+
+typedef struct BREAKPOINT {
+  unsigned long long addr;
+  unsigned long len;
+  struct BREAKPOINT *next;
+} breakpoint;
+
+enum breakpoint_type {
+  SOFTWARE_BP,
+  HARDWARE_BP,
+  WRITE_BP,
+  READ_BP,
+  ACCESS_BP
+};
+
+enum successcode {
+  FAIL = 0,
+  PASS = 1
+};
+
+enum direction_code {
+  DIR_FORWARD = 0,
+  DIR_BACKWARD,
+  PLAY_O_PACKETS
+};
+
+extern enum successcode remote_remove_breakpoint (enum breakpoint_type,
+						  unsigned long long,
+						  unsigned long);
+
+extern enum successcode remote_set_breakpoint    (enum breakpoint_type,
+						  unsigned long long,
+						  unsigned long);
+
+extern enum successcode remote_breakpoint_here_p (enum breakpoint_type,
+						  unsigned long long);
+
+#endif
+

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