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