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