GNU Binutils with patches for OS216
Revision | f5fa7161cadee2056fa62142dfbbc3b5dbdab67e (tree) |
---|---|
Time | 2008-06-22 04:41:01 |
Author | Michael Snyder <msnyder@vmwa...> |
Commiter | Michael Snyder |
2008-06-21 Michael Snyder <msnyder@specifix.com>
* gdbfreeplay-front.c: New file. Extended gdbreplay.
* gdbfreeplay-back.c: New file.
* gdbfreeplay-i386.c: New file.
* gdbfreeplay.h: New file.
* remote-breakpoint.c: New file.
* remote-breakpoint.h: New file.
* Makefile.in: Add rules for gdb-freeplay.
* configure.srv: Ditto.
* configure.ac: Ditto.
* configure: Regenerate.
@@ -1,3 +1,16 @@ | ||
1 | +2008-06-21 Michael Snyder <msnyder@specifix.com> | |
2 | + | |
3 | + * gdbfreeplay-front.c: New file. Extended gdbreplay. | |
4 | + * gdbfreeplay-back.c: New file. | |
5 | + * gdbfreeplay-i386.c: New file. | |
6 | + * gdbfreeplay.h: New file. | |
7 | + * remote-breakpoint.c: New file. | |
8 | + * remote-breakpoint.h: New file. | |
9 | + * Makefile.in: Add rules for gdb-freeplay. | |
10 | + * configure.srv: Ditto. | |
11 | + * configure.ac: Ditto. | |
12 | + * configure: Regenerate. | |
13 | + | |
1 | 14 | 2008-06-06 Daniel Jacobowitz <dan@codesourcery.com> |
2 | 15 | |
3 | 16 | * Makefile.in (gdbreplay.o): New rule. |
@@ -136,6 +136,8 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/inferiors.c \ | ||
136 | 136 | |
137 | 137 | DEPFILES = @GDBSERVER_DEPFILES@ |
138 | 138 | |
139 | +FREEPLAY_DEPFILES = @GDBFREEPLAY_DEPFILES@ | |
140 | + | |
139 | 141 | LIBOBJS = @LIBOBJS@ |
140 | 142 | |
141 | 143 | SOURCES = $(SFILES) |
@@ -199,6 +201,13 @@ gdbreplay$(EXEEXT): gdbreplay.o version.o | ||
199 | 201 | ${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) -o gdbreplay$(EXEEXT) $^ \ |
200 | 202 | $(XM_CLIBS) |
201 | 203 | |
204 | +gdb-freeplay$(EXEEXT): gdbfreeplay-front.o gdbfreeplay-back.o version.o \ | |
205 | + $(FREEPLAY_DEPFILES) remote-breakpoint.o | |
206 | + rm -f gdb-freeplay$(EXEEXT) | |
207 | + ${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) -o gdb-freeplay$(EXEEXT) $^ \ | |
208 | + $(XM_CLIBS) | |
209 | + | |
210 | + | |
202 | 211 | # Put the proper machine-specific files first, so M-. on a machine |
203 | 212 | # specific routine gets the one for the correct machine. |
204 | 213 | # The xyzzy stuff below deals with empty DEPFILES |
@@ -217,7 +226,8 @@ tags: TAGS | ||
217 | 226 | clean: |
218 | 227 | rm -f *.o ${ADD_FILES} *~ |
219 | 228 | rm -f version.c |
220 | - rm -f gdbserver$(EXEEXT) gdbreplay$(EXEEXT) core make.log | |
229 | + rm -f gdbserver$(EXEEXT) gdbreplay$(EXEEXT) gdb-freeplay$(EXEEXT) | |
230 | + rm -f core make.log | |
221 | 231 | rm -f reg-arm.c reg-i386.c reg-ia64.c reg-m32r.c reg-m68k.c |
222 | 232 | rm -f reg-sh.c reg-spu.c reg-x86-64.c reg-i386-linux.c |
223 | 233 | rm -f reg-cris.c reg-crisv32.c reg-x86-64-linux.c reg-xtensa.c |
@@ -289,6 +299,8 @@ target.o: target.c $(server_h) | ||
289 | 299 | thread-db.o: thread-db.c $(server_h) $(gdb_proc_service_h) |
290 | 300 | utils.o: utils.c $(server_h) |
291 | 301 | gdbreplay.o: gdbreplay.c config.h |
302 | +gdbfreeplay-front.o: gdbfreeplay-front.c gdbfreeplay.h | |
303 | +gdbfreeplay-back.o: gdbfreeplay-back.c gdbfreeplay.h | |
292 | 304 | |
293 | 305 | signals.o: ../signals/signals.c $(server_h) |
294 | 306 | $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER |
@@ -310,7 +310,7 @@ ac_includes_default="\ | ||
310 | 310 | # include <unistd.h> |
311 | 311 | #endif" |
312 | 312 | |
313 | -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CPP EGREP LIBOBJS PKGVERSION REPORT_BUGS_TO REPORT_BUGS_TEXI RDYNAMIC GDBSERVER_DEPFILES GDBSERVER_LIBS USE_THREAD_DB srv_xmlbuiltin srv_xmlfiles LTLIBOBJS' | |
313 | +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CPP EGREP LIBOBJS PKGVERSION REPORT_BUGS_TO REPORT_BUGS_TEXI RDYNAMIC GDBSERVER_DEPFILES GDBSERVER_LIBS USE_THREAD_DB srv_xmlbuiltin srv_xmlfiles GDBFREEPLAY_DEPFILES LTLIBOBJS' | |
314 | 314 | ac_subst_files='' |
315 | 315 | |
316 | 316 | # Initialize some variables set by options. |
@@ -4491,6 +4491,8 @@ fi | ||
4491 | 4491 | |
4492 | 4492 | GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_hostio_err_objs $srv_thread_depfiles" |
4493 | 4493 | GDBSERVER_LIBS="$srv_libs" |
4494 | +GDBFREEPLAY_DEPFILES="$freeplay_tgtobj" | |
4495 | + | |
4494 | 4496 | |
4495 | 4497 | |
4496 | 4498 |
@@ -5150,6 +5152,7 @@ s,@GDBSERVER_LIBS@,$GDBSERVER_LIBS,;t t | ||
5150 | 5152 | s,@USE_THREAD_DB@,$USE_THREAD_DB,;t t |
5151 | 5153 | s,@srv_xmlbuiltin@,$srv_xmlbuiltin,;t t |
5152 | 5154 | s,@srv_xmlfiles@,$srv_xmlfiles,;t t |
5155 | +s,@GDBFREEPLAY_DEPFILES@,$GDBFREEPLAY_DEPFILES,;t t | |
5153 | 5156 | s,@LTLIBOBJS@,$LTLIBOBJS,;t t |
5154 | 5157 | CEOF |
5155 | 5158 |
@@ -183,12 +183,14 @@ fi | ||
183 | 183 | |
184 | 184 | GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_hostio_err_objs $srv_thread_depfiles" |
185 | 185 | GDBSERVER_LIBS="$srv_libs" |
186 | +GDBFREEPLAY_DEPFILES="$freeplay_tgtobj" | |
186 | 187 | |
187 | 188 | AC_SUBST(GDBSERVER_DEPFILES) |
188 | 189 | AC_SUBST(GDBSERVER_LIBS) |
189 | 190 | AC_SUBST(USE_THREAD_DB) |
190 | 191 | AC_SUBST(srv_xmlbuiltin) |
191 | 192 | AC_SUBST(srv_xmlfiles) |
193 | +AC_SUBST(GDBFREEPLAY_DEPFILES) | |
192 | 194 | |
193 | 195 | AC_OUTPUT(Makefile, |
194 | 196 | [case x$CONFIG_HEADERS in |
@@ -10,6 +10,7 @@ | ||
10 | 10 | # target method. |
11 | 11 | # srv_xmlfiles All XML files which should be available for |
12 | 12 | # gdbserver in this configuration. |
13 | +# freeplay_tgtobj Target module for gdb-freeplay. | |
13 | 14 | # |
14 | 15 | # In addition, on GNU/Linux the following shell variables will be set: |
15 | 16 | # srv_linux_regsets Set to "yes" if ptrace(PTRACE_GETREGS) and friends |
@@ -54,16 +55,19 @@ case "${target}" in | ||
54 | 55 | ;; |
55 | 56 | i[34567]86-*-cygwin*) srv_regobj=reg-i386.o |
56 | 57 | srv_tgtobj="win32-low.o win32-i386-low.o" |
58 | + freeplay_tgtobj="gdbfreeplay-i386.o" | |
57 | 59 | ;; |
58 | 60 | i[34567]86-*-linux*) srv_regobj=reg-i386-linux.o |
59 | 61 | srv_tgtobj="linux-low.o linux-i386-low.o i387-fp.o" |
60 | 62 | srv_linux_usrregs=yes |
61 | 63 | srv_linux_regsets=yes |
62 | 64 | srv_linux_thread_db=yes |
65 | + freeplay_tgtobj="gdbfreeplay-i386.o" | |
63 | 66 | ;; |
64 | 67 | i[34567]86-*-mingw*) srv_regobj=reg-i386.o |
65 | 68 | srv_tgtobj="win32-low.o win32-i386-low.o" |
66 | 69 | srv_mingw=yes |
70 | + freeplay_tgtobj="gdbfreeplay-i386.o" | |
67 | 71 | ;; |
68 | 72 | ia64-*-linux*) srv_regobj=reg-ia64.o |
69 | 73 | srv_tgtobj="linux-low.o linux-ia64-low.o" |
@@ -0,0 +1,934 @@ | ||
1 | +/* | |
2 | + * gdbfreeplay-back.c | |
3 | + * | |
4 | + * Backend for gdbfreeplay. | |
5 | + */ | |
6 | + | |
7 | +#include <stdio.h> | |
8 | +#include <stdlib.h> | |
9 | +#include <ctype.h> | |
10 | +#include <string.h> | |
11 | + | |
12 | +typedef struct STOPFRAME { | |
13 | + /* frame_id -- a unique identifier per stop frame. */ | |
14 | + unsigned int frame_id; | |
15 | + /* pc -- address of the next instruction to be "executed". */ | |
16 | + unsigned long pc; | |
17 | + /* predecr_pc -- address that will be reported by a breakpoint. | |
18 | + These two are different on some targets, see gdb source code, | |
19 | + DECR_PC_AFTER_BREAK. */ | |
20 | + unsigned long predecr_pc; | |
21 | + unsigned long eventpos; | |
22 | + unsigned long gpos; | |
23 | +} StopFrame; | |
24 | + | |
25 | +StopFrame *stopframe; | |
26 | +int last_cached_frame = 0; | |
27 | +int framecache_size = 0; | |
28 | +int cur_frame = -1; | |
29 | + | |
30 | +#include "gdbfreeplay.h" | |
31 | + | |
32 | +/* | |
33 | + * scan_gdbreplay_file | |
34 | + * | |
35 | + * Make a pass through the replay log, noting the positions | |
36 | + * of the stop events and certain packets within them. | |
37 | + * | |
38 | + * Returns: FAIL, PASS. | |
39 | + */ | |
40 | + | |
41 | +#define INBUF_SIZE 4096 | |
42 | +static char inbuf[INBUF_SIZE]; | |
43 | + | |
44 | +#define GDB_LINEBUF_SIZE 4096 | |
45 | +static char gdb_linebuf[GDB_LINEBUF_SIZE]; | |
46 | + | |
47 | +static enum successcode | |
48 | +scan_gdbreplay_file (FILE *infile) | |
49 | +{ | |
50 | + /* Make a pass over the entire file -- cache the record positions. */ | |
51 | + char *line, *p; | |
52 | + int next_id = 0; | |
53 | + unsigned long nextpos; | |
54 | + unsigned long GPC; | |
55 | + | |
56 | + /* First skip empty lines. */ | |
57 | + do { | |
58 | + nextpos = ftell (infile); | |
59 | + line = fgets (inbuf, sizeof (inbuf), infile); | |
60 | + } while (line != NULL && inbuf[0] == '\n'); | |
61 | + | |
62 | + if (line == NULL) | |
63 | + return FAIL; /* End of file, nothing there. */ | |
64 | + | |
65 | + /* Now for the main loop. | |
66 | + * | |
67 | + * Scan the rest of the file, recording stop events etc. | |
68 | + */ | |
69 | + do { | |
70 | + /* 'g' packet message? */ | |
71 | + if (strstr (line, "$g#67") != NULL) | |
72 | + { | |
73 | + /* Record position of 'g' packet (next line). */ | |
74 | + stopframe[last_cached_frame].gpos = ftell (infile); | |
75 | + | |
76 | + /* See if we need to grab the PC from this packet. */ | |
77 | + if (stopframe[last_cached_frame].pc == 0 || | |
78 | + stopframe[last_cached_frame].pc == (unsigned long) -1) | |
79 | + stopframe[last_cached_frame].pc = target_pc_from_g (infile); | |
80 | + | |
81 | + if (verbose) | |
82 | + fprintf (stdout, "Found 'g' packet at %u\n", | |
83 | + stopframe[last_cached_frame].gpos); | |
84 | + } | |
85 | + | |
86 | + /* Reset PC after breakpoint? */ | |
87 | + else if ((p = strstr (line, "$G")) != NULL) | |
88 | + { | |
89 | + GPC = target_pc_from_G (p); | |
90 | + if (stopframe[last_cached_frame].pc == 0 || | |
91 | + stopframe[last_cached_frame].pc == (unsigned long) -1) | |
92 | + { | |
93 | + /* Unlikely, but if we need to, we can just grab this PC. */ | |
94 | + stopframe[last_cached_frame].pc = GPC; | |
95 | + } | |
96 | + else if (stopframe[last_cached_frame].pc == GPC + 1) | |
97 | + { | |
98 | + /* OK, this is gdb decrementing the PC after a breakpoint. */ | |
99 | + stopframe[last_cached_frame].predecr_pc = | |
100 | + stopframe[last_cached_frame].pc; | |
101 | + stopframe[last_cached_frame].pc = GPC; | |
102 | + } | |
103 | + } | |
104 | + | |
105 | + /* Stop event message? */ | |
106 | + else if ((p = strstr (line, "$T")) != NULL || | |
107 | + (p = strstr (line, "$S")) != NULL) | |
108 | + { | |
109 | + if (last_cached_frame >= framecache_size - 1) | |
110 | + { | |
111 | + /* Time to grow the frame cache buffer. */ | |
112 | + framecache_size += 2048; | |
113 | + if (verbose) | |
114 | + fprintf (stdout, "Growing frame cache to %d\n", | |
115 | + framecache_size); | |
116 | + stopframe = realloc (stopframe, | |
117 | + framecache_size * sizeof (stopframe[0])); | |
118 | + } | |
119 | + /* Special case: 0th frame. | |
120 | + | |
121 | + On the first pass, the FIRST time I see a stop event | |
122 | + for the 0th frame, I will not increment the stopframe | |
123 | + index. | |
124 | + | |
125 | + The 0th frame is special, in that its "stop event" | |
126 | + happens not as a result of a real stop event, but | |
127 | + as a result of gdb sending the "?" query. Therefore | |
128 | + the apparent event falls in the middle of the frame. | |
129 | + */ | |
130 | + | |
131 | + if (last_cached_frame != 0 || | |
132 | + stopframe [last_cached_frame].eventpos != 0) | |
133 | + last_cached_frame++; | |
134 | + | |
135 | + /* Since we now have a known frame, default to using it. */ | |
136 | + cur_frame = 0; | |
137 | + stopframe[last_cached_frame].eventpos = nextpos + p - line; | |
138 | + | |
139 | + if (p[1] == 'T') | |
140 | + stopframe[last_cached_frame].pc = target_pc_from_T (p); | |
141 | + | |
142 | + if (verbose) | |
143 | + fprintf (stdout, "Record event pos at %u\n", | |
144 | + stopframe [last_cached_frame].eventpos); | |
145 | + } | |
146 | + nextpos = ftell (infile); | |
147 | + line = fgets (inbuf, sizeof (inbuf), infile); | |
148 | + } while (line != NULL); | |
149 | + | |
150 | + return PASS; | |
151 | +} | |
152 | + | |
153 | +/* | |
154 | + * gdbfreeplay_open | |
155 | + * | |
156 | + * Open the gdbfreeplay "target", which mainly means opening | |
157 | + * the gdbreplay log file. | |
158 | + * | |
159 | + * Returns: FAIL, PASS. | |
160 | + */ | |
161 | + | |
162 | +static FILE *replay_file; | |
163 | + | |
164 | +enum successcode | |
165 | +gdbfreeplay_open (char *filename) | |
166 | +{ | |
167 | + if ((replay_file = fopen (filename, "r")) == NULL) | |
168 | + { | |
169 | + fprintf (stderr, "GDBFREEPLAY: could not open file %s for input.\n", | |
170 | + filename); | |
171 | + return FAIL; | |
172 | + } | |
173 | + | |
174 | + return scan_gdbreplay_file (replay_file); | |
175 | +} | |
176 | + | |
177 | +/* | |
178 | + * gdbreadline | |
179 | + * | |
180 | + * Read a line of input from gdb (from the socket). | |
181 | + */ | |
182 | + | |
183 | +static char * | |
184 | +gdbreadline (int fd) | |
185 | +{ | |
186 | + char *p = gdb_linebuf; | |
187 | + int saw_dollar = 0; | |
188 | + int saw_pound = 0; | |
189 | + int cksum_chars = 0; | |
190 | + | |
191 | + while (p < gdb_linebuf + sizeof (gdb_linebuf) - 1) | |
192 | + { | |
193 | + read (fd, p, 1); | |
194 | + p[1] = '\0'; | |
195 | + switch (p[0]) { | |
196 | + case '+': | |
197 | + /* Receive "ack" from gdb. | |
198 | + Assuming it is first char in buffer, ignore. */ | |
199 | + if (p - gdb_linebuf != 0) | |
200 | + fprintf (stdout, "gdbreadline: '+' ack in middle of line?\n"); | |
201 | + continue; | |
202 | + break; | |
203 | + case '-': | |
204 | + if (p - gdb_linebuf == 0) | |
205 | + { | |
206 | + /* Receive "nack" from gdb. FIXME what to do? */ | |
207 | + return gdb_linebuf; | |
208 | + } | |
209 | + goto default_label; | |
210 | + break; | |
211 | + case '$': | |
212 | + if (saw_dollar) | |
213 | + fprintf (stdout, "gdbreadline: two '$' in one line:\n\t'%s'\n", | |
214 | + gdb_linebuf); | |
215 | + | |
216 | + saw_dollar = 1; | |
217 | + break; | |
218 | + case '#': | |
219 | + if (!saw_dollar) | |
220 | + fprintf (stdout, "gdbreadline: '#' before '$':\n\t'%s'\n", | |
221 | + gdb_linebuf); | |
222 | + | |
223 | + if (saw_pound) | |
224 | + fprintf (stdout, "gdbreadline: two '#' in one line:\n\t'%s'\n", | |
225 | + gdb_linebuf); | |
226 | + | |
227 | + saw_pound = 1; | |
228 | + break; | |
229 | + default: | |
230 | + default_label: | |
231 | + if (saw_pound) | |
232 | + cksum_chars++; | |
233 | + if (cksum_chars >= 2) | |
234 | + { | |
235 | + /* Got a complete message. */ | |
236 | + return gdb_linebuf; | |
237 | + } | |
238 | + break; | |
239 | + } | |
240 | + ++p; | |
241 | + } | |
242 | + /* Shouldn't get here except for buffer overflow. */ | |
243 | + fprintf (stdout, "gdbreadline: buffer overflow?\n"); | |
244 | + return gdb_linebuf; | |
245 | +} | |
246 | + | |
247 | +/* | |
248 | + * find_reply | |
249 | + * | |
250 | + * Given a filepos pointing to just after a given gdb request, | |
251 | + * find and return a char * containing the reply to that request. | |
252 | + * | |
253 | + * Return NULL on failure. | |
254 | + */ | |
255 | + | |
256 | +static char * | |
257 | +find_reply (FILE *logfile, long filepos) | |
258 | +{ | |
259 | + char *line; | |
260 | + | |
261 | + if (filepos == -1) | |
262 | + return NULL; | |
263 | + | |
264 | + /* Position the file (it's probably already there...) */ | |
265 | + fseek (logfile, filepos, SEEK_SET); | |
266 | + /* In principle, the next input line should contain the reply. */ | |
267 | + line = fgets (inbuf, sizeof (inbuf), logfile); | |
268 | + | |
269 | + return line; | |
270 | +} | |
271 | + | |
272 | +/* | |
273 | + * frame_find_request | |
274 | + * | |
275 | + * Given a request from gdb, in the form of a string, | |
276 | + * find an identical request within the current frame | |
277 | + * of the gdbreplay log file. | |
278 | + * | |
279 | + * Return a file position immediately following the request. | |
280 | + */ | |
281 | + | |
282 | +static long | |
283 | +frame_find_request (FILE *logfile, char *request) | |
284 | +{ | |
285 | + long curpos; | |
286 | + char *line; | |
287 | + | |
288 | + if (cur_frame >= 0) | |
289 | + { | |
290 | + /* Position the input at the beginning of the frame. | |
291 | + SPECIAL CASE: for frame zero, go to the beginning of the log. | |
292 | + This is so we can find gdb requests that preceed the first | |
293 | + stop-event message. | |
294 | + */ | |
295 | + if (cur_frame == 0) | |
296 | + curpos = 0; | |
297 | + else | |
298 | + curpos = stopframe[cur_frame].eventpos; | |
299 | + fseek (logfile, curpos, SEEK_SET); | |
300 | + /* Now search for a matching request. */ | |
301 | + while (curpos < stopframe[cur_frame + 1].eventpos) | |
302 | + { | |
303 | + line = fgets (inbuf, sizeof (inbuf), logfile); | |
304 | + /* End of input? */ | |
305 | + if (line == NULL) | |
306 | + break; | |
307 | + curpos = ftell (logfile); | |
308 | + if (strstr (line, request) != NULL) | |
309 | + { | |
310 | + /* Matching request found. Return position. */ | |
311 | + return curpos; | |
312 | + } | |
313 | + } | |
314 | + } | |
315 | + | |
316 | + /* Not found. */ | |
317 | + return -1; | |
318 | +} | |
319 | + | |
320 | +/* | |
321 | + * gdb_ack | |
322 | + * | |
323 | + * Send an ack to gdb. | |
324 | + * Returns void. | |
325 | + */ | |
326 | + | |
327 | +static void | |
328 | +gdb_ack (int fd) | |
329 | +{ | |
330 | + static const char *ack = "+"; | |
331 | + write (fd, ack, 1); | |
332 | +} | |
333 | + | |
334 | +/* | |
335 | + * hex_to_int | |
336 | + */ | |
337 | + | |
338 | +int | |
339 | +hex_to_int (int c) | |
340 | +{ | |
341 | + if (c >= '0' && c <= '9') | |
342 | + return c - '0'; | |
343 | + else if (c >= 'a' && c <= 'f') | |
344 | + return c - 'a' + 10; | |
345 | + else if (c >= 'A' && c <= 'F') | |
346 | + return c - 'F' + 10; | |
347 | + else | |
348 | + return 0; | |
349 | +} | |
350 | + | |
351 | +/* | |
352 | + * int_to_hex | |
353 | + * | |
354 | + * (lifted from gdb) | |
355 | + */ | |
356 | + | |
357 | +int | |
358 | +int_to_hex (int nib) | |
359 | +{ | |
360 | + if (nib < 10) | |
361 | + return '0' + nib; | |
362 | + else | |
363 | + return 'a' + nib - 10; | |
364 | +} | |
365 | + | |
366 | +#if 0 | |
367 | +/* | |
368 | + * tohex | |
369 | + * | |
370 | + * Borrowed from gdbreplay. | |
371 | + */ | |
372 | + | |
373 | +static int | |
374 | +tohex (int ch) | |
375 | +{ | |
376 | + if (ch >= '0' && ch <= '9') | |
377 | + { | |
378 | + return (ch - '0'); | |
379 | + } | |
380 | + if (ch >= 'A' && ch <= 'F') | |
381 | + { | |
382 | + return (ch - 'A' + 10); | |
383 | + } | |
384 | + if (ch >= 'a' && ch <= 'f') | |
385 | + { | |
386 | + return (ch - 'a' + 10); | |
387 | + } | |
388 | + fprintf (stderr, "\nInvalid hex digit '%c'\n", ch); | |
389 | + fflush (stderr); | |
390 | + exit (1); | |
391 | +} | |
392 | +#endif | |
393 | + | |
394 | +#define EOI 0xdeadbeef | |
395 | + | |
396 | +/* | |
397 | + * convert_logchar | |
398 | + * | |
399 | + * Adapted from gdbreplay. | |
400 | + * Some characters in the log are escaped, and | |
401 | + * need to be translated before being sent back | |
402 | + * to gdb. | |
403 | + * | |
404 | + * Returns a de-escaped char. | |
405 | + */ | |
406 | + | |
407 | +static int | |
408 | +convert_logchar (char **logp) | |
409 | +{ | |
410 | + int ch, ch2; | |
411 | + | |
412 | + ch = *(*logp)++; | |
413 | + switch (ch) | |
414 | + { | |
415 | + case '\0': | |
416 | + case '\n': | |
417 | + ch = EOI; /* End of input (out of band). */ | |
418 | + break; | |
419 | + case '\\': | |
420 | + ch = *(*logp)++; | |
421 | + switch (ch) | |
422 | + { | |
423 | + case '\\': | |
424 | + break; | |
425 | + case 'b': | |
426 | + ch = '\b'; | |
427 | + break; | |
428 | + case 'f': | |
429 | + ch = '\f'; | |
430 | + break; | |
431 | + case 'n': | |
432 | + ch = '\n'; | |
433 | + break; | |
434 | + case 'r': | |
435 | + ch = '\r'; | |
436 | + break; | |
437 | + case 't': | |
438 | + ch = '\t'; | |
439 | + break; | |
440 | + case 'v': | |
441 | + ch = '\v'; | |
442 | + break; | |
443 | + case 'x': | |
444 | + ch2 = *(*logp)++; | |
445 | + ch = hex_to_int (ch2) << 4; | |
446 | + ch2 = *(*logp)++; | |
447 | + ch |= hex_to_int (ch2); | |
448 | + break; | |
449 | + default: | |
450 | + /* Treat any other char as just itself */ | |
451 | + break; | |
452 | + } | |
453 | + default: | |
454 | + break; | |
455 | + } | |
456 | + return (ch); | |
457 | +} | |
458 | + | |
459 | +/* | |
460 | + * gdbwriteline | |
461 | + * | |
462 | + * Send a line of data to gdb, stripped of any | |
463 | + * prefix or suffix stuff. | |
464 | + * | |
465 | + * Prefix is anything preceeding a '$'. | |
466 | + * Suffix is anything following a "#xx" (including any newlines). | |
467 | + * | |
468 | + * Returns void. | |
469 | + */ | |
470 | + | |
471 | +static void | |
472 | +gdbwriteline (int fd, char *line) | |
473 | +{ | |
474 | + char *end; | |
475 | + int len, ich; | |
476 | + char ch; | |
477 | + | |
478 | + if (line) | |
479 | + { | |
480 | + /* Strip prefix. */ | |
481 | + while (*line && *line != '$') | |
482 | + line++; | |
483 | + if (*line) | |
484 | + { | |
485 | + /* Strip suffix. */ | |
486 | + end = strchr (line, '#'); | |
487 | + if (end && *end) | |
488 | + { | |
489 | + /* Got line, send it. */ | |
490 | + end[3] = '\0'; | |
491 | + while ((ich = convert_logchar (&line)) != EOI) | |
492 | + { | |
493 | + ch = ich; | |
494 | + write (fd, &ch, 1); | |
495 | + } | |
496 | + } | |
497 | + } | |
498 | + } | |
499 | +} | |
500 | + | |
501 | +/* | |
502 | + * stopframe_signal | |
503 | + * | |
504 | + * Return the signal number for which the stopframe stopped. | |
505 | + * This is a brute force implementation. Probably should plan | |
506 | + * on caching the value to speed things up. | |
507 | + */ | |
508 | + | |
509 | +static int | |
510 | +stopframe_signal (FILE *infile, int id) | |
511 | +{ | |
512 | + char *line, *p; | |
513 | + int sig; | |
514 | + | |
515 | + fseek (infile, stopframe[id].eventpos, SEEK_SET); | |
516 | + line = fgets (inbuf, sizeof (inbuf), infile); | |
517 | + | |
518 | + if ((p = strstr (line, "$T")) != NULL || | |
519 | + (p = strstr (line, "$S")) != NULL) | |
520 | + { | |
521 | + /* Signal value is two ascii/hex bytes following "$S" or "$T". */ | |
522 | + sig = hex_to_int (p[2]) << 8 + hex_to_int (p[3]); | |
523 | + return sig; | |
524 | + } | |
525 | + return 0; | |
526 | +} | |
527 | + | |
528 | +/* | |
529 | + * freeplay_find_event | |
530 | + * | |
531 | + * Scan (forward or backward) thru the stop events, | |
532 | + * looking for a reason to stop now (assuming gdb has | |
533 | + * asked us to continue). | |
534 | + * | |
535 | + * Returns event frame index, or -1 for failure. | |
536 | + */ | |
537 | + | |
538 | +static int | |
539 | +freeplay_find_event (FILE *infile, int start, enum direction_code direction) | |
540 | +{ | |
541 | + int i; | |
542 | + int signum; | |
543 | + | |
544 | + /* Here's a strange loop for you: goes forward or backward. */ | |
545 | + for (i = start; | |
546 | + i >= 0 && i < last_cached_frame; | |
547 | + direction == DIR_FORWARD ? i++ : i--) | |
548 | + { | |
549 | + signum = stopframe_signal (infile, i); | |
550 | + /* FIXME we need some modal handling for SIGTRAP. */ | |
551 | + if (signum != 0 && signum != 5) | |
552 | + { | |
553 | + /* Here's an event we can stop at, regardless of breakpoints. */ | |
554 | + /* FIXME: there is some signal-ignoring stuff to be looked at. */ | |
555 | + return i; | |
556 | + } | |
557 | + else if (remote_breakpoint_here_p (SOFTWARE_BP, stopframe[i].pc) == PASS) | |
558 | + { | |
559 | + /* Found a breakpoint. | |
560 | + FIXME need some DECR_PC_AFTER_BREAK handling. */ | |
561 | + return i; | |
562 | + } | |
563 | + } | |
564 | + /* Found no reason to stop. */ | |
565 | + return -1; | |
566 | +} | |
567 | + | |
568 | +/* | |
569 | + * add_checksum | |
570 | + * | |
571 | + * Compute the checksum for a string that will go to gdb, | |
572 | + * and concatenate it onto the string. | |
573 | + * | |
574 | + * Returns input string modified in place (better have room!) | |
575 | + */ | |
576 | + | |
577 | +char * | |
578 | +add_checksum (char *inbuf) | |
579 | +{ | |
580 | + int cksum = 0; | |
581 | + char *p = inbuf; | |
582 | + | |
583 | + /* If the string doesn't start with a '$', it's broken. */ | |
584 | + if (*p++ != '$') | |
585 | + return inbuf; | |
586 | + | |
587 | + /* Now add up the cksum. */ | |
588 | + while (*p) | |
589 | + { | |
590 | + /* If there's already a '#', it doesn't count toward the cksum. | |
591 | + Stop there. */ | |
592 | + if (*p == '#') | |
593 | + break; | |
594 | + cksum += *p++; | |
595 | + } | |
596 | + | |
597 | + /* Add a '#' at the end, even if it's already there, and | |
598 | + overwrite any old checksum that may be there already. */ | |
599 | + *p++ = '#'; | |
600 | + *p++ = int_to_hex ((cksum >> 4) & 0xf); | |
601 | + *p++ = int_to_hex (cksum & 0xf); | |
602 | + *p++ = '\0'; | |
603 | + | |
604 | + /* Done. */ | |
605 | + return inbuf; | |
606 | +} | |
607 | + | |
608 | +/* | |
609 | + * handle_special_case | |
610 | + * | |
611 | + * Handle these requests locally, rather than through the replay buffer. | |
612 | + * | |
613 | + * Returns: a string reply for gdb. | |
614 | + */ | |
615 | + | |
616 | +static char OK[8] = "$OK#9a"; | |
617 | +static char EMPTY[8] = "$#00"; | |
618 | +static char STOP[8] = "$S00#44"; | |
619 | +static char E01[8] = "$E01#a6"; | |
620 | + | |
621 | +static char * | |
622 | +handle_special_case (FILE *infile, int fd, char *request) | |
623 | +{ | |
624 | + unsigned long addr; | |
625 | + unsigned long len; | |
626 | + int next_event_frame; | |
627 | + char *p; | |
628 | + | |
629 | + /* Handle 'k' (kill) request by exiting. */ | |
630 | + if (strstr (request, "$k#6b") != NULL) | |
631 | + { | |
632 | + /* Well, to PROPERLY honor the 'kill' request, | |
633 | + we need to actually shoot ourselves in the head... */ | |
634 | + gdb_ack (fd); | |
635 | + fprintf (stdout, "gdbfreeplay -- killed by gdb.\n"); | |
636 | + exit (0); | |
637 | + } | |
638 | + | |
639 | + /* Handle 'D' (detach) request by exiting. | |
640 | + FIXME might want to stay alive and offer the socket again. */ | |
641 | + if (strstr (request, "$D#44") != NULL) | |
642 | + { | |
643 | + /* OK, we're not yet set up to close and re-open the socket, | |
644 | + and a new gdb does not seem able to re-attach, so for now | |
645 | + let's actually quit. */ | |
646 | + gdb_ack (fd); | |
647 | + gdbwriteline (fd, OK); | |
648 | + fprintf (stdout, "gdbfreeplay -- gdb has detached. Exiting.\n"); | |
649 | + exit (0); | |
650 | + } | |
651 | + | |
652 | + /* Handle "monitor verbose on". */ | |
653 | + if (strstr (request, "$qRcmd,766572626f7365206f6e#d6") != NULL) | |
654 | + { | |
655 | + verbose = 1; | |
656 | + return OK; | |
657 | + } | |
658 | + | |
659 | + /* Handle "monitor verbose off". */ | |
660 | + if (strstr (request, "$qRcmd,766572626f7365206f6666#13") != NULL) | |
661 | + { | |
662 | + verbose = 0; | |
663 | + return OK; | |
664 | + } | |
665 | + | |
666 | + /* Handle 's' (step) by advancing the cur_frame index. */ | |
667 | + if (strstr (request, "$s#73") != NULL) | |
668 | + { | |
669 | + step_label: | |
670 | + if (cur_frame < last_cached_frame) | |
671 | + cur_frame++; | |
672 | + | |
673 | + if (stopframe[cur_frame].eventpos != 0) | |
674 | + { | |
675 | + return find_reply (infile, | |
676 | + stopframe[cur_frame].eventpos); | |
677 | + } | |
678 | + else | |
679 | + { | |
680 | + /* Not sure how this could even happen, but... */ | |
681 | + return STOP; | |
682 | + } | |
683 | + } | |
684 | + | |
685 | + /* Handle 'bs' (reverse stepi) by decrementing the cur_frame index. */ | |
686 | + if (strstr (request, "$bs#d5") != NULL) | |
687 | + { | |
688 | + if (cur_frame > 0) | |
689 | + cur_frame--; | |
690 | + | |
691 | + if (stopframe[cur_frame].eventpos != 0) | |
692 | + { | |
693 | + return find_reply (infile, | |
694 | + stopframe[cur_frame].eventpos); | |
695 | + } | |
696 | + else | |
697 | + { | |
698 | + /* Not sure how this could even happen, but... */ | |
699 | + return STOP; | |
700 | + } | |
701 | + } | |
702 | + | |
703 | + /* Handle 'c' (continue) by searching the cache for a stop event. */ | |
704 | + if (strstr (request, "$c#63") != NULL) | |
705 | + { | |
706 | + next_event_frame = freeplay_find_event (infile, cur_frame, DIR_FORWARD); | |
707 | + if (next_event_frame != -1) | |
708 | + { | |
709 | + /* Got a stop event. Make it the current frame, and tell gdb. | |
710 | + */ | |
711 | + cur_frame = next_event_frame; | |
712 | +#if 0 | |
713 | + /* FIXME: we want to do something about DECR_PC_AFTER_BREAK. */ | |
714 | + return find_reply (infile, | |
715 | + stopframe[cur_frame].eventpos); | |
716 | +#endif | |
717 | + } | |
718 | + else | |
719 | + { | |
720 | + /* FIXME now what? Go to final frame? */ | |
721 | + cur_frame = last_cached_frame - 1; | |
722 | +#if 0 | |
723 | + /* FIXME: we want to do something about DECR_PC_AFTER_BREAK. */ | |
724 | + return find_reply (infile, | |
725 | + stopframe[cur_frame].eventpos); | |
726 | +#endif | |
727 | + } | |
728 | +#if 1 | |
729 | + /* Find the original event message for this stop event. */ | |
730 | + fseek (infile, stopframe[cur_frame].eventpos, SEEK_SET); | |
731 | + fgets (inbuf, sizeof (inbuf), infile); | |
732 | + /* If it's a "$T", give the target a chance to re-compose it | |
733 | + (possibly allowing for DECR_PC_AFTER_BREAK). */ | |
734 | + if ((p = strstr (inbuf, "$T")) != NULL) | |
735 | + return add_checksum (target_compose_T_packet (p)); | |
736 | + /* If it's a "$S", just return it (FIXME?) */ | |
737 | + else | |
738 | + return &inbuf[0]; | |
739 | +#endif | |
740 | + } | |
741 | + | |
742 | + /* Handle 'bc' (revese continue) by searching the cache for a stop event. */ | |
743 | + if (strstr (request, "$bc#c5") != NULL) | |
744 | + { | |
745 | + next_event_frame = freeplay_find_event (infile, cur_frame, DIR_BACKWARD); | |
746 | + if (next_event_frame != -1) | |
747 | + { | |
748 | + /* Got a stop event. Make it the current frame, and tell gdb. | |
749 | + */ | |
750 | + cur_frame = next_event_frame; | |
751 | +#if 0 | |
752 | + /* FIXME: we want to do something about DECR_PC_AFTER_BREAK. */ | |
753 | + return find_reply (infile, | |
754 | + stopframe[cur_frame].eventpos); | |
755 | +#endif | |
756 | + } | |
757 | + else | |
758 | + { | |
759 | + /* FIXME now what? Go to initial frame? */ | |
760 | + cur_frame = 0; | |
761 | +#if 0 | |
762 | + /* FIXME: we want to do something about DECR_PC_AFTER_BREAK. */ | |
763 | + return find_reply (infile, | |
764 | + stopframe[cur_frame].eventpos); | |
765 | +#endif | |
766 | + } | |
767 | +#if 1 | |
768 | + /* Find the original event message for this stop event. */ | |
769 | + fseek (infile, stopframe[cur_frame].eventpos, SEEK_SET); | |
770 | + fgets (inbuf, sizeof (inbuf), infile); | |
771 | + /* If it's a "$T", give the target a chance to re-compose it | |
772 | + (possibly allowing for DECR_PC_AFTER_BREAK). */ | |
773 | + if ((p = strstr (inbuf, "$T")) != NULL) | |
774 | + return add_checksum (target_compose_T_packet (p)); | |
775 | + /* If it's a "$S", just return it (FIXME?) */ | |
776 | + else | |
777 | + return &inbuf[0]; | |
778 | +#endif | |
779 | + } | |
780 | + | |
781 | + /* Handle Z0 set breakpoint. */ | |
782 | + if ((p = strstr (request, "$Z0")) != NULL) | |
783 | + { | |
784 | + if (p[3] == ',') | |
785 | + { | |
786 | + addr = strtoul (p + 4, &p, 16); | |
787 | + if (p[0] == ',') | |
788 | + { | |
789 | + len = strtoul (p + 1, NULL, 16); | |
790 | + remote_set_breakpoint (SOFTWARE_BP, addr, len); | |
791 | + return OK; | |
792 | + } | |
793 | + } | |
794 | + } | |
795 | + | |
796 | + /* Handle z0 remove breakpoint. */ | |
797 | + if ((p = strstr (request, "$z0")) != NULL) | |
798 | + { | |
799 | + if (p[3] == ',') | |
800 | + { | |
801 | + addr = strtoul (p + 4, &p, 16); | |
802 | + if (p[0] == ',') | |
803 | + { | |
804 | + len = strtoul (p + 1, NULL, 16); | |
805 | + remote_remove_breakpoint (SOFTWARE_BP, addr, len); | |
806 | + return OK; | |
807 | + } | |
808 | + } | |
809 | + } | |
810 | + | |
811 | + /* Handle "vCont" by saying we can't do it. | |
812 | + FIXME we could do it, and moreover 'fallbacks' could handle this. */ | |
813 | + if (strstr (request, "$vCont") != 0) | |
814 | + return EMPTY; | |
815 | + | |
816 | + /* TODO: vCont, M, X, G, P/p, tfind... */ | |
817 | + | |
818 | + /* Let the replay buffer handle it. */ | |
819 | + return NULL; | |
820 | +} | |
821 | + | |
822 | +/* | |
823 | + * fallbacks | |
824 | + * | |
825 | + * Attempt to handle requests that were not handled otherwise. | |
826 | + * Returns: char * or NULL. | |
827 | + */ | |
828 | + | |
829 | +static char * | |
830 | +fallbacks (FILE *infile, int fd, char *request) | |
831 | +{ | |
832 | + /* Handle "Hc0" request. */ | |
833 | + if (strstr (request, "$Hc0#db") != NULL) | |
834 | + { | |
835 | + /* Debugger just wants to set the "continue thread" to zero. | |
836 | + Don't know why this isn't in the replay cache, but it's | |
837 | + harmless, just say OK. */ | |
838 | + if (verbose) | |
839 | + fprintf (stdout, "fallbacks: absorbing 'Hc0'\n"); | |
840 | + return OK; | |
841 | + } | |
842 | + /* Handle un-cached memory request (if not handled upstream). */ | |
843 | + if (strstr (request, "$m") != NULL) | |
844 | + { | |
845 | + /* FIXME this need not happen so often, | |
846 | + once we do a better job of simulating memory. */ | |
847 | + if (verbose) | |
848 | + fprintf (stdout, "fallbacks: failing memory request '%s'.\n", | |
849 | + request); | |
850 | + return E01; | |
851 | + } | |
852 | + /* Handle P/p requests (if they weren't handled upstream). */ | |
853 | + if (strstr (request, "$p") != NULL || | |
854 | + strstr (request, "$p") != NULL) | |
855 | + { | |
856 | + if (verbose) | |
857 | + fprintf (stdout, "fallbacks: absorbing P/p request.\n"); | |
858 | + return EMPTY; /* Tell gdb we don't know that one. */ | |
859 | + } | |
860 | + | |
861 | + /* Default for any other un-handled request -- return empty string. */ | |
862 | + if (verbose) | |
863 | + fprintf (stdout, "fallbacks: absorbing unknown request '%s'.\n", | |
864 | + request); | |
865 | + return EMPTY; | |
866 | +} | |
867 | + | |
868 | +/* | |
869 | + * gdbfreeplay | |
870 | + * | |
871 | + * This is the function that manages the interaction with gdb. | |
872 | + */ | |
873 | + | |
874 | +void | |
875 | +gdbfreeplay (int fd) | |
876 | +{ | |
877 | + char *request; | |
878 | + char *reply; | |
879 | + | |
880 | + /* Process requests from gdb. */ | |
881 | + while (1) | |
882 | + { | |
883 | + /* Read next request from gdb. */ | |
884 | + request = gdbreadline (fd); | |
885 | + if (verbose) | |
886 | + fprintf (stdout, "gdb request: %s\n", request); | |
887 | + | |
888 | + /* FIXME -- this is where we should handle special cases, including: | |
889 | + -- kill $k#6b | |
890 | + -- detach $D#44 | |
891 | + -- step $s#73 | |
892 | + -- backstep | |
893 | + -- continue $c#63 | |
894 | + -- back-cont | |
895 | + -- breakpoint $Z0... | |
896 | + -- vCont | |
897 | + -- M | |
898 | + -- X | |
899 | + -- G | |
900 | + -- P/p | |
901 | + -- tfind | |
902 | + */ | |
903 | + | |
904 | + reply = handle_special_case (replay_file, fd, request); | |
905 | + | |
906 | + if (reply == NULL) | |
907 | + { | |
908 | + /* Now see if we have a matching request in the current frame. */ | |
909 | + reply = find_reply (replay_file, | |
910 | + frame_find_request (replay_file, request)); | |
911 | + } | |
912 | + | |
913 | + if (reply == NULL) | |
914 | + { | |
915 | + /* Found no matching request in the current frame. | |
916 | + Do fall-backs and last resorts. */ | |
917 | + reply = fallbacks (replay_file, fd, request); | |
918 | + } | |
919 | + | |
920 | + if (verbose) | |
921 | + fprintf (stdout, "freeplay reply: %s\n", | |
922 | + reply == NULL ? "<not found>" : reply); | |
923 | + | |
924 | + if (reply) | |
925 | + { | |
926 | + gdb_ack (fd); | |
927 | + gdbwriteline (fd, reply); | |
928 | + } | |
929 | + else | |
930 | + { | |
931 | + /* FIXME -- gotta say something back... */ | |
932 | + } | |
933 | + } | |
934 | +} |
@@ -0,0 +1,311 @@ | ||
1 | +/* | |
2 | + * gdb-freeplay | |
3 | + * | |
4 | + * Freely replay a remote debug session logfile for GDB. | |
5 | + * | |
6 | + * Parts of gdb-freeplay are adapted from gdbreplay, | |
7 | + * by Stu Grossman and Fred Fish. | |
8 | + */ | |
9 | + | |
10 | +#include "config.h" | |
11 | +#include <stdio.h> | |
12 | +#if HAVE_SYS_FILE_H | |
13 | +#include <sys/file.h> | |
14 | +#endif | |
15 | +#if HAVE_SIGNAL_H | |
16 | +#include <signal.h> | |
17 | +#endif | |
18 | +#include <ctype.h> | |
19 | +#if HAVE_FCNTL_H | |
20 | +#include <fcntl.h> | |
21 | +#endif | |
22 | +#if HAVE_ERRNO_H | |
23 | +#include <errno.h> | |
24 | +#endif | |
25 | +#ifdef HAVE_STDLIB_H | |
26 | +#include <stdlib.h> | |
27 | +#endif | |
28 | +#ifdef HAVE_STRING_H | |
29 | +#include <string.h> | |
30 | +#endif | |
31 | +#ifdef HAVE_UNISTD_H | |
32 | +#include <unistd.h> | |
33 | +#endif | |
34 | +#ifdef HAVE_NETINET_IN_H | |
35 | +#include <netinet/in.h> | |
36 | +#endif | |
37 | +#ifdef HAVE_SYS_SOCKET_H | |
38 | +#include <sys/socket.h> | |
39 | +#endif | |
40 | +#if HAVE_NETDB_H | |
41 | +#include <netdb.h> | |
42 | +#endif | |
43 | +#if HAVE_NETINET_TCP_H | |
44 | +#include <netinet/tcp.h> | |
45 | +#endif | |
46 | +#if HAVE_MALLOC_H | |
47 | +#include <malloc.h> | |
48 | +#endif | |
49 | + | |
50 | +#if USE_WIN32API | |
51 | +#include <winsock.h> | |
52 | +#endif | |
53 | + | |
54 | +#ifndef HAVE_SOCKLEN_T | |
55 | +typedef int socklen_t; | |
56 | +#endif | |
57 | + | |
58 | +/* Version information, from version.c. */ | |
59 | +extern const char version[]; | |
60 | +extern const char host_name[]; | |
61 | + | |
62 | +/* Print the system error message for errno, and also mention STRING | |
63 | + as the file name for which the error was encountered. | |
64 | + Then return to command level. */ | |
65 | + | |
66 | +static void | |
67 | +perror_with_name (char *string) | |
68 | +{ | |
69 | +#ifndef STDC_HEADERS | |
70 | + extern int errno; | |
71 | +#endif | |
72 | + const char *err; | |
73 | + char *combined; | |
74 | + | |
75 | + err = strerror (errno); | |
76 | + if (err == NULL) | |
77 | + err = "unknown error"; | |
78 | + | |
79 | + combined = (char *) alloca (strlen (err) + strlen (string) + 3); | |
80 | + strcpy (combined, string); | |
81 | + strcat (combined, ": "); | |
82 | + strcat (combined, err); | |
83 | + fprintf (stderr, "\n%s.\n", combined); | |
84 | + fflush (stderr); | |
85 | + exit (1); | |
86 | +} | |
87 | + | |
88 | +/* | |
89 | + * remote_close | |
90 | + * | |
91 | + * Close the gdb socket. | |
92 | + */ | |
93 | + | |
94 | +static void | |
95 | +remote_close (int remote_desc) | |
96 | +{ | |
97 | +#ifdef USE_WIN32API | |
98 | + closesocket (remote_desc); | |
99 | +#else | |
100 | + close (remote_desc); | |
101 | +#endif | |
102 | +} | |
103 | + | |
104 | +/* | |
105 | + * remote_open | |
106 | + * | |
107 | + * Open a connection to a remote debugger (gdb). | |
108 | + * NAME is the filename used for communication. | |
109 | + * | |
110 | + * Returns: file descriptor. | |
111 | + * Does not return in case of error. | |
112 | + */ | |
113 | + | |
114 | +static int | |
115 | +remote_open (char *name) | |
116 | +{ | |
117 | + int remote_desc; | |
118 | + | |
119 | + if (!strchr (name, ':')) | |
120 | + { | |
121 | + fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name); | |
122 | + fflush (stderr); | |
123 | + exit (1); | |
124 | + } | |
125 | + else | |
126 | + { | |
127 | +#ifdef USE_WIN32API | |
128 | + static int winsock_initialized; | |
129 | +#endif | |
130 | + char *port_str; | |
131 | + int port; | |
132 | + struct sockaddr_in sockaddr; | |
133 | + socklen_t tmp; | |
134 | + int tmp_desc; | |
135 | + | |
136 | + port_str = strchr (name, ':'); | |
137 | + | |
138 | + port = atoi (port_str + 1); | |
139 | + | |
140 | +#ifdef USE_WIN32API | |
141 | + if (!winsock_initialized) | |
142 | + { | |
143 | + WSADATA wsad; | |
144 | + | |
145 | + WSAStartup (MAKEWORD (1, 0), &wsad); | |
146 | + winsock_initialized = 1; | |
147 | + } | |
148 | +#endif | |
149 | + | |
150 | + tmp_desc = socket (PF_INET, SOCK_STREAM, 0); | |
151 | + if (tmp_desc < 0) | |
152 | + perror_with_name ("Can't open socket"); | |
153 | + | |
154 | + /* Allow rapid reuse of this port. */ | |
155 | + tmp = 1; | |
156 | + setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, | |
157 | + sizeof (tmp)); | |
158 | + | |
159 | + sockaddr.sin_family = PF_INET; | |
160 | + sockaddr.sin_port = htons (port); | |
161 | + sockaddr.sin_addr.s_addr = INADDR_ANY; | |
162 | + | |
163 | + if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) | |
164 | + || listen (tmp_desc, 1)) | |
165 | + perror_with_name ("Can't bind address"); | |
166 | + | |
167 | + tmp = sizeof (sockaddr); | |
168 | + remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp); | |
169 | + if (remote_desc == -1) | |
170 | + perror_with_name ("Accept failed"); | |
171 | + | |
172 | + /* Enable TCP keep alive process. */ | |
173 | + tmp = 1; | |
174 | + setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE, (char *) &tmp, sizeof (tmp)); | |
175 | + | |
176 | + /* Tell TCP not to delay small packets. This greatly speeds up | |
177 | + interactive response. */ | |
178 | + tmp = 1; | |
179 | + setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY, | |
180 | + (char *) &tmp, sizeof (tmp)); | |
181 | + | |
182 | +#ifndef USE_WIN32API | |
183 | + close (tmp_desc); /* No longer need this */ | |
184 | + | |
185 | + signal (SIGPIPE, SIG_IGN); /* If we don't do this, then gdbreplay simply | |
186 | + exits when the remote side dies. */ | |
187 | +#else | |
188 | + closesocket (tmp_desc); /* No longer need this */ | |
189 | +#endif | |
190 | + } | |
191 | + | |
192 | +#if defined(F_SETFL) && defined (FASYNC) | |
193 | + fcntl (remote_desc, F_SETFL, FASYNC); | |
194 | +#endif | |
195 | + | |
196 | + fprintf (stderr, "Replay logfile using %s\n", name); | |
197 | + fflush (stderr); | |
198 | + | |
199 | + return remote_desc; | |
200 | +} | |
201 | + | |
202 | +/* | |
203 | + * freeplay_version | |
204 | + * | |
205 | + * Print version information. | |
206 | + */ | |
207 | + | |
208 | +static void | |
209 | +freeplay_version (FILE *stream) | |
210 | +{ | |
211 | + fprintf (stream, "GNU gdb-freeplay %s%s\n" | |
212 | + "Copyright (C) 2008 Free Software Foundation, Inc.\n" | |
213 | + "gdb-freeplay is free software, covered by the GNU General Public License.\n" | |
214 | + "\n" | |
215 | + "This gdb-freeplay was configured as \"%s\"\n", | |
216 | + PKGVERSION, version, host_name); | |
217 | +} | |
218 | + | |
219 | +/* | |
220 | + * freeplay_usage | |
221 | + * | |
222 | + * Print usage information. | |
223 | + */ | |
224 | + | |
225 | +static void | |
226 | +freeplay_usage (FILE *stream) | |
227 | +{ | |
228 | + fprintf (stream, "Usage:\tgdb-freeplay [--verbose] [--version] <logfile> <host:port>\n"); | |
229 | + if (REPORT_BUGS_TO[0] && stream == stdout) | |
230 | + fprintf (stream, "Report bugs to \"%s\".\n", REPORT_BUGS_TO); | |
231 | +} | |
232 | + | |
233 | +#include "gdbfreeplay.h" | |
234 | + | |
235 | +/* | |
236 | + * TODO: | |
237 | + * | |
238 | + * Actually accept packets from gdb, with handshake where appropriate. | |
239 | + * Parse tfind requests and handle them in the frame cache. | |
240 | + * Try to match everything else from the frame cache and send cached reply. | |
241 | + */ | |
242 | + | |
243 | +int verbose; | |
244 | + | |
245 | +extern int | |
246 | +main (int argc, char **argv) | |
247 | +{ | |
248 | + char *default_socket = "localhost:2345"; | |
249 | + int logfile_arg = 1; | |
250 | + int socket_arg = 2; | |
251 | + int remote_desc = -1, i; | |
252 | + | |
253 | + for (i = 0; i < argc; i++) | |
254 | + { | |
255 | + if (strcmp (argv[i], "--version") == 0) | |
256 | + { | |
257 | + freeplay_version (stdout); | |
258 | + logfile_arg++; | |
259 | + socket_arg++; | |
260 | + } | |
261 | + if (strcmp (argv[i], "--verbose") == 0) | |
262 | + { | |
263 | + verbose = 1; | |
264 | + logfile_arg++; | |
265 | + socket_arg++; | |
266 | + } | |
267 | + else if (strcmp (argv[i], "--help") == 0) | |
268 | + { | |
269 | + freeplay_usage (stdout); | |
270 | + exit (0); | |
271 | + } | |
272 | + else if (i == logfile_arg) | |
273 | + { | |
274 | + if (gdbfreeplay_open (argv[i]) != PASS) | |
275 | + perror_with_name (argv[1]); | |
276 | + logfile_arg = 0; | |
277 | + } | |
278 | + else if (i == socket_arg) | |
279 | + { | |
280 | + if (verbose) | |
281 | + fprintf (stdout, "Open socket: %s\n", argv[i]); | |
282 | + remote_desc = remote_open (argv[i]); | |
283 | + socket_arg = 0; | |
284 | + } | |
285 | + } | |
286 | + | |
287 | + if (logfile_arg) | |
288 | + { | |
289 | + freeplay_usage (stderr); | |
290 | + exit (1); | |
291 | + } | |
292 | + | |
293 | + if (socket_arg) | |
294 | + { | |
295 | + if (verbose) | |
296 | + fprintf (stdout, "Open default socket: %s\n", default_socket); | |
297 | + remote_desc = remote_open (default_socket); | |
298 | + } | |
299 | + | |
300 | + if (remote_desc > 0) | |
301 | + { | |
302 | + gdbfreeplay (remote_desc); | |
303 | + | |
304 | + if (verbose) | |
305 | + fprintf (stdout, "Close remote socket.\n"); | |
306 | + remote_close (remote_desc); | |
307 | + } | |
308 | + | |
309 | + exit (0); | |
310 | +} | |
311 | + |
@@ -0,0 +1,334 @@ | ||
1 | +/* | |
2 | + * gdbfreeplay-i386.c | |
3 | + * | |
4 | + * Target-dependent component of gdbfreeplay for i386. | |
5 | + */ | |
6 | + | |
7 | +#include <stdio.h> | |
8 | +#include <stdlib.h> | |
9 | +#include <string.h> | |
10 | + | |
11 | +#include "gdbfreeplay.h" | |
12 | + | |
13 | +/* | |
14 | + * Utility functions | |
15 | + */ | |
16 | + | |
17 | +/* | |
18 | + * ix86_hex_to_unsigned_long | |
19 | + * | |
20 | + * Convert target-order ascii bytes to host unsigned long. | |
21 | + * Returns host unsigned long. | |
22 | + */ | |
23 | + | |
24 | +static unsigned long | |
25 | +ix86_hex_to_unsigned_long (char *hex) | |
26 | +{ | |
27 | + unsigned long ul; | |
28 | + int i; | |
29 | + | |
30 | + for (i = 6; i >= 0; i-=2) | |
31 | + { | |
32 | + ul <<= 8; | |
33 | + ul += hex_to_int (hex[i]) << 4; | |
34 | + ul += hex_to_int (hex[i+1]); | |
35 | + } | |
36 | + return ul; | |
37 | +} | |
38 | + | |
39 | +/* | |
40 | + * nybble_to_char | |
41 | + */ | |
42 | + | |
43 | +static char | |
44 | +nybble_to_char (int nybble) | |
45 | +{ | |
46 | + if (nybble < 10) | |
47 | + return '0' + nybble; | |
48 | + else | |
49 | + return 'a' + (nybble - 10); | |
50 | +} | |
51 | + | |
52 | +/* | |
53 | + * ix86_unsigned_long_to_hex | |
54 | + * | |
55 | + * Convert host unsigned long to target-order ascii string. | |
56 | + * Return char buffer, static, use before calling again. | |
57 | + */ | |
58 | + | |
59 | +static char * | |
60 | +ix86_unsigned_long_to_hex (unsigned long value) | |
61 | +{ | |
62 | + int i; | |
63 | + static char buf[16]; | |
64 | + char *p; | |
65 | + | |
66 | + p = buf; | |
67 | + for (i = 0; i < 4; i++) | |
68 | + { | |
69 | + /* Intel byte order. */ | |
70 | + *p++ = nybble_to_char ((value >> 4) & 0xf); | |
71 | + *p++ = nybble_to_char (value & 0xf); | |
72 | + value >>= 8; | |
73 | + } | |
74 | + *p = '\0'; | |
75 | + return buf; | |
76 | +} | |
77 | + | |
78 | +/* | |
79 | + * target_pc_from_T | |
80 | + * | |
81 | + * Extract the PC value from the gdb protocol 'T' packet. | |
82 | + * Returns PC as host unsigned long. | |
83 | + */ | |
84 | + | |
85 | +unsigned long | |
86 | +target_pc_from_T (char *tpacket) | |
87 | +{ | |
88 | + char *p; | |
89 | + | |
90 | + if (tpacket[0] == '$' && tpacket[1] == 'T' && | |
91 | + (p = strstr (tpacket, ";08:")) != NULL) | |
92 | + { | |
93 | + return ix86_hex_to_unsigned_long (p + 4); | |
94 | + } | |
95 | + | |
96 | + /* Fail -- just assume no legitimate PC will ever be -1... */ | |
97 | + return (unsigned long) -1; | |
98 | +} | |
99 | + | |
100 | +/* | |
101 | + * ix86_pc_from_registers | |
102 | + * | |
103 | + * Extract the PC value from a gdb protocol registers file. | |
104 | + * Returns PC as host unsigned long. | |
105 | + */ | |
106 | + | |
107 | +static unsigned long | |
108 | +ix86_pc_from_registers (char *regs) | |
109 | +{ | |
110 | + return ix86_hex_to_unsigned_long (regs + 64); | |
111 | +} | |
112 | + | |
113 | +/* | |
114 | + * target_pc_from_G | |
115 | + * | |
116 | + * Extract the PC value from the gdb protocol 'G' packet. | |
117 | + * Returns PC as host unsigned long. | |
118 | + */ | |
119 | + | |
120 | +unsigned long | |
121 | +target_pc_from_G (char *gpacket) | |
122 | +{ | |
123 | + if (gpacket[0] == '$' && gpacket[1] == 'G') | |
124 | + { | |
125 | + return ix86_pc_from_registers (gpacket + 2); | |
126 | + } | |
127 | + | |
128 | + /* Fail -- just assume no legitimate PC will ever be -1... */ | |
129 | + return (unsigned long) -1; | |
130 | +} | |
131 | + | |
132 | +/* | |
133 | + * expand_rle | |
134 | + * | |
135 | + * Stubs sometimes send us data with RLE compression. | |
136 | + * FIXME this code is generic, move into gdbfreeplay-back.c | |
137 | + * The code for this function lifted from gdb. | |
138 | + */ | |
139 | + | |
140 | +static char * | |
141 | +expand_rle (char *input) | |
142 | +{ | |
143 | + static char *buf = NULL; | |
144 | + static int sizeof_buf; | |
145 | + long bc = 0; | |
146 | + int c, repeat; | |
147 | + | |
148 | + /* Allocate buf on first time thru. */ | |
149 | + if (buf == NULL) | |
150 | + { | |
151 | + sizeof_buf = 4096; | |
152 | + buf = realloc (buf, sizeof_buf); | |
153 | + } | |
154 | + | |
155 | + while (1) | |
156 | + { | |
157 | + c = *input++; | |
158 | + switch (c) { | |
159 | + case '#': /* End of interesting data. We don't care about checksum. */ | |
160 | + case '\0':/* End of string data. */ | |
161 | + buf[bc] = '\0'; | |
162 | + return buf; | |
163 | + break; | |
164 | + case '$': | |
165 | + /* Shouldn't happen, but... */ | |
166 | + fprintf (stderr, "Warning: expand_rle saw '$' in middle of packet.\n"); | |
167 | + default: | |
168 | + if (bc >= sizeof_buf - 1) | |
169 | + { | |
170 | + /* Make some more room in the buffer. */ | |
171 | + sizeof_buf *= 2; | |
172 | + buf = realloc (buf, sizeof_buf); | |
173 | + } | |
174 | + buf[bc++] = c; | |
175 | + continue; | |
176 | + break; | |
177 | + case '*': /* Run length encoding. */ | |
178 | + c = *input++; | |
179 | + repeat = c - ' ' + 3; /* Compute repeat count. */ | |
180 | + | |
181 | + /* The character before '*' is repeated. */ | |
182 | + if (bc + repeat - 1 >= sizeof_buf - 1) | |
183 | + { | |
184 | + /* Make some more room in the buffer. */ | |
185 | + sizeof_buf *= 2; | |
186 | + buf = realloc (buf, sizeof_buf); | |
187 | + } | |
188 | + | |
189 | + memset (&buf[bc], buf[bc - 1], repeat); | |
190 | + bc += repeat; | |
191 | + continue; | |
192 | + break; | |
193 | + } | |
194 | + } | |
195 | +} | |
196 | + | |
197 | +/* | |
198 | + * target_pc_from_g | |
199 | + * | |
200 | + * Extract the PC value from the gdb protocol 'g' packet reply. | |
201 | + * | |
202 | + * Unlike the two above, this function accepts a FILE pointer | |
203 | + * rather than a char pointer, and must read data from the file. | |
204 | + * FIXME I could make it like the others for symmetry... | |
205 | + * | |
206 | + * Returns PC as host unsigned long. | |
207 | + */ | |
208 | + | |
209 | +#define INBUF_SIZE 1024 | |
210 | +static char inbuf [INBUF_SIZE]; | |
211 | + | |
212 | +unsigned long | |
213 | +target_pc_from_g (FILE *infile) | |
214 | +{ | |
215 | + char *line = fgets (inbuf, sizeof (inbuf), infile); | |
216 | + | |
217 | + if (line[0] == 'r' && line[1] == ' ') | |
218 | + { | |
219 | + line += 2; | |
220 | + if (line[0] == '+') | |
221 | + line++; | |
222 | + if (line[0] == '$') | |
223 | + line++; | |
224 | + } | |
225 | + | |
226 | + return ix86_pc_from_registers (expand_rle (line)); | |
227 | +} | |
228 | + | |
229 | +/* | |
230 | + * target_compose_T_packet | |
231 | + * | |
232 | + * On targets where DECR_PC_AFTER_BREAK is zero, this is a no-op. | |
233 | + * We just send back the T packet that was sent to us. | |
234 | + * | |
235 | + * On targets like i386, where DECR_PC_AFTER_BREAK is non-zero, | |
236 | + * it gets complicated. We have two pieces of information: | |
237 | + * | |
238 | + * 1) The address of the current instruction, and | |
239 | + * 2) Whether we arrived at the current instruction | |
240 | + * by virtue of a breakpoint -- ie. whether gdb is | |
241 | + * expecting the PC to be off-by-one. | |
242 | + * | |
243 | + * Based on that info, we decide whether the T packet that was | |
244 | + * sent to us is suitable as it is, or else we compose one and | |
245 | + * send it back. | |
246 | + * | |
247 | + * Returns a string T packet, possibly WITHOUT CHECKSUM. | |
248 | + * We leave it to the caller to handle that, if necessary, | |
249 | + * because it is target independent and therefore inappropriate | |
250 | + * to do here. However the caller has to check for it, because | |
251 | + * if we simply return the T packet that we received, it will | |
252 | + * already have a checksum. | |
253 | + */ | |
254 | + | |
255 | +char * | |
256 | +target_compose_T_packet (char *origTpacket, | |
257 | + unsigned long instruction_pc, | |
258 | + int breakpoint_p) | |
259 | +{ | |
260 | + unsigned long origTpacket_pc = target_pc_from_T (origTpacket); | |
261 | + static char reply_buf[128]; | |
262 | + char *p; | |
263 | + | |
264 | + /* There are four possibilities. | |
265 | + 1) We got here by stepping, and instruction_pc == origTpacket_pc. | |
266 | + 2) We got here by stepping, and instruction_pc != origTpacket_pc. | |
267 | + 3) We got here by breakpoint, and instruction_pc == origTpacket_pc. | |
268 | + 4) We got here by breakpoint, and instruction_pc != origTpacket_pc. | |
269 | + | |
270 | + Actually, there's one more (not well understood): | |
271 | + 5) instruction_pc and origTpacket_pc bear no relationship to each other. | |
272 | + | |
273 | + In that case, we should bail and let gdb get the original Tpacket. */ | |
274 | + | |
275 | + /* Case #5. */ | |
276 | + if (instruction_pc != origTpacket_pc && | |
277 | + instruction_pc != origTpacket_pc - 1) | |
278 | + return origTpacket; | |
279 | + | |
280 | + /* For #1 and #4, we don't have to do anything, and can return | |
281 | + the original T packet. */ | |
282 | + | |
283 | + /* Case #1. */ | |
284 | + if (!breakpoint_p && (instruction_pc == origTpacket_pc)) | |
285 | + return origTpacket; | |
286 | + | |
287 | + /* Case #4. */ | |
288 | + if (breakpoint_p && (instruction_pc == origTpacket_pc - 1)) | |
289 | + return origTpacket; | |
290 | + | |
291 | + /* That's it for the easy cases. For the other two, we have work to do. | |
292 | + Start by making a copy of the original T packet. */ | |
293 | + | |
294 | + strcpy (reply_buf, origTpacket); | |
295 | + if ((p = strstr (reply_buf, ";08:")) != NULL) | |
296 | + { | |
297 | +#if 0 | |
298 | + /* Snip off the PC value from the original T packet. */ | |
299 | + p[4] = '\0'; | |
300 | + /* Case #2. */ | |
301 | + if (breakpoint_p) | |
302 | + { | |
303 | + /* Compose a T packet using instruction_pc + 1. */ | |
304 | + strcat (p, ix86_unsigned_long_to_hex (instruction_pc + 1)); | |
305 | + } | |
306 | + else | |
307 | + { | |
308 | + /* Compose a T packet using instruction_pc. */ | |
309 | + strcat (p, ix86_unsigned_long_to_hex (instruction_pc)); | |
310 | + } | |
311 | +#else | |
312 | + /* Insert the new PC value in place | |
313 | + (without disturbing whatever follows it). */ | |
314 | + | |
315 | + /* Case #2. */ | |
316 | + if (breakpoint_p) | |
317 | + { | |
318 | + /* Compose a T packet using instruction_pc + 1. */ | |
319 | + memcpy (p + 4, | |
320 | + ix86_unsigned_long_to_hex (instruction_pc + 1), | |
321 | + 8); | |
322 | + } | |
323 | + else | |
324 | + { | |
325 | + /* Compose a T packet using instruction_pc. */ | |
326 | + memcpy (p + 4, | |
327 | + ix86_unsigned_long_to_hex (instruction_pc), | |
328 | + 8); | |
329 | + } | |
330 | +#endif | |
331 | + /* Caller has to recompute checksum. */ | |
332 | + return reply_buf; | |
333 | + } | |
334 | +} |
@@ -0,0 +1,21 @@ | ||
1 | +/* | |
2 | + * gdbfreeplay -- interface | |
3 | + */ | |
4 | + | |
5 | +#ifndef GDBFREEPLAY_H | |
6 | +#define GDBFREEPLAY_H | |
7 | + | |
8 | +#include "remote-breakpoint.h" | |
9 | + | |
10 | +extern int verbose; | |
11 | + | |
12 | +extern enum successcode gdbfreeplay_open (char *filename); | |
13 | +extern void gdbfreeplay (int socket_fd); | |
14 | + | |
15 | +extern unsigned long target_pc_from_T (char *tpacket); | |
16 | +extern unsigned long target_pc_from_G (char *gpacket); | |
17 | +extern unsigned long target_pc_from_g (FILE *infile); | |
18 | + | |
19 | +extern int hex_to_int (int ch); | |
20 | + | |
21 | +#endif |
@@ -0,0 +1,159 @@ | ||
1 | +/* | |
2 | + * remote-breakpoint.c | |
3 | + * | |
4 | + * A breakpoint list for a remote gdb target. | |
5 | + */ | |
6 | + | |
7 | +#include <stdio.h> | |
8 | +#include <stdlib.h> | |
9 | +#include <ctype.h> | |
10 | +#include <string.h> | |
11 | + | |
12 | +#include "remote-breakpoint.h" | |
13 | + | |
14 | +static breakpoint *bplist[5]; | |
15 | + | |
16 | +extern int verbose; | |
17 | + | |
18 | +/* | |
19 | + * insert_breakpoint | |
20 | + * | |
21 | + * returns: FAIL/PASS | |
22 | + */ | |
23 | + | |
24 | +static enum successcode | |
25 | +insert_breakpoint (enum breakpoint_type bptype, | |
26 | + unsigned long addr, | |
27 | + unsigned long len) | |
28 | +{ | |
29 | + breakpoint *this_bp; | |
30 | + | |
31 | + switch (bptype) { | |
32 | + case ACCESS_BP: | |
33 | + case HARDWARE_BP: | |
34 | + case READ_BP: | |
35 | + case WRITE_BP: | |
36 | + default: | |
37 | + /* Can't do those. */ | |
38 | + return FAIL; | |
39 | + break; | |
40 | + case SOFTWARE_BP: | |
41 | + this_bp = malloc (sizeof (breakpoint)); | |
42 | + this_bp->addr = addr; | |
43 | + this_bp->len = len; | |
44 | + this_bp->next = bplist[bptype]; | |
45 | + bplist[bptype] = this_bp; | |
46 | + return PASS; | |
47 | + } | |
48 | +} | |
49 | + | |
50 | +/* | |
51 | + * unlink_breakpoint | |
52 | + * | |
53 | + * returns: 0 for fail, 1 for success | |
54 | + */ | |
55 | + | |
56 | +static int | |
57 | +unlink_breakpoint (enum breakpoint_type bptype, | |
58 | + unsigned long addr, | |
59 | + unsigned long len) | |
60 | +{ | |
61 | + breakpoint *this_bp, *tmp; | |
62 | + | |
63 | + switch (bptype) { | |
64 | + case ACCESS_BP: | |
65 | + case HARDWARE_BP: | |
66 | + case READ_BP: | |
67 | + case WRITE_BP: | |
68 | + default: | |
69 | + /* Can't do those. */ | |
70 | + return FAIL; | |
71 | + break; | |
72 | + case SOFTWARE_BP: | |
73 | + /* Special case - list is empty. */ | |
74 | + if (bplist[bptype] == NULL) | |
75 | + return FAIL; | |
76 | + | |
77 | + /* Start from list head. */ | |
78 | + this_bp = bplist[bptype]; | |
79 | + /* Special case -- remove head of list. */ | |
80 | + if (this_bp->addr == addr && | |
81 | + this_bp->len == len) | |
82 | + { | |
83 | + bplist[bptype] = this_bp->next; | |
84 | + return PASS; | |
85 | + } | |
86 | + | |
87 | + /* Scan list. */ | |
88 | + for (; this_bp && this_bp->next; this_bp = this_bp->next) | |
89 | + if (this_bp->next->addr == addr && | |
90 | + this_bp->next->len == len) | |
91 | + { | |
92 | + /* Remove from middle of list. */ | |
93 | + tmp = this_bp->next->next; | |
94 | + free (this_bp->next); | |
95 | + this_bp->next = tmp; | |
96 | + return PASS; | |
97 | + } | |
98 | + | |
99 | + /* Not found. */ | |
100 | + return FAIL; | |
101 | + } | |
102 | +} | |
103 | + | |
104 | + | |
105 | +extern enum successcode | |
106 | +remote_remove_breakpoint (enum breakpoint_type bptype, | |
107 | + unsigned long addr, | |
108 | + unsigned long len) | |
109 | +{ | |
110 | + if (verbose) | |
111 | + fprintf (stdout, "remote-breakpoint: Remove sw breakpoint type %d\n", | |
112 | + bptype); | |
113 | + if (unlink_breakpoint (bptype, addr, len) == 0) | |
114 | + { | |
115 | + fprintf (stderr, " FAILED!\n"); | |
116 | + return FAIL; | |
117 | + } | |
118 | + return PASS; | |
119 | +} | |
120 | + | |
121 | +extern enum successcode | |
122 | +remote_set_breakpoint (enum breakpoint_type bptype, | |
123 | + unsigned long addr, | |
124 | + unsigned long len) | |
125 | +{ | |
126 | + if (verbose) | |
127 | + fprintf (stdout, "remote-breakpoint: Set sw breakpoint type %d\n", | |
128 | + bptype); | |
129 | + if (insert_breakpoint (bptype, addr, len) == 0) | |
130 | + { | |
131 | + fprintf (stderr, " FAILED!\n"); | |
132 | + return FAIL; | |
133 | + } | |
134 | + return PASS; | |
135 | +} | |
136 | + | |
137 | +/* | |
138 | + * remote_breakpoint_here_p | |
139 | + * | |
140 | + * Scan the list of breakpoints of type BPTYPE. | |
141 | + * Return PASS if there is one that matches ADDR, else FAIL. | |
142 | + * | |
143 | + * FIXME: do I need to consider the length? | |
144 | + */ | |
145 | + | |
146 | +enum successcode | |
147 | +remote_breakpoint_here_p (enum breakpoint_type bptype, | |
148 | + unsigned long addr) | |
149 | +{ | |
150 | + breakpoint *bp = bplist[bptype]; | |
151 | + | |
152 | + while (bp != NULL) | |
153 | + { | |
154 | + if (bp->addr == addr) | |
155 | + return PASS; | |
156 | + bp = bp->next; | |
157 | + } | |
158 | + return FAIL; | |
159 | +} |
@@ -0,0 +1,44 @@ | ||
1 | +/* | |
2 | + * remote-breakpoint -- interface | |
3 | + */ | |
4 | + | |
5 | +#ifndef REMOTE_BREAKPOINT_H | |
6 | +#define REMOTE_BREAKPOINT_H | |
7 | + | |
8 | +typedef struct BREAKPOINT { | |
9 | + unsigned long addr; | |
10 | + unsigned long len; | |
11 | + struct BREAKPOINT *next; | |
12 | +} breakpoint; | |
13 | + | |
14 | +enum breakpoint_type { | |
15 | + SOFTWARE_BP, | |
16 | + HARDWARE_BP, | |
17 | + WRITE_BP, | |
18 | + READ_BP, | |
19 | + ACCESS_BP | |
20 | +}; | |
21 | + | |
22 | +enum successcode { | |
23 | + FAIL = 0, | |
24 | + PASS = 1 | |
25 | +}; | |
26 | + | |
27 | +enum direction_code { | |
28 | + DIR_FORWARD = 0, | |
29 | + DIR_BACKWARD | |
30 | +}; | |
31 | + | |
32 | +extern enum successcode remote_remove_breakpoint (enum breakpoint_type, | |
33 | + unsigned long, | |
34 | + unsigned long); | |
35 | + | |
36 | +extern enum successcode remote_set_breakpoint (enum breakpoint_type, | |
37 | + unsigned long, | |
38 | + unsigned long); | |
39 | + | |
40 | +extern enum successcode remote_breakpoint_here_p (enum breakpoint_type, | |
41 | + unsigned long); | |
42 | + | |
43 | +#endif | |
44 | + |