winedbg: Make lexeme_alloc() static in debug.l.
[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)" : "",
505                                p ? dbg_W2A(p->imageName, -1) : "");
506                     lastProcessId = entry.th32OwnerProcessID;
507                 }
508                 dbg_printf("\t%08x %4d%s\n",
509                            entry.th32ThreadID, entry.tpBasePri,
510                            (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
511
512             }
513             ok = Thread32Next(snap, &entry);
514         }
515
516         CloseHandle(snap);
517     }
518 }
519
520 /***********************************************************************
521  *           info_win32_exceptions
522  *
523  * Get info on the exception frames of a given thread.
524  */
525 void info_win32_exceptions(DWORD tid)
526 {
527     struct dbg_thread*  thread;
528     void*               next_frame;
529
530     if (!dbg_curr_process || !dbg_curr_thread)
531     {
532         dbg_printf("Cannot get info on exceptions while no process is loaded\n");
533         return;
534     }
535
536     dbg_printf("Exception frames:\n");
537
538     if (tid == dbg_curr_tid) thread = dbg_curr_thread;
539     else
540     {
541         thread = dbg_get_thread(dbg_curr_process, tid);
542
543         if (!thread)
544         {
545             dbg_printf("Unknown thread id (%04x) in current process\n", tid);
546             return;
547         }
548         if (SuspendThread(thread->handle) == -1)
549         {
550             dbg_printf("Can't suspend thread id (%04x)\n", tid);
551             return;
552         }
553     }
554
555     if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
556     {
557         dbg_printf("Can't read TEB:except_frame\n");
558         return;
559     }
560
561     while (next_frame != (void*)-1)
562     {
563         EXCEPTION_REGISTRATION_RECORD frame;
564
565         dbg_printf("%p: ", next_frame);
566         if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
567         {
568             dbg_printf("Invalid frame address\n");
569             break;
570         }
571         dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
572         next_frame = frame.Prev;
573     }
574
575     if (tid != dbg_curr_tid) ResumeThread(thread->handle);
576 }
577
578 void info_win32_segments(DWORD start, int length)
579 {
580     char        flags[3];
581     DWORD       i;
582     LDT_ENTRY   le;
583
584     if (length == -1) length = (8192 - start);
585
586     for (i = start; i < start + length; i++)
587     {
588         if (!dbg_curr_process->process_io->get_selector(dbg_curr_thread->handle, (i << 3) | 7, &le))
589             continue;
590
591         if (le.HighWord.Bits.Type & 0x08)
592         {
593             flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
594             flags[1] = '-';
595             flags[2] = 'x';
596         }
597         else
598         {
599             flags[0] = 'r';
600             flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
601             flags[2] = '-';
602         }
603         dbg_printf("%04x: sel=%04x base=%08x limit=%08x %d-bit %c%c%c\n",
604                    i, (i << 3) | 7,
605                    (le.HighWord.Bits.BaseHi << 24) +
606                    (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
607                    ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
608                    (le.HighWord.Bits.Granularity ? 12 : 0),
609                    le.HighWord.Bits.Default_Big ? 32 : 16,
610                    flags[0], flags[1], flags[2]);
611     }
612 }
613
614 void info_win32_virtual(DWORD pid)
615 {
616     MEMORY_BASIC_INFORMATION    mbi;
617     char*                       addr = 0;
618     const char*                 state;
619     const char*                 type;
620     char                        prot[3+1];
621     HANDLE                      hProc;
622
623     if (pid == dbg_curr_pid)
624     {
625         if (dbg_curr_process == NULL)
626         {
627             dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
628             return;
629         }
630         hProc = dbg_curr_process->handle;
631     }
632     else
633     {
634         hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
635         if (hProc == NULL)
636         {
637             dbg_printf("Cannot open process <%04x>\n", pid);
638             return;
639         }
640     }
641
642     dbg_printf("Address  Size     State   Type    RWX\n");
643
644     while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
645     {
646         switch (mbi.State)
647         {
648         case MEM_COMMIT:        state = "commit "; break;
649         case MEM_FREE:          state = "free   "; break;
650         case MEM_RESERVE:       state = "reserve"; break;
651         default:                state = "???    "; break;
652         }
653         if (mbi.State != MEM_FREE)
654         {
655             switch (mbi.Type)
656             {
657             case MEM_IMAGE:         type = "image  "; break;
658             case MEM_MAPPED:        type = "mapped "; break;
659             case MEM_PRIVATE:       type = "private"; break;
660             case 0:                 type = "       "; break;
661             default:                type = "???    "; break;
662             }
663             memset(prot, ' ' , sizeof(prot) - 1);
664             prot[sizeof(prot) - 1] = '\0';
665             if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
666                 prot[0] = 'R';
667             if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
668                 prot[1] = 'W';
669             if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
670                 prot[1] = 'C';
671             if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
672                 prot[2] = 'X';
673         }
674         else
675         {
676             type = "";
677             prot[0] = '\0';
678         }
679         dbg_printf("%08lx %08lx %s %s %s\n",
680                    (DWORD_PTR)addr, (DWORD_PTR)addr + mbi.RegionSize - 1, state, type, prot);
681         if (addr + mbi.RegionSize < addr) /* wrap around ? */
682             break;
683         addr += mbi.RegionSize;
684     }
685     if (pid != dbg_curr_pid) CloseHandle(hProc);
686 }
687
688 void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
689 {
690     struct dbg_lvalue           lvalue;
691     struct __wine_debug_channel channel;
692     unsigned char               mask;
693     int                         done = 0;
694     BOOL                        bAll;
695     void*                       addr;
696
697     if (!dbg_curr_process || !dbg_curr_thread)
698     {
699         dbg_printf("Cannot set/get debug channels while no process is loaded\n");
700         return;
701     }
702
703     if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
704     {
705         return;
706     }
707     addr = memory_to_linear_addr(&lvalue.addr);
708
709     if (!cls)                          mask = ~0;
710     else if (!strcmp(cls, "fixme"))    mask = (1 << __WINE_DBCL_FIXME);
711     else if (!strcmp(cls, "err"))      mask = (1 << __WINE_DBCL_ERR);
712     else if (!strcmp(cls, "warn"))     mask = (1 << __WINE_DBCL_WARN);
713     else if (!strcmp(cls, "trace"))    mask = (1 << __WINE_DBCL_TRACE);
714     else
715     {
716         dbg_printf("Unknown debug class %s\n", cls);
717         return;
718     }
719
720     bAll = !strcmp("all", name);
721     while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
722     {
723         if (!channel.name[0]) break;
724         if (bAll || !strcmp( channel.name, name ))
725         {
726             if (turn_on) channel.flags |= mask;
727             else channel.flags &= ~mask;
728             if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
729         }
730         addr = (struct __wine_debug_channel *)addr + 1;
731     }
732     if (!done) dbg_printf("Unable to find debug channel %s\n", name);
733     else WINE_TRACE("Changed %d channel instances\n", done);
734 }