info.c 23.7 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2
/*
 * Wine debugger utility routines
Alexandre Julliard's avatar
Alexandre Julliard committed
3 4 5
 *
 * Copyright 1993 Eric Youngdale
 * Copyright 1995 Alexandre Julliard
6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Alexandre Julliard's avatar
Alexandre Julliard committed
20 21
 */

22
#include "config.h"
23

Alexandre Julliard's avatar
Alexandre Julliard committed
24
#include <stdlib.h>
25
#include <stdio.h>
26
#include <string.h>
27 28
#include <stdarg.h>

29
#include "debugger.h"
30 31
#include "wingdi.h"
#include "winuser.h"
32
#include "tlhelp32.h"
33 34 35
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
Alexandre Julliard's avatar
Alexandre Julliard committed
36

Alexandre Julliard's avatar
Alexandre Julliard committed
37
/***********************************************************************
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
 *           print_help
 *
 * Implementation of the 'help' command.
 */
void print_help(void)
{
    int i = 0;
    static const char * const helptext[] =
        {
            "The commands accepted by the Wine debugger are a reasonable",
            "subset of the commands that gdb accepts.",
            "The commands currently are:",
            "  help                                   quit",
            "  break [*<addr>]                        watch *<addr>",
            "  delete break bpnum                     disable bpnum",
            "  enable bpnum                           condition <bpnum> [<expr>]",
            "  finish                                 cont [N]",
            "  step [N]                               next [N]",
            "  stepi [N]                              nexti [N]",
            "  x <addr>                               print <expr>",
            "  display <expr>                         undisplay <disnum>",
            "  local display <expr>                   delete display <disnum>",                  
            "  enable display <disnum>                disable display <disnum>",
61
            "  bt [<tid>|all]                         frame <n>",
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
            "  up                                     down",
            "  list <lines>                           disassemble [<addr>][,<addr>]",
            "  show dir                               dir <path>",
            "  set <reg> = <expr>                     set *<addr> = <expr>",
            "  mode [16,32,vm86]                      pass",
            "  whatis                                 info (see 'help info' for options)",

            "The 'x' command accepts repeat counts and formats (including 'i') in the",
            "same way that gdb does.\n",

            "The following are examples of legal expressions:",
            " $eax     $eax+0x3   0x1000   ($eip + 256)  *$eax   *($esp + 3)",
            " Also, a nm format symbol table can be read from a file using the",
            " symbolfile command.", /*  Symbols can also be defined individually with",
                                        " the define command.", */
            "",
            NULL
        };

    while (helptext[i]) dbg_printf("%s\n", helptext[i++]);
}


/***********************************************************************
 *           info_help
Alexandre Julliard's avatar
Alexandre Julliard committed
87
 *
88
 * Implementation of the 'help info' command.
Alexandre Julliard's avatar
Alexandre Julliard committed
89
 */
