xref: /openbsd-src/gnu/usr.bin/binutils/gdb/gdbserver/gdbreplay.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
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 
30 /* Sort of a hack... */
31 #define EOL (EOF - 1)
32 
33 static int remote_desc;
34 
35 /* Print the system error message for errno, and also mention STRING
36    as the file name for which the error was encountered.
37    Then return to command level.  */
38 
39 void
40 perror_with_name (string)
41      char *string;
42 {
43   extern int sys_nerr;
44   extern char *sys_errlist[];
45   extern int errno;
46   char *err;
47   char *combined;
48 
49   err = (errno < sys_nerr) ? sys_errlist[errno] : "unknown error";
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 
318