xref: /openbsd-src/gnu/usr.bin/binutils/gdb/gdbserver/gdbreplay.c (revision b725ae7711052a2233e31a66fefb8a752c388d7a)
1e93f7393Sniklas /* Replay a remote debug session logfile for GDB.
2*b725ae77Skettenis    Copyright 1996, 1998, 1999, 2000, 2002, 2003 Free Software Foundation, Inc.
3e93f7393Sniklas    Written by Fred Fish (fnf@cygnus.com) from pieces of gdbserver.
4e93f7393Sniklas 
5e93f7393Sniklas    This file is part of GDB.
6e93f7393Sniklas 
7e93f7393Sniklas    This program is free software; you can redistribute it and/or modify
8e93f7393Sniklas    it under the terms of the GNU General Public License as published by
9e93f7393Sniklas    the Free Software Foundation; either version 2 of the License, or
10e93f7393Sniklas    (at your option) any later version.
11e93f7393Sniklas 
12e93f7393Sniklas    This program is distributed in the hope that it will be useful,
13e93f7393Sniklas    but WITHOUT ANY WARRANTY; without even the implied warranty of
14e93f7393Sniklas    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15e93f7393Sniklas    GNU General Public License for more details.
16e93f7393Sniklas 
17e93f7393Sniklas    You should have received a copy of the GNU General Public License
18e93f7393Sniklas    along with this program; if not, write to the Free Software
19*b725ae77Skettenis    Foundation, Inc., 59 Temple Place - Suite 330,
20*b725ae77Skettenis    Boston, MA 02111-1307, USA.  */
21e93f7393Sniklas 
22*b725ae77Skettenis #include "config.h"
23e93f7393Sniklas #include <stdio.h>
24e93f7393Sniklas #include <sys/file.h>
25e93f7393Sniklas #include <netinet/in.h>
26e93f7393Sniklas #include <sys/socket.h>
27e93f7393Sniklas #include <netdb.h>
28e93f7393Sniklas #include <netinet/tcp.h>
29e93f7393Sniklas #include <signal.h>
30e93f7393Sniklas #include <ctype.h>
31*b725ae77Skettenis #include <fcntl.h>
32*b725ae77Skettenis #include <errno.h>
33*b725ae77Skettenis 
34*b725ae77Skettenis #ifdef HAVE_STDLIB_H
35*b725ae77Skettenis #include <stdlib.h>
36*b725ae77Skettenis #endif
37*b725ae77Skettenis #ifdef HAVE_STRING_H
38*b725ae77Skettenis #include <string.h>
39*b725ae77Skettenis #endif
40*b725ae77Skettenis #ifdef HAVE_UNISTD_H
41*b725ae77Skettenis #include <unistd.h>
42*b725ae77Skettenis #endif
43e93f7393Sniklas 
44e93f7393Sniklas /* Sort of a hack... */
45e93f7393Sniklas #define EOL (EOF - 1)
46e93f7393Sniklas 
47e93f7393Sniklas static int remote_desc;
48e93f7393Sniklas 
49e93f7393Sniklas /* Print the system error message for errno, and also mention STRING
50e93f7393Sniklas    as the file name for which the error was encountered.
51e93f7393Sniklas    Then return to command level.  */
52e93f7393Sniklas 
53*b725ae77Skettenis static void
perror_with_name(char * string)54*b725ae77Skettenis perror_with_name (char *string)
55e93f7393Sniklas {
56*b725ae77Skettenis #ifndef STDC_HEADERS
57e93f7393Sniklas   extern int errno;
58*b725ae77Skettenis #endif
59*b725ae77Skettenis   const char *err;
60e93f7393Sniklas   char *combined;
61e93f7393Sniklas 
62*b725ae77Skettenis   err = strerror (errno);
63*b725ae77Skettenis   if (err == NULL)
64*b725ae77Skettenis     err = "unknown error";
65*b725ae77Skettenis 
66e93f7393Sniklas   combined = (char *) alloca (strlen (err) + strlen (string) + 3);
67e93f7393Sniklas   strcpy (combined, string);
68e93f7393Sniklas   strcat (combined, ": ");
69e93f7393Sniklas   strcat (combined, err);
70e93f7393Sniklas   fprintf (stderr, "\n%s.\n", combined);
71e93f7393Sniklas   fflush (stderr);
72e93f7393Sniklas   exit (1);
73e93f7393Sniklas }
74e93f7393Sniklas 
75e93f7393Sniklas static void
sync_error(FILE * fp,char * desc,int expect,int got)76*b725ae77Skettenis sync_error (FILE *fp, char *desc, int expect, int got)
77e93f7393Sniklas {
78e93f7393Sniklas   fprintf (stderr, "\n%s\n", desc);
79e93f7393Sniklas   fprintf (stderr, "At logfile offset %ld, expected '0x%x' got '0x%x'\n",
80e93f7393Sniklas 	   ftell (fp), expect, got);
81e93f7393Sniklas   fflush (stderr);
82e93f7393Sniklas   exit (1);
83e93f7393Sniklas }
84e93f7393Sniklas 
85*b725ae77Skettenis static void
remote_close(void)86*b725ae77Skettenis remote_close (void)
87e93f7393Sniklas {
88e93f7393Sniklas   close (remote_desc);
89e93f7393Sniklas }
90e93f7393Sniklas 
91e93f7393Sniklas /* Open a connection to a remote debugger.
92e93f7393Sniklas    NAME is the filename used for communication.  */
93e93f7393Sniklas 
94*b725ae77Skettenis static void
remote_open(char * name)95*b725ae77Skettenis remote_open (char *name)
96e93f7393Sniklas {
97e93f7393Sniklas   if (!strchr (name, ':'))
98e93f7393Sniklas     {
99e93f7393Sniklas       fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
100e93f7393Sniklas       fflush (stderr);
101e93f7393Sniklas       exit (1);
102e93f7393Sniklas     }
103e93f7393Sniklas   else
104e93f7393Sniklas     {
105e93f7393Sniklas       char *port_str;
106e93f7393Sniklas       int port;
107e93f7393Sniklas       struct sockaddr_in sockaddr;
108e93f7393Sniklas       int tmp;
109e93f7393Sniklas       int tmp_desc;
110e93f7393Sniklas 
111e93f7393Sniklas       port_str = strchr (name, ':');
112e93f7393Sniklas 
113e93f7393Sniklas       port = atoi (port_str + 1);
114e93f7393Sniklas 
115e93f7393Sniklas       tmp_desc = socket (PF_INET, SOCK_STREAM, 0);
116e93f7393Sniklas       if (tmp_desc < 0)
117e93f7393Sniklas 	perror_with_name ("Can't open socket");
118e93f7393Sniklas 
119e93f7393Sniklas       /* Allow rapid reuse of this port. */
120e93f7393Sniklas       tmp = 1;
121e93f7393Sniklas       setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
122e93f7393Sniklas 		  sizeof (tmp));
123e93f7393Sniklas 
124e93f7393Sniklas       sockaddr.sin_family = PF_INET;
125e93f7393Sniklas       sockaddr.sin_port = htons (port);
126e93f7393Sniklas       sockaddr.sin_addr.s_addr = INADDR_ANY;
127e93f7393Sniklas 
128e93f7393Sniklas       if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
129e93f7393Sniklas 	  || listen (tmp_desc, 1))
130e93f7393Sniklas 	perror_with_name ("Can't bind address");
131e93f7393Sniklas 
132e93f7393Sniklas       tmp = sizeof (sockaddr);
133e93f7393Sniklas       remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp);
134e93f7393Sniklas       if (remote_desc == -1)
135e93f7393Sniklas 	perror_with_name ("Accept failed");
136e93f7393Sniklas 
137e93f7393Sniklas       /* Enable TCP keep alive process. */
138e93f7393Sniklas       tmp = 1;
139e93f7393Sniklas       setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE, (char *) &tmp, sizeof (tmp));
140e93f7393Sniklas 
141e93f7393Sniklas       /* Tell TCP not to delay small packets.  This greatly speeds up
142e93f7393Sniklas          interactive response. */
143e93f7393Sniklas       tmp = 1;
144*b725ae77Skettenis       setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
145e93f7393Sniklas 		  (char *) &tmp, sizeof (tmp));
146e93f7393Sniklas 
147e93f7393Sniklas       close (tmp_desc);		/* No longer need this */
148e93f7393Sniklas 
149e93f7393Sniklas       signal (SIGPIPE, SIG_IGN);	/* If we don't do this, then gdbreplay simply
150e93f7393Sniklas 					   exits when the remote side dies.  */
151e93f7393Sniklas     }
152e93f7393Sniklas 
153e93f7393Sniklas   fcntl (remote_desc, F_SETFL, FASYNC);
154e93f7393Sniklas 
155e93f7393Sniklas   fprintf (stderr, "Replay logfile using %s\n", name);
156e93f7393Sniklas   fflush (stderr);
157e93f7393Sniklas }
158e93f7393Sniklas 
159*b725ae77Skettenis static int
tohex(int ch)160*b725ae77Skettenis tohex (int ch)
161e93f7393Sniklas {
162e93f7393Sniklas   if (ch >= '0' && ch <= '9')
163e93f7393Sniklas     {
164e93f7393Sniklas       return (ch - '0');
165e93f7393Sniklas     }
166e93f7393Sniklas   if (ch >= 'A' && ch <= 'F')
167e93f7393Sniklas     {
168e93f7393Sniklas       return (ch - 'A' + 10);
169e93f7393Sniklas     }
170e93f7393Sniklas   if (ch >= 'a' && ch <= 'f')
171e93f7393Sniklas     {
172e93f7393Sniklas       return (ch - 'a' + 10);
173e93f7393Sniklas     }
174e93f7393Sniklas   fprintf (stderr, "\nInvalid hex digit '%c'\n", ch);
175e93f7393Sniklas   fflush (stderr);
176e93f7393Sniklas   exit (1);
177e93f7393Sniklas }
178e93f7393Sniklas 
179e93f7393Sniklas static int
logchar(FILE * fp)180*b725ae77Skettenis logchar (FILE *fp)
181e93f7393Sniklas {
182e93f7393Sniklas   int ch;
183e93f7393Sniklas   int ch2;
184e93f7393Sniklas 
185e93f7393Sniklas   ch = fgetc (fp);
186e93f7393Sniklas   fputc (ch, stdout);
187e93f7393Sniklas   fflush (stdout);
188e93f7393Sniklas   switch (ch)
189e93f7393Sniklas     {
190e93f7393Sniklas     case '\n':
191e93f7393Sniklas       ch = EOL;
192e93f7393Sniklas       break;
193e93f7393Sniklas     case '\\':
194e93f7393Sniklas       ch = fgetc (fp);
195e93f7393Sniklas       fputc (ch, stdout);
196e93f7393Sniklas       fflush (stdout);
197e93f7393Sniklas       switch (ch)
198e93f7393Sniklas 	{
199*b725ae77Skettenis 	case '\\':
200*b725ae77Skettenis 	  break;
201*b725ae77Skettenis 	case 'b':
202*b725ae77Skettenis 	  ch = '\b';
203*b725ae77Skettenis 	  break;
204*b725ae77Skettenis 	case 'f':
205*b725ae77Skettenis 	  ch = '\f';
206*b725ae77Skettenis 	  break;
207*b725ae77Skettenis 	case 'n':
208*b725ae77Skettenis 	  ch = '\n';
209*b725ae77Skettenis 	  break;
210*b725ae77Skettenis 	case 'r':
211*b725ae77Skettenis 	  ch = '\r';
212*b725ae77Skettenis 	  break;
213*b725ae77Skettenis 	case 't':
214*b725ae77Skettenis 	  ch = '\t';
215*b725ae77Skettenis 	  break;
216*b725ae77Skettenis 	case 'v':
217*b725ae77Skettenis 	  ch = '\v';
218*b725ae77Skettenis 	  break;
219e93f7393Sniklas 	case 'x':
220e93f7393Sniklas 	  ch2 = fgetc (fp);
221e93f7393Sniklas 	  fputc (ch2, stdout);
222e93f7393Sniklas 	  fflush (stdout);
223e93f7393Sniklas 	  ch = tohex (ch2) << 4;
224e93f7393Sniklas 	  ch2 = fgetc (fp);
225e93f7393Sniklas 	  fputc (ch2, stdout);
226e93f7393Sniklas 	  fflush (stdout);
227e93f7393Sniklas 	  ch |= tohex (ch2);
228e93f7393Sniklas 	  break;
229e93f7393Sniklas 	default:
230e93f7393Sniklas 	  /* Treat any other char as just itself */
231e93f7393Sniklas 	  break;
232e93f7393Sniklas 	}
233e93f7393Sniklas     default:
234e93f7393Sniklas       break;
235e93f7393Sniklas     }
236e93f7393Sniklas   return (ch);
237e93f7393Sniklas }
238e93f7393Sniklas 
239e93f7393Sniklas /* Accept input from gdb and match with chars from fp (after skipping one
240e93f7393Sniklas    blank) up until a \n is read from fp (which is not matched) */
241e93f7393Sniklas 
242*b725ae77Skettenis static void
expect(FILE * fp)243*b725ae77Skettenis expect (FILE *fp)
244e93f7393Sniklas {
245e93f7393Sniklas   int fromlog;
246e93f7393Sniklas   unsigned char fromgdb;
247e93f7393Sniklas 
248e93f7393Sniklas   if ((fromlog = logchar (fp)) != ' ')
249e93f7393Sniklas     {
250e93f7393Sniklas       sync_error (fp, "Sync error during gdb read of leading blank", ' ',
251e93f7393Sniklas 		  fromlog);
252e93f7393Sniklas     }
253e93f7393Sniklas   do
254e93f7393Sniklas     {
255e93f7393Sniklas       fromlog = logchar (fp);
256e93f7393Sniklas       if (fromlog == EOL)
257e93f7393Sniklas 	{
258e93f7393Sniklas 	  break;
259e93f7393Sniklas 	}
260e93f7393Sniklas       read (remote_desc, &fromgdb, 1);
261*b725ae77Skettenis     }
262*b725ae77Skettenis   while (fromlog == fromgdb);
263e93f7393Sniklas   if (fromlog != EOL)
264e93f7393Sniklas     {
265e93f7393Sniklas       sync_error (fp, "Sync error during read of gdb packet", fromlog,
266e93f7393Sniklas 		  fromgdb);
267e93f7393Sniklas     }
268e93f7393Sniklas }
269e93f7393Sniklas 
270e93f7393Sniklas /* Play data back to gdb from fp (after skipping leading blank) up until a
271e93f7393Sniklas    \n is read from fp (which is discarded and not sent to gdb). */
272e93f7393Sniklas 
273*b725ae77Skettenis static void
play(FILE * fp)274*b725ae77Skettenis play (FILE *fp)
275e93f7393Sniklas {
276e93f7393Sniklas   int fromlog;
277e93f7393Sniklas   char ch;
278e93f7393Sniklas 
279e93f7393Sniklas   if ((fromlog = logchar (fp)) != ' ')
280e93f7393Sniklas     {
281e93f7393Sniklas       sync_error (fp, "Sync error skipping blank during write to gdb", ' ',
282e93f7393Sniklas 		  fromlog);
283e93f7393Sniklas     }
284e93f7393Sniklas   while ((fromlog = logchar (fp)) != EOL)
285e93f7393Sniklas     {
286e93f7393Sniklas       ch = fromlog;
287e93f7393Sniklas       write (remote_desc, &ch, 1);
288e93f7393Sniklas     }
289e93f7393Sniklas }
290e93f7393Sniklas 
291e93f7393Sniklas int
main(int argc,char * argv[])292*b725ae77Skettenis main (int argc, char *argv[])
293e93f7393Sniklas {
294e93f7393Sniklas   FILE *fp;
295e93f7393Sniklas   int ch;
296e93f7393Sniklas 
297e93f7393Sniklas   if (argc < 3)
298e93f7393Sniklas     {
299e93f7393Sniklas       fprintf (stderr, "Usage: gdbreplay <logfile> <host:port>\n");
300e93f7393Sniklas       fflush (stderr);
301e93f7393Sniklas       exit (1);
302e93f7393Sniklas     }
303e93f7393Sniklas   fp = fopen (argv[1], "r");
304e93f7393Sniklas   if (fp == NULL)
305e93f7393Sniklas     {
306e93f7393Sniklas       perror_with_name (argv[1]);
307e93f7393Sniklas     }
308e93f7393Sniklas   remote_open (argv[2]);
309e93f7393Sniklas   while ((ch = logchar (fp)) != EOF)
310e93f7393Sniklas     {
311e93f7393Sniklas       switch (ch)
312e93f7393Sniklas 	{
313e93f7393Sniklas 	case 'w':
314e93f7393Sniklas 	  /* data sent from gdb to gdbreplay, accept and match it */
315e93f7393Sniklas 	  expect (fp);
316e93f7393Sniklas 	  break;
317e93f7393Sniklas 	case 'r':
318e93f7393Sniklas 	  /* data sent from gdbreplay to gdb, play it */
319e93f7393Sniklas 	  play (fp);
320e93f7393Sniklas 	  break;
321e93f7393Sniklas 	case 'c':
322e93f7393Sniklas 	  /* Command executed by gdb */
323e93f7393Sniklas 	  while ((ch = logchar (fp)) != EOL);
324e93f7393Sniklas 	  break;
325e93f7393Sniklas 	}
326e93f7393Sniklas     }
327e93f7393Sniklas   remote_close ();
328e93f7393Sniklas   exit (0);
329e93f7393Sniklas }
330