wine/debugger/info.c
Eric Pouech 800773f9d4 Extended bt command to display backtrace of another thread.
Enhanced process & thread display with process name.
2001-08-06 17:51:52 +00:00

552 lines
16 KiB
C

/*
* Wine debugger utility routines
*
* Copyright 1993 Eric Youngdale
* Copyright 1995 Alexandre Julliard
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "tlhelp32.h"
#include "debugger.h"
#include "expr.h"
/***********************************************************************
* DEBUG_PrintBasic
*
* Implementation of the 'print' command.
*/
void DEBUG_PrintBasic( const DBG_VALUE* value, int count, char format )
{
char * default_format;
long long int res;
assert(value->cookie == DV_TARGET || value->cookie == DV_HOST);
if( value->type == NULL )
{
DEBUG_Printf(DBG_CHN_MESG, "Unable to evaluate expression\n");
return;
}
default_format = NULL;
res = DEBUG_GetExprValue(value, &default_format);
switch(format)
{
case 'x':
if (value->addr.seg)
{
DEBUG_nchar += DEBUG_Printf( DBG_CHN_MESG, "0x%04lx", (long unsigned int) res );
}
else
{
DEBUG_nchar += DEBUG_Printf( DBG_CHN_MESG, "0x%08lx", (long unsigned int) res );
}
break;
case 'd':
DEBUG_nchar += DEBUG_Printf( DBG_CHN_MESG, "%ld\n", (long int) res );
break;
case 'c':
DEBUG_nchar += DEBUG_Printf( DBG_CHN_MESG, "%d = '%c'",
(char)(res & 0xff), (char)(res & 0xff) );
break;
case 'i':
case 's':
case 'w':
case 'b':
DEBUG_Printf( DBG_CHN_MESG, "Format specifier '%c' is meaningless in 'print' command\n", format );
case 0:
if( default_format != NULL )
{
if (strstr(default_format, "%S") == NULL)
{
DEBUG_nchar += DEBUG_Printf( DBG_CHN_MESG, default_format, res );
}
else
{
char* ptr;
int state = 0;
/* FIXME: simplistic implementation for default_format being
* foo%Sbar => will print foo, then string then bar
*/
for (ptr = default_format; *ptr; ptr++)
{
if (*ptr == '%') state++;
else if (state == 1)
{
if (*ptr == 'S')
{
char ch;
char* str = (char*)(long)res;
for (; DEBUG_READ_MEM(str, &ch, 1) && ch; str++) {
DEBUG_Output(DBG_CHN_MESG, &ch, 1);
DEBUG_nchar++;
}
}
else
{
/* shouldn't happen */
DEBUG_Printf(DBG_CHN_MESG, "%%%c", *ptr);
DEBUG_nchar += 2;
}
state = 0;
}
else
{
DEBUG_Output(DBG_CHN_MESG, ptr, 1);
DEBUG_nchar++;
}
}
}
}
break;
}
}
/***********************************************************************
* DEBUG_PrintAddress
*
* Print an 16- or 32-bit address, with the nearest symbol if any.
*/
struct symbol_info
DEBUG_PrintAddress( const DBG_ADDR *addr, enum dbg_mode mode, int flag )
{
struct symbol_info rtn;
const char *name = DEBUG_FindNearestSymbol( addr, flag, &rtn.sym, 0,
&rtn.list );
if (addr->seg) DEBUG_Printf( DBG_CHN_MESG, "0x%04lx:", addr->seg&0xFFFF );
if (mode != MODE_32) DEBUG_Printf( DBG_CHN_MESG, "0x%04lx", addr->off );
else DEBUG_Printf( DBG_CHN_MESG, "0x%08lx", addr->off );
if (name) DEBUG_Printf( DBG_CHN_MESG, " (%s)", name );
return rtn;
}
/***********************************************************************
* DEBUG_PrintAddressAndArgs
*
* Print an 16- or 32-bit address, with the nearest symbol if any.
* Similar to DEBUG_PrintAddress, but we print the arguments to
* each function (if known). This is useful in a backtrace.
*/
struct symbol_info
DEBUG_PrintAddressAndArgs( const DBG_ADDR *addr, enum dbg_mode mode,
unsigned int ebp, int flag )
{
struct symbol_info rtn;
const char *name = DEBUG_FindNearestSymbol( addr, flag, &rtn.sym, ebp,
&rtn.list );
if (addr->seg) DEBUG_Printf( DBG_CHN_MESG, "0x%04lx:", addr->seg );
if (mode != MODE_32) DEBUG_Printf( DBG_CHN_MESG, "0x%04lx", addr->off );
else DEBUG_Printf( DBG_CHN_MESG, "0x%08lx", addr->off );
if (name) DEBUG_Printf( DBG_CHN_MESG, " (%s)", name );
return rtn;
}
/***********************************************************************
* DEBUG_Help
*
* Implementation of the 'help' command.
*/
void DEBUG_Help(void)
{
int i = 0;
static const char * const helptext[] =
{
"The commands accepted by the Wine debugger are a reasonable",
"subset of the commands that gdb accepts.",
"The commands currently are:",
" help quit",
" break [*<addr>] delete break bpnum",
" disable bpnum enable bpnum",
" condition <bpnum> [<expr>] pass",
" bt cont [N]",
" step [N] next [N]",
" stepi [N] nexti [N]",
" x <addr> print <expr>",
" set <reg> = <expr> set *<addr> = <expr>",
" up down",
" list <lines> disassemble [<addr>][,<addr>]",
" frame <n> finish",
" show dir dir <path>",
" display <expr> undisplay <disnum>",
" delete display <disnum> debugmsg <class>[-+]<type>\n",
" mode [16,32,vm86] walk [wnd,class,queue,module,",
" whatis process,modref <pid>]",
" info (see 'help info' for options)\n",
"The 'x' command accepts repeat counts and formats (including 'i') in the",
"same way that gdb does.\n",
" The following are examples of legal expressions:",
" $eax $eax+0x3 0x1000 ($eip + 256) *$eax *($esp + 3)",
" Also, a nm format symbol table can be read from a file using the",
" symbolfile command. Symbols can also be defined individually with",
" the define command.",
"",
NULL
};
while(helptext[i]) DEBUG_Printf(DBG_CHN_MESG,"%s\n", helptext[i++]);
}
/***********************************************************************
* DEBUG_HelpInfo
*
* Implementation of the 'help info' command.
*/
void DEBUG_HelpInfo(void)
{
int i = 0;
static const char * const infotext[] =
{
"The info commands allow you to get assorted bits of interesting stuff",
"to be displayed. The options are:",
" info break Dumps information about breakpoints",
" info display Shows auto-display expressions in use",
" info locals Displays values of all local vars for current frame",
" info maps Dumps all virtual memory mappings",
" info module <handle> Displays internal module state",
" info queue <handle> Displays internal queue state",
" info reg Displays values in all registers at top of stack",
" info segments Dumps information about all known segments",
" info share Dumps information about shared libraries",
" info stack Dumps information about top of stack",
" info wnd <handle> Displays internal window state",
"",
NULL
};
while(infotext[i]) DEBUG_Printf(DBG_CHN_MESG,"%s\n", infotext[i++]);
}
/* FIXME: merge InfoClass and InfoClass2 */
void DEBUG_InfoClass(const char* name)
{
WNDCLASSEXA wca;
if (!GetClassInfoEx(0, name, &wca)) {
DEBUG_Printf(DBG_CHN_MESG, "Cannot find class '%s'\n", name);
return;
}
DEBUG_Printf(DBG_CHN_MESG, "Class '%s':\n", name);
DEBUG_Printf(DBG_CHN_MESG,
"style=%08x wndProc=%08lx\n"
"inst=%p icon=%p cursor=%p bkgnd=%p\n"
"clsExtra=%d winExtra=%d\n",
wca.style, (DWORD)wca.lpfnWndProc, wca.hInstance,
wca.hIcon, wca.hCursor, wca.hbrBackground,
wca.cbClsExtra, wca.cbWndExtra);
/* FIXME:
* + print #windows (or even list of windows...)
* + print extra bytes => this requires a window handle on this very class...
*/
}
static void DEBUG_InfoClass2(HWND hWnd, const char* name)
{
WNDCLASSEXA wca;
if (!GetClassInfoEx((HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), name, &wca)) {
DEBUG_Printf(DBG_CHN_MESG, "Cannot find class '%s'\n", name);
return;
}
DEBUG_Printf(DBG_CHN_MESG, "Class '%s':\n", name);
DEBUG_Printf(DBG_CHN_MESG,
"style=%08x wndProc=%08lx\n"
"inst=%p icon=%p cursor=%p bkgnd=%p\n"
"clsExtra=%d winExtra=%d\n",
wca.style, (DWORD)wca.lpfnWndProc, wca.hInstance,
wca.hIcon, wca.hCursor, wca.hbrBackground,
wca.cbClsExtra, wca.cbWndExtra);
if (wca.cbClsExtra) {
int i;
WORD w;
DEBUG_Printf(DBG_CHN_MESG, "Extra bytes:" );
for (i = 0; i < wca.cbClsExtra / 2; i++) {
w = GetClassWord(hWnd, i * 2);
/* FIXME: depends on i386 endian-ity */
DEBUG_Printf(DBG_CHN_MESG, " %02x", HIBYTE(w));
DEBUG_Printf(DBG_CHN_MESG, " %02x", LOBYTE(w));
}
DEBUG_Printf(DBG_CHN_MESG, "\n" );
}
DEBUG_Printf(DBG_CHN_MESG, "\n" );
}
struct class_walker {
ATOM* table;
int used;
int alloc;
};
static void DEBUG_WalkClassesHelper(HWND hWnd, struct class_walker* cw)
{
char clsName[128];
int i;
ATOM atom;
HWND child;
if (!GetClassName(hWnd, clsName, sizeof(clsName)))
return;
if ((atom = FindAtom(clsName)) == 0)
return;
for (i = 0; i < cw->used; i++) {
if (cw->table[i] == atom)
break;
}
if (i == cw->used) {
if (cw->used >= cw->alloc) {
cw->alloc += 16;
cw->table = DBG_realloc(cw->table, cw->alloc * sizeof(ATOM));
}
cw->table[cw->used++] = atom;
DEBUG_InfoClass2(hWnd, clsName);
}
do {
if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
DEBUG_WalkClassesHelper(child, cw);
} while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
}
void DEBUG_WalkClasses(void)
{
struct class_walker cw;
cw.table = NULL;
cw.used = cw.alloc = 0;
DEBUG_WalkClassesHelper(GetDesktopWindow(), &cw);
DBG_free(cw.table);
}
void DEBUG_DumpQueue(DWORD q)
{
DEBUG_Printf(DBG_CHN_MESG, "No longer doing info queue '0x%08lx'\n", q);
}
void DEBUG_WalkQueues(void)
{
DEBUG_Printf(DBG_CHN_MESG, "No longer walking queues list\n");
}
void DEBUG_InfoWindow(HWND hWnd)
{
char clsName[128];
char wndName[128];
RECT clientRect;
RECT windowRect;
int i;
WORD w;
if (!GetClassName(hWnd, clsName, sizeof(clsName)))
strcpy(clsName, "-- Unknown --");
if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
strcpy(wndName, "-- Empty --");
if (!GetClientRect(hWnd, &clientRect))
SetRectEmpty(&clientRect);
if (!GetWindowRect(hWnd, &windowRect))
SetRectEmpty(&windowRect);
/* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
DEBUG_Printf(DBG_CHN_MESG,
"next=%p child=%p parent=%p owner=%p class='%s'\n"
"inst=%p active=%p idmenu=%08lx\n"
"style=%08lx exstyle=%08lx wndproc=%08lx text='%s'\n"
"client=%d,%d-%d,%d window=%d,%d-%d,%d sysmenu=%p\n",
GetWindow(hWnd, GW_HWNDNEXT),
GetWindow(hWnd, GW_CHILD),
GetParent(hWnd),
GetWindow(hWnd, GW_OWNER),
clsName,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
GetLastActivePopup(hWnd),
GetWindowLong(hWnd, GWL_ID),
GetWindowLong(hWnd, GWL_STYLE),
GetWindowLong(hWnd, GWL_EXSTYLE),
GetWindowLong(hWnd, GWL_WNDPROC),
wndName,
clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
GetSystemMenu(hWnd, FALSE));
if (GetClassLong(hWnd, GCL_CBWNDEXTRA)) {
DEBUG_Printf(DBG_CHN_MESG, "Extra bytes:" );
for (i = 0; i < GetClassLong(hWnd, GCL_CBWNDEXTRA) / 2; i++) {
w = GetWindowWord(hWnd, i * 2);
/* FIXME: depends on i386 endian-ity */
DEBUG_Printf(DBG_CHN_MESG, " %02x", HIBYTE(w));
DEBUG_Printf(DBG_CHN_MESG, " %02x", LOBYTE(w));
}
DEBUG_Printf(DBG_CHN_MESG, "\n");
}
DEBUG_Printf(DBG_CHN_MESG, "\n");
}
void DEBUG_WalkWindows(HWND hWnd, int indent)
{
char clsName[128];
char wndName[128];
HWND child;
if (!IsWindow(hWnd))
hWnd = GetDesktopWindow();
if (!indent) /* first time around */
DEBUG_Printf(DBG_CHN_MESG,
"%-16.16s %-17.17s %-8.8s %s\n",
"hwnd", "Class Name", " Style", " WndProc Text");
do {
if (!GetClassName(hWnd, clsName, sizeof(clsName)))
strcpy(clsName, "-- Unknown --");
if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
strcpy(wndName, "-- Empty --");
/* FIXME: missing hmemTaskQ */
DEBUG_Printf(DBG_CHN_MESG, "%*s%04x%*s", indent, "", (UINT)hWnd, 13-indent,"");
DEBUG_Printf(DBG_CHN_MESG, "%-17.17s %08lx %08lx %.14s\n",
clsName, GetWindowLong(hWnd, GWL_STYLE),
GetWindowLong(hWnd, GWL_WNDPROC), wndName);
if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
DEBUG_WalkWindows(child, indent + 1 );
} while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
}
void DEBUG_WalkProcess(void)
{
HANDLE snap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if (snap != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 entry;
DWORD current = DEBUG_CurrProcess ? DEBUG_CurrProcess->pid : 0;
BOOL ok;
entry.dwSize = sizeof(entry);
ok = Process32First( snap, &entry );
DEBUG_Printf(DBG_CHN_MESG, "%-8.8s %-8.8s %-8.8s %s\n",
"pid", "threads", "parent", "exe" );
while (ok)
{
if (entry.th32ProcessID != GetCurrentProcessId())
DEBUG_Printf(DBG_CHN_MESG, "%08lx %8ld %08lx '%s'%s\n",
entry.th32ProcessID, entry.cntThreads,
entry.th32ParentProcessID, entry.szExeFile,
(entry.th32ProcessID == current) ? " <==" : "" );
ok = Process32Next( snap, &entry );
}
CloseHandle( snap );
}
}
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;
DWORD lastProcessId = 0;
entry.dwSize = sizeof(entry);
ok = Thread32First( snap, &entry );
DEBUG_Printf(DBG_CHN_MESG, "%-8.8s %-8.8s %s\n", "process", "tid", "prio" );
while (ok)
{
if (entry.th32OwnerProcessID != GetCurrentProcessId())
{
/* 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 );
}
}
void DEBUG_WalkModref(DWORD p)
{
DEBUG_Printf(DBG_CHN_MESG, "No longer walking module references list\n");
}
void DEBUG_InfoSegments(DWORD start, int length)
{
char flags[3];
DWORD i;
LDT_ENTRY le;
if (length == -1) length = (8192 - start);
for (i = start; i < start + length; i++)
{
if (!GetThreadSelectorEntry(DEBUG_CurrThread->handle, (i << 3)|7, &le))
continue;
if (le.HighWord.Bits.Type & 0x08)
{
flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
flags[1] = '-';
flags[2] = 'x';
}
else
{
flags[0] = 'r';
flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
flags[2] = '-';
}
DEBUG_Printf(DBG_CHN_MESG,
"%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
i, (i<<3)|7,
(le.HighWord.Bits.BaseHi << 24) +
(le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
(le.HighWord.Bits.Granularity ? 12 : 0),
le.HighWord.Bits.Default_Big ? 32 : 16,
flags[0], flags[1], flags[2] );
}
}
void DEBUG_InfoVirtual(void)
{
DEBUG_Printf(DBG_CHN_MESG, "No longer providing virtual mapping information\n");
}