The vCont handler used some overcomplicated logic, we only need to iterate over the actions and apply them on the matching threads that didn't match yet.
Thanks to DBG_REPLY_LATER we can now continue/step any thread regardless of whether it is the one that raised the debug event. Just suspend all active threads after debug event is raised and resume them one by one, according to the gdb request. If the thread that raised the debug event should not be resumed, reply with DBG_REPLY_LATER.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- programs/winedbg/debugger.h | 1 + programs/winedbg/gdbproxy.c | 289 ++++++++++-------------------------- programs/winedbg/winedbg.c | 1 + 3 files changed, 80 insertions(+), 211 deletions(-)
diff --git a/programs/winedbg/debugger.h b/programs/winedbg/debugger.h index 40d93d669c82..ddac129bab53 100644 --- a/programs/winedbg/debugger.h +++ b/programs/winedbg/debugger.h @@ -207,6 +207,7 @@ struct dbg_thread }* frames; int num_frames; int curr_frame; + BOOL suspended; };
struct dbg_delayed_bp diff --git a/programs/winedbg/gdbproxy.c b/programs/winedbg/gdbproxy.c index 078a140a611d..0e60bbb6457f 100644 --- a/programs/winedbg/gdbproxy.c +++ b/programs/winedbg/gdbproxy.c @@ -88,6 +88,7 @@ struct gdb_context int other_tid; /* tid to be used in any other operation */ /* current Win32 trap env */ DEBUG_EVENT de; + DWORD de_reply; unsigned last_sig; BOOL in_trap; /* Win32 information */ @@ -137,17 +138,6 @@ static inline unsigned char hex_to0(int x) return "0123456789abcdef"[x]; }
-static int hex_to_int(const char* src, size_t len) -{ - unsigned int returnval = 0; - while (len--) - { - returnval <<= 4; - returnval |= hex_from0(*src++); - } - return returnval; -} - static void hex_from(void* dst, const char* src, size_t len) { unsigned char *p = dst; @@ -397,6 +387,7 @@ static BOOL handle_exception(struct gdb_context* gdbctx, EXCEPTION_DEBUG_INFO* e static void handle_debug_event(struct gdb_context* gdbctx) { DEBUG_EVENT *de = &gdbctx->de; + struct dbg_thread *thread;
union { char bufferA[256]; @@ -406,6 +397,7 @@ static void handle_debug_event(struct gdb_context* gdbctx) dbg_curr_thread = dbg_get_thread(gdbctx->process, de->dwThreadId); gdbctx->exec_tid = de->dwThreadId; gdbctx->other_tid = de->dwThreadId; + gdbctx->de_reply = DBG_REPLY_LATER;
switch (de->dwDebugEventCode) { @@ -518,35 +510,34 @@ static void handle_debug_event(struct gdb_context* gdbctx) FIXME("%08x:%08x: unknown event (%u)\n", de->dwProcessId, de->dwThreadId, de->dwDebugEventCode); } -}
-static void resume_debuggee(struct gdb_context* gdbctx, DWORD cont) -{ - if (dbg_curr_thread) + if (!gdbctx->in_trap || !gdbctx->process) return; + + LIST_FOR_EACH_ENTRY(thread, &gdbctx->process->threads, struct dbg_thread, entry) { - if (!ContinueDebugEvent(gdbctx->process->pid, dbg_curr_thread->tid, cont)) - ERR("Failed to continue thread %04x, error %u\n", - dbg_curr_thread->tid, GetLastError()); + if (!thread->suspended) SuspendThread(thread->handle); + thread->suspended = TRUE; } - else - ERR("Cannot find last thread\n"); }
- -static void resume_debuggee_thread(struct gdb_context* gdbctx, DWORD cont, unsigned int threadid) +static void handle_step_or_continue(struct gdb_context* gdbctx, int tid, BOOL step, int sig) { + struct dbg_process *process = gdbctx->process; + struct dbg_thread *thread;
- if (dbg_curr_thread) + if (tid == 0) tid = dbg_curr_thread->tid; + LIST_FOR_EACH_ENTRY(thread, &process->threads, struct dbg_thread, entry) { - if(dbg_curr_thread->tid == threadid){ - /* Windows debug and GDB don't seem to work well here, windows only likes ContinueDebugEvent being used on the reporter of the event */ - if (!ContinueDebugEvent(gdbctx->process->pid, dbg_curr_thread->tid, cont)) - ERR("Failed to continue thread %04x, error %u\n", - dbg_curr_thread->tid, GetLastError()); - } + if (tid != -1 && thread->tid != tid) continue; + if (!thread->suspended) continue; + thread->suspended = FALSE; + + if (process->pid == gdbctx->de.dwProcessId && thread->tid == gdbctx->de.dwThreadId) + gdbctx->de_reply = (sig == -1 ? DBG_CONTINUE : DBG_EXCEPTION_NOT_HANDLED); + + dbg_thread_set_single_step(thread, step); + ResumeThread(thread->handle); } - else - ERR("Cannot find last thread\n"); }
static BOOL check_for_interrupt(struct gdb_context* gdbctx) @@ -578,6 +569,9 @@ static BOOL check_for_interrupt(struct gdb_context* gdbctx)
static void wait_for_debuggee(struct gdb_context* gdbctx) { + if (gdbctx->de.dwDebugEventCode) + ContinueDebugEvent(gdbctx->de.dwProcessId, gdbctx->de.dwThreadId, gdbctx->de_reply); + gdbctx->in_trap = FALSE; for (;;) { @@ -610,9 +604,11 @@ static void wait_for_debuggee(struct gdb_context* gdbctx)
static void detach_debuggee(struct gdb_context* gdbctx, BOOL kill) { - assert(gdbctx->process->be_cpu); - if (dbg_curr_thread) dbg_thread_set_single_step(dbg_curr_thread, FALSE); - resume_debuggee(gdbctx, DBG_CONTINUE); + handle_step_or_continue(gdbctx, -1, FALSE, -1); + + if (gdbctx->de.dwDebugEventCode) + ContinueDebugEvent(gdbctx->de.dwProcessId, gdbctx->de.dwThreadId, DBG_CONTINUE); + if (!kill) DebugActiveProcessStop(gdbctx->process->pid); dbg_del_process(gdbctx->process); @@ -881,51 +877,25 @@ static enum packet_return packet_last_signal(struct gdb_context* gdbctx)
static enum packet_return packet_continue(struct gdb_context* gdbctx) { - /* FIXME: add support for address in packet */ - assert(gdbctx->in_packet_len == 0); - if (gdbctx->exec_tid > 0 && dbg_curr_thread->tid != gdbctx->exec_tid) - FIXME("Can't continue thread %04x while on thread %04x\n", - gdbctx->exec_tid, dbg_curr_thread->tid); - resume_debuggee(gdbctx, DBG_CONTINUE); + void *addr; + + if (sscanf(gdbctx->in_packet, "%p", &addr) == 1) + FIXME("Continue at address %p not supported\n", addr); + + handle_step_or_continue(gdbctx, gdbctx->exec_tid, FALSE, -1); + wait_for_debuggee(gdbctx); return packet_reply_status(gdbctx); }
static enum packet_return packet_verbose_cont(struct gdb_context* gdbctx) { - int i; - int defaultAction = -1; /* magic non action */ - unsigned char sig; - int actions =0; - int actionIndex[20]; /* allow for up to 20 actions */ - int threadIndex[20]; - int threadCount = 0; - unsigned int threadIDs[100]; /* TODO: Should make this dynamic */ - unsigned int threadID = 0; - struct dbg_thread* thd; - - /* OK we have vCont followed by.. - * ? for query - * c for packet_continue - * Csig for packet_continue_signal - * s for step - * Ssig for step signal - * and then an optional thread ID at the end.. - * *******************************************/ + char *buf = gdbctx->in_packet, *end = gdbctx->in_packet + gdbctx->in_packet_len;
- /* Query */ if (gdbctx->in_packet[4] == '?') { - /* - Reply: - `vCont[;action]...' - The vCont packet is supported. Each action is a supported command in the vCont packet. - `' - The vCont packet is not supported. (this didn't seem to be obeyed!) - */ packet_reply_open(gdbctx); packet_reply_add(gdbctx, "vCont"); - /* add all the supported actions to the reply (all of them for now) */ packet_reply_add(gdbctx, ";c"); packet_reply_add(gdbctx, ";C"); packet_reply_add(gdbctx, ";s"); @@ -934,141 +904,37 @@ static enum packet_return packet_verbose_cont(struct gdb_context* gdbctx) return packet_done; }
- /* go through the packet and identify where all the actions start at */ - for (i = 4; i < gdbctx->in_packet_len - 1; i++) + while (buf < end && (buf = memchr(buf + 1, ';', end - buf - 1))) { - if (gdbctx->in_packet[i] == ';') - { - threadIndex[actions] = 0; - actionIndex[actions++] = i; - } - else if (gdbctx->in_packet[i] == ':') - { - threadIndex[actions - 1] = i; - } - } + int tid = -1, sig = -1; + int action, n;
- /* now look up the default action */ - for (i = 0 ; i < actions; i++) - { - if (threadIndex[i] == 0) + switch ((action = buf[1])) { - if (defaultAction != -1) - { - fprintf(stderr,"Too many default actions specified\n"); + default: + return packet_error; + case 'c': + case 's': + buf += 2; + break; + case 'C': + case 'S': + if (sscanf(buf, ";%*c%2x", &sig) <= 0 || + sig != gdbctx->last_sig) return packet_error; - } - defaultAction = i; - } - } - - /* Now, I have this default action thing that needs to be applied to all non counted threads */ - - /* go through all the threads and stick their ids in the to be done list. */ - LIST_FOR_EACH_ENTRY(thd, &gdbctx->process->threads, struct dbg_thread, entry) - { - threadIDs[threadCount++] = thd->tid; - /* check to see if we have more threads than I counted on, and tell the user what to do - * (they're running winedbg, so I'm sure they can fix the problem from the error message!) */ - if (threadCount == 100) - { - fprintf(stderr, "Wow, that's a lot of threads, change threadIDs in wine/programs/winedbg/gdbproxy.c to be higher\n"); + buf += 4; break; } - } - - /* Ok, now we have... actionIndex full of actions and we know what threads there are, so all - * that remains is to apply the actions to the threads and the default action to any threads - * left */ - if (gdbctx->exec_tid > 0 && dbg_curr_thread->tid != gdbctx->exec_tid) - FIXME("Can't continue thread %04x while on thread %04x\n", - gdbctx->exec_tid, dbg_curr_thread->tid); - - /* deal with the threaded stuff first */ - for (i = 0; i < actions ; i++) - { - if (threadIndex[i] != 0) - { - int j, idLength = 0; - if (i < actions - 1) - { - idLength = (actionIndex[i+1] - threadIndex[i]) - 1; - } - else - { - idLength = (gdbctx->in_packet_len - threadIndex[i]) - 1; - }
- threadID = hex_to_int(gdbctx->in_packet + threadIndex[i] + 1 , idLength); - /* process the action */ - switch (gdbctx->in_packet[actionIndex[i] + 1]) - { - case 's': /* step */ - if (dbg_curr_thread) dbg_thread_set_single_step(dbg_curr_thread, TRUE); - /* fall through*/ - case 'c': /* continue */ - resume_debuggee_thread(gdbctx, DBG_CONTINUE, threadID); - break; - case 'S': /* step Sig, */ - if (dbg_curr_thread) dbg_thread_set_single_step(dbg_curr_thread, TRUE); - /* fall through */ - case 'C': /* continue sig */ - hex_from(&sig, gdbctx->in_packet + actionIndex[i] + 2, 1); - /* cannot change signals on the fly */ - TRACE("sigs: %u %u\n", sig, gdbctx->last_sig); - if (sig != gdbctx->last_sig) - return packet_error; - resume_debuggee_thread(gdbctx, DBG_EXCEPTION_NOT_HANDLED, threadID); - break; - } - for (j = 0 ; j < threadCount; j++) - { - if (threadIDs[j] == threadID) - { - threadIDs[j] = 0; - break; - } - } - } - } /* for i=0 ; i< actions */ + if (buf > end) + return packet_error; + if (buf < end && *buf == ':' && (n = sscanf(buf, ":%x", &tid)) <= 0) + return packet_error;
- /* now we have manage the default action */ - if (defaultAction >= 0) - { - for (i = 0 ; i< threadCount; i++) - { - /* check to see if we've already done something to the thread*/ - if (threadIDs[i] != 0) - { - /* if not apply the default action*/ - threadID = threadIDs[i]; - /* process the action (yes this is almost identical to the one above!) */ - switch (gdbctx->in_packet[actionIndex[defaultAction] + 1]) - { - case 's': /* step */ - if (dbg_curr_thread) dbg_thread_set_single_step(dbg_curr_thread, TRUE); - /* fall through */ - case 'c': /* continue */ - resume_debuggee_thread(gdbctx, DBG_CONTINUE, threadID); - break; - case 'S': - if (dbg_curr_thread) dbg_thread_set_single_step(dbg_curr_thread, TRUE); - /* fall through */ - case 'C': /* continue sig */ - hex_from(&sig, gdbctx->in_packet + actionIndex[defaultAction] + 2, 1); - /* cannot change signals on the fly */ - TRACE("sigs: %u %u\n", sig, gdbctx->last_sig); - if (sig != gdbctx->last_sig) - return packet_error; - resume_debuggee_thread(gdbctx, DBG_EXCEPTION_NOT_HANDLED, threadID); - break; - } - } - } - } /* if(defaultAction >=0) */ + handle_step_or_continue(gdbctx, tid, action == 's' || action == 'S', sig); + }
wait_for_debuggee(gdbctx); - if (gdbctx->process && dbg_curr_thread) dbg_thread_set_single_step(dbg_curr_thread, FALSE); return packet_reply_status(gdbctx); }
@@ -1087,19 +953,21 @@ static enum packet_return packet_verbose(struct gdb_context* gdbctx)
static enum packet_return packet_continue_signal(struct gdb_context* gdbctx) { - unsigned char sig; + void *addr; + int sig, n; + + if ((n = sscanf(gdbctx->in_packet, "%x;%p", &sig, &addr)) == 2) + FIXME("Continue at address %p not supported\n", addr); + if (n < 1) return packet_error;
- /* FIXME: add support for address in packet */ - assert(gdbctx->in_packet_len == 2); - if (gdbctx->exec_tid > 0 && dbg_curr_thread->tid != gdbctx->exec_tid) - FIXME("Can't continue thread %04x while on thread %04x\n", - gdbctx->exec_tid, dbg_curr_thread->tid); - hex_from(&sig, gdbctx->in_packet, 1); - /* cannot change signals on the fly */ - TRACE("sigs: %u %u\n", sig, gdbctx->last_sig); if (sig != gdbctx->last_sig) + { + ERR("Changing signals is not supported.\n"); return packet_error; - resume_debuggee(gdbctx, DBG_EXCEPTION_NOT_HANDLED); + } + + handle_step_or_continue(gdbctx, gdbctx->exec_tid, FALSE, sig); + wait_for_debuggee(gdbctx); return packet_reply_status(gdbctx); } @@ -1674,15 +1542,14 @@ static enum packet_return packet_set(struct gdb_context* gdbctx)
static enum packet_return packet_step(struct gdb_context* gdbctx) { - /* FIXME: add support for address in packet */ - assert(gdbctx->in_packet_len == 0); - if (gdbctx->exec_tid > 0 && dbg_curr_thread->tid != gdbctx->exec_tid) - FIXME("Can't single-step thread %04x while on thread %04x\n", - gdbctx->exec_tid, dbg_curr_thread->tid); - if (dbg_curr_thread) dbg_thread_set_single_step(dbg_curr_thread, TRUE); - resume_debuggee(gdbctx, DBG_CONTINUE); + void *addr; + + if (sscanf(gdbctx->in_packet, "%p", &addr) == 1) + FIXME("Continue at address %p not supported\n", addr); + + handle_step_or_continue(gdbctx, gdbctx->exec_tid, TRUE, -1); + wait_for_debuggee(gdbctx); - if (dbg_curr_thread) dbg_thread_set_single_step(dbg_curr_thread, FALSE); return packet_reply_status(gdbctx); }
diff --git a/programs/winedbg/winedbg.c b/programs/winedbg/winedbg.c index 5dee3b7e5ab4..19f30f04e5dd 100644 --- a/programs/winedbg/winedbg.c +++ b/programs/winedbg/winedbg.c @@ -500,6 +500,7 @@ struct dbg_thread* dbg_add_thread(struct dbg_process* p, DWORD tid, t->num_frames = 0; t->curr_frame = -1; t->addr_mode = AddrModeFlat; + t->suspended = FALSE;
snprintf(t->name, sizeof(t->name), "%04x", tid);