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
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(winedbg
);
35 /***********************************************************************
38 * Implementation of the 'help' command.
43 static const char * const helptext
[] =
45 "The commands accepted by the Wine debugger are a reasonable",
46 "subset of the commands that gdb accepts.",
47 "The commands currently are:",
49 " attach <wpid> detach",
50 " break [*<addr>] watch | rwatch *<addr>",
51 " delete break bpnum disable bpnum",
52 " enable bpnum condition <bpnum> [<expr>]",
55 " stepi [N] nexti [N]",
56 " x <addr> print <expr>",
57 " display <expr> undisplay <disnum>",
58 " local display <expr> delete display <disnum>",
59 " enable display <disnum> disable display <disnum>",
60 " bt [<tid>|all] frame <n>",
62 " list <lines> disassemble [<addr>][,<addr>]",
63 " show dir dir <path>",
64 " set <reg> = <expr> set *<addr> = <expr>",
66 " info (see 'help info' for options) thread <tid>",
68 "The 'x' command accepts repeat counts and formats (including 'i') in the",
69 "same way that gdb does.\n",
71 "The following are examples of legal expressions:",
72 " $eax $eax+0x3 0x1000 ($eip + 256) *$eax *($esp + 3)",
73 " Also, a nm format symbol table can be read from a file using the",
74 " symbolfile command.", /* Symbols can also be defined individually with",
75 " the define command.", */
80 while (helptext
[i
]) dbg_printf("%s\n", helptext
[i
++]);
84 /***********************************************************************
87 * Implementation of the 'help info' command.
92 static const char * const infotext
[] =
94 "The info commands allow you to get assorted bits of interesting stuff",
95 "to be displayed. The options are:",
96 " info break Displays information about breakpoints",
97 " info class <name> Displays information about window class <name>",
98 " info display Shows auto-display expressions in use",
99 " info except <pid> Shows exception handler chain (in a given process)",
100 " info locals Displays values of all local vars for current frame",
101 " info maps <pid> Shows virtual mappings (in a given process)",
102 " info process Shows all running processes",
103 " info reg Displays values of the general registers at top of stack",
104 " info all-reg Displays the general and floating point registers",
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 [<len>] Dumps information about top of stack, up to len words",
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(const IMAGEHLP_MODULE64
* mi
)
124 case SymNone
: return "--none--";
125 case SymCoff
: return "COFF";
126 case SymCv
: return "CodeView";
127 case SymPdb
: return "PDB";
128 case SymExport
: return "Export";
129 case SymDeferred
: return "Deferred";
130 case SymSym
: return "Sym";
134 case 'S' | ('T' << 8) | ('A' << 16) | ('B' << 24):
136 case 'D' | ('W' << 8) | ('A' << 16) | ('R' << 24):
137 /* previous versions of dbghelp used to report this... */
140 if ((mi
->CVSig
& 0x00FFFFFF) == ('D' | ('W' << 8) | ('F' << 16)))
143 DWORD versbit
= mi
->CVSig
>> 24;
144 strcpy(tmp
, "Dwarf");
145 if (versbit
& 1) strcat(tmp
, "-2");
146 if (versbit
& 2) strcat(tmp
, "-3");
147 if (versbit
& 4) strcat(tmp
, "-4");
148 if (versbit
& 8) strcat(tmp
, "-5");
158 IMAGEHLP_MODULE64 mi
;
164 struct info_module
*modules
;
169 static const char* get_machine_str(DWORD machine
)
174 case IMAGE_FILE_MACHINE_AMD64
: return "x86_64";
175 case IMAGE_FILE_MACHINE_I386
: return "i386";
176 case IMAGE_FILE_MACHINE_ARM64
: return "arm64";
177 case IMAGE_FILE_MACHINE_ARM
:
178 case IMAGE_FILE_MACHINE_ARMNT
: return "arm";
179 default: sprintf(tmp
, "<%lx>", machine
); return tmp
;
183 static void module_print_info(const struct info_module
*module
, BOOL is_embedded
, BOOL multi_machine
)
186 dbg_printf("%16I64x-%16I64x\t%s\t%-16s%s\n",
187 module
->mi
.BaseOfImage
,
188 module
->mi
.BaseOfImage
+ module
->mi
.ImageSize
,
189 get_machine_str(module
->mi
.MachineType
),
190 is_embedded
? "\\" : get_symtype_str(&module
->mi
), module
->name
);
192 dbg_printf("%*.*I64x-%*.*I64x\t%-16s%s\n",
193 ADDRWIDTH
, ADDRWIDTH
, module
->mi
.BaseOfImage
,
194 ADDRWIDTH
, ADDRWIDTH
, module
->mi
.BaseOfImage
+ module
->mi
.ImageSize
,
195 is_embedded
? "\\" : get_symtype_str(&module
->mi
), module
->name
);
198 static int __cdecl
module_compare(const void* p1
, const void* p2
)
200 struct info_module
*left
= (struct info_module
*)p1
;
201 struct info_module
*right
= (struct info_module
*)p2
;
202 LONGLONG val
= left
->mi
.BaseOfImage
- right
->mi
.BaseOfImage
;
204 if (val
< 0) return -1;
205 else if (val
> 0) return 1;
209 static inline BOOL
module_is_container(const struct info_module
*wmod_cntnr
,
210 const struct info_module
*wmod_child
)
212 return wmod_cntnr
->mi
.BaseOfImage
<= wmod_child
->mi
.BaseOfImage
&&
213 wmod_cntnr
->mi
.BaseOfImage
+ wmod_cntnr
->mi
.ImageSize
>=
214 wmod_child
->mi
.BaseOfImage
+ wmod_child
->mi
.ImageSize
;
217 static BOOL CALLBACK
info_mod_cb(PCSTR mod_name
, DWORD64 base
, PVOID ctx
)
219 struct info_modules
*im
= ctx
;
221 if (im
->num_used
+ 1 > im
->num_alloc
)
223 struct info_module
* new = realloc(im
->modules
, (im
->num_alloc
+ 16) * sizeof(*im
->modules
));
224 if (!new) return FALSE
; /* stop enumeration in case of OOM */
228 im
->modules
[im
->num_used
].mi
.SizeOfStruct
= sizeof(im
->modules
[im
->num_used
].mi
);
229 if (SymGetModuleInfo64(dbg_curr_process
->handle
, base
, &im
->modules
[im
->num_used
].mi
))
231 const int dst_len
= sizeof(im
->modules
[im
->num_used
].name
);
232 lstrcpynA(im
->modules
[im
->num_used
].name
, mod_name
, dst_len
- 1);
233 im
->modules
[im
->num_used
].name
[dst_len
- 1] = 0;
239 /***********************************************************************
242 * Display information about a given module (DLL or EXE), or about all modules
244 void info_win32_module(DWORD64 base
, BOOL multi_machine
)
246 struct info_modules im
;
247 UINT i
, j
, num_printed
= 0;
251 if (!dbg_curr_process
)
253 dbg_printf("Cannot get info on module while no process is loaded\n");
258 im
.num_alloc
= im
.num_used
= 0;
260 /* this is a wine specific options to return also ELF modules in the
263 opt
= SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES
, TRUE
);
264 SymEnumerateModules64(dbg_curr_process
->handle
, info_mod_cb
, &im
);
265 SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES
, opt
);
267 if (!im
.num_used
) return;
269 qsort(im
.modules
, im
.num_used
, sizeof(im
.modules
[0]), module_compare
);
270 machine
= im
.modules
[0].mi
.MachineType
;
273 dbg_printf("Module\tAddress\t\t\t\t\tMachine\tDebug info\tName (%d modules)\n", im
.num_used
);
276 unsigned same_machine
= 0;
277 for (i
= 0; i
< im
.num_used
; i
++)
278 if (machine
== im
.modules
[i
].mi
.MachineType
) same_machine
++;
279 dbg_printf("Module\tAddress\t\t\t%sDebug info\tName (%d modules",
280 ADDRWIDTH
== 16 ? "\t\t" : "", same_machine
);
281 if (same_machine
!= im
.num_used
)
282 dbg_printf(", %u for wow64 not listed", im
.num_used
- same_machine
);
286 for (i
= 0; i
< im
.num_used
; i
++)
289 (base
< im
.modules
[i
].mi
.BaseOfImage
|| base
>= im
.modules
[i
].mi
.BaseOfImage
+ im
.modules
[i
].mi
.ImageSize
))
291 if (!multi_machine
&& machine
!= im
.modules
[i
].mi
.MachineType
) continue;
292 if (strstr(im
.modules
[i
].name
, "<elf>"))
295 module_print_info(&im
.modules
[i
], FALSE
, multi_machine
);
296 /* print all modules embedded in this one */
297 for (j
= 0; j
< im
.num_used
; j
++)
299 if (!strstr(im
.modules
[j
].name
, "<elf>") && module_is_container(&im
.modules
[i
], &im
.modules
[j
]))
301 dbg_printf(" \\-PE\t");
302 module_print_info(&im
.modules
[j
], TRUE
, multi_machine
);
308 /* check module is not embedded in another module */
309 for (j
= 0; j
< im
.num_used
; j
++)
311 if (strstr(im
.modules
[j
].name
, "<elf>") && module_is_container(&im
.modules
[j
], &im
.modules
[i
]))
314 if (j
< im
.num_used
) continue;
315 if (strstr(im
.modules
[i
].name
, ".so") || strchr(im
.modules
[i
].name
, '<'))
319 module_print_info(&im
.modules
[i
], FALSE
, multi_machine
);
325 if (base
&& !num_printed
)
326 dbg_printf("'0x%0*I64x' is not a valid module address\n", ADDRWIDTH
, base
);
336 static void class_walker(HWND hWnd
, struct class_walker
* cw
)
343 if (!GetClassNameA(hWnd
, clsName
, sizeof(clsName
)))
345 if ((atom
= FindAtomA(clsName
)) == 0)
348 for (i
= 0; i
< cw
->used
; i
++)
350 if (cw
->table
[i
] == atom
)
355 if (cw
->used
>= cw
->alloc
)
357 ATOM
* new = realloc(cw
->table
, (cw
->alloc
+ 16) * sizeof(ATOM
));
362 cw
->table
[cw
->used
++] = atom
;
363 info_win32_class(hWnd
, clsName
);
367 if ((child
= GetWindow(hWnd
, GW_CHILD
)) != 0)
368 class_walker(child
, cw
);
369 } while ((hWnd
= GetWindow(hWnd
, GW_HWNDNEXT
)) != 0);
372 void info_win32_class(HWND hWnd
, const char* name
)
375 HINSTANCE hInst
= hWnd
? (HINSTANCE
)GetWindowLongPtrW(hWnd
, GWLP_HINSTANCE
) : 0;
379 struct class_walker cw
;
382 cw
.used
= cw
.alloc
= 0;
383 class_walker(GetDesktopWindow(), &cw
);
388 if (!GetClassInfoExA(hInst
, name
, &wca
))
390 dbg_printf("Cannot find class '%s'\n", name
);
394 dbg_printf("Class '%s':\n", name
);
395 dbg_printf("style=0x%08x wndProc=%p\n"
396 "inst=%p icon=%p cursor=%p bkgnd=%p\n"
397 "clsExtra=%d winExtra=%d\n",
398 wca
.style
, wca
.lpfnWndProc
, wca
.hInstance
,
399 wca
.hIcon
, wca
.hCursor
, wca
.hbrBackground
,
400 wca
.cbClsExtra
, wca
.cbWndExtra
);
402 if (hWnd
&& wca
.cbClsExtra
)
407 dbg_printf("Extra bytes:");
408 for (i
= 0; i
< wca
.cbClsExtra
/ 2; i
++)
410 w
= GetClassWord(hWnd
, i
* 2);
411 /* FIXME: depends on i386 endian-ity */
412 dbg_printf(" %02x %02x", HIBYTE(w
), LOBYTE(w
));
418 * + print #windows (or even list of windows...)
419 * + print extra bytes => this requires a window handle on this very class...
423 static void info_window(HWND hWnd
, int indent
)
431 if (!GetClassNameA(hWnd
, clsName
, sizeof(clsName
)))
432 strcpy(clsName
, "-- Unknown --");
433 if (!GetWindowTextA(hWnd
, wndName
, sizeof(wndName
)))
434 strcpy(wndName
, "-- Empty --");
436 dbg_printf("%*s%08Ix%*s %-17.17s %08lx %0*Ix %08lx %.14s\n",
437 indent
, "", (DWORD_PTR
)hWnd
, 12 - indent
, "",
438 clsName
, GetWindowLongW(hWnd
, GWL_STYLE
),
439 ADDRWIDTH
, (ULONG_PTR
)GetWindowLongPtrW(hWnd
, GWLP_WNDPROC
),
440 GetWindowThreadProcessId(hWnd
, NULL
), wndName
);
442 if ((child
= GetWindow(hWnd
, GW_CHILD
)) != 0)
443 info_window(child
, indent
+ 1);
444 } while ((hWnd
= GetWindow(hWnd
, GW_HWNDNEXT
)) != 0);
447 void info_win32_window(HWND hWnd
, BOOL detailed
)
455 if (!IsWindow(hWnd
)) hWnd
= GetDesktopWindow();
459 dbg_printf("%-20.20s %-17.17s %-8.8s %-*.*s %-8.8s %s\n",
460 "Window handle", "Class Name", "Style",
461 ADDRWIDTH
, ADDRWIDTH
, "WndProc", "Thread", "Text");
462 info_window(hWnd
, 0);
466 if (!GetClassNameA(hWnd
, clsName
, sizeof(clsName
)))
467 strcpy(clsName
, "-- Unknown --");
468 if (!GetWindowTextA(hWnd
, wndName
, sizeof(wndName
)))
469 strcpy(wndName
, "-- Empty --");
470 if (!GetClientRect(hWnd
, &clientRect
) ||
471 !MapWindowPoints(hWnd
, 0, (LPPOINT
) &clientRect
, 2))
472 SetRectEmpty(&clientRect
);
473 if (!GetWindowRect(hWnd
, &windowRect
))
474 SetRectEmpty(&windowRect
);
476 /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
477 dbg_printf("next=%p child=%p parent=%p owner=%p class='%s'\n"
478 "inst=%p active=%p idmenu=%08Ix\n"
479 "style=0x%08lx exstyle=0x%08lx wndproc=%p text='%s'\n"
480 "client=%ld,%ld-%ld,%ld window=%ld,%ld-%ld,%ld sysmenu=%p\n",
481 GetWindow(hWnd
, GW_HWNDNEXT
),
482 GetWindow(hWnd
, GW_CHILD
),
484 GetWindow(hWnd
, GW_OWNER
),
486 (HINSTANCE
)GetWindowLongPtrW(hWnd
, GWLP_HINSTANCE
),
487 GetLastActivePopup(hWnd
),
488 (ULONG_PTR
)GetWindowLongPtrW(hWnd
, GWLP_ID
),
489 GetWindowLongW(hWnd
, GWL_STYLE
),
490 GetWindowLongW(hWnd
, GWL_EXSTYLE
),
491 (void*)GetWindowLongPtrW(hWnd
, GWLP_WNDPROC
),
493 clientRect
.left
, clientRect
.top
, clientRect
.right
, clientRect
.bottom
,
494 windowRect
.left
, windowRect
.top
, windowRect
.right
, windowRect
.bottom
,
495 GetSystemMenu(hWnd
, FALSE
));
497 if (GetClassLongW(hWnd
, GCL_CBWNDEXTRA
))
501 dbg_printf("Extra bytes:");
502 for (i
= 0; i
< GetClassLongW(hWnd
, GCL_CBWNDEXTRA
) / 2; i
++)
504 w
= GetWindowWord(hWnd
, i
* 2);
505 /* FIXME: depends on i386 endian-ity */
506 dbg_printf(" %02x %02x", HIBYTE(w
), LOBYTE(w
));
513 struct dump_proc_entry
516 unsigned children
; /* index in dump_proc.entries of first child */
517 unsigned sibling
; /* index in dump_proc.entries of next sibling */
522 struct dump_proc_entry
*entries
;
527 static unsigned get_parent(const struct dump_proc
* dp
, unsigned idx
)
531 for (i
= 0; i
< dp
->count
; i
++)
533 if (i
!= idx
&& dp
->entries
[i
].proc
.th32ProcessID
== dp
->entries
[idx
].proc
.th32ParentProcessID
)
539 static void dump_proc_info(const struct dump_proc
* dp
, unsigned idx
, unsigned depth
)
541 struct dump_proc_entry
* dpe
;
543 for ( ; idx
!= -1; idx
= dp
->entries
[idx
].sibling
)
545 assert(idx
< dp
->count
);
546 dpe
= &dp
->entries
[idx
];
547 if (dbg_curr_process
&& dpe
->proc
.th32ProcessID
== dbg_curr_process
->pid
)
549 else if (dpe
->proc
.th32ProcessID
== GetCurrentProcessId())
553 dbg_printf("%c%08lx %-8ld ", info
, dpe
->proc
.th32ProcessID
, dpe
->proc
.cntThreads
);
557 for (i
= 3 * (depth
- 1); i
> 0; i
--) dbg_printf(" ");
560 dbg_printf("'%s'\n", dpe
->proc
.szExeFile
);
561 dump_proc_info(dp
, dpe
->children
, depth
+ 1);
565 void info_win32_processes(void)
567 HANDLE snap
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
568 if (snap
!= INVALID_HANDLE_VALUE
)
571 unsigned i
, first
= -1;
576 dp
.entries
= malloc(sizeof(*dp
.entries
) * dp
.alloc
);
582 dp
.entries
[dp
.count
].proc
.dwSize
= sizeof(dp
.entries
[dp
.count
].proc
);
583 ok
= Process32First(snap
, &dp
.entries
[dp
.count
].proc
);
585 /* fetch all process information into dp */
588 dp
.entries
[dp
.count
++].children
= -1;
589 if (dp
.count
>= dp
.alloc
)
591 struct dump_proc_entry
* new = realloc(dp
.entries
, sizeof(*dp
.entries
) * (dp
.alloc
* 2));
601 dp
.entries
[dp
.count
].proc
.dwSize
= sizeof(dp
.entries
[dp
.count
].proc
);
602 ok
= Process32Next(snap
, &dp
.entries
[dp
.count
].proc
);
605 /* chain the siblings wrt. their parent */
606 for (i
= 0; i
< dp
.count
; i
++)
608 unsigned parent
= get_parent(&dp
, i
);
609 unsigned *chain
= parent
== -1 ? &first
: &dp
.entries
[parent
].children
;
610 dp
.entries
[i
].sibling
= *chain
;
613 dbg_printf(" %-8.8s %-8.8s %s (all id:s are in hex)\n", "pid", "threads", "executable");
614 dump_proc_info(&dp
, first
, 0);
619 static BOOL
get_process_name(DWORD pid
, PROCESSENTRY32W
* entry
)
622 HANDLE snap
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
624 if (snap
!= INVALID_HANDLE_VALUE
)
626 entry
->dwSize
= sizeof(*entry
);
627 if (Process32FirstW(snap
, entry
))
628 while (!(ret
= (entry
->th32ProcessID
== pid
)) &&
629 Process32NextW(snap
, entry
));
635 WCHAR
* fetch_thread_description(DWORD tid
)
637 static HRESULT (WINAPI
*my_GetThreadDescription
)(HANDLE
, PWSTR
*) = NULL
;
638 static BOOL resolved
= FALSE
;
644 HMODULE kernelbase
= GetModuleHandleA("kernelbase.dll");
646 my_GetThreadDescription
= (void *)GetProcAddress(kernelbase
, "GetThreadDescription");
650 if (!my_GetThreadDescription
)
653 h
= OpenThread(THREAD_QUERY_LIMITED_INFORMATION
, FALSE
, tid
);
657 my_GetThreadDescription(h
, &desc
);
660 if (desc
&& desc
[0] == '\0')
668 void info_win32_threads(void)
670 HANDLE snap
= CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD
, 0);
671 if (snap
!= INVALID_HANDLE_VALUE
)
675 DWORD lastProcessId
= 0;
676 struct dbg_process
* p
= NULL
;
677 struct dbg_thread
* t
= NULL
;
680 entry
.dwSize
= sizeof(entry
);
681 ok
= Thread32First(snap
, &entry
);
683 dbg_printf("%-8.8s %-8.8s %s %s (all IDs are in hex)\n",
684 "process", "tid", "prio", "name");
687 if (entry
.th32OwnerProcessID
!= GetCurrentProcessId())
689 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
690 * listed sequentially, which is not specified in the doc (Wine's implementation
693 if (entry
.th32OwnerProcessID
!= lastProcessId
)
695 PROCESSENTRY32W pcs_entry
;
696 const WCHAR
* exename
;
698 p
= dbg_get_process(entry
.th32OwnerProcessID
);
700 exename
= p
->imageName
;
701 else if (get_process_name(entry
.th32OwnerProcessID
, &pcs_entry
))
702 exename
= pcs_entry
.szExeFile
;
706 dbg_printf("%08lx%s %ls\n",
707 entry
.th32OwnerProcessID
, p
? " (D)" : "", exename
);
708 lastProcessId
= entry
.th32OwnerProcessID
;
710 dbg_printf("\t%08lx %4ld%s ",
711 entry
.th32ThreadID
, entry
.tpBasePri
,
712 (entry
.th32ThreadID
== dbg_curr_tid
) ? " <==" : " ");
714 if ((description
= fetch_thread_description(entry
.th32ThreadID
)))
716 dbg_printf("%ls\n", description
);
717 LocalFree(description
);
721 t
= dbg_get_thread(p
, entry
.th32ThreadID
);
722 dbg_printf("%s\n", t
? t
->name
: "");
725 ok
= Thread32Next(snap
, &entry
);
732 /***********************************************************************
733 * info_win32_frame_exceptions
735 * Get info on the exception frames of a given thread.
737 void info_win32_frame_exceptions(DWORD tid
)
739 struct dbg_thread
* thread
;
742 if (!dbg_curr_process
|| !dbg_curr_thread
)
744 dbg_printf("Cannot get info on exceptions while no process is loaded\n");
748 dbg_printf("Exception frames:\n");
750 if (tid
== dbg_curr_tid
) thread
= dbg_curr_thread
;
753 thread
= dbg_get_thread(dbg_curr_process
, tid
);
757 dbg_printf("Unknown thread id (%04lx) in current process\n", tid
);
760 if (SuspendThread(thread
->handle
) == -1)
762 dbg_printf("Can't suspend thread id (%04lx)\n", tid
);
767 if (!dbg_read_memory(thread
->teb
, &next_frame
, sizeof(next_frame
)))
769 dbg_printf("Can't read TEB:except_frame\n");
773 while (next_frame
!= (void*)-1)
775 EXCEPTION_REGISTRATION_RECORD frame
;
777 dbg_printf("%p: ", next_frame
);
778 if (!dbg_read_memory(next_frame
, &frame
, sizeof(frame
)))
780 dbg_printf("Invalid frame address\n");
783 dbg_printf("prev=%p handler=%p\n", frame
.Prev
, frame
.Handler
);
784 next_frame
= frame
.Prev
;
787 if (tid
!= dbg_curr_tid
) ResumeThread(thread
->handle
);
790 void info_win32_segments(DWORD start
, int length
)
796 if (length
== -1) length
= (8192 - start
);
798 for (i
= start
; i
< start
+ length
; i
++)
800 if (!dbg_curr_process
->process_io
->get_selector(dbg_curr_thread
->handle
, (i
<< 3) | 7, &le
))
803 if (le
.HighWord
.Bits
.Type
& 0x08)
805 flags
[0] = (le
.HighWord
.Bits
.Type
& 0x2) ? 'r' : '-';
812 flags
[1] = (le
.HighWord
.Bits
.Type
& 0x2) ? 'w' : '-';
815 dbg_printf("%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
817 (le
.HighWord
.Bits
.BaseHi
<< 24) +
818 (le
.HighWord
.Bits
.BaseMid
<< 16) + le
.BaseLow
,
819 ((le
.HighWord
.Bits
.LimitHi
<< 8) + le
.LimitLow
) <<
820 (le
.HighWord
.Bits
.Granularity
? 12 : 0),
821 le
.HighWord
.Bits
.Default_Big
? 32 : 16,
822 flags
[0], flags
[1], flags
[2]);
826 void info_win32_virtual(DWORD pid
)
828 MEMORY_BASIC_INFORMATION mbi
;
835 if (pid
== dbg_curr_pid
)
837 if (dbg_curr_process
== NULL
)
839 dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
842 hProc
= dbg_curr_process
->handle
;
846 hProc
= OpenProcess(PROCESS_QUERY_INFORMATION
| PROCESS_VM_READ
, FALSE
, pid
);
849 dbg_printf("Cannot open process <%04lx>\n", pid
);
854 dbg_printf("Address End State Type RWX\n");
856 while (VirtualQueryEx(hProc
, addr
, &mbi
, sizeof(mbi
)) >= sizeof(mbi
))
860 case MEM_COMMIT
: state
= "commit "; break;
861 case MEM_FREE
: state
= "free "; break;
862 case MEM_RESERVE
: state
= "reserve"; break;
863 default: state
= "??? "; break;
865 if (mbi
.State
!= MEM_FREE
)
869 case MEM_IMAGE
: type
= "image "; break;
870 case MEM_MAPPED
: type
= "mapped "; break;
871 case MEM_PRIVATE
: type
= "private"; break;
872 case 0: type
= " "; break;
873 default: type
= "??? "; break;
875 memset(prot
, ' ' , sizeof(prot
) - 1);
876 prot
[sizeof(prot
) - 1] = '\0';
877 if (mbi
.AllocationProtect
& (PAGE_READONLY
|PAGE_READWRITE
|PAGE_EXECUTE_READ
|PAGE_EXECUTE_READWRITE
|PAGE_WRITECOPY
|PAGE_EXECUTE_WRITECOPY
))
879 if (mbi
.AllocationProtect
& (PAGE_READWRITE
|PAGE_EXECUTE_READWRITE
))
881 if (mbi
.AllocationProtect
& (PAGE_WRITECOPY
|PAGE_EXECUTE_WRITECOPY
))
883 if (mbi
.AllocationProtect
& (PAGE_EXECUTE
|PAGE_EXECUTE_READ
|PAGE_EXECUTE_READWRITE
|PAGE_EXECUTE_WRITECOPY
))
891 dbg_printf("%0*Ix %0*Ix %s %s %s\n",
892 ADDRWIDTH
, (DWORD_PTR
)addr
, ADDRWIDTH
, (DWORD_PTR
)addr
+ mbi
.RegionSize
- 1, state
, type
, prot
);
893 if (addr
+ mbi
.RegionSize
< addr
) /* wrap around ? */
895 addr
+= mbi
.RegionSize
;
897 if (pid
!= dbg_curr_pid
) CloseHandle(hProc
);
900 void info_wine_dbg_channel(BOOL turn_on
, const char* cls
, const char* name
)
902 struct dbg_lvalue lvalue
;
903 struct __wine_debug_channel channel
;
909 if (!dbg_curr_process
|| !dbg_curr_thread
)
911 dbg_printf("Cannot set/get debug channels while no process is loaded\n");
915 if (symbol_get_lvalue("debug_options", -1, &lvalue
, FALSE
) != sglv_found
)
919 addr
= memory_to_linear_addr(&lvalue
.addr
);
922 else if (!strcmp(cls
, "fixme")) mask
= (1 << __WINE_DBCL_FIXME
);
923 else if (!strcmp(cls
, "err")) mask
= (1 << __WINE_DBCL_ERR
);
924 else if (!strcmp(cls
, "warn")) mask
= (1 << __WINE_DBCL_WARN
);
925 else if (!strcmp(cls
, "trace")) mask
= (1 << __WINE_DBCL_TRACE
);
928 dbg_printf("Unknown debug class %s\n", cls
);
932 bAll
= !strcmp("all", name
);
933 while (addr
&& dbg_read_memory(addr
, &channel
, sizeof(channel
)))
935 if (!channel
.name
[0]) break;
936 if (bAll
|| !strcmp( channel
.name
, name
))
938 if (turn_on
) channel
.flags
|= mask
;
939 else channel
.flags
&= ~mask
;
940 if (dbg_write_memory(addr
, &channel
, sizeof(channel
))) done
++;
942 addr
= (struct __wine_debug_channel
*)addr
+ 1;
944 if (!done
) dbg_printf("Unable to find debug channel %s\n", name
);
945 else WINE_TRACE("Changed %d channel instances\n", done
);
948 void info_win32_exception(void)
950 const EXCEPTION_RECORD
* rec
;
952 char hexbuf
[MAX_OFFSET_TO_STR_LEN
];
954 if (!dbg_curr_thread
->in_exception
)
956 dbg_printf("Thread isn't in an exception\n");
959 rec
= &dbg_curr_thread
->excpt_record
;
960 memory_get_current_pc(&addr
);
962 /* print some infos */
964 dbg_curr_thread
->first_chance
? "First chance exception" : "Unhandled exception");
965 switch (rec
->ExceptionCode
)
967 case EXCEPTION_BREAKPOINT
:
968 dbg_printf("breakpoint");
970 case EXCEPTION_SINGLE_STEP
:
971 dbg_printf("single step");
973 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
974 dbg_printf("divide by zero");
976 case EXCEPTION_INT_OVERFLOW
:
977 dbg_printf("overflow");
979 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED
:
980 dbg_printf("array bounds");
982 case EXCEPTION_ILLEGAL_INSTRUCTION
:
983 dbg_printf("illegal instruction");
985 case EXCEPTION_STACK_OVERFLOW
:
986 dbg_printf("stack overflow");
988 case EXCEPTION_PRIV_INSTRUCTION
:
989 dbg_printf("privileged instruction");
991 case EXCEPTION_ACCESS_VIOLATION
:
992 if (rec
->NumberParameters
== 2)
993 dbg_printf("page fault on %s access to 0x%0*Ix",
994 rec
->ExceptionInformation
[0] == EXCEPTION_WRITE_FAULT
? "write" :
995 rec
->ExceptionInformation
[0] == EXCEPTION_EXECUTE_FAULT
? "execute" : "read",
996 ADDRWIDTH
, rec
->ExceptionInformation
[1]);
998 dbg_printf("page fault");
1000 case EXCEPTION_DATATYPE_MISALIGNMENT
:
1001 dbg_printf("Alignment");
1006 case CONTROL_C_EXIT
:
1009 case STATUS_POSSIBLE_DEADLOCK
:
1013 recaddr
.Mode
= AddrModeFlat
;
1014 recaddr
.Offset
= rec
->ExceptionInformation
[0];
1016 dbg_printf("wait failed on critical section ");
1017 print_address(&recaddr
, FALSE
);
1020 case EXCEPTION_WINE_STUB
:
1022 char dll
[64], name
[256];
1023 memory_get_string(dbg_curr_process
,
1024 (void*)rec
->ExceptionInformation
[0], TRUE
, FALSE
,
1026 if (HIWORD(rec
->ExceptionInformation
[1]))
1027 memory_get_string(dbg_curr_process
,
1028 (void*)rec
->ExceptionInformation
[1], TRUE
, FALSE
,
1029 name
, sizeof(name
));
1031 sprintf( name
, "%Id", rec
->ExceptionInformation
[1] );
1032 dbg_printf("unimplemented function %s.%s called", dll
, name
);
1035 case EXCEPTION_WINE_ASSERTION
:
1036 dbg_printf("assertion failed");
1038 case EXCEPTION_FLT_DENORMAL_OPERAND
:
1039 dbg_printf("denormal float operand");
1041 case EXCEPTION_FLT_DIVIDE_BY_ZERO
:
1042 dbg_printf("divide by zero");
1044 case EXCEPTION_FLT_INEXACT_RESULT
:
1045 dbg_printf("inexact float result");
1047 case EXCEPTION_FLT_INVALID_OPERATION
:
1048 dbg_printf("invalid float operation");
1050 case EXCEPTION_FLT_OVERFLOW
:
1051 dbg_printf("floating point overflow");
1053 case EXCEPTION_FLT_UNDERFLOW
:
1054 dbg_printf("floating point underflow");
1056 case EXCEPTION_FLT_STACK_CHECK
:
1057 dbg_printf("floating point stack check");
1059 case EXCEPTION_WINE_CXX_EXCEPTION
:
1060 if(rec
->NumberParameters
== 3 && rec
->ExceptionInformation
[0] == EXCEPTION_WINE_CXX_FRAME_MAGIC
)
1061 dbg_printf("C++ exception(object = 0x%0*Ix, type = 0x%0*Ix)",
1062 ADDRWIDTH
, rec
->ExceptionInformation
[1], ADDRWIDTH
, rec
->ExceptionInformation
[2]);
1063 else if(rec
->NumberParameters
== 4 && rec
->ExceptionInformation
[0] == EXCEPTION_WINE_CXX_FRAME_MAGIC
)
1064 dbg_printf("C++ exception(object = %p, type = %p, base = %p)",
1065 (void*)rec
->ExceptionInformation
[1], (void*)rec
->ExceptionInformation
[2],
1066 (void*)rec
->ExceptionInformation
[3]);
1068 dbg_printf("C++ exception with strange parameter count %ld or magic 0x%0*Ix",
1069 rec
->NumberParameters
, ADDRWIDTH
, rec
->ExceptionInformation
[0]);
1072 dbg_printf("0x%08lx", rec
->ExceptionCode
);
1075 if (rec
->ExceptionFlags
& EH_STACK_INVALID
)
1076 dbg_printf(", invalid program stack");
1081 dbg_printf(" in %ld-bit code (%s)",
1082 dbg_curr_process
->be_cpu
->pointer_size
* 8,
1083 memory_offset_to_string(hexbuf
, addr
.Offset
, 0));
1086 dbg_printf(" in vm86 code (%04x:%04x)", addr
.Segment
, (unsigned) addr
.Offset
);
1089 dbg_printf(" in 16-bit code (%04x:%04x)", addr
.Segment
, (unsigned) addr
.Offset
);
1092 dbg_printf(" in segmented 32-bit code (%04x:%08x)", addr
.Segment
, (unsigned) addr
.Offset
);
1094 default: dbg_printf(" bad address");