Extended bt command to display backtrace of another thread.
Enhanced process & thread display with process name.
This commit is contained in:
parent
c457fbdde6
commit
800773f9d4
|
@ -120,7 +120,8 @@ command:
|
|||
| tENABLE tNUM tEOL { DEBUG_EnableBreakpoint( $2, TRUE ); }
|
||||
| tDISABLE tNUM tEOL { DEBUG_EnableBreakpoint( $2, FALSE ); }
|
||||
| tDELETE tBREAK tNUM tEOL { DEBUG_DelBreakpoint( $3 ); }
|
||||
| tBACKTRACE tEOL { DEBUG_BackTrace(TRUE); }
|
||||
| tBACKTRACE tEOL { DEBUG_BackTrace(DEBUG_CurrTid, TRUE); }
|
||||
| tBACKTRACE tNUM tEOL { DEBUG_BackTrace($2, TRUE); }
|
||||
| tUP tEOL { DEBUG_SetFrame( curr_frame + 1 ); }
|
||||
| tUP tNUM tEOL { DEBUG_SetFrame( curr_frame + $2 ); }
|
||||
| tDOWN tEOL { DEBUG_SetFrame( curr_frame - 1 ); }
|
||||
|
|
|
@ -191,6 +191,7 @@ typedef struct tagDBG_DELAYED_BP {
|
|||
typedef struct tagDBG_PROCESS {
|
||||
HANDLE handle;
|
||||
DWORD pid;
|
||||
const char* imageName;
|
||||
DBG_THREAD* threads;
|
||||
int num_threads;
|
||||
unsigned continue_on_first_exception;
|
||||
|
@ -434,7 +435,7 @@ extern BOOL DEBUG_DisassembleInstruction(DBG_ADDR *addr);
|
|||
|
||||
/* debugger/stack.c */
|
||||
extern void DEBUG_InfoStack(void);
|
||||
extern void DEBUG_BackTrace(BOOL noisy);
|
||||
extern void DEBUG_BackTrace(DWORD threadID, BOOL noisy);
|
||||
extern int DEBUG_InfoLocals(void);
|
||||
extern int DEBUG_SetFrame(int newframe);
|
||||
extern int DEBUG_GetCurrentFrame(struct name_hash ** name,
|
||||
|
@ -493,6 +494,8 @@ extern int DEBUG_Printf(int chn, const char* format, ...);
|
|||
extern DBG_INTVAR* DEBUG_GetIntVar(const char*);
|
||||
extern BOOL DEBUG_Attach(DWORD pid, BOOL cofe);
|
||||
extern void DEBUG_Run(const char* args);
|
||||
extern DBG_PROCESS* DEBUG_GetProcess(DWORD pid);
|
||||
extern DBG_THREAD* DEBUG_GetThread(DBG_PROCESS* p, DWORD tid);
|
||||
extern int curr_frame;
|
||||
|
||||
/* Choose your allocator! */
|
||||
|
|
|
@ -466,23 +466,39 @@ void DEBUG_WalkThreads(void)
|
|||
HANDLE snap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
|
||||
if (snap != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
THREADENTRY32 entry;
|
||||
DWORD current = DEBUG_CurrThread ? DEBUG_CurrThread->tid : 0;
|
||||
BOOL ok;
|
||||
THREADENTRY32 entry;
|
||||
DWORD current = DEBUG_CurrThread ? DEBUG_CurrThread->tid : 0;
|
||||
BOOL ok;
|
||||
DWORD lastProcessId = 0;
|
||||
|
||||
entry.dwSize = sizeof(entry);
|
||||
ok = Thread32First( snap, &entry );
|
||||
entry.dwSize = sizeof(entry);
|
||||
ok = Thread32First( snap, &entry );
|
||||
|
||||
DEBUG_Printf(DBG_CHN_MESG, "%-8.8s %-8.8s %s\n",
|
||||
"tid", "process", "prio" );
|
||||
DEBUG_Printf(DBG_CHN_MESG, "%-8.8s %-8.8s %s\n", "process", "tid", "prio" );
|
||||
while (ok)
|
||||
{
|
||||
if (entry.th32OwnerProcessID != GetCurrentProcessId())
|
||||
DEBUG_Printf(DBG_CHN_MESG, "%08lx %08lx %4ld%s\n",
|
||||
entry.th32ThreadID, entry.th32OwnerProcessID,
|
||||
entry.tpBasePri, (entry.th32ThreadID == current) ? " <==" : "" );
|
||||
{
|
||||
/* FIXME: this assumes that, in the snapshot, all threads of a same process are
|
||||
* listed sequentially, which is not specified in the doc (Wine's implementation
|
||||
* does it)
|
||||
*/
|
||||
if (entry.th32OwnerProcessID != lastProcessId)
|
||||
{
|
||||
DBG_PROCESS* p = DEBUG_GetProcess(entry.th32OwnerProcessID);
|
||||
|
||||
DEBUG_Printf(DBG_CHN_MESG, "%08lx%s %s\n",
|
||||
entry.th32OwnerProcessID, p ? " (D)" : "", p ? p->imageName : "");
|
||||
lastProcessId = entry.th32OwnerProcessID;
|
||||
}
|
||||
DEBUG_Printf(DBG_CHN_MESG, "\t%08lx %4ld%s\n",
|
||||
entry.th32ThreadID, entry.tpBasePri,
|
||||
(entry.th32ThreadID == current) ? " <==" : "");
|
||||
|
||||
}
|
||||
ok = Thread32Next( snap, &entry );
|
||||
}
|
||||
|
||||
CloseHandle( snap );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ static void DEBUG_ForceFrame(DBG_ADDR *stack, DBG_ADDR *code, int frameno, enum
|
|||
}
|
||||
}
|
||||
|
||||
static BOOL DEBUG_Frame16(DBG_ADDR *addr, unsigned int *cs, int frameno, int noisy)
|
||||
static BOOL DEBUG_Frame16(DBG_THREAD* thread, DBG_ADDR *addr, unsigned int *cs, int frameno, int noisy)
|
||||
{
|
||||
unsigned int possible_cs = 0;
|
||||
FRAME16 frame;
|
||||
|
@ -128,7 +128,7 @@ static BOOL DEBUG_Frame16(DBG_ADDR *addr, unsigned int *cs, int frameno, int noi
|
|||
if (((frame.cs&7)==7) && (frame.cs != *cs)) {
|
||||
LDT_ENTRY le;
|
||||
|
||||
if (GetThreadSelectorEntry( DEBUG_CurrThread->handle, frame.cs, &le) &&
|
||||
if (GetThreadSelectorEntry( thread->handle, frame.cs, &le) &&
|
||||
(le.HighWord.Bits.Type & 0x08)) { /* code segment */
|
||||
/* it is very uncommon to push a code segment cs as
|
||||
* a parameter, so this should work in most cases */
|
||||
|
@ -174,25 +174,64 @@ static BOOL DEBUG_Frame32(DBG_ADDR *addr, unsigned int *cs, int frameno, int noi
|
|||
*
|
||||
* Display a stack back-trace.
|
||||
*/
|
||||
void DEBUG_BackTrace(BOOL noisy)
|
||||
void DEBUG_BackTrace(DWORD tid, BOOL noisy)
|
||||
{
|
||||
#ifdef __i386
|
||||
DBG_ADDR addr, sw_addr, code, tmp;
|
||||
unsigned int ss = DEBUG_context.SegSs;
|
||||
unsigned int cs = DEBUG_context.SegCs;
|
||||
unsigned int ss, cs;
|
||||
int frameno = 0, is16, ok;
|
||||
DWORD next_switch, cur_switch, p;
|
||||
STACK16FRAME frame16;
|
||||
STACK32FRAME frame32;
|
||||
char ch;
|
||||
CONTEXT ctx;
|
||||
DBG_THREAD* thread;
|
||||
|
||||
int copy_nframe = 0;
|
||||
int copy_curr_frame = 0;
|
||||
struct bt_info* copy_frames = NULL;
|
||||
|
||||
if (noisy) DEBUG_Printf( DBG_CHN_MESG, "Backtrace:\n" );
|
||||
|
||||
if (tid == DEBUG_CurrTid)
|
||||
{
|
||||
ctx = DEBUG_context;
|
||||
thread = DEBUG_CurrThread;
|
||||
|
||||
if (frames) DBG_free( frames );
|
||||
/* frames = (struct bt_info *) DBG_alloc( sizeof(struct bt_info) ); */
|
||||
}
|
||||
else
|
||||
{
|
||||
thread = DEBUG_GetThread(DEBUG_CurrProcess, tid);
|
||||
|
||||
if (!thread)
|
||||
{
|
||||
DEBUG_Printf( DBG_CHN_MESG, "Unknown thread id (0x%08lx) in current process\n", tid);
|
||||
return;
|
||||
}
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_SEGMENTS;
|
||||
|
||||
if ( SuspendThread( thread->handle ) == -1 ||
|
||||
!GetThreadContext( thread->handle, &ctx ))
|
||||
{
|
||||
DEBUG_Printf( DBG_CHN_MESG, "Can't get context for thread id (0x%08lx) in current process\n", tid);
|
||||
return;
|
||||
}
|
||||
/* need to avoid trashing stack frame for current thread */
|
||||
copy_nframe = nframe;
|
||||
copy_frames = frames;
|
||||
copy_curr_frame = curr_frame;
|
||||
curr_frame = 0;
|
||||
}
|
||||
|
||||
nframe = 0;
|
||||
if (frames) DBG_free( frames );
|
||||
/* frames = (struct bt_info *) DBG_alloc( sizeof(struct bt_info) ); */
|
||||
frames = NULL;
|
||||
|
||||
cs = ctx.SegCs;
|
||||
ss = ctx.SegSs;
|
||||
|
||||
if (DEBUG_IsSelectorSystem(ss)) ss = 0;
|
||||
if (DEBUG_IsSelectorSystem(cs)) cs = 0;
|
||||
|
||||
|
@ -201,16 +240,16 @@ void DEBUG_BackTrace(BOOL noisy)
|
|||
{
|
||||
case MODE_32:
|
||||
code.seg = cs;
|
||||
code.off = DEBUG_context.Eip;
|
||||
code.off = ctx.Eip;
|
||||
addr.seg = ss;
|
||||
addr.off = DEBUG_context.Ebp;
|
||||
addr.off = ctx.Ebp;
|
||||
DEBUG_ForceFrame( &addr, &code, frameno, MODE_32, noisy, NULL );
|
||||
if (!(code.seg || code.off)) {
|
||||
/* trying to execute a null pointer... yuck...
|
||||
* if it was a call to null, the return EIP should be
|
||||
* available at SS:ESP, so let's try to retrieve it */
|
||||
tmp.seg = ss;
|
||||
tmp.off = DEBUG_context.Esp;
|
||||
tmp.off = ctx.Esp;
|
||||
if (DEBUG_READ_MEM((void *)DEBUG_ToLinear(&tmp), &code.off, sizeof(code.off))) {
|
||||
DEBUG_ForceFrame( &addr, &code, ++frameno, MODE_32, noisy, ", null call assumed" );
|
||||
}
|
||||
|
@ -220,9 +259,9 @@ void DEBUG_BackTrace(BOOL noisy)
|
|||
case MODE_16:
|
||||
case MODE_VM86:
|
||||
code.seg = cs;
|
||||
code.off = LOWORD(DEBUG_context.Eip);
|
||||
code.off = LOWORD(ctx.Eip);
|
||||
addr.seg = ss;
|
||||
addr.off = LOWORD(DEBUG_context.Ebp);
|
||||
addr.off = LOWORD(ctx.Ebp);
|
||||
DEBUG_ForceFrame( &addr, &code, frameno, MODE_16, noisy, NULL );
|
||||
is16 = TRUE;
|
||||
break;
|
||||
|
@ -234,7 +273,7 @@ void DEBUG_BackTrace(BOOL noisy)
|
|||
/* cur_switch holds address of curr_stack's field in TEB in debuggee
|
||||
* address space
|
||||
*/
|
||||
cur_switch = (DWORD)DEBUG_CurrThread->teb + OFFSET_OF(TEB, cur_stack);
|
||||
cur_switch = (DWORD)thread->teb + OFFSET_OF(TEB, cur_stack);
|
||||
if (!DEBUG_READ_MEM((void*)cur_switch, &next_switch, sizeof(next_switch))) {
|
||||
if (noisy) DEBUG_Printf( DBG_CHN_MESG, "Can't read TEB:cur_stack\n");
|
||||
return;
|
||||
|
@ -342,11 +381,21 @@ void DEBUG_BackTrace(BOOL noisy)
|
|||
}
|
||||
} else {
|
||||
/* ordinary stack frame */
|
||||
ok = is16 ? DEBUG_Frame16( &addr, &cs, ++frameno, noisy)
|
||||
ok = is16 ? DEBUG_Frame16( thread, &addr, &cs, ++frameno, noisy)
|
||||
: DEBUG_Frame32( &addr, &cs, ++frameno, noisy);
|
||||
}
|
||||
}
|
||||
if (noisy) DEBUG_Printf( DBG_CHN_MESG, "\n" );
|
||||
|
||||
if (tid != DEBUG_CurrTid)
|
||||
{
|
||||
ResumeThread( thread->handle );
|
||||
/* restore stack frame for current thread */
|
||||
if (frames) DBG_free( frames );
|
||||
frames = copy_frames;
|
||||
nframe = copy_nframe;
|
||||
curr_frame = copy_curr_frame;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ static WINE_EXCEPTION_FILTER(wine_dbg)
|
|||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
static DBG_PROCESS* DEBUG_GetProcess(DWORD pid)
|
||||
DBG_PROCESS* DEBUG_GetProcess(DWORD pid)
|
||||
{
|
||||
DBG_PROCESS* p;
|
||||
|
||||
|
@ -135,13 +135,14 @@ static DBG_PROCESS* DEBUG_GetProcess(DWORD pid)
|
|||
return p;
|
||||
}
|
||||
|
||||
static DBG_PROCESS* DEBUG_AddProcess(DWORD pid, HANDLE h)
|
||||
static DBG_PROCESS* DEBUG_AddProcess(DWORD pid, HANDLE h, const char* imageName)
|
||||
{
|
||||
DBG_PROCESS* p = DBG_alloc(sizeof(DBG_PROCESS));
|
||||
if (!p)
|
||||
return NULL;
|
||||
p->handle = h;
|
||||
p->pid = pid;
|
||||
p->imageName = imageName ? DBG_strdup(imageName) : NULL;
|
||||
p->threads = NULL;
|
||||
p->num_threads = 0;
|
||||
p->continue_on_first_exception = FALSE;
|
||||
|
@ -177,6 +178,7 @@ static void DEBUG_DelProcess(DBG_PROCESS* p)
|
|||
if (p->next) p->next->prev = p->prev;
|
||||
if (p == DEBUG_ProcessList) DEBUG_ProcessList = p->next;
|
||||
if (p == DEBUG_CurrProcess) DEBUG_CurrProcess = NULL;
|
||||
DBG_free((char*)p->imageName);
|
||||
DBG_free(p);
|
||||
}
|
||||
|
||||
|
@ -206,7 +208,7 @@ static BOOL DEBUG_ProcessGetStringIndirect(char* buffer, int size, HANDLE hp, LP
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static DBG_THREAD* DEBUG_GetThread(DBG_PROCESS* p, DWORD tid)
|
||||
DBG_THREAD* DEBUG_GetThread(DBG_PROCESS* p, DWORD tid)
|
||||
{
|
||||
DBG_THREAD* t;
|
||||
|
||||
|
@ -273,7 +275,7 @@ static void DEBUG_DelThread(DBG_THREAD* t)
|
|||
|
||||
BOOL DEBUG_Attach(DWORD pid, BOOL cofe)
|
||||
{
|
||||
if (!(DEBUG_CurrProcess = DEBUG_AddProcess(pid, 0))) return FALSE;
|
||||
if (!(DEBUG_CurrProcess = DEBUG_AddProcess(pid, 0, NULL))) return FALSE;
|
||||
|
||||
if (!DebugActiveProcess(pid)) {
|
||||
DEBUG_Printf(DBG_CHN_MESG, "Can't attach process %lx: error %ld\n", pid, GetLastError());
|
||||
|
@ -338,7 +340,7 @@ static BOOL DEBUG_ExceptionProlog(BOOL is_debug, BOOL force, DWORD code)
|
|||
* Do a quiet backtrace so that we have an idea of what the situation
|
||||
* is WRT the source files.
|
||||
*/
|
||||
DEBUG_BackTrace(FALSE);
|
||||
DEBUG_BackTrace(DEBUG_CurrTid, FALSE);
|
||||
} else {
|
||||
/* This is a real crash, dump some info */
|
||||
DEBUG_InfoRegisters();
|
||||
|
@ -351,7 +353,7 @@ static BOOL DEBUG_ExceptionProlog(BOOL is_debug, BOOL force, DWORD code)
|
|||
}
|
||||
DEBUG_InfoSegments(DEBUG_context.SegFs >> 3, 1);
|
||||
#endif
|
||||
DEBUG_BackTrace(TRUE);
|
||||
DEBUG_BackTrace(DEBUG_CurrTid, TRUE);
|
||||
}
|
||||
|
||||
if (!is_debug ||
|
||||
|
@ -624,11 +626,11 @@ static BOOL DEBUG_HandleDebugEvent(DEBUG_EVENT* de, LPDWORD cont)
|
|||
DEBUG_ProcessGetStringIndirect(buffer, sizeof(buffer),
|
||||
de->u.CreateProcessInfo.hProcess,
|
||||
de->u.CreateProcessInfo.lpImageName);
|
||||
|
||||
|
||||
/* FIXME unicode ? de->u.CreateProcessInfo.fUnicode */
|
||||
DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: create process %s @%08lx (%ld<%ld>)\n",
|
||||
DEBUG_Printf(DBG_CHN_TRACE, "%08lx:%08lx: create process '%s'/%p @%08lx (%ld<%ld>)\n",
|
||||
de->dwProcessId, de->dwThreadId,
|
||||
buffer,
|
||||
buffer, de->u.CreateProcessInfo.lpImageName,
|
||||
(unsigned long)(LPVOID)de->u.CreateProcessInfo.lpStartAddress,
|
||||
de->u.CreateProcessInfo.dwDebugInfoFileOffset,
|
||||
de->u.CreateProcessInfo.nDebugInfoSize);
|
||||
|
@ -639,9 +641,13 @@ static BOOL DEBUG_HandleDebugEvent(DEBUG_EVENT* de, LPDWORD cont)
|
|||
break;
|
||||
}
|
||||
DEBUG_CurrProcess->handle = de->u.CreateProcessInfo.hProcess;
|
||||
if (DEBUG_CurrProcess->imageName == NULL)
|
||||
DEBUG_CurrProcess->imageName = DBG_strdup(buffer[0] ? buffer : "<Debugged Process>");
|
||||
|
||||
} else {
|
||||
DEBUG_CurrProcess = DEBUG_AddProcess(de->dwProcessId,
|
||||
de->u.CreateProcessInfo.hProcess);
|
||||
de->u.CreateProcessInfo.hProcess,
|
||||
buffer[0] ? buffer : "<Debugged Process>");
|
||||
if (DEBUG_CurrProcess == NULL) {
|
||||
DEBUG_Printf(DBG_CHN_ERR, "Unknown process\n");
|
||||
break;
|
||||
|
@ -665,11 +671,7 @@ static BOOL DEBUG_HandleDebugEvent(DEBUG_EVENT* de, LPDWORD cont)
|
|||
DEBUG_InitCurrProcess();
|
||||
DEBUG_InitCurrThread();
|
||||
|
||||
DEBUG_ProcessGetStringIndirect(buffer, sizeof(buffer),
|
||||
DEBUG_CurrThread->process->handle,
|
||||
de->u.CreateProcessInfo.lpImageName);
|
||||
DEBUG_LoadModule32(buffer[0] ? buffer : "<Debugged process>",
|
||||
de->u.CreateProcessInfo.hFile,
|
||||
DEBUG_LoadModule32(DEBUG_CurrProcess->imageName, de->u.CreateProcessInfo.hFile,
|
||||
(DWORD)de->u.CreateProcessInfo.lpBaseOfImage);
|
||||
|
||||
if (buffer[0]) /* we got a process name */
|
||||
|
@ -825,7 +827,7 @@ static BOOL DEBUG_Start(LPSTR cmdLine)
|
|||
return FALSE;
|
||||
}
|
||||
DEBUG_CurrPid = info.dwProcessId;
|
||||
if (!(DEBUG_CurrProcess = DEBUG_AddProcess(DEBUG_CurrPid, 0))) return FALSE;
|
||||
if (!(DEBUG_CurrProcess = DEBUG_AddProcess(DEBUG_CurrPid, 0, NULL))) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -859,7 +861,7 @@ int DEBUG_main(int argc, char** argv)
|
|||
DEBUG_InitTypes();
|
||||
DEBUG_InitCVDataTypes();
|
||||
|
||||
/* Initialize internal vars (types must be initialized before) */
|
||||
/* Initialize internal vars (types must have been initialized before) */
|
||||
if (!DEBUG_IntVarsRW(TRUE)) return -1;
|
||||
|
||||
/* keep it as a guiexe for now, so that Wine won't touch the Unix stdin,
|
||||
|
@ -881,8 +883,6 @@ int DEBUG_main(int argc, char** argv)
|
|||
|
||||
if ((pid = atoi(argv[1])) != 0 && (hEvent = (HANDLE)atoi(argv[2])) != 0) {
|
||||
if (!DEBUG_Attach(pid, TRUE)) {
|
||||
DEBUG_Printf(DBG_CHN_ERR, "Can't attach process %ld: %ld\n",
|
||||
DEBUG_CurrPid, GetLastError());
|
||||
/* don't care about result */
|
||||
SetEvent(hEvent);
|
||||
goto leave;
|
||||
|
@ -920,10 +920,11 @@ int DEBUG_main(int argc, char** argv)
|
|||
}
|
||||
|
||||
retv = DEBUG_MainLoop();
|
||||
leave:
|
||||
|
||||
/* saves modified variables */
|
||||
DEBUG_IntVarsRW(FALSE);
|
||||
|
||||
leave:
|
||||
return retv;
|
||||
|
||||
oom_leave:
|
||||
|
|
|
@ -1349,11 +1349,14 @@ info break lists all (break|watch)points (with state)
|
|||
|
||||
<screen>
|
||||
bt print calling stack of current thread
|
||||
bt N print calling stack of thread of ID N (note: this
|
||||
doesn't change the position of the current frame as
|
||||
manipulated by the up & dn commands)
|
||||
up goes up one frame in current thread's stack
|
||||
up N goes up N frames in current thread's stack
|
||||
dn goes down one frame in current thread's stack
|
||||
dn N goes down N frames in current thread's stack
|
||||
frame N set N as the current frame
|
||||
frame N set N as the current frame for current thread's stack
|
||||
info local prints information on local variables for current
|
||||
function
|
||||
</screen>
|
||||
|
|
Loading…
Reference in New Issue