ole32: Fix some leaks (coverity).
[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 void info_win32_processes(void)
453 {
454     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
455     if (snap != INVALID_HANDLE_VALUE)
456     {
457         PROCESSENTRY32  entry;
458         DWORD           current = dbg_curr_process ? dbg_curr_process->pid : 0;
459         BOOL            ok;
460
461         entry.dwSize = sizeof(entry);
462         ok = Process32First(snap, &entry);
463
464         dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
465                    "pid", "threads", "parent", "executable");
466         while (ok)
467         {
468             if (entry.th32ProcessID != GetCurrentProcessId())
469                 dbg_printf("%c%08x %-8d %08x '%s'\n",
470                            (entry.th32ProcessID == current) ? '>' : ' ',
471                            entry.th32ProcessID, entry.cntThreads,
472                            entry.th32ParentProcessID, entry.szExeFile);
473             ok = Process32Next(snap, &entry);
474         }
475         CloseHandle(snap);
476     }
477 }
478
479 static BOOL get_process_name(DWORD pid, PROCESSENTRY32* entry)
480 {
481     BOOL   ret = FALSE;
482     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
483
484     if (snap != INVALID_HANDLE_VALUE)
485     {
486         entry->dwSize = sizeof(*entry);
487         if (Process32First(snap, entry))
488             while (!(ret = (entry->th32ProcessID == pid)) &&
489                    Process32Next(snap, entry));
490         CloseHandle(snap);
491     }
492     return ret;
493 }
494
495 void info_win32_threads(void)
496 {
497     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
498     if (snap != INVALID_HANDLE_VALUE)
499     {
500         THREADENTRY32   entry;
501         BOOL            ok;
502         DWORD           lastProcessId = 0;
503
504         entry.dwSize = sizeof(entry);
505         ok = Thread32First(snap, &entry);
506
507         dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
508                    "process", "tid", "prio");
509         while (ok)
510         {
511             if (entry.th32OwnerProcessID != GetCurrentProcessId())
512             {
513                 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
514                  * listed sequentially, which is not specified in the doc (Wine's implementation
515                  * does it)
516                  */
517                 if (entry.th32OwnerProcessID != lastProcessId)
518                 {
519                     struct dbg_process* p = dbg_get_process(entry.th32OwnerProcessID);
520                     PROCESSENTRY32 pcs_entry;
521                     const char* exename;
522
523                     if (p)
524                         exename = dbg_W2A(p->imageName, -1);
525                     else if (get_process_name(entry.th32OwnerProcessID, &pcs_entry))
526                         exename = pcs_entry.szExeFile;
527                     else
528                         exename = "";
529
530                     dbg_printf("%08x%s %s\n",
531                                entry.th32OwnerProcessID, p ? " (D)" : "", exename);
532                     lastProcessId = entry.th32OwnerProcessID;
533                 }
534                 dbg_printf("\t%08x %4d%s\n",
535                            entry.th32ThreadID, entry.tpBasePri,
536                            (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
537
538             }
539             ok = Thread32Next(snap, &entry);
540         }
541
542         CloseHandle(snap);
543     }
544 }
545
546 /***********************************************************************
547  *           info_win32_frame_exceptions
548  *
549  * Get info on the exception frames of a given thread.
550  */
551 void info_win32_frame_exceptions(DWORD tid)
552 {
553     struct dbg_thread*  thread;
554     void*               next_frame;
555
556     if (!dbg_curr_process || !dbg_curr_thread)
557     {
558         dbg_printf("Cannot get info on exceptions while no process is loaded\n");
559         return;
560     }
561
562     dbg_printf("Exception frames:\n");
563
564     if (tid == dbg_curr_tid) thread = dbg_curr_thread;
565     else
566     {
567         thread = dbg_get_thread(dbg_curr_process, tid);
568
569         if (!thread)
570         {
571             dbg_printf("Unknown thread id (%04x) in current process\n", tid);
572             return;
573         }
574         if (SuspendThread(thread->handle) == -1)
575         {
576             dbg_printf("Can't suspend thread id (%04x)\n", tid);
577             return;
578         }
579     }
580
581     if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
582     {
583         dbg_printf("Can't read TEB:except_frame\n");
584         return;
585     }
586
587     while (next_frame != (void*)-1)
588     {
589         EXCEPTION_REGISTRATION_RECORD frame;
590
591         dbg_printf("%p: ", next_frame);
592         if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
593         {
594             dbg_printf("Invalid frame address\n");
595             break;
596         }
597         dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
598         next_frame = frame.Prev;
599     }
600
601     if (tid != dbg_curr_tid) ResumeThread(thread->handle);
602 }
603
604 void info_win32_segments(DWORD start, int length)
605 {
606     char        flags[3];
607     DWORD       i;
608     LDT_ENTRY   le;
609
610     if (length == -1) length = (8192 - start);
611
612     for (i = start; i < start + length; i++)
613     {
614         if (!dbg_curr_process->process_io->get_selector(dbg_curr_thread->handle, (i << 3) | 7, &le))
615             continue;
616
617         if (le.HighWord.Bits.Type & 0x08)
618         {
619             flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
620             flags[1] = '-';
621             flags[2] = 'x';
622         }
623         else
624         {
625             flags[0] = 'r';
626             flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
627             flags[2] = '-';
628         }
629         dbg_printf("%04x: sel=%04x base=%08x limit=%08x %d-bit %c%c%c\n",
630                    i, (i << 3) | 7,
631                    (le.HighWord.Bits.BaseHi << 24) +
632                    (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
633                    ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
634                    (le.HighWord.Bits.Granularity ? 12 : 0),
635                    le.HighWord.Bits.Default_Big ? 32 : 16,
636                    flags[0], flags[1], flags[2]);
637     }
638 }
639
640 void info_win32_virtual(DWORD pid)
641 {
642     MEMORY_BASIC_INFORMATION    mbi;
643     char*                       addr = 0;
644     const char*                 state;
645     const char*                 type;
646     char                        prot[3+1];
647     HANDLE                      hProc;
648
649     if (pid == dbg_curr_pid)
650     {
651         if (dbg_curr_process == NULL)
652         {
653             dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
654             return;
655         }
656         hProc = dbg_curr_process->handle;
657     }
658     else
659     {
660         hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
661         if (hProc == NULL)
662         {
663             dbg_printf("Cannot open process <%04x>\n", pid);
664             return;
665         }
666     }
667
668     dbg_printf("Address  End      State   Type    RWX\n");
669
670     while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
671     {
672         switch (mbi.State)
673         {
674         case MEM_COMMIT:        state = "commit "; break;
675         case MEM_FREE:          state = "free   "; break;
676         case MEM_RESERVE:       state = "reserve"; break;
677         default:                state = "???    "; break;
678         }
679         if (mbi.State != MEM_FREE)
680         {
681             switch (mbi.Type)
682             {
683             case MEM_IMAGE:         type = "image  "; break;
684             case MEM_MAPPED:        type = "mapped "; break;
685             case MEM_PRIVATE:       type = "private"; break;
686             case 0:                 type = "       "; break;
687             default:                type = "???    "; break;
688             }
689             memset(prot, ' ' , sizeof(prot) - 1);
690             prot[sizeof(prot) - 1] = '\0';
691             if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
692                 prot[0] = 'R';
693             if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
694                 prot[1] = 'W';
695             if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
696                 prot[1] = 'C';
697             if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
698                 prot[2] = 'X';
699         }
700         else
701         {
702             type = "";
703             prot[0] = '\0';
704         }
705         dbg_printf("%08lx %08lx %s %s %s\n",
706                    (DWORD_PTR)addr, (DWORD_PTR)addr + mbi.RegionSize - 1, state, type, prot);
707         if (addr + mbi.RegionSize < addr) /* wrap around ? */
708             break;
709         addr += mbi.RegionSize;
710     }
711     if (pid != dbg_curr_pid) CloseHandle(hProc);
712 }
713
714 void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
715 {
716     struct dbg_lvalue           lvalue;
717     struct __wine_debug_channel channel;
718     unsigned char               mask;
719     int                         done = 0;
720     BOOL                        bAll;
721     void*                       addr;
722
723     if (!dbg_curr_process || !dbg_curr_thread)
724     {
725         dbg_printf("Cannot set/get debug channels while no process is loaded\n");
726         return;
727     }
728
729     if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
730     {
731         return;
732     }
733     addr = memory_to_linear_addr(&lvalue.addr);
734
735     if (!cls)                          mask = ~0;
736     else if (!strcmp(cls, "fixme"))    mask = (1 << __WINE_DBCL_FIXME);
737     else if (!strcmp(cls, "err"))      mask = (1 << __WINE_DBCL_ERR);
738     else if (!strcmp(cls, "warn"))     mask = (1 << __WINE_DBCL_WARN);
739     else if (!strcmp(cls, "trace"))    mask = (1 << __WINE_DBCL_TRACE);
740     else
741     {
742         dbg_printf("Unknown debug class %s\n", cls);
743         return;
744     }
745
746     bAll = !strcmp("all", name);
747     while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
748     {
749         if (!channel.name[0]) break;
750         if (bAll || !strcmp( channel.name, name ))
751         {
752             if (turn_on) channel.flags |= mask;
753             else channel.flags &= ~mask;
754             if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
755         }
756         addr = (struct __wine_debug_channel *)addr + 1;
757     }
758     if (!done) dbg_printf("Unable to find debug channel %s\n", name);
759     else WINE_TRACE("Changed %d channel instances\n", done);
760 }
761
762 void info_win32_exception(void)
763 {
764     const EXCEPTION_RECORD*     rec;
765     ADDRESS64                   addr;
766     char                        hexbuf[MAX_OFFSET_TO_STR_LEN];
767
768     if (!dbg_curr_thread->in_exception)
769     {
770         dbg_printf("Thread isn't in an exception\n");
771         return;
772     }
773     rec = &dbg_curr_thread->excpt_record;
774     memory_get_current_pc(&addr);
775
776     /* print some infos */
777     dbg_printf("%s: ",
778                dbg_curr_thread->first_chance ? "First chance exception" : "Unhandled exception");
779     switch (rec->ExceptionCode)
780     {
781     case EXCEPTION_BREAKPOINT:
782         dbg_printf("breakpoint");
783         break;
784     case EXCEPTION_SINGLE_STEP:
785         dbg_printf("single step");
786         break;
787     case EXCEPTION_INT_DIVIDE_BY_ZERO:
788         dbg_printf("divide by zero");
789         break;
790     case EXCEPTION_INT_OVERFLOW:
791         dbg_printf("overflow");
792         break;
793     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
794         dbg_printf("array bounds");
795         break;
796     case EXCEPTION_ILLEGAL_INSTRUCTION:
797         dbg_printf("illegal instruction");
798         break;
799     case EXCEPTION_STACK_OVERFLOW:
800         dbg_printf("stack overflow");
801         break;
802     case EXCEPTION_PRIV_INSTRUCTION:
803         dbg_printf("privileged instruction");
804         break;
805     case EXCEPTION_ACCESS_VIOLATION:
806         if (rec->NumberParameters == 2)
807             dbg_printf("page fault on %s access to 0x%08lx",
808                        rec->ExceptionInformation[0] == EXCEPTION_WRITE_FAULT ? "write" :
809                        rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT ? "execute" : "read",
810                        rec->ExceptionInformation[1]);
811         else
812             dbg_printf("page fault");
813         break;
814     case EXCEPTION_DATATYPE_MISALIGNMENT:
815         dbg_printf("Alignment");
816         break;
817     case DBG_CONTROL_C:
818         dbg_printf("^C");
819         break;
820     case CONTROL_C_EXIT:
821         dbg_printf("^C");
822         break;
823     case STATUS_POSSIBLE_DEADLOCK:
824         {
825             ADDRESS64       recaddr;
826
827             recaddr.Mode   = AddrModeFlat;
828             recaddr.Offset = rec->ExceptionInformation[0];
829
830             dbg_printf("wait failed on critical section ");
831             print_address(&recaddr, FALSE);
832         }
833         break;
834     case EXCEPTION_WINE_STUB:
835         {
836             char dll[32], name[256];
837             memory_get_string(dbg_curr_process,
838                               (void*)rec->ExceptionInformation[0], TRUE, FALSE,
839                               dll, sizeof(dll));
840             if (HIWORD(rec->ExceptionInformation[1]))
841                 memory_get_string(dbg_curr_process,
842                                   (void*)rec->ExceptionInformation[1], TRUE, FALSE,
843                                   name, sizeof(name));
844             else
845                 sprintf( name, "%ld", rec->ExceptionInformation[1] );
846             dbg_printf("unimplemented function %s.%s called", dll, name);
847         }
848         break;
849     case EXCEPTION_WINE_ASSERTION:
850         dbg_printf("assertion failed");
851         break;
852     case EXCEPTION_VM86_INTx:
853         dbg_printf("interrupt %02lx in vm86 mode", rec->ExceptionInformation[0]);
854         break;
855     case EXCEPTION_VM86_STI:
856         dbg_printf("sti in vm86 mode");
857         break;
858     case EXCEPTION_VM86_PICRETURN:
859         dbg_printf("PIC return in vm86 mode");
860         break;
861     case EXCEPTION_FLT_DENORMAL_OPERAND:
862         dbg_printf("denormal float operand");
863         break;
864     case EXCEPTION_FLT_DIVIDE_BY_ZERO:
865         dbg_printf("divide by zero");
866         break;
867     case EXCEPTION_FLT_INEXACT_RESULT:
868         dbg_printf("inexact float result");
869         break;
870     case EXCEPTION_FLT_INVALID_OPERATION:
871         dbg_printf("invalid float operation");
872         break;
873     case EXCEPTION_FLT_OVERFLOW:
874         dbg_printf("floating point overflow");
875         break;
876     case EXCEPTION_FLT_UNDERFLOW:
877         dbg_printf("floating point underflow");
878         break;
879     case EXCEPTION_FLT_STACK_CHECK:
880         dbg_printf("floating point stack check");
881         break;
882     case CXX_EXCEPTION:
883         if(rec->NumberParameters == 3 && rec->ExceptionInformation[0] == CXX_FRAME_MAGIC)
884             dbg_printf("C++ exception(object = 0x%08lx, type = 0x%08lx)",
885                        rec->ExceptionInformation[1], rec->ExceptionInformation[2]);
886         else
887             dbg_printf("C++ exception with strange parameter count %d or magic 0x%08lx",
888                        rec->NumberParameters, rec->ExceptionInformation[0]);
889         break;
890     default:
891         dbg_printf("0x%08x", rec->ExceptionCode);
892         break;
893     }
894     if (rec->ExceptionFlags & EH_STACK_INVALID)
895         dbg_printf(", invalid program stack");
896
897     switch (addr.Mode)
898     {
899     case AddrModeFlat:
900         dbg_printf(" in %d-bit code (%s)",
901                    be_cpu->pointer_size * 8,
902                    memory_offset_to_string(hexbuf, addr.Offset, 0));
903         break;
904     case AddrModeReal:
905         dbg_printf(" in vm86 code (%04x:%04x)", addr.Segment, (unsigned) addr.Offset);
906         break;
907     case AddrMode1616:
908         dbg_printf(" in 16-bit code (%04x:%04x)", addr.Segment, (unsigned) addr.Offset);
909         break;
910     case AddrMode1632:
911         dbg_printf(" in segmented 32-bit code (%04x:%08lx)", addr.Segment, (unsigned long) addr.Offset);
912         break;
913     default: dbg_printf(" bad address");
914     }
915     dbg_printf(".\n");
916 }