2 * Wine debugger utility routines
4 * Copyright 1993 Eric Youngdale
5 * Copyright 1995 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
37 /***********************************************************************
40 * Implementation of the 'help' command.
45 static const char * const helptext[] =
47 "The commands accepted by the Wine debugger are a reasonable",
48 "subset of the commands that gdb accepts.",
49 "The commands currently are:",
51 " break [*<addr>] watch *<addr>",
52 " delete break bpnum disable bpnum",
53 " enable bpnum condition <bpnum> [<expr>]",
56 " stepi [N] nexti [N]",
57 " x <addr> print <expr>",
58 " display <expr> undisplay <disnum>",
59 " local display <expr> delete display <disnum>",
60 " enable display <disnum> disable display <disnum>",
63 " list <lines> disassemble [<addr>][,<addr>]",
64 " show dir dir <path>",
65 " set <reg> = <expr> set *<addr> = <expr>",
66 " mode [16,32,vm86] pass",
67 " whatis info (see 'help info' for options)",
69 "The 'x' command accepts repeat counts and formats (including 'i') in the",
70 "same way that gdb does.\n",
72 "The following are examples of legal expressions:",
73 " $eax $eax+0x3 0x1000 ($eip + 256) *$eax *($esp + 3)",
74 " Also, a nm format symbol table can be read from a file using the",
75 " symbolfile command.", /* Symbols can also be defined individually with",
76 " the define command.", */
81 while (helptext[i]) dbg_printf("%s\n", helptext[i++]);
85 /***********************************************************************
88 * Implementation of the 'help info' command.
93 static const char * const infotext[] =
95 "The info commands allow you to get assorted bits of interesting stuff",
96 "to be displayed. The options are:",
97 " info break Displays information about breakpoints",
98 " info class <name> Displays information about window class <name>",
99 " info display Shows auto-display expressions in use",
100 " info except <pid> Shows exception handler chain (in a given process)",
101 " info locals Displays values of all local vars for current frame",
102 " info maps <pid> Shows virtual mappings (in a given process)",
103 " info process Shows all running processes",
104 " info reg Displays values in all registers at top of stack",
105 " info segments <pid> Displays information about all known segments",
106 " info share Displays all loaded modules",
107 " info share <addr> Displays internal module state",
108 " info stack Dumps information about top of stack",
109 " info symbol <sym> Displays information about a given symbol",
110 " info thread Shows all running threads",
111 " info wnd <handle> Displays internal window state",
116 while (infotext[i]) dbg_printf("%s\n", infotext[i++]);
119 static const char* get_symtype_str(SYM_TYPE st)
123 case -1: return "\\";
125 case SymNone: return "--none--";
126 case SymCoff: return "COFF";
127 case SymCv: return "CodeView";
128 case SymPdb: return "PDB";
129 case SymExport: return "Export";
130 case SymDeferred: return "Deferred";
131 case SymSym: return "Sym";
132 case SymDia: return "DIA";
133 case NumSymTypes: return "Stabs";
144 static void module_print_info(const IMAGEHLP_MODULE* mi, SYM_TYPE st)
146 dbg_printf("0x%08lx-%08lx\t%-16s%s\n",
147 mi->BaseOfImage, mi->BaseOfImage + mi->ImageSize,
148 get_symtype_str(st), mi->ModuleName);
151 static int module_compare(const void* p1, const void* p2)
153 return (char*)(((const IMAGEHLP_MODULE*)p1)->BaseOfImage) -
154 (char*)(((const IMAGEHLP_MODULE*)p2)->BaseOfImage);
157 static inline BOOL module_is_container(const IMAGEHLP_MODULE* wmod_cntnr,
158 const IMAGEHLP_MODULE* wmod_child)
160 return wmod_cntnr->BaseOfImage <= wmod_child->BaseOfImage &&
161 (DWORD)wmod_cntnr->BaseOfImage + wmod_cntnr->ImageSize >=
162 (DWORD)wmod_child->BaseOfImage + wmod_child->ImageSize;
165 static BOOL CALLBACK info_mod_cb(PSTR mod_name, DWORD base, void* ctx)
167 struct info_module* im = (struct info_module*)ctx;
169 if (im->num_used + 1 > im->num_alloc)
172 im->mi = dbg_heap_realloc(im->mi, im->num_alloc * sizeof(*im->mi));
174 im->mi[im->num_used].SizeOfStruct = sizeof(im->mi[im->num_used]);
175 if (SymGetModuleInfo(dbg_curr_process->handle, base, &im->mi[im->num_used]))
182 /***********************************************************************
185 * Display information about a given module (DLL or EXE), or about all modules
187 void info_win32_module(DWORD base)
189 if (!dbg_curr_process || !dbg_curr_thread)
191 dbg_printf("Cannot get info on module while no process is loaded\n");
199 mi.SizeOfStruct = sizeof(mi);
201 if (!SymGetModuleInfo(dbg_curr_process->handle, base, &mi))
203 dbg_printf("'0x%08lx' is not a valid module address\n", base);
206 module_print_info(&mi, mi.SymType);
210 struct info_module im;
215 im.num_alloc = im.num_used = 0;
217 /* this is a wine specific options to return also ELF modules in the
220 SymSetOptions((opt = SymGetOptions()) | 0x40000000);
221 SymEnumerateModules(dbg_curr_process->handle, info_mod_cb, (void*)&im);
224 qsort(im.mi, im.num_used, sizeof(im.mi[0]), module_compare);
226 dbg_printf("Module\tAddress\t\t\tDebug info\tName (%d modules)\n", im.num_used);
228 for (i = 0; i < im.num_used; i++)
230 if (strstr(im.mi[i].ModuleName, "<elf>"))
233 module_print_info(&im.mi[i], (im.mi[i].SymType == SymDia) ? NumSymTypes : im.mi[i].SymType);
234 /* print all modules embedded in this one */
235 for (j = 0; j < im.num_used; j++)
237 if (!strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[i], &im.mi[j]))
239 dbg_printf(" \\-PE\t");
240 module_print_info(&im.mi[j], -1);
246 /* check module is not embedded in another module */
247 for (j = 0; j < im.num_used; j++)
249 if (strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[j], &im.mi[i]))
252 if (j < im.num_used) continue;
253 if (strstr(im.mi[i].ModuleName, ".so") || strchr(im.mi[i].ModuleName, '<'))
257 module_print_info(&im.mi[i], im.mi[i].SymType);
260 HeapFree(GetProcessHeap(), 0, im.mi);
271 static void class_walker(HWND hWnd, struct class_walker* cw)
278 if (!GetClassName(hWnd, clsName, sizeof(clsName)))
280 if ((atom = FindAtom(clsName)) == 0)
283 for (i = 0; i < cw->used; i++)
285 if (cw->table[i] == atom)
290 if (cw->used >= cw->alloc)
293 cw->table = dbg_heap_realloc(cw->table, cw->alloc * sizeof(ATOM));
295 cw->table[cw->used++] = atom;
296 info_win32_class(hWnd, clsName);
300 if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
301 class_walker(child, cw);
302 } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
305 void info_win32_class(HWND hWnd, const char* name)
308 HINSTANCE hInst = hWnd ? (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE) : 0;
312 struct class_walker cw;
315 cw.used = cw.alloc = 0;
316 class_walker(GetDesktopWindow(), &cw);
317 HeapFree(GetProcessHeap(), 0, cw.table);
321 if (!GetClassInfoEx(hInst, name, &wca))
323 dbg_printf("Cannot find class '%s'\n", name);
327 dbg_printf("Class '%s':\n", name);
328 dbg_printf("style=0x%08x wndProc=0x%08lx\n"
329 "inst=%p icon=%p cursor=%p bkgnd=%p\n"
330 "clsExtra=%d winExtra=%d\n",
331 wca.style, (DWORD)wca.lpfnWndProc, wca.hInstance,
332 wca.hIcon, wca.hCursor, wca.hbrBackground,
333 wca.cbClsExtra, wca.cbWndExtra);
335 if (hWnd && wca.cbClsExtra)
340 dbg_printf("Extra bytes:");
341 for (i = 0; i < wca.cbClsExtra / 2; i++)
343 w = GetClassWord(hWnd, i * 2);
344 /* FIXME: depends on i386 endian-ity */
345 dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
351 * + print #windows (or even list of windows...)
352 * + print extra bytes => this requires a window handle on this very class...
356 static void info_window(HWND hWnd, int indent)
364 if (!GetClassName(hWnd, clsName, sizeof(clsName)))
365 strcpy(clsName, "-- Unknown --");
366 if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
367 strcpy(wndName, "-- Empty --");
369 dbg_printf("%*s%08x%*s %-17.17s %08lx %08lx %.14s\n",
370 indent, "", (UINT)hWnd, 12 - indent, "",
371 clsName, GetWindowLong(hWnd, GWL_STYLE),
372 GetWindowLong(hWnd, GWL_WNDPROC), wndName);
374 if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
375 info_window(child, indent + 1);
376 } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
379 void info_win32_window(HWND hWnd, BOOL detailed)
388 if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
392 dbg_printf("%-20.20s %-17.17s %-8.8s %-8.8s %s\n",
393 "Window handle", "Class Name", "Style", "WndProc", "Text");
394 info_window(hWnd, 0);
398 if (!GetClassName(hWnd, clsName, sizeof(clsName)))
399 strcpy(clsName, "-- Unknown --");
400 if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
401 strcpy(wndName, "-- Empty --");
402 if (!GetClientRect(hWnd, &clientRect) ||
403 !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
404 SetRectEmpty(&clientRect);
405 if (!GetWindowRect(hWnd, &windowRect))
406 SetRectEmpty(&windowRect);
408 /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
409 dbg_printf("next=%p child=%p parent=%p owner=%p class='%s'\n"
410 "inst=%p active=%p idmenu=%08lx\n"
411 "style=0x%08lx exstyle=0x%08lx wndproc=0x%08lx text='%s'\n"
412 "client=%ld,%ld-%ld,%ld window=%ld,%ld-%ld,%ld sysmenu=%p\n",
413 GetWindow(hWnd, GW_HWNDNEXT),
414 GetWindow(hWnd, GW_CHILD),
416 GetWindow(hWnd, GW_OWNER),
418 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
419 GetLastActivePopup(hWnd),
420 GetWindowLong(hWnd, GWL_ID),
421 GetWindowLong(hWnd, GWL_STYLE),
422 GetWindowLong(hWnd, GWL_EXSTYLE),
423 GetWindowLong(hWnd, GWL_WNDPROC),
425 clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
426 windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
427 GetSystemMenu(hWnd, FALSE));
429 if (GetClassLong(hWnd, GCL_CBWNDEXTRA))
431 dbg_printf("Extra bytes:");
432 for (i = 0; i < GetClassLong(hWnd, GCL_CBWNDEXTRA) / 2; i++)
434 w = GetWindowWord(hWnd, i * 2);
435 /* FIXME: depends on i386 endian-ity */
436 dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
443 void info_win32_processes(void)
445 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
446 if (snap != INVALID_HANDLE_VALUE)
448 PROCESSENTRY32 entry;
449 DWORD current = dbg_curr_process ? dbg_curr_process->pid : 0;
452 entry.dwSize = sizeof(entry);
453 ok = Process32First(snap, &entry);
455 dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
456 "pid", "threads", "parent", "executable");
459 if (entry.th32ProcessID != GetCurrentProcessId())
460 dbg_printf("%c%08lx %-8ld %08lx '%s'\n",
461 (entry.th32ProcessID == current) ? '>' : ' ',
462 entry.th32ProcessID, entry.cntThreads,
463 entry.th32ParentProcessID, entry.szExeFile);
464 ok = Process32Next(snap, &entry);
470 void info_win32_threads(void)
472 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
473 if (snap != INVALID_HANDLE_VALUE)
477 DWORD lastProcessId = 0;
479 entry.dwSize = sizeof(entry);
480 ok = Thread32First(snap, &entry);
482 dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
483 "process", "tid", "prio");
486 if (entry.th32OwnerProcessID != GetCurrentProcessId())
488 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
489 * listed sequentially, which is not specified in the doc (Wine's implementation
492 if (entry.th32OwnerProcessID != lastProcessId)
494 struct dbg_process* p = dbg_get_process(entry.th32OwnerProcessID);
496 dbg_printf("%08lx%s %s\n",
497 entry.th32OwnerProcessID, p ? " (D)" : "", p ? p->imageName : "");
498 lastProcessId = entry.th32OwnerProcessID;
500 dbg_printf("\t%08lx %4ld%s\n",
501 entry.th32ThreadID, entry.tpBasePri,
502 (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
505 ok = Thread32Next(snap, &entry);
512 /***********************************************************************
513 * info_win32_exceptions
515 * Get info on the exception frames of a given thread.
517 void info_win32_exceptions(DWORD tid)
519 struct dbg_thread* thread;
522 if (!dbg_curr_process || !dbg_curr_thread)
524 dbg_printf("Cannot get info on exceptions while no process is loaded\n");
528 dbg_printf("Exception frames:\n");
530 if (tid == dbg_curr_tid) thread = dbg_curr_thread;
533 thread = dbg_get_thread(dbg_curr_process, tid);
537 dbg_printf("Unknown thread id (0x%08lx) in current process\n", tid);
540 if (SuspendThread(thread->handle) == -1)
542 dbg_printf("Can't suspend thread id (0x%08lx)\n", tid);
547 if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
549 dbg_printf("Can't read TEB:except_frame\n");
553 while (next_frame != (void*)-1)
555 EXCEPTION_REGISTRATION_RECORD frame;
557 dbg_printf("%p: ", next_frame);
558 if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
560 dbg_printf("Invalid frame address\n");
563 dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
564 next_frame = frame.Prev;
567 if (tid != dbg_curr_tid) ResumeThread(thread->handle);
570 void info_win32_segments(DWORD start, int length)
576 if (length == -1) length = (8192 - start);
578 for (i = start; i < start + length; i++)
580 if (!GetThreadSelectorEntry(dbg_curr_thread->handle, (i << 3) | 7, &le))
583 if (le.HighWord.Bits.Type & 0x08)
585 flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
592 flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
595 dbg_printf("%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
597 (le.HighWord.Bits.BaseHi << 24) +
598 (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
599 ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
600 (le.HighWord.Bits.Granularity ? 12 : 0),
601 le.HighWord.Bits.Default_Big ? 32 : 16,
602 flags[0], flags[1], flags[2]);
606 void info_win32_virtual(DWORD pid)
608 MEMORY_BASIC_INFORMATION mbi;
615 if (pid == dbg_curr_pid)
617 if (dbg_curr_process == NULL)
619 dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
622 hProc = dbg_curr_process->handle;
626 hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
629 dbg_printf("Cannot open process <%lu>\n", pid);
634 dbg_printf("Address Size State Type RWX\n");
636 while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
640 case MEM_COMMIT: state = "commit "; break;
641 case MEM_FREE: state = "free "; break;
642 case MEM_RESERVE: state = "reserve"; break;
643 default: state = "??? "; break;
645 if (mbi.State != MEM_FREE)
649 case MEM_IMAGE: type = "image "; break;
650 case MEM_MAPPED: type = "mapped "; break;
651 case MEM_PRIVATE: type = "private"; break;
652 case 0: type = " "; break;
653 default: type = "??? "; break;
655 memset(prot, ' ' , sizeof(prot) - 1);
656 prot[sizeof(prot) - 1] = '\0';
657 if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
659 if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
661 if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
663 if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
671 dbg_printf("%08lx %08lx %s %s %s\n",
672 (DWORD)addr, (DWORD)addr + mbi.RegionSize - 1, state, type, prot);
673 if (addr + mbi.RegionSize < addr) /* wrap around ? */
675 addr += mbi.RegionSize;
677 if (pid != dbg_curr_pid) CloseHandle(hProc);
680 struct dll_option_layout
684 char* const* channels;
688 void info_wine_dbg_channel(BOOL turn_on, const char* chnl, const char* name)
690 struct dbg_lvalue lvalue;
691 struct dll_option_layout dol;
694 unsigned char buffer[32];
700 if (!dbg_curr_process || !dbg_curr_thread)
702 dbg_printf("Cannot set/get debug channels while no process is loaded\n");
706 if (symbol_get_lvalue("first_dll", -1, &lvalue, FALSE) != sglv_found)
710 addr = memory_to_linear_addr(&lvalue.addr);
711 if (!chnl) mask = 15;
712 else if (!strcmp(chnl, "fixme")) mask = 1;
713 else if (!strcmp(chnl, "err")) mask = 2;
714 else if (!strcmp(chnl, "warn")) mask = 4;
715 else if (!strcmp(chnl, "trace")) mask = 8;
716 else { dbg_printf("Unknown channel %s\n", chnl); return; }
718 bAll = !strcmp("all", name);
719 while (addr && dbg_read_memory(addr, &dol, sizeof(dol)))
721 for (i = 0; i < dol.nb_channels; i++)
723 if (dbg_read_memory(dol.channels + i, &str, sizeof(str)) &&
724 dbg_read_memory(str, buffer, sizeof(buffer)) &&
725 (!strcmp(buffer + 1, name) || bAll))
727 if (turn_on) buffer[0] |= mask; else buffer[0] &= ~mask;
728 if (dbg_write_memory(str, buffer, 1)) done++;
733 if (!done) dbg_printf("Unable to find debug channel %s\n", name);
734 else WINE_TRACE("Changed %d channel instances\n", done);