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