From: Eric Pouech <epouech@codeweavers.com> - 'bt <tid>' will backtrace the relevant thread from the minidump. - 'bt all' will backtrace all threads from the minidump. Signed-off-by: Eric Pouech <epouech@codeweavers.com> --- programs/winedbg/dbg.y | 2 +- programs/winedbg/debugger.h | 4 +- programs/winedbg/stack.c | 75 +++++++++++++++++++++++---------- programs/winedbg/tgt_active.c | 13 +++--- programs/winedbg/tgt_minidump.c | 44 ++++++++++++++++++- programs/winedbg/winedbg.c | 3 +- 6 files changed, 105 insertions(+), 36 deletions(-) diff --git a/programs/winedbg/dbg.y b/programs/winedbg/dbg.y index a0d0ebb34f4..a142d67a6dc 100644 --- a/programs/winedbg/dbg.y +++ b/programs/winedbg/dbg.y @@ -140,7 +140,7 @@ command: | tSYMBOLFILE pathname { symbol_read_symtable($2, 0); } | tSYMBOLFILE pathname expr_rvalue { symbol_read_symtable($2, $3); } | tWHATIS expr_lvalue { dbg_printf("type = "); types_print_type(&$2.type, FALSE, NULL); dbg_printf("\n"); } - | tATTACH tNUM { if (dbg_attach_debuggee($2)) dbg_active_wait_for_first_exception(); } + | tATTACH tNUM { if (dbg_attach_debuggee($2, TRUE)) dbg_active_wait_for_first_exception(); } | tATTACH pathname { minidump_reload($2); } | tDETACH { dbg_curr_process->process_io->close_process(dbg_curr_process, FALSE); } | tTHREAD tNUM { dbg_set_curr_thread($2); } diff --git a/programs/winedbg/debugger.h b/programs/winedbg/debugger.h index ff27997c888..25b510f3902 100644 --- a/programs/winedbg/debugger.h +++ b/programs/winedbg/debugger.h @@ -302,6 +302,7 @@ struct be_process_io BOOL (*write)(HANDLE, void*, const void*, SIZE_T, SIZE_T*); BOOL (*get_selector)(HANDLE, DWORD, LDT_ENTRY*); BOOL (*fetch_thread_name)(const struct dbg_thread*, WCHAR**); + BOOL (*fetch_thread_context)(const struct dbg_thread*, dbg_ctx_t *); }; extern struct dbg_process* dbg_curr_process; @@ -311,6 +312,7 @@ extern DWORD dbg_curr_tid; extern dbg_ctx_t dbg_context; extern BOOL dbg_interactiveP; extern HANDLE dbg_houtput; +extern struct list dbg_process_list; struct dbg_internal_var { @@ -483,7 +485,7 @@ extern enum dbg_start dbg_active_launch(int argc, char* argv[]); extern enum dbg_start dbg_active_auto(int argc, char* argv[]); extern enum dbg_start dbg_active_minidump(int argc, char* argv[]); extern void dbg_active_wait_for_first_exception(void); -extern BOOL dbg_attach_debuggee(DWORD pid); +extern BOOL dbg_attach_debuggee(DWORD pid, BOOL verbose); extern void fetch_module_name(void* name_addr, void* mod_addr, WCHAR* buffer, size_t bufsz); extern BOOL dbg_fetch_active_thread_name(DWORD tid, WCHAR **description); diff --git a/programs/winedbg/stack.c b/programs/winedbg/stack.c index 7860e73c088..059b6f37396 100644 --- a/programs/winedbg/stack.c +++ b/programs/winedbg/stack.c @@ -327,6 +327,20 @@ static void backtrace(void) stack_set_local_scope(); } +static const char *thread_display(const struct dbg_thread *thread) +{ + static char buffer[256]; + WCHAR *thread_descr = dbg_fetch_thread_name(thread); + if (thread_descr) + { + snprintf(buffer, sizeof(buffer), "%04lx (%ls)", thread->tid, thread_descr); + free(thread_descr); + } + else + snprintf(buffer, sizeof(buffer), "%04lx", thread->tid); + return buffer; +} + /****************************************************************** * backtrace_tid * @@ -336,23 +350,24 @@ static void backtrace(void) static void backtrace_tid(struct dbg_process* pcs, DWORD tid) { struct dbg_thread* thread = dbg_curr_thread; + dbg_ctx_t ctx = {{0}}; if (!(dbg_curr_thread = dbg_get_thread(pcs, tid))) dbg_printf("Unknown thread id (%04lx) in process (%04lx)\n", tid, pcs->pid); - else + else if (pcs->active_debuggee) { - dbg_ctx_t ctx = {{0}}; - dbg_curr_tid = dbg_curr_thread->tid; if (SuspendThread(dbg_curr_thread->handle) != -1) { if (!pcs->be_cpu->get_context(dbg_curr_thread->handle, &ctx)) { - dbg_printf("Can't get context for thread %04lx in current process\n", - tid); + dbg_printf("Can't get context for thread %s in current process\n", + thread_display(dbg_curr_thread)); } else { + dbg_printf("\nBacktracing for thread %s in process %04lx (%ls):\n", + thread_display(dbg_curr_thread), dbg_curr_pid, dbg_curr_process->imageName); stack_fetch_frames(&ctx); backtrace(); } @@ -360,17 +375,28 @@ static void backtrace_tid(struct dbg_process* pcs, DWORD tid) } else dbg_printf("Can't suspend thread %04lx in current process\n", tid); } + else if (pcs->process_io->fetch_thread_context && + pcs->process_io->fetch_thread_context(dbg_curr_thread, &ctx)) + { + dbg_curr_tid = dbg_curr_thread->tid; + dbg_printf("\nBacktracing for thread %s in process %04lx (%ls):\n", + thread_display(dbg_curr_thread), dbg_curr_pid, dbg_curr_process->imageName); + stack_fetch_frames(&ctx); + backtrace(); + } + else dbg_printf("Can't backtrace thread %04lx in current process\n", tid); + dbg_curr_thread = thread; dbg_curr_tid = thread ? thread->tid : 0; } /****************************************************************** - * backtrace_all + * backtrace_all_active * * Do a backtrace on every running thread in the system (except the debugger) * (preserves current process information) */ -static void backtrace_all(void) +static void backtrace_all_active(void) { struct dbg_process* process = dbg_curr_process; struct dbg_thread* thread = dbg_curr_thread; @@ -402,17 +428,11 @@ static void backtrace_all(void) } else if (entry.th32OwnerProcessID != dbg_curr_pid) { - if (!dbg_attach_debuggee(entry.th32OwnerProcessID)) - { - dbg_printf("\nwarning: could not attach to %04lx\n", - entry.th32OwnerProcessID); + dbg_curr_process = NULL; + if (!dbg_attach_debuggee(entry.th32OwnerProcessID, FALSE)) continue; - } dbg_active_wait_for_first_exception(); } - - dbg_printf("\nBacktracing for thread %04lx in process %04lx (%ls):\n", - entry.th32ThreadID, dbg_curr_pid, dbg_curr_process->imageName); backtrace_tid(dbg_curr_process, entry.th32ThreadID); } while (Thread32Next(snapshot, &entry)); @@ -428,6 +448,18 @@ static void backtrace_all(void) dbg_context = ctx; } +static void backtrace_all_attached(void) +{ + struct dbg_process *pcs; + struct dbg_thread *thread; + + LIST_FOR_EACH_ENTRY(pcs, &dbg_process_list, struct dbg_process, entry) + { + LIST_FOR_EACH_ENTRY(thread, &pcs->threads, struct dbg_thread, entry) + backtrace_tid(pcs, thread->tid); + } +} + void stack_backtrace(DWORD tid) { /* backtrace every thread in every process except the debugger itself, @@ -435,17 +467,16 @@ void stack_backtrace(DWORD tid) */ if (tid == -1) { - backtrace_all(); - return; + if (!dbg_curr_process || dbg_curr_process->active_debuggee) + backtrace_all_active(); + else + backtrace_all_attached(); } - - if (!dbg_curr_process) + else if (!dbg_curr_process) { dbg_printf("You must be attached to a process to run this command.\n"); - return; } - - if (tid == dbg_curr_tid) + else if (tid == dbg_curr_tid) { backtrace(); } diff --git a/programs/winedbg/tgt_active.c b/programs/winedbg/tgt_active.c index 52fae345b98..02bc3bc481e 100644 --- a/programs/winedbg/tgt_active.c +++ b/programs/winedbg/tgt_active.c @@ -63,12 +63,8 @@ static unsigned dbg_handle_debug_event(DEBUG_EVENT* de); * dbg_attach_debuggee * * Sets the debuggee to <pid> - * cofe instructs winedbg what to do when first exception is received - * (break=FALSE, continue=TRUE) - * wfe is set to TRUE if dbg_attach_debuggee should also proceed with all debug events - * until the first exception is received (aka: attach to an already running process) */ -BOOL dbg_attach_debuggee(DWORD pid) +BOOL dbg_attach_debuggee(DWORD pid, BOOL verbose) { if (pid == GetCurrentProcessId()) { @@ -92,7 +88,8 @@ BOOL dbg_attach_debuggee(DWORD pid) SetEnvironmentVariableA("DBGHELP_NOLIVE", NULL); dbg_curr_process->active_debuggee = TRUE; - dbg_printf("WineDbg attached to pid %04lx\n", pid); + if (verbose) + dbg_printf("WineDbg attached to pid %04lx\n", pid); dbg_curr_pid = pid; dbg_curr_thread = NULL; dbg_curr_tid = 0; @@ -840,14 +837,14 @@ enum dbg_start dbg_active_attach(int argc, char* argv[]) /* try the form <myself> pid */ if (argc == 1 && str2int(argv[0], &pid) && pid != 0) { - if (!dbg_attach_debuggee(pid)) + if (!dbg_attach_debuggee(pid, TRUE)) return start_error_init; } /* try the form <myself> pid evt (Win32 JIT debugger) */ else if (argc == 2 && str2int(argv[0], &pid) && pid != 0 && str2int(argv[1], &evt) && evt != 0) { - if (!dbg_attach_debuggee(pid)) + if (!dbg_attach_debuggee(pid, TRUE)) { /* don't care about result */ SetEvent((HANDLE)evt); diff --git a/programs/winedbg/tgt_minidump.c b/programs/winedbg/tgt_minidump.c index bed25406e22..8a13a4639bf 100644 --- a/programs/winedbg/tgt_minidump.c +++ b/programs/winedbg/tgt_minidump.c @@ -253,6 +253,24 @@ static BOOL is_pe_module_embedded(struct tgt_process_minidump_data* data, return FALSE; } +static BOOL copy_context(dbg_ctx_t *ctx, struct tgt_process_minidump_data* data, + const MINIDUMP_LOCATION_DESCRIPTOR *loc_desc) +{ + BOOL ret = TRUE; + unsigned len = loc_desc->DataSize; + + if (len > sizeof(*ctx)) + { + ERR("Incoming context size is larger than internal structure\n"); + len = sizeof(*ctx); + ret = FALSE; + } + memcpy(ctx, (char*)data->mapping + loc_desc->Rva, len); + if (len < sizeof(*ctx)) + memset((char*)ctx + len, 0, sizeof(*ctx) - len); + return ret; +} + static enum dbg_start minidump_do_reload(struct tgt_process_minidump_data* data) { void* stream; @@ -556,8 +574,7 @@ static enum dbg_start minidump_do_reload(struct tgt_process_minidump_data* data) { dbg_curr_thread->excpt_record.ExceptionInformation[i] = mes->ExceptionRecord.ExceptionInformation[i]; } - memcpy(&dbg_context, (char*)data->mapping + mes->ThreadContext.Rva, - min(sizeof(dbg_context), mes->ThreadContext.DataSize)); + copy_context(&dbg_context, data, &mes->ThreadContext); memory_get_current_pc(&addr); stack_fetch_frames(&dbg_context); dbg_curr_process->be_cpu->print_context(dbg_curr_thread->handle, &dbg_context, 0); @@ -676,6 +693,28 @@ static BOOL tgt_process_minidump_fetch_thread_name(const struct dbg_thread *thre return FALSE; } +static BOOL tgt_process_minidump_fetch_thread_context(const struct dbg_thread* thread, dbg_ctx_t *ctx) +{ + struct tgt_process_minidump_data *data = private_data(thread->process); + void *stream; + + if (MiniDumpReadDumpStream(data->mapping, ThreadListStream, NULL, &stream, NULL)) + { + MINIDUMP_THREAD_LIST* mtl = stream; + ULONG i; + + for (i = 0; i < mtl->NumberOfThreads; i++) + { + if (thread->tid == mtl->Threads[i].ThreadId) + { + copy_context(ctx, data, &mtl->Threads[i].ThreadContext); + return TRUE; + } + } + } + return FALSE; +} + static struct be_process_io be_process_minidump_io = { tgt_process_minidump_close_process, @@ -683,4 +722,5 @@ static struct be_process_io be_process_minidump_io = tgt_process_minidump_write, tgt_process_minidump_get_selector, tgt_process_minidump_fetch_thread_name, + tgt_process_minidump_fetch_thread_context, }; diff --git a/programs/winedbg/winedbg.c b/programs/winedbg/winedbg.c index 6d9afba7a6d..9707120dfc7 100644 --- a/programs/winedbg/winedbg.c +++ b/programs/winedbg/winedbg.c @@ -82,8 +82,7 @@ DWORD dbg_curr_pid = 0; dbg_ctx_t dbg_context; BOOL dbg_interactiveP = FALSE; HANDLE dbg_houtput = 0; - -static struct list dbg_process_list = LIST_INIT(dbg_process_list); +struct list dbg_process_list = LIST_INIT(dbg_process_list); struct dbg_internal_var dbg_internal_vars[DBG_IV_LAST]; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10562