Make gdbreplay use more common routines
[external/binutils.git] / gdb / gdbserver / gdbreplay.c
1 /* Replay a remote debug session logfile for GDB.
2    Copyright (C) 1996-2018 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 3 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, see <http://www.gnu.org/licenses/>.  */
19
20 #include "common-defs.h"
21 #include "version.h"
22
23 #if HAVE_SYS_FILE_H
24 #include <sys/file.h>
25 #endif
26 #if HAVE_SIGNAL_H
27 #include <signal.h>
28 #endif
29 #include <ctype.h>
30 #if HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33 #include <unistd.h>
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37 #ifdef HAVE_SYS_SOCKET_H
38 #include <sys/socket.h>
39 #endif
40 #if HAVE_NETDB_H
41 #include <netdb.h>
42 #endif
43 #if HAVE_NETINET_TCP_H
44 #include <netinet/tcp.h>
45 #endif
46
47 #if USE_WIN32API
48 #include <winsock2.h>
49 #endif
50
51 #ifndef HAVE_SOCKLEN_T
52 typedef int socklen_t;
53 #endif
54
55 /* Sort of a hack... */
56 #define EOL (EOF - 1)
57
58 static int remote_desc;
59
60 #ifdef __MINGW32CE__
61
62 #ifndef COUNTOF
63 #define COUNTOF(STR) (sizeof (STR) / sizeof ((STR)[0]))
64 #endif
65
66 #define errno (GetLastError ())
67
68 char *
69 strerror (DWORD error)
70 {
71   static char buf[1024];
72   WCHAR *msgbuf;
73   DWORD lasterr = GetLastError ();
74   DWORD chars = FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM
75                                 | FORMAT_MESSAGE_ALLOCATE_BUFFER,
76                                 NULL,
77                                 error,
78                                 0, /* Default language */
79                                 (LPVOID)&msgbuf,
80                                 0,
81                                 NULL);
82   if (chars != 0)
83     {
84       /* If there is an \r\n appended, zap it.  */
85       if (chars >= 2
86           && msgbuf[chars - 2] == '\r'
87           && msgbuf[chars - 1] == '\n')
88         {
89           chars -= 2;
90           msgbuf[chars] = 0;
91         }
92
93       if (chars > ((COUNTOF (buf)) - 1))
94         {
95           chars = COUNTOF (buf) - 1;
96           msgbuf [chars] = 0;
97         }
98
99       wcstombs (buf, msgbuf, chars + 1);
100       LocalFree (msgbuf);
101     }
102   else
103     sprintf (buf, "unknown win32 error (%ld)", error);
104
105   SetLastError (lasterr);
106   return buf;
107 }
108
109 #endif /* __MINGW32CE__ */
110
111 static void
112 sync_error (FILE *fp, const char *desc, int expect, int got)
113 {
114   fprintf (stderr, "\n%s\n", desc);
115   fprintf (stderr, "At logfile offset %ld, expected '0x%x' got '0x%x'\n",
116            ftell (fp), expect, got);
117   fflush (stderr);
118   exit (1);
119 }
120
121 static void
122 remote_error (const char *desc)
123 {
124   fprintf (stderr, "\n%s\n", desc);
125   fflush (stderr);
126   exit (1);
127 }
128
129 static void
130 remote_close (void)
131 {
132 #ifdef USE_WIN32API
133   closesocket (remote_desc);
134 #else
135   close (remote_desc);
136 #endif
137 }
138
139 /* Open a connection to a remote debugger.
140    NAME is the filename used for communication.  */
141
142 static void
143 remote_open (char *name)
144 {
145   if (!strchr (name, ':'))
146     {
147       fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
148       fflush (stderr);
149       exit (1);
150     }
151   else
152     {
153 #ifdef USE_WIN32API
154       static int winsock_initialized;
155 #endif
156       char *port_str;
157       int port;
158       struct sockaddr_in sockaddr;
159       socklen_t tmp;
160       int tmp_desc;
161
162       port_str = strchr (name, ':');
163
164       port = atoi (port_str + 1);
165
166 #ifdef USE_WIN32API
167       if (!winsock_initialized)
168         {
169           WSADATA wsad;
170
171           WSAStartup (MAKEWORD (1, 0), &wsad);
172           winsock_initialized = 1;
173         }
174 #endif
175
176       tmp_desc = socket (PF_INET, SOCK_STREAM, 0);
177       if (tmp_desc == -1)
178         perror_with_name ("Can't open socket");
179
180       /* Allow rapid reuse of this port. */
181       tmp = 1;
182       setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
183                   sizeof (tmp));
184
185       sockaddr.sin_family = PF_INET;
186       sockaddr.sin_port = htons (port);
187       sockaddr.sin_addr.s_addr = INADDR_ANY;
188
189       if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
190           || listen (tmp_desc, 1))
191         perror_with_name ("Can't bind address");
192
193       tmp = sizeof (sockaddr);
194       remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp);
195       if (remote_desc == -1)
196         perror_with_name ("Accept failed");
197
198       /* Enable TCP keep alive process. */
199       tmp = 1;
200       setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE,
201                   (char *) &tmp, sizeof (tmp));
202
203       /* Tell TCP not to delay small packets.  This greatly speeds up
204          interactive response. */
205       tmp = 1;
206       setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
207                   (char *) &tmp, sizeof (tmp));
208
209 #ifndef USE_WIN32API
210       close (tmp_desc);         /* No longer need this */
211
212       signal (SIGPIPE, SIG_IGN);        /* If we don't do this, then
213                                            gdbreplay simply exits when
214                                            the remote side dies.  */
215 #else
216       closesocket (tmp_desc);   /* No longer need this */
217 #endif
218     }
219
220 #if defined(F_SETFL) && defined (FASYNC)
221   fcntl (remote_desc, F_SETFL, FASYNC);
222 #endif
223
224   fprintf (stderr, "Replay logfile using %s\n", name);
225   fflush (stderr);
226 }
227
228 static int
229 fromhex (int ch)
230 {
231   if (ch >= '0' && ch <= '9')
232     {
233       return (ch - '0');
234     }
235   if (ch >= 'A' && ch <= 'F')
236     {
237       return (ch - 'A' + 10);
238     }
239   if (ch >= 'a' && ch <= 'f')
240     {
241       return (ch - 'a' + 10);
242     }
243   fprintf (stderr, "\nInvalid hex digit '%c'\n", ch);
244   fflush (stderr);
245   exit (1);
246 }
247
248 static int
249 logchar (FILE *fp)
250 {
251   int ch;
252   int ch2;
253
254   ch = fgetc (fp);
255   fputc (ch, stdout);
256   fflush (stdout);
257   switch (ch)
258     {
259     case '\n':
260       ch = EOL;
261       break;
262     case '\\':
263       ch = fgetc (fp);
264       fputc (ch, stdout);
265       fflush (stdout);
266       switch (ch)
267         {
268         case '\\':
269           break;
270         case 'b':
271           ch = '\b';
272           break;
273         case 'f':
274           ch = '\f';
275           break;
276         case 'n':
277           ch = '\n';
278           break;
279         case 'r':
280           ch = '\r';
281           break;
282         case 't':
283           ch = '\t';
284           break;
285         case 'v':
286           ch = '\v';
287           break;
288         case 'x':
289           ch2 = fgetc (fp);
290           fputc (ch2, stdout);
291           fflush (stdout);
292           ch = fromhex (ch2) << 4;
293           ch2 = fgetc (fp);
294           fputc (ch2, stdout);
295           fflush (stdout);
296           ch |= fromhex (ch2);
297           break;
298         default:
299           /* Treat any other char as just itself */
300           break;
301         }
302     default:
303       break;
304     }
305   return (ch);
306 }
307
308 static int
309 gdbchar (int desc)
310 {
311   unsigned char fromgdb;
312
313   if (read (desc, &fromgdb, 1) != 1)
314     return -1;
315   else
316     return fromgdb;
317 }
318
319 /* Accept input from gdb and match with chars from fp (after skipping one
320    blank) up until a \n is read from fp (which is not matched) */
321
322 static void
323 expect (FILE *fp)
324 {
325   int fromlog;
326   int fromgdb;
327
328   if ((fromlog = logchar (fp)) != ' ')
329     {
330       sync_error (fp, "Sync error during gdb read of leading blank", ' ',
331                   fromlog);
332     }
333   do
334     {
335       fromlog = logchar (fp);
336       if (fromlog == EOL)
337         break;
338       fromgdb = gdbchar (remote_desc);
339       if (fromgdb < 0)
340         remote_error ("Error during read from gdb");
341     }
342   while (fromlog == fromgdb);
343
344   if (fromlog != EOL)
345     {
346       sync_error (fp, "Sync error during read of gdb packet from log", fromlog,
347                   fromgdb);
348     }
349 }
350
351 /* Play data back to gdb from fp (after skipping leading blank) up until a
352    \n is read from fp (which is discarded and not sent to gdb). */
353
354 static void
355 play (FILE *fp)
356 {
357   int fromlog;
358   char ch;
359
360   if ((fromlog = logchar (fp)) != ' ')
361     {
362       sync_error (fp, "Sync error skipping blank during write to gdb", ' ',
363                   fromlog);
364     }
365   while ((fromlog = logchar (fp)) != EOL)
366     {
367       ch = fromlog;
368       if (write (remote_desc, &ch, 1) != 1)
369         remote_error ("Error during write to gdb");
370     }
371 }
372
373 static void
374 gdbreplay_version (void)
375 {
376   printf ("GNU gdbreplay %s%s\n"
377           "Copyright (C) 2018 Free Software Foundation, Inc.\n"
378           "gdbreplay is free software, covered by "
379           "the GNU General Public License.\n"
380           "This gdbreplay was configured as \"%s\"\n",
381           PKGVERSION, version, host_name);
382 }
383
384 static void
385 gdbreplay_usage (FILE *stream)
386 {
387   fprintf (stream, "Usage:\tgdbreplay <logfile> <host:port>\n");
388   if (REPORT_BUGS_TO[0] && stream == stdout)
389     fprintf (stream, "Report bugs to \"%s\".\n", REPORT_BUGS_TO);
390 }
391
392 /* Main function.  This is called by the real "main" function,
393    wrapped in a TRY_CATCH that handles any uncaught exceptions.  */
394
395 static void ATTRIBUTE_NORETURN
396 captured_main (int argc, char *argv[])
397 {
398   FILE *fp;
399   int ch;
400
401   if (argc >= 2 && strcmp (argv[1], "--version") == 0)
402     {
403       gdbreplay_version ();
404       exit (0);
405     }
406   if (argc >= 2 && strcmp (argv[1], "--help") == 0)
407     {
408       gdbreplay_usage (stdout);
409       exit (0);
410     }
411
412   if (argc < 3)
413     {
414       gdbreplay_usage (stderr);
415       exit (1);
416     }
417   fp = fopen (argv[1], "r");
418   if (fp == NULL)
419     {
420       perror_with_name (argv[1]);
421     }
422   remote_open (argv[2]);
423   while ((ch = logchar (fp)) != EOF)
424     {
425       switch (ch)
426         {
427         case 'w':
428           /* data sent from gdb to gdbreplay, accept and match it */
429           expect (fp);
430           break;
431         case 'r':
432           /* data sent from gdbreplay to gdb, play it */
433           play (fp);
434           break;
435         case 'c':
436           /* Command executed by gdb */
437           while ((ch = logchar (fp)) != EOL);
438           break;
439         }
440     }
441   remote_close ();
442   exit (0);
443 }
444
445 int
446 main (int argc, char *argv[])
447 {
448   TRY
449     {
450       captured_main (argc, argv);
451     }
452   CATCH (exception, RETURN_MASK_ALL)
453     {
454       if (exception.reason == RETURN_ERROR)
455         {
456           fflush (stdout);
457           fprintf (stderr, "%s\n", exception.message);
458         }
459
460       exit (1);
461     }
462   END_CATCH
463
464   gdb_assert_not_reached ("captured_main should never return");
465 }