aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog32
-rw-r--r--gdb/NEWS10
-rw-r--r--gdb/gdbserver/gdbreplay.c319
-rw-r--r--gdb/gdbserver/low-linux.c1
-rw-r--r--gdb/remote-array.c81
-rw-r--r--gdb/serial.c134
-rw-r--r--gdb/serial.h15
-rw-r--r--gdb/top.c4
8 files changed, 551 insertions, 45 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 69faad2..1cbdf9b 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,35 @@
+Wed Jan 24 13:19:10 1996 Fred Fish <fnf@cygnus.com>
+
+ * NEWS: Make note of new record and replay feature for
+ remote debug sessions.
+ * serial.c (gdbcmd.h): Include.
+ (serial_logfile, serial_logfp, serial_reading, serial_writing):
+ Define here, for remote debug session logging.
+ (serial_log_command, serial_logchar, serial_write, serial_readchar):
+ New functions for remote debug session logging.
+ (serial_open): Open remote debug session log file when needed.
+ (serial_close): Close remote debug session log file when needed.
+ (_initialize_serial): Add set/show commands for name of remote
+ debug session log file.
+ * serial.h (serial_readchar): Declare
+ (SERIAL_READCHAR): Call serial_readchar().
+ (SERIAL_WRITE): Call serial_write().
+ (serial_close): Declare as extern.
+ (serial_logfile, serial_logfp): Declare.
+ * top.c (execute_command): Declare serial_logfp. Log user command
+ in remote debug session log if log file is open.
+ * remote-array.c (array_wait): #ifdef out echo to gdb_stdout.
+ (array_read_inferior_memory): Rewrite to fix memory overwrite bug.
+ * remote-array.c (SREC_SIZE): Remove, duplicates define in
+ monitor.h.
+ * remote-array.c (hexchars, hex2mem): Remove, unused.
+ * gdbserver/low-linux.c (store_inferior_registers): Remove
+ unnecessary extern declaration of registers[].
+ * gdbserver/Makefile.in (all): Add gdbreplay.
+ * gdbserver/gdbreplay.c: New file.
+ * gdbserver/README: Give example of recording a remote
+ debug session with gdb and then replaying it with gdbreplay.
+
Tue Jan 23 18:02:35 1996 Per Bothner <bothner@kalessin.cygnus.com>
* stabsread.c (rs6000_builtin_type): Make bool type unsigned.
diff --git a/gdb/NEWS b/gdb/NEWS
index 0867bde..813b60a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -32,6 +32,16 @@ Note this feature does not work on hpux8. On hpux9 you must link
/usr/lib/end.o into your program. This feature should work automatically
on hpux10.
+* Recording and replaying remote debug sessions
+
+If you set "remotelogfile" gdb will use that filename to make a
+"recording" of a remote debug session which can be replayed back to
+gdb using "gdbreplay". See gdbserver/README for details. This is
+useful when you have a problem with gdb while doing remote debugging.
+By making a recording of the session and sending it to the gdb
+maintainers, it is possible to recreate your problem without access to
+the remote hardware you are using.
+
*** Changes in GDB-4.15:
* Psymtabs for XCOFF
diff --git a/gdb/gdbserver/gdbreplay.c b/gdb/gdbserver/gdbreplay.c
new file mode 100644
index 0000000..4d5795a
--- /dev/null
+++ b/gdb/gdbserver/gdbreplay.c
@@ -0,0 +1,319 @@
+/* Replay a remote debug session logfile for GDB.
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Fred Fish (fnf@cygnus.com) from pieces of gdbserver.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <sgtty.h>
+#include <sys/file.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+#include <signal.h>
+#include <ctype.h>
+
+/* Sort of a hack... */
+#define EOL (EOF - 1)
+
+static int remote_desc;
+
+/* 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. */
+
+void
+perror_with_name (string)
+ char *string;
+{
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ extern int errno;
+ char *err;
+ char *combined;
+
+ err = (errno < sys_nerr) ? sys_errlist[errno] : "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);
+}
+
+static void
+sync_error (fp, desc, expect, got)
+ FILE *fp;
+ char *desc;
+ int expect;
+ int got;
+{
+ fprintf (stderr, "\n%s\n", desc);
+ fprintf (stderr, "At logfile offset %ld, expected '0x%x' got '0x%x'\n",
+ ftell (fp), expect, got);
+ fflush (stderr);
+ exit (1);
+}
+
+void
+remote_close()
+{
+ close (remote_desc);
+}
+
+/* Open a connection to a remote debugger.
+ NAME is the filename used for communication. */
+
+void
+remote_open (name)
+ char *name;
+{
+ struct sgttyb sg;
+ extern char *strchr ();
+
+ if (!strchr (name, ':'))
+ {
+ fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
+ fflush (stderr);
+ exit (1);
+ }
+ else
+ {
+ char *port_str;
+ int port;
+ struct sockaddr_in sockaddr;
+ int tmp;
+ struct protoent *protoent;
+ int tmp_desc;
+
+ port_str = strchr (name, ':');
+
+ port = atoi (port_str + 1);
+
+ 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");
+
+ protoent = getprotobyname ("tcp");
+ if (!protoent)
+ perror_with_name ("getprotobyname");
+
+ /* 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, protoent->p_proto, TCP_NODELAY,
+ (char *)&tmp, sizeof(tmp));
+
+ 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. */
+ }
+
+ fcntl (remote_desc, F_SETFL, FASYNC);
+
+ fprintf (stderr, "Replay logfile using %s\n", name);
+ fflush (stderr);
+}
+
+static int tohex (ch)
+ 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);
+}
+
+static int
+logchar (fp)
+ FILE *fp;
+{
+ int ch;
+ int ch2;
+
+ ch = fgetc (fp);
+ fputc (ch, stdout);
+ fflush (stdout);
+ switch (ch)
+ {
+ case '\n':
+ ch = EOL;
+ break;
+ case '\\':
+ ch = fgetc (fp);
+ fputc (ch, stdout);
+ fflush (stdout);
+ 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 = fgetc (fp);
+ fputc (ch2, stdout);
+ fflush (stdout);
+ ch = tohex (ch2) << 4;
+ ch2 = fgetc (fp);
+ fputc (ch2, stdout);
+ fflush (stdout);
+ ch |= tohex (ch2);
+ break;
+ default:
+ /* Treat any other char as just itself */
+ break;
+ }
+ default:
+ break;
+ }
+ return (ch);
+}
+
+/* Accept input from gdb and match with chars from fp (after skipping one
+ blank) up until a \n is read from fp (which is not matched) */
+
+void
+expect (fp)
+ FILE *fp;
+{
+ int fromlog;
+ unsigned char fromgdb;
+
+ if ((fromlog = logchar (fp)) != ' ')
+ {
+ sync_error (fp, "Sync error during gdb read of leading blank", ' ',
+ fromlog);
+ }
+ do
+ {
+ fromlog = logchar (fp);
+ if (fromlog == EOL)
+ {
+ break;
+ }
+ read (remote_desc, &fromgdb, 1);
+ } while (fromlog == fromgdb);
+ if (fromlog != EOL)
+ {
+ sync_error (fp, "Sync error during read of gdb packet", fromlog,
+ fromgdb);
+ }
+}
+
+/* Play data back to gdb from fp (after skipping leading blank) up until a
+ \n is read from fp (which is discarded and not sent to gdb). */
+
+void
+play (fp)
+ FILE *fp;
+{
+ int fromlog;
+ char ch;
+
+ if ((fromlog = logchar (fp)) != ' ')
+ {
+ sync_error (fp, "Sync error skipping blank during write to gdb", ' ',
+ fromlog);
+ }
+ while ((fromlog = logchar (fp)) != EOL)
+ {
+ ch = fromlog;
+ write (remote_desc, &ch, 1);
+ }
+}
+
+int
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ FILE *fp;
+ int ch;
+
+ if (argc < 3)
+ {
+ fprintf (stderr, "Usage: gdbreplay <logfile> <host:port>\n");
+ fflush (stderr);
+ exit (1);
+ }
+ fp = fopen (argv[1], "r");
+ if (fp == NULL)
+ {
+ perror_with_name (argv[1]);
+ }
+ remote_open (argv[2]);
+ while ((ch = logchar (fp)) != EOF)
+ {
+ switch (ch)
+ {
+ case 'w':
+ /* data sent from gdb to gdbreplay, accept and match it */
+ expect (fp);
+ break;
+ case 'r':
+ /* data sent from gdbreplay to gdb, play it */
+ play (fp);
+ break;
+ case 'c':
+ /* Command executed by gdb */
+ while ((ch = logchar (fp)) != EOL);
+ break;
+ }
+ }
+ remote_close ();
+ exit (0);
+}
+
diff --git a/gdb/gdbserver/low-linux.c b/gdb/gdbserver/low-linux.c
index 046a9be..34dc643 100644
--- a/gdb/gdbserver/low-linux.c
+++ b/gdb/gdbserver/low-linux.c
@@ -273,7 +273,6 @@ store_inferior_registers (regno)
{
register unsigned int regaddr;
char buf[80];
- extern char registers[];
register int i;
unsigned int offset = U_REGS_OFFSET;
int scratch;
diff --git a/gdb/remote-array.c b/gdb/remote-array.c
index 0b36af1..0ed84ca 100644
--- a/gdb/remote-array.c
+++ b/gdb/remote-array.c
@@ -43,10 +43,6 @@
extern int baud_rate;
-static const char hexchars[]="0123456789abcdef";
-static char *hex2mem();
-
-#define SREC_SIZE 160
#define ARRAY_PROMPT ">> "
#define SWAP_TARGET_AND_HOST(buffer,len) \
@@ -758,8 +754,10 @@ array_wait (pid, status)
/* do this so it looks like there's keyboard echo */
if (c == 3) /* exit on Control-C */
break;
+#if 0
fputc_unfiltered (c, gdb_stdout);
fflush (stdout);
+#endif
}
}
SERIAL_SET_TTY_STATE (tty_desc, ttystate);
@@ -947,21 +945,12 @@ array_read_inferior_memory(memaddr, myaddr, len)
char *myaddr;
int len;
{
- int i, j;
+ int j;
char buf[20];
char packet[PBUFSIZ];
-
- /* Number of bytes read so far. */
- int count;
-
- /* Starting address of this pass. */
- unsigned long startaddr;
-
- /* Starting address of this pass. */
- unsigned long endaddr;
-
- /* Number of bytes to read in this pass. */
- int len_this_pass;
+ int count; /* Number of bytes read so far. */
+ unsigned long startaddr; /* Starting address of this pass. */
+ int len_this_pass; /* Number of bytes to read in this pass. */
debuglogs (1, "array_read_inferior_memory (memaddr=0x%x, myaddr=0x%x, len=%d)", memaddr, myaddr, len);
@@ -979,36 +968,46 @@ array_read_inferior_memory(memaddr, myaddr, len)
return 0;
}
- startaddr = memaddr;
- count = 0;
- while (count < len) {
- len_this_pass = 16;
- if ((startaddr % 16) != 0)
- len_this_pass -= startaddr % 16;
- if (len_this_pass > (len - count))
- len_this_pass = (len - count);
-
- debuglogs (3, "Display %d bytes at %x for Big Endian host", len_this_pass, startaddr);
-
- for (i = 0; i < len_this_pass; i++) {
+ for (count = 0, startaddr = memaddr; count < len; startaddr += len_this_pass)
+ {
+ /* Try to align to 16 byte boundry (why?) */
+ len_this_pass = 16;
+ if ((startaddr % 16) != 0)
+ {
+ len_this_pass -= startaddr % 16;
+ }
+ /* Only transfer bytes we need */
+ if (len_this_pass > (len - count))
+ {
+ len_this_pass = (len - count);
+ }
+ /* Fetch the bytes */
+ debuglogs (3, "read %d bytes from inferior address %x", len_this_pass,
+ startaddr);
sprintf (buf, "m%08x,%04x", startaddr, len_this_pass);
make_gdb_packet (packet, buf);
if (array_send_packet (packet) == 0)
- error ("Couldn't transmit packet\n");
+ {
+ error ("Couldn't transmit packet\n");
+ }
if (array_get_packet (packet) == 0)
- error ("Couldn't receive packet\n");
+ {
+ error ("Couldn't receive packet\n");
+ }
if (*packet == 0)
- error ("Got no data in the GDB packet\n");
- debuglogs (4, "array_read_inferior: Got a \"%s\" back\n", packet);
- for (j = 0; j < len_this_pass ; j++) { /* extract the byte values */
- myaddr[count++] = from_hex (*(packet+(j*2))) * 16 + from_hex (*(packet+(j*2)+1));
- debuglogs (5, "myaddr set to %x\n", myaddr[count-1]);
- }
- startaddr += 1;
+ {
+ error ("Got no data in the GDB packet\n");
+ }
+ /* Pick packet apart and xfer bytes to myaddr */
+ debuglogs (4, "array_read_inferior_memory: Got a \"%s\" back\n", packet);
+ for (j = 0; j < len_this_pass ; j++)
+ {
+ /* extract the byte values */
+ myaddr[count++] = from_hex (*(packet+(j*2))) * 16 + from_hex (*(packet+(j*2)+1));
+ debuglogs (5, "myaddr[%d] set to %x\n", count-1, myaddr[count-1]);
+ }
}
-
- }
- return len;
+ return (count);
}
/* FIXME-someday! merge these two. */
diff --git a/gdb/serial.c b/gdb/serial.c
index 72bfc50..c984d7a 100644
--- a/gdb/serial.c
+++ b/gdb/serial.c
@@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "defs.h"
#include "serial.h"
#include "gdb_string.h"
+#include "gdbcmd.h"
/* Linked list of serial I/O handlers */
@@ -33,6 +34,110 @@ static serial_t last_serial_opened = NULL;
static serial_t scb_base;
+/* Non-NULL gives filename which contains a recording of the remote session,
+ suitable for playback by gdbserver. */
+
+char *serial_logfile = NULL;
+FILE *serial_logfp = NULL;
+
+
+static int serial_reading = 0;
+static int serial_writing = 0;
+
+void
+serial_log_command (cmd)
+ const char *cmd;
+{
+ if (serial_reading || serial_writing)
+ {
+ fputc ('\n', serial_logfp);
+ serial_reading = 0;
+ serial_writing = 0;
+ }
+ fprintf (serial_logfp, "c %s\n", cmd);
+ /* Make sure that the log file is as up-to-date as possible,
+ in case we are getting ready to dump core or something. */
+ fflush (serial_logfp);
+}
+
+static void
+serial_logchar (ch)
+ int ch;
+{
+ switch (ch)
+ {
+ case '\\': fputs ("\\\\", serial_logfp); break;
+ case '\b': fputs ("\\b", serial_logfp); break;
+ case '\f': fputs ("\\f", serial_logfp); break;
+ case '\n': fputs ("\\n", serial_logfp); break;
+ case '\r': fputs ("\\r", serial_logfp); break;
+ case '\t': fputs ("\\t", serial_logfp); break;
+ case '\v': fputs ("\\v", serial_logfp); break;
+ default: fprintf (serial_logfp, isprint (ch) ? "%c" : "\\x%02x", ch & 0xFF); break;
+ }
+}
+
+int
+serial_write (scb, str, len)
+ serial_t scb;
+ const char *str;
+ int len;
+{
+ int count;
+
+ if (serial_logfp != NULL)
+ {
+ if (serial_reading)
+ {
+ fputc ('\n', serial_logfp);
+ serial_reading = 0;
+ }
+ if (!serial_writing)
+ {
+ serial_logchar ('w');
+ serial_logchar (' ');
+ serial_writing = 1;
+ }
+ for (count = 0; count < len; count++)
+ {
+ serial_logchar (str[count]);
+ }
+ /* Make sure that the log file is as up-to-date as possible,
+ in case we are getting ready to dump core or something. */
+ fflush (serial_logfp);
+ }
+ return (scb -> ops -> write (scb, str, len));
+}
+
+int
+serial_readchar (scb, timeout)
+ serial_t scb;
+ int timeout;
+{
+ int ch;
+
+ ch = scb -> ops -> readchar (scb, timeout);
+ if (serial_logfp != NULL)
+ {
+ if (serial_writing)
+ {
+ fputc ('\n', serial_logfp);
+ serial_writing = 0;
+ }
+ if (!serial_reading)
+ {
+ serial_logchar ('r');
+ serial_logchar (' ');
+ serial_reading = 1;
+ }
+ serial_logchar (ch);
+ /* Make sure that the log file is as up-to-date as possible,
+ in case we are getting ready to dump core or something. */
+ fflush (serial_logfp);
+ }
+ return (ch);
+}
+
static struct serial_ops *
serial_interface_lookup (name)
char *name;
@@ -102,6 +207,15 @@ serial_open (name)
last_serial_opened = scb;
+ if (serial_logfile != NULL)
+ {
+ serial_logfp = fopen (serial_logfile, "w");
+ if (serial_logfp == NULL)
+ {
+ perror_with_name (serial_logfile);
+ }
+ }
+
return scb;
}
@@ -152,6 +266,18 @@ serial_close(scb, really_close)
last_serial_opened = NULL;
+ if (serial_logfp)
+ {
+ if (serial_reading || serial_writing)
+ {
+ fputc ('\n', serial_logfp);
+ serial_reading = 0;
+ serial_writing = 0;
+ }
+ fclose (serial_logfp);
+ serial_logfp = NULL;
+ }
+
/* This is bogus. It's not our fault if you pass us a bad scb...! Rob, you
should fix your code instead. */
@@ -346,4 +472,12 @@ _initialize_serial ()
"Connect the terminal directly up to the command monitor.\n\
Use <CR>~. or <CR>~^D to break out.");
#endif /* 0 */
+
+ add_show_from_set (add_set_cmd ("remotelogfile", no_class,
+ var_filename, (char *)&serial_logfile,
+ "Set filename for remote session recording.\n\
+This file is used to record the remote session for future playback\n\
+by gdbserver.", &setlist),
+ &showlist);
+
}
diff --git a/gdb/serial.h b/gdb/serial.h
index 2cf06f3..8abcb3d 100644
--- a/gdb/serial.h
+++ b/gdb/serial.h
@@ -136,7 +136,9 @@ serial_t serial_fdopen PARAMS ((const int fd));
#define SERIAL_TIMEOUT -2
#define SERIAL_EOF -3
-#define SERIAL_READCHAR(SERIAL_T, TIMEOUT) ((SERIAL_T)->ops->readchar((SERIAL_T), TIMEOUT))
+extern int serial_readchar PARAMS ((serial_t scb, int timeout));
+
+#define SERIAL_READCHAR(SERIAL_T, TIMEOUT) serial_readchar (SERIAL_T, TIMEOUT)
/* Set the baudrate to the decimal value supplied. Returns 0 for success,
-1 for failure. */
@@ -155,11 +157,13 @@ serial_t serial_fdopen PARAMS ((const int fd));
/* Write LEN chars from STRING to the port SERIAL_T. Returns 0 for
success, non-zero for failure. */
-#define SERIAL_WRITE(SERIAL_T, STRING, LEN) ((SERIAL_T)->ops->write((SERIAL_T), STRING, LEN))
+extern int serial_write PARAMS ((serial_t scb, const char *str, int len));
+
+#define SERIAL_WRITE(SERIAL_T, STRING,LEN) serial_write (SERIAL_T, STRING, LEN)
/* Push out all buffers, close the device and destroy SERIAL_T. */
-void serial_close PARAMS ((serial_t, int));
+extern void serial_close PARAMS ((serial_t, int));
#define SERIAL_CLOSE(SERIAL_T) serial_close(SERIAL_T, 1)
@@ -170,4 +174,9 @@ void serial_close PARAMS ((serial_t, int));
extern void serial_printf PARAMS ((serial_t desc, const char *, ...))
ATTR_FORMAT(printf, 2, 3);
+/* File in which to record the remote debugging session */
+
+extern char *serial_logfile;
+extern FILE *serial_logfp;
+
#endif /* SERIAL_H */
diff --git a/gdb/top.c b/gdb/top.c
index 675ad80..cf22bce 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -1139,6 +1139,7 @@ execute_command (p, from_tty)
register struct cmd_list_element *c;
register enum language flang;
static int warned = 0;
+ extern FILE *serial_logfp;
free_all_values ();
@@ -1146,6 +1147,9 @@ execute_command (p, from_tty)
if (p == NULL)
return;
+ if (serial_logfp != NULL)
+ serial_log_command (p);
+
while (*p == ' ' || *p == '\t') p++;
if (*p)
{