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