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