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>",
61 " bt [<tid>|all] frame <n>",
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)GetWindowLongPtr(hWnd, GWLP_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 %08lx %.14s\n",
370 indent, "", (UINT)hWnd, 12 - indent, "",
371 clsName, GetWindowLong(hWnd, GWL_STYLE),
372 GetWindowLongPtr(hWnd, GWLP_WNDPROC),
373 GetWindowThreadProcessId(hWnd, NULL), wndName);
375 if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
376 info_window(child, indent + 1);
377 } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
380 void info_win32_window(HWND hWnd, BOOL detailed)
389 if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
393 dbg_printf("%-20.20s %-17.17s %-8.8s %-8.8s %-8.8s %s\n",
394 "Window handle", "Class Name", "Style", "WndProc", "Thread", "Text");
395 info_window(hWnd, 0);
399 if (!GetClassName(hWnd, clsName, sizeof(clsName)))
400 strcpy(clsName, "-- Unknown --");
401 if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
402 strcpy(wndName, "-- Empty --");
403 if (!GetClientRect(hWnd, &clientRect) ||
404 !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
405 SetRectEmpty(&clientRect);
406 if (!GetWindowRect(hWnd, &windowRect))
407 SetRectEmpty(&windowRect);
409 /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
410 dbg_printf("next=%p child=%p parent=%p owner=%p class='%s'\n"
411 "inst=%p active=%p idmenu=%08lx\n"
412 "style=0x%08lx exstyle=0x%08lx wndproc=0x%08lx text='%s'\n"
413 "client=%ld,%ld-%ld,%ld window=%ld,%ld-%ld,%ld sysmenu=%p\n",
414 GetWindow(hWnd, GW_HWNDNEXT),
415 GetWindow(hWnd, GW_CHILD),
417 GetWindow(hWnd, GW_OWNER),
419 (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
420 GetLastActivePopup(hWnd),
421 GetWindowLongPtr(hWnd, GWLP_ID),
422 GetWindowLong(hWnd, GWL_STYLE),
423 GetWindowLong(hWnd, GWL_EXSTYLE),
424 GetWindowLongPtr(hWnd, GWLP_WNDPROC),
426 clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
427 windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
428 GetSystemMenu(hWnd, FALSE));
430 if (GetClassLong(hWnd, GCL_CBWNDEXTRA))
432 dbg_printf("Extra bytes:");
433 for (i = 0; i < GetClassLong(hWnd, GCL_CBWNDEXTRA) / 2; i++)
435 w = GetWindowWord(hWnd, i * 2);
436 /* FIXME: depends on i386 endian-ity */
437 dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
444 void info_win32_processes(void)
446 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
447 if (snap != INVALID_HANDLE_VALUE)
449 PROCESSENTRY32 entry;
450 DWORD current = dbg_curr_process ? dbg_curr_process->pid : 0;
453 entry.dwSize = sizeof(entry);
454 ok = Process32First(snap, &entry);
456 dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
457 "pid", "threads", "parent", "executable");
460 if (entry.th32ProcessID != GetCurrentProcessId())
461 dbg_printf("%c%08lx %-8ld %08lx '%s'\n",
462 (entry.th32ProcessID == current) ? '>' : ' ',
463 entry.th32ProcessID, entry.cntThreads,
464 entry.th32ParentProcessID, entry.szExeFile);
465 ok = Process32Next(snap, &entry);
471 void info_win32_threads(void)
473 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
474 if (snap != INVALID_HANDLE_VALUE)
478 DWORD lastProcessId = 0;
480 entry.dwSize = sizeof(entry);
481 ok = Thread32First(snap, &entry);
483 dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
484 "process", "tid", "prio");
487 if (entry.th32OwnerProcessID != GetCurrentProcessId())
489 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
490 * listed sequentially, which is not specified in the doc (Wine's implementation
493 if (entry.th32OwnerProcessID != lastProcessId)
495 struct dbg_process* p = dbg_get_process(entry.th32OwnerProcessID);
497 dbg_printf("%08lx%s %s\n",
498 entry.th32OwnerProcessID, p ? " (D)" : "", p ? p->imageName : "");
499 lastProcessId = entry.th32OwnerProcessID;
501 dbg_printf("\t%08lx %4ld%s\n",
502 entry.th32ThreadID, entry.tpBasePri,
503 (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
506 ok = Thread32Next(snap, &entry);
513 /***********************************************************************
514 * info_win32_exceptions
516 * Get info on the exception frames of a given thread.
518 void info_win32_exceptions(DWORD tid)
520 struct dbg_thread* thread;
523 if (!dbg_curr_process || !dbg_curr_thread)
525 dbg_printf("Cannot get info on exceptions while no process is loaded\n");
529 dbg_printf("Exception frames:\n");
531 if (tid == dbg_curr_tid) thread = dbg_curr_thread;
534 thread = dbg_get_thread(dbg_curr_process, tid);
538 dbg_printf("Unknown thread id (0x%08lx) in current process\n", tid);
541 if (SuspendThread(thread->handle) == -1)
543 dbg_printf("Can't suspend thread id (0x%08lx)\n", tid);
548 if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
550 dbg_printf("Can't read TEB:except_frame\n");
554 while (next_frame != (void*)-1)
556 EXCEPTION_REGISTRATION_RECORD frame;
558 dbg_printf("%p: ", next_frame);
559 if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
561 dbg_printf("Invalid frame address\n");
564 dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
565 next_frame = frame.Prev;
568 if (tid != dbg_curr_tid) ResumeThread(thread->handle);
571 void info_win32_segments(DWORD start, int length)
577 if (length == -1) length = (8192 - start);
579 for (i = start; i < start + length; i++)
581 if (!GetThreadSelectorEntry(dbg_curr_thread->handle, (i << 3) | 7, &le))
584 if (le.HighWord.Bits.Type & 0x08)
586 flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
593 flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
596 dbg_printf("%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
598 (le.HighWord.Bits.BaseHi << 24) +
599 (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
600 ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
601 (le.HighWord.Bits.Granularity ? 12 : 0),
602 le.HighWord.Bits.Default_Big ? 32 : 16,
603 flags[0], flags[1], flags[2]);
607 void info_win32_virtual(DWORD pid)
609 MEMORY_BASIC_INFORMATION mbi;
616 if (pid == dbg_curr_pid)
618 if (dbg_curr_process == NULL)
620 dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
623 hProc = dbg_curr_process->handle;
627 hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
630 dbg_printf("Cannot open process <%lu>\n", pid);
635 dbg_printf("Address Size State Type RWX\n");
637 while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
641 case MEM_COMMIT: state = "commit "; break;
642 case MEM_FREE: state = "free "; break;
643 case MEM_RESERVE: state = "reserve"; break;
644 default: state = "??? "; break;
646 if (mbi.State != MEM_FREE)
650 case MEM_IMAGE: type = "image "; break;
651 case MEM_MAPPED: type = "mapped "; break;
652 case MEM_PRIVATE: type = "private"; break;
653 case 0: type = " "; break;
654 default: type = "??? "; break;
656 memset(prot, ' ' , sizeof(prot) - 1);
657 prot[sizeof(prot) - 1] = '\0';
658 if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
660 if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
662 if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
664 if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
672 dbg_printf("%08lx %08lx %s %s %s\n",
673 (DWORD)addr, (DWORD)addr + mbi.RegionSize - 1, state, type, prot);
674 if (addr + mbi.RegionSize < addr) /* wrap around ? */
676 addr += mbi.RegionSize;
678 if (pid != dbg_curr_pid) CloseHandle(hProc);
681 void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
683 struct dbg_lvalue lvalue;
684 struct __wine_debug_channel channel;
690 if (!dbg_curr_process || !dbg_curr_thread)
692 dbg_printf("Cannot set/get debug channels while no process is loaded\n");
696 if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
700 addr = memory_to_linear_addr(&lvalue.addr);
703 else if (!strcmp(cls, "fixme")) mask = (1 << __WINE_DBCL_FIXME);
704 else if (!strcmp(cls, "err")) mask = (1 << __WINE_DBCL_ERR);
705 else if (!strcmp(cls, "warn")) mask = (1 << __WINE_DBCL_WARN);
706 else if (!strcmp(cls, "trace")) mask = (1 << __WINE_DBCL_TRACE);
709 dbg_printf("Unknown debug class %s\n", cls);
713 bAll = !strcmp("all", name);
714 while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
716 if (!channel.name[0]) break;
717 if (bAll || !strcmp( channel.name, name ))
719 if (turn_on) channel.flags |= mask;
720 else channel.flags &= ~mask;
721 if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
723 addr = (struct __wine_debug_channel *)addr + 1;
725 if (!done) dbg_printf("Unable to find debug channel %s\n", name);
726 else WINE_TRACE("Changed %d channel instances\n", done);