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