regedit: Add/remove favorite opens a dialog, thus should have ellipsis.
[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     DWORD               base;
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("%8s-%8s\t%-16s%s\n",
157                wine_dbgstr_longlong(mi->BaseOfImage),
158                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 = (struct info_module*)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\tDebug info\tName (%d modules)\n", im.num_used);
226
227     for (i = 0; i < im.num_used; i++)
228     {
229         if (base && 
230             (base < im.mi[i].BaseOfImage || base >= im.mi[i].BaseOfImage + im.mi[i].ImageSize))
231             continue;
232         if (strstr(im.mi[i].ModuleName, "<elf>"))
233         {
234             dbg_printf("ELF\t");
235             module_print_info(&im.mi[i], FALSE);
236             /* print all modules embedded in this one */
237             for (j = 0; j < im.num_used; j++)
238             {
239                 if (!strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[i], &im.mi[j]))
240                 {
241                     dbg_printf("  \\-PE\t");
242                     module_print_info(&im.mi[j], TRUE);
243                 }
244             }
245         }
246         else
247         {
248             /* check module is not embedded in another module */
249             for (j = 0; j < im.num_used; j++) 
250             {
251                 if (strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[j], &im.mi[i]))
252                     break;
253             }
254             if (j < im.num_used) continue;
255             if (strstr(im.mi[i].ModuleName, ".so") || strchr(im.mi[i].ModuleName, '<'))
256                 dbg_printf("ELF\t");
257             else
258                 dbg_printf("PE\t");
259             module_print_info(&im.mi[i], FALSE);
260         }
261         num_printed++;
262     }
263     HeapFree(GetProcessHeap(), 0, im.mi);
264
265     if (base && !num_printed)
266         dbg_printf("'0x%x%08x' is not a valid module address\n", (DWORD)(base >> 32), (DWORD)base);
267 }
268
269 struct class_walker
270 {
271     ATOM*       table;
272     int         used;
273     int         alloc;
274 };
275
276 static void class_walker(HWND hWnd, struct class_walker* cw)
277 {
278     char        clsName[128];
279     int         i;
280     ATOM        atom;
281     HWND        child;
282
283     if (!GetClassName(hWnd, clsName, sizeof(clsName)))
284         return;
285     if ((atom = FindAtom(clsName)) == 0)
286         return;
287
288     for (i = 0; i < cw->used; i++)
289     {
290         if (cw->table[i] == atom)
291             break;
292     }
293     if (i == cw->used)
294     {
295         if (cw->used >= cw->alloc)
296         {
297             cw->alloc += 16;
298             cw->table = dbg_heap_realloc(cw->table, cw->alloc * sizeof(ATOM));
299         }
300         cw->table[cw->used++] = atom;
301         info_win32_class(hWnd, clsName);
302     }
303     do
304     {
305         if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
306             class_walker(child, cw);
307     } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
308 }
309
310 void info_win32_class(HWND hWnd, const char* name)
311 {
312     WNDCLASSEXA wca;
313     HINSTANCE   hInst = hWnd ? (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE) : 0;
314
315     if (!name)
316     {
317         struct class_walker cw;
318
319         cw.table = NULL;
320         cw.used = cw.alloc = 0;
321         class_walker(GetDesktopWindow(), &cw);
322         HeapFree(GetProcessHeap(), 0, cw.table);
323         return;
324     }
325
326     if (!GetClassInfoEx(hInst, name, &wca))
327     {
328         dbg_printf("Cannot find class '%s'\n", name);
329         return;
330     }
331
332     dbg_printf("Class '%s':\n", name);
333     dbg_printf("style=0x%08x  wndProc=%p\n"
334                "inst=%p  icon=%p  cursor=%p  bkgnd=%p\n"
335                "clsExtra=%d  winExtra=%d\n",
336                wca.style, wca.lpfnWndProc, wca.hInstance,
337                wca.hIcon, wca.hCursor, wca.hbrBackground,
338                wca.cbClsExtra, wca.cbWndExtra);
339
340     if (hWnd && wca.cbClsExtra)
341     {
342         int             i;
343         WORD            w;
344
345         dbg_printf("Extra bytes:");
346         for (i = 0; i < wca.cbClsExtra / 2; i++)
347         {
348             w = GetClassWord(hWnd, i * 2);
349             /* FIXME: depends on i386 endian-ity */
350             dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
351         }
352         dbg_printf("\n");
353     }
354     dbg_printf("\n");
355     /* FIXME:
356      * + print #windows (or even list of windows...)
357      * + print extra bytes => this requires a window handle on this very class...
358      */
359 }
360
361 static void info_window(HWND hWnd, int indent)
362 {
363     char        clsName[128];
364     char        wndName[128];
365     HWND        child;
366
367     do
368     {
369         if (!GetClassName(hWnd, clsName, sizeof(clsName)))
370             strcpy(clsName, "-- Unknown --");
371         if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
372             strcpy(wndName, "-- Empty --");
373
374         dbg_printf("%*s%08lx%*s %-17.17s %08x %08x %08x %.14s\n",
375                    indent, "", (DWORD_PTR)hWnd, 12 - indent, "",
376                    clsName, GetWindowLong(hWnd, GWL_STYLE),
377                    GetWindowLongPtr(hWnd, GWLP_WNDPROC),
378                    GetWindowThreadProcessId(hWnd, NULL), wndName);
379
380         if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
381             info_window(child, indent + 1);
382     } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
383 }
384
385 void info_win32_window(HWND hWnd, BOOL detailed)
386 {
387     char        clsName[128];
388     char        wndName[128];
389     RECT        clientRect;
390     RECT        windowRect;
391     WORD        w;
392
393     if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
394
395     if (!detailed)
396     {
397         dbg_printf("%-20.20s %-17.17s %-8.8s %-8.8s %-8.8s %s\n",
398                    "Window handle", "Class Name", "Style", "WndProc", "Thread", "Text");
399         info_window(hWnd, 0);
400         return;
401     }
402
403     if (!GetClassName(hWnd, clsName, sizeof(clsName)))
404         strcpy(clsName, "-- Unknown --");
405     if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
406         strcpy(wndName, "-- Empty --");
407     if (!GetClientRect(hWnd, &clientRect) || 
408         !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
409         SetRectEmpty(&clientRect);
410     if (!GetWindowRect(hWnd, &windowRect))
411         SetRectEmpty(&windowRect);
412
413     /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
414     dbg_printf("next=%p  child=%p  parent=%p  owner=%p  class='%s'\n"
415                "inst=%p  active=%p  idmenu=%08x\n"
416                "style=0x%08x  exstyle=0x%08x  wndproc=0x%08x  text='%s'\n"
417                "client=%d,%d-%d,%d  window=%d,%d-%d,%d sysmenu=%p\n",
418                GetWindow(hWnd, GW_HWNDNEXT),
419                GetWindow(hWnd, GW_CHILD),
420                GetParent(hWnd),
421                GetWindow(hWnd, GW_OWNER),
422                clsName,
423                (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
424                GetLastActivePopup(hWnd),
425                GetWindowLongPtr(hWnd, GWLP_ID),
426                GetWindowLong(hWnd, GWL_STYLE),
427                GetWindowLong(hWnd, GWL_EXSTYLE),
428                GetWindowLongPtr(hWnd, GWLP_WNDPROC),
429                wndName,
430                clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
431                windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
432                GetSystemMenu(hWnd, FALSE));
433
434     if (GetClassLong(hWnd, GCL_CBWNDEXTRA))
435     {
436         UINT i;
437
438         dbg_printf("Extra bytes:");
439         for (i = 0; i < GetClassLong(hWnd, GCL_CBWNDEXTRA) / 2; i++)
440         {
441             w = GetWindowWord(hWnd, i * 2);
442             /* FIXME: depends on i386 endian-ity */
443             dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
444         }
445         dbg_printf("\n");
446     }
447     dbg_printf("\n");
448 }
449
450 void info_win32_processes(void)
451 {
452     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
453     if (snap != INVALID_HANDLE_VALUE)
454     {
455         PROCESSENTRY32  entry;
456         DWORD           current = dbg_curr_process ? dbg_curr_process->pid : 0;
457         BOOL            ok;
458
459         entry.dwSize = sizeof(entry);
460         ok = Process32First(snap, &entry);
461
462         dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
463                    "pid", "threads", "parent", "executable");
464         while (ok)
465         {
466             if (entry.th32ProcessID != GetCurrentProcessId())
467                 dbg_printf("%c%08x %-8d %08x '%s'\n",
468                            (entry.th32ProcessID == current) ? '>' : ' ',
469                            entry.th32ProcessID, entry.cntThreads,
470                            entry.th32ParentProcessID, entry.szExeFile);
471             ok = Process32Next(snap, &entry);
472         }
473         CloseHandle(snap);
474     }
475 }
476
477 void info_win32_threads(void)
478 {
479     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
480     if (snap != INVALID_HANDLE_VALUE)
481     {
482         THREADENTRY32   entry;
483         BOOL            ok;
484         DWORD           lastProcessId = 0;
485
486         entry.dwSize = sizeof(entry);
487         ok = Thread32First(snap, &entry);
488
489         dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
490                    "process", "tid", "prio");
491         while (ok)
492         {
493             if (entry.th32OwnerProcessID != GetCurrentProcessId())
494             {
495                 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
496                  * listed sequentially, which is not specified in the doc (Wine's implementation
497                  * does it)
498                  */
499                 if (entry.th32OwnerProcessID != lastProcessId)
500                 {
501                     struct dbg_process* p = dbg_get_process(entry.th32OwnerProcessID);
502
503                     dbg_printf("%08x%s %s\n",
504                                entry.th32OwnerProcessID, p ? " (D)" : "", p ? p->imageName : "");
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  Size     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 }