90
void info_help(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
91
{
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    int i = 0;
    static const char * const infotext[] =
        {
            "The info commands allow you to get assorted bits of interesting stuff",
            "to be displayed.  The options are:",
            "  info break           Displays information about breakpoints",
            "  info class <name>    Displays information about window class <name>",
            "  info display         Shows auto-display expressions in use",
            "  info except <pid>    Shows exception handler chain (in a given process)",
            "  info locals          Displays values of all local vars for current frame",
            "  info maps <pid>      Shows virtual mappings (in a given process)",
            "  info process         Shows all running processes",
            "  info reg             Displays values in all registers at top of stack",
            "  info segments <pid>  Displays information about all known segments",
            "  info share           Displays all loaded modules",
            "  info share <addr>    Displays internal module state",
            "  info stack           Dumps information about top of stack",
            "  info symbol <sym>    Displays information about a given symbol",
            "  info thread          Shows all running threads",
            "  info wnd <handle>    Displays internal window state",
            "",
            NULL
        };

    while (infotext[i]) dbg_printf("%s\n", infotext[i++]);
}
118

119 120 121
static const char* get_symtype_str(SYM_TYPE st)
{
    switch (st)
Alexandre Julliard's avatar
Alexandre Julliard committed
122
    {
123
    case -1:            return "\\";
124 125 126 127 128 129 130 131 132
    default:
    case SymNone:       return "--none--";
    case SymCoff:       return "COFF";
    case SymCv:         return "CodeView";
    case SymPdb:        return "PDB";
    case SymExport:     return "Export";
    case SymDeferred:   return "Deferred";
    case SymSym:        return "Sym";
    case SymDia:        return "DIA";
133
    case NumSymTypes:   return "Stabs";
Alexandre Julliard's avatar
Alexandre Julliard committed
134
    }
135
}
136

137 138 139 140 141 142
struct info_module
{
    IMAGEHLP_MODULE*    mi;
    unsigned            num_alloc;
    unsigned            num_used;
};
143

144
static void module_print_info(const IMAGEHLP_MODULE* mi, SYM_TYPE st)
145 146 147
{
    dbg_printf("0x%08lx-%08lx\t%-16s%s\n",
               mi->BaseOfImage, mi->BaseOfImage + mi->ImageSize,
148
               get_symtype_str(st), mi->ModuleName);
149 150 151 152 153 154 155
}

static int      module_compare(const void* p1, const void* p2)
{
    return (char*)(((const IMAGEHLP_MODULE*)p1)->BaseOfImage) -
        (char*)(((const IMAGEHLP_MODULE*)p2)->BaseOfImage);
}
156

157 158 159 160 161 162 163
static inline BOOL module_is_container(const IMAGEHLP_MODULE* wmod_cntnr,
                                     const IMAGEHLP_MODULE* wmod_child)
{
    return wmod_cntnr->BaseOfImage <= wmod_child->BaseOfImage &&
        (DWORD)wmod_cntnr->BaseOfImage + wmod_cntnr->ImageSize >=
        (DWORD)wmod_child->BaseOfImage + wmod_child->ImageSize;
}
164

165 166 167
static BOOL CALLBACK info_mod_cb(PSTR mod_name, DWORD base, void* ctx)
{
    struct info_module* im = (struct info_module*)ctx;
168

169
    if (im->num_used + 1 > im->num_alloc)
170
    {
171 172
        im->num_alloc += 16;
        im->mi = dbg_heap_realloc(im->mi, im->num_alloc * sizeof(*im->mi));
173
    }
174 175 176 177 178 179 180
    im->mi[im->num_used].SizeOfStruct = sizeof(im->mi[im->num_used]);
    if (SymGetModuleInfo(dbg_curr_process->handle, base, &im->mi[im->num_used]))
    {
        im->num_used++;
    }   
    return TRUE;
}
181

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
/***********************************************************************
 *           info_win32_module
 *
 * Display information about a given module (DLL or EXE), or about all modules
 */
void info_win32_module(DWORD base)
{
    if (!dbg_curr_process || !dbg_curr_thread)
    {
        dbg_printf("Cannot get info on module while no process is loaded\n");
        return;
    }

    if (base)
    {
        IMAGEHLP_MODULE     mi;

        mi.SizeOfStruct = sizeof(mi);
200

201
        if (!SymGetModuleInfo(dbg_curr_process->handle, base, &mi))
202
        {
203 204 205
            dbg_printf("'0x%08lx' is not a valid module address\n", base);
            return;
        }
206
        module_print_info(&mi, mi.SymType);
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
    }
    else
    {
        struct info_module      im;
        int			i, j;
        DWORD                   opt;

        im.mi = NULL;
        im.num_alloc = im.num_used = 0;

        /* this is a wine specific options to return also ELF modules in the
         * enumeration
         */
        SymSetOptions((opt = SymGetOptions()) | 0x40000000);
        SymEnumerateModules(dbg_curr_process->handle, info_mod_cb, (void*)&im);
        SymSetOptions(opt);

        qsort(im.mi, im.num_used, sizeof(im.mi[0]), module_compare);
225

226 227 228 229 230
        dbg_printf("Module\tAddress\t\t\tDebug info\tName (%d modules)\n", im.num_used);

        for (i = 0; i < im.num_used; i++)
        {
            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 235
                /* print all modules embedded in this one */
                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 245 246 247
            }
            else
            {
                /* check module is not embedded in another module */
                for (j = 0; j < im.num_used; j++) 
248
                {
249 250
                    if (strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[j], &im.mi[i]))
                        break;
251
                }
252 253 254 255 256
                if (j < im.num_used) continue;
                if (strstr(im.mi[i].ModuleName, ".so") || strchr(im.mi[i].ModuleName, '<'))
                    dbg_printf("ELF\t");
                else
                    dbg_printf("PE\t");
257
                module_print_info(&im.mi[i], im.mi[i].SymType);
258
            }
259
        }
260
        HeapFree(GetProcessHeap(), 0, im.mi);
Alexandre Julliard's avatar
Alexandre Julliard committed
261
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
262
}
Alexandre Julliard's avatar
Alexandre Julliard committed
263

