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>",
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 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(SYM_TYPE st)
124 case -1: return "\\";
126 case SymNone: return "--none--";
127 case SymCoff: return "COFF";
128 case SymCv: return "CodeView";
129 case SymPdb: return "PDB";
130 case SymExport: return "Export";
131 case SymDeferred: return "Deferred";
132 case SymSym: return "Sym";
133 case SymDia: return "DIA";
134 case NumSymTypes: return "Stabs";
145 static void module_print_info(const IMAGEHLP_MODULE* mi, SYM_TYPE st)
147 dbg_printf("0x%08lx-%08lx\t%-16s%s\n",
148 mi->BaseOfImage, mi->BaseOfImage + mi->ImageSize,
149 get_symtype_str(st), mi->ModuleName);
152 static int module_compare(const void* p1, const void* p2)
154 return (char*)(((const IMAGEHLP_MODULE*)p1)->BaseOfImage) -
155 (char*)(((const IMAGEHLP_MODULE*)p2)->BaseOfImage);
158 static inline BOOL module_is_container(const IMAGEHLP_MODULE* wmod_cntnr,
159 const IMAGEHLP_MODULE* wmod_child)
161 return wmod_cntnr->BaseOfImage <= wmod_child->BaseOfImage &&
162 (DWORD)wmod_cntnr->BaseOfImage + wmod_cntnr->ImageSize >=
163 (DWORD)wmod_child->BaseOfImage + wmod_child->ImageSize;
166 static BOOL CALLBACK info_mod_cb(PSTR mod_name, DWORD base, void* ctx)
168 struct info_module* im = (struct info_module*)ctx;
170 if (im->num_used + 1 > im->num_alloc)
173 im->mi = dbg_heap_realloc(im->mi, im->num_alloc * sizeof(*im->mi));
175 im->mi[im->num_used].SizeOfStruct = sizeof(im->mi[im->num_used]);
176 if (SymGetModuleInfo(dbg_curr_process->handle, base, &im->mi[im->num_used]))
183 /***********************************************************************
186 * Display information about a given module (DLL or EXE), or about all modules
188 void info_win32_module(DWORD base)
190 if (!dbg_curr_process || !dbg_curr_thread)
192 dbg_printf("Cannot get info on module while no process is loaded\n");
200 mi.SizeOfStruct = sizeof(mi);
202 if (!SymGetModuleInfo(dbg_curr_process->handle, base, &mi))
204 dbg_printf("'0x%08lx' is not a valid module address\n", base);
207 module_print_info(&mi, mi.SymType);
211 struct info_module im;
216 im.num_alloc = im.num_used = 0;
218 /* this is a wine specific options to return also ELF modules in the
221 SymSetOptions((opt = SymGetOptions()) | 0x40000000);
222 SymEnumerateModules(dbg_curr_process->handle, info_mod_cb, (void*)&im);
225 qsort(im.mi, im.num_used, sizeof(im.mi[0]), module_compare);
227 dbg_printf("Module\tAddress\t\t\tDebug info\tName (%d modules)\n", im.num_used);
229 for (i = 0; i < im.num_used; i++)
231 if (strstr(im.mi[i].ModuleName, "<elf>"))
234 module_print_info(&im.mi[i], (im.mi[i].SymType == SymDia) ? NumSymTypes : im.mi[i].SymType);
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], -1);
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], im.mi[i].SymType);
261 HeapFree(GetProcessHeap(), 0, im.mi);
272 static void class_walker(HWND hWnd, struct class_walker* cw)
279 if (!GetClassName(hWnd, clsName, sizeof(clsName)))
281 if ((atom = FindAtom(clsName)) == 0)
284 for (i = 0; i < cw->used; i++)
286 if (cw->table[i] == atom)
291 if (cw->used >= cw->alloc)
294 cw->table = dbg_heap_realloc(cw->table, cw->alloc * sizeof(ATOM));
296 cw->table[cw->used++] = atom;
297 info_win32_class(hWnd, clsName);
301 if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
302 class_walker(child, cw);
303 } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
306 void info_win32_class(HWND hWnd, const char* name)
309 HINSTANCE hInst = hWnd ? (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE) : 0;
313 struct class_walker cw;
316 cw.used = cw.alloc = 0;
317 class_walker(GetDesktopWindow(), &cw);
318 HeapFree(GetProcessHeap(), 0, cw.table);
322 if (!GetClassInfoEx(hInst, name, &wca))
324 dbg_printf("Cannot find class '%s'\n", name);
328 dbg_printf("Class '%s':\n", name);
329 dbg_printf("style=0x%08x wndProc=0x%08lx\n"
330 "inst=%p icon=%p cursor=%p bkgnd=%p\n"
331 "clsExtra=%d winExtra=%d\n",
332 wca.style, (DWORD)wca.lpfnWndProc, wca.hInstance,
333 wca.hIcon, wca.hCursor, wca.hbrBackground,
334 wca.cbClsExtra, wca.cbWndExtra);
336 if (hWnd && wca.cbClsExtra)
341 dbg_printf("Extra bytes:");
342 for (i = 0; i < wca.cbClsExtra / 2; i++)
344 w = GetClassWord(hWnd, i * 2);
345 /* FIXME: depends on i386 endian-ity */
346 dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
352 * + print #windows (or even list of windows...)
353 * + print extra bytes => this requires a window handle on this very class...
357 static void info_window(HWND hWnd, int indent)
365 if (!GetClassName(hWnd, clsName, sizeof(clsName)))
366 strcpy(clsName, "-- Unknown --");
367 if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
368 strcpy(wndName, "-- Empty --");
370 dbg_printf("%*s%08x%*s %-17.17s %08lx %08lx %08lx %.14s\n",
371 indent, "", (UINT)hWnd, 12 - indent, "",
372 clsName, GetWindowLong(hWnd, GWL_STYLE),
373 GetWindowLongPtr(hWnd, GWLP_WNDPROC),
374 GetWindowThreadProcessId(hWnd, NULL), wndName);
376 if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
377 info_window(child, indent + 1);
378 } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
381 void info_win32_window(HWND hWnd, BOOL detailed)
390 if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
394 dbg_printf("%-20.20s %-17.17s %-8.8s %-8.8s %-8.8s %s\n",
395 "Window handle", "Class Name", "Style", "WndProc", "Thread", "Text");
396 info_window(hWnd, 0);
400 if (!GetClassName(hWnd, clsName, sizeof(clsName)))
401 strcpy(clsName, "-- Unknown --");
402 if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
403 strcpy(wndName, "-- Empty --");
404 if (!GetClientRect(hWnd, &clientRect) ||
405 !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
406 SetRectEmpty(&clientRect);
407 if (!GetWindowRect(hWnd, &windowRect))
408 SetRectEmpty(&windowRect);
410 /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
411 dbg_printf("next=%p child=%p parent=%p owner=%p class='%s'\n"
412 "inst=%p active=%p idmenu=%08lx\n"
413 "style=0x%08lx exstyle=0x%08lx wndproc=0x%08lx text='%s'\n"
414 "client=%ld,%ld-%ld,%ld window=%ld,%ld-%ld,%ld sysmenu=%p\n",
415 GetWindow(hWnd, GW_HWNDNEXT),
416 GetWindow(hWnd, GW_CHILD),
418 GetWindow(hWnd, GW_OWNER),
420 (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
421 GetLastActivePopup(hWnd),
422 GetWindowLongPtr(hWnd, GWLP_ID),
423 GetWindowLong(hWnd, GWL_STYLE),
424 GetWindowLong(hWnd, GWL_EXSTYLE),
425 GetWindowLongPtr(hWnd, GWLP_WNDPROC),
427 clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
428 windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
429 GetSystemMenu(hWnd, FALSE));
431 if (GetClassLong(hWnd, GCL_CBWNDEXTRA))
433 dbg_printf("Extra bytes:");
434 for (i = 0; i < GetClassLong(hWnd, GCL_CBWNDEXTRA) / 2; i++)
436 w = GetWindowWord(hWnd, i * 2);
437 /* FIXME: depends on i386 endian-ity */
438 dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
445 void info_win32_processes(void)
447 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
448 if (snap != INVALID_HANDLE_VALUE)
450 PROCESSENTRY32 entry;
451 DWORD current = dbg_curr_process ? dbg_curr_process->pid : 0;
454 entry.dwSize = sizeof(entry);
455 ok = Process32First(snap, &entry);
457 dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
458 "pid", "threads", "parent", "executable");
461 if (entry.th32ProcessID != GetCurrentProcessId())
462 dbg_printf("%c%08lx %-8ld %08lx '%s'\n",
463 (entry.th32ProcessID == current) ? '>' : ' ',
464 entry.th32ProcessID, entry.cntThreads,
465 entry.th32ParentProcessID, entry.szExeFile);
466 ok = Process32Next(snap, &entry);
472 void info_win32_threads(void)
474 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
475 if (snap != INVALID_HANDLE_VALUE)
479 DWORD lastProcessId = 0;
481 entry.dwSize = sizeof(entry);
482 ok = Thread32First(snap, &entry);
484 dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
485 "process", "tid", "prio");
488 if (entry.th32OwnerProcessID != GetCurrentProcessId())
490 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
491 * listed sequentially, which is not specified in the doc (Wine's implementation
494 if (entry.th32OwnerProcessID != lastProcessId)
496 struct dbg_process* p = dbg_get_process(entry.th32OwnerProcessID);
498 dbg_printf("%08lx%s %s\n",
499 entry.th32OwnerProcessID, p ? " (D)" : "", p ? p->imageName : "");
500 lastProcessId = entry.th32OwnerProcessID;
502 dbg_printf("\t%08lx %4ld%s\n",
503 entry.th32ThreadID, entry.tpBasePri,
504 (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
507 ok = Thread32Next(snap, &entry);
514 /***********************************************************************
515 * info_win32_exceptions
517 * Get info on the exception frames of a given thread.
519 void info_win32_exceptions(DWORD tid)
521 struct dbg_thread* thread;
524 if (!dbg_curr_process || !dbg_curr_thread)
526 dbg_printf("Cannot get info on exceptions while no process is loaded\n");
530 dbg_printf("Exception frames:\n");
532 if (tid == dbg_curr_tid) thread = dbg_curr_thread;
535 thread = dbg_get_thread(dbg_curr_process, tid);
539 dbg_printf("Unknown thread id (0x%08lx) in current process\n", tid);
542 if (SuspendThread(thread->handle) == -1)
544 dbg_printf("Can't suspend thread id (0x%08lx)\n", tid);
549 if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
551 dbg_printf("Can't read TEB:except_frame\n");
555 while (next_frame != (void*)-1)
557 EXCEPTION_REGISTRATION_RECORD frame;
559 dbg_printf("%p: ", next_frame);
560 if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
562 dbg_printf("Invalid frame address\n");
565 dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
566 next_frame = frame.Prev;
569 if (tid != dbg_curr_tid) ResumeThread(thread->handle);
572 void info_win32_segments(DWORD start, int length)
578 if (length == -1) length = (8192 - start);
580 for (i = start; i < start + length; i++)
582 if (!GetThreadSelectorEntry(dbg_curr_thread->handle, (i << 3) | 7, &le))
585 if (le.HighWord.Bits.Type & 0x08)
587 flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
594 flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
597 dbg_printf("%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
599 (le.HighWord.Bits.BaseHi << 24) +
600 (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
601 ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
602 (le.HighWord.Bits.Granularity ? 12 : 0),
603 le.HighWord.Bits.Default_Big ? 32 : 16,
604 flags[0], flags[1], flags[2]);
608 void info_win32_virtual(DWORD pid)
610 MEMORY_BASIC_INFORMATION mbi;
617 if (pid == dbg_curr_pid)
619 if (dbg_curr_process == NULL)
621 dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
624 hProc = dbg_curr_process->handle;
628 hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
631 dbg_printf("Cannot open process <%lu>\n", pid);
636 dbg_printf("Address Size State Type RWX\n");
638 while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
642 case MEM_COMMIT: state = "commit "; break;
643 case MEM_FREE: state = "free "; break;
644 case MEM_RESERVE: state = "reserve"; break;
645 default: state = "??? "; break;
647 if (mbi.State != MEM_FREE)
651 case MEM_IMAGE: type = "image "; break;
652 case MEM_MAPPED: type = "mapped "; break;
653 case MEM_PRIVATE: type = "private"; break;
654 case 0: type = " "; break;
655 default: type = "??? "; break;
657 memset(prot, ' ' , sizeof(prot) - 1);
658 prot[sizeof(prot) - 1] = '\0';
659 if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
661 if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
663 if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
665 if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
673 dbg_printf("%08lx %08lx %s %s %s\n",
674 (DWORD)addr, (DWORD)addr + mbi.RegionSize - 1, state, type, prot);
675 if (addr + mbi.RegionSize < addr) /* wrap around ? */
677 addr += mbi.RegionSize;
679 if (pid != dbg_curr_pid) CloseHandle(hProc);
682 void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
684 struct dbg_lvalue lvalue;
685 struct __wine_debug_channel channel;
691 if (!dbg_curr_process || !dbg_curr_thread)
693 dbg_printf("Cannot set/get debug channels while no process is loaded\n");
697 if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
701 addr = memory_to_linear_addr(&lvalue.addr);
704 else if (!strcmp(cls, "fixme")) mask = (1 << __WINE_DBCL_FIXME);
705 else if (!strcmp(cls, "err")) mask = (1 << __WINE_DBCL_ERR);
706 else if (!strcmp(cls, "warn")) mask = (1 << __WINE_DBCL_WARN);
707 else if (!strcmp(cls, "trace")) mask = (1 << __WINE_DBCL_TRACE);
710 dbg_printf("Unknown debug class %s\n", cls);
714 bAll = !strcmp("all", name);
715 while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
717 if (!channel.name[0]) break;
718 if (bAll || !strcmp( channel.name, name ))
720 if (turn_on) channel.flags |= mask;
721 else channel.flags &= ~mask;
722 if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
724 addr = (struct __wine_debug_channel *)addr + 1;
726 if (!done) dbg_printf("Unable to find debug channel %s\n", name);
727 else WINE_TRACE("Changed %d channel instances\n", done);