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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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>",
61 " bt [<tid>|all] frame <n>",
63 " list <lines> disassemble [<addr>][,<addr>]",
64 " show dir dir <path>",
65 " set <reg> = <expr> set *<addr> = <expr>",
67 " 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 of the general registers at top of stack",
105 " info all-reg Displays the general and floating point registers",
106 " info segments <pid> Displays information about all known segments",
107 " info share Displays all loaded modules",
108 " info share <addr> Displays internal module state",
109 " info stack Dumps information about top of stack",
110 " info symbol <sym> Displays information about a given symbol",
111 " info thread Shows all running threads",
112 " info wnd <handle> Displays internal window state",
117 while (infotext[i]) dbg_printf("%s\n", infotext[i++]);
120 static const char* get_symtype_str(const IMAGEHLP_MODULE64* mi)
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";
135 case 'S' | ('T' << 8) | ('A' << 16) | ('B' << 24):
137 case 'D' | ('W' << 8) | ('A' << 16) | ('R' << 24):
148 IMAGEHLP_MODULE64* mi;
153 static void module_print_info(const IMAGEHLP_MODULE64* mi, BOOL is_embedded)
155 dbg_printf("%8s-%8s\t%-16s%s\n",
156 wine_dbgstr_longlong(mi->BaseOfImage),
157 wine_dbgstr_longlong(mi->BaseOfImage + mi->ImageSize),
158 is_embedded ? "\\" : get_symtype_str(mi), mi->ModuleName);
161 static int module_compare(const void* p1, const void* p2)
163 LONGLONG val = ((const IMAGEHLP_MODULE64*)p1)->BaseOfImage -
164 ((const IMAGEHLP_MODULE64*)p2)->BaseOfImage;
165 if (val < 0) return -1;
166 else if (val > 0) return 1;
170 static inline BOOL module_is_container(const IMAGEHLP_MODULE64* wmod_cntnr,
171 const IMAGEHLP_MODULE64* wmod_child)
173 return wmod_cntnr->BaseOfImage <= wmod_child->BaseOfImage &&
174 wmod_cntnr->BaseOfImage + wmod_cntnr->ImageSize >=
175 wmod_child->BaseOfImage + wmod_child->ImageSize;
178 static BOOL CALLBACK info_mod_cb(PCSTR mod_name, DWORD64 base, PVOID ctx)
180 struct info_module* im = ctx;
182 if (im->num_used + 1 > im->num_alloc)
185 im->mi = dbg_heap_realloc(im->mi, im->num_alloc * sizeof(*im->mi));
187 im->mi[im->num_used].SizeOfStruct = sizeof(im->mi[im->num_used]);
188 if (SymGetModuleInfo64(dbg_curr_process->handle, base, &im->mi[im->num_used]))
195 /***********************************************************************
198 * Display information about a given module (DLL or EXE), or about all modules
200 void info_win32_module(DWORD64 base)
202 struct info_module im;
203 UINT i, j, num_printed = 0;
206 if (!dbg_curr_process)
208 dbg_printf("Cannot get info on module while no process is loaded\n");
213 im.num_alloc = im.num_used = 0;
215 /* this is a wine specific options to return also ELF modules in the
218 SymSetOptions((opt = SymGetOptions()) | 0x40000000);
219 SymEnumerateModules64(dbg_curr_process->handle, info_mod_cb, (void*)&im);
222 qsort(im.mi, im.num_used, sizeof(im.mi[0]), module_compare);
224 dbg_printf("Module\tAddress\t\t\tDebug info\tName (%d modules)\n", im.num_used);
226 for (i = 0; i < im.num_used; i++)
229 (base < im.mi[i].BaseOfImage || base >= im.mi[i].BaseOfImage + im.mi[i].ImageSize))
231 if (strstr(im.mi[i].ModuleName, "<elf>"))
234 module_print_info(&im.mi[i], FALSE);
235 /* print all modules embedded in this one */
236 for (j = 0; j < im.num_used; j++)
238 if (!strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[i], &im.mi[j]))
240 dbg_printf(" \\-PE\t");
241 module_print_info(&im.mi[j], TRUE);
247 /* check module is not embedded in another module */
248 for (j = 0; j < im.num_used; j++)
250 if (strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[j], &im.mi[i]))
253 if (j < im.num_used) continue;
254 if (strstr(im.mi[i].ModuleName, ".so") || strchr(im.mi[i].ModuleName, '<'))
258 module_print_info(&im.mi[i], FALSE);
262 HeapFree(GetProcessHeap(), 0, im.mi);
264 if (base && !num_printed)
265 dbg_printf("'0x%x%08x' is not a valid module address\n", (DWORD)(base >> 32), (DWORD)base);
275 static void class_walker(HWND hWnd, struct class_walker* cw)
282 if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
284 if ((atom = FindAtomA(clsName)) == 0)
287 for (i = 0; i < cw->used; i++)
289 if (cw->table[i] == atom)
294 if (cw->used >= cw->alloc)
297 cw->table = dbg_heap_realloc(cw->table, cw->alloc * sizeof(ATOM));
299 cw->table[cw->used++] = atom;
300 info_win32_class(hWnd, clsName);
304 if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
305 class_walker(child, cw);
306 } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
309 void info_win32_class(HWND hWnd, const char* name)
312 HINSTANCE hInst = hWnd ? (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE) : 0;
316 struct class_walker cw;
319 cw.used = cw.alloc = 0;
320 class_walker(GetDesktopWindow(), &cw);
321 HeapFree(GetProcessHeap(), 0, cw.table);
325 if (!GetClassInfoExA(hInst, name, &wca))
327 dbg_printf("Cannot find class '%s'\n", name);
331 dbg_printf("Class '%s':\n", name);
332 dbg_printf("style=0x%08x wndProc=%p\n"
333 "inst=%p icon=%p cursor=%p bkgnd=%p\n"
334 "clsExtra=%d winExtra=%d\n",
335 wca.style, wca.lpfnWndProc, wca.hInstance,
336 wca.hIcon, wca.hCursor, wca.hbrBackground,
337 wca.cbClsExtra, wca.cbWndExtra);
339 if (hWnd && wca.cbClsExtra)
344 dbg_printf("Extra bytes:");
345 for (i = 0; i < wca.cbClsExtra / 2; i++)
347 w = GetClassWord(hWnd, i * 2);
348 /* FIXME: depends on i386 endian-ity */
349 dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
355 * + print #windows (or even list of windows...)
356 * + print extra bytes => this requires a window handle on this very class...
360 static void info_window(HWND hWnd, int indent)
368 if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
369 strcpy(clsName, "-- Unknown --");
370 if (!GetWindowTextA(hWnd, wndName, sizeof(wndName)))
371 strcpy(wndName, "-- Empty --");
373 dbg_printf("%*s%08lx%*s %-17.17s %08x %08lx %08x %.14s\n",
374 indent, "", (DWORD_PTR)hWnd, 12 - indent, "",
375 clsName, GetWindowLongW(hWnd, GWL_STYLE),
376 (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_WNDPROC),
377 GetWindowThreadProcessId(hWnd, NULL), wndName);
379 if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
380 info_window(child, indent + 1);
381 } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
384 void info_win32_window(HWND hWnd, BOOL detailed)
392 if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
396 dbg_printf("%-20.20s %-17.17s %-8.8s %-8.8s %-8.8s %s\n",
397 "Window handle", "Class Name", "Style", "WndProc", "Thread", "Text");
398 info_window(hWnd, 0);
402 if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
403 strcpy(clsName, "-- Unknown --");
404 if (!GetWindowTextA(hWnd, wndName, sizeof(wndName)))
405 strcpy(wndName, "-- Empty --");
406 if (!GetClientRect(hWnd, &clientRect) ||
407 !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
408 SetRectEmpty(&clientRect);
409 if (!GetWindowRect(hWnd, &windowRect))
410 SetRectEmpty(&windowRect);
412 /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
413 dbg_printf("next=%p child=%p parent=%p owner=%p class='%s'\n"
414 "inst=%p active=%p idmenu=%08lx\n"
415 "style=0x%08x exstyle=0x%08x wndproc=0x%08lx text='%s'\n"
416 "client=%d,%d-%d,%d window=%d,%d-%d,%d sysmenu=%p\n",
417 GetWindow(hWnd, GW_HWNDNEXT),
418 GetWindow(hWnd, GW_CHILD),
420 GetWindow(hWnd, GW_OWNER),
422 (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE),
423 GetLastActivePopup(hWnd),
424 (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_ID),
425 GetWindowLongW(hWnd, GWL_STYLE),
426 GetWindowLongW(hWnd, GWL_EXSTYLE),
427 (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_WNDPROC),
429 clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
430 windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
431 GetSystemMenu(hWnd, FALSE));
433 if (GetClassLongW(hWnd, GCL_CBWNDEXTRA))
437 dbg_printf("Extra bytes:");
438 for (i = 0; i < GetClassLongW(hWnd, GCL_CBWNDEXTRA) / 2; i++)
440 w = GetWindowWord(hWnd, i * 2);
441 /* FIXME: depends on i386 endian-ity */
442 dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
449 void info_win32_processes(void)
451 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
452 if (snap != INVALID_HANDLE_VALUE)
454 PROCESSENTRY32 entry;
455 DWORD current = dbg_curr_process ? dbg_curr_process->pid : 0;
458 entry.dwSize = sizeof(entry);
459 ok = Process32First(snap, &entry);
461 dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
462 "pid", "threads", "parent", "executable");
465 if (entry.th32ProcessID != GetCurrentProcessId())
466 dbg_printf("%c%08x %-8d %08x '%s'\n",
467 (entry.th32ProcessID == current) ? '>' : ' ',
468 entry.th32ProcessID, entry.cntThreads,
469 entry.th32ParentProcessID, entry.szExeFile);
470 ok = Process32Next(snap, &entry);
476 void info_win32_threads(void)
478 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
479 if (snap != INVALID_HANDLE_VALUE)
483 DWORD lastProcessId = 0;
485 entry.dwSize = sizeof(entry);
486 ok = Thread32First(snap, &entry);
488 dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
489 "process", "tid", "prio");
492 if (entry.th32OwnerProcessID != GetCurrentProcessId())
494 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
495 * listed sequentially, which is not specified in the doc (Wine's implementation
498 if (entry.th32OwnerProcessID != lastProcessId)
500 struct dbg_process* p = dbg_get_process(entry.th32OwnerProcessID);
502 dbg_printf("%08x%s %s\n",
503 entry.th32OwnerProcessID, p ? " (D)" : "",
504 p ? dbg_W2A(p->imageName, -1) : "");
505 lastProcessId = entry.th32OwnerProcessID;
507 dbg_printf("\t%08x %4d%s\n",
508 entry.th32ThreadID, entry.tpBasePri,
509 (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
512 ok = Thread32Next(snap, &entry);
519 /***********************************************************************
520 * info_win32_exceptions
522 * Get info on the exception frames of a given thread.
524 void info_win32_exceptions(DWORD tid)
526 struct dbg_thread* thread;
529 if (!dbg_curr_process || !dbg_curr_thread)
531 dbg_printf("Cannot get info on exceptions while no process is loaded\n");
535 dbg_printf("Exception frames:\n");
537 if (tid == dbg_curr_tid) thread = dbg_curr_thread;
540 thread = dbg_get_thread(dbg_curr_process, tid);
544 dbg_printf("Unknown thread id (%04x) in current process\n", tid);
547 if (SuspendThread(thread->handle) == -1)
549 dbg_printf("Can't suspend thread id (%04x)\n", tid);
554 if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
556 dbg_printf("Can't read TEB:except_frame\n");
560 while (next_frame != (void*)-1)
562 EXCEPTION_REGISTRATION_RECORD frame;
564 dbg_printf("%p: ", next_frame);
565 if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
567 dbg_printf("Invalid frame address\n");
570 dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
571 next_frame = frame.Prev;
574 if (tid != dbg_curr_tid) ResumeThread(thread->handle);
577 void info_win32_segments(DWORD start, int length)
583 if (length == -1) length = (8192 - start);
585 for (i = start; i < start + length; i++)
587 if (!dbg_curr_process->process_io->get_selector(dbg_curr_thread->handle, (i << 3) | 7, &le))
590 if (le.HighWord.Bits.Type & 0x08)
592 flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
599 flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
602 dbg_printf("%04x: sel=%04x base=%08x limit=%08x %d-bit %c%c%c\n",
604 (le.HighWord.Bits.BaseHi << 24) +
605 (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
606 ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
607 (le.HighWord.Bits.Granularity ? 12 : 0),
608 le.HighWord.Bits.Default_Big ? 32 : 16,
609 flags[0], flags[1], flags[2]);
613 void info_win32_virtual(DWORD pid)
615 MEMORY_BASIC_INFORMATION mbi;
622 if (pid == dbg_curr_pid)
624 if (dbg_curr_process == NULL)
626 dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
629 hProc = dbg_curr_process->handle;
633 hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
636 dbg_printf("Cannot open process <%04x>\n", pid);
641 dbg_printf("Address End State Type RWX\n");
643 while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
647 case MEM_COMMIT: state = "commit "; break;
648 case MEM_FREE: state = "free "; break;
649 case MEM_RESERVE: state = "reserve"; break;
650 default: state = "??? "; break;
652 if (mbi.State != MEM_FREE)
656 case MEM_IMAGE: type = "image "; break;
657 case MEM_MAPPED: type = "mapped "; break;
658 case MEM_PRIVATE: type = "private"; break;
659 case 0: type = " "; break;
660 default: type = "??? "; break;
662 memset(prot, ' ' , sizeof(prot) - 1);
663 prot[sizeof(prot) - 1] = '\0';
664 if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
666 if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
668 if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
670 if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
678 dbg_printf("%08lx %08lx %s %s %s\n",
679 (DWORD_PTR)addr, (DWORD_PTR)addr + mbi.RegionSize - 1, state, type, prot);
680 if (addr + mbi.RegionSize < addr) /* wrap around ? */
682 addr += mbi.RegionSize;
684 if (pid != dbg_curr_pid) CloseHandle(hProc);
687 void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
689 struct dbg_lvalue lvalue;
690 struct __wine_debug_channel channel;
696 if (!dbg_curr_process || !dbg_curr_thread)
698 dbg_printf("Cannot set/get debug channels while no process is loaded\n");
702 if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
706 addr = memory_to_linear_addr(&lvalue.addr);
709 else if (!strcmp(cls, "fixme")) mask = (1 << __WINE_DBCL_FIXME);
710 else if (!strcmp(cls, "err")) mask = (1 << __WINE_DBCL_ERR);
711 else if (!strcmp(cls, "warn")) mask = (1 << __WINE_DBCL_WARN);
712 else if (!strcmp(cls, "trace")) mask = (1 << __WINE_DBCL_TRACE);
715 dbg_printf("Unknown debug class %s\n", cls);
719 bAll = !strcmp("all", name);
720 while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
722 if (!channel.name[0]) break;
723 if (bAll || !strcmp( channel.name, name ))
725 if (turn_on) channel.flags |= mask;
726 else channel.flags &= ~mask;
727 if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
729 addr = (struct __wine_debug_channel *)addr + 1;
731 if (!done) dbg_printf("Unable to find debug channel %s\n", name);
732 else WINE_TRACE("Changed %d channel instances\n", done);