1 /* Replay a remote debug session logfile for GDB. 2 Copyright 1996, 1998, 1999, 2000, 2002, 2003 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, 20 Boston, MA 02111-1307, USA. */ 21 22 #include "config.h" 23 #include <stdio.h> 24 #include <sys/file.h> 25 #include <netinet/in.h> 26 #include <sys/socket.h> 27 #include <netdb.h> 28 #include <netinet/tcp.h> 29 #include <signal.h> 30 #include <ctype.h> 31 #include <fcntl.h> 32 #include <errno.h> 33 34 #ifdef HAVE_STDLIB_H 35 #include <stdlib.h> 36 #endif 37 #ifdef HAVE_STRING_H 38 #include <string.h> 39 #endif 40 #ifdef HAVE_UNISTD_H 41 #include <unistd.h> 42 #endif 43 44 /* Sort of a hack... */ 45 #define EOL (EOF - 1) 46 47 static int remote_desc; 48 49 /* Print the system error message for errno, and also mention STRING 50 as the file name for which the error was encountered. 51 Then return to command level. */ 52 53 static void 54 perror_with_name (char *string) 55 { 56 #ifndef STDC_HEADERS 57 extern int errno; 58 #endif 59 const char *err; 60 char *combined; 61 62 err = strerror (errno); 63 if (err == NULL) 64 err = "unknown error"; 65 66 combined = (char *) alloca (strlen (err) + strlen (string) + 3); 67 strcpy (combined, string); 68 strcat (combined, ": "); 69 strcat (combined, err); 70 fprintf (stderr, "\n%s.\n", combined); 71 fflush (stderr); 72 exit (1); 73 } 74 75 static void 76 sync_error (FILE *fp, char *desc, int expect, int got) 77 { 78 fprintf (stderr, "\n%s\n", desc); 79 fprintf (stderr, "At logfile offset %ld, expected '0x%x' got '0x%x'\n", 80 ftell (fp), expect, got); 81 fflush (stderr); 82 exit (1); 83 } 84 85 static void 86 remote_close (void) 87 { 88 close (remote_desc); 89 } 90 91 /* Open a connection to a remote debugger. 92 NAME is the filename used for communication. */ 93 94 static void 95 remote_open (char *name) 96 { 97 if (!strchr (name, ':')) 98 { 99 fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name); 100 fflush (stderr); 101 exit (1); 102 } 103 else 104 { 105 char *port_str; 106 int port; 107 struct sockaddr_in sockaddr; 108 int tmp; 109 int tmp_desc; 110 111 port_str = strchr (name, ':'); 112 113 port = atoi (port_str + 1); 114 115 tmp_desc = socket (PF_INET, SOCK_STREAM, 0); 116 if (tmp_desc < 0) 117 perror_with_name ("Can't open socket"); 118 119 /* Allow rapid reuse of this port. */ 120 tmp = 1; 121 setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, 122 sizeof (tmp)); 123 124 sockaddr.sin_family = PF_INET; 125 sockaddr.sin_port = htons (port); 126 sockaddr.sin_addr.s_addr = INADDR_ANY; 127 128 if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) 129 || listen (tmp_desc, 1)) 130 perror_with_name ("Can't bind address"); 131 132 tmp = sizeof (sockaddr); 133 remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp); 134 if (remote_desc == -1) 135 perror_with_name ("Accept failed"); 136 137 /* Enable TCP keep alive process. */ 138 tmp = 1; 139 setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE, (char *) &tmp, sizeof (tmp)); 140 141 /* Tell TCP not to delay small packets. This greatly speeds up 142 interactive response. */ 143 tmp = 1; 144 setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY, 145 (char *) &tmp, sizeof (tmp)); 146 147 close (tmp_desc); /* No longer need this */ 148 149 signal (SIGPIPE, SIG_IGN); /* If we don't do this, then gdbreplay simply 150 exits when the remote side dies. */ 151 } 152 153 fcntl (remote_desc, F_SETFL, FASYNC); 154 155 fprintf (stderr, "Replay logfile using %s\n", name); 156 fflush (stderr); 157 } 158 159 static int 160 tohex (int ch) 161 { 162 if (ch >= '0' && ch <= '9') 163 { 164 return (ch - '0'); 165 } 166 if (ch >= 'A' && ch <= 'F') 167 { 168 return (ch - 'A' + 10); 169 } 170 if (ch >= 'a' && ch <= 'f') 171 { 172 return (ch - 'a' + 10); 173 } 174 fprintf (stderr, "\nInvalid hex digit '%c'\n", ch); 175 fflush (stderr); 176 exit (1); 177 } 178 179 static int 180 logchar (FILE *fp) 181 { 182 int ch; 183 int ch2; 184 185 ch = fgetc (fp); 186 fputc (ch, stdout); 187 fflush (stdout); 188 switch (ch) 189 { 190 case '\n': 191 ch = EOL; 192 break; 193 case '\\': 194 ch = fgetc (fp); 195 fputc (ch, stdout); 196 fflush (stdout); 197 switch (ch) 198 { 199 case '\\': 200 break; 201 case 'b': 202 ch = '\b'; 203 break; 204 case 'f': 205 ch = '\f'; 206 break; 207 case 'n': 208 ch = '\n'; 209 break; 210 case 'r': 211 ch = '\r'; 212 break; 213 case 't': 214 ch = '\t'; 215 break; 216 case 'v': 217 ch = '\v'; 218 break; 219 case 'x': 220 ch2 = fgetc (fp); 221 fputc (ch2, stdout); 222 fflush (stdout); 223 ch = tohex (ch2) << 4; 224 ch2 = fgetc (fp); 225 fputc (ch2, stdout); 226 fflush (stdout); 227 ch |= tohex (ch2); 228 break; 229 default: 230 /* Treat any other char as just itself */ 231 break; 232 } 233 default: 234 break; 235 } 236 return (ch); 237 } 238 239 /* Accept input from gdb and match with chars from fp (after skipping one 240 blank) up until a \n is read from fp (which is not matched) */ 241 242 static void 243 expect (FILE *fp) 244 { 245 int fromlog; 246 unsigned char fromgdb; 247 248 if ((fromlog = logchar (fp)) != ' ') 249 { 250 sync_error (fp, "Sync error during gdb read of leading blank", ' ', 251 fromlog); 252 } 253 do 254 { 255 fromlog = logchar (fp); 256 if (fromlog == EOL) 257 { 258 break; 259 } 260 read (remote_desc, &fromgdb, 1); 261 } 262 while (fromlog == fromgdb); 263 if (fromlog != EOL) 264 { 265 sync_error (fp, "Sync error during read of gdb packet", fromlog, 266 fromgdb); 267 } 268 } 269 270 /* Play data back to gdb from fp (after skipping leading blank) up until a 271 \n is read from fp (which is discarded and not sent to gdb). */ 272 273 static void 274 play (FILE *fp) 275 { 276 int fromlog; 277 char ch; 278 279 if ((fromlog = logchar (fp)) != ' ') 280 { 281 sync_error (fp, "Sync error skipping blank during write to gdb", ' ', 282 fromlog); 283 } 284 while ((fromlog = logchar (fp)) != EOL) 285 { 286 ch = fromlog; 287 write (remote_desc, &ch, 1); 288 } 289 } 290 291 int 292 main (int argc, char *argv[]) 293 { 294 FILE *fp; 295 int ch; 296 297 if (argc < 3) 298 { 299 fprintf (stderr, "Usage: gdbreplay <logfile> <host:port>\n"); 300 fflush (stderr); 301 exit (1); 302 } 303 fp = fopen (argv[1], "r"); 304 if (fp == NULL) 305 { 306 perror_with_name (argv[1]); 307 } 308 remote_open (argv[2]); 309 while ((ch = logchar (fp)) != EOF) 310 { 311 switch (ch) 312 { 313 case 'w': 314 /* data sent from gdb to gdbreplay, accept and match it */ 315 expect (fp); 316 break; 317 case 'r': 318 /* data sent from gdbreplay to gdb, play it */ 319 play (fp); 320 break; 321 case 'c': 322 /* Command executed by gdb */ 323 while ((ch = logchar (fp)) != EOL); 324 break; 325 } 326 } 327 remote_close (); 328 exit (0); 329 } 330