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
++]);
121 IMAGEHLP_MODULEW64 mi
;
122 struct dhext_module_information ext_module_info
;
128 struct info_module
*modules
;
133 static const char* get_module_type(const struct info_module
* im
, BOOL is_embedded
)
135 switch (im
->ext_module_info
.type
)
137 case DMT_ELF
: return "ELF";
138 case DMT_MACHO
: return "Mach-O";
139 case DMT_PE
: return !is_embedded
&& im
->ext_module_info
.is_wine_builtin
? "PE-Wine" : "PE";
140 default: return "----";
144 static const char* get_symtype_str(const struct info_module
* im
)
146 switch (im
->mi
.SymType
)
149 case SymNone
: return "--none--";
150 case SymCoff
: return "COFF";
151 case SymCv
: return "CodeView";
152 case SymPdb
: return "PDB";
153 case SymExport
: return "Export";
154 case SymDeferred
: return "Deferred";
155 case SymSym
: return "Sym";
157 if (im
->ext_module_info
.debug_format_bitmask
)
161 if (im
->ext_module_info
.debug_format_bitmask
& DHEXT_FORMAT_STABS
) strcpy(tmp
, "stabs");
162 if (im
->ext_module_info
.debug_format_bitmask
& (DHEXT_FORMAT_DWARF2
| DHEXT_FORMAT_DWARF3
| DHEXT_FORMAT_DWARF4
| DHEXT_FORMAT_DWARF5
))
164 if (tmp
[0]) strcat(tmp
, ", ");
165 strcat(tmp
, "Dwarf");
166 if (im
->ext_module_info
.debug_format_bitmask
& DHEXT_FORMAT_DWARF2
) strcat(tmp
, "-2");
167 if (im
->ext_module_info
.debug_format_bitmask
& DHEXT_FORMAT_DWARF3
) strcat(tmp
, "-3");
168 if (im
->ext_module_info
.debug_format_bitmask
& DHEXT_FORMAT_DWARF4
) strcat(tmp
, "-4");
169 if (im
->ext_module_info
.debug_format_bitmask
& DHEXT_FORMAT_DWARF5
) strcat(tmp
, "-5");
177 static const char* get_machine_str(DWORD machine
)
182 case IMAGE_FILE_MACHINE_AMD64
: return "x86_64";
183 case IMAGE_FILE_MACHINE_I386
: return "i386";
184 case IMAGE_FILE_MACHINE_ARM64
: return "arm64";
185 case IMAGE_FILE_MACHINE_ARM
:
186 case IMAGE_FILE_MACHINE_ARMNT
: return "arm";
187 default: sprintf(tmp
, "<%lx>", machine
); return tmp
;
191 static void module_print_info(const struct info_module
*module
, BOOL is_embedded
, BOOL multi_machine
)
194 snprintf(buffer
, sizeof(buffer
), "%s%s%s",
195 is_embedded
? " \\-" : "",
196 get_module_type(module
, is_embedded
),
197 module
->ext_module_info
.has_file_image
? "" : "^");
200 dbg_printf("%-8s%16I64x-%16I64x %-16s%-16s%s\n",
202 module
->mi
.BaseOfImage
,
203 module
->mi
.BaseOfImage
+ module
->mi
.ImageSize
,
204 get_machine_str(module
->mi
.MachineType
),
205 is_embedded
? "\\" : get_symtype_str(module
), module
->name
);
207 dbg_printf("%-8s%*I64x-%*I64x %-16s%s\n",
209 ADDRWIDTH
, module
->mi
.BaseOfImage
,
210 ADDRWIDTH
, module
->mi
.BaseOfImage
+ module
->mi
.ImageSize
,
211 is_embedded
? "\\" : get_symtype_str(module
), module
->name
);
214 static int __cdecl
module_compare(const void* p1
, const void* p2
)
216 struct info_module
*left
= (struct info_module
*)p1
;
217 struct info_module
*right
= (struct info_module
*)p2
;
218 LONGLONG val
= left
->mi
.BaseOfImage
- right
->mi
.BaseOfImage
;
220 if (val
< 0) return -1;
221 else if (val
> 0) return 1;
225 static inline BOOL
module_is_container(const struct info_module
*wmod_cntnr
,
226 const struct info_module
*wmod_child
)
228 return (wmod_cntnr
->ext_module_info
.type
== DMT_ELF
|| wmod_cntnr
->ext_module_info
.type
== DMT_MACHO
) &&
229 (wmod_child
->ext_module_info
.type
== DMT_PE
) &&
230 wmod_cntnr
->mi
.BaseOfImage
<= wmod_child
->mi
.BaseOfImage
&&
231 wmod_cntnr
->mi
.BaseOfImage
+ wmod_cntnr
->mi
.ImageSize
>=
232 wmod_child
->mi
.BaseOfImage
+ wmod_child
->mi
.ImageSize
;
235 static BOOL CALLBACK
info_mod_cb(PCSTR mod_name
, DWORD64 base
, PVOID ctx
)
237 struct info_modules
*im
= ctx
;
239 if (im
->num_used
+ 1 > im
->num_alloc
)
241 struct info_module
* new = realloc(im
->modules
, (im
->num_alloc
+ 16) * sizeof(*im
->modules
));
242 if (!new) return FALSE
; /* stop enumeration in case of OOM */
246 im
->modules
[im
->num_used
].mi
.SizeOfStruct
= sizeof(im
->modules
[im
->num_used
].mi
);
247 if (SymGetModuleInfoW64(dbg_curr_process
->handle
, base
, &im
->modules
[im
->num_used
].mi
) &&
248 wine_get_module_information(dbg_curr_process
->handle
, base
, &im
->modules
[im
->num_used
].ext_module_info
,
249 sizeof(im
->modules
[im
->num_used
].ext_module_info
)))
251 const int dst_len
= sizeof(im
->modules
[im
->num_used
].name
);
252 lstrcpynA(im
->modules
[im
->num_used
].name
, mod_name
, dst_len
- 1);
253 im
->modules
[im
->num_used
].name
[dst_len
- 1] = 0;
259 /***********************************************************************
262 * Display information about a given module (DLL or EXE), or about all modules
264 void info_win32_module(DWORD64 base
, BOOL multi_machine
)
266 struct info_modules im
;
267 UINT i
, j
, num_printed
= 0;
270 BOOL has_missing_filename
= FALSE
;
272 if (!dbg_curr_process
)
274 dbg_printf("Cannot get info on module while no process is loaded\n");
279 im
.num_alloc
= im
.num_used
= 0;
281 /* this is a wine specific options to return also ELF modules in the
284 opt
= SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES
, TRUE
);
285 SymEnumerateModules64(dbg_curr_process
->handle
, info_mod_cb
, &im
);
286 SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES
, opt
);
288 if (!im
.num_used
) return;
290 /* main module is the first PE module in enumeration */
291 for (i
= 0; i
< im
.num_used
; i
++)
292 if (im
.modules
[i
].ext_module_info
.type
== DMT_PE
)
294 machine
= im
.modules
[i
].mi
.MachineType
;
297 if (i
== im
.num_used
) machine
= IMAGE_FILE_MACHINE_UNKNOWN
;
298 qsort(im
.modules
, im
.num_used
, sizeof(im
.modules
[0]), module_compare
);
301 dbg_printf("%-8s%-40s%-16s%-16sName (%d modules)\n", "Module", "Address", "Machine", "Debug info", im
.num_used
);
304 unsigned same_machine
= 0;
305 for (i
= 0; i
< im
.num_used
; i
++)
306 if (machine
== im
.modules
[i
].mi
.MachineType
) same_machine
++;
307 dbg_printf("%-8s%-*s%-16sName (%d modules",
308 "Module", ADDRWIDTH
== 16 ? 40 : 24, "Address", "Debug info", same_machine
);
309 if (same_machine
!= im
.num_used
)
310 dbg_printf(", %u for wow64 not listed", im
.num_used
- same_machine
);
314 for (i
= 0; i
< im
.num_used
; i
++)
317 (base
< im
.modules
[i
].mi
.BaseOfImage
|| base
>= im
.modules
[i
].mi
.BaseOfImage
+ im
.modules
[i
].mi
.ImageSize
))
319 if (!multi_machine
&& machine
!= im
.modules
[i
].mi
.MachineType
) continue;
320 if (!im
.modules
[i
].ext_module_info
.has_file_image
) has_missing_filename
= TRUE
;
321 if (im
.modules
[i
].ext_module_info
.type
== DMT_ELF
|| im
.modules
[i
].ext_module_info
.type
== DMT_MACHO
)
323 module_print_info(&im
.modules
[i
], FALSE
, multi_machine
);
324 /* print all modules embedded in this one */
325 for (j
= 0; j
< im
.num_used
; j
++)
327 if (module_is_container(&im
.modules
[i
], &im
.modules
[j
]))
328 module_print_info(&im
.modules
[j
], TRUE
, multi_machine
);
333 /* check module is not embedded in another module */
334 for (j
= 0; j
< im
.num_used
; j
++)
336 if (module_is_container(&im
.modules
[j
], &im
.modules
[i
]))
339 if (j
< im
.num_used
) continue;
340 module_print_info(&im
.modules
[i
], FALSE
, multi_machine
);
346 if (base
&& !num_printed
)
347 dbg_printf("'0x%0*I64x' is not a valid module address\n", ADDRWIDTH
, base
);
348 if (has_missing_filename
)
349 dbg_printf("^ denotes modules for which image file couldn't be found\n");
359 static void class_walker(HWND hWnd
, struct class_walker
* cw
)
366 if (!GetClassNameA(hWnd
, clsName
, sizeof(clsName
)))
368 if ((atom
= FindAtomA(clsName
)) == 0)
371 for (i
= 0; i
< cw
->used
; i
++)
373 if (cw
->table
[i
] == atom
)
378 if (cw
->used
>= cw
->alloc
)
380 ATOM
* new = realloc(cw
->table
, (cw
->alloc
+ 16) * sizeof(ATOM
));
385 cw
->table
[cw
->used
++] = atom
;
386 info_win32_class(hWnd
, clsName
);
390 if ((child
= GetWindow(hWnd
, GW_CHILD
)) != 0)
391 class_walker(child
, cw
);
392 } while ((hWnd
= GetWindow(hWnd
, GW_HWNDNEXT
)) != 0);
395 void info_win32_class(HWND hWnd
, const char* name
)
398 HINSTANCE hInst
= hWnd
? (HINSTANCE
)GetWindowLongPtrW(hWnd
, GWLP_HINSTANCE
) : 0;
402 struct class_walker cw
;
405 cw
.used
= cw
.alloc
= 0;
406 class_walker(GetDesktopWindow(), &cw
);
411 if (!GetClassInfoExA(hInst
, name
, &wca
))
413 dbg_printf("Cannot find class '%s'\n", name
);
417 dbg_printf("Class '%s':\n", name
);
418 dbg_printf("style=0x%08x wndProc=%p\n"
419 "inst=%p icon=%p cursor=%p bkgnd=%p\n"
420 "clsExtra=%d winExtra=%d\n",
421 wca
.style
, wca
.lpfnWndProc
, wca
.hInstance
,
422 wca
.hIcon
, wca
.hCursor
, wca
.hbrBackground
,
423 wca
.cbClsExtra
, wca
.cbWndExtra
);
425 if (hWnd
&& wca
.cbClsExtra
)
430 dbg_printf("Extra bytes:");
431 for (i
= 0; i
< wca
.cbClsExtra
/ 2; i
++)
433 w
= GetClassWord(hWnd
, i
* 2);
434 /* FIXME: depends on i386 endian-ity */
435 dbg_printf(" %02x %02x", HIBYTE(w
), LOBYTE(w
));
441 * + print #windows (or even list of windows...)
442 * + print extra bytes => this requires a window handle on this very class...
446 static void info_window(HWND hWnd
, int indent
)
454 if (!GetClassNameA(hWnd
, clsName
, sizeof(clsName
)))
455 strcpy(clsName
, "-- Unknown --");
456 if (!GetWindowTextA(hWnd
, wndName
, sizeof(wndName
)))
457 strcpy(wndName
, "-- Empty --");
459 dbg_printf("%*s%08Ix%*s %-17.17s %08lx %0*Ix %08lx %.14s\n",
460 indent
, "", (DWORD_PTR
)hWnd
, 12 - indent
, "",
461 clsName
, GetWindowLongW(hWnd
, GWL_STYLE
),
462 ADDRWIDTH
, (ULONG_PTR
)GetWindowLongPtrW(hWnd
, GWLP_WNDPROC
),
463 GetWindowThreadProcessId(hWnd
, NULL
), wndName
);
465 if ((child
= GetWindow(hWnd
, GW_CHILD
)) != 0)
466 info_window(child
, indent
+ 1);
467 } while ((hWnd
= GetWindow(hWnd
, GW_HWNDNEXT
)) != 0);
470 void info_win32_window(HWND hWnd
, BOOL detailed
)
478 if (!IsWindow(hWnd
)) hWnd
= GetDesktopWindow();
482 dbg_printf("%-20.20s %-17.17s %-8.8s %-*.*s %-8.8s %s\n",
483 "Window handle", "Class Name", "Style",
484 ADDRWIDTH
, ADDRWIDTH
, "WndProc", "Thread", "Text");
485 info_window(hWnd
, 0);
489 if (!GetClassNameA(hWnd
, clsName
, sizeof(clsName
)))
490 strcpy(clsName
, "-- Unknown --");
491 if (!GetWindowTextA(hWnd
, wndName
, sizeof(wndName
)))
492 strcpy(wndName
, "-- Empty --");
493 if (!GetClientRect(hWnd
, &clientRect
) ||
494 !MapWindowPoints(hWnd
, 0, (LPPOINT
) &clientRect
, 2))
495 SetRectEmpty(&clientRect
);
496 if (!GetWindowRect(hWnd
, &windowRect
))
497 SetRectEmpty(&windowRect
);
499 /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
500 dbg_printf("next=%p child=%p parent=%p owner=%p class='%s'\n"
501 "inst=%p active=%p idmenu=%08Ix\n"
502 "style=0x%08lx exstyle=0x%08lx wndproc=%p text='%s'\n"
503 "client=%ld,%ld-%ld,%ld window=%ld,%ld-%ld,%ld sysmenu=%p\n",
504 GetWindow(hWnd
, GW_HWNDNEXT
),
505 GetWindow(hWnd
, GW_CHILD
),
507 GetWindow(hWnd
, GW_OWNER
),
509 (HINSTANCE
)GetWindowLongPtrW(hWnd
, GWLP_HINSTANCE
),
510 GetLastActivePopup(hWnd
),
511 (ULONG_PTR
)GetWindowLongPtrW(hWnd
, GWLP_ID
),
512 GetWindowLongW(hWnd
, GWL_STYLE
),
513 GetWindowLongW(hWnd
, GWL_EXSTYLE
),
514 (void*)GetWindowLongPtrW(hWnd
, GWLP_WNDPROC
),
516 clientRect
.left
, clientRect
.top
, clientRect
.right
, clientRect
.bottom
,
517 windowRect
.left
, windowRect
.top
, windowRect
.right
, windowRect
.bottom
,
518 GetSystemMenu(hWnd
, FALSE
));
520 if (GetClassLongW(hWnd
, GCL_CBWNDEXTRA
))
524 dbg_printf("Extra bytes:");
525 for (i
= 0; i
< GetClassLongW(hWnd
, GCL_CBWNDEXTRA
) / 2; i
++)
527 w
= GetWindowWord(hWnd
, i
* 2);
528 /* FIXME: depends on i386 endian-ity */
529 dbg_printf(" %02x %02x", HIBYTE(w
), LOBYTE(w
));
536 struct dump_proc_entry
539 unsigned children
; /* index in dump_proc.entries of first child */
540 unsigned sibling
; /* index in dump_proc.entries of next sibling */
545 struct dump_proc_entry
*entries
;
550 static unsigned get_parent(const struct dump_proc
* dp
, unsigned idx
)
554 for (i
= 0; i
< dp
->count
; i
++)
556 if (i
!= idx
&& dp
->entries
[i
].proc
.th32ProcessID
== dp
->entries
[idx
].proc
.th32ParentProcessID
)
562 static void dump_proc_info(const struct dump_proc
* dp
, unsigned idx
, unsigned depth
)
564 struct dump_proc_entry
* dpe
;
566 for ( ; idx
!= -1; idx
= dp
->entries
[idx
].sibling
)
568 assert(idx
< dp
->count
);
569 dpe
= &dp
->entries
[idx
];
570 if (dbg_curr_process
&& dpe
->proc
.th32ProcessID
== dbg_curr_process
->pid
)
572 else if (dpe
->proc
.th32ProcessID
== GetCurrentProcessId())
576 dbg_printf("%c%08lx %-8ld ", info
, dpe
->proc
.th32ProcessID
, dpe
->proc
.cntThreads
);
580 for (i
= 3 * (depth
- 1); i
> 0; i
--) dbg_printf(" ");
583 dbg_printf("'%s'\n", dpe
->proc
.szExeFile
);
584 dump_proc_info(dp
, dpe
->children
, depth
+ 1);
588 void info_win32_processes(void)
590 HANDLE snap
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
591 if (snap
!= INVALID_HANDLE_VALUE
)
594 unsigned i
, first
= -1;
599 dp
.entries
= malloc(sizeof(*dp
.entries
) * dp
.alloc
);
605 dp
.entries
[dp
.count
].proc
.dwSize
= sizeof(dp
.entries
[dp
.count
].proc
);
606 ok
= Process32First(snap
, &dp
.entries
[dp
.count
].proc
);
608 /* fetch all process information into dp */
611 dp
.entries
[dp
.count
++].children
= -1;
612 if (dp
.count
>= dp
.alloc
)
614 struct dump_proc_entry
* new = realloc(dp
.entries
, sizeof(*dp
.entries
) * (dp
.alloc
* 2));
624 dp
.entries
[dp
.count
].proc
.dwSize
= sizeof(dp
.entries
[dp
.count
].proc
);
625 ok
= Process32Next(snap
, &dp
.entries
[dp
.count
].proc
);
628 /* chain the siblings wrt. their parent */
629 for (i
= 0; i
< dp
.count
; i
++)
631 unsigned parent
= get_parent(&dp
, i
);
632 unsigned *chain
= parent
== -1 ? &first
: &dp
.entries
[parent
].children
;
633 dp
.entries
[i
].sibling
= *chain
;
636 dbg_printf(" %-8.8s %-8.8s %s (all id:s are in hex)\n", "pid", "threads", "executable");
637 dump_proc_info(&dp
, first
, 0);
642 static BOOL
get_process_name(DWORD pid
, PROCESSENTRY32W
* entry
)
645 HANDLE snap
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
647 if (snap
!= INVALID_HANDLE_VALUE
)
649 entry
->dwSize
= sizeof(*entry
);
650 if (Process32FirstW(snap
, entry
))
651 while (!(ret
= (entry
->th32ProcessID
== pid
)) &&
652 Process32NextW(snap
, entry
));
658 WCHAR
* fetch_thread_description(DWORD tid
)
660 static HRESULT (WINAPI
*my_GetThreadDescription
)(HANDLE
, PWSTR
*) = NULL
;
661 static BOOL resolved
= FALSE
;
667 HMODULE kernelbase
= GetModuleHandleA("kernelbase.dll");
669 my_GetThreadDescription
= (void *)GetProcAddress(kernelbase
, "GetThreadDescription");
673 if (!my_GetThreadDescription
)
676 h
= OpenThread(THREAD_QUERY_LIMITED_INFORMATION
, FALSE
, tid
);
680 my_GetThreadDescription(h
, &desc
);
683 if (desc
&& desc
[0] == '\0')
691 void info_win32_threads(void)
693 HANDLE snap
= CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD
, 0);
694 if (snap
!= INVALID_HANDLE_VALUE
)
698 DWORD lastProcessId
= 0;
699 struct dbg_process
* p
= NULL
;
700 struct dbg_thread
* t
= NULL
;
703 entry
.dwSize
= sizeof(entry
);
704 ok
= Thread32First(snap
, &entry
);
706 dbg_printf("%-8.8s %-8.8s %s %s (all IDs are in hex)\n",
707 "process", "tid", "prio", "name");
710 if (entry
.th32OwnerProcessID
!= GetCurrentProcessId())
712 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
713 * listed sequentially, which is not specified in the doc (Wine's implementation
716 if (entry
.th32OwnerProcessID
!= lastProcessId
)
718 PROCESSENTRY32W pcs_entry
;
719 const WCHAR
* exename
;
721 p
= dbg_get_process(entry
.th32OwnerProcessID
);
723 exename
= p
->imageName
;
724 else if (get_process_name(entry
.th32OwnerProcessID
, &pcs_entry
))
725 exename
= pcs_entry
.szExeFile
;
729 dbg_printf("%08lx%s %ls\n",
730 entry
.th32OwnerProcessID
, p
? " (D)" : "", exename
);
731 lastProcessId
= entry
.th32OwnerProcessID
;
733 dbg_printf("\t%08lx %4ld%s ",
734 entry
.th32ThreadID
, entry
.tpBasePri
,
735 (entry
.th32ThreadID
== dbg_curr_tid
) ? " <==" : " ");
737 if ((description
= fetch_thread_description(entry
.th32ThreadID
)))
739 dbg_printf("%ls\n", description
);
740 LocalFree(description
);
744 t
= dbg_get_thread(p
, entry
.th32ThreadID
);
745 dbg_printf("%s\n", t
? t
->name
: "");
748 ok
= Thread32Next(snap
, &entry
);
755 /***********************************************************************
756 * info_win32_frame_exceptions
758 * Get info on the exception frames of a given thread.
760 void info_win32_frame_exceptions(DWORD tid
)
762 struct dbg_thread
* thread
;
765 if (!dbg_curr_process
|| !dbg_curr_thread
)
767 dbg_printf("Cannot get info on exceptions while no process is loaded\n");
771 dbg_printf("Exception frames:\n");
773 if (tid
== dbg_curr_tid
) thread
= dbg_curr_thread
;
776 thread
= dbg_get_thread(dbg_curr_process
, tid
);
780 dbg_printf("Unknown thread id (%04lx) in current process\n", tid
);
783 if (SuspendThread(thread
->handle
) == -1)
785 dbg_printf("Can't suspend thread id (%04lx)\n", tid
);
790 if (!dbg_read_memory(thread
->teb
, &next_frame
, sizeof(next_frame
)))
792 dbg_printf("Can't read TEB:except_frame\n");
796 while (next_frame
!= (void*)-1)
798 EXCEPTION_REGISTRATION_RECORD frame
;
800 dbg_printf("%p: ", next_frame
);
801 if (!dbg_read_memory(next_frame
, &frame
, sizeof(frame
)))
803 dbg_printf("Invalid frame address\n");
806 dbg_printf("prev=%p handler=%p\n", frame
.Prev
, frame
.Handler
);
807 next_frame
= frame
.Prev
;
810 if (tid
!= dbg_curr_tid
) ResumeThread(thread
->handle
);
813 void info_win32_segments(DWORD start
, int length
)
819 if (length
== -1) length
= (8192 - start
);
821 for (i
= start
; i
< start
+ length
; i
++)
823 if (!dbg_curr_process
->process_io
->get_selector(dbg_curr_thread
->handle
, (i
<< 3) | 7, &le
))
826 if (le
.HighWord
.Bits
.Type
& 0x08)
828 flags
[0] = (le
.HighWord
.Bits
.Type
& 0x2) ? 'r' : '-';
835 flags
[1] = (le
.HighWord
.Bits
.Type
& 0x2) ? 'w' : '-';
838 dbg_printf("%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
840 (le
.HighWord
.Bits
.BaseHi
<< 24) +
841 (le
.HighWord
.Bits
.BaseMid
<< 16) + le
.BaseLow
,
842 ((le
.HighWord
.Bits
.LimitHi
<< 8) + le
.LimitLow
) <<
843 (le
.HighWord
.Bits
.Granularity
? 12 : 0),
844 le
.HighWord
.Bits
.Default_Big
? 32 : 16,
845 flags
[0], flags
[1], flags
[2]);
849 void info_win32_virtual(DWORD pid
)
851 MEMORY_BASIC_INFORMATION mbi
;
858 if (pid
== dbg_curr_pid
)
860 if (dbg_curr_process
== NULL
)
862 dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
865 hProc
= dbg_curr_process
->handle
;
869 hProc
= OpenProcess(PROCESS_QUERY_INFORMATION
| PROCESS_VM_READ
, FALSE
, pid
);
872 dbg_printf("Cannot open process <%04lx>\n", pid
);
877 dbg_printf("Address End State Type RWX\n");
879 while (VirtualQueryEx(hProc
, addr
, &mbi
, sizeof(mbi
)) >= sizeof(mbi
))
883 case MEM_COMMIT
: state
= "commit "; break;
884 case MEM_FREE
: state
= "free "; break;
885 case MEM_RESERVE
: state
= "reserve"; break;
886 default: state
= "??? "; break;
888 if (mbi
.State
!= MEM_FREE
)
892 case MEM_IMAGE
: type
= "image "; break;
893 case MEM_MAPPED
: type
= "mapped "; break;
894 case MEM_PRIVATE
: type
= "private"; break;
895 case 0: type
= " "; break;
896 default: type
= "??? "; break;
898 memset(prot
, ' ' , sizeof(prot
) - 1);
899 prot
[sizeof(prot
) - 1] = '\0';
900 if (mbi
.AllocationProtect
& (PAGE_READONLY
|PAGE_READWRITE
|PAGE_EXECUTE_READ
|PAGE_EXECUTE_READWRITE
|PAGE_WRITECOPY
|PAGE_EXECUTE_WRITECOPY
))
902 if (mbi
.AllocationProtect
& (PAGE_READWRITE
|PAGE_EXECUTE_READWRITE
))
904 if (mbi
.AllocationProtect
& (PAGE_WRITECOPY
|PAGE_EXECUTE_WRITECOPY
))
906 if (mbi
.AllocationProtect
& (PAGE_EXECUTE
|PAGE_EXECUTE_READ
|PAGE_EXECUTE_READWRITE
|PAGE_EXECUTE_WRITECOPY
))
914 dbg_printf("%0*Ix %0*Ix %s %s %s\n",
915 ADDRWIDTH
, (DWORD_PTR
)addr
, ADDRWIDTH
, (DWORD_PTR
)addr
+ mbi
.RegionSize
- 1, state
, type
, prot
);
916 if (addr
+ mbi
.RegionSize
< addr
) /* wrap around ? */
918 addr
+= mbi
.RegionSize
;
920 if (pid
!= dbg_curr_pid
) CloseHandle(hProc
);
923 void info_wine_dbg_channel(BOOL turn_on
, const char* cls
, const char* name
)
925 struct dbg_lvalue lvalue
;
926 struct __wine_debug_channel channel
;
932 if (!dbg_curr_process
|| !dbg_curr_thread
)
934 dbg_printf("Cannot set/get debug channels while no process is loaded\n");
938 if (symbol_get_lvalue("debug_options", -1, &lvalue
, FALSE
) != sglv_found
)
942 addr
= memory_to_linear_addr(&lvalue
.addr
);
945 else if (!strcmp(cls
, "fixme")) mask
= (1 << __WINE_DBCL_FIXME
);
946 else if (!strcmp(cls
, "err")) mask
= (1 << __WINE_DBCL_ERR
);
947 else if (!strcmp(cls
, "warn")) mask
= (1 << __WINE_DBCL_WARN
);
948 else if (!strcmp(cls
, "trace")) mask
= (1 << __WINE_DBCL_TRACE
);
951 dbg_printf("Unknown debug class %s\n", cls
);
955 bAll
= !strcmp("all", name
);
956 while (addr
&& dbg_read_memory(addr
, &channel
, sizeof(channel
)))
958 if (!channel
.name
[0]) break;
959 if (bAll
|| !strcmp( channel
.name
, name
))
961 if (turn_on
) channel
.flags
|= mask
;
962 else channel
.flags
&= ~mask
;
963 if (dbg_write_memory(addr
, &channel
, sizeof(channel
))) done
++;
965 addr
= (struct __wine_debug_channel
*)addr
+ 1;
967 if (!done
) dbg_printf("Unable to find debug channel %s\n", name
);
968 else WINE_TRACE("Changed %d channel instances\n", done
);
971 void info_win32_exception(void)
973 const EXCEPTION_RECORD
* rec
;
975 char hexbuf
[MAX_OFFSET_TO_STR_LEN
];
977 if (!dbg_curr_thread
->in_exception
)
979 dbg_printf("Thread isn't in an exception\n");
982 rec
= &dbg_curr_thread
->excpt_record
;
983 memory_get_current_pc(&addr
);
985 /* print some infos */
987 dbg_curr_thread
->first_chance
? "First chance exception" : "Unhandled exception");
988 switch (rec
->ExceptionCode
)
990 case EXCEPTION_BREAKPOINT
:
991 dbg_printf("breakpoint");
993 case EXCEPTION_SINGLE_STEP
:
994 dbg_printf("single step");
996 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
997 dbg_printf("divide by zero");
999 case EXCEPTION_INT_OVERFLOW
:
1000 dbg_printf("overflow");
1002 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED
:
1003 dbg_printf("array bounds");
1005 case EXCEPTION_ILLEGAL_INSTRUCTION
:
1006 dbg_printf("illegal instruction");
1008 case EXCEPTION_STACK_OVERFLOW
:
1009 dbg_printf("stack overflow");
1011 case EXCEPTION_PRIV_INSTRUCTION
:
1012 dbg_printf("privileged instruction");
1014 case EXCEPTION_ACCESS_VIOLATION
:
1015 if (rec
->NumberParameters
== 2)
1016 dbg_printf("page fault on %s access to 0x%0*Ix",
1017 rec
->ExceptionInformation
[0] == EXCEPTION_WRITE_FAULT
? "write" :
1018 rec
->ExceptionInformation
[0] == EXCEPTION_EXECUTE_FAULT
? "execute" : "read",
1019 ADDRWIDTH
, rec
->ExceptionInformation
[1]);
1021 dbg_printf("page fault");
1023 case EXCEPTION_DATATYPE_MISALIGNMENT
:
1024 dbg_printf("Alignment");
1029 case CONTROL_C_EXIT
:
1032 case STATUS_POSSIBLE_DEADLOCK
:
1036 recaddr
.Mode
= AddrModeFlat
;
1037 recaddr
.Offset
= rec
->ExceptionInformation
[0];
1039 dbg_printf("wait failed on critical section ");
1040 print_address(&recaddr
, FALSE
);
1043 case EXCEPTION_WINE_STUB
:
1045 char dll
[64], name
[256];
1046 memory_get_string(dbg_curr_process
,
1047 (void*)rec
->ExceptionInformation
[0], TRUE
, FALSE
,
1049 if (HIWORD(rec
->ExceptionInformation
[1]))
1050 memory_get_string(dbg_curr_process
,
1051 (void*)rec
->ExceptionInformation
[1], TRUE
, FALSE
,
1052 name
, sizeof(name
));
1054 sprintf( name
, "%Id", rec
->ExceptionInformation
[1] );
1055 dbg_printf("unimplemented function %s.%s called", dll
, name
);
1058 case EXCEPTION_WINE_ASSERTION
:
1059 dbg_printf("assertion failed");
1061 case EXCEPTION_FLT_DENORMAL_OPERAND
:
1062 dbg_printf("denormal float operand");
1064 case EXCEPTION_FLT_DIVIDE_BY_ZERO
:
1065 dbg_printf("divide by zero");
1067 case EXCEPTION_FLT_INEXACT_RESULT
:
1068 dbg_printf("inexact float result");
1070 case EXCEPTION_FLT_INVALID_OPERATION
:
1071 dbg_printf("invalid float operation");
1073 case EXCEPTION_FLT_OVERFLOW
:
1074 dbg_printf("floating point overflow");
1076 case EXCEPTION_FLT_UNDERFLOW
:
1077 dbg_printf("floating point underflow");
1079 case EXCEPTION_FLT_STACK_CHECK
:
1080 dbg_printf("floating point stack check");
1082 case EXCEPTION_WINE_CXX_EXCEPTION
:
1083 if(rec
->NumberParameters
== 3 && rec
->ExceptionInformation
[0] == EXCEPTION_WINE_CXX_FRAME_MAGIC
)
1084 dbg_printf("C++ exception(object = 0x%0*Ix, type = 0x%0*Ix)",
1085 ADDRWIDTH
, rec
->ExceptionInformation
[1], ADDRWIDTH
, rec
->ExceptionInformation
[2]);
1086 else if(rec
->NumberParameters
== 4 && rec
->ExceptionInformation
[0] == EXCEPTION_WINE_CXX_FRAME_MAGIC
)
1087 dbg_printf("C++ exception(object = %p, type = %p, base = %p)",
1088 (void*)rec
->ExceptionInformation
[1], (void*)rec
->ExceptionInformation
[2],
1089 (void*)rec
->ExceptionInformation
[3]);
1091 dbg_printf("C++ exception with strange parameter count %ld or magic 0x%0*Ix",
1092 rec
->NumberParameters
, ADDRWIDTH
, rec
->ExceptionInformation
[0]);
1095 dbg_printf("0x%08lx", rec
->ExceptionCode
);
1098 if (rec
->ExceptionFlags
& EXCEPTION_STACK_INVALID
)
1099 dbg_printf(", invalid program stack");
1104 dbg_printf(" in %s%ld-bit code (%s)",
1105 dbg_curr_process
->is_wow64
? "wow64 " : "",
1106 dbg_curr_process
->be_cpu
->pointer_size
* 8,
1107 memory_offset_to_string(hexbuf
, addr
.Offset
, 0));
1110 dbg_printf(" in vm86 code (%04x:%04x)", addr
.Segment
, (unsigned) addr
.Offset
);
1113 dbg_printf(" in 16-bit code (%04x:%04x)", addr
.Segment
, (unsigned) addr
.Offset
);
1116 dbg_printf(" in segmented 32-bit code (%04x:%08x)", addr
.Segment
, (unsigned) addr
.Offset
);
1118 default: dbg_printf(" bad address");
1133 { 0, VER_PLATFORM_WIN32s
, 2, 0, "2.0" },
1134 { 0, VER_PLATFORM_WIN32s
, 3, 0, "3.0" },
1135 { 0, VER_PLATFORM_WIN32s
, 3, 10, "3.1" },
1136 { 0, VER_PLATFORM_WIN32_WINDOWS
, 4, 0, "95" },
1137 { 0, VER_PLATFORM_WIN32_WINDOWS
, 4, 10, "98" },
1138 { 0, VER_PLATFORM_WIN32_WINDOWS
, 4, 90, "ME" },
1139 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 3, 51, "NT 3.51" },
1140 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 4, 0, "NT 4.0" },
1141 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 5, 0, "2000" },
1142 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 5, 1, "XP" },
1143 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 5, 2, "XP" },
1144 { VER_NT_SERVER
, VER_PLATFORM_WIN32_NT
, 5, 2, "Server 2003" },
1145 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 6, 0, "Vista" },
1146 { VER_NT_SERVER
, VER_PLATFORM_WIN32_NT
, 6, 0, "Server 2008" },
1147 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 6, 1, "7" },
1148 { VER_NT_SERVER
, VER_PLATFORM_WIN32_NT
, 6, 1, "Server 2008 R2" },
1149 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 6, 2, "8" },
1150 { VER_NT_SERVER
, VER_PLATFORM_WIN32_NT
, 6, 2, "Server 2012" },
1151 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 6, 3, "8.1" },
1152 { VER_NT_SERVER
, VER_PLATFORM_WIN32_NT
, 6, 3, "Server 2012 R2" },
1153 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 10, 0, "10" },
1156 static const char *get_windows_version(void)
1158 RTL_OSVERSIONINFOEXW info
= { sizeof(RTL_OSVERSIONINFOEXW
) };
1159 static char str
[64];
1162 RtlGetVersion( &info
);
1164 for (i
= 0; i
< ARRAY_SIZE(version_table
); i
++)
1166 if (version_table
[i
].type
== info
.wProductType
&&
1167 version_table
[i
].platform
== info
.dwPlatformId
&&
1168 version_table
[i
].major
== info
.dwMajorVersion
&&
1169 version_table
[i
].minor
== info
.dwMinorVersion
)
1171 return version_table
[i
].str
;
1175 snprintf( str
, sizeof(str
), "%ld.%ld (%d)", info
.dwMajorVersion
,
1176 info
.dwMinorVersion
, info
.wProductType
);
1180 static BOOL
is_guest(USHORT native
, USHORT guest
)
1184 return native
!= guest
&& !RtlWow64IsWowGuestMachineSupported(guest
, &supported
) && supported
;
1187 void info_win32_system(void)
1189 USHORT current
, native
;
1192 const char *(CDECL
*wine_get_build_id
)(void);
1193 void (CDECL
*wine_get_host_version
)( const char **sysname
, const char **release
);
1195 static USHORT guest_machines
[] =
1197 IMAGE_FILE_MACHINE_I386
, IMAGE_FILE_MACHINE_ARM
, IMAGE_FILE_MACHINE_ARMNT
,
1200 wine_get_build_id
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
1201 wine_get_host_version
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
1203 RtlWow64GetProcessMachines( GetCurrentProcess(), ¤t
, &native
);
1205 dbg_printf( "System information:\n" );
1206 if (wine_get_build_id
) dbg_printf( " Wine build: %s\n", wine_get_build_id() );
1207 dbg_printf( " Platform: %s", get_machine_str(native
));
1208 for (count
= i
= 0; i
< ARRAY_SIZE(guest_machines
); i
++)
1210 if (is_guest(native
, guest_machines
[i
]))
1212 if (!count
++) dbg_printf(" (guest:");
1213 dbg_printf(" %s", get_machine_str(guest_machines
[i
]));
1216 dbg_printf("%s\n", count
? ")" : "");
1218 dbg_printf( " Version: Windows %s\n", get_windows_version() );
1219 if (wine_get_host_version
)
1221 const char *sysname
, *release
;
1222 wine_get_host_version( &sysname
, &release
);
1223 dbg_printf( " Host system: %s\n", sysname
);
1224 dbg_printf( " Host version: %s\n", release
);