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 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 <sys/file.h>
23 #include <netinet/in.h>
24 #include <sys/socket.h>
25 #include <netdb.h>
26 #include <netinet/tcp.h>
27 #include <signal.h>
28 #include <ctype.h>
29 #include <fcntl.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   extern char *strchr ();
88
89   if (!strchr (name, ':'))
90     {
91       fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
92       fflush (stderr);
93       exit (1);
94     }
95   else
96     {
97       char *port_str;
98       int port;
99       struct sockaddr_in sockaddr;
100       int tmp;
101       struct protoent *protoent;
102       int tmp_desc;
103
104       port_str = strchr (name, ':');
105
106       port = atoi (port_str + 1);
107
108       tmp_desc = socket (PF_INET, SOCK_STREAM, 0);
109       if (tmp_desc < 0)
110         perror_with_name ("Can't open socket");
111
112       /* Allow rapid reuse of this port. */
113       tmp = 1;
114       setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *)&tmp,
115                   sizeof(tmp));
116
117       sockaddr.sin_family = PF_INET;
118       sockaddr.sin_port = htons(port);
119       sockaddr.sin_addr.s_addr = INADDR_ANY;
120
121       if (bind (tmp_desc, (struct sockaddr *)&sockaddr, sizeof (sockaddr))
122           || listen (tmp_desc, 1))
123         perror_with_name ("Can't bind address");
124
125       tmp = sizeof (sockaddr);
126       remote_desc = accept (tmp_desc, (struct sockaddr *)&sockaddr, &tmp);
127       if (remote_desc == -1)
128         perror_with_name ("Accept failed");
129
130       protoent = getprotobyname ("tcp");
131       if (!protoent)
132         perror_with_name ("getprotobyname");
133
134       /* Enable TCP keep alive process. */
135       tmp = 1;
136       setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE, (char *)&tmp, sizeof(tmp));
137
138       /* Tell TCP not to delay small packets.  This greatly speeds up
139          interactive response. */
140       tmp = 1;
141       setsockopt (remote_desc, protoent->p_proto, TCP_NODELAY,
142                   (char *)&tmp, sizeof(tmp));
143
144       close (tmp_desc);         /* No longer need this */
145
146       signal (SIGPIPE, SIG_IGN); /* If we don't do this, then gdbreplay simply
147                                     exits when the remote side dies.  */
148     }
149
150   fcntl (remote_desc, F_SETFL, FASYNC);
151
152   fprintf (stderr, "Replay logfile using %s\n", name);
153   fflush (stderr);
154 }
155
156 static int tohex (ch)
157      int ch;
158 {
159   if (ch >= '0' && ch <= '9')
160     {
161       return (ch - '0');
162     }
163   if (ch >= 'A' && ch <= 'F')
164     {
165       return (ch - 'A' + 10);
166     }
167   if (ch >= 'a' && ch <= 'f')
168     {
169       return (ch - 'a' + 10);
170     }
171   fprintf (stderr, "\nInvalid hex digit '%c'\n", ch);
172   fflush (stderr);
173   exit (1);
174 }
175
176 static int
177 logchar (fp)
178      FILE *fp;
179 {
180   int ch;
181   int ch2;
182
183   ch = fgetc (fp);
184   fputc (ch, stdout);
185   fflush (stdout);
186   switch (ch)
187     {
188     case '\n':
189       ch = EOL;
190       break;
191     case '\\':
192       ch = fgetc (fp);
193       fputc (ch, stdout);
194       fflush (stdout);
195       switch (ch)
196         {
197         case '\\': break;
198         case 'b': ch = '\b'; break;
199         case 'f': ch = '\f'; break;
200         case 'n': ch = '\n'; break;
201         case 'r': ch = '\r'; break;
202         case 't': ch = '\t'; break;
203         case 'v': ch = '\v'; break;
204         case 'x':
205           ch2 = fgetc (fp);
206           fputc (ch2, stdout);
207           fflush (stdout);
208           ch = tohex (ch2) << 4;
209           ch2 = fgetc (fp);
210           fputc (ch2, stdout);
211           fflush (stdout);
212           ch |= tohex (ch2);
213           break;
214         default:
215           /* Treat any other char as just itself */
216           break;
217         }
218     default:
219       break;
220     }
221   return (ch);
222 }
223
224 /* Accept input from gdb and match with chars from fp (after skipping one
225    blank) up until a \n is read from fp (which is not matched) */
226
227 void
228 expect (fp)
229      FILE *fp;
230 {
231   int fromlog;
232   unsigned char fromgdb;
233
234   if ((fromlog = logchar (fp)) != ' ')
235     {
236       sync_error (fp, "Sync error during gdb read of leading blank", ' ',
237                   fromlog);
238     }
239   do
240     {
241       fromlog = logchar (fp);
242       if (fromlog == EOL)
243         {
244           break;
245         }
246       read (remote_desc, &fromgdb, 1);
247     } while (fromlog == fromgdb);
248   if (fromlog != EOL)
249     {
250       sync_error (fp, "Sync error during read of gdb packet", fromlog,
251                   fromgdb);
252     }
253 }
254
255 /* Play data back to gdb from fp (after skipping leading blank) up until a
256    \n is read from fp (which is discarded and not sent to gdb). */
257
258 void
259 play (fp)
260      FILE *fp;
261 {
262   int fromlog;
263   char ch;
264
265   if ((fromlog = logchar (fp)) != ' ')
266     {
267       sync_error (fp, "Sync error skipping blank during write to gdb", ' ',
268                   fromlog);
269     }
270   while ((fromlog = logchar (fp)) != EOL)
271     {
272       ch = fromlog;
273       write (remote_desc, &ch, 1);
274     }
275 }
276
277 int
278 main (argc, argv)
279      int argc;
280      char *argv[];
281 {
282   FILE *fp;
283   int ch;
284
285   if (argc < 3)
286     {
287       fprintf (stderr, "Usage: gdbreplay <logfile> <host:port>\n");
288       fflush (stderr);
289       exit (1);
290     }
291   fp = fopen (argv[1], "r");
292   if (fp == NULL)
293     {
294       perror_with_name (argv[1]);
295     }      
296   remote_open (argv[2]);
297   while ((ch = logchar (fp)) != EOF)
298     {
299       switch (ch)
300         {
301         case 'w':
302           /* data sent from gdb to gdbreplay, accept and match it */
303           expect (fp);
304           break;
305         case 'r':
306           /* data sent from gdbreplay to gdb, play it */
307           play (fp);
308           break;
309         case 'c':
310           /* Command executed by gdb */
311           while ((ch = logchar (fp)) != EOL);
312           break;
313         }
314     }
315   remote_close ();
316   exit (0);
317 }
318