~ chicken-core (chicken-5) /dbg-stub.c
Trap1/* dbg-stub.c - Client-side interface, lowlevel part
2;
3; Copyright (c) 2008-2022, The CHICKEN Team
4; Copyright (c) 2000-2007, Felix L. Winkelmann
5; All rights reserved.
6;
7; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8; conditions are met:
9;
10; Redistributions of source code must retain the above copyright notice, this list of conditions and the following
11; disclaimer.
12; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
13; disclaimer in the documentation and/or other materials provided with the distribution.
14; Neither the name of the author nor the names of its contributors may be used to endorse or promote
15; products derived from this software without specific prior written permission.
16;
17; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
18; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
20; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25; POSSIBILITY OF SUCH DAMAGE.
26*/
27
28
29/* included from debugger-client.scm */
30
31#include <assert.h>
32
33#ifdef _WIN32
34# include <winsock2.h>
35# include <ws2tcpip.h>
36/* Beware: winsock2.h must come BEFORE windows.h */
37# define socklen_t int
38static WSADATA wsa;
39#else
40# include <errno.h>
41# include <fcntl.h>
42# include <sys/socket.h>
43# include <sys/time.h>
44# include <netinet/in.h>
45# include <netdb.h>
46# include <signal.h>
47# define closesocket close
48# define INVALID_SOCKET (-1)
49# define SOCKET_ERROR (-1)
50# ifndef h_addr
51# define h_addr h_addr_list[ 0 ]
52# endif
53#endif
54
55
56#define C_DEBUG_PROTOCOL_VERSION 1
57
58#define C_DEBUG_REPLY_UNUSED 0
59#define C_DEBUG_REPLY_SETMASK 1
60#define C_DEBUG_REPLY_TERMINATE 2
61#define C_DEBUG_REPLY_CONTINUE 3
62#define C_DEBUG_REPLY_SET_BREAKPOINT 4
63#define C_DEBUG_REPLY_CLEAR_BREAKPOINT 5
64#define C_DEBUG_REPLY_LIST_EVENTS 6
65#define C_DEBUG_REPLY_GET_BYTES 7
66#define C_DEBUG_REPLY_GET_AV 8
67#define C_DEBUG_REPLY_GET_SLOTS 9
68#define C_DEBUG_REPLY_GET_GLOBAL 10
69#define C_DEBUG_REPLY_GET_STATS 11
70#define C_DEBUG_REPLY_GET_TRACE 12
71
72#define INPUT_BUFFER_SIZE 4096
73#define RW_BUFFER_SIZE 1024
74#define DEFAULT_DEBUGGER_PORT 9999
75
76#ifdef C_SIXTY_FOUR
77# define C_HEADER_BITS_SHIFT 56
78# ifdef C_LLP
79# define UWORD_COUNT_FORMAT_STRING "%llu"
80# else
81# define UWORD_COUNT_FORMAT_STRING "%lu"
82# endif
83#else
84# define C_HEADER_BITS_SHIFT 24
85# define UWORD_COUNT_FORMAT_STRING "%u"
86#endif
87
88#define C_VALUE_CUTOFF_LIMIT 300
89#define get_header_bits(x) ((int)(C_header_bits((x)) >> C_HEADER_BITS_SHIFT))
90
91
92struct bp_item {
93 char *name;
94 int len;
95 struct bp_item *next;
96};
97
98struct dbg_info_list {
99 C_DEBUG_INFO *info;
100 struct dbg_info_list *next;
101};
102
103
104static long event_mask = 0;
105static int socket_fd = 0;
106static char input_buffer[ INPUT_BUFFER_SIZE + 1 ];
107static char *input_buffer_top = NULL;
108static int input_buffer_len = 0;
109static char rw_buffer[ RW_BUFFER_SIZE + 1 ];
110static struct bp_item *breakpoints = NULL;
111static struct dbg_info_list
112 *dbg_info_list = NULL,
113 *last_dbg_info_list = NULL,
114 *unseen_dbg_info_list = NULL;
115static C_word current_c = 0;
116static C_word *current_av;
117static volatile int interrupted = 0;
118static int dbg_info_count = 0;
119
120
121static C_word debug_event_hook(C_DEBUG_INFO *cell, C_word c, C_word *av, C_char *cloc);
122
123
124void
125C_register_debug_info(C_DEBUG_INFO *info)
126{
127 struct dbg_info_list *node =
128 (struct dbg_info_list *)C_malloc(sizeof(struct dbg_info_list));
129
130 /* fprintf(stderr, "Registering: %p (%s/%s)\n", node, info->loc, info->val); */
131 assert(node);
132 node->info = info;
133 node->next = NULL;
134
135 if(last_dbg_info_list != NULL) last_dbg_info_list->next = node;
136
137 last_dbg_info_list = node;
138
139 if(unseen_dbg_info_list == NULL) unseen_dbg_info_list = node;
140
141 if(dbg_info_list == NULL) dbg_info_list = node;
142
143 /* fprintf(stderr, "first: %p, last: %p, unseen: %p\n", dbg_info_list, last_dbg_info_list, unseen_dbg_info_list); */
144}
145
146
147static int
148socket_read()
149{
150 int p = 0, s = 0, e = 0;
151 int n, off = 0;
152 char *ptr = rw_buffer;
153
154 /* copy from input_buffer into rw_buffer until newline: */
155 for(;;) {
156 while(input_buffer_len > 0) {
157 *(ptr++) = *input_buffer_top;
158
159 if(*(input_buffer_top++) == '\n') {
160 *ptr = '\0';
161 --input_buffer_len;
162 return 1;
163 }
164
165 if(++off >= RW_BUFFER_SIZE) return -1; /* read-buffer overflow */
166
167 --input_buffer_len;
168 }
169
170 n = recv(socket_fd, input_buffer, INPUT_BUFFER_SIZE, 0);
171
172 if(n == SOCKET_ERROR) return -1; /* read failed */
173
174 if(n == 0) return 0; /* client disconnect */
175
176 input_buffer_len = n;
177 input_buffer_top = input_buffer;
178 }
179}
180
181
182static int
183socket_write(char *buf, int len)
184{
185 int n, m = 0, off = 0;
186
187 while(m < len) {
188 n = send(socket_fd, buf + off, len, 0);
189
190 if(n == SOCKET_ERROR) return -1; /* write failed */
191
192 off += n;
193 m += n;
194 }
195
196 return 0;
197}
198
199
200static void
201socket_close()
202{
203 closesocket(socket_fd);
204 socket_fd = 0;
205}
206
207
208static void
209terminate(char *msg)
210{
211 fprintf(stderr, "%s\n", msg);
212 socket_close();
213 C_exit_runtime(C_fix(1));
214}
215
216
217static char *
218name_and_length(char *buf, int *len)
219{
220 char *str, *ptr;
221
222 for(str = buf; *str && *str != '\"'; ++str);
223
224 if(!*str) return "";
225
226 for(ptr = ++str; *ptr != '\"'; ++ptr) {
227 if(*ptr == '\\') ++ptr;
228 }
229
230 *len = ptr - str;
231 return str;
232}
233
234
235static void
236enable_debug_info(int n, int f)
237{
238 int i = 0;
239 struct dbg_info_list *dip;
240 C_DEBUG_INFO *dinfo;
241
242 for(dip = dbg_info_list; dip != NULL; dip = dip->next) {
243 for(dinfo = dip->info; dinfo->event; ++dinfo) {
244 if(i++ == n) {
245 dinfo->enabled = f;
246 return;
247 }
248 }
249 }
250
251 terminate("invalid debug-info index");
252}
253
254
255static void
256send_string(C_char *str)
257{
258 /* fprintf(stderr, "<SENT: %s>\n", str); */
259 C_fflush(stderr);
260
261 if(socket_write(str, C_strlen(str)) != 0)
262 terminate("write failed");
263}
264
265static void
266send_string_value(C_char *str) {
267 if (str == 0 || *str == 0)
268 send_string(" #f");
269 else {
270 C_snprintf(rw_buffer, sizeof(rw_buffer), " \"%s\"", str);
271 send_string(rw_buffer);
272 }
273}
274
275static void
276send_scheme_value(C_word x)
277{
278 if((x & C_FIXNUM_BIT) != 0)
279 C_snprintf(rw_buffer, sizeof(rw_buffer), " %ld", (long)C_unfix(x));
280 else if((x & C_IMMEDIATE_MARK_BITS) != 0)
281 C_snprintf(rw_buffer, sizeof(rw_buffer), " =%lu", (unsigned long)x);
282 else
283 C_snprintf(rw_buffer, sizeof(rw_buffer), " @%lu", (unsigned long)x);
284
285 send_string(rw_buffer);
286}
287
288
289static void
290send_event(int event, C_char *loc, C_char *val, C_char *cloc)
291{
292 int n;
293 int reply, mask;
294 struct bp_item *bp, *prev;
295 C_char *str, *ptr;
296 struct dbg_info_list *dip;
297 C_DEBUG_INFO *dinfo;
298 C_word x;
299 void **stats;
300
301 for(;;) {
302 C_snprintf(rw_buffer, sizeof(rw_buffer), "(%d", event);
303 send_string(rw_buffer);
304 send_string_value(loc);
305 send_string_value(val);
306 send_string_value(cloc);
307 send_string(")\n");
308
309 n = socket_read();
310
311 if(n < 0) terminate("read failed");
312
313 if(n == 0) terminate("debugger disconnected");
314
315 /* fprintf(stderr, "<READ: %s>\n", rw_buffer); */
316 n = sscanf(rw_buffer, "(%d ", &reply);
317
318 if(n == 0) terminate("invalid reply");
319
320 switch(reply) {
321 case C_DEBUG_REPLY_SETMASK:
322 n = sscanf(rw_buffer, "(%d %d)", &reply, &mask);
323
324 if(n != 2) terminate("invalid SETMASK reply");
325
326 event_mask = mask;
327 break;
328
329 case C_DEBUG_REPLY_TERMINATE:
330 terminate("terminated by debugger");
331
332 case C_DEBUG_REPLY_CONTINUE:
333 return;
334
335 case C_DEBUG_REPLY_SET_BREAKPOINT:
336 n = sscanf(rw_buffer, "(%d %d)", &reply, &mask);
337
338 if(n != 2) terminate("invalid SET BREAKPOINT reply");
339
340 enable_debug_info(mask, 1);
341 break;
342
343 case C_DEBUG_REPLY_CLEAR_BREAKPOINT:
344 n = sscanf(rw_buffer, "(%d %d)", &reply, &mask);
345
346 if(n != 2) terminate("invalid CLEAR BREAKPOINT reply");
347
348 enable_debug_info(mask, 0);
349 break;
350
351 case C_DEBUG_REPLY_LIST_EVENTS:
352 str = name_and_length(rw_buffer, &n);
353 str[ n ] = '\0';
354 str = C_strdup(str);
355
356 for(dip = unseen_dbg_info_list; dip != NULL; dip = dip->next) {
357 for(dinfo = dip->info; dinfo->event; ++dinfo) {
358 if(*str == '\0' || strstr(dinfo->val, str)) {
359 C_snprintf(rw_buffer, sizeof(rw_buffer), "(* %d %d", dbg_info_count++, dinfo->event);
360 send_string(rw_buffer);
361 send_string_value(dinfo->loc);
362 send_string_value(dinfo->val);
363 send_string(")\n");
364 }
365
366 ++n;
367 }
368 }
369
370 unseen_dbg_info_list = NULL;
371 C_free(str);
372 break;
373
374 case C_DEBUG_REPLY_GET_BYTES:
375 n = sscanf(rw_buffer, "(%d " UWORD_COUNT_FORMAT_STRING " %d)",
376 &reply, &x, &mask);
377
378 if(n != 3) terminate("invalid GET_BYTES reply");
379
380 ptr = (char *)x;
381
382 send_string("(*");
383
384 while(mask--) {
385 C_snprintf(rw_buffer, sizeof(rw_buffer), " %u", (unsigned char) *(ptr++));
386 send_string(rw_buffer);
387 }
388
389 send_string(")\n");
390 break;
391
392 case C_DEBUG_REPLY_GET_AV:
393 send_string("(*");
394
395 for(n = 0; n < current_c; ++n)
396 send_scheme_value(current_av[ n ]);
397
398 send_string(")\n");
399 break;
400
401 case C_DEBUG_REPLY_GET_SLOTS:
402 sscanf(rw_buffer, "(%d " UWORD_COUNT_FORMAT_STRING ")", &mask, &x);
403
404 if(mask >= C_VALUE_CUTOFF_LIMIT)
405 mask = C_VALUE_CUTOFF_LIMIT;
406
407 if((C_header_bits(x) & C_BYTEBLOCK_BIT) != 0) {
408 reply = C_header_size(x);
409 C_snprintf(rw_buffer, sizeof(rw_buffer), "(* BLOB %d", get_header_bits(x));
410 send_string(rw_buffer);
411
412 for(n = 0; n < reply; ++n) {
413 C_snprintf(rw_buffer, sizeof(rw_buffer), " %u", ((unsigned char *)C_data_pointer(x))[ n ]);
414 send_string(rw_buffer);
415 }
416
417 send_string(")\n");
418 break;
419 }
420
421 n = 0;
422
423 if((C_header_bits(x) & C_SPECIALBLOCK_BIT) != 0) {
424 C_snprintf(rw_buffer, sizeof(rw_buffer), "(* SPECIAL %d " UWORD_COUNT_FORMAT_STRING,
425 get_header_bits(x), C_block_item(x, 0));
426 n = 1;
427 }
428 else C_snprintf(rw_buffer, sizeof(rw_buffer), "(* VECTOR %d", get_header_bits(x));
429
430 send_string(rw_buffer);
431
432 for(mask = C_header_size(x); n < mask; ++n)
433 send_scheme_value(C_block_item(x, n));
434
435 send_string(")\n");
436 break;
437
438 case C_DEBUG_REPLY_GET_GLOBAL:
439 str = name_and_length(rw_buffer, &n);
440 ptr = malloc(sizeof(C_header) + n + 1);
441 memcpy(((C_SCHEME_BLOCK*)ptr)->data, str, n + 1);
442 ((C_SCHEME_BLOCK *)ptr)->header = C_make_header(C_STRING_TYPE, n);
443 x = C_find_symbol((C_word)ptr, NULL);
444
445 if(x == C_SCHEME_FALSE)
446 send_string("(* UNKNOWN)\n");
447 else {
448 send_string("(*");
449 send_scheme_value(C_symbol_value(x));
450 send_string(")\n");
451 }
452
453 break;
454
455 case C_DEBUG_REPLY_GET_STATS:
456 stats = C_get_statistics();
457 send_string("(*");
458
459 for(n = 0; n < 8; ++n) {
460 C_snprintf(rw_buffer, sizeof(rw_buffer), " " UWORD_COUNT_FORMAT_STRING, (C_uword)stats[ n ]);
461 send_string(rw_buffer);
462 }
463
464 C_snprintf(rw_buffer, sizeof(rw_buffer), " " UWORD_COUNT_FORMAT_STRING ")\n",
465 (C_uword)C_stack_pointer);
466 send_string(rw_buffer);
467 break;
468
469 case C_DEBUG_REPLY_GET_TRACE:
470 str = ptr = C_dump_trace(0);
471
472 while((n = C_strcspn(ptr, "\n"))) {
473 ptr[ n++ ] = '\0';
474 send_string("(* \"");
475 send_string(ptr);
476 send_string("\")\n");
477 ptr += n;
478 }
479
480 free(str);
481 break;
482
483 default: terminate("invalid reply code");
484 }
485
486 event = C_DEBUG_LISTEN;
487 val = unseen_dbg_info_list ? "1" : "0";
488 }
489}
490
491
492#ifndef _WIN32
493static void
494interrupt_signal_handler(int signum)
495{
496 interrupted = 1;
497 C_signal(SIGUSR2, interrupt_signal_handler);
498}
499#endif
500
501
502static C_word
503connect_to_debugger()
504{
505 char *addr = getenv("CHICKEN_DEBUGGER");
506 char *host;
507 static char info[ 256 ];
508 struct hostent *he;
509 struct sockaddr_in sa;
510 int i, port = DEFAULT_DEBUGGER_PORT;
511 int yes = 1;
512 int r;
513
514 C_debugger_hook = debug_event_hook;
515
516 if(addr == NULL) return C_SCHEME_FALSE; /* no debugger address given */
517
518 /* parse host and port number */
519 for(i = C_strlen(addr) - 1; i > 0; --i) {
520 if(addr[ i ] == ':') break;
521 }
522
523 if(i == 0) host = addr;
524 else {
525 port = atoi(addr + i + 1);
526 host = C_strdup(addr);
527 host[i] = '\0'; /* We don't use strndup() for compat reasons */
528 }
529
530#ifdef _WIN32
531 if(WSAStartup(MAKEWORD(1, 1), &wsa) != 0)
532 return C_SCHEME_FALSE; /* failed to init sockets */
533#endif
534
535 /* obtain host address */
536 he = gethostbyname(host);
537
538 if(he == NULL) return C_SCHEME_FALSE; /* invalid host */
539
540 C_memset(&sa, 0, sizeof(struct sockaddr_in));
541 sa.sin_family = AF_INET;
542 sa.sin_port = htons((short)port);
543 sa.sin_addr = *((struct in_addr *)he->h_addr);
544 socket_fd = socket(AF_INET, SOCK_STREAM, 0);
545
546 if(socket_fd == INVALID_SOCKET)
547 return C_SCHEME_FALSE; /* can not create socket */
548
549 /* socket options */
550 r = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(int));
551
552 if(r != 0) return C_SCHEME_FALSE; /* failed to set socket options */
553
554 /* connect */
555 if(connect(socket_fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) == SOCKET_ERROR)
556 return C_SCHEME_FALSE; /* failed to connect */
557
558 C_snprintf(info, sizeof(info), "%s:%d:%d", C_main_argv[ 0 ], getpid(), C_DEBUG_PROTOCOL_VERSION);
559 send_event(C_DEBUG_CONNECT, info, NULL, NULL);
560#ifndef _WIN32
561 C_signal(SIGUSR2, interrupt_signal_handler);
562#endif
563 return C_SCHEME_TRUE;
564}
565
566
567static C_word
568debug_event_hook(C_DEBUG_INFO *cell, C_word c, C_word *av, C_char *cloc)
569{
570 if(socket_fd != 0) {
571 if(cell->enabled || interrupted || ((1 << cell->event) & event_mask) != 0 ) {
572 /* fprintf(stderr, "event: %s\n", cloc); */
573 current_c = c;
574 current_av = av;
575 send_event(interrupted ? C_DEBUG_INTERRUPTED : cell->event, cell->loc, cell->val, cloc);
576 interrupted = 0;
577 }
578 }
579
580 return C_SCHEME_UNDEFINED;
581}
582
583
584/* TODO:
585
586 - escape '\"' + '\\' in transmitted strings
587 - error-condition (SIGNAL event) doesn't seem to terminate
588
589*/