264
struct class_walker
Alexandre Julliard's avatar
Alexandre Julliard committed
265
{
266 267 268 269
    ATOM*	table;
    int		used;
    int		alloc;
};
Alexandre Julliard's avatar
Alexandre Julliard committed
270

271
static void class_walker(HWND hWnd, struct class_walker* cw)
Alexandre Julliard's avatar
Alexandre Julliard committed
272
{
273 274 275 276
    char	clsName[128];
    int	        i;
    ATOM	atom;
    HWND	child;
Alexandre Julliard's avatar
Alexandre Julliard committed
277

278 279 280 281
    if (!GetClassName(hWnd, clsName, sizeof(clsName)))
        return;
    if ((atom = FindAtom(clsName)) == 0)
        return;
Alexandre Julliard's avatar
Alexandre Julliard committed
282

283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
    for (i = 0; i < cw->used; i++)
    {
        if (cw->table[i] == atom)
            break;
    }
    if (i == cw->used)
    {
        if (cw->used >= cw->alloc)
        {
            cw->alloc += 16;
            cw->table = dbg_heap_realloc(cw->table, cw->alloc * sizeof(ATOM));
        }
        cw->table[cw->used++] = atom;
        info_win32_class(hWnd, clsName);
    }
    do
    {
        if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
            class_walker(child, cw);
    } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
303 304
}

