Cleanup of dbghelp.h (and a couple of definition fixes). The
[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                                     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)GetWindowLong(hWnd, GWL_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 %.14s\n",
370                    indent, "", (UINT)hWnd, 12 - indent, "",
371                    clsName, GetWindowLong(hWnd, GWL_STYLE),
372                    GetWindowLong(hWnd, GWL_WNDPROC), wndName);
373
374         if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
375             info_window(child, indent + 1);
376     } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
377 }
378
379 void info_win32_window(HWND hWnd, BOOL detailed)
380 {
381     char        clsName[128];
382     char        wndName[128];
383     RECT        clientRect;
384     RECT        windowRect;
385     int         i;
386     WORD        w;
387
388     if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
389
390     if (!detailed)
391     {
392         dbg_printf("%-20.20s %-17.17s %-8.8s %-8.8s %s\n",
393                    "Window handle", "Class Name", "Style", "WndProc", "Text");
394         info_window(hWnd, 0);
395         return;
396     }
397
398     if (!GetClassName(hWnd, clsName, sizeof(clsName)))
399         strcpy(clsName, "-- Unknown --");
400     if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
401         strcpy(wndName, "-- Empty --");
402     if (!GetClientRect(hWnd, &clientRect) || 
403         !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
404         SetRectEmpty(&clientRect);
405     if (!GetWindowRect(hWnd, &windowRect))
406         SetRectEmpty(&windowRect);
407
408     /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
409     dbg_printf("next=%p  child=%p  parent=%p  owner=%p  class='%s'\n"
410                "inst=%p  active=%p  idmenu=%08lx\n"
411                "style=0x%08lx  exstyle=0x%08lx  wndproc=0x%08lx  text='%s'\n"
412                "client=%ld,%ld-%ld,%ld  window=%ld,%ld-%ld,%ld sysmenu=%p\n",
413                GetWindow(hWnd, GW_HWNDNEXT),
414                GetWindow(hWnd, GW_CHILD),
415                GetParent(hWnd),
416                GetWindow(hWnd, GW_OWNER),
417                clsName,
418                (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
419                GetLastActivePopup(hWnd),
420                GetWindowLong(hWnd, GWL_ID),
421                GetWindowLong(hWnd, GWL_STYLE),
422                GetWindowLong(hWnd, GWL_EXSTYLE),
423                GetWindowLong(hWnd, GWL_WNDPROC),
424                wndName,
425                clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
426                windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
427                GetSystemMenu(hWnd, FALSE));
428
429     if (GetClassLong(hWnd, GCL_CBWNDEXTRA))
430     {
431         dbg_printf("Extra bytes:");
432         for (i = 0; i < GetClassLong(hWnd, GCL_CBWNDEXTRA) / 2; i++)
433         {
434             w = GetWindowWord(hWnd, i * 2);
435             /* FIXME: depends on i386 endian-ity */
436             dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
437         }
438         dbg_printf("\n");
439     }
440     dbg_printf("\n");
441 }
442
443 void info_win32_processes(void)
444 {
445     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
446     if (snap != INVALID_HANDLE_VALUE)
447     {
448         PROCESSENTRY32  entry;
449         DWORD           current = dbg_curr_process ? dbg_curr_process->pid : 0;
450         BOOL            ok;
451
452         entry.dwSize = sizeof(entry);
453         ok = Process32First(snap, &entry);
454
455         dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
456                    "pid", "threads", "parent", "executable");
457         while (ok)
458         {
459             if (entry.th32ProcessID != GetCurrentProcessId())
460                 dbg_printf("%c%08lx %-8ld %08lx '%s'\n",
461                            (entry.th32ProcessID == current) ? '>' : ' ',
462                            entry.th32ProcessID, entry.cntThreads,
463                            entry.th32ParentProcessID, entry.szExeFile);
464             ok = Process32Next(snap, &entry);
465         }
466         CloseHandle(snap);
467     }
468 }
469
470 void info_win32_threads(void)
471 {
472     HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
473     if (snap != INVALID_HANDLE_VALUE)
474     {
475         THREADENTRY32   entry;
476         BOOL            ok;
477         DWORD           lastProcessId = 0;
478
479         entry.dwSize = sizeof(entry);
480         ok = Thread32First(snap, &entry);
481
482         dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
483                    "process", "tid", "prio");
484         while (ok)
485         {
486             if (entry.th32OwnerProcessID != GetCurrentProcessId())
487             {
488                 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
489                  * listed sequentially, which is not specified in the doc (Wine's implementation
490                  * does it)
491                  */
492                 if (entry.th32OwnerProcessID != lastProcessId)
493                 {
494                     struct dbg_process* p = dbg_get_process(entry.th32OwnerProcessID);
495
496                     dbg_printf("%08lx%s %s\n",
497                                entry.th32OwnerProcessID, p ? " (D)" : "", p ? p->imageName : "");
498                     lastProcessId = entry.th32OwnerProcessID;
499                 }
500                 dbg_printf("\t%08lx %4ld%s\n",
501                            entry.th32ThreadID, entry.tpBasePri,
502                            (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
503
504             }
505             ok = Thread32Next(snap, &entry);
506         }
507
508         CloseHandle(snap);
509     }
510 }
511
512 /***********************************************************************
513  *           info_win32_exceptions
514  *
515  * Get info on the exception frames of a given thread.
516  */
517 void info_win32_exceptions(DWORD tid)
518 {
519     struct dbg_thread*  thread;
520     void*               next_frame;
521
522     if (!dbg_curr_process || !dbg_curr_thread)
523     {
524         dbg_printf("Cannot get info on exceptions while no process is loaded\n");
525         return;
526     }
527
528     dbg_printf("Exception frames:\n");
529
530     if (tid == dbg_curr_tid) thread = dbg_curr_thread;
531     else
532     {
533         thread = dbg_get_thread(dbg_curr_process, tid);
534
535         if (!thread)
536         {
537             dbg_printf("Unknown thread id (0x%08lx) in current process\n", tid);
538             return;
539         }
540         if (SuspendThread(thread->handle) == -1)
541         {
542             dbg_printf("Can't suspend thread id (0x%08lx)\n", tid);
543             return;
544         }
545     }
546
547     if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
548     {
549         dbg_printf("Can't read TEB:except_frame\n");
550         return;
551     }
552
553     while (next_frame != (void*)-1)
554     {
555         EXCEPTION_REGISTRATION_RECORD frame;
556
557         dbg_printf("%p: ", next_frame);
558         if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
559         {
560             dbg_printf("Invalid frame address\n");
561             break;
562         }
563         dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
564         next_frame = frame.Prev;
565     }
566
567     if (tid != dbg_curr_tid) ResumeThread(thread->handle);
568 }
569
570 void info_win32_segments(DWORD start, int length)
571 {
572     char        flags[3];
573     DWORD       i;
574     LDT_ENTRY   le;
575
576     if (length == -1) length = (8192 - start);
577
578     for (i = start; i < start + length; i++)
579     {
580         if (!GetThreadSelectorEntry(dbg_curr_thread->handle, (i << 3) | 7, &le))
581             continue;
582
583         if (le.HighWord.Bits.Type & 0x08)
584         {
585             flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
586             flags[1] = '-';
587             flags[2] = 'x';
588         }
589         else
590         {
591             flags[0] = 'r';
592             flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
593             flags[2] = '-';
594         }
595         dbg_printf("%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
596                    i, (i << 3) | 7,
597                    (le.HighWord.Bits.BaseHi << 24) +
598                    (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
599                    ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
600                    (le.HighWord.Bits.Granularity ? 12 : 0),
601                    le.HighWord.Bits.Default_Big ? 32 : 16,
602                    flags[0], flags[1], flags[2]);
603     }
604 }
605
606 void info_win32_virtual(DWORD pid)
607 {
608     MEMORY_BASIC_INFORMATION    mbi;
609     char*                       addr = 0;
610     const char*                 state;
611     const char*                 type;
612     char                        prot[3+1];
613     HANDLE                      hProc;
614
615     if (pid == dbg_curr_pid)
616     {
617         if (dbg_curr_process == NULL)
618         {
619             dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
620             return;
621         }
622         hProc = dbg_curr_process->handle;
623     }
624     else
625     {
626         hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
627         if (hProc == NULL)
628         {
629             dbg_printf("Cannot open process <%lu>\n", pid);
630             return;
631         }
632     }
633
634     dbg_printf("Address  Size     State   Type    RWX\n");
635
636     while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
637     {
638         switch (mbi.State)
639         {
640         case MEM_COMMIT:        state = "commit "; break;
641         case MEM_FREE:          state = "free   "; break;
642         case MEM_RESERVE:       state = "reserve"; break;
643         default:                state = "???    "; break;
644         }
645         if (mbi.State != MEM_FREE)
646         {
647             switch (mbi.Type)
648             {
649             case MEM_IMAGE:         type = "image  "; break;
650             case MEM_MAPPED:        type = "mapped "; break;
651             case MEM_PRIVATE:       type = "private"; break;
652             case 0:                 type = "       "; break;
653             default:                type = "???    "; break;
654             }
655             memset(prot, ' ' , sizeof(prot) - 1);
656             prot[sizeof(prot) - 1] = '\0';
657             if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
658                 prot[0] = 'R';
659             if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
660                 prot[1] = 'W';
661             if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
662                 prot[1] = 'C';
663             if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
664                 prot[2] = 'X';
665         }
666         else
667         {
668             type = "";
669             prot[0] = '\0';
670         }
671         dbg_printf("%08lx %08lx %s %s %s\n",
672                    (DWORD)addr, (DWORD)addr + mbi.RegionSize - 1, state, type, prot);
673         if (addr + mbi.RegionSize < addr) /* wrap around ? */
674             break;
675         addr += mbi.RegionSize;
676     }
677     if (pid != dbg_curr_pid) CloseHandle(hProc);
678 }
679
680 struct dll_option_layout
681 {
682     void*               next;
683     void*               prev;
684     char* const*        channels;
685     int                 nb_channels;
686 };
687
688 void info_wine_dbg_channel(BOOL turn_on, const char* chnl, const char* name)
689 {
690     struct dbg_lvalue           lvalue;
691     struct dll_option_layout    dol;
692     int                         i;
693     char*                       str;
694     unsigned char               buffer[32];
695     unsigned char               mask;
696     int                         done = 0;
697     BOOL                        bAll;
698     void*                       addr;
699
700     if (!dbg_curr_process || !dbg_curr_thread)
701     {
702         dbg_printf("Cannot set/get debug channels while no process is loaded\n");
703         return;
704     }
705
706     if (symbol_get_lvalue("first_dll", -1, &lvalue, FALSE) != sglv_found)
707     {
708         return;
709     }
710     addr = memory_to_linear_addr(&lvalue.addr);
711     if (!chnl)                          mask = 15;
712     else if (!strcmp(chnl, "fixme"))    mask = 1;
713     else if (!strcmp(chnl, "err"))      mask = 2;
714     else if (!strcmp(chnl, "warn"))     mask = 4;
715     else if (!strcmp(chnl, "trace"))    mask = 8;
716     else { dbg_printf("Unknown channel %s\n", chnl); return; }
717
718     bAll = !strcmp("all", name);
719     while (addr && dbg_read_memory(addr, &dol, sizeof(dol)))
720     {
721         for (i = 0; i < dol.nb_channels; i++)
722         {
723             if (dbg_read_memory(dol.channels + i, &str, sizeof(str)) &&
724                 dbg_read_memory(str, buffer, sizeof(buffer)) &&
725                 (!strcmp(buffer + 1, name) || bAll))
726             {
727                 if (turn_on) buffer[0] |= mask; else buffer[0] &= ~mask;
728                 if (dbg_write_memory(str, buffer, 1)) done++;
729             }
730         }
731         addr = dol.next;
732     }
733     if (!done) dbg_printf("Unable to find debug channel %s\n", name);
734     else WINE_TRACE("Changed %d channel instances\n", done);
735 }