xdebug

next generation of mdebug (work in progress)
git clone http://frotz.net/git/xdebug.git
Log | Files | Refs | README

xdebug.c (6119B)


      1 // Copyright 2023, Brian Swetland <swetland@frotz.net>
      2 // Licensed under the Apache License, Version 2.0.
      3 
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <unistd.h>
      7 #include <string.h>
      8 #include <stdarg.h>
      9 
     10 #include <pthread.h>
     11 #include <sys/eventfd.h>
     12 #include <poll.h>
     13 
     14 #include "xdebug.h"
     15 #include "tui.h"
     16 
     17 #include "transport.h"
     18 
     19 #define MAX_ARGS 16
     20 
     21 static int debug = 0;
     22 
     23 static const char* NTH(unsigned n) {
     24 	switch (n) {
     25 	case 1: return "1st ";
     26 	case 2: return "2nd ";
     27 	case 3: return "3rd ";
     28 	case 4: return "4th ";
     29 	case 5: return "5th ";
     30 	default: return "";
     31 	}
     32 }
     33 
     34 #define tSTRING 0x01
     35 #define tNUMBER 0x02
     36 
     37 typedef struct token {
     38 	const char* s;
     39 	uint32_t n;
     40 	uint32_t info;
     41 } TOKEN;
     42 
     43 struct command_context {
     44 	TOKEN tok[MAX_ARGS];
     45 	unsigned count;
     46 };
     47 
     48 const char* cmd_name(CC* cc) {
     49 	return cc->tok[0].s;
     50 }
     51 
     52 int cmd_argc(CC* cc) {
     53 	return cc->count;
     54 }
     55 
     56 int cmd_arg_u32(CC* cc, unsigned nth, uint32_t* out) {
     57 	if (nth >= cc->count) {
     58 		ERROR("%s: missing %sargument\n", cc->tok[0].s, NTH(nth));
     59 		return DBG_ERR;
     60 	}
     61 	if (!(cc->tok[nth].info & tNUMBER)) {
     62 		ERROR("%s: %sargument not a number\n", cc->tok[0].s, NTH(nth));
     63 		return DBG_ERR;
     64 	}
     65 	*out = cc->tok[nth].n;
     66 	return 0;
     67 }
     68 
     69 int cmd_arg_u32_opt(CC* cc, unsigned nth, uint32_t* out, uint32_t val) {
     70 	if (nth >= cc->count) {
     71 		*out = val;
     72 		return 0;
     73 	}
     74 	if (!(cc->tok[nth].info & tNUMBER)) {
     75 		ERROR("%s: %sargument not a number\n", cc->tok[0].s, NTH(nth));
     76 		return DBG_ERR;
     77 	}
     78 	*out = cc->tok[nth].n;
     79 	return 0;
     80 }
     81 
     82 int cmd_arg_str(CC* cc, unsigned nth, const char** out) {
     83 	if (nth >= cc->count) {
     84 		ERROR("%s: missing %sargument\n", cc->tok[0].s, NTH(nth));
     85 		return DBG_ERR;
     86 	}
     87 	*out = cc->tok[nth].s;
     88 	return 0;
     89 }
     90 
     91 int cmd_arg_str_opt(CC* cc, unsigned nth, const char** out, const char* str) {
     92 	if (nth >= cc->count) {
     93 		*out = str;
     94 	} else {
     95 		*out = cc->tok[nth].s;
     96 	}
     97 	return 0;
     98 }
     99 
    100 static DC* dc;
    101 
    102 int parse(TOKEN* tok) {
    103 	char *end;
    104 	if ((tok->s[0] == '.') && tok->s[1]) {
    105 		// decimal
    106 		tok->n = strtoul(tok->s + 1, &end, 10);
    107 		if (*end == 0) {
    108 			tok->info = tNUMBER;
    109 			return 0;
    110 		}
    111 	}
    112 	tok->n = strtoul(tok->s, &end, 16);
    113 	if (*end == 0) {
    114 		tok->info = tNUMBER;
    115 		return 0;
    116 	}
    117 	tok->n = 0;
    118 	tok->info = tSTRING;
    119 	return 0;
    120 }
    121 
    122 void debug_command(char *line) {
    123 	CC cc;
    124 
    125 	INFO("> %s\n", line);
    126 
    127 	while (*line && (*line <= ' ')) line++;
    128 	if (*line == '/') {
    129 		cc.count = 2;
    130 		cc.tok[0].s = "wconsole";
    131 		cc.tok[0].info = tSTRING;
    132 		cc.tok[1].s = line + 1;
    133 		cc.tok[1].info = tSTRING;
    134 		cc.count = 2;
    135 		debugger_command(dc, &cc);
    136 		return;
    137 	}
    138 
    139 	unsigned c, n = 0;
    140 	while ((c = *line)) {
    141 		if (c <= ' ') {
    142 			line++;
    143 			continue;
    144 		}
    145 		if (n == MAX_ARGS) {
    146 			ERROR("too many arguments\n");
    147 			return;
    148 		}
    149 		cc.tok[n].s = line;
    150 		for (;;) {
    151 			if (c == 0) {
    152 				n++;
    153 				break;
    154 			} else if (c == '#') {
    155 				*line = 0;
    156 				break;
    157 			} else if (c <= ' ') {
    158 				*line++ = 0;
    159 				n++;
    160 				break;
    161 			}
    162 			c = *++line;
    163 		}
    164 	}
    165 
    166 	if (n == 0) {
    167 		return;
    168 	}
    169 
    170 	cc.tok[0].info = tSTRING;
    171 	for (c = 1; c < n; c++) {
    172 		if (parse(cc.tok + c) < 0) {
    173 			return;
    174 		}	
    175 	}
    176 	cc.count = n;
    177 	debugger_command(dc, &cc);
    178 }
    179 
    180 static volatile int running = 1;
    181 static volatile int busy = 0;
    182 static int efd = -1;
    183 static char linebuf[1024];
    184 
    185 static void *work_thread(void* arg) {
    186 	struct pollfd pfd = {
    187 		.fd = efd,
    188 		.events = POLLIN,
    189 	};
    190 	int timeout = 250;
    191 	while (running) {
    192 		int r = poll(&pfd, 1, timeout);
    193 		if (r < 0) {
    194 			exit(-1);
    195 		}
    196 		if (r == 0) {
    197 			timeout = dc_periodic(dc);
    198 			if (timeout < 100) {
    199 				timeout = 100;
    200 			}
    201 			continue;
    202 		}
    203 		uint64_t n;
    204 		if (read(efd, &n, sizeof(n)) != sizeof(n)) {
    205 			break;
    206 		}
    207 		if (busy) {
    208 			debug_command(linebuf);
    209 			busy = 0;
    210 		}
    211 	}
    212 	return 0;
    213 }
    214 
    215 const char* status_text(uint32_t status) {
    216 	switch (status) {
    217 	case DC_ATTACHED:
    218 		return "[ATTACHED]";
    219 	case DC_FAILURE:
    220 	case DC_DETACHED:
    221 	case DC_UNCONFIG:
    222 		return "[DETACHED]";
    223 	case DC_OFFLINE:
    224 	default:
    225 		return "[OFFLINE]";
    226 	}
    227 }
    228 
    229 void debugger_exit(void) {
    230 	// tell threads to exit
    231 	running = 0;
    232 
    233 	// cancel long lived operations
    234 	dc_interrupt(dc);
    235 
    236 	// wake debugger thread
    237 	uint64_t n = 1;
    238 	if (write(efd, &n, sizeof(n))) {}
    239 }
    240 
    241 void handle_status(void* cookie, uint32_t status) {
    242 	tui_status_rhs(status_text(status));
    243 }
    244 
    245 void handle_line(char *line, unsigned len) {
    246 	if (!strcmp(line, "@ESC@")) {
    247 		dc_interrupt(dc);
    248 		return;
    249 	}
    250 	if (!strcmp(line, "quit") || !strcmp(line, "exit")) {
    251 		debugger_exit();
    252 		return;
    253 	}
    254 	if (len == 0) {
    255 		return;
    256 	}
    257 	if (busy) {
    258 		INFO("busy\n");
    259 		return;
    260 	}
    261 	if (len < (sizeof(linebuf)-1)) {
    262 		memcpy(linebuf, line, len + 1);
    263 		busy = 1;
    264 		uint64_t n = 1;
    265 		if (write(efd, &n, sizeof(n))) {}
    266 	}
    267 }
    268 
    269 static tui_ch_t* ch;
    270 
    271 int main(int argc, char** argv) {
    272 	for (int n = 1; n < argc; n++) {
    273 		if (!strcmp(argv[n], "-usb")) {
    274 			n++;
    275 			if (n == argc) {
    276 				fprintf(stderr, "option -usb requires vid:pid\n");
    277 				return -1;
    278 			}
    279 			char *x = strchr(argv[n], ':');
    280 			if (x == NULL) {
    281 				fprintf(stderr, "option -usb requires vid:pid\n");
    282 				return -1;
    283 			}
    284 			dc_require_vid_pid(strtoul(argv[n], 0, 16), strtoul(x+1, 0, 16));
    285 		} else if (!strcmp(argv[n], "-sn")) {
    286 			n++;
    287 			if (n == argc) {
    288 				fprintf(stderr, "option -sn requires serialno\n");
    289 				return -1;
    290 			}
    291 			dc_require_serialno(argv[n]);
    292 		} else {
    293 			fprintf(stderr, "unknown option '%s'\n", argv[n]);
    294 			return -1;
    295 		}
    296 	}
    297 
    298 	if ((efd = eventfd(0, 0)) < 0) {
    299 		fprintf(stderr, "cannot create eventfd\n");
    300 		return -1;
    301 	}
    302 
    303 	tui_init();
    304 	tui_ch_create(&ch, 0);
    305 	dc_create(&dc, handle_status, NULL);
    306 
    307 	pthread_t t;
    308 	if (pthread_create(&t, NULL, work_thread, NULL) != 0) {
    309 		fprintf(stderr, "cannot start thread\n");
    310 		return -1;
    311 	}
    312 
    313 	while (running && (tui_handle_event(handle_line) == 0)) ;
    314 
    315 	debugger_exit();
    316 
    317 	pthread_join(t, NULL);
    318 	
    319 	tui_exit();
    320 	return 0;
    321 }
    322 
    323 void MSG(uint32_t flags, const char* fmt, ...) {
    324 	va_list ap;
    325 	va_start(ap, fmt);
    326 	switch (flags) {
    327 	case mDEBUG:
    328 		if (debug) {
    329 			tui_ch_printf(ch, "debug: ");
    330 		} else {
    331 			return;
    332 		}
    333 		break;
    334 	case mTRACE:
    335 		tui_ch_printf(ch, "trace: ");
    336 		break;
    337 	case mPANIC:
    338 		tui_exit();
    339 		fprintf(stderr,"panic: ");
    340 		vfprintf(stderr, fmt, ap);
    341 		exit(-1);
    342 	}
    343 	tui_ch_vprintf(ch, fmt, ap);
    344 	va_end(ap);
    345 }