info.c 30 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
            "  break [*<addr>]                        watch | rwatch *<addr>",
53 54 55 56 57 58 59 60 61
            "  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>",
62
            "  bt [<tid>|all]                         frame <n>",
63 64 65 66
            "  up                                     down",
            "  list <lines>                           disassemble [<addr>][,<addr>]",
            "  show dir                               dir <path>",
            "  set <reg> = <expr>                     set *<addr> = <expr>",
67 68
            "  pass                                   whatis",
            "  info (see 'help info' for options)",
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

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

121
static const char* get_symtype_str(const IMAGEHLP_MODULE64* mi)
122
{
123
    switch (mi->SymType)
Alexandre Julliard's avatar
Alexandre Julliard committed
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";
133 134 135 136 137 138 139 140 141 142 143
    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
144
    }
145
}
146

147 148
struct info_module
{
149
    IMAGEHLP_MODULE64*  mi;
150 151 152
    unsigned            num_alloc;
    unsigned            num_used;
};
153

154
static void module_print_info(const IMAGEHLP_MODULE64* mi, BOOL is_embedded)
155
{
156 157 158
    dbg_printf("%*.*s-%*.*s\t%-16s%s\n",
               ADDRWIDTH, ADDRWIDTH, wine_dbgstr_longlong(mi->BaseOfImage),
               ADDRWIDTH, ADDRWIDTH, wine_dbgstr_longlong(mi->BaseOfImage + mi->ImageSize),
159
               is_embedded ? "\\" : get_symtype_str(mi), mi->ModuleName);
160 161 162 163
}

static int      module_compare(const void* p1, const void* p2)
{
164 165 166 167 168
    LONGLONG val = ((const IMAGEHLP_MODULE64*)p1)->BaseOfImage -
        ((const IMAGEHLP_MODULE64*)p2)->BaseOfImage;
    if (val < 0) return -1;
    else if (val > 0) return 1;
    else return 0;
169
}
170

171 172
static inline BOOL module_is_container(const IMAGEHLP_MODULE64* wmod_cntnr,
                                       const IMAGEHLP_MODULE64* wmod_child)
173 174
{
    return wmod_cntnr->BaseOfImage <= wmod_child->BaseOfImage &&
175 176
        wmod_cntnr->BaseOfImage + wmod_cntnr->ImageSize >=
        wmod_child->BaseOfImage + wmod_child->ImageSize;
177
}
178

179
static BOOL CALLBACK info_mod_cb(PCSTR mod_name, DWORD64 base, PVOID ctx)
180
{
181
    struct info_module* im = ctx;
182

183
    if (im->num_used + 1 > im->num_alloc)
184
    {
185 186
        im->num_alloc += 16;
        im->mi = dbg_heap_realloc(im->mi, im->num_alloc * sizeof(*im->mi));
187
    }
188
    im->mi[im->num_used].SizeOfStruct = sizeof(im->mi[im->num_used]);
189
    if (SymGetModuleInfo64(dbg_curr_process->handle, base, &im->mi[im->num_used]))
190 191 192 193 194
    {
        im->num_used++;
    }   
    return TRUE;
}
195

196 197 198 199 200
/***********************************************************************
 *           info_win32_module
 *
 * Display information about a given module (DLL or EXE), or about all modules
 */
