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 qsort(im
.modules
, im
.num_used
, sizeof(im
.modules
[0]), module_compare
);
291 machine
= im
.modules
[0].mi
.MachineType
;
294 dbg_printf("%-8s%-40s%-16s%-16sName (%d modules)\n", "Module", "Address", "Machine", "Debug info", im
.num_used
);
297 unsigned same_machine
= 0;
298 for (i
= 0; i
< im
.num_used
; i
++)
299 if (machine
== im
.modules
[i
].mi
.MachineType
) same_machine
++;
300 dbg_printf("%-8s%-*s%-16sName (%d modules",
301 "Module", ADDRWIDTH
== 16 ? 40 : 24, "Address", "Debug info", same_machine
);
302 if (same_machine
!= im
.num_used
)
303 dbg_printf(", %u for wow64 not listed", im
.num_used
- same_machine
);
307 for (i
= 0; i
< im
.num_used
; i
++)
310 (base
< im
.modules
[i
].mi
.BaseOfImage
|| base
>= im
.modules
[i
].mi
.BaseOfImage
+ im
.modules
[i
].mi
.ImageSize
))
312 if (!multi_machine
&& machine
!= im
.modules
[i
].mi
.MachineType
) continue;
313 if (!im
.modules
[i
].ext_module_info
.has_file_image
) has_missing_filename
= TRUE
;
314 if (im
.modules
[i
].ext_module_info
.type
== DMT_ELF
|| im
.modules
[i
].ext_module_info
.type
== DMT_MACHO
)
316 module_print_info(&im
.modules
[i
], FALSE
, multi_machine
);
317 /* print all modules embedded in this one */
318 for (j
= 0; j
< im
.num_used
; j
++)
320 if (module_is_container(&im
.modules
[i
], &im
.modules
[j
]))
321 module_print_info(&im
.modules
[j
], TRUE
, multi_machine
);
326 /* check module is not embedded in another module */
327 for (j
= 0; j
< im
.num_used
; j
++)
329 if (module_is_container(&im
.modules
[j
], &im
.modules
[i
]))
332 if (j
< im
.num_used
) continue;
333 module_print_info(&im
.modules
[i
], FALSE
, multi_machine
);
339 if (base
&& !num_printed
)
340 dbg_printf("'0x%0*I64x' is not a valid module address\n", ADDRWIDTH
, base
);
341 if (has_missing_filename
)
342 dbg_printf("^ denotes modules for which image file couldn't be found\n");
352 static void class_walker(HWND hWnd
, struct class_walker
* cw
)
359 if (!GetClassNameA(hWnd
, clsName
, sizeof(clsName
)))
361 if ((atom
= FindAtomA(clsName
)) == 0)
364 for (i
= 0; i
< cw
->used
; i
++)
366 if (cw
->table
[i
] == atom
)
371 if (cw
->used
>= cw
->alloc
)
373 ATOM
* new = realloc(cw
->table
, (cw
->alloc
+ 16) * sizeof(ATOM
));
378 cw
->table
[cw
->used
++] = atom
;
379 info_win32_class(hWnd
, clsName
);
383 if ((child
= GetWindow(hWnd
, GW_CHILD
)) != 0)
384 class_walker(child
, cw
);
385 } while ((hWnd
= GetWindow(hWnd
, GW_HWNDNEXT
)) != 0);
388 void info_win32_class(HWND hWnd
, const char* name
)
391 HINSTANCE hInst
= hWnd
? (HINSTANCE
)GetWindowLongPtrW(hWnd
, GWLP_HINSTANCE
) : 0;
395 struct class_walker cw
;
398 cw
.used
= cw
.alloc
= 0;
399 class_walker(GetDesktopWindow(), &cw
);
404 if (!GetClassInfoExA(hInst
, name
, &wca
))
406 dbg_printf("Cannot find class '%s'\n", name
);
410 dbg_printf("Class '%s':\n", name
);
411 dbg_printf("style=0x%08x wndProc=%p\n"
412 "inst=%p icon=%p cursor=%p bkgnd=%p\n"
413 "clsExtra=%d winExtra=%d\n",
414 wca
.style
, wca
.lpfnWndProc
, wca
.hInstance
,
415 wca
.hIcon
, wca
.hCursor
, wca
.hbrBackground
,
416 wca
.cbClsExtra
, wca
.cbWndExtra
);
418 if (hWnd
&& wca
.cbClsExtra
)
423 dbg_printf("Extra bytes:");
424 for (i
= 0; i
< wca
.cbClsExtra
/ 2; i
++)
426 w
= GetClassWord(hWnd
, i
* 2);
427 /* FIXME: depends on i386 endian-ity */
428 dbg_printf(" %02x %02x", HIBYTE(w
), LOBYTE(w
));
434 * + print #windows (or even list of windows...)
435 * + print extra bytes => this requires a window handle on this very class...
439 static void info_window(HWND hWnd
, int indent
)
447 if (!GetClassNameA(hWnd
, clsName
, sizeof(clsName
)))
448 strcpy(clsName
, "-- Unknown --");
449 if (!GetWindowTextA(hWnd
, wndName
, sizeof(wndName
)))
450 strcpy(wndName
, "-- Empty --");
452 dbg_printf("%*s%08Ix%*s %-17.17s %08lx %0*Ix %08lx %.14s\n",
453 indent
, "", (DWORD_PTR
)hWnd
, 12 - indent
, "",
454 clsName
, GetWindowLongW(hWnd
, GWL_STYLE
),
455 ADDRWIDTH
, (ULONG_PTR
)GetWindowLongPtrW(hWnd
, GWLP_WNDPROC
),
456 GetWindowThreadProcessId(hWnd
, NULL
), wndName
);
458 if ((child
= GetWindow(hWnd
, GW_CHILD
)) != 0)
459 info_window(child
, indent
+ 1);
460 } while ((hWnd
= GetWindow(hWnd
, GW_HWNDNEXT
)) != 0);
463 void info_win32_window(HWND hWnd
, BOOL detailed
)
471 if (!IsWindow(hWnd
)) hWnd
= GetDesktopWindow();
475 dbg_printf("%-20.20s %-17.17s %-8.8s %-*.*s %-8.8s %s\n",
476 "Window handle", "Class Name", "Style",
477 ADDRWIDTH
, ADDRWIDTH
, "WndProc", "Thread", "Text");
478 info_window(hWnd
, 0);
482 if (!GetClassNameA(hWnd
, clsName
, sizeof(clsName
)))
483 strcpy(clsName
, "-- Unknown --");
484 if (!GetWindowTextA(hWnd
, wndName
, sizeof(wndName
)))
485 strcpy(wndName
, "-- Empty --");
486 if (!GetClientRect(hWnd
, &clientRect
) ||
487 !MapWindowPoints(hWnd
, 0, (LPPOINT
) &clientRect
, 2))
488 SetRectEmpty(&clientRect
);
489 if (!GetWindowRect(hWnd
, &windowRect
))
490 SetRectEmpty(&windowRect
);
492 /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
493 dbg_printf("next=%p child=%p parent=%p owner=%p class='%s'\n"
494 "inst=%p active=%p idmenu=%08Ix\n"
495 "style=0x%08lx exstyle=0x%08lx wndproc=%p text='%s'\n"
496 "client=%ld,%ld-%ld,%ld window=%ld,%ld-%ld,%ld sysmenu=%p\n",
497 GetWindow(hWnd
, GW_HWNDNEXT
),
498 GetWindow(hWnd
, GW_CHILD
),
500 GetWindow(hWnd
, GW_OWNER
),
502 (HINSTANCE
)GetWindowLongPtrW(hWnd
, GWLP_HINSTANCE
),
503 GetLastActivePopup(hWnd
),
504 (ULONG_PTR
)GetWindowLongPtrW(hWnd
, GWLP_ID
),
505 GetWindowLongW(hWnd
, GWL_STYLE
),
506 GetWindowLongW(hWnd
, GWL_EXSTYLE
),
507 (void*)GetWindowLongPtrW(hWnd
, GWLP_WNDPROC
),
509 clientRect
.left
, clientRect
.top
, clientRect
.right
, clientRect
.bottom
,
510 windowRect
.left
, windowRect
.top
, windowRect
.right
, windowRect
.bottom
,
511 GetSystemMenu(hWnd
, FALSE
));
513 if (GetClassLongW(hWnd
, GCL_CBWNDEXTRA
))
517 dbg_printf("Extra bytes:");
518 for (i
= 0; i
< GetClassLongW(hWnd
, GCL_CBWNDEXTRA
) / 2; i
++)
520 w
= GetWindowWord(hWnd
, i
* 2);
521 /* FIXME: depends on i386 endian-ity */
522 dbg_printf(" %02x %02x", HIBYTE(w
), LOBYTE(w
));
529 struct dump_proc_entry
532 unsigned children
; /* index in dump_proc.entries of first child */
533 unsigned sibling
; /* index in dump_proc.entries of next sibling */
538 struct dump_proc_entry
*entries
;
543 static unsigned get_parent(const struct dump_proc
* dp
, unsigned idx
)
547 for (i
= 0; i
< dp
->count
; i
++)
549 if (i
!= idx
&& dp
->entries
[i
].proc
.th32ProcessID
== dp
->entries
[idx
].proc
.th32ParentProcessID
)
555 static void dump_proc_info(const struct dump_proc
* dp
, unsigned idx
, unsigned depth
)
557 struct dump_proc_entry
* dpe
;
559 for ( ; idx
!= -1; idx
= dp
->entries
[idx
].sibling
)
561 assert(idx
< dp
->count
);
562 dpe
= &dp
->entries
[idx
];
563 if (dbg_curr_process
&& dpe
->proc
.th32ProcessID
== dbg_curr_process
->pid
)
565 else if (dpe
->proc
.th32ProcessID
== GetCurrentProcessId())
569 dbg_printf("%c%08lx %-8ld ", info
, dpe
->proc
.th32ProcessID
, dpe
->proc
.cntThreads
);
573 for (i
= 3 * (depth
- 1); i
> 0; i
--) dbg_printf(" ");
576 dbg_printf("'%s'\n", dpe
->proc
.szExeFile
);
577 dump_proc_info(dp
, dpe
->children
, depth
+ 1);
581 void info_win32_processes(void)
583 HANDLE snap
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
584 if (snap
!= INVALID_HANDLE_VALUE
)
587 unsigned i
, first
= -1;
592 dp
.entries
= malloc(sizeof(*dp
.entries
) * dp
.alloc
);
598 dp
.entries
[dp
.count
].proc
.dwSize
= sizeof(dp
.entries
[dp
.count
].proc
);
599 ok
= Process32First(snap
, &dp
.entries
[dp
.count
].proc
);
601 /* fetch all process information into dp */
604 dp
.entries
[dp
.count
++].children
= -1;
605 if (dp
.count
>= dp
.alloc
)
607 struct dump_proc_entry
* new = realloc(dp
.entries
, sizeof(*dp
.entries
) * (dp
.alloc
* 2));
617 dp
.entries
[dp
.count
].proc
.dwSize
= sizeof(dp
.entries
[dp
.count
].proc
);
618 ok
= Process32Next(snap
, &dp
.entries
[dp
.count
].proc
);
621 /* chain the siblings wrt. their parent */
622 for (i
= 0; i
< dp
.count
; i
++)
624 unsigned parent
= get_parent(&dp
, i
);
625 unsigned *chain
= parent
== -1 ? &first
: &dp
.entries
[parent
].children
;
626 dp
.entries
[i
].sibling
= *chain
;
629 dbg_printf(" %-8.8s %-8.8s %s (all id:s are in hex)\n", "pid", "threads", "executable");
630 dump_proc_info(&dp
, first
, 0);
635 static BOOL
get_process_name(DWORD pid
, PROCESSENTRY32W
* entry
)
638 HANDLE snap
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
640 if (snap
!= INVALID_HANDLE_VALUE
)
642 entry
->dwSize
= sizeof(*entry
);
643 if (Process32FirstW(snap
, entry
))
644 while (!(ret
= (entry
->th32ProcessID
== pid
)) &&
645 Process32NextW(snap
, entry
));
651 WCHAR
* fetch_thread_description(DWORD tid
)
653 static HRESULT (WINAPI
*my_GetThreadDescription
)(HANDLE
, PWSTR
*) = NULL
;
654 static BOOL resolved
= FALSE
;
660 HMODULE kernelbase
= GetModuleHandleA("kernelbase.dll");
662 my_GetThreadDescription
= (void *)GetProcAddress(kernelbase
, "GetThreadDescription");
666 if (!my_GetThreadDescription
)
669 h
= OpenThread(THREAD_QUERY_LIMITED_INFORMATION
, FALSE
, tid
);
673 my_GetThreadDescription(h
, &desc
);
676 if (desc
&& desc
[0] == '\0')
684 void info_win32_threads(void)
686 HANDLE snap
= CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD
, 0);
687 if (snap
!= INVALID_HANDLE_VALUE
)
691 DWORD lastProcessId
= 0;
692 struct dbg_process
* p
= NULL
;
693 struct dbg_thread
* t
= NULL
;
696 entry
.dwSize
= sizeof(entry
);
697 ok
= Thread32First(snap
, &entry
);
699 dbg_printf("%-8.8s %-8.8s %s %s (all IDs are in hex)\n",
700 "process", "tid", "prio", "name");
703 if (entry
.th32OwnerProcessID
!= GetCurrentProcessId())
705 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
706 * listed sequentially, which is not specified in the doc (Wine's implementation
709 if (entry
.th32OwnerProcessID
!= lastProcessId
)
711 PROCESSENTRY32W pcs_entry
;
712 const WCHAR
* exename
;
714 p
= dbg_get_process(entry
.th32OwnerProcessID
);
716 exename
= p
->imageName
;
717 else if (get_process_name(entry
.th32OwnerProcessID
, &pcs_entry
))
718 exename
= pcs_entry
.szExeFile
;
722 dbg_printf("%08lx%s %ls\n",
723 entry
.th32OwnerProcessID
, p
? " (D)" : "", exename
);
724 lastProcessId
= entry
.th32OwnerProcessID
;
726 dbg_printf("\t%08lx %4ld%s ",
727 entry
.th32ThreadID
, entry
.tpBasePri
,
728 (entry
.th32ThreadID
== dbg_curr_tid
) ? " <==" : " ");
730 if ((description
= fetch_thread_description(entry
.th32ThreadID
)))
732 dbg_printf("%ls\n", description
);
733 LocalFree(description
);
737 t
= dbg_get_thread(p
, entry
.th32ThreadID
);
738 dbg_printf("%s\n", t
? t
->name
: "");
741 ok
= Thread32Next(snap
, &entry
);
748 /***********************************************************************
749 * info_win32_frame_exceptions
751 * Get info on the exception frames of a given thread.
753 void info_win32_frame_exceptions(DWORD tid
)
755 struct dbg_thread
* thread
;
758 if (!dbg_curr_process
|| !dbg_curr_thread
)
760 dbg_printf("Cannot get info on exceptions while no process is loaded\n");
764 dbg_printf("Exception frames:\n");
766 if (tid
== dbg_curr_tid
) thread
= dbg_curr_thread
;
769 thread
= dbg_get_thread(dbg_curr_process
, tid
);
773 dbg_printf("Unknown thread id (%04lx) in current process\n", tid
);
776 if (SuspendThread(thread
->handle
) == -1)
778 dbg_printf("Can't suspend thread id (%04lx)\n", tid
);
783 if (!dbg_read_memory(thread
->teb
, &next_frame
, sizeof(next_frame
)))
785 dbg_printf("Can't read TEB:except_frame\n");
789 while (next_frame
!= (void*)-1)
791 EXCEPTION_REGISTRATION_RECORD frame
;
793 dbg_printf("%p: ", next_frame
);
794 if (!dbg_read_memory(next_frame
, &frame
, sizeof(frame
)))
796 dbg_printf("Invalid frame address\n");
799 dbg_printf("prev=%p handler=%p\n", frame
.Prev
, frame
.Handler
);
800 next_frame
= frame
.Prev
;
803 if (tid
!= dbg_curr_tid
) ResumeThread(thread
->handle
);
806 void info_win32_segments(DWORD start
, int length
)
812 if (length
== -1) length
= (8192 - start
);
814 for (i
= start
; i
< start
+ length
; i
++)
816 if (!dbg_curr_process
->process_io
->get_selector(dbg_curr_thread
->handle
, (i
<< 3) | 7, &le
))
819 if (le
.HighWord
.Bits
.Type
& 0x08)
821 flags
[0] = (le
.HighWord
.Bits
.Type
& 0x2) ? 'r' : '-';
828 flags
[1] = (le
.HighWord
.Bits
.Type
& 0x2) ? 'w' : '-';
831 dbg_printf("%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
833 (le
.HighWord
.Bits
.BaseHi
<< 24) +
834 (le
.HighWord
.Bits
.BaseMid
<< 16) + le
.BaseLow
,
835 ((le
.HighWord
.Bits
.LimitHi
<< 8) + le
.LimitLow
) <<
836 (le
.HighWord
.Bits
.Granularity
? 12 : 0),
837 le
.HighWord
.Bits
.Default_Big
? 32 : 16,
838 flags
[0], flags
[1], flags
[2]);
842 void info_win32_virtual(DWORD pid
)
844 MEMORY_BASIC_INFORMATION mbi
;
851 if (pid
== dbg_curr_pid
)
853 if (dbg_curr_process
== NULL
)
855 dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
858 hProc
= dbg_curr_process
->handle
;
862 hProc
= OpenProcess(PROCESS_QUERY_INFORMATION
| PROCESS_VM_READ
, FALSE
, pid
);
865 dbg_printf("Cannot open process <%04lx>\n", pid
);
870 dbg_printf("Address End State Type RWX\n");
872 while (VirtualQueryEx(hProc
, addr
, &mbi
, sizeof(mbi
)) >= sizeof(mbi
))
876 case MEM_COMMIT
: state
= "commit "; break;
877 case MEM_FREE
: state
= "free "; break;
878 case MEM_RESERVE
: state
= "reserve"; break;
879 default: state
= "??? "; break;
881 if (mbi
.State
!= MEM_FREE
)
885 case MEM_IMAGE
: type
= "image "; break;
886 case MEM_MAPPED
: type
= "mapped "; break;
887 case MEM_PRIVATE
: type
= "private"; break;
888 case 0: type
= " "; break;
889 default: type
= "??? "; break;
891 memset(prot
, ' ' , sizeof(prot
) - 1);
892 prot
[sizeof(prot
) - 1] = '\0';
893 if (mbi
.AllocationProtect
& (PAGE_READONLY
|PAGE_READWRITE
|PAGE_EXECUTE_READ
|PAGE_EXECUTE_READWRITE
|PAGE_WRITECOPY
|PAGE_EXECUTE_WRITECOPY
))
895 if (mbi
.AllocationProtect
& (PAGE_READWRITE
|PAGE_EXECUTE_READWRITE
))
897 if (mbi
.AllocationProtect
& (PAGE_WRITECOPY
|PAGE_EXECUTE_WRITECOPY
))
899 if (mbi
.AllocationProtect
& (PAGE_EXECUTE
|PAGE_EXECUTE_READ
|PAGE_EXECUTE_READWRITE
|PAGE_EXECUTE_WRITECOPY
))
907 dbg_printf("%0*Ix %0*Ix %s %s %s\n",
908 ADDRWIDTH
, (DWORD_PTR
)addr
, ADDRWIDTH
, (DWORD_PTR
)addr
+ mbi
.RegionSize
- 1, state
, type
, prot
);
909 if (addr
+ mbi
.RegionSize
< addr
) /* wrap around ? */
911 addr
+= mbi
.RegionSize
;
913 if (pid
!= dbg_curr_pid
) CloseHandle(hProc
);
916 void info_wine_dbg_channel(BOOL turn_on
, const char* cls
, const char* name
)
918 struct dbg_lvalue lvalue
;
919 struct __wine_debug_channel channel
;
925 if (!dbg_curr_process
|| !dbg_curr_thread
)
927 dbg_printf("Cannot set/get debug channels while no process is loaded\n");
931 if (symbol_get_lvalue("debug_options", -1, &lvalue
, FALSE
) != sglv_found
)
935 addr
= memory_to_linear_addr(&lvalue
.addr
);
938 else if (!strcmp(cls
, "fixme")) mask
= (1 << __WINE_DBCL_FIXME
);
939 else if (!strcmp(cls
, "err")) mask
= (1 << __WINE_DBCL_ERR
);
940 else if (!strcmp(cls
, "warn")) mask
= (1 << __WINE_DBCL_WARN
);
941 else if (!strcmp(cls
, "trace")) mask
= (1 << __WINE_DBCL_TRACE
);
944 dbg_printf("Unknown debug class %s\n", cls
);
948 bAll
= !strcmp("all", name
);
949 while (addr
&& dbg_read_memory(addr
, &channel
, sizeof(channel
)))
951 if (!channel
.name
[0]) break;
952 if (bAll
|| !strcmp( channel
.name
, name
))
954 if (turn_on
) channel
.flags
|= mask
;
955 else channel
.flags
&= ~mask
;
956 if (dbg_write_memory(addr
, &channel
, sizeof(channel
))) done
++;
958 addr
= (struct __wine_debug_channel
*)addr
+ 1;
960 if (!done
) dbg_printf("Unable to find debug channel %s\n", name
);
961 else WINE_TRACE("Changed %d channel instances\n", done
);
964 void info_win32_exception(void)
966 const EXCEPTION_RECORD
* rec
;
968 char hexbuf
[MAX_OFFSET_TO_STR_LEN
];
970 if (!dbg_curr_thread
->in_exception
)
972 dbg_printf("Thread isn't in an exception\n");
975 rec
= &dbg_curr_thread
->excpt_record
;
976 memory_get_current_pc(&addr
);
978 /* print some infos */
980 dbg_curr_thread
->first_chance
? "First chance exception" : "Unhandled exception");
981 switch (rec
->ExceptionCode
)
983 case EXCEPTION_BREAKPOINT
:
984 dbg_printf("breakpoint");
986 case EXCEPTION_SINGLE_STEP
:
987 dbg_printf("single step");
989 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
990 dbg_printf("divide by zero");
992 case EXCEPTION_INT_OVERFLOW
:
993 dbg_printf("overflow");
995 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED
:
996 dbg_printf("array bounds");
998 case EXCEPTION_ILLEGAL_INSTRUCTION
:
999 dbg_printf("illegal instruction");
1001 case EXCEPTION_STACK_OVERFLOW
:
1002 dbg_printf("stack overflow");
1004 case EXCEPTION_PRIV_INSTRUCTION
:
1005 dbg_printf("privileged instruction");
1007 case EXCEPTION_ACCESS_VIOLATION
:
1008 if (rec
->NumberParameters
== 2)
1009 dbg_printf("page fault on %s access to 0x%0*Ix",
1010 rec
->ExceptionInformation
[0] == EXCEPTION_WRITE_FAULT
? "write" :
1011 rec
->ExceptionInformation
[0] == EXCEPTION_EXECUTE_FAULT
? "execute" : "read",
1012 ADDRWIDTH
, rec
->ExceptionInformation
[1]);
1014 dbg_printf("page fault");
1016 case EXCEPTION_DATATYPE_MISALIGNMENT
:
1017 dbg_printf("Alignment");
1022 case CONTROL_C_EXIT
:
1025 case STATUS_POSSIBLE_DEADLOCK
:
1029 recaddr
.Mode
= AddrModeFlat
;
1030 recaddr
.Offset
= rec
->ExceptionInformation
[0];
1032 dbg_printf("wait failed on critical section ");
1033 print_address(&recaddr
, FALSE
);
1036 case EXCEPTION_WINE_STUB
:
1038 char dll
[64], name
[256];
1039 memory_get_string(dbg_curr_process
,
1040 (void*)rec
->ExceptionInformation
[0], TRUE
, FALSE
,
1042 if (HIWORD(rec
->ExceptionInformation
[1]))
1043 memory_get_string(dbg_curr_process
,
1044 (void*)rec
->ExceptionInformation
[1], TRUE
, FALSE
,
1045 name
, sizeof(name
));
1047 sprintf( name
, "%Id", rec
->ExceptionInformation
[1] );
1048 dbg_printf("unimplemented function %s.%s called", dll
, name
);
1051 case EXCEPTION_WINE_ASSERTION
:
1052 dbg_printf("assertion failed");
1054 case EXCEPTION_FLT_DENORMAL_OPERAND
:
1055 dbg_printf("denormal float operand");
1057 case EXCEPTION_FLT_DIVIDE_BY_ZERO
:
1058 dbg_printf("divide by zero");
1060 case EXCEPTION_FLT_INEXACT_RESULT
:
1061 dbg_printf("inexact float result");
1063 case EXCEPTION_FLT_INVALID_OPERATION
:
1064 dbg_printf("invalid float operation");
1066 case EXCEPTION_FLT_OVERFLOW
:
1067 dbg_printf("floating point overflow");
1069 case EXCEPTION_FLT_UNDERFLOW
:
1070 dbg_printf("floating point underflow");
1072 case EXCEPTION_FLT_STACK_CHECK
:
1073 dbg_printf("floating point stack check");
1075 case EXCEPTION_WINE_CXX_EXCEPTION
:
1076 if(rec
->NumberParameters
== 3 && rec
->ExceptionInformation
[0] == EXCEPTION_WINE_CXX_FRAME_MAGIC
)
1077 dbg_printf("C++ exception(object = 0x%0*Ix, type = 0x%0*Ix)",
1078 ADDRWIDTH
, rec
->ExceptionInformation
[1], ADDRWIDTH
, rec
->ExceptionInformation
[2]);
1079 else if(rec
->NumberParameters
== 4 && rec
->ExceptionInformation
[0] == EXCEPTION_WINE_CXX_FRAME_MAGIC
)
1080 dbg_printf("C++ exception(object = %p, type = %p, base = %p)",
1081 (void*)rec
->ExceptionInformation
[1], (void*)rec
->ExceptionInformation
[2],
1082 (void*)rec
->ExceptionInformation
[3]);
1084 dbg_printf("C++ exception with strange parameter count %ld or magic 0x%0*Ix",
1085 rec
->NumberParameters
, ADDRWIDTH
, rec
->ExceptionInformation
[0]);
1088 dbg_printf("0x%08lx", rec
->ExceptionCode
);
1091 if (rec
->ExceptionFlags
& EH_STACK_INVALID
)
1092 dbg_printf(", invalid program stack");
1097 dbg_printf(" in %s%ld-bit code (%s)",
1098 dbg_curr_process
->is_wow64
? "wow64 " : "",
1099 dbg_curr_process
->be_cpu
->pointer_size
* 8,
1100 memory_offset_to_string(hexbuf
, addr
.Offset
, 0));
1103 dbg_printf(" in vm86 code (%04x:%04x)", addr
.Segment
, (unsigned) addr
.Offset
);
1106 dbg_printf(" in 16-bit code (%04x:%04x)", addr
.Segment
, (unsigned) addr
.Offset
);
1109 dbg_printf(" in segmented 32-bit code (%04x:%08x)", addr
.Segment
, (unsigned) addr
.Offset
);
1111 default: dbg_printf(" bad address");
1126 { 0, VER_PLATFORM_WIN32s
, 2, 0, "2.0" },
1127 { 0, VER_PLATFORM_WIN32s
, 3, 0, "3.0" },
1128 { 0, VER_PLATFORM_WIN32s
, 3, 10, "3.1" },
1129 { 0, VER_PLATFORM_WIN32_WINDOWS
, 4, 0, "95" },
1130 { 0, VER_PLATFORM_WIN32_WINDOWS
, 4, 10, "98" },
1131 { 0, VER_PLATFORM_WIN32_WINDOWS
, 4, 90, "ME" },
1132 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 3, 51, "NT 3.51" },
1133 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 4, 0, "NT 4.0" },
1134 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 5, 0, "2000" },
1135 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 5, 1, "XP" },
1136 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 5, 2, "XP" },
1137 { VER_NT_SERVER
, VER_PLATFORM_WIN32_NT
, 5, 2, "Server 2003" },
1138 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 6, 0, "Vista" },
1139 { VER_NT_SERVER
, VER_PLATFORM_WIN32_NT
, 6, 0, "Server 2008" },
1140 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 6, 1, "7" },
1141 { VER_NT_SERVER
, VER_PLATFORM_WIN32_NT
, 6, 1, "Server 2008 R2" },
1142 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 6, 2, "8" },
1143 { VER_NT_SERVER
, VER_PLATFORM_WIN32_NT
, 6, 2, "Server 2012" },
1144 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 6, 3, "8.1" },
1145 { VER_NT_SERVER
, VER_PLATFORM_WIN32_NT
, 6, 3, "Server 2012 R2" },
1146 { VER_NT_WORKSTATION
, VER_PLATFORM_WIN32_NT
, 10, 0, "10" },
1149 static const char *get_windows_version(void)
1151 RTL_OSVERSIONINFOEXW info
= { sizeof(RTL_OSVERSIONINFOEXW
) };
1152 static char str
[64];
1155 RtlGetVersion( &info
);
1157 for (i
= 0; i
< ARRAY_SIZE(version_table
); i
++)
1159 if (version_table
[i
].type
== info
.wProductType
&&
1160 version_table
[i
].platform
== info
.dwPlatformId
&&
1161 version_table
[i
].major
== info
.dwMajorVersion
&&
1162 version_table
[i
].minor
== info
.dwMinorVersion
)
1164 return version_table
[i
].str
;
1168 snprintf( str
, sizeof(str
), "%ld.%ld (%d)", info
.dwMajorVersion
,
1169 info
.dwMinorVersion
, info
.wProductType
);
1173 static BOOL
is_guest(USHORT native
, USHORT guest
)
1177 return native
!= guest
&& !RtlWow64IsWowGuestMachineSupported(guest
, &supported
) && supported
;
1180 void info_win32_system(void)
1182 USHORT current
, native
;
1185 const char *(CDECL
*wine_get_build_id
)(void);
1186 void (CDECL
*wine_get_host_version
)( const char **sysname
, const char **release
);
1188 static USHORT guest_machines
[] =
1190 IMAGE_FILE_MACHINE_I386
, IMAGE_FILE_MACHINE_ARM
, IMAGE_FILE_MACHINE_ARMNT
,
1193 wine_get_build_id
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
1194 wine_get_host_version
= (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_host_version");
1196 RtlWow64GetProcessMachines( GetCurrentProcess(), ¤t
, &native
);
1198 dbg_printf( "System information:\n" );
1199 if (wine_get_build_id
) dbg_printf( " Wine build: %s\n", wine_get_build_id() );
1200 dbg_printf( " Platform: %s", get_machine_str(native
));
1201 for (count
= i
= 0; i
< ARRAY_SIZE(guest_machines
); i
++)
1203 if (is_guest(native
, guest_machines
[i
]))
1205 if (!count
++) dbg_printf(" (guest:");
1206 dbg_printf(" %s", get_machine_str(guest_machines
[i
]));
1209 dbg_printf("%s\n", count
? ")" : "");
1211 dbg_printf( " Version: Windows %s\n", get_windows_version() );
1212 if (wine_get_host_version
)
1214 const char *sysname
, *release
;
1215 wine_get_host_version( &sysname
, &release
);
1216 dbg_printf( " Host system: %s\n", sysname
);
1217 dbg_printf( " Host version: %s\n", release
);