dinput: Fix the compile for missing EV_SYN define.
[wine/multimedia.git] / programs / winedbg / info.c
blobc58b3e7721c4e9211c23bc245b6c54dfdbb2394b
1 /*
2 * Wine debugger utility routines
4 * Copyright 1993 Eric Youngdale
5 * Copyright 1995 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "config.h"
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
29 #include "debugger.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "tlhelp32.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
37 /***********************************************************************
38 * print_help
40 * Implementation of the 'help' command.
42 void print_help(void)
44 int i = 0;
45 static const char * const helptext[] =
47 "The commands accepted by the Wine debugger are a reasonable",
48 "subset of the commands that gdb accepts.",
49 "The commands currently are:",
50 " help quit",
51 " break [*<addr>] watch *<addr>",
52 " delete break bpnum disable bpnum",
53 " enable bpnum condition <bpnum> [<expr>]",
54 " finish cont [N]",
55 " step [N] next [N]",
56 " stepi [N] nexti [N]",
57 " x <addr> print <expr>",
58 " display <expr> undisplay <disnum>",
59 " local display <expr> delete display <disnum>",
60 " enable display <disnum> disable display <disnum>",
61 " bt [<tid>|all] frame <n>",
62 " up down",
63 " list <lines> disassemble [<addr>][,<addr>]",
64 " show dir dir <path>",
65 " set <reg> = <expr> set *<addr> = <expr>",
66 " mode [16,32,vm86] pass",
67 " whatis info (see 'help info' for options)",
69 "The 'x' command accepts repeat counts and formats (including 'i') in the",
70 "same way that gdb does.\n",
72 "The following are examples of legal expressions:",
73 " $eax $eax+0x3 0x1000 ($eip + 256) *$eax *($esp + 3)",
74 " Also, a nm format symbol table can be read from a file using the",
75 " symbolfile command.", /* Symbols can also be defined individually with",
76 " the define command.", */
77 "",
78 NULL
81 while (helptext[i]) dbg_printf("%s\n", helptext[i++]);
85 /***********************************************************************
86 * info_help
88 * Implementation of the 'help info' command.
90 void info_help(void)
92 int i = 0;
93 static const char * const infotext[] =
95 "The info commands allow you to get assorted bits of interesting stuff",
96 "to be displayed. The options are:",
97 " info break Displays information about breakpoints",
98 " info class <name> Displays information about window class <name>",
99 " info display Shows auto-display expressions in use",
100 " info except <pid> Shows exception handler chain (in a given process)",
101 " info locals Displays values of all local vars for current frame",
102 " info maps <pid> Shows virtual mappings (in a given process)",
103 " info process Shows all running processes",
104 " info reg Displays values in all registers at top of stack",
105 " info segments <pid> Displays information about all known segments",
106 " info share Displays all loaded modules",
107 " info share <addr> Displays internal module state",
108 " info stack Dumps information about top of stack",
109 " info symbol <sym> Displays information about a given symbol",
110 " info thread Shows all running threads",
111 " info wnd <handle> Displays internal window state",
113 NULL
116 while (infotext[i]) dbg_printf("%s\n", infotext[i++]);
119 static const char* get_symtype_str(SYM_TYPE st)
121 switch (st)
123 case -1: return "\\";
124 default:
125 case SymNone: return "--none--";
126 case SymCoff: return "COFF";
127 case SymCv: return "CodeView";
128 case SymPdb: return "PDB";
129 case SymExport: return "Export";
130 case SymDeferred: return "Deferred";
131 case SymSym: return "Sym";
132 case SymDia: return "DIA";
133 case NumSymTypes: return "Stabs";
137 struct info_module
139 IMAGEHLP_MODULE* mi;
140 unsigned num_alloc;
141 unsigned num_used;
144 static void module_print_info(const IMAGEHLP_MODULE* mi, SYM_TYPE st)
146 dbg_printf("0x%08lx-%08lx\t%-16s%s\n",
147 mi->BaseOfImage, mi->BaseOfImage + mi->ImageSize,
148 get_symtype_str(st), mi->ModuleName);
151 static int module_compare(const void* p1, const void* p2)
153 return (char*)(((const IMAGEHLP_MODULE*)p1)->BaseOfImage) -
154 (char*)(((const IMAGEHLP_MODULE*)p2)->BaseOfImage);
157 static inline BOOL module_is_container(const IMAGEHLP_MODULE* wmod_cntnr,
158 const IMAGEHLP_MODULE* wmod_child)
160 return wmod_cntnr->BaseOfImage <= wmod_child->BaseOfImage &&
161 (DWORD)wmod_cntnr->BaseOfImage + wmod_cntnr->ImageSize >=
162 (DWORD)wmod_child->BaseOfImage + wmod_child->ImageSize;
165 static BOOL CALLBACK info_mod_cb(PSTR mod_name, DWORD base, void* ctx)
167 struct info_module* im = (struct info_module*)ctx;
169 if (im->num_used + 1 > im->num_alloc)
171 im->num_alloc += 16;
172 im->mi = dbg_heap_realloc(im->mi, im->num_alloc * sizeof(*im->mi));
174 im->mi[im->num_used].SizeOfStruct = sizeof(im->mi[im->num_used]);
175 if (SymGetModuleInfo(dbg_curr_process->handle, base, &im->mi[im->num_used]))
177 im->num_used++;
179 return TRUE;
182 /***********************************************************************
183 * info_win32_module
185 * Display information about a given module (DLL or EXE), or about all modules
187 void info_win32_module(DWORD base)
189 if (!dbg_curr_process || !dbg_curr_thread)
191 dbg_printf("Cannot get info on module while no process is loaded\n");
192 return;
195 if (base)
197 IMAGEHLP_MODULE mi;
199 mi.SizeOfStruct = sizeof(mi);
201 if (!SymGetModuleInfo(dbg_curr_process->handle, base, &mi))
203 dbg_printf("'0x%08lx' is not a valid module address\n", base);
204 return;
206 module_print_info(&mi, mi.SymType);
208 else
210 struct info_module im;
211 int i, j;
212 DWORD opt;
214 im.mi = NULL;
215 im.num_alloc = im.num_used = 0;
217 /* this is a wine specific options to return also ELF modules in the
218 * enumeration
220 SymSetOptions((opt = SymGetOptions()) | 0x40000000);
221 SymEnumerateModules(dbg_curr_process->handle, info_mod_cb, (void*)&im);
222 SymSetOptions(opt);
224 qsort(im.mi, im.num_used, sizeof(im.mi[0]), module_compare);
226 dbg_printf("Module\tAddress\t\t\tDebug info\tName (%d modules)\n", im.num_used);
228 for (i = 0; i < im.num_used; i++)
230 if (strstr(im.mi[i].ModuleName, "<elf>"))
232 dbg_printf("ELF\t");
233 module_print_info(&im.mi[i], (im.mi[i].SymType == SymDia) ? NumSymTypes : im.mi[i].SymType);
234 /* print all modules embedded in this one */
235 for (j = 0; j < im.num_used; j++)
237 if (!strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[i], &im.mi[j]))
239 dbg_printf(" \\-PE\t");
240 module_print_info(&im.mi[j], -1);
244 else
246 /* check module is not embedded in another module */
247 for (j = 0; j < im.num_used; j++)
249 if (strstr(im.mi[j].ModuleName, "<elf>") && module_is_container(&im.mi[j], &im.mi[i]))
250 break;
252 if (j < im.num_used) continue;
253 if (strstr(im.mi[i].ModuleName, ".so") || strchr(im.mi[i].ModuleName, '<'))
254 dbg_printf("ELF\t");
255 else
256 dbg_printf("PE\t");
257 module_print_info(&im.mi[i], im.mi[i].SymType);
260 HeapFree(GetProcessHeap(), 0, im.mi);
264 struct class_walker
266 ATOM* table;
267 int used;
268 int alloc;
271 static void class_walker(HWND hWnd, struct class_walker* cw)
273 char clsName[128];
274 int i;
275 ATOM atom;
276 HWND child;
278 if (!GetClassName(hWnd, clsName, sizeof(clsName)))
279 return;
280 if ((atom = FindAtom(clsName)) == 0)
281 return;
283 for (i = 0; i < cw->used; i++)
285 if (cw->table[i] == atom)
286 break;
288 if (i == cw->used)
290 if (cw->used >= cw->alloc)
292 cw->alloc += 16;
293 cw->table = dbg_heap_realloc(cw->table, cw->alloc * sizeof(ATOM));
295 cw->table[cw->used++] = atom;
296 info_win32_class(hWnd, clsName);
300 if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
301 class_walker(child, cw);
302 } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
305 void info_win32_class(HWND hWnd, const char* name)
307 WNDCLASSEXA wca;
308 HINSTANCE hInst = hWnd ? (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE) : 0;
310 if (!name)
312 struct class_walker cw;
314 cw.table = NULL;
315 cw.used = cw.alloc = 0;
316 class_walker(GetDesktopWindow(), &cw);
317 HeapFree(GetProcessHeap(), 0, cw.table);
318 return;
321 if (!GetClassInfoEx(hInst, name, &wca))
323 dbg_printf("Cannot find class '%s'\n", name);
324 return;
327 dbg_printf("Class '%s':\n", name);
328 dbg_printf("style=0x%08x wndProc=0x%08lx\n"
329 "inst=%p icon=%p cursor=%p bkgnd=%p\n"
330 "clsExtra=%d winExtra=%d\n",
331 wca.style, (DWORD)wca.lpfnWndProc, wca.hInstance,
332 wca.hIcon, wca.hCursor, wca.hbrBackground,
333 wca.cbClsExtra, wca.cbWndExtra);
335 if (hWnd && wca.cbClsExtra)
337 int i;
338 WORD w;
340 dbg_printf("Extra bytes:");
341 for (i = 0; i < wca.cbClsExtra / 2; i++)
343 w = GetClassWord(hWnd, i * 2);
344 /* FIXME: depends on i386 endian-ity */
345 dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
347 dbg_printf("\n");
349 dbg_printf("\n");
350 /* FIXME:
351 * + print #windows (or even list of windows...)
352 * + print extra bytes => this requires a window handle on this very class...
356 static void info_window(HWND hWnd, int indent)
358 char clsName[128];
359 char wndName[128];
360 HWND child;
364 if (!GetClassName(hWnd, clsName, sizeof(clsName)))
365 strcpy(clsName, "-- Unknown --");
366 if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
367 strcpy(wndName, "-- Empty --");
369 dbg_printf("%*s%08x%*s %-17.17s %08lx %08lx %08lx %.14s\n",
370 indent, "", (UINT)hWnd, 12 - indent, "",
371 clsName, GetWindowLong(hWnd, GWL_STYLE),
372 GetWindowLongPtr(hWnd, GWLP_WNDPROC),
373 GetWindowThreadProcessId(hWnd, NULL), wndName);
375 if ((child = GetWindow(hWnd, GW_CHILD)) != 0)
376 info_window(child, indent + 1);
377 } while ((hWnd = GetWindow(hWnd, GW_HWNDNEXT)) != 0);
380 void info_win32_window(HWND hWnd, BOOL detailed)
382 char clsName[128];
383 char wndName[128];
384 RECT clientRect;
385 RECT windowRect;
386 int i;
387 WORD w;
389 if (!IsWindow(hWnd)) hWnd = GetDesktopWindow();
391 if (!detailed)
393 dbg_printf("%-20.20s %-17.17s %-8.8s %-8.8s %-8.8s %s\n",
394 "Window handle", "Class Name", "Style", "WndProc", "Thread", "Text");
395 info_window(hWnd, 0);
396 return;
399 if (!GetClassName(hWnd, clsName, sizeof(clsName)))
400 strcpy(clsName, "-- Unknown --");
401 if (!GetWindowText(hWnd, wndName, sizeof(wndName)))
402 strcpy(wndName, "-- Empty --");
403 if (!GetClientRect(hWnd, &clientRect) ||
404 !MapWindowPoints(hWnd, 0, (LPPOINT) &clientRect, 2))
405 SetRectEmpty(&clientRect);
406 if (!GetWindowRect(hWnd, &windowRect))
407 SetRectEmpty(&windowRect);
409 /* FIXME missing fields: hmemTaskQ, hrgnUpdate, dce, flags, pProp, scroll */
410 dbg_printf("next=%p child=%p parent=%p owner=%p class='%s'\n"
411 "inst=%p active=%p idmenu=%08lx\n"
412 "style=0x%08lx exstyle=0x%08lx wndproc=0x%08lx text='%s'\n"
413 "client=%ld,%ld-%ld,%ld window=%ld,%ld-%ld,%ld sysmenu=%p\n",
414 GetWindow(hWnd, GW_HWNDNEXT),
415 GetWindow(hWnd, GW_CHILD),
416 GetParent(hWnd),
417 GetWindow(hWnd, GW_OWNER),
418 clsName,
419 (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
420 GetLastActivePopup(hWnd),
421 GetWindowLongPtr(hWnd, GWLP_ID),
422 GetWindowLong(hWnd, GWL_STYLE),
423 GetWindowLong(hWnd, GWL_EXSTYLE),
424 GetWindowLongPtr(hWnd, GWLP_WNDPROC),
425 wndName,
426 clientRect.left, clientRect.top, clientRect.right, clientRect.bottom,
427 windowRect.left, windowRect.top, windowRect.right, windowRect.bottom,
428 GetSystemMenu(hWnd, FALSE));
430 if (GetClassLong(hWnd, GCL_CBWNDEXTRA))
432 dbg_printf("Extra bytes:");
433 for (i = 0; i < GetClassLong(hWnd, GCL_CBWNDEXTRA) / 2; i++)
435 w = GetWindowWord(hWnd, i * 2);
436 /* FIXME: depends on i386 endian-ity */
437 dbg_printf(" %02x %02x", HIBYTE(w), LOBYTE(w));
439 dbg_printf("\n");
441 dbg_printf("\n");
444 void info_win32_processes(void)
446 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
447 if (snap != INVALID_HANDLE_VALUE)
449 PROCESSENTRY32 entry;
450 DWORD current = dbg_curr_process ? dbg_curr_process->pid : 0;
451 BOOL ok;
453 entry.dwSize = sizeof(entry);
454 ok = Process32First(snap, &entry);
456 dbg_printf(" %-8.8s %-8.8s %-8.8s %s (all id:s are in hex)\n",
457 "pid", "threads", "parent", "executable");
458 while (ok)
460 if (entry.th32ProcessID != GetCurrentProcessId())
461 dbg_printf("%c%08lx %-8ld %08lx '%s'\n",
462 (entry.th32ProcessID == current) ? '>' : ' ',
463 entry.th32ProcessID, entry.cntThreads,
464 entry.th32ParentProcessID, entry.szExeFile);
465 ok = Process32Next(snap, &entry);
467 CloseHandle(snap);
471 void info_win32_threads(void)
473 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
474 if (snap != INVALID_HANDLE_VALUE)
476 THREADENTRY32 entry;
477 BOOL ok;
478 DWORD lastProcessId = 0;
480 entry.dwSize = sizeof(entry);
481 ok = Thread32First(snap, &entry);
483 dbg_printf("%-8.8s %-8.8s %s (all id:s are in hex)\n",
484 "process", "tid", "prio");
485 while (ok)
487 if (entry.th32OwnerProcessID != GetCurrentProcessId())
489 /* FIXME: this assumes that, in the snapshot, all threads of a same process are
490 * listed sequentially, which is not specified in the doc (Wine's implementation
491 * does it)
493 if (entry.th32OwnerProcessID != lastProcessId)
495 struct dbg_process* p = dbg_get_process(entry.th32OwnerProcessID);
497 dbg_printf("%08lx%s %s\n",
498 entry.th32OwnerProcessID, p ? " (D)" : "", p ? p->imageName : "");
499 lastProcessId = entry.th32OwnerProcessID;
501 dbg_printf("\t%08lx %4ld%s\n",
502 entry.th32ThreadID, entry.tpBasePri,
503 (entry.th32ThreadID == dbg_curr_tid) ? " <==" : "");
506 ok = Thread32Next(snap, &entry);
509 CloseHandle(snap);
513 /***********************************************************************
514 * info_win32_exceptions
516 * Get info on the exception frames of a given thread.
518 void info_win32_exceptions(DWORD tid)
520 struct dbg_thread* thread;
521 void* next_frame;
523 if (!dbg_curr_process || !dbg_curr_thread)
525 dbg_printf("Cannot get info on exceptions while no process is loaded\n");
526 return;
529 dbg_printf("Exception frames:\n");
531 if (tid == dbg_curr_tid) thread = dbg_curr_thread;
532 else
534 thread = dbg_get_thread(dbg_curr_process, tid);
536 if (!thread)
538 dbg_printf("Unknown thread id (0x%08lx) in current process\n", tid);
539 return;
541 if (SuspendThread(thread->handle) == -1)
543 dbg_printf("Can't suspend thread id (0x%08lx)\n", tid);
544 return;
548 if (!dbg_read_memory(thread->teb, &next_frame, sizeof(next_frame)))
550 dbg_printf("Can't read TEB:except_frame\n");
551 return;
554 while (next_frame != (void*)-1)
556 EXCEPTION_REGISTRATION_RECORD frame;
558 dbg_printf("%p: ", next_frame);
559 if (!dbg_read_memory(next_frame, &frame, sizeof(frame)))
561 dbg_printf("Invalid frame address\n");
562 break;
564 dbg_printf("prev=%p handler=%p\n", frame.Prev, frame.Handler);
565 next_frame = frame.Prev;
568 if (tid != dbg_curr_tid) ResumeThread(thread->handle);
571 void info_win32_segments(DWORD start, int length)
573 char flags[3];
574 DWORD i;
575 LDT_ENTRY le;
577 if (length == -1) length = (8192 - start);
579 for (i = start; i < start + length; i++)
581 if (!GetThreadSelectorEntry(dbg_curr_thread->handle, (i << 3) | 7, &le))
582 continue;
584 if (le.HighWord.Bits.Type & 0x08)
586 flags[0] = (le.HighWord.Bits.Type & 0x2) ? 'r' : '-';
587 flags[1] = '-';
588 flags[2] = 'x';
590 else
592 flags[0] = 'r';
593 flags[1] = (le.HighWord.Bits.Type & 0x2) ? 'w' : '-';
594 flags[2] = '-';
596 dbg_printf("%04lx: sel=%04lx base=%08x limit=%08x %d-bit %c%c%c\n",
597 i, (i << 3) | 7,
598 (le.HighWord.Bits.BaseHi << 24) +
599 (le.HighWord.Bits.BaseMid << 16) + le.BaseLow,
600 ((le.HighWord.Bits.LimitHi << 8) + le.LimitLow) <<
601 (le.HighWord.Bits.Granularity ? 12 : 0),
602 le.HighWord.Bits.Default_Big ? 32 : 16,
603 flags[0], flags[1], flags[2]);
607 void info_win32_virtual(DWORD pid)
609 MEMORY_BASIC_INFORMATION mbi;
610 char* addr = 0;
611 const char* state;
612 const char* type;
613 char prot[3+1];
614 HANDLE hProc;
616 if (pid == dbg_curr_pid)
618 if (dbg_curr_process == NULL)
620 dbg_printf("Cannot look at mapping of current process, while no process is loaded\n");
621 return;
623 hProc = dbg_curr_process->handle;
625 else
627 hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
628 if (hProc == NULL)
630 dbg_printf("Cannot open process <%lu>\n", pid);
631 return;
635 dbg_printf("Address Size State Type RWX\n");
637 while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)) >= sizeof(mbi))
639 switch (mbi.State)
641 case MEM_COMMIT: state = "commit "; break;
642 case MEM_FREE: state = "free "; break;
643 case MEM_RESERVE: state = "reserve"; break;
644 default: state = "??? "; break;
646 if (mbi.State != MEM_FREE)
648 switch (mbi.Type)
650 case MEM_IMAGE: type = "image "; break;
651 case MEM_MAPPED: type = "mapped "; break;
652 case MEM_PRIVATE: type = "private"; break;
653 case 0: type = " "; break;
654 default: type = "??? "; break;
656 memset(prot, ' ' , sizeof(prot) - 1);
657 prot[sizeof(prot) - 1] = '\0';
658 if (mbi.AllocationProtect & (PAGE_READONLY|PAGE_READWRITE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
659 prot[0] = 'R';
660 if (mbi.AllocationProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
661 prot[1] = 'W';
662 if (mbi.AllocationProtect & (PAGE_WRITECOPY|PAGE_EXECUTE_WRITECOPY))
663 prot[1] = 'C';
664 if (mbi.AllocationProtect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE))
665 prot[2] = 'X';
667 else
669 type = "";
670 prot[0] = '\0';
672 dbg_printf("%08lx %08lx %s %s %s\n",
673 (DWORD)addr, (DWORD)addr + mbi.RegionSize - 1, state, type, prot);
674 if (addr + mbi.RegionSize < addr) /* wrap around ? */
675 break;
676 addr += mbi.RegionSize;
678 if (pid != dbg_curr_pid) CloseHandle(hProc);
681 void info_wine_dbg_channel(BOOL turn_on, const char* cls, const char* name)
683 struct dbg_lvalue lvalue;
684 struct __wine_debug_channel channel;
685 unsigned char mask;
686 int done = 0;
687 BOOL bAll;
688 void* addr;
690 if (!dbg_curr_process || !dbg_curr_thread)
692 dbg_printf("Cannot set/get debug channels while no process is loaded\n");
693 return;
696 if (symbol_get_lvalue("debug_options", -1, &lvalue, FALSE) != sglv_found)
698 return;
700 addr = memory_to_linear_addr(&lvalue.addr);
702 if (!cls) mask = ~0;
703 else if (!strcmp(cls, "fixme")) mask = (1 << __WINE_DBCL_FIXME);
704 else if (!strcmp(cls, "err")) mask = (1 << __WINE_DBCL_ERR);
705 else if (!strcmp(cls, "warn")) mask = (1 << __WINE_DBCL_WARN);
706 else if (!strcmp(cls, "trace")) mask = (1 << __WINE_DBCL_TRACE);
707 else
709 dbg_printf("Unknown debug class %s\n", cls);
710 return;
713 bAll = !strcmp("all", name);
714 while (addr && dbg_read_memory(addr, &channel, sizeof(channel)))
716 if (!channel.name[0]) break;
717 if (bAll || !strcmp( channel.name, name ))
719 if (turn_on) channel.flags |= mask;
720 else channel.flags &= ~mask;
721 if (dbg_write_memory(addr, &channel, sizeof(channel))) done++;
723 addr = (struct __wine_debug_channel *)addr + 1;
725 if (!done) dbg_printf("Unable to find debug channel %s\n", name);
726 else WINE_TRACE("Changed %d channel instances\n", done);