119370Spst/* Replay a remote debug session logfile for GDB. 2130809Smarcel Copyright 1996, 1998, 1999, 2000, 2002, 2003 Free Software Foundation, Inc. 319370Spst Written by Fred Fish (fnf@cygnus.com) from pieces of gdbserver. 419370Spst 598948Sobrien This file is part of GDB. 619370Spst 798948Sobrien This program is free software; you can redistribute it and/or modify 898948Sobrien it under the terms of the GNU General Public License as published by 998948Sobrien the Free Software Foundation; either version 2 of the License, or 1098948Sobrien (at your option) any later version. 1119370Spst 1298948Sobrien This program is distributed in the hope that it will be useful, 1398948Sobrien but WITHOUT ANY WARRANTY; without even the implied warranty of 1498948Sobrien MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1598948Sobrien GNU General Public License for more details. 1619370Spst 1798948Sobrien You should have received a copy of the GNU General Public License 1898948Sobrien along with this program; if not, write to the Free Software 1998948Sobrien Foundation, Inc., 59 Temple Place - Suite 330, 2098948Sobrien Boston, MA 02111-1307, USA. */ 2119370Spst 2298948Sobrien#include "config.h" 2319370Spst#include <stdio.h> 2419370Spst#include <sys/file.h> 2519370Spst#include <netinet/in.h> 2619370Spst#include <sys/socket.h> 2719370Spst#include <netdb.h> 2819370Spst#include <netinet/tcp.h> 2919370Spst#include <signal.h> 3019370Spst#include <ctype.h> 3198948Sobrien#include <fcntl.h> 3224563Spst#include <errno.h> 3319370Spst 34104993Smp#ifdef HAVE_STDLIB_H 35104993Smp#include <stdlib.h> 36104993Smp#endif 37104993Smp#ifdef HAVE_STRING_H 38104993Smp#include <string.h> 39104993Smp#endif 40104993Smp#ifdef HAVE_UNISTD_H 41104993Smp#include <unistd.h> 42104993Smp#endif 43104993Smp 4419370Spst/* Sort of a hack... */ 4519370Spst#define EOL (EOF - 1) 4619370Spst 4719370Spststatic int remote_desc; 4819370Spst 4919370Spst/* Print the system error message for errno, and also mention STRING 5019370Spst as the file name for which the error was encountered. 5119370Spst Then return to command level. */ 5219370Spst 5399675Sobrienstatic void 5498948Sobrienperror_with_name (char *string) 5519370Spst{ 5698948Sobrien#ifndef STDC_HEADERS 5798948Sobrien extern int errno; 5898948Sobrien#endif 5998948Sobrien const char *err; 6019370Spst char *combined; 6119370Spst 62130809Smarcel err = strerror (errno); 63130809Smarcel if (err == NULL) 64130809Smarcel err = "unknown error"; 65130809Smarcel 6619370Spst combined = (char *) alloca (strlen (err) + strlen (string) + 3); 6719370Spst strcpy (combined, string); 6819370Spst strcat (combined, ": "); 6919370Spst strcat (combined, err); 7019370Spst fprintf (stderr, "\n%s.\n", combined); 7119370Spst fflush (stderr); 7219370Spst exit (1); 7319370Spst} 7419370Spst 7519370Spststatic void 7698948Sobriensync_error (FILE *fp, char *desc, int expect, int got) 7719370Spst{ 7819370Spst fprintf (stderr, "\n%s\n", desc); 7919370Spst fprintf (stderr, "At logfile offset %ld, expected '0x%x' got '0x%x'\n", 8019370Spst ftell (fp), expect, got); 8119370Spst fflush (stderr); 8219370Spst exit (1); 8319370Spst} 8419370Spst 8599675Sobrienstatic void 8698948Sobrienremote_close (void) 8719370Spst{ 8819370Spst close (remote_desc); 8919370Spst} 9019370Spst 9119370Spst/* Open a connection to a remote debugger. 9219370Spst NAME is the filename used for communication. */ 9319370Spst 9499675Sobrienstatic void 9598948Sobrienremote_open (char *name) 9619370Spst{ 9719370Spst if (!strchr (name, ':')) 9819370Spst { 9919370Spst fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name); 10019370Spst fflush (stderr); 10119370Spst exit (1); 10219370Spst } 10319370Spst else 10419370Spst { 10519370Spst char *port_str; 10619370Spst int port; 10719370Spst struct sockaddr_in sockaddr; 10819370Spst int tmp; 10919370Spst int tmp_desc; 11019370Spst 11119370Spst port_str = strchr (name, ':'); 11219370Spst 11319370Spst port = atoi (port_str + 1); 11419370Spst 11519370Spst tmp_desc = socket (PF_INET, SOCK_STREAM, 0); 11619370Spst if (tmp_desc < 0) 11719370Spst perror_with_name ("Can't open socket"); 11819370Spst 11919370Spst /* Allow rapid reuse of this port. */ 12019370Spst tmp = 1; 12198948Sobrien setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, 12298948Sobrien sizeof (tmp)); 12319370Spst 12419370Spst sockaddr.sin_family = PF_INET; 12598948Sobrien sockaddr.sin_port = htons (port); 12619370Spst sockaddr.sin_addr.s_addr = INADDR_ANY; 12719370Spst 12898948Sobrien if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) 12919370Spst || listen (tmp_desc, 1)) 13019370Spst perror_with_name ("Can't bind address"); 13119370Spst 13219370Spst tmp = sizeof (sockaddr); 13398948Sobrien remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp); 13419370Spst if (remote_desc == -1) 13519370Spst perror_with_name ("Accept failed"); 13619370Spst 13719370Spst /* Enable TCP keep alive process. */ 13819370Spst tmp = 1; 13998948Sobrien setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE, (char *) &tmp, sizeof (tmp)); 14019370Spst 14119370Spst /* Tell TCP not to delay small packets. This greatly speeds up 14298948Sobrien interactive response. */ 14319370Spst tmp = 1; 14498948Sobrien setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY, 14598948Sobrien (char *) &tmp, sizeof (tmp)); 14619370Spst 14719370Spst close (tmp_desc); /* No longer need this */ 14819370Spst 14998948Sobrien signal (SIGPIPE, SIG_IGN); /* If we don't do this, then gdbreplay simply 15098948Sobrien exits when the remote side dies. */ 15119370Spst } 15219370Spst 15319370Spst fcntl (remote_desc, F_SETFL, FASYNC); 15419370Spst 15519370Spst fprintf (stderr, "Replay logfile using %s\n", name); 15619370Spst fflush (stderr); 15719370Spst} 15819370Spst 15998948Sobrienstatic int 16098948Sobrientohex (int ch) 16119370Spst{ 16219370Spst if (ch >= '0' && ch <= '9') 16319370Spst { 16419370Spst return (ch - '0'); 16519370Spst } 16619370Spst if (ch >= 'A' && ch <= 'F') 16719370Spst { 16819370Spst return (ch - 'A' + 10); 16919370Spst } 17019370Spst if (ch >= 'a' && ch <= 'f') 17119370Spst { 17219370Spst return (ch - 'a' + 10); 17319370Spst } 17419370Spst fprintf (stderr, "\nInvalid hex digit '%c'\n", ch); 17519370Spst fflush (stderr); 17619370Spst exit (1); 17719370Spst} 17819370Spst 17919370Spststatic int 18098948Sobrienlogchar (FILE *fp) 18119370Spst{ 18219370Spst int ch; 18319370Spst int ch2; 18419370Spst 18519370Spst ch = fgetc (fp); 18619370Spst fputc (ch, stdout); 18719370Spst fflush (stdout); 18819370Spst switch (ch) 18919370Spst { 19019370Spst case '\n': 19119370Spst ch = EOL; 19219370Spst break; 19319370Spst case '\\': 19419370Spst ch = fgetc (fp); 19519370Spst fputc (ch, stdout); 19619370Spst fflush (stdout); 19719370Spst switch (ch) 19819370Spst { 19998948Sobrien case '\\': 20098948Sobrien break; 20198948Sobrien case 'b': 20298948Sobrien ch = '\b'; 20398948Sobrien break; 20498948Sobrien case 'f': 20598948Sobrien ch = '\f'; 20698948Sobrien break; 20798948Sobrien case 'n': 20898948Sobrien ch = '\n'; 20998948Sobrien break; 21098948Sobrien case 'r': 21198948Sobrien ch = '\r'; 21298948Sobrien break; 21398948Sobrien case 't': 21498948Sobrien ch = '\t'; 21598948Sobrien break; 21698948Sobrien case 'v': 21798948Sobrien ch = '\v'; 21898948Sobrien break; 21919370Spst case 'x': 22019370Spst ch2 = fgetc (fp); 22119370Spst fputc (ch2, stdout); 22219370Spst fflush (stdout); 22319370Spst ch = tohex (ch2) << 4; 22419370Spst ch2 = fgetc (fp); 22519370Spst fputc (ch2, stdout); 22619370Spst fflush (stdout); 22719370Spst ch |= tohex (ch2); 22819370Spst break; 22919370Spst default: 23019370Spst /* Treat any other char as just itself */ 23119370Spst break; 23219370Spst } 23319370Spst default: 23419370Spst break; 23519370Spst } 23619370Spst return (ch); 23719370Spst} 23819370Spst 23919370Spst/* Accept input from gdb and match with chars from fp (after skipping one 24019370Spst blank) up until a \n is read from fp (which is not matched) */ 24119370Spst 24299675Sobrienstatic void 24398948Sobrienexpect (FILE *fp) 24419370Spst{ 24519370Spst int fromlog; 24619370Spst unsigned char fromgdb; 24719370Spst 24819370Spst if ((fromlog = logchar (fp)) != ' ') 24919370Spst { 25019370Spst sync_error (fp, "Sync error during gdb read of leading blank", ' ', 25119370Spst fromlog); 25219370Spst } 25319370Spst do 25419370Spst { 25519370Spst fromlog = logchar (fp); 25619370Spst if (fromlog == EOL) 25719370Spst { 25819370Spst break; 25919370Spst } 26019370Spst read (remote_desc, &fromgdb, 1); 26198948Sobrien } 26298948Sobrien while (fromlog == fromgdb); 26319370Spst if (fromlog != EOL) 26419370Spst { 26519370Spst sync_error (fp, "Sync error during read of gdb packet", fromlog, 26619370Spst fromgdb); 26719370Spst } 26819370Spst} 26919370Spst 27019370Spst/* Play data back to gdb from fp (after skipping leading blank) up until a 27119370Spst \n is read from fp (which is discarded and not sent to gdb). */ 27219370Spst 27399675Sobrienstatic void 27498948Sobrienplay (FILE *fp) 27519370Spst{ 27619370Spst int fromlog; 27719370Spst char ch; 27819370Spst 27919370Spst if ((fromlog = logchar (fp)) != ' ') 28019370Spst { 28119370Spst sync_error (fp, "Sync error skipping blank during write to gdb", ' ', 28219370Spst fromlog); 28319370Spst } 28419370Spst while ((fromlog = logchar (fp)) != EOL) 28519370Spst { 28619370Spst ch = fromlog; 28719370Spst write (remote_desc, &ch, 1); 28819370Spst } 28919370Spst} 29019370Spst 29119370Spstint 29298948Sobrienmain (int argc, char *argv[]) 29319370Spst{ 29419370Spst FILE *fp; 29519370Spst int ch; 29619370Spst 29719370Spst if (argc < 3) 29819370Spst { 29919370Spst fprintf (stderr, "Usage: gdbreplay <logfile> <host:port>\n"); 30019370Spst fflush (stderr); 30119370Spst exit (1); 30219370Spst } 30319370Spst fp = fopen (argv[1], "r"); 30419370Spst if (fp == NULL) 30519370Spst { 30619370Spst perror_with_name (argv[1]); 30798948Sobrien } 30819370Spst remote_open (argv[2]); 30919370Spst while ((ch = logchar (fp)) != EOF) 31019370Spst { 31119370Spst switch (ch) 31219370Spst { 31319370Spst case 'w': 31419370Spst /* data sent from gdb to gdbreplay, accept and match it */ 31519370Spst expect (fp); 31619370Spst break; 31719370Spst case 'r': 31819370Spst /* data sent from gdbreplay to gdb, play it */ 31919370Spst play (fp); 32019370Spst break; 32119370Spst case 'c': 32219370Spst /* Command executed by gdb */ 32319370Spst while ((ch = logchar (fp)) != EOL); 32419370Spst break; 32519370Spst } 32619370Spst } 32719370Spst remote_close (); 32819370Spst exit (0); 32919370Spst} 330