~ chicken-core (master) /dbg-stub.c
Trap1/* dbg-stub.c - Client-side interface, lowlevel part2;3; Copyright (c) 2008-2022, The CHICKEN Team4; Copyright (c) 2000-2007, Felix L. Winkelmann5; All rights reserved.6;7; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following8; conditions are met:9;10; Redistributions of source code must retain the above copyright notice, this list of conditions and the following11; disclaimer.12; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following13; 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 promote15; 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 EXPRESS18; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY19; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR20; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR21; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR22; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY23; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR24; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE25; POSSIBILITY OF SUCH DAMAGE.26*/272829/* included from debugger-client.scm */3031#include <assert.h>3233#ifdef _WIN3234# include <winsock2.h>35# include <ws2tcpip.h>36/* Beware: winsock2.h must come BEFORE windows.h */37# define socklen_t int38static WSADATA wsa;39#else40# 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 close48# define INVALID_SOCKET (-1)49# define SOCKET_ERROR (-1)50# ifndef h_addr51# define h_addr h_addr_list[ 0 ]52# endif53#endif545556#define C_DEBUG_PROTOCOL_VERSION 15758#define C_DEBUG_REPLY_UNUSED 059#define C_DEBUG_REPLY_SETMASK 160#define C_DEBUG_REPLY_TERMINATE 261#define C_DEBUG_REPLY_CONTINUE 362#define C_DEBUG_REPLY_SET_BREAKPOINT 463#define C_DEBUG_REPLY_CLEAR_BREAKPOINT 564#define C_DEBUG_REPLY_LIST_EVENTS 665#define C_DEBUG_REPLY_GET_BYTES 766#define C_DEBUG_REPLY_GET_AV 867#define C_DEBUG_REPLY_GET_SLOTS 968#define C_DEBUG_REPLY_GET_GLOBAL 1069#define C_DEBUG_REPLY_GET_STATS 1170#define C_DEBUG_REPLY_GET_TRACE 127172#define INPUT_BUFFER_SIZE 409673#define RW_BUFFER_SIZE 102474#define DEFAULT_DEBUGGER_PORT 99997576#ifdef C_SIXTY_FOUR77# define C_HEADER_BITS_SHIFT 5678# ifdef C_LLP79# define UWORD_COUNT_FORMAT_STRING "%llu"80# else81# define UWORD_COUNT_FORMAT_STRING "%lu"82# endif83#else84# define C_HEADER_BITS_SHIFT 2485# define UWORD_COUNT_FORMAT_STRING "%u"86#endif8788#define C_VALUE_CUTOFF_LIMIT 30089#define get_header_bits(x) ((int)(C_header_bits((x)) >> C_HEADER_BITS_SHIFT))909192struct bp_item {93 char *name;94 int len;95 struct bp_item *next;96};9798struct dbg_info_list {99 C_DEBUG_INFO *info;100 struct dbg_info_list *next;101};102103104static 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_list112 *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;119120121static C_word debug_event_hook(C_DEBUG_INFO *cell, C_word c, C_word *av, C_char *cloc);122123124void125C_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));129130 /* fprintf(stderr, "Registering: %p (%s/%s)\n", node, info->loc, info->val); */131 assert(node);132 node->info = info;133 node->next = NULL;134135 if(last_dbg_info_list != NULL) last_dbg_info_list->next = node;136137 last_dbg_info_list = node;138139 if(unseen_dbg_info_list == NULL) unseen_dbg_info_list = node;140141 if(dbg_info_list == NULL) dbg_info_list = node;142143 /* fprintf(stderr, "first: %p, last: %p, unseen: %p\n", dbg_info_list, last_dbg_info_list, unseen_dbg_info_list); */144}145146147static int148socket_read()149{150 int p = 0, s = 0, e = 0;151 int n, off = 0;152 char *ptr = rw_buffer;153154 /* copy from input_buffer into rw_buffer until newline: */155 for(;;) {156 while(input_buffer_len > 0) {157 *(ptr++) = *input_buffer_top;158159 if(*(input_buffer_top++) == '\n') {160 *ptr = '\0';161 --input_buffer_len;162 return 1;163 }164165 if(++off >= RW_BUFFER_SIZE) return -1; /* read-buffer overflow */166167 --input_buffer_len;168 }169170 n = recv(socket_fd, input_buffer, INPUT_BUFFER_SIZE, 0);171172 if(n == SOCKET_ERROR) return -1; /* read failed */173174 if(n == 0) return 0; /* client disconnect */175176 input_buffer_len = n;177 input_buffer_top = input_buffer;178 }179}180181182static int183socket_write(char *buf, int len)184{185 int n, m = 0, off = 0;186187 while(m < len) {188 n = send(socket_fd, buf + off, len, 0);189190 if(n == SOCKET_ERROR) return -1; /* write failed */191192 off += n;193 m += n;194 }195196 return 0;197}198199200static void201socket_close()202{203 closesocket(socket_fd);204 socket_fd = 0;205}206207208static void209terminate(char *msg)210{211 fprintf(stderr, "%s\n", msg);212 socket_close();213 C_fflush(NULL);214 C_exit_runtime(C_fix(1));215}216217218static char *219name_and_length(char *buf, int *len)220{221 char *str, *ptr;222223 for(str = buf; *str && *str != '\"'; ++str);224225 if(!*str) return "";226227 for(ptr = ++str; *ptr != '\"'; ++ptr) {228 if(*ptr == '\\') ++ptr;229 }230231 *len = ptr - str;232 return str;233}234235236static void237enable_debug_info(int n, int f)238{239 int i = 0;240 struct dbg_info_list *dip;241 C_DEBUG_INFO *dinfo;242243 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 }251252 terminate("invalid debug-info index");253}254255256static void257send_string(C_char *str)258{259 /* fprintf(stderr, "<SENT: %s>\n", str); */260 C_fflush(stderr);261262 if(socket_write(str, C_strlen(str)) != 0)263 terminate("write failed");264}265266static void267send_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}275276static void277send_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 else284 C_snprintf(rw_buffer, sizeof(rw_buffer), " @%lu", (unsigned long)x);285286 send_string(rw_buffer);287}288289290static void291send_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;301302 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");309310 n = socket_read();311312 if(n < 0) terminate("read failed");313314 if(n == 0) terminate("debugger disconnected");315316 /* fprintf(stderr, "<READ: %s>\n", rw_buffer); */317 n = sscanf(rw_buffer, "(%d ", &reply);318319 if(n == 0) terminate("invalid reply");320321 switch(reply) {322 case C_DEBUG_REPLY_SETMASK:323 n = sscanf(rw_buffer, "(%d %d)", &reply, &mask);324325 if(n != 2) terminate("invalid SETMASK reply");326327 event_mask = mask;328 break;329330 case C_DEBUG_REPLY_TERMINATE:331 terminate("terminated by debugger");332333 case C_DEBUG_REPLY_CONTINUE:334 return;335336 case C_DEBUG_REPLY_SET_BREAKPOINT:337 n = sscanf(rw_buffer, "(%d %d)", &reply, &mask);338339 if(n != 2) terminate("invalid SET BREAKPOINT reply");340341 enable_debug_info(mask, 1);342 break;343344 case C_DEBUG_REPLY_CLEAR_BREAKPOINT:345 n = sscanf(rw_buffer, "(%d %d)", &reply, &mask);346347 if(n != 2) terminate("invalid CLEAR BREAKPOINT reply");348349 enable_debug_info(mask, 0);350 break;351352 case C_DEBUG_REPLY_LIST_EVENTS:353 str = name_and_length(rw_buffer, &n);354 str[ n ] = '\0';355 str = C_strdup(str);356357 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 }366367 ++n;368 }369 }370371 unseen_dbg_info_list = NULL;372 C_free(str);373 break;374375 case C_DEBUG_REPLY_GET_BYTES:376 n = sscanf(rw_buffer, "(%d " UWORD_COUNT_FORMAT_STRING " %d)",377 &reply, &x, &mask);378379 if(n != 3) terminate("invalid GET_BYTES reply");380381 ptr = (char *)x;382383 send_string("(*");384385 while(mask--) {386 C_snprintf(rw_buffer, sizeof(rw_buffer), " %u", (unsigned char) *(ptr++));387 send_string(rw_buffer);388 }389390 send_string(")\n");391 break;392393 case C_DEBUG_REPLY_GET_AV:394 send_string("(*");395396 for(n = 0; n < current_c; ++n)397 send_scheme_value(current_av[ n ]);398399 send_string(")\n");400 break;401402 case C_DEBUG_REPLY_GET_SLOTS:403 sscanf(rw_buffer, "(%d " UWORD_COUNT_FORMAT_STRING ")", &mask, &x);404405 if(mask >= C_VALUE_CUTOFF_LIMIT)406 mask = C_VALUE_CUTOFF_LIMIT;407408 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);412413 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 }417418 send_string(")\n");419 break;420 }421422 n = 0;423424 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));430431 send_string(rw_buffer);432433 for(mask = C_header_size(x); n < mask; ++n)434 send_scheme_value(C_block_item(x, n));435436 send_string(")\n");437 break;438439 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);445446 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 }453454 break;455456 case C_DEBUG_REPLY_GET_STATS:457 stats = C_get_statistics();458 send_string("(*");459460 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 }464465 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;469470 case C_DEBUG_REPLY_GET_TRACE:471 str = ptr = C_dump_trace(0);472473 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 }480481 free(str);482 break;483484 default: terminate("invalid reply code");485 }486487 event = C_DEBUG_LISTEN;488 val = unseen_dbg_info_list ? "1" : "0";489 }490}491492493#ifndef _WIN32494static void495interrupt_signal_handler(int signum)496{497 interrupted = 1;498 C_signal(SIGUSR2, interrupt_signal_handler);499}500#endif501502503static C_word504connect_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;514515 C_debugger_hook = debug_event_hook;516517 if(addr == NULL) return C_SCHEME_FALSE; /* no debugger address given */518519 /* parse host and port number */520 for(i = C_strlen(addr) - 1; i > 0; --i) {521 if(addr[ i ] == ':') break;522 }523524 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 }530531#ifdef _WIN32532 if(WSAStartup(MAKEWORD(1, 1), &wsa) != 0)533 return C_SCHEME_FALSE; /* failed to init sockets */534#endif535536 /* obtain host address */537 he = gethostbyname(host);538539 if(he == NULL) return C_SCHEME_FALSE; /* invalid host */540541 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);546547 if(socket_fd == INVALID_SOCKET)548 return C_SCHEME_FALSE; /* can not create socket */549550 /* socket options */551 r = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(int));552553 if(r != 0) return C_SCHEME_FALSE; /* failed to set socket options */554555 /* connect */556 if(connect(socket_fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) == SOCKET_ERROR)557 return C_SCHEME_FALSE; /* failed to connect */558559 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 _WIN32562 C_signal(SIGUSR2, interrupt_signal_handler);563#endif564 return C_SCHEME_TRUE;565}566567568static C_word569debug_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 }580581 return C_SCHEME_UNDEFINED;582}583584585/* TODO:586587 - escape '\"' + '\\' in transmitted strings588 - error-condition (SIGNAL event) doesn't seem to terminate589590*/