aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/gdbserver/ChangeLog13
-rw-r--r--gdb/gdbserver/Makefile.in14
-rwxr-xr-xgdb/gdbserver/configure5
-rw-r--r--gdb/gdbserver/configure.ac2
-rw-r--r--gdb/gdbserver/configure.srv4
-rw-r--r--gdb/gdbserver/gdbfreeplay-back.c934
-rw-r--r--gdb/gdbserver/gdbfreeplay-front.c311
-rw-r--r--gdb/gdbserver/gdbfreeplay-i386.c334
-rw-r--r--gdb/gdbserver/gdbfreeplay.h21
-rw-r--r--gdb/gdbserver/remote-breakpoint.c159
-rw-r--r--gdb/gdbserver/remote-breakpoint.h44
11 files changed, 1839 insertions, 2 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index c9ef3ab..deaf51e 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,16 @@
+2008-06-21 Michael Snyder <msnyder@specifix.com>
+
+ * gdbfreeplay-front.c: New file. Extended gdbreplay.
+ * gdbfreeplay-back.c: New file.
+ * gdbfreeplay-i386.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.
+
2008-06-06 Daniel Jacobowitz <dan@codesourcery.com>
* Makefile.in (gdbreplay.o): New rule.
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 7034361..fdf40a1 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -136,6 +136,8 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/inferiors.c \
DEPFILES = @GDBSERVER_DEPFILES@
+FREEPLAY_DEPFILES = @GDBFREEPLAY_DEPFILES@
+
LIBOBJS = @LIBOBJS@
SOURCES = $(SFILES)
@@ -199,6 +201,13 @@ gdbreplay$(EXEEXT): gdbreplay.o version.o
${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) -o gdbreplay$(EXEEXT) $^ \
$(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
@@ -217,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
@@ -289,6 +299,8 @@ 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
signals.o: ../signals/signals.c $(server_h)
$(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER
diff --git a/gdb/gdbserver/configure b/gdb/gdbserver/configure
index 00df01d..2cbd4ce 100755
--- a/gdb/gdbserver/configure
+++ b/gdb/gdbserver/configure
@@ -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=''
# Initialize some variables set by options.
@@ -4491,6 +4491,8 @@ fi
GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_hostio_err_objs $srv_thread_depfiles"
GDBSERVER_LIBS="$srv_libs"
+GDBFREEPLAY_DEPFILES="$freeplay_tgtobj"
+
@@ -5150,6 +5152,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
diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac
index 7cfbaf6..4eb6503 100644
--- a/gdb/gdbserver/configure.ac
+++ b/gdb/gdbserver/configure.ac
@@ -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
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index 74b6b95..3e543fb 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -10,6 +10,7 @@
# target method.
# srv_xmlfiles All XML files which should be available for
# gdbserver in this configuration.
+# freeplay_tgtobj Target module for gdb-freeplay.
#
# In addition, on GNU/Linux the following shell variables will be set:
# srv_linux_regsets Set to "yes" if ptrace(PTRACE_GETREGS) and friends
@@ -54,16 +55,19 @@ case "${target}" in
;;
i[34567]86-*-cygwin*) srv_regobj=reg-i386.o
srv_tgtobj="win32-low.o win32-i386-low.o"
+ freeplay_tgtobj="gdbfreeplay-i386.o"
;;
i[34567]86-*-linux*) srv_regobj=reg-i386-linux.o
srv_tgtobj="linux-low.o linux-i386-low.o i387-fp.o"
srv_linux_usrregs=yes
srv_linux_regsets=yes
srv_linux_thread_db=yes
+ freeplay_tgtobj="gdbfreeplay-i386.o"
;;
i[34567]86-*-mingw*) srv_regobj=reg-i386.o
srv_tgtobj="win32-low.o win32-i386-low.o"
srv_mingw=yes
+ freeplay_tgtobj="gdbfreeplay-i386.o"
;;
ia64-*-linux*) srv_regobj=reg-ia64.o
srv_tgtobj="linux-low.o linux-ia64-low.o"
diff --git a/gdb/gdbserver/gdbfreeplay-back.c b/gdb/gdbserver/gdbfreeplay-back.c
new file mode 100644
index 0000000..b7535db
--- /dev/null
+++ b/gdb/gdbserver/gdbfreeplay-back.c
@@ -0,0 +1,934 @@
+/*
+ * gdbfreeplay-back.c
+ *
+ * Backend for gdbfreeplay.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+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 pc;
+ /* 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 predecr_pc;
+ unsigned long eventpos;
+ unsigned long gpos;
+} 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;
+ int next_id = 0;
+ unsigned long nextpos;
+ unsigned 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)
+ {
+ /* Record position of 'g' packet (next line). */
+ stopframe[last_cached_frame].gpos = ftell (infile);
+
+ /* 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) -1)
+ stopframe[last_cached_frame].pc = target_pc_from_g (infile);
+
+ if (verbose)
+ fprintf (stdout, "Found 'g' packet at %u\n",
+ stopframe[last_cached_frame].gpos);
+ }
+
+ /* 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) -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)
+ {
+ /* 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;
+ }
+ }
+
+ /* Stop event message? */
+ else if ((p = strstr (line, "$T")) != NULL ||
+ (p = strstr (line, "$S")) != NULL)
+ {
+ 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;
+
+ if (p[1] == 'T')
+ stopframe[last_cached_frame].pc = target_pc_from_T (p);
+
+ if (verbose)
+ fprintf (stdout, "Record event pos at %u\n",
+ stopframe [last_cached_frame].eventpos);
+ }
+ 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? */
+ 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 (curpos < stopframe[cur_frame + 1].eventpos)
+ {
+ line = fgets (inbuf, sizeof (inbuf), logfile);
+ /* End of input? */
+ if (line == NULL)
+ 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;
+}
+
+#if 0
+/*
+ * tohex
+ *
+ * Borrowed from gdbreplay.
+ */
+
+static int
+tohex (int ch)
+{
+ if (ch >= '0' && ch <= '9')
+ {
+ return (ch - '0');
+ }
+ if (ch >= 'A' && ch <= 'F')
+ {
+ return (ch - 'A' + 10);
+ }
+ if (ch >= 'a' && ch <= 'f')
+ {
+ return (ch - 'a' + 10);
+ }
+ fprintf (stderr, "\nInvalid hex digit '%c'\n", ch);
+ fflush (stderr);
+ exit (1);
+}
+#endif
+
+#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 len, 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]) << 8 + hex_to_int (p[3]);
+ return sig;
+ }
+ return 0;
+}
+
+/*
+ * 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 start, enum direction_code direction)
+{
+ int i;
+ int signum;
+
+ /* 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;
+ }
+ }
+ /* 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;
+
+ /* 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;
+}
+
+/*
+ * 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 addr;
+ unsigned long len;
+ int next_event_frame;
+ char *p;
+
+ /* 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, "$qRcmd,766572626f7365206f6e#d6") != NULL)
+ {
+ verbose = 1;
+ return OK;
+ }
+
+ /* Handle "monitor verbose off". */
+ if (strstr (request, "$qRcmd,766572626f7365206f6666#13") != NULL)
+ {
+ verbose = 0;
+ return OK;
+ }
+
+ /* 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) by searching the cache for a stop event. */
+ if (strstr (request, "$c#63") != NULL)
+ {
+ next_event_frame = freeplay_find_event (infile, cur_frame, DIR_FORWARD);
+ if (next_event_frame != -1)
+ {
+ /* Got a stop event. Make it the current frame, and tell gdb.
+ */
+ cur_frame = next_event_frame;
+#if 0
+ /* FIXME: we want to do something about DECR_PC_AFTER_BREAK. */
+ return find_reply (infile,
+ stopframe[cur_frame].eventpos);
+#endif
+ }
+ else
+ {
+ /* FIXME now what? Go to final frame? */
+ cur_frame = last_cached_frame - 1;
+#if 0
+ /* FIXME: we want to do something about DECR_PC_AFTER_BREAK. */
+ return find_reply (infile,
+ stopframe[cur_frame].eventpos);
+#endif
+ }
+#if 1
+ /* 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 re-compose it
+ (possibly allowing for DECR_PC_AFTER_BREAK). */
+ if ((p = strstr (inbuf, "$T")) != NULL)
+ return add_checksum (target_compose_T_packet (p));
+ /* If it's a "$S", just return it (FIXME?) */
+ else
+ return &inbuf[0];
+#endif
+ }
+
+ /* 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, cur_frame, DIR_BACKWARD);
+ if (next_event_frame != -1)
+ {
+ /* Got a stop event. Make it the current frame, and tell gdb.
+ */
+ cur_frame = next_event_frame;
+#if 0
+ /* FIXME: we want to do something about DECR_PC_AFTER_BREAK. */
+ return find_reply (infile,
+ stopframe[cur_frame].eventpos);
+#endif
+ }
+ else
+ {
+ /* FIXME now what? Go to initial frame? */
+ cur_frame = 0;
+#if 0
+ /* FIXME: we want to do something about DECR_PC_AFTER_BREAK. */
+ return find_reply (infile,
+ stopframe[cur_frame].eventpos);
+#endif
+ }
+#if 1
+ /* 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 re-compose it
+ (possibly allowing for DECR_PC_AFTER_BREAK). */
+ if ((p = strstr (inbuf, "$T")) != NULL)
+ return add_checksum (target_compose_T_packet (p));
+ /* If it's a "$S", just return it (FIXME?) */
+ else
+ return &inbuf[0];
+#endif
+ }
+
+ /* Handle Z0 set breakpoint. */
+ if ((p = strstr (request, "$Z0")) != NULL)
+ {
+ if (p[3] == ',')
+ {
+ addr = strtoul (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 = strtoul (p + 4, &p, 16);
+ if (p[0] == ',')
+ {
+ len = strtoul (p + 1, NULL, 16);
+ remote_remove_breakpoint (SOFTWARE_BP, addr, len);
+ return OK;
+ }
+ }
+ }
+
+ /* Handle "vCont" by saying we can't do it.
+ FIXME we could do it, and moreover 'fallbacks' could handle this. */
+ if (strstr (request, "$vCont") != 0)
+ return EMPTY;
+
+ /* TODO: vCont, M, X, G, P/p, tfind... */
+
+ /* 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)
+{
+ /* 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. */
+ }
+
+ /* 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:
+ -- kill $k#6b
+ -- detach $D#44
+ -- step $s#73
+ -- backstep
+ -- continue $c#63
+ -- back-cont
+ -- breakpoint $Z0...
+ -- vCont
+ -- M
+ -- X
+ -- G
+ -- P/p
+ -- 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... */
+ }
+ }
+}
diff --git a/gdb/gdbserver/gdbfreeplay-front.c b/gdb/gdbserver/gdbfreeplay-front.c
new file mode 100644
index 0000000..fa8ccee
--- /dev/null
+++ b/gdb/gdbserver/gdbfreeplay-front.c
@@ -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);
+}
+
diff --git a/gdb/gdbserver/gdbfreeplay-i386.c b/gdb/gdbserver/gdbfreeplay-i386.c
new file mode 100644
index 0000000..319064f
--- /dev/null
+++ b/gdb/gdbserver/gdbfreeplay-i386.c
@@ -0,0 +1,334 @@
+/*
+ * 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.
+ */
+
+unsigned long
+target_pc_from_T (char *tpacket)
+{
+ char *p;
+
+ if (tpacket[0] == '$' && tpacket[1] == 'T' &&
+ (p = strstr (tpacket, ";08:")) != NULL)
+ {
+ return ix86_hex_to_unsigned_long (p + 4);
+ }
+
+ /* Fail -- just assume no legitimate PC will ever be -1... */
+ return (unsigned long) -1;
+}
+
+/*
+ * ix86_pc_from_registers
+ *
+ * Extract the PC value from a gdb protocol registers file.
+ * Returns PC as host unsigned long.
+ */
+
+static unsigned long
+ix86_pc_from_registers (char *regs)
+{
+ return 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.
+ */
+
+unsigned long
+target_pc_from_G (char *gpacket)
+{
+ if (gpacket[0] == '$' && gpacket[1] == 'G')
+ {
+ return ix86_pc_from_registers (gpacket + 2);
+ }
+
+ /* Fail -- just assume no legitimate PC will ever be -1... */
+ return (unsigned 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.
+ * FIXME I could make it like the others for symmetry...
+ *
+ * Returns PC as host unsigned long.
+ */
+
+#define INBUF_SIZE 1024
+static char inbuf [INBUF_SIZE];
+
+unsigned long
+target_pc_from_g (FILE *infile)
+{
+ char *line = fgets (inbuf, sizeof (inbuf), infile);
+
+ if (line[0] == 'r' && line[1] == ' ')
+ {
+ line += 2;
+ if (line[0] == '+')
+ line++;
+ if (line[0] == '$')
+ line++;
+ }
+
+ return ix86_pc_from_registers (expand_rle (line));
+}
+
+/*
+ * 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 instruction_pc,
+ int breakpoint_p)
+{
+ unsigned 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;
+ }
+}
diff --git a/gdb/gdbserver/gdbfreeplay.h b/gdb/gdbserver/gdbfreeplay.h
new file mode 100644
index 0000000..b6cbe66
--- /dev/null
+++ b/gdb/gdbserver/gdbfreeplay.h
@@ -0,0 +1,21 @@
+/*
+ * 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 target_pc_from_T (char *tpacket);
+extern unsigned long target_pc_from_G (char *gpacket);
+extern unsigned long target_pc_from_g (FILE *infile);
+
+extern int hex_to_int (int ch);
+
+#endif
diff --git a/gdb/gdbserver/remote-breakpoint.c b/gdb/gdbserver/remote-breakpoint.c
new file mode 100644
index 0000000..5308932
--- /dev/null
+++ b/gdb/gdbserver/remote-breakpoint.c
@@ -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 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 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 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 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 addr)
+{
+ breakpoint *bp = bplist[bptype];
+
+ while (bp != NULL)
+ {
+ if (bp->addr == addr)
+ return PASS;
+ bp = bp->next;
+ }
+ return FAIL;
+}
diff --git a/gdb/gdbserver/remote-breakpoint.h b/gdb/gdbserver/remote-breakpoint.h
new file mode 100644
index 0000000..1ee1b5c
--- /dev/null
+++ b/gdb/gdbserver/remote-breakpoint.h
@@ -0,0 +1,44 @@
+/*
+ * remote-breakpoint -- interface
+ */
+
+#ifndef REMOTE_BREAKPOINT_H
+#define REMOTE_BREAKPOINT_H
+
+typedef struct BREAKPOINT {
+ unsigned 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
+};
+
+extern enum successcode remote_remove_breakpoint (enum breakpoint_type,
+ unsigned long,
+ unsigned long);
+
+extern enum successcode remote_set_breakpoint (enum breakpoint_type,
+ unsigned long,
+ unsigned long);
+
+extern enum successcode remote_breakpoint_here_p (enum breakpoint_type,
+ unsigned long);
+
+#endif
+