winedbg: Put back the %d format for printing integral values even for
[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("%s-%s\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(PSTR mod_name, DWORD64 base, void* 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(DWORD base)
202 {
203     struct info_module  im;
204     int                 i, j, num_printed = 0;
205     DWORD               opt;
206
207     if (!dbg_curr_process || !dbg_curr_thread)
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%08lx' is not a valid module address\n", 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=0x%08lx\n"
334                "inst=%p  icon=%p  cursor=%p  bkgnd=%p\n"
335                "clsExtra=%d  winExtra=%d\n",
336                wca.style, (DWORD)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%08x%*s %-17.17s %08lx %08lx %08lx %.14s\n",
375                    indent, "", (UINT)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     int         i;
392     WORD        w;
393
394     if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
395
396     if (!detailed)
397     {
398         dbg_printf("%-20.20s %-17.17s %-8.8s %-8.8s %-8.8s %s\n",
399                    "Window handle", "Class Name", "Style", "WndProc", "Thread", "Text");
400         info_window(hWnd, 0);
401         return;
402     }
403
404     if (!GetClassName(hWnd, clsName, sizeof(clsName)))
405         strcpy(clsName, "-- Unknown --");
406     if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
407         strcpy(wndName, "-- Empty --");
408     if (!GetClientRect(hWnd, &clientRect) || 
409         !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
410         SetRectEmpty(&clientRect);
411     if (!GetWindowRect(hWnd, &windowRect))
412         SetRectEmpty(&windowRect);
413
414     /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
415     dbg_printf("next=%p  child=%p  parent=%p  owner=%p  class='%s'\n"
416                "inst=%p  active=%p  idmenu=%08lx\n"
417                "style=0x%08lx  exstyle=0x%08lx  wndproc=0x%08lx  text='%s'\n"
418                "client=%ld,%ld-%ld,%ld  window=%ld,%ld-%ld,%ld sysmenu=%p\n",
419                GetWindow(hWnd, GW_HWNDNEXT),
420                GetWindow(hWnd, GW_CHILD),
421                GetParent(hWnd),
422                GetWindow(hWnd, GW_OWNER),
423                clsName,
424                (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
425                GetLastActivePopup(hWnd),
426                GetWindowLongPtr(hWnd, GWLP_ID),
427                GetWindowLong(hWnd, GWL_STYLE),
428                GetWindowLong(hWnd, GWL_EXSTYLE),
429                GetWindowLongPtr(hWnd, GWLP_WNDPROC),
430                wndName,
431                clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
432                windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
433                GetSystemMenu(hWnd, FALSE));
434
435     if (GetClassLong(hWnd, GCL_CBWNDEXTRA))
436     {
437         dbg_printf("Extra bytes:");
438         for (i = 0; i < GetClassLong(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%08lx %-8ld %08lx '%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("%08lx%s %s\n",
503                                entry.th32OwnerProcessID, p ? " (D)" : "", p ? p->imageName : "");
504                     lastProcessId = entry.th32OwnerProcessID;
505                 }
506                 dbg_printf("\t%08lx %4ld%s\n",
507                            entry.th32ThreadID, entry.tpBasePri,
508                            (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
509
510             }
511             ok = Thread32Next(snap, &entry);
512         }
513
514         CloseHandle(snap);
515     }
516 }
517
518 /***********************************************************************
519  *           info_win32_exceptions
520  *
521  * Get info on the exception frames of a given thread.
522  */
523 void info_win32_exceptions(DWORD tid)
524 {
525     struct dbg_thread*  thread;
526     void*               next_frame;
527
528     if (!dbg_curr_process || !dbg_curr_thread)
529     {
530         dbg_printf("Cannot get info on exceptions while no process is loaded\n");
531         return;
532     }
533
534     dbg_printf("Exception frames:\n");
535
536     if (tid == dbg_curr_tid) thread = dbg_curr_thread;
537     else
538     {
539         thread = dbg_get_thread(dbg_curr_process, tid);
540
541         if (!thread)
542         {
543             dbg_printf("Unknown thread id (0x%08lx) in current process\n", tid);
544             return;
545         }
546         if (SuspendThread(thread->handle) == -1)
547         {
548             dbg_printf("Can't suspend thread id (0x%08lx)\n", tid);
549             return;
550         }
551     }
552
553     if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
554     {
555         dbg_printf("Can't read TEB:except_frame\n");
556         return;
557     }
558
559     while (next_frame != (void*)-1)
560     {
561         EXCEPTION_REGISTRATION_RECORD frame;
562
563         dbg_printf("%p: ", next_frame);
564         if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
565         {
566             dbg_printf("Invalid frame address\n");
567             break;
568         }
569         dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
570         next_frame = frame.Prev;
571     }
572
573     if (tid != dbg_curr_tid) ResumeThread(thread->handle);
574 }
575
576 void info_win32_segments(DWORD start, int length)
577 {
578     char        flags[3];
579     DWORD       i;
580     LDT_ENTRY   le;
581
582     if (length == -1) length = (8192 - start);
583
584     for (i = start; i < start + length; i++)
585     {
586         if (!GetThreadSelectorEntry(dbg_curr_thread->handle, (i << 3) | 7, &le))
587             continue;
588
589         if (le.HighWord.Bits.Type & 0x08)
590         {
591             flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
592             flags[1] = '-';
593             flags[2] = 'x';
594         }
595         else
596         {
597             flags[0] = 'r';
598             flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
599             flags[2] = '-';
600         }
601         dbg_printf("%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
602                    i, (i << 3) | 7,
603                    (le.HighWord.Bits.BaseHi << 24) +
604                    (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
605                    ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
606                    (le.HighWord.Bits.Granularity ? 12 : 0),
607                    le.HighWord.Bits.Default_Big ? 32 : 16,
608                    flags[0], flags[1], flags[2]);
609     }
610 }
611
612 void info_win32_virtual(DWORD pid)
613 {
614     MEMORY_BASIC_INFORMATION    mbi;
615     char*                       addr = 0;
616     const char*                 state;
617     const char*                 type;
618     char                        prot[3+1];
619     HANDLE                      hProc;
620
621     if (pid == dbg_curr_pid)
622     {
623         if (dbg_curr_process == NULL)
624         {
625             dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
626             return;
627         }
628         hProc = dbg_curr_process->handle;
629     }
630     else
631     {
632         hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
633         if (hProc == NULL)
634         {
635             dbg_printf("Cannot open process <%lu>\n", pid);
636             return;
637         }
638     }
639
640     dbg_printf("Address  Size     State   Type    RWX\n");
641
642     while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
643     {
644         switch (mbi.State)
645         {
646         case MEM_COMMIT:        state = "commit "; break;
647         case MEM_FREE:          state = "free   "; break;
648         case MEM_RESERVE:       state = "reserve"; break;
649         default:                state = "???    "; break;
650         }
651         if (mbi.State != MEM_FREE)
652         {
653             switch (mbi.Type)
654             {
655             case MEM_IMAGE:         type = "image  "; break;
656             case MEM_MAPPED:        type = "mapped "; break;
657             case MEM_PRIVATE:       type = "private"; break;
658             case 0:                 type = "       "; break;
659             default:                type = "???    "; break;
660             }
661             memset(prot, ' ' , sizeof(prot) - 1);
662             prot[sizeof(prot) - 1] = '\0';
663             if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
664                 prot[0] = 'R';
665             if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
666                 prot[1] = 'W';
667             if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
668                 prot[1] = 'C';
669             if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
670                 prot[2] = 'X';
671         }
672         else
673         {
674             type = "";
675             prot[0] = '\0';
676         }
677         dbg_printf("%08lx %08lx %s %s %s\n",
678                    (DWORD)addr, (DWORD)addr + mbi.RegionSize - 1, state, type, prot);
679         if (addr + mbi.RegionSize < addr) /* wrap around ? */
680             break;
681         addr += mbi.RegionSize;
682     }
683     if (pid != dbg_curr_pid) CloseHandle(hProc);
684 }
685
686 void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
687 {
688     struct dbg_lvalue           lvalue;
689     struct __wine_debug_channel channel;
690     unsigned char               mask;
691     int                         done = 0;
692     BOOL                        bAll;
693     void*                       addr;
694
695     if (!dbg_curr_process || !dbg_curr_thread)
696     {
697         dbg_printf("Cannot set/get debug channels while no process is loaded\n");
698         return;
699     }
700
701     if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
702     {
703         return;
704     }
705     addr = memory_to_linear_addr(&lvalue.addr);
706
707     if (!cls)                          mask = ~0;
708     else if (!strcmp(cls, "fixme"))    mask = (1 << __WINE_DBCL_FIXME);
709     else if (!strcmp(cls, "err"))      mask = (1 << __WINE_DBCL_ERR);
710     else if (!strcmp(cls, "warn"))     mask = (1 << __WINE_DBCL_WARN);
711     else if (!strcmp(cls, "trace"))    mask = (1 << __WINE_DBCL_TRACE);
712     else
713     {
714         dbg_printf("Unknown debug class %s\n", cls);
715         return;
716     }
717
718     bAll = !strcmp("all", name);
719     while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
720     {
721         if (!channel.name[0]) break;
722         if (bAll || !strcmp( channel.name, name ))
723         {
724             if (turn_on) channel.flags |= mask;
725             else channel.flags &= ~mask;
726             if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
727         }
728         addr = (struct __wine_debug_channel *)addr + 1;
729     }
730     if (!done) dbg_printf("Unable to find debug channel %s\n", name);
731     else WINE_TRACE("Changed %d channel instances\n", done);
732 }