| <chapter id="debugger"> |
| <title>Debugging Wine</title> |
| |
| <sect1 id="dbg-intro"> |
| <title>Introduction</title> |
| |
| <para> |
| Written by &name-eric-pouech; <email>&email-eric-pouech;</email> |
| (Last updated: 9/15/2002) |
| </para> |
| <para> |
| (Extracted from <filename>wine/documentation/winedbg</filename>) |
| </para> |
| |
| <sect2> |
| <title>Processes and threads: in underlying OS and in Windows</title> |
| |
| <para> |
| Before going into the depths of debugging in Wine, here's |
| a small overview of process and thread handling in Wine. |
| It has to be clear that there are two different beasts: |
| processes/threads from the Unix point of view and |
| processes/threads from a Windows point of view. |
| </para> |
| <para> |
| Each Windows' thread is implemented as a Unix process (under |
| Linux using the <function>clone</function> syscall), meaning |
| that all threads of a same Windows' process share the same |
| (unix) address space. |
| </para> |
| <para> |
| In the following: |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para><varname>W-process</varname> means a process in Windows' terminology</para> |
| </listitem> |
| <listitem> |
| <para><varname>U-process</varname> means a process in Unix' terminology</para> |
| </listitem> |
| <listitem> |
| <para><varname>W-thread</varname> means a thread in Windows' terminology</para> |
| </listitem> |
| </itemizedlist> |
| <para> |
| A <varname>W-process</varname> is made of one or several |
| <varname>W-threads</varname>. Each |
| <varname>W-thread</varname> is mapped to one and only one |
| <varname>U-process</varname>. All |
| <varname>U-processes</varname> of a same |
| <varname>W-process</varname> share the same address space. |
| </para> |
| <para> |
| Each Unix process can be identified by two values: |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para>the Unix process id (<varname>upid</varname> in the following)</para> |
| </listitem> |
| <listitem> |
| <para>the Windows's thread id (<varname>tid</varname>)</para> |
| </listitem> |
| </itemizedlist> |
| <para> |
| Each Windows' process has also a Windows' process id |
| (<varname>wpid</varname> in the following). It must be clear |
| that <varname>upid</varname> and <varname>wpid</varname> are |
| different and shall not be used instead of the other. |
| </para> |
| <para> |
| <varname>Wpid</varname> and <varname>tid</varname> are |
| defined (Windows) system wide. They must not be confused |
| with process or thread handles which, as any handle, is an |
| indirection to a system object (in this case process or |
| thread). A same process can have several different handles |
| on the same kernel object. The handles can be defined as |
| local (the values is only valid in a process), or system |
| wide (the same handle can be used by any |
| <varname>W-process</varname>). |
| </para> |
| </sect2> |
| |
| <sect2> |
| <title>Wine, debugging and WineDbg</title> |
| |
| <para> |
| When talking of debugging in Wine, there are at least two |
| levels to think of: |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para>the Windows' debugging API.</para> |
| </listitem> |
| <listitem> |
| <para>the Wine integrated debugger, dubbed |
| <command>WineDbg</command>.</para> |
| </listitem> |
| </itemizedlist> |
| <para> |
| Wine implements most of the Windows' debugging API (the |
| part in <filename>KERNEL32.DLL</filename>, not the one in |
| <filename>IMAGEHLP.DLL</filename>), and allows any program |
| (emulated or Winelib) using that API to debug a |
| <varname>W-process</varname>. |
| </para> |
| <para> |
| <command>WineDbg</command> is a Winelib application making |
| use of this API to allow debugging both any Wine or Winelib |
| applications as well as Wine itself (kernel and all DLLs). |
| </para> |
| </sect2> |
| </sect1> |
| |
| |
| <sect1 id="dbg-modes"> |
| <title>WineDbg's modes of invocation</title> |
| |
| <sect2> |
| <title>Starting a process</title> |
| |
| <para> |
| Any application (either a Windows' native executable, or a |
| Winelib application) can be run through |
| <command>WineDbg</command>. Command line options and tricks |
| are the same as for wine: |
| </para> |
| <screen> |
| winedbg telnet.exe |
| winedbg "hl.exe -windowed" |
| </screen> |
| </sect2> |
| |
| <sect2> |
| <title>Attaching</title> |
| |
| <para> |
| <command>WineDbg</command> can also be launched without any |
| command line argument: <command>WineDbg</command> is started |
| without any attached process. You can get a list of running |
| <varname>W-processes</varname> (and their |
| <varname>wpid</varname>'s) using the <command>walk |
| process</command> command, and then, with the |
| <command>attach</command> command, pick up the |
| <varname>wpid</varname> of the <varname>W-process</varname> |
| you want to debug. This is (for now) a neat feature for the |
| following reasons: |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para>you can debug an already started application</para> |
| </listitem> |
| </itemizedlist> |
| </sect2> |
| |
| <sect2 id="dbg-on-exception"> |
| <title id="dbg-exception-title">On exceptions</title> |
| |
| <para> |
| When something goes wrong, Windows tracks this as an |
| exception. Exceptions exist for segmentation violation, |
| stack overflow, division by zero... |
| </para> |
| <para> |
| When an exception occurs, Wine checks if the <varname>W-process</varname> is |
| debugged. If so, the exception event is sent to the |
| debugger, which takes care of it: end of the story. This |
| mechanism is part of the standard Windows' debugging API. |
| </para> |
| <para> |
| If the <varname>W-process</varname> is not debugged, Wine |
| tries to launch a debugger. This debugger (normally |
| <command>WineDbg</command>, see III Configuration for more |
| details), at startup, attaches to the |
| <varname>W-process</varname> which generated the exception |
| event. In this case, you are able to look at the causes of |
| the exception, and either fix the causes (and continue |
| further the execution) or dig deeper to understand what went |
| wrong. |
| </para> |
| <para> |
| If <command>WineDbg</command> is the standard debugger, the |
| <command>pass</command> and <command>cont</command> commands |
| are the two ways to let the process go further for the |
| handling of the exception event. |
| </para> |
| <para> |
| To be more precise on the way Wine (and Windows) generates |
| exception events, when a fault occurs (segmentation |
| violation, stack overflow...), the event is first sent to |
| the debugger (this is known as a first chance exception). |
| The debugger can give two answers: |
| </para> |
| |
| <variablelist> |
| <varlistentry> |
| <term>continue:</term> |
| <listitem> |
| <para> |
| the debugger had the ability to correct what's |
| generated the exception, and is now able to continue |
| process execution. |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term>pass:</term> |
| <listitem> |
| <para> |
| the debugger couldn't correct the cause of the |
| first chance exception. Wine will now try to walk |
| the list of exception handlers to see if one of them |
| can handle the exception. If no exception handler is |
| found, the exception is sent once again to the |
| debugger to indicate the failure of the exception |
| handling. |
| </para> |
| </listitem> |
| </varlistentry> |
| </variablelist> |
| <note> |
| <para> |
| since some of Wine's code uses exceptions and |
| <function>try/catch</function> blocks to provide some |
| functionality, <command>WineDbg</command> can be entered |
| in such cases with segv exceptions. This happens, for |
| example, with <function>IsBadReadPtr</function> function. |
| In that case, the <command>pass</command> command shall be |
| used, to let the handling of the exception to be done by |
| the <function>catch</function> block in |
| <function>IsBadReadPtr</function>. |
| </para> |
| </note> |
| </sect2> |
| |
| <sect2 id="interrupt"> |
| <title>Interrupting</title> |
| |
| <para> |
| You can stop the debugger while it's running by hitting |
| Ctrl-C in its window. This will stop the debugged process, |
| and let you manipulate the current context |
| </para> |
| </sect2> |
| |
| <sect2> |
| <title>Quitting</title> |
| |
| <para> |
| Wine supports the new XP APIs, allowing for a debugger to |
| detach from a program being debugged (see |
| <command>detach</command> command). Unfortunately, as the |
| debugger cannot, for now, neither clear its internal |
| information, nor restart a new process, the debugger, after |
| detaching itself, cannot do much except being quited. |
| </para> |
| </sect2> |
| </sect1> |
| |
| |
| <sect1 id="wine-debugger"> |
| <title>Using the Wine Debugger</title> |
| |
| <para> |
| Written by &name-marcus-meissner; <email>&email-marcus-meissner;</email>, |
| additions welcome. |
| </para> |
| <para> |
| (Extracted from <filename>wine/documentation/debugging</filename>) |
| </para> |
| |
| <para> |
| This file describes where to start debugging Wine. If at any |
| point you get stuck and want to ask for help, please read the |
| <emphasis>How to Report A Bug</emphasis> section of the |
| <emphasis>Wine Users Guide</emphasis> for information on how to write |
| useful bug reports. |
| </para> |
| |
| <sect2> |
| <title>Crashes</title> |
| |
| <para> |
| These usually show up like this: |
| </para> |
| <screen> |
| |Unexpected Windows program segfault - opcode = 8b |
| |Segmentation fault in Windows program 1b7:c41. |
| |Loading symbols from ELF file /root/wine/wine... |
| |....more Loading symbols from ... |
| |In 16 bit mode. |
| |Register dump: |
| | CS:01b7 SS:016f DS:0287 ES:0000 |
| | IP:0c41 SP:878a BP:8796 FLAGS:0246 |
| | AX:811e BX:0000 CX:0000 DX:0000 SI:0001 DI:ffff |
| |Stack dump: |
| |0x016f:0x878a: 0001 016f ffed 0000 0000 0287 890b 1e5b |
| |0x016f:0x879a: 01b7 0001 000d 1050 08b7 016f 0001 000d |
| |0x016f:0x87aa: 000a 0003 0004 0000 0007 0007 0190 0000 |
| |0x016f:0x87ba: |
| | |
| |0050: sel=0287 base=40211d30 limit=0b93f (bytes) 16-bit rw- |
| |Backtrace: |
| |0 0x01b7:0x0c41 (PXSRV_FONGETFACENAME+0x7c) |
| |1 0x01b7:0x1e5b (PXSRV_FONPUTCATFONT+0x2cd) |
| |2 0x01a7:0x05aa |
| |3 0x01b7:0x0768 (PXSRV_FONINITFONTS+0x81) |
| |4 0x014f:0x03ed (PDOXWIN_@SQLCURCB$Q6CBTYPEULN8CBSCTYPE+0x1b1) |
| |5 0x013f:0x00ac |
| | |
| |0x01b7:0x0c41 (PXSRV_FONGETFACENAME+0x7c): movw %es:0x38(%bx),%dx |
| </screen> |
| <para> |
| Steps to debug a crash. You may stop at any step, but please |
| report the bug and provide as much of the information |
| gathered to the bug report as feasible. |
| </para> |
| |
| <orderedlist> |
| <listitem> |
| <para> |
| Get the reason for the crash. This is usually an access to |
| an invalid selector, an access to an out of range address |
| in a valid selector, popping a segmentregister from the |
| stack or the like. When reporting a crash, report this |
| <emphasis>whole</emphasis> crashdump even if it doesn't |
| make sense to you. |
| </para> |
| <para> |
| (In this case it is access to an invalid selector, for |
| <systemitem>%es</systemitem> is <literal>0000</literal>, as |
| seen in the register dump). |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Determine the cause of the crash. Since this is usually |
| a primary/secondary reaction to a failed or misbehaving |
| Wine function, rerun Wine with <parameter>-debugmsg |
| +relay</parameter> added to the commandline. This will |
| generate quite a lot of output, but usually the reason is |
| located in the last call(s). Those lines usually look like |
| this: |
| </para> |
| <screen> |
| |Call KERNEL.90: LSTRLEN(0227:0692 "text") ret=01e7:2ce7 ds=0227 |
| ^^^^^^^^^ ^ ^^^^^^^^^ ^^^^^^ ^^^^^^^^^ ^^^^ |
| | | | | | |Datasegment |
| | | | | |Return address |
| | | | |textual parameter |
| | | | |
| | | |Argument(s). This one is a win16 segmented pointer. |
| | |Function called. |
| |The module, the function is called in. In this case it is KERNEL. |
| |
| |Ret KERNEL.90: LSTRLEN() retval=0x0004 ret=01e7:2ce7 ds=0227 |
| ^^^^^^ |
| |Returnvalue is 16 bit and has the value 4. |
| </screen> |
| </listitem> |
| <listitem> |
| <para> |
| If you have found a misbehaving function, try to find out |
| why it misbehaves. Find the function in the source code. |
| Try to make sense of the arguments passed. Usually there is |
| a <function>WINE_DEFAULT_DEBUG_CHANNEL(<channel>);</function> |
| at the beginning of the file. Rerun wine with |
| <parameter>-debugmsg +xyz,+relay</parameter> added to the |
| commandline. |
| </para> |
| <para> |
| Occasionally there are additional debug channels defined at the |
| begining of the file in the form. |
| <function>WINE_DECLARE_DEBUG_CHANNEL(<channel>);</function> |
| If so the offending fuction may also uses one of these alternate |
| channels. Look through the the function for |
| <function>TRACE_(<channel>)(" ... /n");</function> and add any |
| additional channels to the commandline. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Additional information on how to debug using the internal |
| debugger can be found in |
| <filename>programs/winedbg/README</filename>. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| If this information isn't clear enough or if you want to |
| know more about what's happening in the function itself, |
| try running wine with <parameter>-debugmsg |
| +all</parameter>, which dumps ALL included debug |
| information in wine. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| If even that isn't enough, add more debug output for yourself |
| into the functions you find relevant. See The section on Debug |
| Logging in this guide for more information. You might |
| also try to run the program in <command>gdb</command> |
| instead of using the WINE-debugger. If you do that, use |
| <parameter>handle SIGSEGV nostop noprint</parameter> to |
| disable the handling of seg faults inside |
| <command>gdb</command> (needed for Win16). |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| You can also set a breakpoint for that function. Start wine |
| useing <command>winedbg</command> instead of |
| <command>wine</command>. Once the debugger is is running enter |
| <command>break</command> <parameter>KERNEL_LSTRLEN</parameter> |
| (replace by function you want to debug, CASE IS RELEVANT) |
| to set a breakpoint. Then |
| use <command>continue</command> to start normal |
| program-execution. Wine will stop if it reaches the |
| breakpoint. If the program isn't yet at the crashing call |
| of that function, use <command>continue</command> again |
| until you are about to enter that function. You may now |
| proceed with single-stepping the function until you reach |
| the point of crash. Use the other debugger commands to |
| print registers and the like. |
| </para> |
| </listitem> |
| </orderedlist> |
| </sect2> |
| |
| <sect2> |
| <title>Program hangs, nothing happens</title> |
| |
| <para> |
| Start the program with <command>winedbg</command> instead of |
| <command>wine</command>. When the program locks up switch to the |
| winedbg terminal and press |
| <keycombo><keycap>Ctrl</keycap><keycap>C</keycap></keycombo>. this |
| will stop the program and let you debug the program as you would for |
| a crash. |
| </para> |
| </sect2> |
| |
| <sect2> |
| <title>Program reports an error with a Messagebox</title> |
| |
| <para> |
| Sometimes programs are reporting failure using more or |
| less nondescript messageboxes. We can debug this using the |
| same method as Crashes, but there is one problem... For |
| setting up a message box the program also calls Wine |
| producing huge chunks of debug code. |
| </para> |
| <para> |
| Since the failure happens usually directly before setting up |
| the Messagebox you can start winedbg and set a |
| breakpoint at <function>MessageBoxA</function> (called by win16 |
| and win32 programs) and proceed with |
| <command>continue</command>. With <parameter>--debugmsg |
| +all</parameter> Wine will now stop directly before setting |
| up the Messagebox. Proceed as explained above. |
| </para> |
| <para> |
| You can also run wine using <command>wine -debugmsg +relay |
| program.exe 2>&1 | less -i</command> and in |
| <command>less</command> search for <quote>MessageBox</quote>. |
| </para> |
| </sect2> |
| |
| <sect2> |
| <title>Disassembling programs:</title> |
| |
| <para> |
| You may also try to disassemble the offending program to |
| check for undocumented features and/or use of them. |
| </para> |
| <para> |
| The best, freely available, disassembler for Win16 programs is |
| <application>Windows Codeback</application>, archivename |
| <filename>wcbxxx.zip</filename>, which usually can be found in |
| the <filename>Cica-Mirror</filename> subdirectory on the WINE |
| ftpsites. (See <filename>ANNOUNCE</filename>). |
| </para> |
| <para> |
| Disassembling win32 programs is possible using |
| <application>Windows Disassembler 32</application>, archivename |
| something like <filename>w32dsm87.zip</filename> (or similar) |
| on <systemitem class="systemname">ftp.winsite.com</systemitem> |
| and mirrors. The shareware version does not allow saving of |
| disassembly listings. You can also use the newer (and in the |
| full version better) <application>Interactive |
| Disassembler</application> (IDA) from the ftp sites mentioned |
| at the end of the document. Understanding disassembled code is |
| mostly a question of exercise. |
| </para> |
| <para> |
| Most code out there uses standard C function entries (for it |
| is usually written in C). Win16 function entries usually |
| look like that: |
| </para> |
| <programlisting> |
| push bp |
| mov bp, sp |
| ... function code .. |
| retf XXXX <--------- XXXX is number of bytes of arguments |
| </programlisting> |
| <para> |
| This is a <function>FAR</function> function with no local |
| storage. The arguments usually start at |
| <literal>[bp+6]</literal> with increasing offsets. Note, that |
| <literal>[bp+6]</literal> belongs to the |
| <emphasis>rightmost</emphasis> argument, for exported win16 |
| functions use the PASCAL calling convention. So, if we use |
| <function>strcmp(a,b)</function> with <parameter>a</parameter> |
| and <parameter>b</parameter> both 32 bit variables |
| <parameter>b</parameter> would be at <literal>[bp+6]</literal> |
| and <parameter>a</parameter> at <literal>[bp+10]</literal>. |
| </para> |
| <para> |
| Most functions make also use of local storage in the stackframe: |
| </para> |
| <programlisting> |
| enter 0086, 00 |
| ... function code ... |
| leave |
| retf XXXX |
| </programlisting> |
| <para> |
| This does mostly the same as above, but also adds |
| <literal>0x86</literal> bytes of stackstorage, which is |
| accessed using <literal>[bp-xx]</literal>. Before calling a |
| function, arguments are pushed on the stack using something |
| like this: |
| </para> |
| <programlisting> |
| push word ptr [bp-02] <- will be at [bp+8] |
| push di <- will be at [bp+6] |
| call KERNEL.LSTRLEN |
| </programlisting> |
| <para> |
| Here first the selector and then the offset to the passed |
| string are pushed. |
| </para> |
| </sect2> |
| |
| <sect2> |
| <title>Sample debugging session:</title> |
| |
| <para> |
| Let's debug the infamous Word <filename>SHARE.EXE</filename> |
| messagebox: |
| </para> |
| <screen> |
| |marcus@jet $ wine winword.exe |
| | +---------------------------------------------+ |
| | | ! You must leave Windows and load SHARE.EXE| |
| | | before starting Word. | |
| | +---------------------------------------------+ |
| </screen> |
| <screen> |
| |marcus@jet $ wine winword.exe -debugmsg +relay -debug |
| |CallTo32(wndproc=0x40065bc0,hwnd=000001ac,msg=00000081,wp=00000000,lp=00000000) |
| |Win16 task 'winword': Breakpoint 1 at 0x01d7:0x001a |
| |CallTo16(func=0127:0070,ds=0927) |
| |Call WPROCS.24: TASK_RESCHEDULE() ret=00b7:1456 ds=0927 |
| |Ret WPROCS.24: TASK_RESCHEDULE() retval=0x8672 ret=00b7:1456 ds=0927 |
| |CallTo16(func=01d7:001a,ds=0927) |
| | AX=0000 BX=3cb4 CX=1f40 DX=0000 SI=0000 DI=0927 BP=0000 ES=11f7 |
| |Loading symbols: /home/marcus/wine/wine... |
| |Stopped on breakpoint 1 at 0x01d7:0x001a |
| |In 16 bit mode. |
| |Wine-dbg>break MessageBoxA <---- Set Breakpoint |
| |Breakpoint 2 at 0x40189100 (MessageBoxA [msgbox.c:190]) |
| |Wine-dbg>c <---- Continue |
| |Call KERNEL.91: INITTASK() ret=0157:0022 ds=08a7 |
| | AX=0000 BX=3cb4 CX=1f40 DX=0000 SI=0000 DI=08a7 ES=11d7 EFL=00000286 |
| |CallTo16(func=090f:085c,ds=0dcf,0x0000,0x0000,0x0000,0x0000,0x0800,0x0000,0x0000,0x0dcf) |
| |... <----- Much debugoutput |
| |Call KERNEL.136: GETDRIVETYPE(0x0000) ret=060f:097b ds=0927 |
| ^^^^^^ Drive 0 (A:) |
| |Ret KERNEL.136: GETDRIVETYPE() retval=0x0002 ret=060f:097b ds=0927 |
| ^^^^^^ DRIVE_REMOVEABLE |
| (It is a floppy diskdrive.) |
| |
| |Call KERNEL.136: GETDRIVETYPE(0x0001) ret=060f:097b ds=0927 |
| ^^^^^^ Drive 1 (B:) |
| |Ret KERNEL.136: GETDRIVETYPE() retval=0x0000 ret=060f:097b ds=0927 |
| ^^^^^^ DRIVE_CANNOTDETERMINE |
| (I don't have drive B: assigned) |
| |
| |Call KERNEL.136: GETDRIVETYPE(0x0002) ret=060f:097b ds=0927 |
| ^^^^^^^ Drive 2 (C:) |
| |Ret KERNEL.136: GETDRIVETYPE() retval=0x0003 ret=060f:097b ds=0927 |
| ^^^^^^ DRIVE_FIXED |
| (specified as a harddisk) |
| |
| |Call KERNEL.97: GETTEMPFILENAME(0x00c3,0x09278364"doc",0x0000,0927:8248) ret=060f:09b1 ds=0927 |
| ^^^^^^ ^^^^^ ^^^^^^^^^ |
| | | |buffer for fname |
| | |temporary name ~docXXXX.tmp |
| |Force use of Drive C:. |
| |
| |Warning: GetTempFileName returns 'C:~doc9281.tmp', which doesn't seem to be writeable. |
| |Please check your configuration file if this generates a failure. |
| </screen> |
| <para> |
| Whoops, it even detects that something is wrong! |
| </para> |
| <screen> |
| |Ret KERNEL.97: GETTEMPFILENAME() retval=0x9281 ret=060f:09b1 ds=0927 |
| ^^^^^^ Temporary storage ID |
| |
| |Call KERNEL.74: OPENFILE(0x09278248"C:~doc9281.tmp",0927:82da,0x1012) ret=060f:09d8 ds=0927 |
| ^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^ |
| |filename |OFSTRUCT |open mode: |
| |
| OF_CREATE|OF_SHARE_EXCLUSIVE|OF_READWRITE |
| </screen> |
| <para> |
| This fails, since my <medialabel>C:</medialabel> drive is in |
| this case mounted readonly. |
| </para> |
| <screen> |
| |Ret KERNEL.74: OPENFILE() retval=0xffff ret=060f:09d8 ds=0927 |
| ^^^^^^ HFILE_ERROR16, yes, it failed. |
| |
| |Call USER.1: MESSAGEBOX(0x0000,0x09278376"Sie mussen Windows verlassen und SHARE.EXE laden bevor Sie Word starten.",0x00000000,0x1030) ret=060f:084f ds=0927 |
| </screen> |
| <para> |
| And MessageBox'ed. |
| </para> |
| <screen> |
| |Stopped on breakpoint 2 at 0x40189100 (MessageBoxA [msgbox.c:190]) |
| |190 { <- the sourceline |
| In 32 bit mode. |
| Wine-dbg> |
| </screen> |
| <para> |
| The code seems to find a writeable harddisk and tries to create |
| a file there. To work around this bug, you can define |
| <medialabel>C:</medialabel> as a networkdrive, which is ignored |
| by the code above. |
| </para> |
| </sect2> |
| |
| <sect2> |
| <title>Debugging Tips</title> |
| |
| <para> |
| Here are some useful debugging tips, added by Andreas Mohr: |
| </para> |
| |
| <itemizedlist> |
| <listitem> |
| <para> |
| If you have a program crashing at such an early loader phase that you can't |
| use the Wine debugger normally, but Wine already executes the program's |
| start code, then you may use a special trick. You should do a |
| <programlisting> |
| wine --debugmsg +relay program |
| </programlisting> |
| to get a listing of the functions the program calls in its start function. |
| Now you do a |
| <programlisting> |
| winedbg winfile.exe |
| </programlisting> |
| </para> |
| <para> |
| This way, you get into <command>winedbg</command>. Now you |
| can set a breakpoint on any function the program calls in |
| the start function and just type <userinput>c</userinput> |
| to bypass the eventual calls of Winfile to this function |
| until you are finally at the place where this function gets |
| called by the crashing start function. Now you can proceed |
| with your debugging as usual. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| If you try to run a program and it quits after showing an error messagebox, |
| the problem can usually be identified in the return value of one of the |
| functions executed before <function>MessageBox()</function>. |
| That's why you should re-run the program with e.g. |
| <programlisting> |
| wine --debugmsg +relay <program name> &>relmsg |
| </programlisting> |
| Then do a <command>more relmsg</command> and search for the |
| last occurrence of a call to the string "MESSAGEBOX". This is a line like |
| <programlisting> |
| Call USER.1: MESSAGEBOX(0x0000,0x01ff1246 "Runtime error 219 at 0004:1056.",0x00000000,0x1010) ret=01f7:2160 ds=01ff |
| </programlisting> |
| In my example the lines before the call to |
| <function>MessageBox()</function> look like that: |
| <programlisting> |
| Call KERNEL.96: FREELIBRARY(0x0347) ret=01cf:1033 ds=01ff |
| CallTo16(func=033f:0072,ds=01ff,0x0000) |
| Ret KERNEL.96: FREELIBRARY() retval=0x0001 ret=01cf:1033 ds=01ff |
| Call KERNEL.96: FREELIBRARY(0x036f) ret=01cf:1043 ds=01ff |
| CallTo16(func=0367:0072,ds=01ff,0x0000) |
| Ret KERNEL.96: FREELIBRARY() retval=0x0001 ret=01cf:1043 ds=01ff |
| Call KERNEL.96: FREELIBRARY(0x031f) ret=01cf:105c ds=01ff |
| CallTo16(func=0317:0072,ds=01ff,0x0000) |
| Ret KERNEL.96: FREELIBRARY() retval=0x0001 ret=01cf:105c ds=01ff |
| Call USER.171: WINHELP(0x02ac,0x01ff05b4 "COMET.HLP",0x0002,0x00000000) ret=01cf:1070 ds=01ff |
| CallTo16(func=0117:0080,ds=01ff) |
| Call WPROCS.24: TASK_RESCHEDULE() ret=00a7:0a2d ds=002b |
| Ret WPROCS.24: TASK_RESCHEDULE() retval=0x0000 ret=00a7:0a2d ds=002b |
| Ret USER.171: WINHELP() retval=0x0001 ret=01cf:1070 ds=01ff |
| Call KERNEL.96: FREELIBRARY(0x01be) ret=01df:3e29 ds=01ff |
| Ret KERNEL.96: FREELIBRARY() retval=0x0000 ret=01df:3e29 ds=01ff |
| Call KERNEL.52: FREEPROCINSTANCE(0x02cf00ba) ret=01f7:1460 ds=01ff |
| Ret KERNEL.52: FREEPROCINSTANCE() retval=0x0001 ret=01f7:1460 ds=01ff |
| Call USER.1: MESSAGEBOX(0x0000,0x01ff1246 "Runtime error 219 at 0004:1056.",0x00000000,0x1010) ret=01f7:2160 ds=01ff |
| </programlisting> |
| </para> |
| <para> |
| I think that the call to <function>MessageBox()</function> |
| in this example is <emphasis>not</emphasis> caused by a |
| wrong result value of some previously executed function |
| (it's happening quite often like that), but instead the |
| messagebox complains about a runtime error at |
| <literal>0x0004:0x1056</literal>. |
| </para> |
| <para> |
| As the segment value of the address is only |
| <literal>4</literal>, I think that that is only an internal |
| program value. But the offset address reveals something |
| quite interesting: Offset <literal>1056</literal> is |
| <emphasis>very</emphasis> close to the return address of |
| <function>FREELIBRARY()</function>: |
| <programlisting> |
| Call KERNEL.96: FREELIBRARY(0x031f) ret=01cf:105c ds=01ff |
| ^^^^ |
| </programlisting> |
| </para> |
| <para> |
| Provided that segment <literal>0x0004</literal> is indeed segment |
| <literal>0x1cf</literal>, we now we can use IDA (available at |
| <ulink url="ftp://ftp.uni-koeln.de/pc/msdos/programming/assembler/ida35bx.zip"> |
| ftp://ftp.uni-koeln.de/pc/msdos/programming/assembler/ida35bx.zip</ulink>) to |
| disassemble the part that caused the error. We just have to find the address of |
| the call to <function>FreeLibrary()</function>. Some lines before that the |
| runtime error occurred. But be careful! In some cases you don't have to |
| disassemble the main program, but instead some DLL called by it in order to find |
| the correct place where the runtime error occurred. That can be determined by |
| finding the origin of the segment value (in this case <literal>0x1cf</literal>). |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| If you have created a relay file of some crashing |
| program and want to set a breakpoint at a certain |
| location which is not yet available as the program loads |
| the breakpoint's segment during execution, you may set a |
| breakpoint to <function>GetVersion16/32</function> as |
| those functions are called very often. |
| </para> |
| <para> |
| Then do a <userinput>c</userinput> until you are able to |
| set this breakpoint without error message. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Some useful programs: |
| </para> |
| <variablelist> |
| <varlistentry> |
| <term> |
| <application>IDA</application>: |
| <filename> |
| <ulink url="ftp://ftp.uni-koeln.de/pc/msdos/programming/assembler/ida35bx.zip"> |
| ftp://ftp.uni-koeln.de/pc/msdos/programming/assembler/ida35bx.zip</ulink> |
| </filename> |
| </term> |
| <listitem> |
| <para> |
| <emphasis>Very</emphasis> good DOS disassembler ! It's badly needed |
| for debugging Wine sometimes. |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term> |
| <application>XRAY</application>: |
| <filename> |
| <ulink url="ftp://ftp.th-darmstadt.de/pub/machines/ms-dos/SimTel/msdos/asmutil/xray15.zip"> |
| ftp://ftp.th-darmstadt.de/pub/machines/ms-dos/SimTel/msdos/asmutil/xray15.zip</ulink> |
| </filename> |
| </term> |
| <listitem> |
| <para> |
| Traces DOS calls (Int 21h, DPMI, ...). Use it with |
| Windows to correct file management problems etc. |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term> |
| <application>pedump</application>: |
| <filename> |
| <ulink url="http://oak.oakland.edu/pub/simtelnet/win95/prog/pedump.zip"> |
| http://oak.oakland.edu/pub/simtelnet/win95/prog/pedump.zip</ulink> |
| </filename> |
| </term> |
| <listitem> |
| <para> |
| Dumps the imports and exports of a PE (Portable |
| Executable) DLL. |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term> |
| <application>winedump</application>: |
| </term> |
| <listitem> |
| <para> |
| Dumps the imports and exports of a PE (Portable |
| Executable) DLL (included in wine tree). |
| </para> |
| </listitem> |
| </varlistentry> |
| </variablelist> |
| </listitem> |
| </itemizedlist> |
| </sect2> |
| |
| <sect2> |
| <title>Some basic debugger usages:</title> |
| |
| <para> |
| After starting your program with |
| </para> |
| <screen> |
| wine -debug myprog.exe |
| </screen> |
| <para> |
| the program loads and you get a prompt at the program |
| starting point. Then you can set breakpoints: |
| </para> |
| <screen> |
| b RoutineName (by outine name) OR |
| b *0x812575 (by address) |
| </screen> |
| <para> |
| Then you hit <command>c</command> (continue) to run the |
| program. It stops at the breakpoint. You can type |
| </para> |
| <screen> |
| step (to step one line) OR |
| stepi (to step one machine instruction at a time; |
| here, it helps to know the basic 386 |
| instruction set) |
| info reg (to see registers) |
| info stack (to see hex values in the stack) |
| info local (to see local variables) |
| list <line number> (to list source code) |
| x <variable name> (to examine a variable; only works if code |
| is not compiled with optimization) |
| x 0x4269978 (to examine a memory location) |
| ? (help) |
| q (quit) |
| </screen> |
| <para> |
| By hitting <keycap>Enter</keycap>, you repeat the last |
| command. |
| </para> |
| </sect2> |
| </sect1> |
| |
| |
| <sect1 id="memory-addresses"> |
| <title>Useful memory addresses</title> |
| <para> |
| Written by &name-andreas-mohr; <email>&email-andreas-mohr;</email> |
| </para> |
| <para> |
| Wine uses several different kinds of memory addresses. |
| </para> |
| <variablelist> |
| <varlistentry> |
| <term> |
| Win32/"normal" Wine addresses/Linux: linear addresses. |
| </term> |
| <listitem> |
| <para> |
| Linear addresses can be everything from 0x0 up to |
| 0xffffffff. In Wine on Linux they are often around |
| e.g. 0x08000000, 0x00400000 (std. Win32 program load |
| address), 0x40000000. Every Win32 process has its own |
| private 4GB address space (that is, from 0x0 up to |
| 0xffffffff). |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term> |
| Win16 "enhanced mode": segmented addresses. |
| </term> |
| <listitem> |
| <para> |
| These are the "normal" Win16 addresses, called SEGPTR. |
| They have a segment:offset notation, e.g. 0x01d7:0x0012. |
| The segment part usually is a "selector", which *always* |
| has the lowest 3 bits set. Some sample selectors are |
| 0x1f7, 0x16f, 0x8f. If these bits are set except for |
| the lowest bit, as e.g. with 0x1f6,xi then it might be a |
| handle to global memory. Just set the lowest bit to get |
| the selector in these cases. A selector kind of |
| "points" to a certain linear (see above) base address. |
| It has more or less three important attributes: segment |
| base address, segment limit, segment access rights. |
| </para> |
| <para> |
| Example: |
| </para> |
| <para> |
| Selector 0x1f7 (0x40320000, 0x0000ffff, r-x) So 0x1f7 |
| has a base address of 0x40320000, the segment's last |
| address is 0x4032ffff (limit 0xffff), and it's readable |
| and executable. So an address of 0x1f7:0x2300 would be |
| the linear address of 0x40322300. |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term> |
| DOS/Win16 "standard mode" |
| </term> |
| <listitem> |
| <para> |
| They, too, have a segment:offset notation. But they are |
| completely different from "normal" Win16 addresses, as |
| they just represent at most 1MB of memory: The segment |
| part can be anything from 0 to 0xffff, and it's the same |
| with the offset part. |
| </para> |
| <para> |
| Now the strange thing is the calculation that's behind |
| these addresses: Just calculate segment*16 + offset in |
| order to get a "linear DOS" address. So |
| e.g. 0x0f04:0x3628 results in 0xf040 + 0x3628 = 0x12668. |
| And the highest address you can get is 0xfffff (1MB), of |
| course. In Wine, this "linear DOS" address of 0x12668 |
| has to be added to the linear base address of the |
| corresponding DOS memory allocated for dosmod in order |
| to get the true linear address of a DOS seg:offs |
| address. And make sure that you're doing this in the |
| correct process with the correct linear address space, |
| of course ;-) |
| </para> |
| </listitem> |
| </varlistentry> |
| </variablelist> |
| </sect1> |
| |
| <sect1 id="dbg-config"> |
| <title>Configuration</title> |
| |
| <sect2> |
| <title>Registry configuration</title> |
| |
| <para> |
| The Windows' debugging API uses a registry entry to know |
| which debugger to invoke when an unhandled exception occurs |
| (see <link endterm="dbg-exception-title" |
| linkend="dbg-on-exception"></link> for some details). Two |
| values in key |
| </para> |
| <programlisting> |
| "MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug" |
| </programlisting> |
| <para> |
| Determine the behavior: |
| </para> |
| <variablelist> |
| <varlistentry> |
| <term>Debugger:</term> |
| <listitem> |
| <para> |
| this is the command line used to launch the debugger |
| (it uses two <function>printf</function> formats |
| (<literal>%ld</literal>) to pass context dependent |
| information to the debugger). You should put here a |
| complete path to your debugger |
| (<command>WineDbg</command> can of course be used, but |
| any other Windows' debugging API aware debugger will |
| do). |
| The path to the debugger you chose to use must be reachable |
| via a DOS drive in the Wine config file ! |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term>Auto:</term> |
| <listitem> |
| <para> |
| if this value is zero, a message box will ask the |
| user if he/she wishes to launch the debugger when an |
| unhandled exception occurs. Otherwise, the debugger |
| is automatically started. |
| </para> |
| </listitem> |
| </varlistentry> |
| </variablelist> |
| |
| <para> |
| A regular Wine registry looks like: |
| </para> |
| <programlisting> |
| [MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug] 957636538 |
| "Auto"=dword:00000001 |
| "Debugger"="winedbg --debugmsg -all %ld %ld" |
| </programlisting> |
| |
| <note> |
| <title>Note 1</title> |
| <para> |
| creating this key is mandatory. Not doing so will not |
| fire the debugger when an exception occurs. |
| </para> |
| </note> |
| <note> |
| <title>Note 2</title> |
| <para> |
| <command>wineinstall</command> (available in Wine source) |
| sets up this correctly. |
| However, due to some limitation of the registry installed, |
| if a previous Wine installation exists, it's safer to |
| remove the whole |
| </para> |
| <programlisting> |
| [MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug] |
| </programlisting> |
| <para> |
| key before running again <command>wineinstall</command> to |
| regenerate this key. |
| </para> |
| </note> |
| </sect2> |
| |
| <sect2> |
| <title>WineDbg configuration</title> |
| |
| <para> |
| <command>WineDbg</command> can be configured through a number |
| of options. Those options are stored in the registry, on a |
| per user basis. The key is (in <emphasis>my</emphasis> registry) |
| </para> |
| <programlisting> |
| [eric\\Software\\Wine\\WineDbg] |
| </programlisting> |
| <para> |
| Those options can be read/written while inside |
| <command>WineDbg</command>, as part of the debugger |
| expressions. To refer to one of these options, its name must |
| be prefixed by a <literal>$</literal> sign. For example, |
| </para> |
| <programlisting> |
| set $BreakAllThreadsStartup = 1 |
| </programlisting> |
| <para> |
| sets the option <varname>BreakAllThreadsStartup</varname> to |
| <literal>TRUE</literal>. |
| </para> |
| <para> |
| All the options are read from the registry when |
| <command>WineDbg</command> starts (if no corresponding value |
| is found, a default value is used), and are written back to |
| the registry when <command>WineDbg</command> exits (hence, |
| all modifications to those options are automatically saved |
| when <command>WineDbg</command> terminates). |
| </para> |
| <para> |
| Here's the list of all options: |
| </para> |
| |
| <sect3> |
| <title>Controlling when the debugger is entered</title> |
| |
| <variablelist> |
| <varlistentry> |
| <term><varname>BreakAllThreadsStartup</varname></term> |
| <listitem> |
| <para> |
| Set to <literal>TRUE</literal> if at all threads |
| start-up the debugger stops set to |
| <literal>FALSE</literal> if only at the first thread |
| startup of a given process the debugger stops. |
| <literal>FALSE</literal> by default. |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><varname>BreakOnCritSectTimeOut</varname></term> |
| <listitem> |
| <para> |
| Set to <literal>TRUE</literal> if the debugger stops |
| when a critical section times out (5 minutes); |
| <literal>TRUE</literal> by default. |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><varname>BreakOnAttach</varname></term> |
| <listitem> |
| <para> |
| Set to <literal>TRUE</literal> if when |
| <command>WineDbg</command> attaches to an existing |
| process after an unhandled exception, |
| <command>WineDbg</command> shall be entered on the |
| first attach event. Since the attach event is |
| meaningless in the context of an exception event |
| (the next event which is the exception event is of |
| course relevant), that option is likely to be |
| <literal>FALSE</literal>. |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><varname>BreakOnFirstChance</varname></term> |
| <listitem> |
| <para> |
| An exception can generate two debug events. The |
| first one is passed to the debugger (known as a |
| first chance) just after the exception. The debugger |
| can then decides either to resume execution (see |
| <command>WineDbg</command>'s <command>cont</command> |
| command) or pass the exception up to the exception |
| handler chain in the program (if it exists) |
| (<command>WineDbg</command> implements this through the |
| <command>pass</command> command). If none of the |
| exception handlers takes care of the exception, the |
| exception event is sent again to the debugger (known |
| as last chance exception). You cannot pass on a last |
| exception. When the |
| <varname>BreakOnFirstChance</varname> exception is |
| <literal>TRUE</literal>, then winedbg is entered for |
| both first and last chance execptions (to |
| <literal>FALSE</literal>, it's only entered for last |
| chance exceptions). |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><varname>BreakOnDllLoad</varname></term> |
| <listitem> |
| <para> |
| Set to <literal>TRUE</literal> if the debugger stops |
| when a DLL is loaded into memory; when the debugger |
| is invoked after a crash, the DLLs already mapped in |
| memory will not trigger this break. |
| <literal>FALSE</literal> by default. |
| </para> |
| </listitem> |
| </varlistentry> |
| </variablelist> |
| </sect3> |
| |
| <sect3> |
| <title>Output handling</title> |
| |
| <variablelist> |
| <varlistentry> |
| <term><varname>ConChannelMask</varname></term> |
| <listitem> |
| <para> |
| Mask of active debugger output channels on console |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><varname>StdChannelMask</varname></term> |
| <listitem> |
| <para> |
| Mask of active debugger output channels on <filename>stderr</filename> |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><varname>UseXTerm</varname></term> |
| <listitem> |
| <para> |
| Set to <literal>TRUE</literal> if the debugger uses |
| its own <command>xterm</command> window for console |
| input/output. Set to <literal>FALSE</literal> if |
| the debugger uses the current Unix console for |
| input/output |
| </para> |
| </listitem> |
| </varlistentry> |
| </variablelist> |
| |
| <para> |
| Those last 3 variables are jointly used in two generic ways: |
| </para> |
| |
| <orderedlist> |
| <listitem> |
| <para>default</para> |
| <programlisting> |
| ConChannelMask = DBG_CHN_MESG (1) |
| StdChannelMask = 0 |
| UseXTerm = 1 |
| </programlisting> |
| <para> |
| In this case, all input/output goes into a specific |
| <command>xterm</command> window (but all debug |
| messages <function>TRACE</function>, |
| <function>WARN</function>... still goes to tty where |
| wine is run from). |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| to have all input/output go into the tty where Wine |
| was started from (to be used in a X11-free |
| environment) |
| </para> |
| <screen> |
| ConChannelMask = 0 |
| StdChannelMask = DBG_CHN_MESG (1) |
| UseXTerm = 1 |
| </screen> |
| </listitem> |
| </orderedlist> |
| <para> |
| Those variables also allow, for example for debugging |
| purposes, to use: |
| </para> |
| <screen> |
| ConChannelMask = 0xfff |
| StdChannelMask = 0xfff |
| UseXTerm = 1 |
| </screen> |
| <para> |
| This allows to redirect all <function>WineDbg</function> |
| output to both tty Wine was started from, and |
| <command>xterm</command> debugging window. If Wine (or |
| <command>WineDbg</command>) was started with a redirection |
| of <filename>stdout</filename> and/or |
| <filename>stderr</filename> to a file (with for |
| example >& shell redirect command), you'll get in that |
| file both outputs. It may be interesting to look in the |
| relay trace for specific values which the process segv'ed |
| on. |
| </para> |
| </sect3> |
| |
| <sect3> |
| <title>Context information</title> |
| |
| <variablelist> |
| <varlistentry> |
| <term><varname>ThreadId</varname></term> |
| <listitem> |
| <para>ID of the <varname>W-thread</varname> currently |
| examined by the debugger</para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><varname>ProcessId</varname></term> |
| <listitem> |
| <para>ID of the <varname>W-thread</varname> currently |
| examined by the debugger</para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term><registers></term> |
| <listitem> |
| <para>All CPU registers are also available</para> |
| </listitem> |
| </varlistentry> |
| </variablelist> |
| |
| <para> |
| The <varname>ThreadId</varname> and |
| <varname>ProcessId</varname> variables can be handy to set |
| conditional breakpoints on a given thread or process. |
| </para> |
| </sect3> |
| </sect2> |
| </sect1> |
| |
| |
| <sect1 id="dbg-commands"> |
| <title>WineDbg Command Reference</title> |
| |
| <sect2> |
| <title>Misc</title> |
| |
| <screen> |
| abort aborts the debugger |
| quit exits the debugger |
| |
| attach N attach to a W-process (N is its ID). IDs can be |
| obtained using the walk process command |
| detach detach from a W-process. WineDbg will exit (this may |
| be changed later on) |
| </screen> |
| <screen> |
| help prints some help on the commands |
| help info prints some help on info commands |
| </screen> |
| <screen> |
| mode 16 switch to 16 bit mode |
| mode 32 switch to 32 bit mode |
| </screen> |
| </sect2> |
| |
| <sect2> |
| <title>Flow control</title> |
| |
| <screen> |
| cont continue execution until next breakpoint or exception. |
| pass pass the exception event up to the filter chain. |
| step continue execution until next C line of code (enters |
| function call) |
| next continue execution until next C line of code (doesn't |
| enter function call) |
| stepi execute next assembly instruction (enters function |
| call) |
| nexti execute next assembly instruction (doesn't enter |
| function call) |
| finish do nexti commands until current function is exited |
| </screen> |
| <para> |
| cont, step, next, stepi, nexti can be postfixed by a |
| number (N), meaning that the command must be executed N |
| times. |
| </para> |
| </sect2> |
| |
| <sect2> |
| <title>Breakpoints, watch points</title> |
| |
| <screen> |
| enable N enables (break|watch)point #N |
| disable N disables (break|watch)point #N |
| delete N deletes (break|watch)point #N |
| cond N removes any a existing condition to (break|watch)point N |
| cond N <expr> adds condition <expr> to (break|watch)point N. <expr> |
| will be evaluated each time the breakpoint is hit. If |
| the result is a zero value, the breakpoint isn't |
| triggered |
| break * N adds a breakpoint at address N |
| break <id> adds a breakpoint at the address of symbol <id> |
| break <id> N adds a breakpoint at the address of symbol <id> (N ?) |
| break N adds a breakpoint at line N of current source file |
| break adds a breakpoint at current $pc address |
| watch * N adds a watch command (on write) at address N (on 4 bytes) |
| watch <id> adds a watch command (on write) at the address of |
| symbol <id> |
| info break lists all (break|watch)points (with state) |
| </screen> |
| <para> |
| When setting a breakpoint on an <id>, if several symbols with this |
| <id> exist, the debugger will prompt for the symbol you want to use. |
| Pick up the one you want from its number. |
| </para> |
| <para> |
| Alternatively you can specify a DLL in the <id> (for example |
| MYDLL.DLL.myFunc for function myFunc of |
| <filename>G:\AnyPath\MyDll.dll)</filename>. |
| </para> |
| <para> |
| You can use the symbol <emphasis>EntryPoint</emphasis> to stand for |
| the entry point of the Dll. |
| </para> |
| <para> |
| When setting a break/watch-point by <id>, if the symbol cannot be found (for example, the symbol is contained in a not yet loaded module), winedbg will |
| recall the name of the symbol and will try to set the breakpoint each time a new module is loaded (until it succeeds). |
| </para> |
| </sect2> |
| |
| <sect2> |
| <title>Stack manipulation</title> |
| |
| <screen> |
| bt print calling stack of current thread |
| bt N print calling stack of thread of ID N (note: this |
| doesn't change the position of the current frame as |
| manipulated by the up & dn commands) |
| up goes up one frame in current thread's stack |
| up N goes up N frames in current thread's stack |
| dn goes down one frame in current thread's stack |
| dn N goes down N frames in current thread's stack |
| frame N set N as the current frame for current thread's stack |
| info local prints information on local variables for current |
| function |
| </screen> |
| </sect2> |
| |
| <sect2> |
| <title>Directory & source file manipulation</title> |
| |
| <screen> |
| show dir |
| dir <pathname> |
| dir |
| symbolfile <pathname> loads external symbol definition |
| symbolfile <pathname> N loads external symbol definition |
| (applying an offset of N to addresses) |
| </screen> |
| <screen> |
| list lists 10 source lines from current position |
| list - lists 10 source lines before current position |
| list N lists 10 source lines from line N in current file |
| list <path>:N lists 10 source lines from line N in file <path> |
| list <id> lists 10 source lines of function <id> |
| list * N lists 10 source lines from address N |
| </screen> |
| <para> |
| You can specify the end target (to change the 10 lines |
| value) using the ','. For example: |
| </para> |
| <screen> |
| list 123, 234 lists source lines from line 123 up to line 234 in |
| current file |
| list foo.c:1,56 lists source lines from line 1 up to 56 in file foo.c |
| </screen> |
| </sect2> |
| |
| <sect2> |
| <title>Displaying</title> |
| |
| <para> |
| A display is an expression that's evaluated and printed |
| after the execution of any <command>WineDbg</command> |
| command. |
| </para> |
| <screen> |
| display lists the active displays |
| info display (same as above command) |
| display <expr> adds a display for expression <expr> |
| display /fmt <expr> adds a display for expression <expr>. Printing |
| evaluated <expr> is done using the given format (see |
| print command for more on formats) |
| del display N deletes display #N |
| undisplay N (same as del display) |
| </screen> |
| </sect2> |
| |
| <sect2> |
| <title>Disassembly</title> |
| |
| <screen> |
| disas disassemble from current position |
| disas <expr> disassemble from address <expr> |
| disas <expr>,<expr>disassembles code between addresses specified by |
| the two <expr> |
| </screen> |
| </sect2> |
| |
| <sect2> |
| <title>Information on Wine's internals</title> |
| |
| <screen> |
| info class <id> prints information on Windows's class <id> |
| walk class lists all Windows' class registered in Wine |
| info share lists all the dynamic libraries loaded the debugged |
| program (including .so files, NE and PE DLLs) |
| info module N prints information on module of handle N |
| walk module lists all modules loaded by debugged program |
| info queue N prints information on Wine's queue N |
| walk queue lists all queues allocated in Wine |
| info regs prints the value of CPU register |
| info segment N prints information on segment N |
| info segment lists all allocated segments |
| info stack prints the values on top of the stack |
| info map lists all virtual mappings used by the debugged |
| program |
| info wnd N prints information of Window of handle N |
| walk wnd lists all the window hierarchy starting from the |
| desktop window |
| walk wnd N lists all the window hierarchy starting from the |
| window of handle N |
| walk process lists all w-processes in Wine session |
| walk thread lists all w-threads in Wine session |
| walk modref (no longer avail) |
| walk exception lists the exception frames (starting from current |
| stack frame) |
| </screen> |
| </sect2> |
| |
| <sect2> |
| <title>Memory (reading, writing, typing)</title> |
| |
| <screen> |
| x <expr> examines memory at <expr> address |
| x /fmt <expr> examines memory at <expr> address using format /fmt |
| print <expr> prints the value of <expr> (possibly using its type) |
| print /fmt <expr> prints the value of <expr> (possibly using its |
| type) |
| set <lval>=<expr> writes the value of <expr> in <lval> |
| whatis <expr> prints the C type of expression <expr> |
| </screen> |
| <para> |
| <filename>/fmt</filename> is either <filename>/<letter></filename> or |
| <filename>/<count><letter></filename> letter can be |
| </para> |
| <screen> |
| s => an ASCII string |
| u => an Unicode UTF16 string |
| i => instructions (disassemble) |
| x => 32 bit unsigned hexadecimal integer |
| d => 32 bit signed decimal integer |
| w => 16 bit unsigned hexadecimal integer |
| c => character (only printable 0x20-0x7f are actuallyprinted) |
| b => 8 bit unsigned hexadecimal integer |
| g => GUID |
| </screen> |
| </sect2> |
| |
| <sect2> |
| <title>Expressions</title> |
| |
| <para> |
| Expressions in Wine Debugger are mostly written in a C form. However, there |
| are a few discrepancies: |
| <itemizedlist> |
| <listitem> |
| <para> |
| Identifiers can take a '.' in their names. This allow |
| mainly to access symbols from different DLLs like |
| <function>USER32.DLL.CreateWindowA</function>. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| The debugger will try to distinguish this writing with structure operations. |
| Therefore, you can only use the previous writing in operations manipulating |
| symbols ({break|watch}points, type information command...). |
| </para> |
| </listitem> |
| </itemizedlist> |
| </para> |
| </sect2> |
| <sect2> |
| <title>Debug channels</title> |
| <para> |
| It is possible to turn on and off debug messages as you are debuging using |
| the set command. |
| </para> |
| <screen> |
| set + warn win => turn on warn on 'win' channel |
| set + win => turn on warn/fixme/err/trace on 'win' channel |
| set - win => turn off warn/fixme/err/trace on 'win' channel |
| set - fixme => turn off the 'fixme' class |
| </screen> |
| </sect2> |
| |
| </sect1> |
| |
| <sect1 id="dbg-others"> |
| <title>Other debuggers</title> |
| |
| <sect2> |
| <title>GDB mode</title> |
| |
| <para> |
| WineDbg can act as a remote monitor for GDB. This allows to |
| use all the power of GDB, but while debugging wine and/or |
| any Win32 application. To enable this mode, just add |
| <parameter>--gdb</parameter> to winedbg command line. You'll |
| end up on a GDB prompt. You'll have to use the GDB commands |
| (not the wine nes). |
| </para> |
| |
| <para> |
| However, some limitation in GDB while debugging wine (see |
| below) don't ppear in this mode: |
| <itemizedlist> |
| <listitem> |
| <para> |
| GDB will correctly present Win32 thread |
| information and breakpoint behavior |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Moreover, it also provides support for the Dwarf II |
| debug format (which became the default format (instead |
| of stabs) in gcc 3.1). |
| </para> |
| </listitem> |
| </itemizedlist> |
| </para> |
| |
| <para> |
| A few wine extensions available through the monitor command. |
| <screen> |
| monitor wnd lists all window in the Wine session |
| monitor proc lists all processes in the Wine session |
| monitor mem displays memory mapping of debugged process |
| (doesn't work) |
| </screen> |
| </para> |
| </sect2> |
| |
| <sect2> |
| <title>Using other Unix debuggers</title> |
| |
| <para> |
| You can also use other debuggers (like |
| <command>gdb</command>), but you must be aware of a few |
| items: |
| </para> |
| <para> |
| You need to attach the unix debugger to the correct unix |
| process (representing the correct windows thread) (you can |
| "guess" it from a <command>ps fax</command> for example: |
| When running the emulator, usually the first two |
| <varname>upids</varname> are for the Windows' application |
| running the desktop, the first thread of the application is |
| generally the third <varname>upid</varname>; when running a |
| Winelib program, the first thread of the application is |
| generally the first <varname>upid</varname>) |
| </para> |
| <note> |
| <para> |
| Even if latest <command>gdb</command> implements the |
| notion of threads, it won't work with Wine because the |
| thread abstraction used for implementing Windows' thread |
| is not 100% mapped onto the linux posix threads |
| implementation. It means that you'll have to spawn a |
| different <command>gdb</command> session for each Windows' |
| thread you wish to debug. |
| </para> |
| </note> |
| |
| <!-- *** Extra content spliced in from article by Andreas Mohr *** --> |
| <para> |
| Following text written by &name-andreas-mohr; <email>&email-andreas-mohr;</email> |
| </para> |
| <para> |
| Here's how to get info about the current execution status of a |
| certain Wine process: |
| </para> |
| <para> |
| Change into your Wine source dir and enter: |
| </para> |
| <screen> |
| $ gdb wine |
| </screen> |
| <para> |
| Switch to another console and enter <command>ps ax | grep |
| wine</command> to find all wine processes. Inside |
| <command>gdb</command>, repeat for all Wine processes: |
| </para> |
| <screen> |
| (gdb) attach <userinput>PID</userinput> |
| </screen> |
| <para> |
| with <userinput>PID</userinput> being the process ID of one of |
| the Wine processes. Use |
| </para> |
| <screen> |
| (gdb) bt |
| </screen> |
| <para> |
| to get the backtrace of the current Wine process, i.e. the |
| function call history. That way you can find out what the |
| current process is doing right now. And then you can use |
| several times: |
| </para> |
| <screen> |
| (gdb) n |
| </screen> |
| <para> |
| or maybe even |
| </para> |
| <screen> |
| (gdb) b <userinput>SomeFunction</userinput> |
| </screen> |
| <para> |
| and |
| </para> |
| <screen> |
| (gdb) c |
| </screen> |
| <para> |
| to set a breakpoint at a certain function and continue up to |
| that function. Finally you can enter |
| </para> |
| <screen> |
| (gdb) detach |
| </screen> |
| <para> |
| to detach from the Wine process. |
| </para> |
| <!-- *** End of xtra content *** --> |
| </sect2> |
| |
| <sect2> |
| <title>Using other Windows debuggers</title> |
| |
| <para> |
| You can use any Windows' debugging API compliant debugger |
| with Wine. Some reports have been made of success with |
| VisualStudio debugger (in remote mode, only the hub runs |
| in Wine). GoVest fully runs in Wine. |
| </para> |
| </sect2> |
| |
| <sect2> |
| <title>Main differences between winedbg and regular Unix debuggers</title> |
| <table><title>Debuggers comparison</title> |
| <tgroup cols=2 align="left"> |
| <tbody> |
| <row> |
| <entry>WineDbg</entry><entry>gdb</entry> |
| </row> |
| <row> |
| <entry> |
| WineDbg debugs a Windows' process: the various |
| threads will be handled by the same WineDbg session, |
| and a breakpoint will be triggered for any thread of |
| the W-process |
| </entry> |
| <entry> |
| gdb debugs a Windows' thread: a separate gdb session |
| is needed for each thread of a Windows' process and |
| a breakpoint will be triggered only for the w-thread |
| debugged |
| </entry> |
| </row> |
| <row> |
| <entry> |
| WineDbg supports debug information from stabs |
| (standard Unix format) and Microsoft's C, CodeView, |
| .DBG |
| </entry> |
| <entry> |
| GDB supports debug information from stabs (standard |
| Unix format) and Dwarf II. |
| </entry> |
| </row> |
| </tbody> |
| </tgroup> |
| </table> |
| </sect2> |
| </sect1> |
| |
| |
| <sect1 id="dbg-limits"> |
| <title>Limitations</title> |
| |
| <itemizedlist> |
| <listitem> |
| <para> |
| 16 bit processes are not supported (but calls to 16 bit |
| code in 32 bit applications are). |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Function call in expression is no longer supported |
| </para> |
| </listitem> |
| </itemizedlist> |
| </sect1> |
| </chapter> |
| |
| <!-- Keep this comment at the end of the file |
| Local variables: |
| mode: sgml |
| sgml-parent-document:("wine-doc.sgml" "set" "book" "part" "chapter" "") |
| End: |
| --> |