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