* 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:
parent
76e45938c3
commit
e8f1ad9a8b
8 changed files with 551 additions and 45 deletions
|
@ -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.
|
||||
|
|
10
gdb/NEWS
10
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
|
||||
|
|
319
gdb/gdbserver/gdbreplay.c
Normal file
319
gdb/gdbserver/gdbreplay.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
134
gdb/serial.c
134
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);
|
||||
|
||||
}
|
||||
|
|
15
gdb/serial.h
15
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 */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue