info.c 32.9 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
 *
 * 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
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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
#include "wine/debug.h"
34
#include "wine/exception.h"
35 36

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

Alexandre Julliard's avatar
Alexandre Julliard committed
38
/***********************************************************************
39 40 41 42 43 44 45 46 47 48 49 50 51
 *           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",
52
            "  attach <wpid>                          detach",
53
            "  break [*<addr>]                        watch | rwatch *<addr>",
54 55 56 57 58 59 60 61 62
            "  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>",
63
            "  bt [<tid>|all]                         frame <n>",
64 65 66 67
            "  up                                     down",
            "  list <lines>                           disassemble [<addr>][,<addr>]",
            "  show dir                               dir <path>",
            "  set <reg> = <expr>                     set *<addr> = <expr>",
68
            "  pass                                   whatis",
69
            "  info (see 'help info' for options)     thread <tid>",
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

            "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
89
 *
90
 * Implementation of the 'help info' command.
Alexandre Julliard's avatar
Alexandre Julliard committed
91
 */
92
void info_help(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
93
{
94 95 96 97 98 99 100 101 102 103 104 105
    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",
106 107
            "  info reg             Displays values of the general registers at top of stack",
            "  info all-reg         Displays the general and floating point registers",
108 109 110
            "  info segments <pid>  Displays information about all known segments",
            "  info share           Displays all loaded modules",
            "  info share <addr>    Displays internal module state",
111
            "  info stack [<len>]   Dumps information about top of stack, up to len words",
112 113 114 115 116 117 118 119 120
            "  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++]);
}
121

122
static const char* get_symtype_str(const IMAGEHLP_MODULE64* mi)
123
{
124
    switch (mi->SymType)
Alexandre Julliard's avatar
Alexandre Julliard committed
125
    {
126 127 128 129 130 131 132 133
    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";
134 135 136 137 138 139 140 141 142 143 144
    case SymDia:
        switch (mi->CVSig)
        {
        case 'S' | ('T' << 8) | ('A' << 16) | ('B' << 24):
            return "Stabs";
        case 'D' | ('W' << 8) | ('A' << 16) | ('R' << 24):
            return "Dwarf";
        default:
            return "DIA";

        }
Alexandre Julliard's avatar
Alexandre Julliard committed
145
    }
146
}
147

148 149
struct info_module
{
150 151 152 153 154 155 156
    IMAGEHLP_MODULE64 mi;
    char              name[64];
};

struct info_modules
{
    struct info_module *modules;
157 158 159
    unsigned            num_alloc;
    unsigned            num_used;
};
160

161
static void module_print_info(const struct info_module *module, BOOL is_embedded)
162
{
163
    dbg_printf("%*.*s-%*.*s\t%-16s%s\n",
164 165 166
               ADDRWIDTH, ADDRWIDTH, wine_dbgstr_longlong(module->mi.BaseOfImage),
               ADDRWIDTH, ADDRWIDTH, wine_dbgstr_longlong(module->mi.BaseOfImage + module->mi.ImageSize),
               is_embedded ? "\\" : get_symtype_str(&module->mi), module->name);
167 168 169 170
}

static int      module_compare(const void* p1, const void* p2)
{
171 172 173 174
    struct info_module *left = (struct info_module *)p1;
    struct info_module *right = (struct info_module *)p2;
    LONGLONG val = left->mi.BaseOfImage - right->mi.BaseOfImage;

175 176 177
    if (val < 0) return -1;
    else if (val > 0) return 1;
    else return 0;
178
}
179

180 181
static inline BOOL module_is_container(const struct info_module *wmod_cntnr,
        const struct info_module *wmod_child)
182
{
183 184 185
    return wmod_cntnr->mi.BaseOfImage <= wmod_child->mi.BaseOfImage &&
        wmod_cntnr->mi.BaseOfImage + wmod_cntnr->mi.ImageSize >=
        wmod_child->mi.BaseOfImage + wmod_child->mi.ImageSize;
186
}
187

188
static BOOL CALLBACK info_mod_cb(PCSTR mod_name, DWORD64 base, PVOID ctx)
189
{
190
    struct info_modules *im = ctx;
191

192
    if (im->num_used + 1 > im->num_alloc)
193
    {
194
        im->num_alloc += 16;
195
        im->modules = dbg_heap_realloc(im->modules, im->num_alloc * sizeof(*im->modules));
196
    }
197 198
    im->modules[im->num_used].mi.SizeOfStruct = sizeof(im->modules[im->num_used].mi);
    if (SymGetModuleInfo64(dbg_curr_process->handle, base, &im->modules[im->num_used].mi))
199
    {
200 201 202
        const int dst_len = sizeof(im->modules[im->num_used].name);
        lstrcpynA(im->modules[im->num_used].name, mod_name, dst_len - 1);
        im->modules[im->num_used].name[dst_len - 1] = 0;
203
        im->num_used++;
204
    }
205 206
    return TRUE;
}
207

208 209 210 211 212
/***********************************************************************
 *           info_win32_module
 *
 * Display information about a given module (DLL or EXE), or about all modules
 */
213
void info_win32_module(DWORD64 base)
214
{
215
    struct info_modules im;
Gerald Pfeifer's avatar
Gerald Pfeifer committed
216
    UINT                i, j, num_printed = 0;
217
    BOOL                opt;
Eric Pouech's avatar
Eric Pouech committed
218

219
    if (!dbg_curr_process)
220 221 222 223 224
    {
        dbg_printf("Cannot get info on module while no process is loaded\n");
        return;
    }

225
    im.modules = NULL;
Eric Pouech's avatar
Eric Pouech committed
226
    im.num_alloc = im.num_used = 0;
227

Eric Pouech's avatar
Eric Pouech committed
228 229 230
    /* this is a wine specific options to return also ELF modules in the
     * enumeration
     */
231
    opt = SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES, TRUE);
232
    SymEnumerateModules64(dbg_curr_process->handle, info_mod_cb, &im);
233
    SymSetExtendedOption(SYMOPT_EX_WINE_NATIVE_MODULES, opt);
234

235
    qsort(im.modules, im.num_used, sizeof(im.modules[0]), module_compare);
236

237 238
    dbg_printf("Module\tAddress\t\t\t%sDebug info\tName (%d modules)\n",
	       ADDRWIDTH == 16 ? "\t\t" : "", im.num_used);
239

Eric Pouech's avatar
Eric Pouech committed
240 241 242
    for (i = 0; i < im.num_used; i++)
    {
        if (base && 
243
            (base < im.modules[i].mi.BaseOfImage || base >= im.modules[i].mi.BaseOfImage + im.modules[i].mi.ImageSize))
Eric Pouech's avatar
Eric Pouech committed
244
            continue;
245
        if (strstr(im.modules[i].name, "<elf>"))
246
        {
Eric Pouech's avatar
Eric Pouech committed
247
            dbg_printf("ELF\t");
248
            module_print_info(&im.modules[i], FALSE);
Eric Pouech's avatar
Eric Pouech committed
249 250
            /* print all modules embedded in this one */
            for (j = 0; j < im.num_used; j++)
251
            {
252
                if (!strstr(im.modules[j].name, "<elf>") && module_is_container(&im.modules[i], &im.modules[j]))
253
                {
Eric Pouech's avatar
Eric Pouech committed
254
                    dbg_printf("  \\-PE\t");
255
                    module_print_info(&im.modules[j], TRUE);
256
                }
257
            }
Eric Pouech's avatar
Eric Pouech committed
258 259 260 261 262
        }
        else
        {
            /* check module is not embedded in another module */
            for (j = 0; j < im.num_used; j++) 
263
            {
264
                if (strstr(im.modules[j].name, "<elf>") && module_is_container(&im.modules[j], &im.modules[i]))
Eric Pouech's avatar
Eric Pouech committed
265
                    break;
266
            }
Eric Pouech's avatar
Eric Pouech committed
267
            if (j < im.num_used) continue;
268
            if (strstr(im.modules[i].name, ".so") || strchr(im.modules[i].name, '<'))
Eric Pouech's avatar
Eric Pouech committed
269 270 271
                dbg_printf("ELF\t");
            else
                dbg_printf("PE\t");
272
            module_print_info(&im.modules[i], FALSE);
273
        }
Eric Pouech's avatar
Eric Pouech committed
274
        num_printed++;
Alexandre Julliard's avatar
Alexandre Julliard committed
275
    }
276
    HeapFree(GetProcessHeap(), 0, im.modules);
Eric Pouech's avatar
Eric Pouech committed
277 278

    if (base && !num_printed)
279
        dbg_printf("'0x%x%08x' is not a valid module address\n", (DWORD)(base >> 32), (DWORD)base);
Alexandre Julliard's avatar
Alexandre Julliard committed
280
}
Alexandre Julliard's avatar
Alexandre Julliard committed
281

282
struct class_walker
Alexandre Julliard's avatar
Alexandre Julliard committed
283
{
284 285 286 287
    ATOM*	table;
    int		used;
    int		alloc;
};
Alexandre Julliard's avatar
Alexandre Julliard committed
288

289
static void class_walker(HWND hWnd, struct class_walker* cw)
Alexandre Julliard's avatar
Alexandre Julliard committed
290
{
291 292 293 294
    char	clsName[128];
    int	        i;
    ATOM	atom;
    HWND	child;
Alexandre Julliard's avatar
Alexandre Julliard committed
295

296
    if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
297
        return;
298
    if ((atom = FindAtomA(clsName)) == 0)
299
        return;
Alexandre Julliard's avatar
Alexandre Julliard committed
300

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
    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
321 322
}

323
void info_win32_class(HWND hWnd, const char* name)
Alexandre Julliard's avatar
Alexandre Julliard committed
324
{
325
    WNDCLASSEXA	wca;
326
    HINSTANCE   hInst = hWnd ? (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE) : 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
327

328 329 330
    if (!name)
    {
        struct class_walker cw;
Alexandre Julliard's avatar
Alexandre Julliard committed
331

332 333 334 335 336 337
        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
338

339
    if (!GetClassInfoExA(hInst, name, &wca))
340 341 342 343
    {
        dbg_printf("Cannot find class '%s'\n", name);
        return;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
344

345
    dbg_printf("Class '%s':\n", name);
346
    dbg_printf("style=0x%08x  wndProc=%p\n"
347 348
               "inst=%p  icon=%p  cursor=%p  bkgnd=%p\n"
               "clsExtra=%d  winExtra=%d\n",
349
               wca.style, wca.lpfnWndProc, wca.hInstance,
350 351
               wca.hIcon, wca.hCursor, wca.hbrBackground,
               wca.cbClsExtra, wca.cbWndExtra);
352

353 354 355 356
    if (hWnd && wca.cbClsExtra)
    {
        int		i;
        WORD		w;
357

358 359 360 361 362 363 364 365
        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");
366
    }
367 368 369 370 371
    dbg_printf("\n");
    /* FIXME:
     * + print #windows (or even list of windows...)
     * + print extra bytes => this requires a window handle on this very class...
     */
372 373
}

374
static void info_window(HWND hWnd, int indent)
375
{
376 377 378 379 380 381
    char	clsName[128];
    char	wndName[128];
    HWND	child;

    do
    {
382
        if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
383
            strcpy(clsName, "-- Unknown --");
384
        if (!GetWindowTextA(hWnd, wndName, sizeof(wndName)))
385 386
            strcpy(wndName, "-- Empty --");

387
        dbg_printf("%*s%08lx%*s %-17.17s %08x %0*lx %08x %.14s\n",
388
                   indent, "", (DWORD_PTR)hWnd, 12 - indent, "",
389
                   clsName, GetWindowLongW(hWnd, GWL_STYLE),
390
                   ADDRWIDTH, (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_WNDPROC),
391
                   GetWindowThreadProcessId(hWnd, NULL), wndName);
392 393 394 395

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

398
void info_win32_window(HWND hWnd, BOOL detailed)
399
{
400 401 402 403 404
    char	clsName[128];
    char	wndName[128];
    RECT	clientRect;
    RECT	windowRect;
    WORD	w;
405

406
    if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
407

408 409
    if (!detailed)
    {
410 411 412
        dbg_printf("%-20.20s %-17.17s %-8.8s %-*.*s %-8.8s %s\n",
                   "Window handle", "Class Name", "Style",
		   ADDRWIDTH, ADDRWIDTH, "WndProc", "Thread", "Text");
413 414
        info_window(hWnd, 0);
        return;
415 416
    }

417
    if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
418
        strcpy(clsName, "-- Unknown --");
419
    if (!GetWindowTextA(hWnd, wndName, sizeof(wndName)))
420 421 422 423 424 425 426 427 428
        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"
429
               "inst=%p  active=%p  idmenu=%08lx\n"
430
               "style=0x%08x  exstyle=0x%08x  wndproc=%p  text='%s'\n"
431
               "client=%d,%d-%d,%d  window=%d,%d-%d,%d sysmenu=%p\n",
432 433 434 435 436
               GetWindow(hWnd, GW_HWNDNEXT),
               GetWindow(hWnd, GW_CHILD),
               GetParent(hWnd),
               GetWindow(hWnd, GW_OWNER),
               clsName,
437
               (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE),
438
               GetLastActivePopup(hWnd),
439
               (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_ID),
440 441
               GetWindowLongW(hWnd, GWL_STYLE),
               GetWindowLongW(hWnd, GWL_EXSTYLE),
442
               (void*)GetWindowLongPtrW(hWnd, GWLP_WNDPROC),
443 444 445 446 447
               wndName,
               clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
               windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
               GetSystemMenu(hWnd, FALSE));

448
    if (GetClassLongW(hWnd, GCL_CBWNDEXTRA))
449
    {
Gerald Pfeifer's avatar
Gerald Pfeifer committed
450 451
        UINT i;

452
        dbg_printf("Extra bytes:");
453
        for (i = 0; i < GetClassLongW(hWnd, GCL_CBWNDEXTRA) / 2; i++)
454 455 456 457 458 459 460 461
        {
            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");
462 463
}

464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
struct dump_proc_entry
{
    PROCESSENTRY32         proc;
    unsigned               children; /* index in dump_proc.entries of first child */
    unsigned               sibling;  /* index in dump_proc.entries of next sibling */
};

struct dump_proc
{
    struct dump_proc_entry*entries;
    unsigned               count;
    unsigned               alloc;
};

static unsigned get_parent(const struct dump_proc* dp, unsigned idx)
{
    unsigned i;

    for (i = 0; i < dp->count; i++)
    {
        if (i != idx && dp->entries[i].proc.th32ProcessID == dp->entries[idx].proc.th32ParentProcessID)
            return i;
    }
    return -1;
}

static void dump_proc_info(const struct dump_proc* dp, unsigned idx, unsigned depth)
{
    struct dump_proc_entry* dpe;
    for ( ; idx != -1; idx = dp->entries[idx].sibling)
    {
        assert(idx < dp->count);
        dpe = &dp->entries[idx];
        dbg_printf("%c%08x %-8d ",
                   (dpe->proc.th32ProcessID == (dbg_curr_process ?
                                                dbg_curr_process->pid : 0)) ? '>' : ' ',
                   dpe->proc.th32ProcessID, dpe->proc.cntThreads);
        if (depth)
        {
            unsigned i;
            for (i = 3 * (depth - 1); i > 0; i--) dbg_printf(" ");
            dbg_printf("\\_ ");
        }
        dbg_printf("'%s'\n", dpe->proc.szExeFile);
        dump_proc_info(dp, dpe->children, depth + 1);
    }
}

512
void info_win32_processes(void)
513
{
514
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
515 516
    if (snap != INVALID_HANDLE_VALUE)
    {
517 518 519 520 521 522 523
        struct dump_proc  dp;
        unsigned          i, first = -1;
        BOOL              ok;

        dp.count   = 0;
        dp.alloc   = 16;
        dp.entries = HeapAlloc(GetProcessHeap(), 0, sizeof(*dp.entries) * dp.alloc);
524 525 526 527 528
        if (!dp.entries)
        {
             CloseHandle(snap);
             return;
        }
529 530 531 532
        dp.entries[dp.count].proc.dwSize = sizeof(dp.entries[dp.count].proc);
        ok = Process32First(snap, &dp.entries[dp.count].proc);

        /* fetch all process information into dp (skipping this debugger) */
533 534
        while (ok)
        {
535 536 537 538 539 540 541 542 543
            if (dp.entries[dp.count].proc.th32ProcessID != GetCurrentProcessId())
                dp.entries[dp.count++].children = -1;
            if (dp.count >= dp.alloc)
            {
                dp.entries = HeapReAlloc(GetProcessHeap(), 0, dp.entries, sizeof(*dp.entries) * (dp.alloc *= 2));
                if (!dp.entries) return;
            }
            dp.entries[dp.count].proc.dwSize = sizeof(dp.entries[dp.count].proc);
            ok = Process32Next(snap, &dp.entries[dp.count].proc);
544
        }
545
        CloseHandle(snap);
546 547 548 549 550 551 552 553 554 555 556
        /* chain the siblings wrt. their parent */
        for (i = 0; i < dp.count; i++)
        {
            unsigned parent = get_parent(&dp, i);
            unsigned *chain = parent == -1 ? &first : &dp.entries[parent].children;
            dp.entries[i].sibling = *chain;
            *chain = i;
        }
        dbg_printf(" %-8.8s %-8.8s %s (all id:s are in hex)\n", "pid", "threads", "executable");
        dump_proc_info(&dp, first, 0);
        HeapFree(GetProcessHeap(), 0, dp.entries);
557 558 559
    }
}

560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
static BOOL get_process_name(DWORD pid, PROCESSENTRY32* entry)
{
    BOOL   ret = FALSE;
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if (snap != INVALID_HANDLE_VALUE)
    {
        entry->dwSize = sizeof(*entry);
        if (Process32First(snap, entry))
            while (!(ret = (entry->th32ProcessID == pid)) &&
                   Process32Next(snap, entry));
        CloseHandle(snap);
    }
    return ret;
}

576
void info_win32_threads(void)
577
{
578
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
579 580
    if (snap != INVALID_HANDLE_VALUE)
    {
581 582 583
        THREADENTRY32	entry;
        BOOL 		ok;
	DWORD		lastProcessId = 0;
584

585
	entry.dwSize = sizeof(entry);
586
	ok = Thread32First(snap, &entry);
587

588 589
        dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
                   "process", "tid", "prio");
590 591 592
        while (ok)
        {
            if (entry.th32OwnerProcessID != GetCurrentProcessId())
593 594 595 596 597 598 599
	    {
		/* 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)
		{
600
		    struct dbg_process*	p = dbg_get_process(entry.th32OwnerProcessID);
601 602 603 604 605 606 607 608 609
                    PROCESSENTRY32 pcs_entry;
                    const char* exename;

                    if (p)
                        exename = dbg_W2A(p->imageName, -1);
                    else if (get_process_name(entry.th32OwnerProcessID, &pcs_entry))
                        exename = pcs_entry.szExeFile;
                    else
                        exename = "";
610

611
		    dbg_printf("%08x%s %s\n",
612 613
                               entry.th32OwnerProcessID, p ? " (D)" : "", exename);
                    lastProcessId = entry.th32OwnerProcessID;
614
		}
615
                dbg_printf("\t%08x %4d%s\n",
616 617
                           entry.th32ThreadID, entry.tpBasePri,
                           (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
618 619

	    }
620
            ok = Thread32Next(snap, &entry);
621
        }
622

623
        CloseHandle(snap);
624
    }
625 626
}

627
/***********************************************************************
628
 *           info_win32_frame_exceptions
629
 *
630
 * Get info on the exception frames of a given thread.
631
 */
632
void info_win32_frame_exceptions(DWORD tid)
633
{
634 635
    struct dbg_thread*  thread;
    void*               next_frame;
636

637
    if (!dbg_curr_process || !dbg_curr_thread)
638
    {
639
        dbg_printf("Cannot get info on exceptions while no process is loaded\n");
640 641 642
        return;
    }

643
    dbg_printf("Exception frames:\n");
644

645
    if (tid == dbg_curr_tid) thread = dbg_curr_thread;
646 647
    else
    {
648 649 650 651
        thread = dbg_get_thread(dbg_curr_process, tid);

        if (!thread)
        {
652
            dbg_printf("Unknown thread id (%04x) in current process\n", tid);
653 654 655 656
            return;
        }
        if (SuspendThread(thread->handle) == -1)
        {
657
            dbg_printf("Can't suspend thread id (%04x)\n", tid);
658 659
            return;
        }
660 661
    }

662
    if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
663
    {
664
        dbg_printf("Can't read TEB:except_frame\n");
665 666 667
        return;
    }

668
    while (next_frame != (void*)-1)
669
    {
670
        EXCEPTION_REGISTRATION_RECORD frame;
671

672 673
        dbg_printf("%p: ", next_frame);
        if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
674
        {
675
            dbg_printf("Invalid frame address\n");
676 677
            break;
        }
678
        dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
679 680 681
        next_frame = frame.Prev;
    }

682
    if (tid != dbg_curr_tid) ResumeThread(thread->handle);
683 684
}

685
void info_win32_segments(DWORD start, int length)
686 687 688 689 690 691 692 693 694
{
    char 	flags[3];
    DWORD 	i;
    LDT_ENTRY	le;

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

    for (i = start; i < start + length; i++)
    {
695
        if (!dbg_curr_process->process_io->get_selector(dbg_curr_thread->handle, (i << 3) | 7, &le))
696
            continue;
697

698
        if (le.HighWord.Bits.Type & 0x08)
699 700 701 702 703 704 705 706 707 708 709
        {
            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] = '-';
        }
710
        dbg_printf("%04x: sel=%04x base=%08x limit=%08x %d-bit %c%c%c\n",
711 712 713 714 715 716 717
                   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]);
718 719 720
    }
}

721
void info_win32_virtual(DWORD pid)
722
{
723 724
    MEMORY_BASIC_INFORMATION    mbi;
    char*                       addr = 0;
725 726
    const char*                 state;
    const char*                 type;
727
    char                        prot[3+1];
728
    HANDLE                      hProc;
729

730
    if (pid == dbg_curr_pid)
731
    {
732
        if (dbg_curr_process == NULL)
733
        {
734
            dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
735 736
            return;
        }
737
        hProc = dbg_curr_process->handle;
738 739 740 741 742 743
    }
    else
    {
        hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
        if (hProc == NULL)
        {
744
            dbg_printf("Cannot open process <%04x>\n", pid);
745 746 747
            return;
        }
    }
748

749
    dbg_printf("Address  End      State   Type    RWX\n");
750

751
    while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
    {
        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;
            }
770 771
            memset(prot, ' ' , sizeof(prot) - 1);
            prot[sizeof(prot) - 1] = '\0';
772 773 774 775 776 777 778 779 780 781 782 783 784 785
            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';
        }
786
        dbg_printf("%08lx %08lx %s %s %s\n",
787
                   (DWORD_PTR)addr, (DWORD_PTR)addr + mbi.RegionSize - 1, state, type, prot);
788 789 790 791
        if (addr + mbi.RegionSize < addr) /* wrap around ? */
            break;
        addr += mbi.RegionSize;
    }
792
    if (pid != dbg_curr_pid) CloseHandle(hProc);
793
}
794

795
void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
796
{
797
    struct dbg_lvalue           lvalue;
798
    struct __wine_debug_channel channel;
799 800 801 802 803
    unsigned char               mask;
    int                         done = 0;
    BOOL                        bAll;
    void*                       addr;

804 805 806 807 808 809
    if (!dbg_curr_process || !dbg_curr_thread)
    {
        dbg_printf("Cannot set/get debug channels while no process is loaded\n");
        return;
    }

810
    if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
811 812 813
    {
        return;
    }
814
    addr = memory_to_linear_addr(&lvalue.addr);
815 816 817 818 819 820 821 822 823 824 825

    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;
    }
826

827
    bAll = !strcmp("all", name);
828
    while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
829
    {
830 831
        if (!channel.name[0]) break;
        if (bAll || !strcmp( channel.name, name ))
832
        {
833 834 835
            if (turn_on) channel.flags |= mask;
            else channel.flags &= ~mask;
            if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
836
        }
837
        addr = (struct __wine_debug_channel *)addr + 1;
838
    }
839
    if (!done) dbg_printf("Unable to find debug channel %s\n", name);
840
    else WINE_TRACE("Changed %d channel instances\n", done);
841
}
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916

void info_win32_exception(void)
{
    const EXCEPTION_RECORD*     rec;
    ADDRESS64                   addr;
    char                        hexbuf[MAX_OFFSET_TO_STR_LEN];

    if (!dbg_curr_thread->in_exception)
    {
        dbg_printf("Thread isn't in an exception\n");
        return;
    }
    rec = &dbg_curr_thread->excpt_record;
    memory_get_current_pc(&addr);

    /* print some infos */
    dbg_printf("%s: ",
               dbg_curr_thread->first_chance ? "First chance exception" : "Unhandled exception");
    switch (rec->ExceptionCode)
    {
    case EXCEPTION_BREAKPOINT:
        dbg_printf("breakpoint");
        break;
    case EXCEPTION_SINGLE_STEP:
        dbg_printf("single step");
        break;
    case EXCEPTION_INT_DIVIDE_BY_ZERO:
        dbg_printf("divide by zero");
        break;
    case EXCEPTION_INT_OVERFLOW:
        dbg_printf("overflow");
        break;
    case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
        dbg_printf("array bounds");
        break;
    case EXCEPTION_ILLEGAL_INSTRUCTION:
        dbg_printf("illegal instruction");
        break;
    case EXCEPTION_STACK_OVERFLOW:
        dbg_printf("stack overflow");
        break;
    case EXCEPTION_PRIV_INSTRUCTION:
        dbg_printf("privileged instruction");
        break;
    case EXCEPTION_ACCESS_VIOLATION:
        if (rec->NumberParameters == 2)
            dbg_printf("page fault on %s access to 0x%08lx",
                       rec->ExceptionInformation[0] == EXCEPTION_WRITE_FAULT ? "write" :
                       rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT ? "execute" : "read",
                       rec->ExceptionInformation[1]);
        else
            dbg_printf("page fault");
        break;
    case EXCEPTION_DATATYPE_MISALIGNMENT:
        dbg_printf("Alignment");
        break;
    case DBG_CONTROL_C:
        dbg_printf("^C");
        break;
    case CONTROL_C_EXIT:
        dbg_printf("^C");
        break;
    case STATUS_POSSIBLE_DEADLOCK:
        {
            ADDRESS64       recaddr;

            recaddr.Mode   = AddrModeFlat;
            recaddr.Offset = rec->ExceptionInformation[0];

            dbg_printf("wait failed on critical section ");
            print_address(&recaddr, FALSE);
        }
        break;
    case EXCEPTION_WINE_STUB:
        {
917
            char dll[64], name[256];
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
            memory_get_string(dbg_curr_process,
                              (void*)rec->ExceptionInformation[0], TRUE, FALSE,
                              dll, sizeof(dll));
            if (HIWORD(rec->ExceptionInformation[1]))
                memory_get_string(dbg_curr_process,
                                  (void*)rec->ExceptionInformation[1], TRUE, FALSE,
                                  name, sizeof(name));
            else
                sprintf( name, "%ld", rec->ExceptionInformation[1] );
            dbg_printf("unimplemented function %s.%s called", dll, name);
        }
        break;
    case EXCEPTION_WINE_ASSERTION:
        dbg_printf("assertion failed");
        break;
    case EXCEPTION_FLT_DENORMAL_OPERAND:
        dbg_printf("denormal float operand");
        break;
    case EXCEPTION_FLT_DIVIDE_BY_ZERO:
        dbg_printf("divide by zero");
        break;
    case EXCEPTION_FLT_INEXACT_RESULT:
        dbg_printf("inexact float result");
        break;
    case EXCEPTION_FLT_INVALID_OPERATION:
        dbg_printf("invalid float operation");
        break;
    case EXCEPTION_FLT_OVERFLOW:
        dbg_printf("floating point overflow");
        break;
    case EXCEPTION_FLT_UNDERFLOW:
        dbg_printf("floating point underflow");
        break;
    case EXCEPTION_FLT_STACK_CHECK:
        dbg_printf("floating point stack check");
        break;
954 955
    case EXCEPTION_WINE_CXX_EXCEPTION:
        if(rec->NumberParameters == 3 && rec->ExceptionInformation[0] == EXCEPTION_WINE_CXX_FRAME_MAGIC)
956 957
            dbg_printf("C++ exception(object = 0x%08lx, type = 0x%08lx)",
                       rec->ExceptionInformation[1], rec->ExceptionInformation[2]);
958
        else if(rec->NumberParameters == 4 && rec->ExceptionInformation[0] == EXCEPTION_WINE_CXX_FRAME_MAGIC)
959 960 961
            dbg_printf("C++ exception(object = %p, type = %p, base = %p)",
                       (void*)rec->ExceptionInformation[1], (void*)rec->ExceptionInformation[2],
                       (void*)rec->ExceptionInformation[3]);
962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
        else
            dbg_printf("C++ exception with strange parameter count %d or magic 0x%08lx",
                       rec->NumberParameters, rec->ExceptionInformation[0]);
        break;
    default:
        dbg_printf("0x%08x", rec->ExceptionCode);
        break;
    }
    if (rec->ExceptionFlags & EH_STACK_INVALID)
        dbg_printf(", invalid program stack");

    switch (addr.Mode)
    {
    case AddrModeFlat:
        dbg_printf(" in %d-bit code (%s)",
977
                   dbg_curr_process->be_cpu->pointer_size * 8,
978 979 980 981 982 983 984 985 986
                   memory_offset_to_string(hexbuf, addr.Offset, 0));
        break;
    case AddrModeReal:
        dbg_printf(" in vm86 code (%04x:%04x)", addr.Segment, (unsigned) addr.Offset);
        break;
    case AddrMode1616:
        dbg_printf(" in 16-bit code (%04x:%04x)", addr.Segment, (unsigned) addr.Offset);
        break;
    case AddrMode1632:
987
        dbg_printf(" in segmented 32-bit code (%04x:%08x)", addr.Segment, (unsigned) addr.Offset);
988 989 990 991 992
        break;
    default: dbg_printf(" bad address");
    }
    dbg_printf(".\n");
}