ddraw: Avoid LPD3DSTATS.
[wine] / programs / winedbg / info.c
1 /*
2  * Wine debugger utility routines
3  *
4  * Copyright 1993 Eric Youngdale
5  * Copyright 1995 Alexandre Julliard
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include "config.h"
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
28
29 #include "debugger.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "tlhelp32.h"
33 #include "wine/debug.h"
34 #include "wine/exception.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
37
38 /***********************************************************************
39  *           print_help
40  *
41  * Implementation of the 'help' command.
42  */
43 void print_help(void)
44 {
45     int i = 0;
46     static const char * const helptext[] =
47         {
48             "The commands accepted by the Wine debugger are a reasonable",
49             "subset of the commands that gdb accepts.",
50             "The commands currently are:",
51             "  help                                   quit",
52             "  break [*<addr>]                        watch | rwatch *<addr>",
53             "  delete break bpnum                     disable bpnum",
54             "  enable bpnum                           condition <bpnum> [<expr>]",
55             "  finish                                 cont [N]",
56             "  step [N]                               next [N]",
57             "  stepi [N]                              nexti [N]",
58             "  x <addr>                               print <expr>",
59             "  display <expr>                         undisplay <disnum>",
60             "  local display <expr>                   delete display <disnum>",                  
61             "  enable display <disnum>                disable display <disnum>",
62             "  bt [<tid>|all]                         frame <n>",
63             "  up                                     down",
64             "  list <lines>                           disassemble [<addr>][,<addr>]",
65             "  show dir                               dir <path>",
66             "  set <reg> = <expr>                     set *<addr> = <expr>",
67             "  pass                                   whatis",
68             "  info (see 'help info' for options)",
69
70             "The 'x' command accepts repeat counts and formats (including 'i') in the",
71             "same way that gdb does.\n",
72
73             "The following are examples of legal expressions:",
74             " $eax     $eax+0x3   0x1000   ($eip + 256)  *$eax   *($esp + 3)",
75             " Also, a nm format symbol table can be read from a file using the",
76             " symbolfile command.", /*  Symbols can also be defined individually with",
77                                         " the define command.", */
78             "",
79             NULL
80         };
81
82     while (helptext[i]) dbg_printf("%s\n", helptext[i++]);
83 }
84
85
86 /***********************************************************************
87  *           info_help
88  *
89  * Implementation of the 'help info' command.
90  */
91 void info_help(void)
92 {
93     int i = 0;
94     static const char * const infotext[] =
95         {
96             "The info commands allow you to get assorted bits of interesting stuff",
97             "to be displayed.  The options are:",
98             "  info break           Displays information about breakpoints",
99             "  info class <name>    Displays information about window class <name>",
100             "  info display         Shows auto-display expressions in use",
101             "  info except <pid>    Shows exception handler chain (in a given process)",
102             "  info locals          Displays values of all local vars for current frame",
103             "  info maps <pid>      Shows virtual mappings (in a given process)",
104             "  info process         Shows all running processes",
105             "  info reg             Displays values of the general registers at top of stack",
106             "  info all-reg         Displays the general and floating point registers",
107             "  info segments <pid>  Displays information about all known segments",
108             "  info share           Displays all loaded modules",
109             "  info share <addr>    Displays internal module state",
110             "  info stack [<len>]   Dumps information about top of stack, up to len words",
111             "  info symbol <sym>    Displays information about a given symbol",
112             "  info thread          Shows all running threads",
113             "  info wnd <handle>    Displays internal window state",
114             "",
115             NULL
116         };
117
118     while (infotext[i]) dbg_printf("%s\n", infotext[i++]);
119 }
120
121 static const char* get_symtype_str(const IMAGEHLP_MODULE64* mi)
122 {
123     switch (mi->SymType)
124     {
125     default:
126     case SymNone:       return "--none--";
127     case SymCoff:       return "COFF";
128     case SymCv:         return "CodeView";
129     case SymPdb:        return "PDB";
130     case SymExport:     return "Export";
131     case SymDeferred:   return "Deferred";
132     case SymSym:        return "Sym";
133     case SymDia:
134         switch (mi->CVSig)
135         {
136         case 'S' | ('T' << 8) | ('A' << 16) | ('B' << 24):
137             return "Stabs";
138         case 'D' | ('W' << 8) | ('A' << 16) | ('R' << 24):
139             return "Dwarf";
140         default:
141             return "DIA";
142
143         }
144     }
145 }
146
147 struct info_module
148 {
149     IMAGEHLP_MODULE64*  mi;
150     unsigned            num_alloc;
151     unsigned            num_used;
152 };
153
154 static void module_print_info(const IMAGEHLP_MODULE64* mi, BOOL is_embedded)
155 {
156     dbg_printf("%*.*s-%*.*s\t%-16s%s\n",
157                ADDRWIDTH, ADDRWIDTH, wine_dbgstr_longlong(mi->BaseOfImage),
158                ADDRWIDTH, ADDRWIDTH, wine_dbgstr_longlong(mi->BaseOfImage + mi->ImageSize),
159                is_embedded ? "\\" : get_symtype_str(mi), mi->ModuleName);
160 }
161
162 static int      module_compare(const void* p1, const void* p2)
163 {
164     LONGLONG val = ((const IMAGEHLP_MODULE64*)p1)->BaseOfImage -
165         ((const IMAGEHLP_MODULE64*)p2)->BaseOfImage;
166     if (val < 0) return -1;
167     else if (val > 0) return 1;
168     else return 0;
169 }
170
171 static inline BOOL module_is_container(const IMAGEHLP_MODULE64* wmod_cntnr,
172                                        const IMAGEHLP_MODULE64* wmod_child)
173 {
174     return wmod_cntnr->BaseOfImage <= wmod_child->BaseOfImage &&
175         wmod_cntnr->BaseOfImage + wmod_cntnr->ImageSize >=
176         wmod_child->BaseOfImage + wmod_child->ImageSize;
177 }
178
179 static BOOL CALLBACK info_mod_cb(PCSTR mod_name, DWORD64 base, PVOID ctx)
180 {
181     struct info_module* im = ctx;
182
183     if (im->num_used + 1 > im->num_alloc)
184     {
185         im->num_alloc += 16;
186         im->mi = dbg_heap_realloc(im->mi, im->num_alloc * sizeof(*im->mi));
187     }
188     im->mi[im->num_used].SizeOfStruct = sizeof(im->mi[im->num_used]);
189     if (SymGetModuleInfo64(dbg_curr_process->handle, base, &im->mi[im->num_used]))
190     {
191         im->num_used++;
192     }   
193     return TRUE;
194 }
195
196 /***********************************************************************
197  *           info_win32_module
198  *
199  * Display information about a given module (DLL or EXE), or about all modules
200  */
201 void info_win32_module(DWORD64 base)
202 {
203     struct info_module  im;
204     UINT                i, j, num_printed = 0;
205     DWORD               opt;
206
207     if (!dbg_curr_process)
208     {
209         dbg_printf("Cannot get info on module while no process is loaded\n");
210         return;
211     }
212
213     im.mi = NULL;
214     im.num_alloc = im.num_used = 0;
215
216     /* this is a wine specific options to return also ELF modules in the
217      * enumeration
218      */
219     SymSetOptions((opt = SymGetOptions()) | 0x40000000);
220     SymEnumerateModules64(dbg_curr_process->handle, info_mod_cb, (void*)&im);
221     SymSetOptions(opt);
222
223     qsort(im.mi, im.num_used, sizeof(im.mi[0]), module_compare);
224
225     dbg_printf("Module\tAddress\t\t\t%sDebug info\tName (%d modules)\n",
226                ADDRWIDTH == 16 ? "\t\t" : "", im.num_used);
227
228     for (i = 0; i < im.num_used; i++)
229     {
230         if (base && 
231             (base < im.mi[i].BaseOfImage || base >= im.mi[i].BaseOfImage + im.mi[i].ImageSize))
232             continue;
233         if (strstr(im.mi[i].ModuleName, "<elf>"))
234         {
235             dbg_printf("ELF\t");
236             module_print_info(&im.mi[i], FALSE);
237             /* print all modules embedded in this one */
238             for (j = 0; j < im.num_used; j++)
239             {
240                 if (!strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[i], &im.mi[j]))
241                 {
242                     dbg_printf("  \\-PE\t");
243                     module_print_info(&im.mi[j], TRUE);
244                 }
245             }
246         }
247         else
248         {
249             /* check module is not embedded in another module */
250             for (j = 0; j < im.num_used; j++) 
251             {
252                 if (strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[j], &im.mi[i]))
253                     break;
254             }
255             if (j < im.num_used) continue;
256             if (strstr(im.mi[i].ModuleName, ".so") || strchr(im.mi[i].ModuleName, '<'))
257                 dbg_printf("ELF\t");
258             else
259                 dbg_printf("PE\t");
260             module_print_info(&im.mi[i], FALSE);
261         }
262         num_printed++;
263     }
264     HeapFree(GetProcessHeap(), 0, im.mi);
265
266     if (base && !num_printed)
267         dbg_printf("'0x%x%08x' is not a valid module address\n", (DWORD)(base >> 32), (DWORD)base);
268 }
269
270 struct class_walker
271 {
272     ATOM*       table;
273     int         used;
274     int         alloc;
275 };
276
277 static void class_walker(HWND hWnd, struct class_walker* cw)
278 {
279     char        clsName[128];
280     int         i;
281     ATOM        atom;
282     HWND        child;
283
284     if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
285         return;
286     if ((atom = FindAtomA(clsName)) == 0)
287         return;
288
289     for (i = 0; i < cw->used; i++)
290     {
291         if (cw->table[i] == atom)
292             break;
293     }
294     if (i == cw->used)
295     {
296         if (cw->used >= cw->alloc)
297         {
298             cw->alloc += 16;
299             cw->table = dbg_heap_realloc(cw->table, cw->alloc * sizeof(ATOM));
300         }
301         cw->table[cw->used++] = atom;
302         info_win32_class(hWnd, clsName);
303     }
304     do
305     {
306         if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
307             class_walker(child, cw);
308     } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
309 }
310
311 void info_win32_class(HWND hWnd, const char* name)
312 {
313     WNDCLASSEXA wca;
314     HINSTANCE   hInst = hWnd ? (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE) : 0;
315
316     if (!name)
317     {
318         struct class_walker cw;
319
320         cw.table = NULL;
321         cw.used = cw.alloc = 0;
322         class_walker(GetDesktopWindow(), &cw);
323         HeapFree(GetProcessHeap(), 0, cw.table);
324         return;
325     }
326
327     if (!GetClassInfoExA(hInst, name, &wca))
328     {
329         dbg_printf("Cannot find class '%s'\n", name);
330         return;
331     }
332
333     dbg_printf("Class '%s':\n", name);
334     dbg_printf("style=0x%08x  wndProc=%p\n"
335                "inst=%p  icon=%p  cursor=%p  bkgnd=%p\n"
336                "clsExtra=%d  winExtra=%d\n",
337                wca.style, wca.lpfnWndProc, wca.hInstance,
338                wca.hIcon, wca.hCursor, wca.hbrBackground,
339                wca.cbClsExtra, wca.cbWndExtra);
340
341     if (hWnd && wca.cbClsExtra)
342     {
343         int             i;
344         WORD            w;
345
346         dbg_printf("Extra bytes:");
347         for (i = 0; i < wca.cbClsExtra / 2; i++)
348         {
349             w = GetClassWord(hWnd, i * 2);
350             /* FIXME: depends on i386 endian-ity */
351             dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
352         }
353         dbg_printf("\n");
354     }
355     dbg_printf("\n");
356     /* FIXME:
357      * + print #windows (or even list of windows...)
358      * + print extra bytes => this requires a window handle on this very class...
359      */
360 }
361
362 static void info_window(HWND hWnd, int indent)
363 {
364     char        clsName[128];
365     char        wndName[128];
366     HWND        child;
367
368     do
369     {
370         if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
371             strcpy(clsName, "-- Unknown --");
372         if (!GetWindowTextA(hWnd, wndName, sizeof(wndName)))
373             strcpy(wndName, "-- Empty --");
374
375         dbg_printf("%*s%08lx%*s %-17.17s %08x %0*lx %08x %.14s\n",
376                    indent, "", (DWORD_PTR)hWnd, 12 - indent, "",
377                    clsName, GetWindowLongW(hWnd, GWL_STYLE),
378                    ADDRWIDTH, (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_WNDPROC),
379                    GetWindowThreadProcessId(hWnd, NULL), wndName);
380
381         if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
382             info_window(child, indent + 1);
383     } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
384 }
385
386 void info_win32_window(HWND hWnd, BOOL detailed)
387 {
388     char        clsName[128];
389     char        wndName[128];
390     RECT        clientRect;
391     RECT        windowRect;
392     WORD        w;
393
394     if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
395
396     if (!detailed)
397     {
398         dbg_printf("%-20.20s %-17.17s %-8.8s %-*.*s %-8.8s %s\n",
399                    "Window handle", "Class Name", "Style",
400                    ADDRWIDTH, ADDRWIDTH, "WndProc", "Thread", "Text");
401         info_window(hWnd, 0);
402         return;
403     }
404
405     if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
406         strcpy(clsName, "-- Unknown --");
407     if (!GetWindowTextA(hWnd, wndName, sizeof(wndName)))
408         strcpy(wndName, "-- Empty --");
409     if (!GetClientRect(hWnd, &clientRect) || 
410         !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
411         SetRectEmpty(&clientRect);
412     if (!GetWindowRect(hWnd, &windowRect))
413         SetRectEmpty(&windowRect);
414
415     /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
416     dbg_printf("next=%p  child=%p  parent=%p  owner=%p  class='%s'\n"
417                "inst=%p  active=%p  idmenu=%08lx\n"
418                "style=0x%08x  exstyle=0x%08x  wndproc=%p  text='%s'\n"
419                "client=%d,%d-%d,%d  window=%d,%d-%d,%d sysmenu=%p\n",
420                GetWindow(hWnd, GW_HWNDNEXT),
421                GetWindow(hWnd, GW_CHILD),
422                GetParent(hWnd),
423                GetWindow(hWnd, GW_OWNER),
424                clsName,
425                (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE),
426                GetLastActivePopup(hWnd),
427                (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_ID),
428                GetWindowLongW(hWnd, GWL_STYLE),
429                GetWindowLongW(hWnd, GWL_EXSTYLE),
430                (void*)GetWindowLongPtrW(hWnd, GWLP_WNDPROC),
431                wndName,
432                clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
433                windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
434                GetSystemMenu(hWnd, FALSE));
435
436     if (GetClassLongW(hWnd, GCL_CBWNDEXTRA))
437     {
438         UINT i;
439
440         dbg_printf("Extra bytes:");
441         for (i = 0; i < GetClassLongW(hWnd, GCL_CBWNDEXTRA) / 2; i++)
442         {
443             w = GetWindowWord(hWnd, i * 2);
444             /* FIXME: depends on i386 endian-ity */
445             dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
446         }
447         dbg_printf("\n");
448     }
449     dbg_printf("\n");
450 }
451
452 struct dump_proc_entry
453 {
454     PROCESSENTRY32         proc;
455     unsigned               children; /* index in dump_proc.entries of first child */
456     unsigned               sibling;  /* index in dump_proc.entries of next sibling */
457 };
458
459 struct dump_proc
460 {
461     struct dump_proc_entry*entries;
462     unsigned               count;
463     unsigned               alloc;
464 };
465
466 static unsigned get_parent(const struct dump_proc* dp, unsigned idx)
467 {
468     unsigned i;
469
470     for (i = 0; i < dp->count; i++)
471     {
472         if (i != idx && dp->entries[i].proc.th32ProcessID == dp->entries[idx].proc.th32ParentProcessID)
473             return i;
474     }
475     return -1;
476 }
477
478 static void dump_proc_info(const struct dump_proc* dp, unsigned idx, unsigned depth)
479 {
480     struct dump_proc_entry* dpe;
481     for ( ; idx != -1; idx = dp->entries[idx].sibling)
482     {
483         assert(idx < dp->count);
484         dpe = &dp->entries[idx];
485         dbg_printf("%c%08x %-8d ",
486                    (dpe->proc.th32ProcessID == (dbg_curr_process ?
487                                                 dbg_curr_process->pid : 0)) ? '>' : ' ',
488                    dpe->proc.th32ProcessID, dpe->proc.cntThreads);
489         if (depth)
490         {
491             unsigned i;
492             for (i = 3 * (depth - 1); i > 0; i--) dbg_printf(" ");
493             dbg_printf("\\_ ");
494         }
495         dbg_printf("'%s'\n", dpe->proc.szExeFile);
496         dump_proc_info(dp, dpe->children, depth + 1);
497     }
498 }
499
500 void info_win32_processes(void)
501 {
502     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
503     if (snap != INVALID_HANDLE_VALUE)
504     {
505         struct dump_proc  dp;
506         unsigned          i, first = -1;
507         BOOL              ok;
508
509         dp.count   = 0;
510         dp.alloc   = 16;
511         dp.entries = HeapAlloc(GetProcessHeap(), 0, sizeof(*dp.entries) * dp.alloc);
512         if (!dp.entries)
513         {
514              CloseHandle(snap);
515              return;
516         }
517         dp.entries[dp.count].proc.dwSize = sizeof(dp.entries[dp.count].proc);
518         ok = Process32First(snap, &dp.entries[dp.count].proc);
519
520         /* fetch all process information into dp (skipping this debugger) */
521         while (ok)
522         {
523             if (dp.entries[dp.count].proc.th32ProcessID != GetCurrentProcessId())
524                 dp.entries[dp.count++].children = -1;
525             if (dp.count >= dp.alloc)
526             {
527                 dp.entries = HeapReAlloc(GetProcessHeap(), 0, dp.entries, sizeof(*dp.entries) * (dp.alloc *= 2));
528                 if (!dp.entries) return;
529             }
530             dp.entries[dp.count].proc.dwSize = sizeof(dp.entries[dp.count].proc);
531             ok = Process32Next(snap, &dp.entries[dp.count].proc);
532         }
533         CloseHandle(snap);
534         /* chain the siblings wrt. their parent */
535         for (i = 0; i < dp.count; i++)
536         {
537             unsigned parent = get_parent(&dp, i);
538             unsigned *chain = parent == -1 ? &first : &dp.entries[parent].children;
539             dp.entries[i].sibling = *chain;
540             *chain = i;
541         }
542         dbg_printf(" %-8.8s %-8.8s %s (all id:s are in hex)\n", "pid", "threads", "executable");
543         dump_proc_info(&dp, first, 0);
544         HeapFree(GetProcessHeap(), 0, dp.entries);
545     }
546 }
547
548 static BOOL get_process_name(DWORD pid, PROCESSENTRY32* entry)
549 {
550     BOOL   ret = FALSE;
551     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
552
553     if (snap != INVALID_HANDLE_VALUE)
554     {
555         entry->dwSize = sizeof(*entry);
556         if (Process32First(snap, entry))
557             while (!(ret = (entry->th32ProcessID == pid)) &&
558                    Process32Next(snap, entry));
559         CloseHandle(snap);
560     }
561     return ret;
562 }
563
564 void info_win32_threads(void)
565 {
566     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
567     if (snap != INVALID_HANDLE_VALUE)
568     {
569         THREADENTRY32   entry;
570         BOOL            ok;
571         DWORD           lastProcessId = 0;
572
573         entry.dwSize = sizeof(entry);
574         ok = Thread32First(snap, &entry);
575
576         dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
577                    "process", "tid", "prio");
578         while (ok)
579         {
580             if (entry.th32OwnerProcessID != GetCurrentProcessId())
581             {
582                 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
583                  * listed sequentially, which is not specified in the doc (Wine's implementation
584                  * does it)
585                  */
586                 if (entry.th32OwnerProcessID != lastProcessId)
587                 {
588                     struct dbg_process* p = dbg_get_process(entry.th32OwnerProcessID);
589                     PROCESSENTRY32 pcs_entry;
590                     const char* exename;
591
592                     if (p)
593                         exename = dbg_W2A(p->imageName, -1);
594                     else if (get_process_name(entry.th32OwnerProcessID, &pcs_entry))
595                         exename = pcs_entry.szExeFile;
596                     else
597                         exename = "";
598
599                     dbg_printf("%08x%s %s\n",
600                                entry.th32OwnerProcessID, p ? " (D)" : "", exename);
601                     lastProcessId = entry.th32OwnerProcessID;
602                 }
603                 dbg_printf("\t%08x %4d%s\n",
604                            entry.th32ThreadID, entry.tpBasePri,
605                            (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
606
607             }
608             ok = Thread32Next(snap, &entry);
609         }
610
611         CloseHandle(snap);
612     }
613 }
614
615 /***********************************************************************
616  *           info_win32_frame_exceptions
617  *
618  * Get info on the exception frames of a given thread.
619  */
620 void info_win32_frame_exceptions(DWORD tid)
621 {
622     struct dbg_thread*  thread;
623     void*               next_frame;
624
625     if (!dbg_curr_process || !dbg_curr_thread)
626     {
627         dbg_printf("Cannot get info on exceptions while no process is loaded\n");
628         return;
629     }
630
631     dbg_printf("Exception frames:\n");
632
633     if (tid == dbg_curr_tid) thread = dbg_curr_thread;
634     else
635     {
636         thread = dbg_get_thread(dbg_curr_process, tid);
637
638         if (!thread)
639         {
640             dbg_printf("Unknown thread id (%04x) in current process\n", tid);
641             return;
642         }
643         if (SuspendThread(thread->handle) == -1)
644         {
645             dbg_printf("Can't suspend thread id (%04x)\n", tid);
646             return;
647         }
648     }
649
650     if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
651     {
652         dbg_printf("Can't read TEB:except_frame\n");
653         return;
654     }
655
656     while (next_frame != (void*)-1)
657     {
658         EXCEPTION_REGISTRATION_RECORD frame;
659
660         dbg_printf("%p: ", next_frame);
661         if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
662         {
663             dbg_printf("Invalid frame address\n");
664             break;
665         }
666         dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
667         next_frame = frame.Prev;
668     }
669
670     if (tid != dbg_curr_tid) ResumeThread(thread->handle);
671 }
672
673 void info_win32_segments(DWORD start, int length)
674 {
675     char        flags[3];
676     DWORD       i;
677     LDT_ENTRY   le;
678
679     if (length == -1) length = (8192 - start);
680
681     for (i = start; i < start + length; i++)
682     {
683         if (!dbg_curr_process->process_io->get_selector(dbg_curr_thread->handle, (i << 3) | 7, &le))
684             continue;
685
686         if (le.HighWord.Bits.Type & 0x08)
687         {
688             flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
689             flags[1] = '-';
690             flags[2] = 'x';
691         }
692         else
693         {
694             flags[0] = 'r';
695             flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
696             flags[2] = '-';
697         }
698         dbg_printf("%04x: sel=%04x base=%08x limit=%08x %d-bit %c%c%c\n",
699                    i, (i << 3) | 7,
700                    (le.HighWord.Bits.BaseHi << 24) +
701                    (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
702                    ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
703                    (le.HighWord.Bits.Granularity ? 12 : 0),
704                    le.HighWord.Bits.Default_Big ? 32 : 16,
705                    flags[0], flags[1], flags[2]);
706     }
707 }
708
709 void info_win32_virtual(DWORD pid)
710 {
711     MEMORY_BASIC_INFORMATION    mbi;
712     char*                       addr = 0;
713     const char*                 state;
714     const char*                 type;
715     char                        prot[3+1];
716     HANDLE                      hProc;
717
718     if (pid == dbg_curr_pid)
719     {
720         if (dbg_curr_process == NULL)
721         {
722             dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
723             return;
724         }
725         hProc = dbg_curr_process->handle;
726     }
727     else
728     {
729         hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
730         if (hProc == NULL)
731         {
732             dbg_printf("Cannot open process <%04x>\n", pid);
733             return;
734         }
735     }
736
737     dbg_printf("Address  End      State   Type    RWX\n");
738
739     while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
740     {
741         switch (mbi.State)
742         {
743         case MEM_COMMIT:        state = "commit "; break;
744         case MEM_FREE:          state = "free   "; break;
745         case MEM_RESERVE:       state = "reserve"; break;
746         default:                state = "???    "; break;
747         }
748         if (mbi.State != MEM_FREE)
749         {
750             switch (mbi.Type)
751             {
752             case MEM_IMAGE:         type = "image  "; break;
753             case MEM_MAPPED:        type = "mapped "; break;
754             case MEM_PRIVATE:       type = "private"; break;
755             case 0:                 type = "       "; break;
756             default:                type = "???    "; break;
757             }
758             memset(prot, ' ' , sizeof(prot) - 1);
759             prot[sizeof(prot) - 1] = '\0';
760             if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
761                 prot[0] = 'R';
762             if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
763                 prot[1] = 'W';
764             if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
765                 prot[1] = 'C';
766             if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
767                 prot[2] = 'X';
768         }
769         else
770         {
771             type = "";
772             prot[0] = '\0';
773         }
774         dbg_printf("%08lx %08lx %s %s %s\n",
775                    (DWORD_PTR)addr, (DWORD_PTR)addr + mbi.RegionSize - 1, state, type, prot);
776         if (addr + mbi.RegionSize < addr) /* wrap around ? */
777             break;
778         addr += mbi.RegionSize;
779     }
780     if (pid != dbg_curr_pid) CloseHandle(hProc);
781 }
782
783 void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
784 {
785     struct dbg_lvalue           lvalue;
786     struct __wine_debug_channel channel;
787     unsigned char               mask;
788     int                         done = 0;
789     BOOL                        bAll;
790     void*                       addr;
791
792     if (!dbg_curr_process || !dbg_curr_thread)
793     {
794         dbg_printf("Cannot set/get debug channels while no process is loaded\n");
795         return;
796     }
797
798     if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
799     {
800         return;
801     }
802     addr = memory_to_linear_addr(&lvalue.addr);
803
804     if (!cls)                          mask = ~0;
805     else if (!strcmp(cls, "fixme"))    mask = (1 << __WINE_DBCL_FIXME);
806     else if (!strcmp(cls, "err"))      mask = (1 << __WINE_DBCL_ERR);
807     else if (!strcmp(cls, "warn"))     mask = (1 << __WINE_DBCL_WARN);
808     else if (!strcmp(cls, "trace"))    mask = (1 << __WINE_DBCL_TRACE);
809     else
810     {
811         dbg_printf("Unknown debug class %s\n", cls);
812         return;
813     }
814
815     bAll = !strcmp("all", name);
816     while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
817     {
818         if (!channel.name[0]) break;
819         if (bAll || !strcmp( channel.name, name ))
820         {
821             if (turn_on) channel.flags |= mask;
822             else channel.flags &= ~mask;
823             if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
824         }
825         addr = (struct __wine_debug_channel *)addr + 1;
826     }
827     if (!done) dbg_printf("Unable to find debug channel %s\n", name);
828     else WINE_TRACE("Changed %d channel instances\n", done);
829 }
830
831 void info_win32_exception(void)
832 {
833     const EXCEPTION_RECORD*     rec;
834     ADDRESS64                   addr;
835     char                        hexbuf[MAX_OFFSET_TO_STR_LEN];
836
837     if (!dbg_curr_thread->in_exception)
838     {
839         dbg_printf("Thread isn't in an exception\n");
840         return;
841     }
842     rec = &dbg_curr_thread->excpt_record;
843     memory_get_current_pc(&addr);
844
845     /* print some infos */
846     dbg_printf("%s: ",
847                dbg_curr_thread->first_chance ? "First chance exception" : "Unhandled exception");
848     switch (rec->ExceptionCode)
849     {
850     case EXCEPTION_BREAKPOINT:
851         dbg_printf("breakpoint");
852         break;
853     case EXCEPTION_SINGLE_STEP:
854         dbg_printf("single step");
855         break;
856     case EXCEPTION_INT_DIVIDE_BY_ZERO:
857         dbg_printf("divide by zero");
858         break;
859     case EXCEPTION_INT_OVERFLOW:
860         dbg_printf("overflow");
861         break;
862     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
863         dbg_printf("array bounds");
864         break;
865     case EXCEPTION_ILLEGAL_INSTRUCTION:
866         dbg_printf("illegal instruction");
867         break;
868     case EXCEPTION_STACK_OVERFLOW:
869         dbg_printf("stack overflow");
870         break;
871     case EXCEPTION_PRIV_INSTRUCTION:
872         dbg_printf("privileged instruction");
873         break;
874     case EXCEPTION_ACCESS_VIOLATION:
875         if (rec->NumberParameters == 2)
876             dbg_printf("page fault on %s access to 0x%08lx",
877                        rec->ExceptionInformation[0] == EXCEPTION_WRITE_FAULT ? "write" :
878                        rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT ? "execute" : "read",
879                        rec->ExceptionInformation[1]);
880         else
881             dbg_printf("page fault");
882         break;
883     case EXCEPTION_DATATYPE_MISALIGNMENT:
884         dbg_printf("Alignment");
885         break;
886     case DBG_CONTROL_C:
887         dbg_printf("^C");
888         break;
889     case CONTROL_C_EXIT:
890         dbg_printf("^C");
891         break;
892     case STATUS_POSSIBLE_DEADLOCK:
893         {
894             ADDRESS64       recaddr;
895
896             recaddr.Mode   = AddrModeFlat;
897             recaddr.Offset = rec->ExceptionInformation[0];
898
899             dbg_printf("wait failed on critical section ");
900             print_address(&recaddr, FALSE);
901         }
902         break;
903     case EXCEPTION_WINE_STUB:
904         {
905             char dll[32], name[256];
906             memory_get_string(dbg_curr_process,
907                               (void*)rec->ExceptionInformation[0], TRUE, FALSE,
908                               dll, sizeof(dll));
909             if (HIWORD(rec->ExceptionInformation[1]))
910                 memory_get_string(dbg_curr_process,
911                                   (void*)rec->ExceptionInformation[1], TRUE, FALSE,
912                                   name, sizeof(name));
913             else
914                 sprintf( name, "%ld", rec->ExceptionInformation[1] );
915             dbg_printf("unimplemented function %s.%s called", dll, name);
916         }
917         break;
918     case EXCEPTION_WINE_ASSERTION:
919         dbg_printf("assertion failed");
920         break;
921     case EXCEPTION_VM86_INTx:
922         dbg_printf("interrupt %02lx in vm86 mode", rec->ExceptionInformation[0]);
923         break;
924     case EXCEPTION_VM86_STI:
925         dbg_printf("sti in vm86 mode");
926         break;
927     case EXCEPTION_VM86_PICRETURN:
928         dbg_printf("PIC return in vm86 mode");
929         break;
930     case EXCEPTION_FLT_DENORMAL_OPERAND:
931         dbg_printf("denormal float operand");
932         break;
933     case EXCEPTION_FLT_DIVIDE_BY_ZERO:
934         dbg_printf("divide by zero");
935         break;
936     case EXCEPTION_FLT_INEXACT_RESULT:
937         dbg_printf("inexact float result");
938         break;
939     case EXCEPTION_FLT_INVALID_OPERATION:
940         dbg_printf("invalid float operation");
941         break;
942     case EXCEPTION_FLT_OVERFLOW:
943         dbg_printf("floating point overflow");
944         break;
945     case EXCEPTION_FLT_UNDERFLOW:
946         dbg_printf("floating point underflow");
947         break;
948     case EXCEPTION_FLT_STACK_CHECK:
949         dbg_printf("floating point stack check");
950         break;
951     case CXX_EXCEPTION:
952         if(rec->NumberParameters == 3 && rec->ExceptionInformation[0] == CXX_FRAME_MAGIC)
953             dbg_printf("C++ exception(object = 0x%08lx, type = 0x%08lx)",
954                        rec->ExceptionInformation[1], rec->ExceptionInformation[2]);
955         else
956             dbg_printf("C++ exception with strange parameter count %d or magic 0x%08lx",
957                        rec->NumberParameters, rec->ExceptionInformation[0]);
958         break;
959     default:
960         dbg_printf("0x%08x", rec->ExceptionCode);
961         break;
962     }
963     if (rec->ExceptionFlags & EH_STACK_INVALID)
964         dbg_printf(", invalid program stack");
965
966     switch (addr.Mode)
967     {
968     case AddrModeFlat:
969         dbg_printf(" in %d-bit code (%s)",
970                    be_cpu->pointer_size * 8,
971                    memory_offset_to_string(hexbuf, addr.Offset, 0));
972         break;
973     case AddrModeReal:
974         dbg_printf(" in vm86 code (%04x:%04x)", addr.Segment, (unsigned) addr.Offset);
975         break;
976     case AddrMode1616:
977         dbg_printf(" in 16-bit code (%04x:%04x)", addr.Segment, (unsigned) addr.Offset);
978         break;
979     case AddrMode1632:
980         dbg_printf(" in segmented 32-bit code (%04x:%08lx)", addr.Segment, (unsigned long) addr.Offset);
981         break;
982     default: dbg_printf(" bad address");
983     }
984     dbg_printf(".\n");
985 }