201
void info_win32_module(DWORD64 base)
202
{
Eric Pouech's avatar
Eric Pouech committed
203
    struct info_module  im;
Gerald Pfeifer's avatar
Gerald Pfeifer committed
204
    UINT                i, j, num_printed = 0;
Eric Pouech's avatar
Eric Pouech committed
205 206
    DWORD               opt;

207
    if (!dbg_curr_process)
208 209 210 211 212
    {
        dbg_printf("Cannot get info on module while no process is loaded\n");
        return;
    }

Eric Pouech's avatar
Eric Pouech committed
213 214
    im.mi = NULL;
    im.num_alloc = im.num_used = 0;
215

Eric Pouech's avatar
Eric Pouech committed
216 217 218 219 220 221
    /* this is a wine specific options to return also ELF modules in the
     * enumeration
     */
    SymSetOptions((opt = SymGetOptions()) | 0x40000000);
    SymEnumerateModules64(dbg_curr_process->handle, info_mod_cb, (void*)&im);
    SymSetOptions(opt);
222

Eric Pouech's avatar
Eric Pouech committed
223
    qsort(im.mi, im.num_used, sizeof(im.mi[0]), module_compare);
224

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

Eric Pouech's avatar
Eric Pouech committed
228 229 230 231 232 233
    for (i = 0; i < im.num_used; i++)
    {
        if (base && 
            (base < im.mi[i].BaseOfImage || base >= im.mi[i].BaseOfImage + im.mi[i].ImageSize))
            continue;
        if (strstr(im.mi[i].ModuleName, "<elf>"))
234
        {
Eric Pouech's avatar
Eric Pouech committed
235 236 237 238
            dbg_printf("ELF\t");
            module_print_info(&im.mi[i], FALSE);
            /* print all modules embedded in this one */
            for (j = 0; j < im.num_used; j++)
239
            {
Eric Pouech's avatar
Eric Pouech committed
240
                if (!strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[i], &im.mi[j]))
241
                {
Eric Pouech's avatar
Eric Pouech committed
242 243
                    dbg_printf("  \\-PE\t");
                    module_print_info(&im.mi[j], TRUE);
244
                }
245
            }
Eric Pouech's avatar
Eric Pouech committed
246 247 248 249 250
        }
        else
        {
            /* check module is not embedded in another module */
            for (j = 0; j < im.num_used; j++) 
251
            {
Eric Pouech's avatar
Eric Pouech committed
252 253
                if (strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[j], &im.mi[i]))
                    break;
254
            }
Eric Pouech's avatar
Eric Pouech committed
255 256 257 258 259 260
            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");
            module_print_info(&im.mi[i], FALSE);
261
        }
Eric Pouech's avatar
Eric Pouech committed
262
        num_printed++;
Alexandre Julliard's avatar
Alexandre Julliard committed
263
    }
Eric Pouech's avatar
Eric Pouech committed
264 265 266
    HeapFree(GetProcessHeap(), 0, im.mi);

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

270
struct class_walker
Alexandre Julliard's avatar
Alexandre Julliard committed
271
{
272 273 274 275
    ATOM*	table;
    int		used;
    int		alloc;
};
Alexandre Julliard's avatar
Alexandre Julliard committed
276

277
static void class_walker(HWND hWnd, struct class_walker* cw)
Alexandre Julliard's avatar
Alexandre Julliard committed
278
{
279 280 281 282
    char	clsName[128];
    int	        i;
    ATOM	atom;
    HWND	child;
Alexandre Julliard's avatar
Alexandre Julliard committed
283

284
    if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
285
        return;
286
    if ((atom = FindAtomA(clsName)) == 0)
287
        return;
Alexandre Julliard's avatar
Alexandre Julliard committed
288

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
    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
309 310
}

