dbghelp: Force 64bit module enumeration on all platforms.
[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
35 WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
36
37 /***********************************************************************
38  *           print_help
39  *
40  * Implementation of the 'help' command.
41  */
42 void print_help(void)
43 {
44     int i = 0;
45     static const char * const helptext[] =
46         {
47             "The commands accepted by the Wine debugger are a reasonable",
48             "subset of the commands that gdb accepts.",
49             "The commands currently are:",
50             "  help                                   quit",
51             "  break [*<addr>]                        watch *<addr>",
52             "  delete break bpnum                     disable bpnum",
53             "  enable bpnum                           condition <bpnum> [<expr>]",
54             "  finish                                 cont [N]",
55             "  step [N]                               next [N]",
56             "  stepi [N]                              nexti [N]",
57             "  x <addr>                               print <expr>",
58             "  display <expr>                         undisplay <disnum>",
59             "  local display <expr>                   delete display <disnum>",                  
60             "  enable display <disnum>                disable display <disnum>",
61             "  bt [<tid>|all]                         frame <n>",
62             "  up                                     down",
63             "  list <lines>                           disassemble [<addr>][,<addr>]",
64             "  show dir                               dir <path>",
65             "  set <reg> = <expr>                     set *<addr> = <expr>",
66             "  pass                                   whatis",
67             "  info (see 'help info' for options)",
68
69             "The 'x' command accepts repeat counts and formats (including 'i') in the",
70             "same way that gdb does.\n",
71
72             "The following are examples of legal expressions:",
73             " $eax     $eax+0x3   0x1000   ($eip + 256)  *$eax   *($esp + 3)",
74             " Also, a nm format symbol table can be read from a file using the",
75             " symbolfile command.", /*  Symbols can also be defined individually with",
76                                         " the define command.", */
77             "",
78             NULL
79         };
80
81     while (helptext[i]) dbg_printf("%s\n", helptext[i++]);
82 }
83
84
85 /***********************************************************************
86  *           info_help
87  *
88  * Implementation of the 'help info' command.
89  */
90 void info_help(void)
91 {
92     int i = 0;
93     static const char * const infotext[] =
94         {
95             "The info commands allow you to get assorted bits of interesting stuff",
96             "to be displayed.  The options are:",
97             "  info break           Displays information about breakpoints",
98             "  info class <name>    Displays information about window class <name>",
99             "  info display         Shows auto-display expressions in use",
100             "  info except <pid>    Shows exception handler chain (in a given process)",
101             "  info locals          Displays values of all local vars for current frame",
102             "  info maps <pid>      Shows virtual mappings (in a given process)",
103             "  info process         Shows all running processes",
104             "  info reg             Displays values of the general registers at top of stack",
105             "  info all-reg         Displays the general and floating point registers",
106             "  info segments <pid>  Displays information about all known segments",
107             "  info share           Displays all loaded modules",
108             "  info share <addr>    Displays internal module state",
109             "  info stack           Dumps information about top of stack",
110             "  info symbol <sym>    Displays information about a given symbol",
111             "  info thread          Shows all running threads",
112             "  info wnd <handle>    Displays internal window state",
113             "",
114             NULL
115         };
116
117     while (infotext[i]) dbg_printf("%s\n", infotext[i++]);
118 }
119
120 static const char* get_symtype_str(const IMAGEHLP_MODULE64* mi)
121 {
122     switch (mi->SymType)
123     {
124     default:
125     case SymNone:       return "--none--";
126     case SymCoff:       return "COFF";
127     case SymCv:         return "CodeView";
128     case SymPdb:        return "PDB";
129     case SymExport:     return "Export";
130     case SymDeferred:   return "Deferred";
131     case SymSym:        return "Sym";
132     case SymDia:
133         switch (mi->CVSig)
134         {
135         case 'S' | ('T' << 8) | ('A' << 16) | ('B' << 24):
136             return "Stabs";
137         case 'D' | ('W' << 8) | ('A' << 16) | ('R' << 24):
138             return "Dwarf";
139         default:
140             return "DIA";
141
142         }
143     }
144 }
145
146 struct info_module
147 {
148     IMAGEHLP_MODULE64*  mi;
149     unsigned            num_alloc;
150     unsigned            num_used;
151 };
152
153 static void module_print_info(const IMAGEHLP_MODULE64* mi, BOOL is_embedded)
154 {
155     dbg_printf("%8s-%8s\t%-16s%s\n",
156                wine_dbgstr_longlong(mi->BaseOfImage),
157                wine_dbgstr_longlong(mi->BaseOfImage + mi->ImageSize),
158                is_embedded ? "\\" : get_symtype_str(mi), mi->ModuleName);
159 }
160
161 static int      module_compare(const void* p1, const void* p2)
162 {
163     LONGLONG val = ((const IMAGEHLP_MODULE64*)p1)->BaseOfImage -
164         ((const IMAGEHLP_MODULE64*)p2)->BaseOfImage;
165     if (val < 0) return -1;
166     else if (val > 0) return 1;
167     else return 0;
168 }
169
170 static inline BOOL module_is_container(const IMAGEHLP_MODULE64* wmod_cntnr,
171                                        const IMAGEHLP_MODULE64* wmod_child)
172 {
173     return wmod_cntnr->BaseOfImage <= wmod_child->BaseOfImage &&
174         wmod_cntnr->BaseOfImage + wmod_cntnr->ImageSize >=
175         wmod_child->BaseOfImage + wmod_child->ImageSize;
176 }
177
178 static BOOL CALLBACK info_mod_cb(PCSTR mod_name, DWORD64 base, PVOID ctx)
179 {
180     struct info_module* im = ctx;
181
182     if (im->num_used + 1 > im->num_alloc)
183     {
184         im->num_alloc += 16;
185         im->mi = dbg_heap_realloc(im->mi, im->num_alloc * sizeof(*im->mi));
186     }
187     im->mi[im->num_used].SizeOfStruct = sizeof(im->mi[im->num_used]);
188     if (SymGetModuleInfo64(dbg_curr_process->handle, base, &im->mi[im->num_used]))
189     {
190         im->num_used++;
191     }   
192     return TRUE;
193 }
194
195 /***********************************************************************
196  *           info_win32_module
197  *
198  * Display information about a given module (DLL or EXE), or about all modules
199  */
200 void info_win32_module(DWORD64 base)
201 {
202     struct info_module  im;
203     UINT                i, j, num_printed = 0;
204     DWORD               opt;
205
206     if (!dbg_curr_process)
207     {
208         dbg_printf("Cannot get info on module while no process is loaded\n");
209         return;
210     }
211
212     im.mi = NULL;
213     im.num_alloc = im.num_used = 0;
214
215     /* this is a wine specific options to return also ELF modules in the
216      * enumeration
217      */
218     SymSetOptions((opt = SymGetOptions()) | 0x40000000);
219     SymEnumerateModules64(dbg_curr_process->handle, info_mod_cb, (void*)&im);
220     SymSetOptions(opt);
221
222     qsort(im.mi, im.num_used, sizeof(im.mi[0]), module_compare);
223
224     dbg_printf("Module\tAddress\t\t\tDebug info\tName (%d modules)\n", im.num_used);
225
226     for (i = 0; i < im.num_used; i++)
227     {
228         if (base && 
229             (base < im.mi[i].BaseOfImage || base >= im.mi[i].BaseOfImage + im.mi[i].ImageSize))
230             continue;
231         if (strstr(im.mi[i].ModuleName, "<elf>"))
232         {
233             dbg_printf("ELF\t");
234             module_print_info(&im.mi[i], FALSE);
235             /* print all modules embedded in this one */
236             for (j = 0; j < im.num_used; j++)
237             {
238                 if (!strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[i], &im.mi[j]))
239                 {
240                     dbg_printf("  \\-PE\t");
241                     module_print_info(&im.mi[j], TRUE);
242                 }
243             }
244         }
245         else
246         {
247             /* check module is not embedded in another module */
248             for (j = 0; j < im.num_used; j++) 
249             {
250                 if (strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[j], &im.mi[i]))
251                     break;
252             }
253             if (j < im.num_used) continue;
254             if (strstr(im.mi[i].ModuleName, ".so") || strchr(im.mi[i].ModuleName, '<'))
255                 dbg_printf("ELF\t");
256             else
257                 dbg_printf("PE\t");
258             module_print_info(&im.mi[i], FALSE);
259         }
260         num_printed++;
261     }
262     HeapFree(GetProcessHeap(), 0, im.mi);
263
264     if (base && !num_printed)
265         dbg_printf("'0x%x%08x' is not a valid module address\n", (DWORD)(base >> 32), (DWORD)base);
266 }
267
268 struct class_walker
269 {
270     ATOM*       table;
271     int         used;
272     int         alloc;
273 };
274
275 static void class_walker(HWND hWnd, struct class_walker* cw)
276 {
277     char        clsName[128];
278     int         i;
279     ATOM        atom;
280     HWND        child;
281
282     if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
283         return;
284     if ((atom = FindAtomA(clsName)) == 0)
285         return;
286
287     for (i = 0; i < cw->used; i++)
288     {
289         if (cw->table[i] == atom)
290             break;
291     }
292     if (i == cw->used)
293     {
294         if (cw->used >= cw->alloc)
295         {
296             cw->alloc += 16;
297             cw->table = dbg_heap_realloc(cw->table, cw->alloc * sizeof(ATOM));
298         }
299         cw->table[cw->used++] = atom;
300         info_win32_class(hWnd, clsName);
301     }
302     do
303     {
304         if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
305             class_walker(child, cw);
306     } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
307 }
308
309 void info_win32_class(HWND hWnd, const char* name)
310 {
311     WNDCLASSEXA wca;
312     HINSTANCE   hInst = hWnd ? (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE) : 0;
313
314     if (!name)
315     {
316         struct class_walker cw;
317
318         cw.table = NULL;
319         cw.used = cw.alloc = 0;
320         class_walker(GetDesktopWindow(), &cw);
321         HeapFree(GetProcessHeap(), 0, cw.table);
322         return;
323     }
324
325     if (!GetClassInfoExA(hInst, name, &wca))
326     {
327         dbg_printf("Cannot find class '%s'\n", name);
328         return;
329     }
330
331     dbg_printf("Class '%s':\n", name);
332     dbg_printf("style=0x%08x  wndProc=%p\n"
333                "inst=%p  icon=%p  cursor=%p  bkgnd=%p\n"
334                "clsExtra=%d  winExtra=%d\n",
335                wca.style, wca.lpfnWndProc, wca.hInstance,
336                wca.hIcon, wca.hCursor, wca.hbrBackground,
337                wca.cbClsExtra, wca.cbWndExtra);
338
339     if (hWnd && wca.cbClsExtra)
340     {
341         int             i;
342         WORD            w;
343
344         dbg_printf("Extra bytes:");
345         for (i = 0; i < wca.cbClsExtra / 2; i++)
346         {
347             w = GetClassWord(hWnd, i * 2);
348             /* FIXME: depends on i386 endian-ity */
349             dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
350         }
351         dbg_printf("\n");
352     }
353     dbg_printf("\n");
354     /* FIXME:
355      * + print #windows (or even list of windows...)
356      * + print extra bytes => this requires a window handle on this very class...
357      */
358 }
359
360 static void info_window(HWND hWnd, int indent)
361 {
362     char        clsName[128];
363     char        wndName[128];
364     HWND        child;
365
366     do
367     {
368         if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
369             strcpy(clsName, "-- Unknown --");
370         if (!GetWindowTextA(hWnd, wndName, sizeof(wndName)))
371             strcpy(wndName, "-- Empty --");
372
373         dbg_printf("%*s%08lx%*s %-17.17s %08x %08lx %08x %.14s\n",
374                    indent, "", (DWORD_PTR)hWnd, 12 - indent, "",
375                    clsName, GetWindowLongW(hWnd, GWL_STYLE),
376                    (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_WNDPROC),
377                    GetWindowThreadProcessId(hWnd, NULL), wndName);
378
379         if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
380             info_window(child, indent + 1);
381     } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
382 }
383
384 void info_win32_window(HWND hWnd, BOOL detailed)
385 {
386     char        clsName[128];
387     char        wndName[128];
388     RECT        clientRect;
389     RECT        windowRect;
390     WORD        w;
391
392     if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
393
394     if (!detailed)
395     {
396         dbg_printf("%-20.20s %-17.17s %-8.8s %-8.8s %-8.8s %s\n",
397                    "Window handle", "Class Name", "Style", "WndProc", "Thread", "Text");
398         info_window(hWnd, 0);
399         return;
400     }
401
402     if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
403         strcpy(clsName, "-- Unknown --");
404     if (!GetWindowTextA(hWnd, wndName, sizeof(wndName)))
405         strcpy(wndName, "-- Empty --");
406     if (!GetClientRect(hWnd, &clientRect) || 
407         !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
408         SetRectEmpty(&clientRect);
409     if (!GetWindowRect(hWnd, &windowRect))
410         SetRectEmpty(&windowRect);
411
412     /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
413     dbg_printf("next=%p  child=%p  parent=%p  owner=%p  class='%s'\n"
414                "inst=%p  active=%p  idmenu=%08lx\n"
415                "style=0x%08x  exstyle=0x%08x  wndproc=0x%08lx  text='%s'\n"
416                "client=%d,%d-%d,%d  window=%d,%d-%d,%d sysmenu=%p\n",
417                GetWindow(hWnd, GW_HWNDNEXT),
418                GetWindow(hWnd, GW_CHILD),
419                GetParent(hWnd),
420                GetWindow(hWnd, GW_OWNER),
421                clsName,
422                (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE),
423                GetLastActivePopup(hWnd),
424                (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_ID),
425                GetWindowLongW(hWnd, GWL_STYLE),
426                GetWindowLongW(hWnd, GWL_EXSTYLE),
427                (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_WNDPROC),
428                wndName,
429                clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
430                windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
431                GetSystemMenu(hWnd, FALSE));
432
433     if (GetClassLongW(hWnd, GCL_CBWNDEXTRA))
434     {
435         UINT i;
436
437         dbg_printf("Extra bytes:");
438         for (i = 0; i < GetClassLongW(hWnd, GCL_CBWNDEXTRA) / 2; i++)
439         {
440             w = GetWindowWord(hWnd, i * 2);
441             /* FIXME: depends on i386 endian-ity */
442             dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
443         }
444         dbg_printf("\n");
445     }
446     dbg_printf("\n");
447 }
448
449 void info_win32_processes(void)
450 {
451     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
452     if (snap != INVALID_HANDLE_VALUE)
453     {
454         PROCESSENTRY32  entry;
455         DWORD           current = dbg_curr_process ? dbg_curr_process->pid : 0;
456         BOOL            ok;
457
458         entry.dwSize = sizeof(entry);
459         ok = Process32First(snap, &entry);
460
461         dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
462                    "pid", "threads", "parent", "executable");
463         while (ok)
464         {
465             if (entry.th32ProcessID != GetCurrentProcessId())
466                 dbg_printf("%c%08x %-8d %08x '%s'\n",
467                            (entry.th32ProcessID == current) ? '>' : ' ',
468                            entry.th32ProcessID, entry.cntThreads,
469                            entry.th32ParentProcessID, entry.szExeFile);
470             ok = Process32Next(snap, &entry);
471         }
472         CloseHandle(snap);
473     }
474 }
475
476 void info_win32_threads(void)
477 {
478     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
479     if (snap != INVALID_HANDLE_VALUE)
480     {
481         THREADENTRY32   entry;
482         BOOL            ok;
483         DWORD           lastProcessId = 0;
484
485         entry.dwSize = sizeof(entry);
486         ok = Thread32First(snap, &entry);
487
488         dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
489                    "process", "tid", "prio");
490         while (ok)
491         {
492             if (entry.th32OwnerProcessID != GetCurrentProcessId())
493             {
494                 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
495                  * listed sequentially, which is not specified in the doc (Wine's implementation
496                  * does it)
497                  */
498                 if (entry.th32OwnerProcessID != lastProcessId)
499                 {
500                     struct dbg_process* p = dbg_get_process(entry.th32OwnerProcessID);
501
502                     dbg_printf("%08x%s %s\n",
503                                entry.th32OwnerProcessID, p ? " (D)" : "",
504                                p ? dbg_W2A(p->imageName, -1) : "");
505                     lastProcessId = entry.th32OwnerProcessID;
506                 }
507                 dbg_printf("\t%08x %4d%s\n",
508                            entry.th32ThreadID, entry.tpBasePri,
509                            (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
510
511             }
512             ok = Thread32Next(snap, &entry);
513         }
514
515         CloseHandle(snap);
516     }
517 }
518
519 /***********************************************************************
520  *           info_win32_exceptions
521  *
522  * Get info on the exception frames of a given thread.
523  */
524 void info_win32_exceptions(DWORD tid)
525 {
526     struct dbg_thread*  thread;
527     void*               next_frame;
528
529     if (!dbg_curr_process || !dbg_curr_thread)
530     {
531         dbg_printf("Cannot get info on exceptions while no process is loaded\n");
532         return;
533     }
534
535     dbg_printf("Exception frames:\n");
536
537     if (tid == dbg_curr_tid) thread = dbg_curr_thread;
538     else
539     {
540         thread = dbg_get_thread(dbg_curr_process, tid);
541
542         if (!thread)
543         {
544             dbg_printf("Unknown thread id (%04x) in current process\n", tid);
545             return;
546         }
547         if (SuspendThread(thread->handle) == -1)
548         {
549             dbg_printf("Can't suspend thread id (%04x)\n", tid);
550             return;
551         }
552     }
553
554     if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
555     {
556         dbg_printf("Can't read TEB:except_frame\n");
557         return;
558     }
559
560     while (next_frame != (void*)-1)
561     {
562         EXCEPTION_REGISTRATION_RECORD frame;
563
564         dbg_printf("%p: ", next_frame);
565         if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
566         {
567             dbg_printf("Invalid frame address\n");
568             break;
569         }
570         dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
571         next_frame = frame.Prev;
572     }
573
574     if (tid != dbg_curr_tid) ResumeThread(thread->handle);
575 }
576
577 void info_win32_segments(DWORD start, int length)
578 {
579     char        flags[3];
580     DWORD       i;
581     LDT_ENTRY   le;
582
583     if (length == -1) length = (8192 - start);
584
585     for (i = start; i < start + length; i++)
586     {
587         if (!dbg_curr_process->process_io->get_selector(dbg_curr_thread->handle, (i << 3) | 7, &le))
588             continue;
589
590         if (le.HighWord.Bits.Type & 0x08)
591         {
592             flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
593             flags[1] = '-';
594             flags[2] = 'x';
595         }
596         else
597         {
598             flags[0] = 'r';
599             flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
600             flags[2] = '-';
601         }
602         dbg_printf("%04x: sel=%04x base=%08x limit=%08x %d-bit %c%c%c\n",
603                    i, (i << 3) | 7,
604                    (le.HighWord.Bits.BaseHi << 24) +
605                    (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
606                    ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
607                    (le.HighWord.Bits.Granularity ? 12 : 0),
608                    le.HighWord.Bits.Default_Big ? 32 : 16,
609                    flags[0], flags[1], flags[2]);
610     }
611 }
612
613 void info_win32_virtual(DWORD pid)
614 {
615     MEMORY_BASIC_INFORMATION    mbi;
616     char*                       addr = 0;
617     const char*                 state;
618     const char*                 type;
619     char                        prot[3+1];
620     HANDLE                      hProc;
621
622     if (pid == dbg_curr_pid)
623     {
624         if (dbg_curr_process == NULL)
625         {
626             dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
627             return;
628         }
629         hProc = dbg_curr_process->handle;
630     }
631     else
632     {
633         hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
634         if (hProc == NULL)
635         {
636             dbg_printf("Cannot open process <%04x>\n", pid);
637             return;
638         }
639     }
640
641     dbg_printf("Address  End      State   Type    RWX\n");
642
643     while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
644     {
645         switch (mbi.State)
646         {
647         case MEM_COMMIT:        state = "commit "; break;
648         case MEM_FREE:          state = "free   "; break;
649         case MEM_RESERVE:       state = "reserve"; break;
650         default:                state = "???    "; break;
651         }
652         if (mbi.State != MEM_FREE)
653         {
654             switch (mbi.Type)
655             {
656             case MEM_IMAGE:         type = "image  "; break;
657             case MEM_MAPPED:        type = "mapped "; break;
658             case MEM_PRIVATE:       type = "private"; break;
659             case 0:                 type = "       "; break;
660             default:                type = "???    "; break;
661             }
662             memset(prot, ' ' , sizeof(prot) - 1);
663             prot[sizeof(prot) - 1] = '\0';
664             if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
665                 prot[0] = 'R';
666             if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
667                 prot[1] = 'W';
668             if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
669                 prot[1] = 'C';
670             if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
671                 prot[2] = 'X';
672         }
673         else
674         {
675             type = "";
676             prot[0] = '\0';
677         }
678         dbg_printf("%08lx %08lx %s %s %s\n",
679                    (DWORD_PTR)addr, (DWORD_PTR)addr + mbi.RegionSize - 1, state, type, prot);
680         if (addr + mbi.RegionSize < addr) /* wrap around ? */
681             break;
682         addr += mbi.RegionSize;
683     }
684     if (pid != dbg_curr_pid) CloseHandle(hProc);
685 }
686
687 void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
688 {
689     struct dbg_lvalue           lvalue;
690     struct __wine_debug_channel channel;
691     unsigned char               mask;
692     int                         done = 0;
693     BOOL                        bAll;
694     void*                       addr;
695
696     if (!dbg_curr_process || !dbg_curr_thread)
697     {
698         dbg_printf("Cannot set/get debug channels while no process is loaded\n");
699         return;
700     }
701
702     if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
703     {
704         return;
705     }
706     addr = memory_to_linear_addr(&lvalue.addr);
707
708     if (!cls)                          mask = ~0;
709     else if (!strcmp(cls, "fixme"))    mask = (1 << __WINE_DBCL_FIXME);
710     else if (!strcmp(cls, "err"))      mask = (1 << __WINE_DBCL_ERR);
711     else if (!strcmp(cls, "warn"))     mask = (1 << __WINE_DBCL_WARN);
712     else if (!strcmp(cls, "trace"))    mask = (1 << __WINE_DBCL_TRACE);
713     else
714     {
715         dbg_printf("Unknown debug class %s\n", cls);
716         return;
717     }
718
719     bAll = !strcmp("all", name);
720     while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
721     {
722         if (!channel.name[0]) break;
723         if (bAll || !strcmp( channel.name, name ))
724         {
725             if (turn_on) channel.flags |= mask;
726             else channel.flags &= ~mask;
727             if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
728         }
729         addr = (struct __wine_debug_channel *)addr + 1;
730     }
731     if (!done) dbg_printf("Unable to find debug channel %s\n", name);
732     else WINE_TRACE("Changed %d channel instances\n", done);
733 }