~ chicken-core (master) /dbg-stub.c


  1/* 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_fflush(NULL);
214  C_exit_runtime(C_fix(1));
215}
216
217
218static char *
219name_and_length(char *buf, int *len)
220{
221    char *str, *ptr;
222
223    for(str = buf; *str && *str != '\"'; ++str);
224
225    if(!*str) return "";
226
227    for(ptr = ++str; *ptr != '\"'; ++ptr) {
228        if(*ptr == '\\') ++ptr;
229    }
230
231    *len = ptr - str;
232    return str;
233}
234
235
236static void
237enable_debug_info(int n, int f)
238{
239    int i = 0;
240    struct dbg_info_list *dip;
241    C_DEBUG_INFO *dinfo;
242
243    for(dip = dbg_info_list; dip != NULL; dip = dip->next) {
244        for(dinfo = dip->info; dinfo->event; ++dinfo) {
245            if(i++ == n) {
246                dinfo->enabled = f;
247                return;
248            }
249        }
250    }
251
252    terminate("invalid debug-info index");
253}
254
255
256static void
257send_string(C_char *str)
258{
259  /* fprintf(stderr, "<SENT: %s>\n", str); */
260  C_fflush(stderr);
261
262  if(socket_write(str, C_strlen(str)) != 0)
263    terminate("write failed");
264}
265
266static void
267send_string_value(C_char *str) {
268  if (str == 0 || *str == 0)
269    send_string(" #f");
270  else {
271    C_snprintf(rw_buffer, sizeof(rw_buffer), " \"%s\"", str);
272    send_string(rw_buffer);
273  }
274}
275
276static void
277send_scheme_value(C_word x)
278{
279  if((x & C_FIXNUM_BIT) != 0)
280    C_snprintf(rw_buffer, sizeof(rw_buffer), " %ld", (long)C_unfix(x));
281  else if((x & C_IMMEDIATE_MARK_BITS) != 0)
282    C_snprintf(rw_buffer, sizeof(rw_buffer), " =%lu", (unsigned long)x);
283  else
284    C_snprintf(rw_buffer, sizeof(rw_buffer), " @%lu", (unsigned long)x);
285
286  send_string(rw_buffer);
287}
288
289
290static void
291send_event(int event, C_char *loc, C_char *val, C_char *cloc)
292{
293  int n;
294  int reply, mask;
295  struct bp_item *bp, *prev;
296  C_char *str, *ptr;
297  struct dbg_info_list *dip;
298  C_DEBUG_INFO *dinfo;
299  C_word x;
300  void **stats;
301
302  for(;;) {
303    C_snprintf(rw_buffer, sizeof(rw_buffer), "(%d", event);
304    send_string(rw_buffer);
305    send_string_value(loc);
306    send_string_value(val);
307    send_string_value(cloc);
308    send_string(")\n");
309
310    n = socket_read();
311
312    if(n < 0) terminate("read failed");
313
314    if(n == 0) terminate("debugger disconnected");
315
316    /* fprintf(stderr, "<READ: %s>\n", rw_buffer); */
317    n = sscanf(rw_buffer, "(%d ", &reply);
318
319    if(n == 0) terminate("invalid reply");
320
321    switch(reply) {
322    case C_DEBUG_REPLY_SETMASK:
323      n = sscanf(rw_buffer, "(%d %d)", &reply, &mask);
324
325      if(n != 2) terminate("invalid SETMASK reply");
326
327      event_mask = mask;
328      break;
329
330    case C_DEBUG_REPLY_TERMINATE:
331      terminate("terminated by debugger");
332
333    case C_DEBUG_REPLY_CONTINUE:
334      return;
335
336    case C_DEBUG_REPLY_SET_BREAKPOINT:
337      n = sscanf(rw_buffer, "(%d %d)", &reply, &mask);
338
339      if(n != 2) terminate("invalid SET BREAKPOINT reply");
340
341      enable_debug_info(mask, 1);
342      break;
343
344    case C_DEBUG_REPLY_CLEAR_BREAKPOINT:
345      n = sscanf(rw_buffer, "(%d %d)", &reply, &mask);
346
347      if(n != 2) terminate("invalid CLEAR BREAKPOINT reply");
348
349      enable_debug_info(mask, 0);
350      break;
351
352    case C_DEBUG_REPLY_LIST_EVENTS:
353      str = name_and_length(rw_buffer, &n);
354      str[ n ] = '\0';
355      str = C_strdup(str);
356
357      for(dip = unseen_dbg_info_list; dip != NULL; dip = dip->next) {
358          for(dinfo = dip->info; dinfo->event; ++dinfo) {
359              if(*str == '\0' || strstr(dinfo->val, str)) {
360                  C_snprintf(rw_buffer, sizeof(rw_buffer), "(* %d %d", dbg_info_count++, dinfo->event);
361                  send_string(rw_buffer);
362                  send_string_value(dinfo->loc);
363                  send_string_value(dinfo->val);
364                  send_string(")\n");
365              }
366
367              ++n;
368          }
369      }
370
371      unseen_dbg_info_list = NULL;
372      C_free(str);
373      break;
374
375    case C_DEBUG_REPLY_GET_BYTES:
376      n = sscanf(rw_buffer, "(%d " UWORD_COUNT_FORMAT_STRING " %d)",
377                 &reply, &x, &mask);
378
379      if(n != 3) terminate("invalid GET_BYTES reply");
380
381      ptr = (char *)x;
382
383      send_string("(*");
384
385      while(mask--) {
386        C_snprintf(rw_buffer, sizeof(rw_buffer), " %u", (unsigned char) *(ptr++));
387        send_string(rw_buffer);
388      }
389
390      send_string(")\n");
391      break;
392
393    case C_DEBUG_REPLY_GET_AV:
394      send_string("(*");
395
396      for(n = 0; n < current_c; ++n)
397        send_scheme_value(current_av[ n ]);
398
399      send_string(")\n");
400      break;
401
402    case C_DEBUG_REPLY_GET_SLOTS:
403      sscanf(rw_buffer, "(%d " UWORD_COUNT_FORMAT_STRING ")", &mask, &x);
404
405      if(mask >= C_VALUE_CUTOFF_LIMIT)
406        mask = C_VALUE_CUTOFF_LIMIT;
407
408      if((C_header_bits(x) & C_BYTEBLOCK_BIT) != 0) {
409        reply = C_header_size(x);
410        C_snprintf(rw_buffer, sizeof(rw_buffer), "(* BLOB %d", get_header_bits(x));
411        send_string(rw_buffer);
412
413        for(n = 0; n < reply; ++n) {
414          C_snprintf(rw_buffer, sizeof(rw_buffer), " %u", ((unsigned char *)C_data_pointer(x))[ n ]);
415          send_string(rw_buffer);
416        }
417
418        send_string(")\n");
419        break;
420      }
421
422      n = 0;
423
424      if((C_header_bits(x) & C_SPECIALBLOCK_BIT) != 0) {
425        C_snprintf(rw_buffer, sizeof(rw_buffer), "(* SPECIAL %d " UWORD_COUNT_FORMAT_STRING,
426            get_header_bits(x), C_block_item(x, 0));
427        n = 1;
428      }
429      else C_snprintf(rw_buffer, sizeof(rw_buffer), "(* VECTOR %d", get_header_bits(x));
430
431      send_string(rw_buffer);
432
433      for(mask = C_header_size(x); n < mask; ++n)
434        send_scheme_value(C_block_item(x, n));
435
436      send_string(")\n");
437      break;
438
439    case C_DEBUG_REPLY_GET_GLOBAL:
440      str = name_and_length(rw_buffer, &n);
441      ptr = malloc(sizeof(C_header) + n + 1);
442      memcpy(((C_SCHEME_BLOCK*)ptr)->data, str, n + 1);
443      ((C_SCHEME_BLOCK *)ptr)->header = C_make_header(C_BYTEVECTOR_TYPE, n);
444      x = C_find_symbol((C_word)ptr, NULL);
445
446      if(x == C_SCHEME_FALSE)
447        send_string("(* UNKNOWN)\n");
448      else {
449        send_string("(*");
450        send_scheme_value(C_symbol_value(x));
451        send_string(")\n");
452      }
453
454      break;
455
456    case C_DEBUG_REPLY_GET_STATS:
457      stats = C_get_statistics();
458      send_string("(*");
459
460      for(n = 0; n < 8; ++n) {
461        C_snprintf(rw_buffer, sizeof(rw_buffer), " " UWORD_COUNT_FORMAT_STRING, (C_uword)stats[ n ]);
462        send_string(rw_buffer);
463      }
464
465      C_snprintf(rw_buffer, sizeof(rw_buffer), " " UWORD_COUNT_FORMAT_STRING ")\n",
466          (C_uword)C_stack_pointer);
467      send_string(rw_buffer);
468      break;
469
470    case C_DEBUG_REPLY_GET_TRACE:
471      str = ptr = C_dump_trace(0);
472
473      while((n = C_strcspn(ptr, "\n"))) {
474        ptr[ n++ ] = '\0';
475        send_string("(* \"");
476        send_string(ptr);
477        send_string("\")\n");
478        ptr += n;
479      }
480
481      free(str);
482      break;
483
484    default: terminate("invalid reply code");
485    }
486
487    event = C_DEBUG_LISTEN;
488    val = unseen_dbg_info_list ? "1" : "0";
489  }
490}
491
492
493#ifndef _WIN32
494static void
495interrupt_signal_handler(int signum)
496{
497    interrupted = 1;
498    C_signal(SIGUSR2, interrupt_signal_handler);
499}
500#endif
501
502
503static C_word
504connect_to_debugger()
505{
506  char *addr = getenv("CHICKEN_DEBUGGER");
507  char *host;
508  static char info[ 256 ];
509  struct hostent *he;
510  struct sockaddr_in sa;
511  int i, port = DEFAULT_DEBUGGER_PORT;
512  int yes = 1;
513  int r;
514
515  C_debugger_hook = debug_event_hook;
516
517  if(addr == NULL) return C_SCHEME_FALSE;      /* no debugger address given */
518
519  /* parse host and port number */
520  for(i = C_strlen(addr) - 1; i > 0; --i) {
521    if(addr[ i ] == ':') break;
522  }
523
524  if(i == 0) host = addr;
525  else {
526    port = atoi(addr + i + 1);
527    host = C_strdup(addr);
528    host[i] = '\0';    /* We don't use strndup() for compat reasons */
529  }
530
531#ifdef _WIN32
532  if(WSAStartup(MAKEWORD(1, 1), &wsa) != 0)
533    return C_SCHEME_FALSE; /* failed to init sockets */
534#endif
535
536  /* obtain host address */
537  he = gethostbyname(host);
538
539  if(he == NULL) return C_SCHEME_FALSE;        /* invalid host */
540
541  C_memset(&sa, 0, sizeof(struct sockaddr_in));
542  sa.sin_family = AF_INET;
543  sa.sin_port = htons((short)port);
544  sa.sin_addr = *((struct in_addr *)he->h_addr);
545  socket_fd = socket(AF_INET, SOCK_STREAM, 0);
546
547  if(socket_fd == INVALID_SOCKET)
548    return C_SCHEME_FALSE; /* can not create socket */
549
550  /* socket options */
551  r = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(int));
552
553  if(r != 0) return C_SCHEME_FALSE;            /* failed to set socket options */
554
555  /* connect */
556  if(connect(socket_fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) == SOCKET_ERROR)
557    return C_SCHEME_FALSE;                     /* failed to connect */
558
559  C_snprintf(info, sizeof(info), "%s:%d:%d", C_main_argv[ 0 ], getpid(), C_DEBUG_PROTOCOL_VERSION);
560  send_event(C_DEBUG_CONNECT, info, NULL, NULL);
561#ifndef _WIN32
562  C_signal(SIGUSR2, interrupt_signal_handler);
563#endif
564  return C_SCHEME_TRUE;
565}
566
567
568static C_word
569debug_event_hook(C_DEBUG_INFO *cell, C_word c, C_word *av, C_char *cloc)
570{
571  if(socket_fd != 0) {
572    if(cell->enabled || interrupted || ((1 << cell->event) & event_mask) != 0 ) {
573      /* fprintf(stderr, "event: %s\n", cloc); */
574      current_c = c;
575      current_av = av;
576      send_event(interrupted ? C_DEBUG_INTERRUPTED : cell->event, cell->loc, cell->val, cloc);
577      interrupted = 0;
578    }
579  }
580
581  return C_SCHEME_UNDEFINED;
582}
583
584
585/* TODO:
586
587    - escape '\"' + '\\' in transmitted strings
588    - error-condition (SIGNAL event) doesn't seem to terminate
589
590*/
Trap