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 void module_print_info(const struct info_module
*module
, BOOL is_embedded
)
171 dbg_printf("%*.*I64x-%*.*I64x\t%-16s%s\n",
172 ADDRWIDTH
, ADDRWIDTH
, module
->mi
.BaseOfImage
,
173 ADDRWIDTH
, ADDRWIDTH
, module
->mi
.BaseOfImage
+ module
->mi
.ImageSize
,
174 is_embedded
? "\\" : get_symtype_str(&module
->mi
), module
->name
);
177 static int __cdecl
module_compare(const void* p1
, const void* p2
)
179 struct info_module
*left
= (struct info_module
*)p1
;
180 struct info_module
*right
= (struct info_module
*)p2
;
181 LONGLONG val
= left
->mi
.BaseOfImage
- right
->mi
.BaseOfImage
;
183 if (val
< 0) return -1;
184 else if (val
> 0) return 1;
188 static inline BOOL
module_is_container(const struct info_module
*wmod_cntnr
,
189 const struct info_module
*wmod_child
)
191 return wmod_cntnr
->mi
.BaseOfImage
<= wmod_child
->mi
.BaseOfImage
&&
192 wmod_cntnr
->mi
.BaseOfImage
+ wmod_cntnr
->mi
.ImageSize
>=
193 wmod_child
->mi
.BaseOfImage
+ wmod_child
->mi
.ImageSize
;
196 static BOOL CALLBACK
info_mod_cb(PCSTR mod_name
, DWORD64 base
, PVOID ctx
)
198 struct info_modules
*im
= ctx
;
200 if (im
->num_used
+ 1 > im
->num_alloc
)
203 im
->modules
= dbg_heap_realloc(im
->modules
, im
->num_alloc
* sizeof(*im
->modules
));
205 im
->modules
[im
->num_used
].mi
.SizeOfStruct
= sizeof(im
->modules
[im
->num_used
].mi
);
206 if (SymGetModuleInfo64(dbg_curr_process
->handle
, base
, &im
->modules
[im
->num_used
].mi
))
208 const int dst_len
= sizeof(im
->modules
[im
->num_used
].name
);
209 lstrcpynA(im
->modules
[im
->num_used
].name
, mod_name
, dst_len
- 1);
210 im
->modules
[im
->num_used
].name
[dst_len
- 1] = 0;
216 /***********************************************************************
219 * Display information about a given module (DLL or EXE), or about all modules
221 void info_win32_module(DWORD64 base
)
223 struct info_modules im
;
224 UINT i
, j
, num_printed
= 0;
227 if (!dbg_curr_process
)
229 dbg_printf("Cannot get info on module while no process is loaded\n");
234 im
.num_alloc
= im
.num_used
= 0;
236 /* this is a wine specific options to return also ELF modules in the
239 opt
= SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES
, TRUE
);
240 SymEnumerateModules64(dbg_curr_process
->handle
, info_mod_cb
, &im
);
241 SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES
, opt
);
243 qsort(im
.modules
, im
.num_used
, sizeof(im
.modules
[0]), module_compare
);
245 dbg_printf("Module\tAddress\t\t\t%sDebug info\tName (%d modules)\n",
246 ADDRWIDTH
== 16 ? "\t\t" : "", im
.num_used
);
248 for (i
= 0; i
< im
.num_used
; i
++)
251 (base
< im
.modules
[i
].mi
.BaseOfImage
|| base
>= im
.modules
[i
].mi
.BaseOfImage
+ im
.modules
[i
].mi
.ImageSize
))
253 if (strstr(im
.modules
[i
].name
, "<elf>"))
256 module_print_info(&im
.modules
[i
], FALSE
);
257 /* print all modules embedded in this one */
258 for (j
= 0; j
< im
.num_used
; j
++)
260 if (!strstr(im
.modules
[j
].name
, "<elf>") && module_is_container(&im
.modules
[i
], &im
.modules
[j
]))
262 dbg_printf(" \\-PE\t");
263 module_print_info(&im
.modules
[j
], TRUE
);
269 /* check module is not embedded in another module */
270 for (j
= 0; j
< im
.num_used
; j
++)
272 if (strstr(im
.modules
[j
].name
, "<elf>") && module_is_container(&im
.modules
[j
], &im
.modules
[i
]))
275 if (j
< im
.num_used
) continue;
276 if (strstr(im
.modules
[i
].name
, ".so") || strchr(im
.modules
[i
].name
, '<'))
280 module_print_info(&im
.modules
[i
], FALSE
);
284 HeapFree(GetProcessHeap(), 0, im
.modules
);
286 if (base
&& !num_printed
)
287 dbg_printf("'0x%0*I64x' is not a valid module address\n", ADDRWIDTH
, base
);
297 static void class_walker(HWND hWnd
, struct class_walker
* cw
)
304 if (!GetClassNameA(hWnd
, clsName
, sizeof(clsName
)))
306 if ((atom
= FindAtomA(clsName
)) == 0)
309 for (i
= 0; i
< cw
->used
; i
++)
311 if (cw
->table
[i
] == atom
)
316 if (cw
->used
>= cw
->alloc
)
319 cw
->table
= dbg_heap_realloc(cw
->table
, cw
->alloc
* sizeof(ATOM
));
321 cw
->table
[cw
->used
++] = atom
;
322 info_win32_class(hWnd
, clsName
);
326 if ((child
= GetWindow(hWnd
, GW_CHILD
)) != 0)
327 class_walker(child
, cw
);
328 } while ((hWnd
= GetWindow(hWnd
, GW_HWNDNEXT
)) != 0);
331 void info_win32_class(HWND hWnd
, const char* name
)
334 HINSTANCE hInst
= hWnd
? (HINSTANCE
)GetWindowLongPtrW(hWnd
, GWLP_HINSTANCE
) : 0;
338 struct class_walker cw
;
341 cw
.used
= cw
.alloc
= 0;
342 class_walker(GetDesktopWindow(), &cw
);
343 HeapFree(GetProcessHeap(), 0, cw
.table
);
347 if (!GetClassInfoExA(hInst
, name
, &wca
))
349 dbg_printf("Cannot find class '%s'\n", name
);
353 dbg_printf("Class '%s':\n", name
);
354 dbg_printf("style=0x%08x wndProc=%p\n"
355 "inst=%p icon=%p cursor=%p bkgnd=%p\n"
356 "clsExtra=%d winExtra=%d\n",
357 wca
.style
, wca
.lpfnWndProc
, wca
.hInstance
,
358 wca
.hIcon
, wca
.hCursor
, wca
.hbrBackground
,
359 wca
.cbClsExtra
, wca
.cbWndExtra
);
361 if (hWnd
&& wca
.cbClsExtra
)
366 dbg_printf("Extra bytes:");
367 for (i
= 0; i
< wca
.cbClsExtra
/ 2; i
++)
369 w
= GetClassWord(hWnd
, i
* 2);
370 /* FIXME: depends on i386 endian-ity */
371 dbg_printf(" %02x %02x", HIBYTE(w
), LOBYTE(w
));
377 * + print #windows (or even list of windows...)
378 * + print extra bytes => this requires a window handle on this very class...
382 static void info_window(HWND hWnd
, int indent
)
390 if (!GetClassNameA(hWnd
, clsName
, sizeof(clsName
)))
391 strcpy(clsName
, "-- Unknown --");
392 if (!GetWindowTextA(hWnd
, wndName
, sizeof(wndName
)))
393 strcpy(wndName
, "-- Empty --");
395 dbg_printf("%*s%08Ix%*s %-17.17s %08lx %0*Ix %08lx %.14s\n",
396 indent
, "", (DWORD_PTR
)hWnd
, 12 - indent
, "",
397 clsName
, GetWindowLongW(hWnd
, GWL_STYLE
),
398 ADDRWIDTH
, (ULONG_PTR
)GetWindowLongPtrW(hWnd
, GWLP_WNDPROC
),
399 GetWindowThreadProcessId(hWnd
, NULL
), wndName
);
401 if ((child
= GetWindow(hWnd
, GW_CHILD
)) != 0)
402 info_window(child
, indent
+ 1);
403 } while ((hWnd
= GetWindow(hWnd
, GW_HWNDNEXT
)) != 0);
406 void info_win32_window(HWND hWnd
, BOOL detailed
)
414 if (!IsWindow(hWnd
)) hWnd
= GetDesktopWindow();
418 dbg_printf("%-20.20s %-17.17s %-8.8s %-*.*s %-8.8s %s\n",
419 "Window handle", "Class Name", "Style",
420 ADDRWIDTH
, ADDRWIDTH
, "WndProc", "Thread", "Text");
421 info_window(hWnd
, 0);
425 if (!GetClassNameA(hWnd
, clsName
, sizeof(clsName
)))
426 strcpy(clsName
, "-- Unknown --");
427 if (!GetWindowTextA(hWnd
, wndName
, sizeof(wndName
)))
428 strcpy(wndName
, "-- Empty --");
429 if (!GetClientRect(hWnd
, &clientRect
) ||
430 !MapWindowPoints(hWnd
, 0, (LPPOINT
) &clientRect
, 2))
431 SetRectEmpty(&clientRect
);
432 if (!GetWindowRect(hWnd
, &windowRect
))
433 SetRectEmpty(&windowRect
);
435 /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
436 dbg_printf("next=%p child=%p parent=%p owner=%p class='%s'\n"
437 "inst=%p active=%p idmenu=%08Ix\n"
438 "style=0x%08lx exstyle=0x%08lx wndproc=%p text='%s'\n"
439 "client=%ld,%ld-%ld,%ld window=%ld,%ld-%ld,%ld sysmenu=%p\n",
440 GetWindow(hWnd
, GW_HWNDNEXT
),
441 GetWindow(hWnd
, GW_CHILD
),
443 GetWindow(hWnd
, GW_OWNER
),
445 (HINSTANCE
)GetWindowLongPtrW(hWnd
, GWLP_HINSTANCE
),
446 GetLastActivePopup(hWnd
),
447 (ULONG_PTR
)GetWindowLongPtrW(hWnd
, GWLP_ID
),
448 GetWindowLongW(hWnd
, GWL_STYLE
),
449 GetWindowLongW(hWnd
, GWL_EXSTYLE
),
450 (void*)GetWindowLongPtrW(hWnd
, GWLP_WNDPROC
),
452 clientRect
.left
, clientRect
.top
, clientRect
.right
, clientRect
.bottom
,
453 windowRect
.left
, windowRect
.top
, windowRect
.right
, windowRect
.bottom
,
454 GetSystemMenu(hWnd
, FALSE
));
456 if (GetClassLongW(hWnd
, GCL_CBWNDEXTRA
))
460 dbg_printf("Extra bytes:");
461 for (i
= 0; i
< GetClassLongW(hWnd
, GCL_CBWNDEXTRA
) / 2; i
++)
463 w
= GetWindowWord(hWnd
, i
* 2);
464 /* FIXME: depends on i386 endian-ity */
465 dbg_printf(" %02x %02x", HIBYTE(w
), LOBYTE(w
));
472 struct dump_proc_entry
475 unsigned children
; /* index in dump_proc.entries of first child */
476 unsigned sibling
; /* index in dump_proc.entries of next sibling */
481 struct dump_proc_entry
*entries
;
486 static unsigned get_parent(const struct dump_proc
* dp
, unsigned idx
)
490 for (i
= 0; i
< dp
->count
; i
++)
492 if (i
!= idx
&& dp
->entries
[i
].proc
.th32ProcessID
== dp
->entries
[idx
].proc
.th32ParentProcessID
)
498 static void dump_proc_info(const struct dump_proc
* dp
, unsigned idx
, unsigned depth
)
500 struct dump_proc_entry
* dpe
;
501 for ( ; idx
!= -1; idx
= dp
->entries
[idx
].sibling
)
503 assert(idx
< dp
->count
);
504 dpe
= &dp
->entries
[idx
];
505 dbg_printf("%c%08lx %-8ld ",
506 (dpe
->proc
.th32ProcessID
== (dbg_curr_process
?
507 dbg_curr_process
->pid
: 0)) ? '>' : ' ',
508 dpe
->proc
.th32ProcessID
, dpe
->proc
.cntThreads
);
512 for (i
= 3 * (depth
- 1); i
> 0; i
--) dbg_printf(" ");
515 dbg_printf("'%s'\n", dpe
->proc
.szExeFile
);
516 dump_proc_info(dp
, dpe
->children
, depth
+ 1);
520 void info_win32_processes(void)
522 HANDLE snap
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
523 if (snap
!= INVALID_HANDLE_VALUE
)
526 unsigned i
, first
= -1;
531 dp
.entries
= HeapAlloc(GetProcessHeap(), 0, sizeof(*dp
.entries
) * dp
.alloc
);
537 dp
.entries
[dp
.count
].proc
.dwSize
= sizeof(dp
.entries
[dp
.count
].proc
);
538 ok
= Process32First(snap
, &dp
.entries
[dp
.count
].proc
);
540 /* fetch all process information into dp (skipping this debugger) */
543 if (dp
.entries
[dp
.count
].proc
.th32ProcessID
!= GetCurrentProcessId())
544 dp
.entries
[dp
.count
++].children
= -1;
545 if (dp
.count
>= dp
.alloc
)
547 dp
.entries
= HeapReAlloc(GetProcessHeap(), 0, dp
.entries
, sizeof(*dp
.entries
) * (dp
.alloc
*= 2));
548 if (!dp
.entries
) return;
550 dp
.entries
[dp
.count
].proc
.dwSize
= sizeof(dp
.entries
[dp
.count
].proc
);
551 ok
= Process32Next(snap
, &dp
.entries
[dp
.count
].proc
);
554 /* chain the siblings wrt. their parent */
555 for (i
= 0; i
< dp
.count
; i
++)
557 unsigned parent
= get_parent(&dp
, i
);
558 unsigned *chain
= parent
== -1 ? &first
: &dp
.entries
[parent
].children
;
559 dp
.entries
[i
].sibling
= *chain
;
562 dbg_printf(" %-8.8s %-8.8s %s (all id:s are in hex)\n", "pid", "threads", "executable");
563 dump_proc_info(&dp
, first
, 0);
564 HeapFree(GetProcessHeap(), 0, dp
.entries
);
568 static BOOL
get_process_name(DWORD pid
, PROCESSENTRY32W
* entry
)
571 HANDLE snap
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
573 if (snap
!= INVALID_HANDLE_VALUE
)
575 entry
->dwSize
= sizeof(*entry
);
576 if (Process32FirstW(snap
, entry
))
577 while (!(ret
= (entry
->th32ProcessID
== pid
)) &&
578 Process32NextW(snap
, entry
));
584 void info_win32_threads(void)
586 HANDLE snap
= CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD
, 0);
587 if (snap
!= INVALID_HANDLE_VALUE
)
591 DWORD lastProcessId
= 0;
592 struct dbg_process
* p
= NULL
;
593 struct dbg_thread
* t
= NULL
;
595 entry
.dwSize
= sizeof(entry
);
596 ok
= Thread32First(snap
, &entry
);
598 dbg_printf("%-8.8s %-8.8s %s %s (all IDs are in hex)\n",
599 "process", "tid", "prio", "name");
602 if (entry
.th32OwnerProcessID
!= GetCurrentProcessId())
604 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
605 * listed sequentially, which is not specified in the doc (Wine's implementation
608 if (entry
.th32OwnerProcessID
!= lastProcessId
)
610 PROCESSENTRY32W pcs_entry
;
611 const WCHAR
* exename
;
613 p
= dbg_get_process(entry
.th32OwnerProcessID
);
615 exename
= p
->imageName
;
616 else if (get_process_name(entry
.th32OwnerProcessID
, &pcs_entry
))
617 exename
= pcs_entry
.szExeFile
;
621 dbg_printf("%08lx%s %ls\n",
622 entry
.th32OwnerProcessID
, p
? " (D)" : "", exename
);
623 lastProcessId
= entry
.th32OwnerProcessID
;
625 t
= dbg_get_thread(p
, entry
.th32ThreadID
);
626 dbg_printf("\t%08lx %4ld%s %s\n",
627 entry
.th32ThreadID
, entry
.tpBasePri
,
628 (entry
.th32ThreadID
== dbg_curr_tid
) ? " <==" : " ",
632 ok
= Thread32Next(snap
, &entry
);
639 /***********************************************************************
640 * info_win32_frame_exceptions
642 * Get info on the exception frames of a given thread.
644 void info_win32_frame_exceptions(DWORD tid
)
646 struct dbg_thread
* thread
;
649 if (!dbg_curr_process
|| !dbg_curr_thread
)
651 dbg_printf("Cannot get info on exceptions while no process is loaded\n");
655 dbg_printf("Exception frames:\n");
657 if (tid
== dbg_curr_tid
) thread
= dbg_curr_thread
;
660 thread
= dbg_get_thread(dbg_curr_process
, tid
);
664 dbg_printf("Unknown thread id (%04lx) in current process\n", tid
);
667 if (SuspendThread(thread
->handle
) == -1)
669 dbg_printf("Can't suspend thread id (%04lx)\n", tid
);
674 if (!dbg_read_memory(thread
->teb
, &next_frame
, sizeof(next_frame
)))
676 dbg_printf("Can't read TEB:except_frame\n");
680 while (next_frame
!= (void*)-1)
682 EXCEPTION_REGISTRATION_RECORD frame
;
684 dbg_printf("%p: ", next_frame
);
685 if (!dbg_read_memory(next_frame
, &frame
, sizeof(frame
)))
687 dbg_printf("Invalid frame address\n");
690 dbg_printf("prev=%p handler=%p\n", frame
.Prev
, frame
.Handler
);
691 next_frame
= frame
.Prev
;
694 if (tid
!= dbg_curr_tid
) ResumeThread(thread
->handle
);
697 void info_win32_segments(DWORD start
, int length
)
703 if (length
== -1) length
= (8192 - start
);
705 for (i
= start
; i
< start
+ length
; i
++)
707 if (!dbg_curr_process
->process_io
->get_selector(dbg_curr_thread
->handle
, (i
<< 3) | 7, &le
))
710 if (le
.HighWord
.Bits
.Type
& 0x08)
712 flags
[0] = (le
.HighWord
.Bits
.Type
& 0x2) ? 'r' : '-';
719 flags
[1] = (le
.HighWord
.Bits
.Type
& 0x2) ? 'w' : '-';
722 dbg_printf("%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
724 (le
.HighWord
.Bits
.BaseHi
<< 24) +
725 (le
.HighWord
.Bits
.BaseMid
<< 16) + le
.BaseLow
,
726 ((le
.HighWord
.Bits
.LimitHi
<< 8) + le
.LimitLow
) <<
727 (le
.HighWord
.Bits
.Granularity
? 12 : 0),
728 le
.HighWord
.Bits
.Default_Big
? 32 : 16,
729 flags
[0], flags
[1], flags
[2]);
733 void info_win32_virtual(DWORD pid
)
735 MEMORY_BASIC_INFORMATION mbi
;
742 if (pid
== dbg_curr_pid
)
744 if (dbg_curr_process
== NULL
)
746 dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
749 hProc
= dbg_curr_process
->handle
;
753 hProc
= OpenProcess(PROCESS_QUERY_INFORMATION
| PROCESS_VM_READ
, FALSE
, pid
);
756 dbg_printf("Cannot open process <%04lx>\n", pid
);
761 dbg_printf("Address End State Type RWX\n");
763 while (VirtualQueryEx(hProc
, addr
, &mbi
, sizeof(mbi
)) >= sizeof(mbi
))
767 case MEM_COMMIT
: state
= "commit "; break;
768 case MEM_FREE
: state
= "free "; break;
769 case MEM_RESERVE
: state
= "reserve"; break;
770 default: state
= "??? "; break;
772 if (mbi
.State
!= MEM_FREE
)
776 case MEM_IMAGE
: type
= "image "; break;
777 case MEM_MAPPED
: type
= "mapped "; break;
778 case MEM_PRIVATE
: type
= "private"; break;
779 case 0: type
= " "; break;
780 default: type
= "??? "; break;
782 memset(prot
, ' ' , sizeof(prot
) - 1);
783 prot
[sizeof(prot
) - 1] = '\0';
784 if (mbi
.AllocationProtect
& (PAGE_READONLY
|PAGE_READWRITE
|PAGE_EXECUTE_READ
|PAGE_EXECUTE_READWRITE
|PAGE_WRITECOPY
|PAGE_EXECUTE_WRITECOPY
))
786 if (mbi
.AllocationProtect
& (PAGE_READWRITE
|PAGE_EXECUTE_READWRITE
))
788 if (mbi
.AllocationProtect
& (PAGE_WRITECOPY
|PAGE_EXECUTE_WRITECOPY
))
790 if (mbi
.AllocationProtect
& (PAGE_EXECUTE
|PAGE_EXECUTE_READ
|PAGE_EXECUTE_READWRITE
|PAGE_EXECUTE_WRITECOPY
))
798 dbg_printf("%0*Ix %0*Ix %s %s %s\n",
799 ADDRWIDTH
, (DWORD_PTR
)addr
, ADDRWIDTH
, (DWORD_PTR
)addr
+ mbi
.RegionSize
- 1, state
, type
, prot
);
800 if (addr
+ mbi
.RegionSize
< addr
) /* wrap around ? */
802 addr
+= mbi
.RegionSize
;
804 if (pid
!= dbg_curr_pid
) CloseHandle(hProc
);
807 void info_wine_dbg_channel(BOOL turn_on
, const char* cls
, const char* name
)
809 struct dbg_lvalue lvalue
;
810 struct __wine_debug_channel channel
;
816 if (!dbg_curr_process
|| !dbg_curr_thread
)
818 dbg_printf("Cannot set/get debug channels while no process is loaded\n");
822 if (symbol_get_lvalue("debug_options", -1, &lvalue
, FALSE
) != sglv_found
)
826 addr
= memory_to_linear_addr(&lvalue
.addr
);
829 else if (!strcmp(cls
, "fixme")) mask
= (1 << __WINE_DBCL_FIXME
);
830 else if (!strcmp(cls
, "err")) mask
= (1 << __WINE_DBCL_ERR
);
831 else if (!strcmp(cls
, "warn")) mask
= (1 << __WINE_DBCL_WARN
);
832 else if (!strcmp(cls
, "trace")) mask
= (1 << __WINE_DBCL_TRACE
);
835 dbg_printf("Unknown debug class %s\n", cls
);
839 bAll
= !strcmp("all", name
);
840 while (addr
&& dbg_read_memory(addr
, &channel
, sizeof(channel
)))
842 if (!channel
.name
[0]) break;
843 if (bAll
|| !strcmp( channel
.name
, name
))
845 if (turn_on
) channel
.flags
|= mask
;
846 else channel
.flags
&= ~mask
;
847 if (dbg_write_memory(addr
, &channel
, sizeof(channel
))) done
++;
849 addr
= (struct __wine_debug_channel
*)addr
+ 1;
851 if (!done
) dbg_printf("Unable to find debug channel %s\n", name
);
852 else WINE_TRACE("Changed %d channel instances\n", done
);
855 void info_win32_exception(void)
857 const EXCEPTION_RECORD
* rec
;
859 char hexbuf
[MAX_OFFSET_TO_STR_LEN
];
861 if (!dbg_curr_thread
->in_exception
)
863 dbg_printf("Thread isn't in an exception\n");
866 rec
= &dbg_curr_thread
->excpt_record
;
867 memory_get_current_pc(&addr
);
869 /* print some infos */
871 dbg_curr_thread
->first_chance
? "First chance exception" : "Unhandled exception");
872 switch (rec
->ExceptionCode
)
874 case EXCEPTION_BREAKPOINT
:
875 dbg_printf("breakpoint");
877 case EXCEPTION_SINGLE_STEP
:
878 dbg_printf("single step");
880 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
881 dbg_printf("divide by zero");
883 case EXCEPTION_INT_OVERFLOW
:
884 dbg_printf("overflow");
886 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED
:
887 dbg_printf("array bounds");
889 case EXCEPTION_ILLEGAL_INSTRUCTION
:
890 dbg_printf("illegal instruction");
892 case EXCEPTION_STACK_OVERFLOW
:
893 dbg_printf("stack overflow");
895 case EXCEPTION_PRIV_INSTRUCTION
:
896 dbg_printf("privileged instruction");
898 case EXCEPTION_ACCESS_VIOLATION
:
899 if (rec
->NumberParameters
== 2)
900 dbg_printf("page fault on %s access to 0x%0*Ix",
901 rec
->ExceptionInformation
[0] == EXCEPTION_WRITE_FAULT
? "write" :
902 rec
->ExceptionInformation
[0] == EXCEPTION_EXECUTE_FAULT
? "execute" : "read",
903 ADDRWIDTH
, rec
->ExceptionInformation
[1]);
905 dbg_printf("page fault");
907 case EXCEPTION_DATATYPE_MISALIGNMENT
:
908 dbg_printf("Alignment");
916 case STATUS_POSSIBLE_DEADLOCK
:
920 recaddr
.Mode
= AddrModeFlat
;
921 recaddr
.Offset
= rec
->ExceptionInformation
[0];
923 dbg_printf("wait failed on critical section ");
924 print_address(&recaddr
, FALSE
);
927 case EXCEPTION_WINE_STUB
:
929 char dll
[64], name
[256];
930 memory_get_string(dbg_curr_process
,
931 (void*)rec
->ExceptionInformation
[0], TRUE
, FALSE
,
933 if (HIWORD(rec
->ExceptionInformation
[1]))
934 memory_get_string(dbg_curr_process
,
935 (void*)rec
->ExceptionInformation
[1], TRUE
, FALSE
,
938 sprintf( name
, "%Id", rec
->ExceptionInformation
[1] );
939 dbg_printf("unimplemented function %s.%s called", dll
, name
);
942 case EXCEPTION_WINE_ASSERTION
:
943 dbg_printf("assertion failed");
945 case EXCEPTION_FLT_DENORMAL_OPERAND
:
946 dbg_printf("denormal float operand");
948 case EXCEPTION_FLT_DIVIDE_BY_ZERO
:
949 dbg_printf("divide by zero");
951 case EXCEPTION_FLT_INEXACT_RESULT
:
952 dbg_printf("inexact float result");
954 case EXCEPTION_FLT_INVALID_OPERATION
:
955 dbg_printf("invalid float operation");
957 case EXCEPTION_FLT_OVERFLOW
:
958 dbg_printf("floating point overflow");
960 case EXCEPTION_FLT_UNDERFLOW
:
961 dbg_printf("floating point underflow");
963 case EXCEPTION_FLT_STACK_CHECK
:
964 dbg_printf("floating point stack check");
966 case EXCEPTION_WINE_CXX_EXCEPTION
:
967 if(rec
->NumberParameters
== 3 && rec
->ExceptionInformation
[0] == EXCEPTION_WINE_CXX_FRAME_MAGIC
)
968 dbg_printf("C++ exception(object = 0x%0*Ix, type = 0x%0*Ix)",
969 ADDRWIDTH
, rec
->ExceptionInformation
[1], ADDRWIDTH
, rec
->ExceptionInformation
[2]);
970 else if(rec
->NumberParameters
== 4 && rec
->ExceptionInformation
[0] == EXCEPTION_WINE_CXX_FRAME_MAGIC
)
971 dbg_printf("C++ exception(object = %p, type = %p, base = %p)",
972 (void*)rec
->ExceptionInformation
[1], (void*)rec
->ExceptionInformation
[2],
973 (void*)rec
->ExceptionInformation
[3]);
975 dbg_printf("C++ exception with strange parameter count %ld or magic 0x%0*Ix",
976 rec
->NumberParameters
, ADDRWIDTH
, rec
->ExceptionInformation
[0]);
979 dbg_printf("0x%08lx", rec
->ExceptionCode
);
982 if (rec
->ExceptionFlags
& EH_STACK_INVALID
)
983 dbg_printf(", invalid program stack");
988 dbg_printf(" in %ld-bit code (%s)",
989 dbg_curr_process
->be_cpu
->pointer_size
* 8,
990 memory_offset_to_string(hexbuf
, addr
.Offset
, 0));
993 dbg_printf(" in vm86 code (%04x:%04x)", addr
.Segment
, (unsigned) addr
.Offset
);
996 dbg_printf(" in 16-bit code (%04x:%04x)", addr
.Segment
, (unsigned) addr
.Offset
);
999 dbg_printf(" in segmented 32-bit code (%04x:%08x)", addr
.Segment
, (unsigned) addr
.Offset
);
1001 default: dbg_printf(" bad address");