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