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
;
154 static void module_print_info(const IMAGEHLP_MODULE64
* mi
, BOOL is_embedded
)
156 dbg_printf("%8s-%8s\t%-16s%s\n",
157 wine_dbgstr_longlong(mi
->BaseOfImage
),
158 wine_dbgstr_longlong(mi
->BaseOfImage
+ mi
->ImageSize
),
159 is_embedded
? "\\" : get_symtype_str(mi
), mi
->ModuleName
);
162 static int module_compare(const void* p1
, const void* p2
)
164 LONGLONG val
= ((const IMAGEHLP_MODULE64
*)p1
)->BaseOfImage
-
165 ((const IMAGEHLP_MODULE64
*)p2
)->BaseOfImage
;
166 if (val
< 0) return -1;
167 else if (val
> 0) return 1;
171 static inline BOOL
module_is_container(const IMAGEHLP_MODULE64
* wmod_cntnr
,
172 const IMAGEHLP_MODULE64
* wmod_child
)
174 return wmod_cntnr
->BaseOfImage
<= wmod_child
->BaseOfImage
&&
175 wmod_cntnr
->BaseOfImage
+ wmod_cntnr
->ImageSize
>=
176 wmod_child
->BaseOfImage
+ wmod_child
->ImageSize
;
179 static BOOL CALLBACK
info_mod_cb(PCSTR mod_name
, DWORD64 base
, PVOID ctx
)
181 struct info_module
* im
= (struct info_module
*)ctx
;
183 if (im
->num_used
+ 1 > im
->num_alloc
)
186 im
->mi
= dbg_heap_realloc(im
->mi
, im
->num_alloc
* sizeof(*im
->mi
));
188 im
->mi
[im
->num_used
].SizeOfStruct
= sizeof(im
->mi
[im
->num_used
]);
189 if (SymGetModuleInfo64(dbg_curr_process
->handle
, base
, &im
->mi
[im
->num_used
]))
196 /***********************************************************************
199 * Display information about a given module (DLL or EXE), or about all modules
201 void info_win32_module(DWORD64 base
)
203 struct info_module im
;
204 int i
, j
, num_printed
= 0;
207 if (!dbg_curr_process
)
209 dbg_printf("Cannot get info on module while no process is loaded\n");
214 im
.num_alloc
= im
.num_used
= 0;
216 /* this is a wine specific options to return also ELF modules in the
219 SymSetOptions((opt
= SymGetOptions()) | 0x40000000);
220 SymEnumerateModules64(dbg_curr_process
->handle
, info_mod_cb
, (void*)&im
);
223 qsort(im
.mi
, im
.num_used
, sizeof(im
.mi
[0]), module_compare
);
225 dbg_printf("Module\tAddress\t\t\tDebug info\tName (%d modules)\n", im
.num_used
);
227 for (i
= 0; i
< im
.num_used
; i
++)
230 (base
< im
.mi
[i
].BaseOfImage
|| base
>= im
.mi
[i
].BaseOfImage
+ im
.mi
[i
].ImageSize
))
232 if (strstr(im
.mi
[i
].ModuleName
, "<elf>"))
235 module_print_info(&im
.mi
[i
], FALSE
);
236 /* print all modules embedded in this one */
237 for (j
= 0; j
< im
.num_used
; j
++)
239 if (!strstr(im
.mi
[j
].ModuleName
, "<elf>") && module_is_container(&im
.mi
[i
], &im
.mi
[j
]))
241 dbg_printf(" \\-PE\t");
242 module_print_info(&im
.mi
[j
], TRUE
);
248 /* check module is not embedded in another module */
249 for (j
= 0; j
< im
.num_used
; j
++)
251 if (strstr(im
.mi
[j
].ModuleName
, "<elf>") && module_is_container(&im
.mi
[j
], &im
.mi
[i
]))
254 if (j
< im
.num_used
) continue;
255 if (strstr(im
.mi
[i
].ModuleName
, ".so") || strchr(im
.mi
[i
].ModuleName
, '<'))
259 module_print_info(&im
.mi
[i
], FALSE
);
263 HeapFree(GetProcessHeap(), 0, im
.mi
);
265 if (base
&& !num_printed
)
266 dbg_printf("'0x%x%08x' is not a valid module address\n", (DWORD
)(base
>> 32), (DWORD
)base
);
276 static void class_walker(HWND hWnd
, struct class_walker
* cw
)
283 if (!GetClassName(hWnd
, clsName
, sizeof(clsName
)))
285 if ((atom
= FindAtom(clsName
)) == 0)
288 for (i
= 0; i
< cw
->used
; i
++)
290 if (cw
->table
[i
] == atom
)
295 if (cw
->used
>= cw
->alloc
)
298 cw
->table
= dbg_heap_realloc(cw
->table
, cw
->alloc
* sizeof(ATOM
));
300 cw
->table
[cw
->used
++] = atom
;
301 info_win32_class(hWnd
, clsName
);
305 if ((child
= GetWindow(hWnd
, GW_CHILD
)) != 0)
306 class_walker(child
, cw
);
307 } while ((hWnd
= GetWindow(hWnd
, GW_HWNDNEXT
)) != 0);
310 void info_win32_class(HWND hWnd
, const char* name
)
313 HINSTANCE hInst
= hWnd
? (HINSTANCE
)GetWindowLongPtr(hWnd
, GWLP_HINSTANCE
) : 0;
317 struct class_walker cw
;
320 cw
.used
= cw
.alloc
= 0;
321 class_walker(GetDesktopWindow(), &cw
);
322 HeapFree(GetProcessHeap(), 0, cw
.table
);
326 if (!GetClassInfoEx(hInst
, name
, &wca
))
328 dbg_printf("Cannot find class '%s'\n", name
);
332 dbg_printf("Class '%s':\n", name
);
333 dbg_printf("style=0x%08x wndProc=%p\n"
334 "inst=%p icon=%p cursor=%p bkgnd=%p\n"
335 "clsExtra=%d winExtra=%d\n",
336 wca
.style
, wca
.lpfnWndProc
, wca
.hInstance
,
337 wca
.hIcon
, wca
.hCursor
, wca
.hbrBackground
,
338 wca
.cbClsExtra
, wca
.cbWndExtra
);
340 if (hWnd
&& wca
.cbClsExtra
)
345 dbg_printf("Extra bytes:");
346 for (i
= 0; i
< wca
.cbClsExtra
/ 2; i
++)
348 w
= GetClassWord(hWnd
, i
* 2);
349 /* FIXME: depends on i386 endian-ity */
350 dbg_printf(" %02x %02x", HIBYTE(w
), LOBYTE(w
));
356 * + print #windows (or even list of windows...)
357 * + print extra bytes => this requires a window handle on this very class...
361 static void info_window(HWND hWnd
, int indent
)
369 if (!GetClassName(hWnd
, clsName
, sizeof(clsName
)))
370 strcpy(clsName
, "-- Unknown --");
371 if (!GetWindowText(hWnd
, wndName
, sizeof(wndName
)))
372 strcpy(wndName
, "-- Empty --");
374 dbg_printf("%*s%08lx%*s %-17.17s %08x %08x %08x %.14s\n",
375 indent
, "", (DWORD_PTR
)hWnd
, 12 - indent
, "",
376 clsName
, GetWindowLong(hWnd
, GWL_STYLE
),
377 GetWindowLongPtr(hWnd
, GWLP_WNDPROC
),
378 GetWindowThreadProcessId(hWnd
, NULL
), wndName
);
380 if ((child
= GetWindow(hWnd
, GW_CHILD
)) != 0)
381 info_window(child
, indent
+ 1);
382 } while ((hWnd
= GetWindow(hWnd
, GW_HWNDNEXT
)) != 0);
385 void info_win32_window(HWND hWnd
, BOOL detailed
)
394 if (!IsWindow(hWnd
)) hWnd
= GetDesktopWindow();
398 dbg_printf("%-20.20s %-17.17s %-8.8s %-8.8s %-8.8s %s\n",
399 "Window handle", "Class Name", "Style", "WndProc", "Thread", "Text");
400 info_window(hWnd
, 0);
404 if (!GetClassName(hWnd
, clsName
, sizeof(clsName
)))
405 strcpy(clsName
, "-- Unknown --");
406 if (!GetWindowText(hWnd
, wndName
, sizeof(wndName
)))
407 strcpy(wndName
, "-- Empty --");
408 if (!GetClientRect(hWnd
, &clientRect
) ||
409 !MapWindowPoints(hWnd
, 0, (LPPOINT
) &clientRect
, 2))
410 SetRectEmpty(&clientRect
);
411 if (!GetWindowRect(hWnd
, &windowRect
))
412 SetRectEmpty(&windowRect
);
414 /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
415 dbg_printf("next=%p child=%p parent=%p owner=%p class='%s'\n"
416 "inst=%p active=%p idmenu=%08x\n"
417 "style=0x%08x exstyle=0x%08x wndproc=0x%08x text='%s'\n"
418 "client=%d,%d-%d,%d window=%d,%d-%d,%d sysmenu=%p\n",
419 GetWindow(hWnd
, GW_HWNDNEXT
),
420 GetWindow(hWnd
, GW_CHILD
),
422 GetWindow(hWnd
, GW_OWNER
),
424 (HINSTANCE
)GetWindowLongPtr(hWnd
, GWLP_HINSTANCE
),
425 GetLastActivePopup(hWnd
),
426 GetWindowLongPtr(hWnd
, GWLP_ID
),
427 GetWindowLong(hWnd
, GWL_STYLE
),
428 GetWindowLong(hWnd
, GWL_EXSTYLE
),
429 GetWindowLongPtr(hWnd
, GWLP_WNDPROC
),
431 clientRect
.left
, clientRect
.top
, clientRect
.right
, clientRect
.bottom
,
432 windowRect
.left
, windowRect
.top
, windowRect
.right
, windowRect
.bottom
,
433 GetSystemMenu(hWnd
, FALSE
));
435 if (GetClassLong(hWnd
, GCL_CBWNDEXTRA
))
437 dbg_printf("Extra bytes:");
438 for (i
= 0; i
< GetClassLong(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)" : "", p
? p
->imageName
: "");
504 lastProcessId
= entry
.th32OwnerProcessID
;
506 dbg_printf("\t%08x %4d%s\n",
507 entry
.th32ThreadID
, entry
.tpBasePri
,
508 (entry
.th32ThreadID
== dbg_curr_tid
) ? " <==" : "");
511 ok
= Thread32Next(snap
, &entry
);
518 /***********************************************************************
519 * info_win32_exceptions
521 * Get info on the exception frames of a given thread.
523 void info_win32_exceptions(DWORD tid
)
525 struct dbg_thread
* thread
;
528 if (!dbg_curr_process
|| !dbg_curr_thread
)
530 dbg_printf("Cannot get info on exceptions while no process is loaded\n");
534 dbg_printf("Exception frames:\n");
536 if (tid
== dbg_curr_tid
) thread
= dbg_curr_thread
;
539 thread
= dbg_get_thread(dbg_curr_process
, tid
);
543 dbg_printf("Unknown thread id (%04x) in current process\n", tid
);
546 if (SuspendThread(thread
->handle
) == -1)
548 dbg_printf("Can't suspend thread id (%04x)\n", tid
);
553 if (!dbg_read_memory(thread
->teb
, &next_frame
, sizeof(next_frame
)))
555 dbg_printf("Can't read TEB:except_frame\n");
559 while (next_frame
!= (void*)-1)
561 EXCEPTION_REGISTRATION_RECORD frame
;
563 dbg_printf("%p: ", next_frame
);
564 if (!dbg_read_memory(next_frame
, &frame
, sizeof(frame
)))
566 dbg_printf("Invalid frame address\n");
569 dbg_printf("prev=%p handler=%p\n", frame
.Prev
, frame
.Handler
);
570 next_frame
= frame
.Prev
;
573 if (tid
!= dbg_curr_tid
) ResumeThread(thread
->handle
);
576 void info_win32_segments(DWORD start
, int length
)
582 if (length
== -1) length
= (8192 - start
);
584 for (i
= start
; i
< start
+ length
; i
++)
586 if (!GetThreadSelectorEntry(dbg_curr_thread
->handle
, (i
<< 3) | 7, &le
))
589 if (le
.HighWord
.Bits
.Type
& 0x08)
591 flags
[0] = (le
.HighWord
.Bits
.Type
& 0x2) ? 'r' : '-';
598 flags
[1] = (le
.HighWord
.Bits
.Type
& 0x2) ? 'w' : '-';
601 dbg_printf("%04x: sel=%04x base=%08x limit=%08x %d-bit %c%c%c\n",
603 (le
.HighWord
.Bits
.BaseHi
<< 24) +
604 (le
.HighWord
.Bits
.BaseMid
<< 16) + le
.BaseLow
,
605 ((le
.HighWord
.Bits
.LimitHi
<< 8) + le
.LimitLow
) <<
606 (le
.HighWord
.Bits
.Granularity
? 12 : 0),
607 le
.HighWord
.Bits
.Default_Big
? 32 : 16,
608 flags
[0], flags
[1], flags
[2]);
612 void info_win32_virtual(DWORD pid
)
614 MEMORY_BASIC_INFORMATION mbi
;
621 if (pid
== dbg_curr_pid
)
623 if (dbg_curr_process
== NULL
)
625 dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
628 hProc
= dbg_curr_process
->handle
;
632 hProc
= OpenProcess(PROCESS_QUERY_INFORMATION
| PROCESS_VM_READ
, FALSE
, pid
);
635 dbg_printf("Cannot open process <%04x>\n", pid
);
640 dbg_printf("Address Size State Type RWX\n");
642 while (VirtualQueryEx(hProc
, addr
, &mbi
, sizeof(mbi
)) >= sizeof(mbi
))
646 case MEM_COMMIT
: state
= "commit "; break;
647 case MEM_FREE
: state
= "free "; break;
648 case MEM_RESERVE
: state
= "reserve"; break;
649 default: state
= "??? "; break;
651 if (mbi
.State
!= MEM_FREE
)
655 case MEM_IMAGE
: type
= "image "; break;
656 case MEM_MAPPED
: type
= "mapped "; break;
657 case MEM_PRIVATE
: type
= "private"; break;
658 case 0: type
= " "; break;
659 default: type
= "??? "; break;
661 memset(prot
, ' ' , sizeof(prot
) - 1);
662 prot
[sizeof(prot
) - 1] = '\0';
663 if (mbi
.AllocationProtect
& (PAGE_READONLY
|PAGE_READWRITE
|PAGE_EXECUTE_READ
|PAGE_EXECUTE_READWRITE
))
665 if (mbi
.AllocationProtect
& (PAGE_READWRITE
|PAGE_EXECUTE_READWRITE
))
667 if (mbi
.AllocationProtect
& (PAGE_WRITECOPY
|PAGE_EXECUTE_WRITECOPY
))
669 if (mbi
.AllocationProtect
& (PAGE_EXECUTE
|PAGE_EXECUTE_READ
|PAGE_EXECUTE_READWRITE
))
677 dbg_printf("%08lx %08lx %s %s %s\n",
678 (DWORD_PTR
)addr
, (DWORD_PTR
)addr
+ mbi
.RegionSize
- 1, state
, type
, prot
);
679 if (addr
+ mbi
.RegionSize
< addr
) /* wrap around ? */
681 addr
+= mbi
.RegionSize
;
683 if (pid
!= dbg_curr_pid
) CloseHandle(hProc
);
686 void info_wine_dbg_channel(BOOL turn_on
, const char* cls
, const char* name
)
688 struct dbg_lvalue lvalue
;
689 struct __wine_debug_channel channel
;
695 if (!dbg_curr_process
|| !dbg_curr_thread
)
697 dbg_printf("Cannot set/get debug channels while no process is loaded\n");
701 if (symbol_get_lvalue("debug_options", -1, &lvalue
, FALSE
) != sglv_found
)
705 addr
= memory_to_linear_addr(&lvalue
.addr
);
708 else if (!strcmp(cls
, "fixme")) mask
= (1 << __WINE_DBCL_FIXME
);
709 else if (!strcmp(cls
, "err")) mask
= (1 << __WINE_DBCL_ERR
);
710 else if (!strcmp(cls
, "warn")) mask
= (1 << __WINE_DBCL_WARN
);
711 else if (!strcmp(cls
, "trace")) mask
= (1 << __WINE_DBCL_TRACE
);
714 dbg_printf("Unknown debug class %s\n", cls
);
718 bAll
= !strcmp("all", name
);
719 while (addr
&& dbg_read_memory(addr
, &channel
, sizeof(channel
)))
721 if (!channel
.name
[0]) break;
722 if (bAll
|| !strcmp( channel
.name
, name
))
724 if (turn_on
) channel
.flags
|= mask
;
725 else channel
.flags
&= ~mask
;
726 if (dbg_write_memory(addr
, &channel
, sizeof(channel
))) done
++;
728 addr
= (struct __wine_debug_channel
*)addr
+ 1;
730 if (!done
) dbg_printf("Unable to find debug channel %s\n", name
);
731 else WINE_TRACE("Changed %d channel instances\n", done
);