| /* | 
 |  * 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"); | 
 | } |