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