311
void info_win32_class(HWND hWnd, const char* name)
Alexandre Julliard's avatar
Alexandre Julliard committed
312
{
313
    WNDCLASSEXA	wca;
314
    HINSTANCE   hInst = hWnd ? (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE) : 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
315

316 317 318
    if (!name)
    {
        struct class_walker cw;
Alexandre Julliard's avatar
Alexandre Julliard committed
319

320 321 322 323 324 325
        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
326

327
    if (!GetClassInfoExA(hInst, name, &wca))
328 329 330 331
    {
        dbg_printf("Cannot find class '%s'\n", name);
        return;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
332

333
    dbg_printf("Class '%s':\n", name);
334
    dbg_printf("style=0x%08x  wndProc=%p\n"
335 336
               "inst=%p  icon=%p  cursor=%p  bkgnd=%p\n"
               "clsExtra=%d  winExtra=%d\n",
337
               wca.style, wca.lpfnWndProc, wca.hInstance,
338 339
               wca.hIcon, wca.hCursor, wca.hbrBackground,
               wca.cbClsExtra, wca.cbWndExtra);
340

341 342 343 344
    if (hWnd && wca.cbClsExtra)
    {
        int		i;
        WORD		w;
345

346 347 348 349 350 351 352 353
        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");
354
    }
355 356 357 358 359
    dbg_printf("\n");
    /* FIXME:
     * + print #windows (or even list of windows...)
     * + print extra bytes => this requires a window handle on this very class...
     */
360 361
}

362
static void info_window(HWND hWnd, int indent)
363
{
364 365 366 367 368 369
    char	clsName[128];
    char	wndName[128];
    HWND	child;

    do
    {
370
        if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
371
            strcpy(clsName, "-- Unknown --");
372
        if (!GetWindowTextA(hWnd, wndName, sizeof(wndName)))
373 374
            strcpy(wndName, "-- Empty --");

375
        dbg_printf("%*s%08lx%*s %-17.17s %08x %0*lx %08x %.14s\n",
376
                   indent, "", (DWORD_PTR)hWnd, 12 - indent, "",
377
                   clsName, GetWindowLongW(hWnd, GWL_STYLE),
378
                   ADDRWIDTH, (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_WNDPROC),
379
                   GetWindowThreadProcessId(hWnd, NULL), wndName);
380 381 382 383

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

386
void info_win32_window(HWND hWnd, BOOL detailed)
387
{
388 389 390 391 392
    char	clsName[128];
    char	wndName[128];
    RECT	clientRect;
    RECT	windowRect;
    WORD	w;
393

394
    if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
395

396 397
    if (!detailed)
    {
398 399 400
        dbg_printf("%-20.20s %-17.17s %-8.8s %-*.*s %-8.8s %s\n",
                   "Window handle", "Class Name", "Style",
		   ADDRWIDTH, ADDRWIDTH, "WndProc", "Thread", "Text");
401 402
        info_window(hWnd, 0);
        return;
403 404
    }

405
    if (!GetClassNameA(hWnd, clsName, sizeof(clsName)))
406
        strcpy(clsName, "-- Unknown --");
407
    if (!GetWindowTextA(hWnd, wndName, sizeof(wndName)))
408 409 410 411 412 413 414 415 416
        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"
417
               "inst=%p  active=%p  idmenu=%08lx\n"
418
               "style=0x%08x  exstyle=0x%08x  wndproc=%p  text='%s'\n"
419
               "client=%d,%d-%d,%d  window=%d,%d-%d,%d sysmenu=%p\n",
420 421 422 423 424
               GetWindow(hWnd, GW_HWNDNEXT),
               GetWindow(hWnd, GW_CHILD),
               GetParent(hWnd),
               GetWindow(hWnd, GW_OWNER),
               clsName,
425
               (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE),
426
               GetLastActivePopup(hWnd),
427
               (ULONG_PTR)GetWindowLongPtrW(hWnd, GWLP_ID),
428 429
               GetWindowLongW(hWnd, GWL_STYLE),
               GetWindowLongW(hWnd, GWL_EXSTYLE),
430
               (void*)GetWindowLongPtrW(hWnd, GWLP_WNDPROC),
431 432 433 434 435
               wndName,
               clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
               windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
               GetSystemMenu(hWnd, FALSE));

436
    if (GetClassLongW(hWnd, GCL_CBWNDEXTRA))
437
    {
Gerald Pfeifer's avatar
Gerald Pfeifer committed
438 439
        UINT i;

440
        dbg_printf("Extra bytes:");
441
        for (i = 0; i < GetClassLongW(hWnd, GCL_CBWNDEXTRA) / 2; i++)
442 443 444 445 446 447 448 449
        {
            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");
450 451
}

452
void info_win32_processes(void)
453
{
454
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
455 456
    if (snap != INVALID_HANDLE_VALUE)
    {
457 458 459
        PROCESSENTRY32  entry;
        DWORD           current = dbg_curr_process ? dbg_curr_process->pid : 0;
        BOOL            ok;
460

461
        entry.dwSize = sizeof(entry);
462
        ok = Process32First(snap, &entry);
463

464 465
        dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
                   "pid", "threads", "parent", "executable");
466 467 468
        while (ok)
        {
            if (entry.th32ProcessID != GetCurrentProcessId())
469
                dbg_printf("%c%08x %-8d %08x '%s'\n",
470 471 472 473
                           (entry.th32ProcessID == current) ? '>' : ' ',
                           entry.th32ProcessID, entry.cntThreads,
                           entry.th32ParentProcessID, entry.szExeFile);
            ok = Process32Next(snap, &entry);
474
        }
475
        CloseHandle(snap);
476 477 478
    }
}

479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
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;
}

495
void info_win32_threads(void)
496
{
497
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
498 499
    if (snap != INVALID_HANDLE_VALUE)
    {
500 501 502
        THREADENTRY32	entry;
        BOOL 		ok;
	DWORD		lastProcessId = 0;
503

504
	entry.dwSize = sizeof(entry);
505
	ok = Thread32First(snap, &entry);
506

507 508
        dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
                   "process", "tid", "prio");
509 510 511
        while (ok)
        {
            if (entry.th32OwnerProcessID != GetCurrentProcessId())
512 513 514 515 516 517 518
	    {
		/* 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)
		{
519
		    struct dbg_process*	p = dbg_get_process(entry.th32OwnerProcessID);
520 521 522 523 524 525 526 527 528
                    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 = "";
529

530
		    dbg_printf("%08x%s %s\n",
531 532
                               entry.th32OwnerProcessID, p ? " (D)" : "", exename);
                    lastProcessId = entry.th32OwnerProcessID;
533
		}
534
                dbg_printf("\t%08x %4d%s\n",
535 536
                           entry.th32ThreadID, entry.tpBasePri,
                           (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
537 538

	    }
539
            ok = Thread32Next(snap, &entry);
540
        }
541

542
        CloseHandle(snap);
543
    }
544 545
}

546
/***********************************************************************
547
 *           info_win32_frame_exceptions
548
 *
549
 * Get info on the exception frames of a given thread.
550
 */
551
void info_win32_frame_exceptions(DWORD tid)
552
{
553 554
    struct dbg_thread*  thread;
    void*               next_frame;
555

556
    if (!dbg_curr_process || !dbg_curr_thread)
557
    {
558
        dbg_printf("Cannot get info on exceptions while no process is loaded\n");
559 560 561
        return;
    }

562
    dbg_printf("Exception frames:\n");
563

564
    if (tid == dbg_curr_tid) thread = dbg_curr_thread;
565 566
    else
    {
567 568 569 570
        thread = dbg_get_thread(dbg_curr_process, tid);

        if (!thread)
        {
571
            dbg_printf("Unknown thread id (%04x) in current process\n", tid);
572 573 574 575
            return;
        }
        if (SuspendThread(thread->handle) == -1)
        {
576
            dbg_printf("Can't suspend thread id (%04x)\n", tid);
577 578
            return;
        }
579 580
    }

581
    if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
582
    {
583
        dbg_printf("Can't read TEB:except_frame\n");
584 585 586
        return;
    }

587
    while (next_frame != (void*)-1)
588
    {
589
        EXCEPTION_REGISTRATION_RECORD frame;
590

591 592
        dbg_printf("%p: ", next_frame);
        if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
593
        {
594
            dbg_printf("Invalid frame address\n");
595 596
            break;
        }
597
        dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
598 599 600
        next_frame = frame.Prev;
    }

601
    if (tid != dbg_curr_tid) ResumeThread(thread->handle);
602 603
}

604
void info_win32_segments(DWORD start, int length)
605 606 607 608 609 610 611 612 613
{
    char 	flags[3];
    DWORD 	i;
    LDT_ENTRY	le;

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

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

617
        if (le.HighWord.Bits.Type & 0x08)
618 619 620 621 622 623 624 625 626 627 628
        {
            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] = '-';
        }
629
        dbg_printf("%04x: sel=%04x base=%08x limit=%08x %d-bit %c%c%c\n",
630 631 632 633 634 635 636
                   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]);
637 638 639
    }
}

640
void info_win32_virtual(DWORD pid)
641
{
642 643
    MEMORY_BASIC_INFORMATION    mbi;
    char*                       addr = 0;
644 645
    const char*                 state;
    const char*                 type;
646
    char                        prot[3+1];
647
    HANDLE                      hProc;
648

649
    if (pid == dbg_curr_pid)
650
    {
651
        if (dbg_curr_process == NULL)
652
        {
653
            dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
654 655
            return;
        }
656
        hProc = dbg_curr_process->handle;
657 658 659 660 661 662
    }
    else
    {
        hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
        if (hProc == NULL)
        {
663
            dbg_printf("Cannot open process <%04x>\n", pid);
664 665 666
            return;
        }
    }
667

668
    dbg_printf("Address  End      State   Type    RWX\n");
669

670
    while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
    {
        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;
            }
689 690
            memset(prot, ' ' , sizeof(prot) - 1);
            prot[sizeof(prot) - 1] = '\0';
691 692 693 694 695 696 697 698 699 700 701 702 703 704
            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';
        }
705
        dbg_printf("%08lx %08lx %s %s %s\n",
706
                   (DWORD_PTR)addr, (DWORD_PTR)addr + mbi.RegionSize - 1, state, type, prot);
707 708 709 710
        if (addr + mbi.RegionSize < addr) /* wrap around ? */
            break;
        addr += mbi.RegionSize;
    }
711
    if (pid != dbg_curr_pid) CloseHandle(hProc);
712
}
713

714
void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
715
{
716
    struct dbg_lvalue           lvalue;
717
    struct __wine_debug_channel channel;
718 719 720 721 722
    unsigned char               mask;
    int                         done = 0;
    BOOL                        bAll;
    void*                       addr;

723 724 725 726 727 728
    if (!dbg_curr_process || !dbg_curr_thread)
    {
        dbg_printf("Cannot set/get debug channels while no process is loaded\n");
        return;
    }

729
    if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
730 731 732
    {
        return;
    }
733
    addr = memory_to_linear_addr(&lvalue.addr);
734 735 736 737 738 739 740 741 742 743 744

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

746
    bAll = !strcmp("all", name);
747
    while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
748
    {
749 750
        if (!channel.name[0]) break;
        if (bAll || !strcmp( channel.name, name ))
751
        {
752 753 754
            if (turn_on) channel.flags |= mask;
            else channel.flags &= ~mask;
            if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
755
        }
756
        addr = (struct __wine_debug_channel *)addr + 1;
757
    }
758
    if (!done) dbg_printf("Unable to find debug channel %s\n", name);
759
    else WINE_TRACE("Changed %d channel instances\n", done);
760
}
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835

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:
        {
836
            char dll[32], name[256];
837 838 839 840 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
            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_VM86_INTx:
        dbg_printf("interrupt %02lx in vm86 mode", rec->ExceptionInformation[0]);
        break;
    case EXCEPTION_VM86_STI:
        dbg_printf("sti in vm86 mode");
        break;
    case EXCEPTION_VM86_PICRETURN:
        dbg_printf("PIC return in vm86 mode");
        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;
    case CXX_EXCEPTION:
        if(rec->NumberParameters == 3 && rec->ExceptionInformation[0] == CXX_FRAME_MAGIC)
            dbg_printf("C++ exception(object = 0x%08lx, type = 0x%08lx)",
                       rec->ExceptionInformation[1], rec->ExceptionInformation[2]);
        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)",
                   be_cpu->pointer_size * 8,
                   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:
        dbg_printf(" in segmented 32-bit code (%04x:%08lx)", addr.Segment, (unsigned long) addr.Offset);
        break;
    default: dbg_printf(" bad address");
    }
    dbg_printf(".\n");
}