Added $regs as a variable for displaying all registers.
[wine] / debugger / 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 #include <stdlib.h>
24 #include <string.h>
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "tlhelp32.h"
29 #include "debugger.h"
30 #include "expr.h"
31
32 /***********************************************************************
33  *           DEBUG_PrintBasic
34  *
35  * Implementation of the 'print' command.
36  */
37 void DEBUG_PrintBasic( const DBG_VALUE* value, int count, char format )
38 {
39     char        * default_format;
40     long long int res;
41
42     assert(value->cookie == DV_TARGET || value->cookie == DV_HOST);
43     if (value->type == NULL)
44     {
45         DEBUG_Printf(DBG_CHN_MESG, "Unable to evaluate expression\n");
46         return;
47     }
48
49     default_format = NULL;
50     res = DEBUG_GetExprValue(value, &default_format);
51
52     switch (format)
53     {
54     case 'x':
55         if (value->addr.seg)
56         {
57             DEBUG_nchar += DEBUG_Printf(DBG_CHN_MESG, "0x%04lx", (long unsigned int)res);
58         }
59         else
60         {
61             DEBUG_nchar += DEBUG_Printf(DBG_CHN_MESG, "0x%08lx", (long unsigned int)res);
62         }
63         break;
64
65     case 'd':
66         DEBUG_nchar += DEBUG_Printf(DBG_CHN_MESG, "%ld\n", (long int)res);
67         break;
68
69     case 'c':
70         DEBUG_nchar += DEBUG_Printf(DBG_CHN_MESG, "%d = '%c'",
71                                     (char)(res & 0xff), (char)(res & 0xff));
72         break;
73
74     case 'u':
75     {
76         WCHAR wch = (WCHAR)(res & 0xFFFF);
77         DEBUG_nchar += DEBUG_Printf(DBG_CHN_MESG, "%d = '", (unsigned)(res & 0xffff));
78         DEBUG_OutputW(DBG_CHN_MESG, &wch, 1);
79         DEBUG_Printf(DBG_CHN_MESG, "'");
80     }
81     break;
82
83     case 'i':
84     case 's':
85     case 'w':
86     case 'b':
87         DEBUG_Printf(DBG_CHN_MESG, "Format specifier '%c' is meaningless in 'print' command\n", format);
88     case 0:
89         if (default_format == NULL) break;
90
91         if (strstr(default_format, "%S") != NULL)
92         {
93             char*       ptr;
94             int state = 0;
95
96             /* FIXME: simplistic implementation for default_format being
97              * foo%Sbar => will print foo, then string then bar
98              */
99             for (ptr = default_format; *ptr; ptr++)
100             {
101                 if (*ptr == '%')
102                 {
103                     state++;
104                 }
105                 else if (state == 1)
106                 {
107                     if (*ptr == 'S')
108                     {
109                         DBG_ADDR    addr;
110
111                         addr.seg = 0;
112                         addr.off = (long)res;
113                         DEBUG_nchar += DEBUG_PrintStringA(DBG_CHN_MESG, &addr, -1);
114                     }
115                     else
116                     {
117                         /* shouldn't happen */
118                         DEBUG_Printf(DBG_CHN_MESG, "%%%c", *ptr);
119                         DEBUG_nchar += 2;
120                     }
121                     state = 0;
122                 }
123                 else
124                 {
125                     DEBUG_OutputA(DBG_CHN_MESG, ptr, 1);
126                     DEBUG_nchar++;
127                 }
128             }
129         }
130         else if (strcmp(default_format, "%B") == 0)
131         {
132             DEBUG_nchar += DEBUG_Printf(DBG_CHN_MESG, "%s", res ? "true" : "false");
133         }
134         else if (strcmp(default_format, "%R") == 0)
135         {
136             if (value->cookie == DV_HOST)
137                 DEBUG_InfoRegisters((CONTEXT*)value->addr.off);
138             else
139                 DEBUG_Printf(DBG_CHN_MESG, "NIY: info on register struct in debuggee address space\n");
140             DEBUG_nchar = 0;
141         }
142         else
143         {
144             DEBUG_nchar += DEBUG_Printf(DBG_CHN_MESG, default_format, res);
145         }
146         break;
147     }
148 }
149
150
151 /***********************************************************************
152  *           DEBUG_PrintAddress
153  *
154  * Print an 16- or 32-bit address, with the nearest symbol if any.
155  */
156 struct symbol_info
157 DEBUG_PrintAddress( const DBG_ADDR *addr, enum dbg_mode mode, int flag )
158 {
159     struct symbol_info rtn;
160
161     const char *name = DEBUG_FindNearestSymbol( addr, flag, &rtn.sym, 0,
162                                                 &rtn.list );
163
164     if (addr->seg) DEBUG_Printf( DBG_CHN_MESG, "0x%04lx:", addr->seg&0xFFFF );
165     if (mode != MODE_32) DEBUG_Printf( DBG_CHN_MESG, "0x%04lx", addr->off );
166     else DEBUG_Printf( DBG_CHN_MESG, "0x%08lx", addr->off );
167     if (name) DEBUG_Printf( DBG_CHN_MESG, " (%s)", name );
168     return rtn;
169 }
170 /***********************************************************************
171  *           DEBUG_PrintAddressAndArgs
172  *
173  * Print an 16- or 32-bit address, with the nearest symbol if any.
174  * Similar to DEBUG_PrintAddress, but we print the arguments to
175  * each function (if known).  This is useful in a backtrace.
176  */
177 struct symbol_info
178 DEBUG_PrintAddressAndArgs( const DBG_ADDR *addr, enum dbg_mode mode,
179                            unsigned int ebp, int flag )
180 {
181     struct symbol_info rtn;
182
183     const char *name = DEBUG_FindNearestSymbol( addr, flag, &rtn.sym, ebp,
184                                                 &rtn.list );
185
186     if (addr->seg) DEBUG_Printf( DBG_CHN_MESG, "0x%04lx:", addr->seg );
187     if (mode != MODE_32) DEBUG_Printf( DBG_CHN_MESG, "0x%04lx", addr->off );
188     else DEBUG_Printf( DBG_CHN_MESG, "0x%08lx", addr->off );
189     if (name) DEBUG_Printf( DBG_CHN_MESG, " (%s)", name );
190
191     return rtn;
192 }
193
194
195 /***********************************************************************
196  *           DEBUG_Help
197  *
198  * Implementation of the 'help' command.
199  */
200 void DEBUG_Help(void)
201 {
202     int i = 0;
203     static const char * const helptext[] =
204 {
205 "The commands accepted by the Wine debugger are a reasonable",
206 "subset of the commands that gdb accepts.",
207 "The commands currently are:",
208 "  help                                   quit",
209 "  break [*<addr>]                        delete break bpnum",
210 "  disable bpnum                          enable bpnum",
211 "  condition <bpnum> [<expr>]             pass",
212 "  bt                                     cont [N]",
213 "  step [N]                               next [N]",
214 "  stepi [N]                              nexti [N]",
215 "  x <addr>                               print <expr>",
216 "  set <reg> = <expr>                     set *<addr> = <expr>",
217 "  up                                     down",
218 "  list <lines>                           disassemble [<addr>][,<addr>]",
219 "  frame <n>                              finish",
220 "  show dir                               dir <path>",
221 "  display <expr>                         undisplay <disnum>",
222 "  delete display <disnum>                debugmsg <class>[-+]<type>\n",
223 "  mode [16,32,vm86]                      walk [wnd,class,queue,module,",
224 "  whatis                                       process,modref <pid>]",
225 "  info (see 'help info' for options)\n",
226
227 "The 'x' command accepts repeat counts and formats (including 'i') in the",
228 "same way that gdb does.\n",
229
230 " The following are examples of legal expressions:",
231 " $eax     $eax+0x3   0x1000   ($eip + 256)  *$eax   *($esp + 3)",
232 " Also, a nm format symbol table can be read from a file using the",
233 " symbolfile command.  Symbols can also be defined individually with",
234 " the define command.",
235 "",
236 NULL
237 };
238
239     while(helptext[i]) DEBUG_Printf(DBG_CHN_MESG,"%s\n", helptext[i++]);
240 }
241
242
243 /***********************************************************************
244  *           DEBUG_HelpInfo
245  *
246  * Implementation of the 'help info' command.
247  */
248 void DEBUG_HelpInfo(void)
249 {
250     int i = 0;
251     static const char * const infotext[] =
252 {
253 "The info commands allow you to get assorted bits of interesting stuff",
254 "to be displayed.  The options are:",
255 "  info break           Dumps information about breakpoints",
256 "  info display         Shows auto-display expressions in use",
257 "  info locals          Displays values of all local vars for current frame",
258 "  info maps            Dumps all virtual memory mappings",
259 "  info module <handle> Displays internal module state",
260 "  info queue <handle>  Displays internal queue state",
261 "  info reg             Displays values in all registers at top of stack",
262 "  info segments        Dumps information about all known segments",
263 "  info share           Dumps information about shared libraries",
264 "  info stack           Dumps information about top of stack",
265 "  info wnd <handle>    Displays internal window state",
266 "",
267 NULL
268 };
269
270     while(infotext[i]) DEBUG_Printf(DBG_CHN_MESG,"%s\n", infotext[i++]);
271 }
272
273 /* FIXME: merge InfoClass and InfoClass2 */
274 void DEBUG_InfoClass(const char* name)
275 {
276    WNDCLASSEXA  wca;
277
278    if (!GetClassInfoEx(0, name, &wca)) {
279       DEBUG_Printf(DBG_CHN_MESG, "Cannot find class '%s'\n", name);
280       return;
281    }
282
283    DEBUG_Printf(DBG_CHN_MESG,  "Class '%s':\n", name);
284    DEBUG_Printf(DBG_CHN_MESG,
285                 "style=%08x  wndProc=%08lx\n"
286                 "inst=%p  icon=%p  cursor=%p  bkgnd=%p\n"
287                 "clsExtra=%d  winExtra=%d\n",
288                 wca.style, (DWORD)wca.lpfnWndProc, wca.hInstance,
289                 wca.hIcon, wca.hCursor, wca.hbrBackground,
290                 wca.cbClsExtra, wca.cbWndExtra);
291
292    /* FIXME:
293     * + print #windows (or even list of windows...)
294     * + print extra bytes => this requires a window handle on this very class...
295     */
296 }
297
298 static  void DEBUG_InfoClass2(HWND hWnd, const char* name)
299 {
300    WNDCLASSEXA  wca;
301
302    if (!GetClassInfoEx((HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), name, &wca)) {
303       DEBUG_Printf(DBG_CHN_MESG, "Cannot find class '%s'\n", name);
304       return;
305    }
306
307    DEBUG_Printf(DBG_CHN_MESG,  "Class '%s':\n", name);
308    DEBUG_Printf(DBG_CHN_MESG,
309                 "style=%08x  wndProc=%08lx\n"
310                 "inst=%p  icon=%p  cursor=%p  bkgnd=%p\n"
311                 "clsExtra=%d  winExtra=%d\n",
312                 wca.style, (DWORD)wca.lpfnWndProc, wca.hInstance,
313                 wca.hIcon, wca.hCursor, wca.hbrBackground,
314                 wca.cbClsExtra, wca.cbWndExtra);
315
316    if (wca.cbClsExtra) {
317       int               i;
318       WORD              w;
319
320       DEBUG_Printf(DBG_CHN_MESG,  "Extra bytes:" );
321       for (i = 0; i < wca.cbClsExtra / 2; i++) {
322          w = GetClassWord(hWnd, i * 2);
323          /* FIXME: depends on i386 endian-ity */
324          DEBUG_Printf(DBG_CHN_MESG,  " %02x", HIBYTE(w));
325          DEBUG_Printf(DBG_CHN_MESG,  " %02x", LOBYTE(w));
326       }
327       DEBUG_Printf(DBG_CHN_MESG,  "\n" );
328     }
329     DEBUG_Printf(DBG_CHN_MESG,  "\n" );
330 }
331
332 struct class_walker {
333    ATOM*        table;
334    int          used;
335    int          alloc;
336 };
337
338 static  void DEBUG_WalkClassesHelper(HWND hWnd, struct class_walker* cw)
339 {
340    char clsName[128];
341    int  i;
342    ATOM atom;
343    HWND child;
344
345    if (!GetClassName(hWnd, clsName, sizeof(clsName)))
346       return;
347    if ((atom = FindAtom(clsName)) == 0)
348       return;
349
350    for (i = 0; i < cw->used; i++) {
351       if (cw->table[i] == atom)
352          break;
353    }
354    if (i == cw->used) {
355       if (cw->used >= cw->alloc) {
356          cw->alloc += 16;
357          cw->table = DBG_realloc(cw->table, cw->alloc * sizeof(ATOM));
358       }
359       cw->table[cw->used++] = atom;
360       DEBUG_InfoClass2(hWnd, clsName);
361    }
362    do {
363       if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
364          DEBUG_WalkClassesHelper(child, cw);
365    } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
366 }
367
368 void DEBUG_WalkClasses(void)
369 {
370    struct class_walker cw;
371
372    cw.table = NULL;
373    cw.used = cw.alloc = 0;
374    DEBUG_WalkClassesHelper(GetDesktopWindow(), &cw);
375    DBG_free(cw.table);
376 }
377
378 void DEBUG_DumpQueue(DWORD q)
379 {
380    DEBUG_Printf(DBG_CHN_MESG, "No longer doing info queue '0x%08lx'\n", q);
381 }
382
383 void DEBUG_WalkQueues(void)
384 {
385    DEBUG_Printf(DBG_CHN_MESG, "No longer walking queues list\n");
386 }
387
388 void DEBUG_InfoWindow(HWND hWnd)
389 {
390    char clsName[128];
391    char wndName[128];
392    RECT clientRect;
393    RECT windowRect;
394    int  i;
395    WORD w;
396
397    if (!GetClassName(hWnd, clsName, sizeof(clsName)))
398        strcpy(clsName, "-- Unknown --");
399    if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
400       strcpy(wndName, "-- Empty --");
401    if (!GetClientRect(hWnd, &clientRect))
402       SetRectEmpty(&clientRect);
403    if (!GetWindowRect(hWnd, &windowRect))
404       SetRectEmpty(&windowRect);
405
406    /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
407    DEBUG_Printf(DBG_CHN_MESG,
408                 "next=%p  child=%p  parent=%p  owner=%p  class='%s'\n"
409                 "inst=%p  active=%p  idmenu=%08lx\n"
410                 "style=%08lx  exstyle=%08lx  wndproc=%08lx  text='%s'\n"
411                 "client=%d,%d-%d,%d  window=%d,%d-%d,%d sysmenu=%p\n",
412                 GetWindow(hWnd, GW_HWNDNEXT),
413                 GetWindow(hWnd, GW_CHILD),
414                 GetParent(hWnd),
415                 GetWindow(hWnd, GW_OWNER),
416                 clsName,
417                 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
418                 GetLastActivePopup(hWnd),
419                 GetWindowLong(hWnd, GWL_ID),
420                 GetWindowLong(hWnd, GWL_STYLE),
421                 GetWindowLong(hWnd, GWL_EXSTYLE),
422                 GetWindowLong(hWnd, GWL_WNDPROC),
423                 wndName,
424                 clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
425                 windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
426                 GetSystemMenu(hWnd, FALSE));
427
428     if (GetClassLong(hWnd, GCL_CBWNDEXTRA)) {
429         DEBUG_Printf(DBG_CHN_MESG,  "Extra bytes:" );
430         for (i = 0; i < GetClassLong(hWnd, GCL_CBWNDEXTRA) / 2; i++) {
431            w = GetWindowWord(hWnd, i * 2);
432            /* FIXME: depends on i386 endian-ity */
433            DEBUG_Printf(DBG_CHN_MESG, " %02x", HIBYTE(w));
434            DEBUG_Printf(DBG_CHN_MESG, " %02x", LOBYTE(w));
435         }
436         DEBUG_Printf(DBG_CHN_MESG, "\n");
437     }
438     DEBUG_Printf(DBG_CHN_MESG, "\n");
439 }
440
441 void DEBUG_WalkWindows(HWND hWnd, int indent)
442 {
443    char clsName[128];
444    char wndName[128];
445    HWND child;
446
447    if (!IsWindow(hWnd))
448       hWnd = GetDesktopWindow();
449
450     if (!indent)  /* first time around */
451        DEBUG_Printf(DBG_CHN_MESG,
452                     "%-16.16s %-17.17s %-8.8s %s\n",
453                     "hwnd", "Class Name", " Style", " WndProc Text");
454
455     do {
456        if (!GetClassName(hWnd, clsName, sizeof(clsName)))
457           strcpy(clsName, "-- Unknown --");
458        if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
459           strcpy(wndName, "-- Empty --");
460
461        /* FIXME: missing hmemTaskQ */
462        DEBUG_Printf(DBG_CHN_MESG, "%*s%04x%*s", indent, "", (UINT)hWnd, 13-indent,"");
463        DEBUG_Printf(DBG_CHN_MESG, "%-17.17s %08lx %08lx %.14s\n",
464                     clsName, GetWindowLong(hWnd, GWL_STYLE),
465                     GetWindowLong(hWnd, GWL_WNDPROC), wndName);
466
467        if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
468           DEBUG_WalkWindows(child, indent + 1 );
469     } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
470 }
471
472 void DEBUG_WalkProcess(void)
473 {
474     HANDLE snap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
475     if (snap != INVALID_HANDLE_VALUE)
476     {
477         PROCESSENTRY32 entry;
478         DWORD current = DEBUG_CurrProcess ? DEBUG_CurrProcess->pid : 0;
479         BOOL ok;
480
481         entry.dwSize = sizeof(entry);
482         ok = Process32First( snap, &entry );
483
484         DEBUG_Printf(DBG_CHN_MESG, " %-8.8s %-8.8s %-8.8s %s\n",
485                      "pid", "threads", "parent", "executable" );
486         while (ok)
487         {
488             if (entry.th32ProcessID != GetCurrentProcessId())
489                 DEBUG_Printf(DBG_CHN_MESG, "%c%08lx %-8ld %08lx '%s'\n",
490                              (entry.th32ProcessID == current) ? '>' : ' ',
491                              entry.th32ProcessID, entry.cntThreads,
492                              entry.th32ParentProcessID, entry.szExeFile);
493             ok = Process32Next( snap, &entry );
494         }
495         CloseHandle( snap );
496     }
497 }
498
499 void DEBUG_WalkThreads(void)
500 {
501     HANDLE snap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
502     if (snap != INVALID_HANDLE_VALUE)
503     {
504         THREADENTRY32   entry;
505         DWORD           current = DEBUG_CurrThread ? DEBUG_CurrThread->tid : 0;
506         BOOL            ok;
507         DWORD           lastProcessId = 0;
508
509         entry.dwSize = sizeof(entry);
510         ok = Thread32First( snap, &entry );
511
512         DEBUG_Printf(DBG_CHN_MESG, "%-8.8s %-8.8s %s\n", "process", "tid", "prio" );
513         while (ok)
514         {
515             if (entry.th32OwnerProcessID != GetCurrentProcessId())
516             {
517                 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
518                  * listed sequentially, which is not specified in the doc (Wine's implementation
519                  * does it)
520                  */
521                 if (entry.th32OwnerProcessID != lastProcessId)
522                 {
523                     DBG_PROCESS*        p = DEBUG_GetProcess(entry.th32OwnerProcessID);
524
525                     DEBUG_Printf(DBG_CHN_MESG, "%08lx%s %s\n",
526                                  entry.th32OwnerProcessID,  p ? " (D)" : "", p ? p->imageName : "");
527                     lastProcessId = entry.th32OwnerProcessID;
528                 }
529                 DEBUG_Printf(DBG_CHN_MESG, "\t%08lx %4ld%s\n",
530                              entry.th32ThreadID, entry.tpBasePri,
531                              (entry.th32ThreadID == current) ? " <==" : "");
532
533             }
534             ok = Thread32Next( snap, &entry );
535         }
536
537         CloseHandle( snap );
538     }
539 }
540
541 void DEBUG_WalkModref(DWORD p)
542 {
543    DEBUG_Printf(DBG_CHN_MESG, "No longer walking module references list\n");
544 }
545
546 void DEBUG_InfoSegments(DWORD start, int length)
547 {
548     char        flags[3];
549     DWORD       i;
550     LDT_ENTRY   le;
551
552     if (length == -1) length = (8192 - start);
553
554     for (i = start; i < start + length; i++)
555     {
556        if (!GetThreadSelectorEntry(DEBUG_CurrThread->handle, (i << 3)|7, &le))
557           continue;
558
559         if (le.HighWord.Bits.Type & 0x08)
560         {
561             flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
562             flags[1] = '-';
563             flags[2] = 'x';
564         }
565         else
566         {
567             flags[0] = 'r';
568             flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
569             flags[2] = '-';
570         }
571         DEBUG_Printf(DBG_CHN_MESG,
572                      "%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
573                      i, (i<<3)|7,
574                      (le.HighWord.Bits.BaseHi << 24) +
575                      (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
576                      ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
577                      (le.HighWord.Bits.Granularity ? 12 : 0),
578                      le.HighWord.Bits.Default_Big ? 32 : 16,
579                      flags[0], flags[1], flags[2] );
580     }
581 }
582
583 void DEBUG_InfoVirtual(void)
584 {
585     MEMORY_BASIC_INFORMATION    mbi;
586     char*                       addr = 0;
587     char*                       state;
588     char*                       type;
589     char                        prot[3+1];
590
591     if (DEBUG_CurrProcess == NULL)
592         return;
593
594     DEBUG_Printf(DBG_CHN_MESG, "Address  Size     State   Type    RWX\n");
595
596     while (VirtualQueryEx(DEBUG_CurrProcess->handle, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
597     {
598         switch (mbi.State)
599         {
600         case MEM_COMMIT:        state = "commit "; break;
601         case MEM_FREE:          state = "free   "; break;
602         case MEM_RESERVE:       state = "reserve"; break;
603         default:                state = "???    "; break;
604         }
605         if (mbi.State != MEM_FREE)
606         {
607             switch (mbi.Type)
608             {
609             case MEM_IMAGE:         type = "image  "; break;
610             case MEM_MAPPED:        type = "mapped "; break;
611             case MEM_PRIVATE:       type = "private"; break;
612             case 0:                 type = "       "; break;
613             default:                type = "???    "; break;
614             }
615             memset(prot, ' ' , sizeof(prot)-1);
616             prot[sizeof(prot)-1] = '\0';
617             if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
618                 prot[0] = 'R';
619             if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
620                 prot[1] = 'W';
621             if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
622                 prot[1] = 'C';
623             if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
624                 prot[2] = 'X';
625         }
626         else
627         {
628             type = "";
629             prot[0] = '\0';
630         }
631         DEBUG_Printf(DBG_CHN_MESG, "%08lx %08lx %s %s %s\n",
632                      (DWORD)addr, mbi.RegionSize, state, type, prot);
633         if (addr + mbi.RegionSize < addr) /* wrap around ? */
634             break;
635         addr += mbi.RegionSize;
636     }
637 }
638
639 struct dll_option_layout
640 {
641     void*               next;
642     void*               prev;
643     char* const*        channels;
644     int                 nb_channels;
645 };
646
647 void DEBUG_DbgChannel(BOOL turn_on, const char* chnl, const char* name)
648 {
649     DBG_VALUE                   val;
650     struct dll_option_layout    dol;
651     int                         i;
652     char*                       str;
653     unsigned char               buffer[32];
654     unsigned char               mask;
655     int                         done = 0;
656     BOOL                        bAll;
657     void*                       addr;
658
659     if (!DEBUG_GetSymbolValue("first_dll", -1, &val, FALSE))
660     {
661         DEBUG_Printf(DBG_CHN_MESG, "Can't get first_option symbol");
662         return;
663     }
664     addr = (void*)DEBUG_ToLinear(&val.addr);
665     if (!chnl)                          mask = 15;
666     else if (!strcmp(chnl, "fixme"))    mask = 1;
667     else if (!strcmp(chnl, "err"))      mask = 2;
668     else if (!strcmp(chnl, "warn"))     mask = 4;
669     else if (!strcmp(chnl, "trace"))    mask = 8;
670     else { DEBUG_Printf(DBG_CHN_MESG, "Unknown channel %s\n", chnl); return; }
671
672     bAll = !strcmp("all", name);
673     while (addr && DEBUG_READ_MEM(addr, &dol, sizeof(dol)))
674     {
675         for (i = 0; i < dol.nb_channels; i++)
676         {
677             if (DEBUG_READ_MEM((void*)(dol.channels + i), &str, sizeof(str)) &&
678                 DEBUG_READ_MEM(str, buffer, sizeof(buffer)) &&
679                 (!strcmp(buffer + 1, name) || bAll))
680             {
681                 if (turn_on) buffer[0] |= mask; else buffer[0] &= ~mask;
682                 if (DEBUG_WRITE_MEM(str, buffer, 1)) done++;
683             }
684         }
685         addr = dol.next;
686     }
687     if (!done) DEBUG_Printf(DBG_CHN_MESG, "Unable to find debug channel %s\n", name);
688     else DEBUG_Printf(DBG_CHN_TRACE, "Changed %d channel instances\n", done);
689 }