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