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