305
void info_win32_class(HWND hWnd, const char* name)
Alexandre Julliard's avatar
Alexandre Julliard committed
306
{
307
    WNDCLASSEXA	wca;
308
    HINSTANCE   hInst = hWnd ? (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE) : 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
309

310 311 312
    if (!name)
    {
        struct class_walker cw;
Alexandre Julliard's avatar
Alexandre Julliard committed
313

314 315 316 317 318 319
        cw.table = NULL;
        cw.used = cw.alloc = 0;
        class_walker(GetDesktopWindow(), &cw);
        HeapFree(GetProcessHeap(), 0, cw.table);
        return;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
320

321 322 323 324 325
    if (!GetClassInfoEx(hInst, name, &wca))
    {
        dbg_printf("Cannot find class '%s'\n", name);
        return;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
326

327 328 329 330 331 332 333
    dbg_printf("Class '%s':\n", name);
    dbg_printf("style=0x%08x  wndProc=0x%08lx\n"
               "inst=%p  icon=%p  cursor=%p  bkgnd=%p\n"
               "clsExtra=%d  winExtra=%d\n",
               wca.style, (DWORD)wca.lpfnWndProc, wca.hInstance,
               wca.hIcon, wca.hCursor, wca.hbrBackground,
               wca.cbClsExtra, wca.cbWndExtra);
334

335 336 337 338
    if (hWnd && wca.cbClsExtra)
    {
        int		i;
        WORD		w;
339

340 341 342 343 344 345 346 347
        dbg_printf("Extra bytes:");
        for (i = 0; i < wca.cbClsExtra / 2; i++)
        {
            w = GetClassWord(hWnd, i * 2);
            /* FIXME: depends on i386 endian-ity */
            dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
        }
        dbg_printf("\n");
348
    }
349 350 351 352 353
    dbg_printf("\n");
    /* FIXME:
     * + print #windows (or even list of windows...)
     * + print extra bytes => this requires a window handle on this very class...
     */
354 355
}

356
static void info_window(HWND hWnd, int indent)
357
{
358 359 360 361 362 363 364 365 366 367 368
    char	clsName[128];
    char	wndName[128];
    HWND	child;

    do
    {
        if (!GetClassName(hWnd, clsName, sizeof(clsName)))
            strcpy(clsName, "-- Unknown --");
        if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
            strcpy(wndName, "-- Empty --");

369
        dbg_printf("%*s%08x%*s %-17.17s %08lx %08lx %08lx %.14s\n",
370 371
                   indent, "", (UINT)hWnd, 12 - indent, "",
                   clsName, GetWindowLong(hWnd, GWL_STYLE),
372
                   GetWindowLongPtr(hWnd, GWLP_WNDPROC),
373
                   GetWindowThreadProcessId(hWnd, NULL), wndName);
374 375 376 377

        if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
            info_window(child, indent + 1);
    } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
378 379
}

380
void info_win32_window(HWND hWnd, BOOL detailed)
381
{
382 383 384 385 386 387
    char	clsName[128];
    char	wndName[128];
    RECT	clientRect;
    RECT	windowRect;
    int         i;
    WORD	w;
388

389
    if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
390

391 392
    if (!detailed)
    {
393 394
        dbg_printf("%-20.20s %-17.17s %-8.8s %-8.8s %-8.8s %s\n",
                   "Window handle", "Class Name", "Style", "WndProc", "Thread", "Text");
395 396
        info_window(hWnd, 0);
        return;
397 398
    }

399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
    if (!GetClassName(hWnd, clsName, sizeof(clsName)))
        strcpy(clsName, "-- Unknown --");
    if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
        strcpy(wndName, "-- Empty --");
    if (!GetClientRect(hWnd, &clientRect) || 
        !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
        SetRectEmpty(&clientRect);
    if (!GetWindowRect(hWnd, &windowRect))
        SetRectEmpty(&windowRect);

    /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
    dbg_printf("next=%p  child=%p  parent=%p  owner=%p  class='%s'\n"
               "inst=%p  active=%p  idmenu=%08lx\n"
               "style=0x%08lx  exstyle=0x%08lx  wndproc=0x%08lx  text='%s'\n"
               "client=%ld,%ld-%ld,%ld  window=%ld,%ld-%ld,%ld sysmenu=%p\n",
               GetWindow(hWnd, GW_HWNDNEXT),
               GetWindow(hWnd, GW_CHILD),
               GetParent(hWnd),
               GetWindow(hWnd, GW_OWNER),
               clsName,
419
               (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
420
               GetLastActivePopup(hWnd),
421
               GetWindowLongPtr(hWnd, GWLP_ID),
422 423
               GetWindowLong(hWnd, GWL_STYLE),
               GetWindowLong(hWnd, GWL_EXSTYLE),
424
               GetWindowLongPtr(hWnd, GWLP_WNDPROC),
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
               wndName,
               clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
               windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
               GetSystemMenu(hWnd, FALSE));

    if (GetClassLong(hWnd, GCL_CBWNDEXTRA))
    {
        dbg_printf("Extra bytes:");
        for (i = 0; i < GetClassLong(hWnd, GCL_CBWNDEXTRA) / 2; i++)
        {
            w = GetWindowWord(hWnd, i * 2);
            /* FIXME: depends on i386 endian-ity */
            dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
	}
        dbg_printf("\n");
    }
    dbg_printf("\n");
442 443
}

444
void info_win32_processes(void)
445
{
446
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
447 448
    if (snap != INVALID_HANDLE_VALUE)
    {
449 450 451
        PROCESSENTRY32  entry;
        DWORD           current = dbg_curr_process ? dbg_curr_process->pid : 0;
        BOOL            ok;
452

453
        entry.dwSize = sizeof(entry);
454
        ok = Process32First(snap, &entry);
455

456 457
        dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
                   "pid", "threads", "parent", "executable");
458 459 460
        while (ok)
        {
            if (entry.th32ProcessID != GetCurrentProcessId())
461 462 463 464 465
                dbg_printf("%c%08lx %-8ld %08lx '%s'\n",
                           (entry.th32ProcessID == current) ? '>' : ' ',
                           entry.th32ProcessID, entry.cntThreads,
                           entry.th32ParentProcessID, entry.szExeFile);
            ok = Process32Next(snap, &entry);
466
        }
467
        CloseHandle(snap);
468 469 470
    }
}

471
void info_win32_threads(void)
472
{
473
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
474 475
    if (snap != INVALID_HANDLE_VALUE)
    {
476 477 478
        THREADENTRY32	entry;
        BOOL 		ok;
	DWORD		lastProcessId = 0;
479

480
	entry.dwSize = sizeof(entry);
481
	ok = Thread32First(snap, &entry);
482

483 484
        dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
                   "process", "tid", "prio");
485 486 487
        while (ok)
        {
            if (entry.th32OwnerProcessID != GetCurrentProcessId())
488 489 490 491 492 493 494
	    {
		/* FIXME: this assumes that, in the snapshot, all threads of a same process are
		 * listed sequentially, which is not specified in the doc (Wine's implementation
		 * does it)
		 */
		if (entry.th32OwnerProcessID != lastProcessId)
		{
495
		    struct dbg_process*	p = dbg_get_process(entry.th32OwnerProcessID);
496

497 498
		    dbg_printf("%08lx%s %s\n",
                               entry.th32OwnerProcessID, p ? " (D)" : "", p ? p->imageName : "");
499 500
		    lastProcessId = entry.th32OwnerProcessID;
		}
501 502 503
                dbg_printf("\t%08lx %4ld%s\n",
                           entry.th32ThreadID, entry.tpBasePri,
                           (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
504 505

	    }
506
            ok = Thread32Next(snap, &entry);
507
        }
508

509
        CloseHandle(snap);
510
    }
511 512
}

513
/***********************************************************************
514
 *           info_win32_exceptions
515
 *
516
 * Get info on the exception frames of a given thread.
517
 */
518
void info_win32_exceptions(DWORD tid)
519
{
520 521
    struct dbg_thread*  thread;
    void*               next_frame;
522

523
    if (!dbg_curr_process || !dbg_curr_thread)
524
    {
525
        dbg_printf("Cannot get info on exceptions while no process is loaded\n");
526 527 528
        return;
    }

529
    dbg_printf("Exception frames:\n");
530

531
    if (tid == dbg_curr_tid) thread = dbg_curr_thread;
532 533
    else
    {
534 535 536 537 538 539 540 541 542 543 544 545
        thread = dbg_get_thread(dbg_curr_process, tid);

        if (!thread)
        {
            dbg_printf("Unknown thread id (0x%08lx) in current process\n", tid);
            return;
        }
        if (SuspendThread(thread->handle) == -1)
        {
            dbg_printf("Can't suspend thread id (0x%08lx)\n", tid);
            return;
        }
546 547
    }

548
    if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
549
    {
550
        dbg_printf("Can't read TEB:except_frame\n");
551 552 553
        return;
    }

554
    while (next_frame != (void*)-1)
555
    {
556
        EXCEPTION_REGISTRATION_RECORD frame;
557

558 559
        dbg_printf("%p: ", next_frame);
        if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
560
        {
561
            dbg_printf("Invalid frame address\n");
562 563
            break;
        }
564
        dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
565 566 567
        next_frame = frame.Prev;
    }

568
    if (tid != dbg_curr_tid) ResumeThread(thread->handle);
569 570
}

571
void info_win32_segments(DWORD start, int length)
572 573 574 575 576 577 578 579 580
{
    char 	flags[3];
    DWORD 	i;
    LDT_ENTRY	le;

    if (length == -1) length = (8192 - start);

    for (i = start; i < start + length; i++)
    {
581
        if (!GetThreadSelectorEntry(dbg_curr_thread->handle, (i << 3) | 7, &le))
582
            continue;
583

584
        if (le.HighWord.Bits.Type & 0x08)
585 586 587 588 589 590 591 592 593 594 595
        {
            flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
            flags[1] = '-';
            flags[2] = 'x';
        }
        else
        {
            flags[0] = 'r';
            flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
            flags[2] = '-';
        }
596 597 598 599 600 601 602 603
        dbg_printf("%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
                   i, (i << 3) | 7,
                   (le.HighWord.Bits.BaseHi << 24) +
                   (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
                   ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
                   (le.HighWord.Bits.Granularity ? 12 : 0),
                   le.HighWord.Bits.Default_Big ? 32 : 16,
                   flags[0], flags[1], flags[2]);
604 605 606
    }
}

607
void info_win32_virtual(DWORD pid)
608
{
609 610
    MEMORY_BASIC_INFORMATION    mbi;
    char*                       addr = 0;
611 612
    const char*                 state;
    const char*                 type;
613
    char                        prot[3+1];
614
    HANDLE                      hProc;
615

616
    if (pid == dbg_curr_pid)
617
    {
618
        if (dbg_curr_process == NULL)
619
        {
620
            dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
621 622
            return;
        }
623
        hProc = dbg_curr_process->handle;
624 625 626 627 628 629
    }
    else
    {
        hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
        if (hProc == NULL)
        {
630
            dbg_printf("Cannot open process <%lu>\n", pid);
631 632 633
            return;
        }
    }
634

635
    dbg_printf("Address  Size     State   Type    RWX\n");
636

637
    while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
    {
        switch (mbi.State)
        {
        case MEM_COMMIT:        state = "commit "; break;
        case MEM_FREE:          state = "free   "; break;
        case MEM_RESERVE:       state = "reserve"; break;
        default:                state = "???    "; break;
        }
        if (mbi.State != MEM_FREE)
        {
            switch (mbi.Type)
            {
            case MEM_IMAGE:         type = "image  "; break;
            case MEM_MAPPED:        type = "mapped "; break;
            case MEM_PRIVATE:       type = "private"; break;
            case 0:                 type = "       "; break;
            default:                type = "???    "; break;
            }
656 657
            memset(prot, ' ' , sizeof(prot) - 1);
            prot[sizeof(prot) - 1] = '\0';
658 659 660 661 662 663 664 665 666 667 668 669 670 671
            if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
                prot[0] = 'R';
            if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
                prot[1] = 'W';
            if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
                prot[1] = 'C';
            if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
                prot[2] = 'X';
        }
        else
        {
            type = "";
            prot[0] = '\0';
        }
672 673
        dbg_printf("%08lx %08lx %s %s %s\n",
                   (DWORD)addr, (DWORD)addr + mbi.RegionSize - 1, state, type, prot);
674 675 676 677
        if (addr + mbi.RegionSize < addr) /* wrap around ? */
            break;
        addr += mbi.RegionSize;
    }
678
    if (pid != dbg_curr_pid) CloseHandle(hProc);
679
}
680

681
void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
682
{
683
    struct dbg_lvalue           lvalue;
684
    struct __wine_debug_channel channel;
685 686 687 688 689
    unsigned char               mask;
    int                         done = 0;
    BOOL                        bAll;
    void*                       addr;

690 691 692 693 694 695
    if (!dbg_curr_process || !dbg_curr_thread)
    {
        dbg_printf("Cannot set/get debug channels while no process is loaded\n");
        return;
    }

696
    if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
697 698 699
    {
        return;
    }
700
    addr = memory_to_linear_addr(&lvalue.addr);
701 702 703 704 705 706 707 708 709 710 711

    if (!cls)                          mask = ~0;
    else if (!strcmp(cls, "fixme"))    mask = (1 << __WINE_DBCL_FIXME);
    else if (!strcmp(cls, "err"))      mask = (1 << __WINE_DBCL_ERR);
    else if (!strcmp(cls, "warn"))     mask = (1 << __WINE_DBCL_WARN);
    else if (!strcmp(cls, "trace"))    mask = (1 << __WINE_DBCL_TRACE);
    else
    {
        dbg_printf("Unknown debug class %s\n", cls);
        return;
    }
712

713
    bAll = !strcmp("all", name);
714
    while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
715
    {
716 717
        if (!channel.name[0]) break;
        if (bAll || !strcmp( channel.name, name ))
718
        {
719 720 721
            if (turn_on) channel.flags |= mask;
            else channel.flags &= ~mask;
            if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
722
        }
723
        addr = (struct __wine_debug_channel *)addr + 1;
724
    }
725
    if (!done) dbg_printf("Unable to find debug channel %s\n", name);
726
    else WINE_TRACE("Changed %d channel instances\n", done);
727
}