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 }