mdebug

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 709c5f44f7d828d549387d944186309923fcf6ff
parent 9de704baccb3c82ed2ec62e2a4c241a52c437433
Author: Brian Swetland <swetland@frotz.net>
Date:   Thu,  9 Jul 2015 16:33:57 -0700

gdb-bridge: support for debugging lk threads

Diffstat:
Mtools/gdb-bridge.c | 294++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 286 insertions(+), 8 deletions(-)

diff --git a/tools/gdb-bridge.c b/tools/gdb-bridge.c @@ -42,9 +42,53 @@ #define S_CHK1 2 #define S_CHK2 3 -#define F_ACK 1 -#define F_RUNNING 4 -#define F_CONSOLE 8 +#define F_ACK 0x01 +#define F_RUNNING 0x04 +#define F_CONSOLE 0x08 +#define F_LK_THREADS 0x10 + +#define DI_MAGIC 0x52474244 +#define DI_OFF_MAGIC 32 +#define DI_OFF_PTR 36 + +#define LIST_OFF_PREV 0 +#define LIST_OFF_NEXT 4 + +#define LK_MAX_STATE 5 +static char *lkstate[] = { + "SUSP ", + "READY", + "RUN ", + "BLOCK", + "SLEEP", + "DEAD ", + "?????", +}; + +typedef struct lkdebuginfo { + u32 version; + u32 thread_list_ptr; + u32 current_thread_ptr; + u8 off_list_node; + u8 off_state; + u8 off_saved_sp; + u8 off_was_preempted; + u8 off_name; + u8 off_waitq; +} lkdebuginfo_t; + +typedef struct lkthread { + struct lkthread *next; + int active; + u32 threadptr; + u32 nextptr; + u32 state; + u32 saved_sp; + u32 preempted; + u32 waitq; + char name[32]; + u32 regs[17]; +} lkthread_t; struct gdbcnxn { int fd; @@ -57,6 +101,9 @@ struct gdbcnxn { unsigned char rxbuf[MAXPKT]; unsigned char txbuf[MAXPKT]; char chk[4]; + lkthread_t *threadlist; + lkthread_t *gselected; + lkthread_t *cselected; }; void zprintf(const char *fmt, ...) { @@ -77,6 +124,7 @@ void gdb_init(struct gdbcnxn *gc, int fd) { gc->txptr = gc->txbuf; gc->rxptr = gc->rxbuf; gc->chk[2] = 0; + gc->threadlist = NULL; } static inline int rx_full(struct gdbcnxn *gc) { @@ -264,18 +312,45 @@ static const char *target_xml = static void handle_query(struct gdbcnxn *gc, char *cmd, char *args) { if (!strcmp(cmd, "fThreadInfo")) { - /* report just one thread id, #1, for now */ - gdb_puts(gc, "m1"); + if (gc->threadlist) { + char tmp[32]; + lkthread_t *t = gc->threadlist; + sprintf(tmp, "m%x", t->threadptr); + gdb_puts(gc, tmp); + for (t = t->next; t != NULL; t = t->next) { + sprintf(tmp, ",%x",t->threadptr); + gdb_puts(gc, tmp); + } + } else { + /* report just one thread id, #1, for now */ + gdb_puts(gc, "m1"); + } } else if(!strcmp(cmd, "sThreadInfo")) { /* no additional thread ids */ gdb_puts(gc, "l"); } else if(!strcmp(cmd, "ThreadExtraInfo")) { + u32 n = strtoul(args, NULL, 16); + lkthread_t *t; /* gdb manual suggest 'Runnable', 'Blocked on Mutex', etc */ /* informational text shown in gdb's "info threads" listing */ + for (t = gc->threadlist; t != NULL; t = t->next) { + if (t->threadptr == n) { + char tmp[128]; + sprintf(tmp, "%s - %s", lkstate[t->state], t->name); + gdb_puthex(gc, tmp, strlen(tmp)); + return; + } + } gdb_puthex(gc, "Native", 6); } else if(!strcmp(cmd, "C")) { /* current thread ID */ - gdb_puts(gc, "QC1"); + if (gc->cselected) { + char tmp[32]; + sprintf(tmp, "QC%x", gc->cselected->threadptr); + gdb_puts(gc, tmp); + } else { + gdb_puts(gc, "QC1"); + } } else if (!strcmp(cmd, "Rcmd")) { char *p = args; cmd = p; @@ -509,6 +584,150 @@ int handle_breakpoint(int add, u32 addr, u32 kind) { } } +void dump_lk_thread(lkthread_t *t) { + xprintf("thread: @%08x sp=%08x wq=%08x st=%d name='%s'\n", + t->threadptr, t->saved_sp, t->waitq, t->state, t->name); + xprintf(" r0 %08x r4 %08x r8 %08x ip %08x\n", + t->regs[0], t->regs[4], t->regs[8], t->regs[12]); + xprintf(" r1 %08x r5 %08x r9 %08x sp %08x\n", + t->regs[1], t->regs[5], t->regs[9], t->regs[13]); + xprintf(" r2 %08x r6 %08x 10 %08x lr %08x\n", + t->regs[2], t->regs[6], t->regs[10], t->regs[14]); + xprintf(" r3 %08x r7 %08x 11 %08x pc %08x\n", + t->regs[3], t->regs[7], t->regs[11], t->regs[15]); +} + +void dump_lk_threads(lkthread_t *t) { + while (t != NULL) { + dump_lk_thread(t); + t = t->next; + } +} + +#define LT_NEXT_PTR(di,tp) ((tp) + di->off_list_node + LIST_OFF_NEXT) +#define LT_STATE(di,tp) ((tp) + di->off_state) +#define LT_SAVED_SP(di,tp) ((tp) + di->off_saved_sp) +#define LT_NAME(di,tp) ((tp) + di->off_name) +#define LT_WAITQ(di,tp) ((tp) + di->off_waitq) + +#define LIST_TO_THREAD(di,lp) ((lp) - (di)->off_list_node) + +lkthread_t *read_lk_thread(lkdebuginfo_t *di, u32 ptr, int active) { + lkthread_t *t = calloc(1, sizeof(lkthread_t)); + int n; + if (t == NULL) goto fail; + t->threadptr = ptr; + if (swdp_ahb_read(LT_NEXT_PTR(di,ptr), &t->nextptr)) goto fail; + if (swdp_ahb_read(LT_STATE(di,ptr), &t->state)) goto fail; + if (swdp_ahb_read(LT_SAVED_SP(di,ptr), &t->saved_sp)) goto fail; + if (swdp_ahb_read(LT_WAITQ(di,ptr), &t->waitq)) goto fail; + if (swdp_ahb_read32(LT_NAME(di,ptr), (void*) t->name, 32 / 4)) goto fail; + t->name[31] = 0; + for (n = 0; n < 31; n++) { + if ((t->name[n] < ' ') || (t->name[n] > 127)) { + if (t->name[n] == 0) break; + t->name[n] = '.'; + } + } + if (t->state > LK_MAX_STATE) t->state = LK_MAX_STATE + 1; + memset(t->regs, 0xee, sizeof(t->regs)); + // lk arm-m context frame: R4 R5 R6 R7 R8 R9 R10 R11 LR + // if LR is FFFFFFxx then: R0 R1 R2 R3 R12 LR PC PSR + t->active = active; + if (!active) { + u32 fr[9]; + if (swdp_ahb_read32(t->saved_sp, (void*) fr, 9)) goto fail; + memcpy(t->regs + 4, fr, 8 * sizeof(u32)); + if ((fr[8] & 0xFFFFFF00) == 0xFFFFFF00) { + if (swdp_ahb_read32(t->saved_sp + 9 * sizeof(u32), (void*) fr, 8)) goto fail; + memcpy(t->regs + 0, fr, 4 * sizeof(u32)); + t->regs[12] = fr[4]; + t->regs[13] = t->saved_sp + 17 * sizeof(u32); + t->regs[14] = fr[5]; + t->regs[15] = fr[6]; + t->regs[16] = fr[7]; + } else { + t->regs[13] = t->saved_sp + 9 * sizeof(u32); + t->regs[15] = fr[8]; + t->regs[16] = 0x10000000; + } + } + return t; +fail: + free(t); + return NULL; +} + +void free_lk_threads(lkthread_t *list) { + lkthread_t *t, *next; + for (t = list; t != NULL; t = next) { + next = t->next; + free(t); + } +} + +lkthread_t *find_lk_threads(int verbose) { + lkdebuginfo_t di; + lkthread_t *list = NULL; + lkthread_t *current = NULL; + lkthread_t *t; + u32 x; + u32 rtp; + if (swdp_ahb_read(DI_OFF_MAGIC, &x)) goto fail; + if (x != DI_MAGIC) { + if (verbose) xprintf("debuginfo: bad magic\n"); + goto fail; + } + if (swdp_ahb_read(DI_OFF_PTR, &x)) goto fail; + if (x & 3) goto fail; + if (verbose) xprintf("debuginfo @ %08x\n", x); + if (swdp_ahb_read32(x, (void*) &di, sizeof(di) / 4)) goto fail; + if (verbose) { + xprintf("di %08x %08x %08x %d %d %d %d %d %d\n", + di.version, di.thread_list_ptr, di.current_thread_ptr, + di.off_list_node, di.off_state, di.off_saved_sp, + di.off_was_preempted, di.off_name, di.off_waitq); + } + if (di.version != 0x0100) { + if (verbose) xprintf("debuginfo: unsupported version\n"); + goto fail; + } + if (swdp_ahb_read(di.current_thread_ptr, &x)) goto fail; + current = read_lk_thread(&di, x, 1); + rtp = di.thread_list_ptr; + for (;;) { + if (swdp_ahb_read(rtp + LIST_OFF_NEXT, &rtp)) goto fail; + if (rtp == di.thread_list_ptr) break; + x = LIST_TO_THREAD(&di, rtp); + if (current->threadptr == x) continue; + t = read_lk_thread(&di, x, 0); + t->next = list; + list = t; + } + current->next = list; + return current; +fail: + if (current) free(current); + free_lk_threads(list); + return NULL; +} + +void gdb_update_threads(struct gdbcnxn *gc) { + zprintf("GDB: sync threadlist\n"); + free_lk_threads(gc->threadlist); + if (gc->flags & F_LK_THREADS) { + if ((gc->threadlist = find_lk_threads(0)) == NULL) { + zprintf("GDB: problem syncing threadlist\n"); + } + gc->cselected = gc->threadlist; + gc->gselected = gc->threadlist; + } else { + gc->threadlist = NULL; + gc->cselected = NULL; + gc->gselected = NULL; + } +} + void handle_command(struct gdbcnxn *gc, unsigned char *cmd) { union { u32 w[256+2]; @@ -544,11 +763,44 @@ void handle_command(struct gdbcnxn *gc, unsigned char *cmd) { gdb_puts(gc, "S00"); gc->flags &= (~F_RUNNING); swdp_core_halt(); + gdb_update_threads(gc); break; case 'H': + if ((cmd[2] == '-') && (cmd[3] == '1')) { + zprintf("GDB: selected -1??\n"); + } else { + lkthread_t *t; + u32 n = strtoul((char*) cmd + 2, NULL, 16); + for (t = gc->threadlist; t != NULL; t = t->next) { + if (t->threadptr == n) { + zprintf("GDB: selected %c tptr=%x\n", cmd[1], n); + if (cmd[1] == 'g') gc->gselected = t; + if (cmd[1] == 'c') gc->cselected = t; + goto hdone; + } + } + } /* select thread - we've only got one */ + if (cmd[1] == 'g') gc->gselected = gc->threadlist; + if (cmd[1] == 'c') gc->cselected = gc->threadlist; + hdone: gdb_puts(gc, "OK"); break; + // is thread alive? + case 'T': { + lkthread_t *t; + n = strtoul((char*) cmd + 1, NULL, 16); + for (t = gc->threadlist; t != NULL; t = t->next) { + if (t->threadptr == n) { + break; + } + } + if (t) { + gdb_puts(gc, "OK"); + } else { + gdb_puts(gc, "E00"); + } + } // m hexaddr , hexcount // read from memory case 'm': @@ -582,13 +834,22 @@ void handle_command(struct gdbcnxn *gc, unsigned char *cmd) { // read registers 0... case 'g': { u32 regs[19]; - swdp_core_read_all(regs); + if (gc->gselected && !gc->gselected->active) { + memset(regs, 0, sizeof(regs)); + memcpy(regs, gc->gselected->regs, sizeof(gc->gselected->regs)); + } else { + swdp_core_read_all(regs); + } gdb_puthex(gc, regs, sizeof(regs)); break; } // G hexbytes // write registers 0... case 'G': { + if (gc->gselected && !gc->gselected->active) { + zprintf("GDB: attempting to write to inactive registers\n"); + break; + } int len = hextobin(gc->rxbuf, (char*) cmd + 1, MAXPKT); for (n = 0; n < len / 4; n++) { memcpy(&x, gc->rxbuf + (n * 4), sizeof(x)); @@ -601,7 +862,16 @@ void handle_command(struct gdbcnxn *gc, unsigned char *cmd) { // read from register case 'p': { u32 v; - swdp_core_read(strtoul((char*) cmd + 1, NULL, 16), &v); + u32 n = strtoul((char*) cmd + 1, NULL, 16); + if (gc->gselected && !gc->gselected->active) { + if (n > 16) { + v = 0xeeeeeeee; + } else { + v = gc->gselected->regs[n]; + } + } else { + swdp_core_read(n, &v); + } gdb_puthex(gc, &v, sizeof(v)); break; } @@ -610,6 +880,10 @@ void handle_command(struct gdbcnxn *gc, unsigned char *cmd) { case 'P': { int len; char *data = strchr((char*) cmd + 1, '='); + if (gc->gselected && !gc->gselected->active) { + zprintf("GDB: attempting to write to inactive registers\n"); + break; + } if (data) { *data++ = 0; n = strtoul((char*) cmd + 1, NULL, 16); @@ -624,6 +898,7 @@ void handle_command(struct gdbcnxn *gc, unsigned char *cmd) { // halt (^c) case '$': swdp_core_halt(); + gdb_update_threads(gc); gc->flags &= (~F_RUNNING); gdb_puts(gc, "S00"); break; @@ -705,6 +980,8 @@ void gdb_server(int fd) { zprintf("[ gdb connected ]\n"); debugger_unlock(); + gc.flags |= F_LK_THREADS; + for (;;) { fds[0].fd = fd; @@ -727,6 +1004,7 @@ void gdb_server(int fd) { gdb_prologue(&gc); gdb_puts(&gc, "S00"); gdb_epilogue(&gc); + gdb_update_threads(&gc); } } }