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