1 /* dbg-stub.c - Client-side interface, lowlevel part
2 ;
3 ; Copyright (c) 2008-2021, 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
38 static 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 
92 struct bp_item {
93   char *name;
94   int len;
95   struct bp_item *next;
96 };
97 
98 struct dbg_info_list {
99   C_DEBUG_INFO *info;
100   struct dbg_info_list *next;
101 };
102 
103 
104 static long event_mask = 0;
105 static int socket_fd = 0;
106 static char input_buffer[ INPUT_BUFFER_SIZE + 1 ];
107 static char *input_buffer_top = NULL;
108 static int input_buffer_len = 0;
109 static char rw_buffer[ RW_BUFFER_SIZE + 1 ];
110 static struct bp_item *breakpoints = NULL;
111 static struct dbg_info_list
112     *dbg_info_list = NULL,
113     *last_dbg_info_list = NULL,
114     *unseen_dbg_info_list = NULL;
115 static C_word current_c = 0;
116 static C_word *current_av;
117 static volatile int interrupted = 0;
118 static int dbg_info_count = 0;
119 
120 
121 static C_word debug_event_hook(C_DEBUG_INFO *cell, C_word c, C_word *av, C_char *cloc);
122 
123 
124 void
C_register_debug_info(C_DEBUG_INFO * info)125 C_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 
147 static int
socket_read()148 socket_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 
182 static int
socket_write(char * buf,int len)183 socket_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 
200 static void
socket_close()201 socket_close()
202 {
203   closesocket(socket_fd);
204   socket_fd = 0;
205 }
206 
207 
208 static void
terminate(char * msg)209 terminate(char *msg)
210 {
211   fprintf(stderr, "%s\n", msg);
212   socket_close();
213   C_exit_runtime(C_fix(1));
214 }
215 
216 
217 static char *
name_and_length(char * buf,int * len)218 name_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 
235 static void
enable_debug_info(int n,int f)236 enable_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 
255 static void
send_string(C_char * str)256 send_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 
265 static void
send_string_value(C_char * str)266 send_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 
275 static void
send_scheme_value(C_word x)276 send_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 
289 static void
send_event(int event,C_char * loc,C_char * val,C_char * cloc)290 send_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
493 static void
interrupt_signal_handler(int signum)494 interrupt_signal_handler(int signum)
495 {
496     interrupted = 1;
497     C_signal(SIGUSR2, interrupt_signal_handler);
498 }
499 #endif
500 
501 
502 static C_word
connect_to_debugger()503 connect_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 
567 static C_word
debug_event_hook(C_DEBUG_INFO * cell,C_word c,C_word * av,C_char * cloc)568 debug_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 */
590