* 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.
This commit is contained in:
Fred Fish 1996-01-24 21:30:37 +00:00
parent 76e45938c3
commit e8f1ad9a8b
8 changed files with 551 additions and 45 deletions

View File

@ -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.

View File

@ -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

319
gdb/gdbserver/gdbreplay.c Normal file
View File

@ -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);
}

View File

@ -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;

View File

@ -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. */

View File

@ -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);
}

View File

@ -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 */

View File

@ -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)
{