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