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