|  | /* | 
|  | * Wine debugger utility routines | 
|  | * | 
|  | * Copyright 1993 Eric Youngdale | 
|  | * Copyright 1995 Alexandre Julliard | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  |  | 
|  | #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) | 
|  | { | 
|  | 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++; | 
|  | } | 
|  | } | 
|  | } | 
|  | else if (strcmp(default_format, "%B") == 0) | 
|  | { | 
|  | DEBUG_nchar += DEBUG_Printf( DBG_CHN_MESG, "%s", res ? "true" : "false"); | 
|  | } | 
|  | else | 
|  | { | 
|  | DEBUG_nchar += DEBUG_Printf( DBG_CHN_MESG, default_format, res ); | 
|  | } | 
|  | } | 
|  | 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", "executable" ); | 
|  | while (ok) | 
|  | { | 
|  | if (entry.th32ProcessID != GetCurrentProcessId()) | 
|  | DEBUG_Printf(DBG_CHN_MESG, "%c%08lx %-8ld %08lx '%s'\n", | 
|  | (entry.th32ProcessID == current) ? '>' : ' ', | 
|  | entry.th32ProcessID, entry.cntThreads, | 
|  | entry.th32ParentProcessID, entry.szExeFile); | 
|  | 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"); | 
|  | } |