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