| <chapter id="debugger"> | |
| <title>Debugging Wine</title> | |
| <sect1 id="dbg-intro"> | |
| <title>Introduction</title> | |
| <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> | |
| Wine also implements <filename>DBGHELP.DLL</filename> which | |
| allows to look into symbols and types from any module (if | |
| the module has been compiled with the proper options). | |
| </para> | |
| <para> | |
| <command>winedbg</command> is a Winelib application making | |
| use of these APIs (<filename>KERNEL32.DLL</filename>'s | |
| debugging API and <filename>DBGHELP.DLL</filename>) 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>info | |
| 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 a neat feature as it allows you | |
| to debug an already started application. | |
| </para> | |
| </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, etc. | |
| </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). | |
| </para> | |
| </sect2> | |
| </sect1> | |
| <sect1 id="wine-debugger"> | |
| <title>Using the Wine Debugger</title> | |
| <para> | |
| This section 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 segment register 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 the<parameter>WINEDEBUG=+relay</parameter> | |
| environment variable set. 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 the | |
| <parameter>WINEDEBUG=+xyz,+relay</parameter> environment variable set. | |
| </para> | |
| <para> | |
| Occasionally there are additional debug channels defined at the | |
| beginning of the file in the form. | |
| <function>WINE_DECLARE_DEBUG_CHANNEL(<channel>);</function> | |
| If so the offending function 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>WINEDEBUG=+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's 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>WINEDEBUG=+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>WINEDEBUG=+relay wine | |
| 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>, archive name | |
| <filename>wcbxxx.zip</> (e.g. <filename>wcb105a.zip</>), which | |
| usually can be found in the <filename>Cica-Mirror</filename> | |
| subdirectory on the Wine ftp sites. (See <filename>ANNOUNCE</>). | |
| </para> | |
| <para> | |
| Disassembling win32 programs is possible using | |
| <application>Windows Disassembler 32</>. Look for | |
| a file called <filename>w32dsm87.zip</> (or similar) | |
| on <ulink url="http://www.winsite.com/">http://www.winsite.com</> | |
| 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 $ WINEDEBUG=+relay,-debug wine winword.exe | |
| |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"You must close Windows and load SHARE.EXE before you start Word.",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 additional debugging tips: | |
| </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> | |
| WINEDEBUG=+relay wine 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> | |
| WINEDEBUG=+relay wine <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="http://www.filelibrary.com:8080/cgi-bin/freedownload/DOS/h/72/ida35bx.zip"> | |
| http://www.filelibrary.com:8080/cgi-bin/freedownload/DOS/h/72/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="http://www.filelibrary.com:8080/cgi-bin/freedownload/DOS/h/72/ida35bx.zip"> | |
| http://www.filelibrary.com:8080/cgi-bin/freedownload/DOS/h/72/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="http://garbo.uwasa.fi/pub/pc/sysinfo/xray15.zip"> | |
| http://garbo.uwasa.fi/pub/pc/sysinfo/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="ftp://ftp.simtel.net/pub/simtelnet/win95/prog/pedump.zip"> | |
| ftp://ftp.simtel.net/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 routine 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> | |
| 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 | |
| <emphasis>always</emphasis> | |
| 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 %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> | |
| <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 decide 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>AlwaysShowThunk</varname></term> | |
| <listitem> | |
| <para> | |
| Set to <literal>TRUE</literal> if the debugger, when | |
| looking up for a symbol from its name, displays all | |
| the thunks with that name. The default value | |
| (<literal>FALSE</literal>) allows not to have to | |
| choose between a symbol and all the import thunks | |
| from all the DLLs using that symbols. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| </variablelist> | |
| </sect2> | |
| </sect1> | |
| <sect1 id="dbg-expr"> | |
| <title>WineDbg Expressions and Variables</title> | |
| <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!CreateWindowExA</function>. | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| In cast operation, when specifying a structure or an | |
| union, you must use the <type>struct</type> or | |
| <type>union</type> keyword (even if your program uses a typedef). | |
| </para> | |
| </listitem> | |
| </itemizedlist> | |
| </para> | |
| <para> | |
| When specifying an identifier by its name, if several | |
| symbols with the same name exist, the debugger will prompt | |
| for the symbol you want to use. Pick up the one you want | |
| from its number. | |
| </para> | |
| <para> | |
| In lots of cases, you can also use regular expressions for | |
| looking for a symbol. | |
| </para> | |
| <para> | |
| <command>winedbg</command> defines its own set of | |
| variables. The configuration variables from above are part | |
| of them. Some others include: | |
| <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, using $ as a | |
| prefix. You can use <command>info regs</command> to | |
| get a list of avaible CPU registers | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| </variablelist> | |
| </para> | |
| <para> | |
| The <varname>$ThreadId</varname> and | |
| <varname>$ProcessId</varname> variables can be handy to set | |
| conditional breakpoints on a given thread or process. | |
| </para> | |
| </sect2> | |
| </sect1> | |
| <sect1 id="dbg-commands"> | |
| <title>WineDbg Command Reference</title> | |
| <sect2> | |
| <title>Misc</title> | |
| <para> | |
| <table> | |
| <title>WineDbg's misc. commands</title> | |
| <tgroup cols="2" align="left"> | |
| <tbody> | |
| <row> | |
| <entry><command>abort</command></entry> | |
| <entry>aborts the debugger</entry> | |
| </row> | |
| <row> | |
| <entry><command>quit</command></entry> | |
| <entry>exits the debugger</entry> | |
| </row> | |
| <row> | |
| <entry><command>attach N</command></entry> | |
| <entry> | |
| attach to a W-process (N is its ID, numeric | |
| or hexadecimal (0xN)). IDs can be obtained using | |
| the info process command. Note the info process | |
| command returns hexadecimal values. | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>detach</command></entry> | |
| <entry>detach from a W-process.</entry> | |
| </row> | |
| <row> | |
| <entry><command>help</command></entry> | |
| <entry>prints some help on the commands</entry> | |
| </row> | |
| <row> | |
| <entry><command>help info</command></entry> | |
| <entry>prints some help on info commands</entry> | |
| </row> | |
| </tbody> | |
| </tgroup> | |
| </table> | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Flow control</title> | |
| <para> | |
| <table> | |
| <title>WineDbg's flow control commands</title> | |
| <tgroup cols="2" align="left"> | |
| <tbody> | |
| <row> | |
| <entry> | |
| <msgtext> | |
| <simplelist type="inline"> | |
| <member><command>cont</command></member> | |
| <member><command>c</command></member> | |
| </simplelist> | |
| </msgtext> | |
| </entry> | |
| <entry>continue execution until next breakpoint or | |
| exception.</entry> | |
| </row> | |
| <row> | |
| <entry><command>pass</command></entry> | |
| <entry>pass the exception event up to the filter | |
| chain.</entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <msgtext> | |
| <simplelist type="inline"> | |
| <member><command>step</command></member> | |
| <member><command>s</command></member> | |
| </simplelist> | |
| </msgtext> | |
| </entry> | |
| <entry> | |
| continue execution until next 'C' line of code | |
| (enters function call) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <msgtext> | |
| <simplelist type="inline"> | |
| <member><command>next</command></member> | |
| <member><command>n</command></member> | |
| </simplelist> | |
| </msgtext> | |
| </entry> | |
| <entry> | |
| continue execution until next 'C' line of code | |
| (doesn't enter function call) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <msgtext> | |
| <simplelist type="inline"> | |
| <member><command>stepi</command></member> | |
| <member><command>si</command></member> | |
| </simplelist> | |
| </msgtext> | |
| </entry> | |
| <entry> | |
| execute next assembly instruction (enters function | |
| call) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <msgtext> | |
| <simplelist type="inline"> | |
| <member><command>nexti</command></member> | |
| <member><command>ni</command></member> | |
| </simplelist> | |
| </msgtext> | |
| </entry> | |
| <entry> | |
| execute next assembly instruction (doesn't enter | |
| function call) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <msgtext> | |
| <simplelist type="inline"> | |
| <member><command>finish</command></member> | |
| <member><command>f</command></member> | |
| </simplelist> | |
| </msgtext> | |
| </entry> | |
| <entry>execute until current function is exited</entry> | |
| </row> | |
| </tbody> | |
| </tgroup> | |
| </table> | |
| </para> | |
| <para> | |
| <command>cont</command>, <command>step</command>, | |
| <command>next</command>, <command>stepi</command>, | |
| <command>nexti</command> can be postfixed by a number (N), | |
| meaning that the command must be executed N times. | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Breakpoints, watch points</title> | |
| <para> | |
| <table> | |
| <title>WineDbg's break & watch points</title> | |
| <tgroup cols="2" align="left"> | |
| <tbody> | |
| <row> | |
| <entry><command>enable N</command></entry> | |
| <entry>enables (break|watch)point #N</entry> | |
| </row> | |
| <row> | |
| <entry><command>disable N</command></entry> | |
| <entry>disables (break|watch)point #N</entry> | |
| </row> | |
| <row> | |
| <entry><command>delete N</command></entry> | |
| <entry>deletes (break|watch)point #N</entry> | |
| </row> | |
| <row> | |
| <entry><command>cond N</command></entry> | |
| <entry> | |
| removes any existing condition to | |
| (break|watch)point N | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>cond N <expr></command> | |
| </entry> | |
| <entry> | |
| 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 | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>break * N</command></entry> | |
| <entry>adds a breakpoint at address N</entry> | |
| </row> | |
| <row> | |
| <entry><command>break <id></command></entry> | |
| <entry> | |
| adds a breakpoint at the address of symbol | |
| <id> | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>break <id> N</command></entry> | |
| <entry> | |
| adds a breakpoint at the address of symbol | |
| <id> (N ?) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>break N</command></entry> | |
| <entry> | |
| adds a breakpoint at line N of current source file | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>break</command></entry> | |
| <entry> | |
| adds a breakpoint at current $PC address | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>watch * N</command></entry> | |
| <entry> | |
| adds a watch command (on write) at address N (on 4 | |
| bytes) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>watch <id></command></entry> | |
| <entry> | |
| adds a watch command (on write) at the address of | |
| symbol <id> | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>info break</command></entry> | |
| <entry> | |
| lists all (break|watch)points (with state) | |
| </entry> | |
| </row> | |
| </tbody> | |
| </tgroup> | |
| </table> | |
| </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> | |
| <para> | |
| <table> | |
| <title>WineDbg's stack manipulation</title> | |
| <tgroup cols="2" align="left"> | |
| <tbody> | |
| <row> | |
| <entry><command>bt</command></entry> | |
| <entry>print calling stack of current thread</entry> | |
| </row> | |
| <row> | |
| <entry><command>bt N</command></entry> | |
| <entry> | |
| print calling stack of thread of ID N (note: this | |
| doesn't change the position of the current frame | |
| as manipulated by the <command>up</command> and | |
| <command>dn</command> commands) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>up</command></entry> | |
| <entry> | |
| goes up one frame in current thread's stack | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>up N</command></entry> | |
| <entry> | |
| goes up N frames in current thread's stack | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>dn</command></entry> | |
| <entry> | |
| goes down one frame in current thread's stack | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>dn N</command></entry> | |
| <entry> | |
| goes down N frames in current thread's stack | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>frame N</command></entry> | |
| <entry> | |
| set N as the current frame for current thread's | |
| stack | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>info local</command></entry> | |
| <entry> | |
| prints information on local variables for current | |
| function frame | |
| </entry> | |
| </row> | |
| </tbody> | |
| </tgroup> | |
| </table> | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Directory & source file manipulation</title> | |
| <para> | |
| <table> | |
| <title>WineDbg's directory & source file manipulation</title> | |
| <tgroup cols="2" align="left"> | |
| <tbody> | |
| <row> | |
| <entry><command>show dir</command></entry> | |
| <entry> | |
| prints the list of dir:s where source files are | |
| looked for | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>dir <pathname></command> | |
| </entry> | |
| <entry> | |
| adds <pathname> to the list of dir:s | |
| where to look for source files | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>dir</command></entry> | |
| <entry> | |
| deletes the list of dir:s where to look for source | |
| files | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command> | |
| symbolfile <pathname> | |
| </command> | |
| </entry> | |
| <entry>loads external symbol definition</entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command> | |
| symbolfile <pathname> N | |
| </command> | |
| </entry> | |
| <entry> | |
| loads external symbol definition (applying an | |
| offset of N to addresses) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>list</command></entry> | |
| <entry> | |
| lists 10 source lines forwards from current | |
| position | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>list -</command></entry> | |
| <entry> | |
| lists 10 source lines backwards from current | |
| position | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>list N</command></entry> | |
| <entry> | |
| lists 10 source lines from line N in current file | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>list <path>:N</command> | |
| </entry> | |
| <entry> | |
| lists 10 source lines from line N in file | |
| <path> | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>list <id></command></entry> | |
| <entry> | |
| lists 10 source lines of function <id> | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>list * N</command></entry> | |
| <entry>lists 10 source lines from address N</entry> | |
| </row> | |
| </tbody> | |
| </tgroup> | |
| </table> | |
| </para> | |
| <para> | |
| You can specify the end target (to change the 10 lines | |
| value) using the ','. For example: | |
| <table> | |
| <title>WineDbg's list command examples</title> | |
| <tgroup cols="2" align="left"> | |
| <tbody> | |
| <row> | |
| <entry><command>list 123, 234</command></entry> | |
| <entry> | |
| lists source lines from line 123 up to line 234 in | |
| current file | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>list foo.c:1, 56</command></entry> | |
| <entry> | |
| lists source lines from line 1 up to 56 in file | |
| foo.c | |
| </entry> | |
| </row> | |
| </tbody> | |
| </tgroup> | |
| </table> | |
| </para> | |
| </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> | |
| <para> | |
| <command>winedbg</command> will automatically detect if the | |
| expression you entered contains a local variable. If so, | |
| display will only be shown if the context is still in the | |
| same function as the one the debugger was in when the | |
| display expression was entered. | |
| </para> | |
| <para> | |
| <table> | |
| <title>WineDbg's displays</title> | |
| <tgroup cols="2" align="left"> | |
| <tbody> | |
| <row> | |
| <entry> | |
| <command>info display</command> | |
| </entry> | |
| <entry>lists the active displays</entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>display</command> | |
| </entry> | |
| <entry> | |
| print the active displays' values (as done each | |
| time the debugger stops) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>display <expr></command> | |
| </entry> | |
| <entry> | |
| adds a display for expression <expr> | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command> | |
| display /fmt <expr> | |
| </command> | |
| </entry> | |
| <entry> | |
| adds a display for expression | |
| <expr>. Printing evaluated <expr> is | |
| done using the given format (see | |
| <command>print</command> command for more on | |
| formats) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <msgtext> | |
| <simplelist type="inline"> | |
| <member> | |
| <command>del display N</command> | |
| </member> | |
| <member> | |
| <command>undisplay N</command> | |
| </member> | |
| </simplelist> | |
| </msgtext> | |
| </entry> | |
| <entry>deletes display #N</entry> | |
| </row> | |
| </tbody> | |
| </tgroup> | |
| </table> | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Disassembly</title> | |
| <para> | |
| <table> | |
| <title>WineDbg's dissassembly</title> | |
| <tgroup cols="2" align="left"> | |
| <tbody> | |
| <row> | |
| <entry><command>disas</command></entry> | |
| <entry>disassemble from current position</entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>disas <expr></command> | |
| </entry> | |
| <entry> | |
| disassemble from address <expr> | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command> | |
| disas <expr>,<expr> | |
| </command> | |
| </entry> | |
| <entry> | |
| disassembles code between addresses specified by | |
| the two <expr> | |
| </entry> | |
| </row> | |
| </tbody> | |
| </tgroup> | |
| </table> | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Memory (reading, writing, typing)</title> | |
| <para> | |
| <table> | |
| <title>WineDbg's memory management</title> | |
| <tgroup cols="2" align="left"> | |
| <tbody> | |
| <row> | |
| <entry> | |
| <command>x <expr></command> | |
| </entry> | |
| <entry> | |
| examines memory at <expr> address | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command> | |
| x /fmt <expr> | |
| </command> | |
| </entry> | |
| <entry> | |
| examines memory at <expr> address using | |
| format /fmt | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command> | |
| print <expr> | |
| </command> | |
| </entry> | |
| <entry> | |
| prints the value of <expr> (possibly using | |
| its type) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command> | |
| print /fmt <expr> | |
| </command> | |
| </entry> | |
| <entry>prints the value of <expr> (possibly | |
| using its type) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command> | |
| set <lval> = <expr> | |
| </command> | |
| </entry> | |
| <entry> | |
| writes the value of <expr> in <lval> | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command> | |
| whatis <expr> | |
| </command> | |
| </entry> | |
| <entry> | |
| prints the C type of expression <expr> | |
| </entry> | |
| </row> | |
| </tbody> | |
| </tgroup> | |
| </table> | |
| </para> | |
| <para> | |
| <filename>/fmt</filename> is either <filename>/<letter></filename> or | |
| <filename>/<count><letter></filename> letter can be | |
| <simplelist type="horiz" columns="2"> | |
| <member>s</member><member>an ASCII string</member> | |
| <member>u</member><member>an Unicode UTF16 string</member> | |
| <member>i</member><member>instructions (disassemble)</member> | |
| <member>x</member><member>32 bit unsigned hexadecimal integer</member> | |
| <member>d</member><member>32 bit signed decimal integer</member> | |
| <member>w</member><member>16 bit unsigned hexadecimal integer</member> | |
| <member>c</member><member>character (only printable 0x20-0x7f are actually printed)</member> | |
| <member>b</member><member>8 bit unsigned hexadecimal integer</member> | |
| <member>g</member><member>GUID</member> | |
| </simplelist> | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Information on Wine's internals</title> | |
| <para> | |
| <table> | |
| <title>WineDbg's Win32 objects management</title> | |
| <tgroup cols="2" align="left"> | |
| <tbody> | |
| <row> | |
| <entry><command>info class</command></entry> | |
| <entry> | |
| lists all Windows' classes registered in Wine | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command> | |
| info class <id> | |
| </command> | |
| </entry> | |
| <entry> | |
| prints information on Windows's class <id> | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>info share;</command></entry> | |
| <entry> | |
| lists all the dynamic libraries loaded in the | |
| debugged program (including .so files, NE and PE | |
| DLLs) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command> | |
| info share <N>; | |
| </command> | |
| </entry> | |
| <entry> | |
| prints information on module at address <N> | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>info regs;</command></entry> | |
| <entry> | |
| prints the value of the CPU registers | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>info segment <N>;</command> | |
| </entry> | |
| <entry> | |
| prints information on segment <N> (i386 | |
| only) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>info segment;</command> | |
| </entry> | |
| <entry> | |
| lists all allocated segments (i386 only) | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>info stack;</command></entry> | |
| <entry> | |
| prints the values on top of the stack | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>info map;</command></entry> | |
| <entry> | |
| lists all virtual mappings used by the debugged | |
| program | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>info map <N></command> | |
| </entry> | |
| <entry> | |
| lists all virtual mappings used by the program of | |
| pid <N> | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>info wnd <N></command> | |
| </entry> | |
| <entry> | |
| prints information of Window of handle <N> | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>info wnd</command></entry> | |
| <entry> | |
| lists all the window hierarchy starting from the | |
| desktop window | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>info process</command></entry> | |
| <entry> | |
| lists all w-processes in Wine session | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>info thread</command></entry> | |
| <entry>lists all w-threads in Wine session</entry> | |
| </row> | |
| <row> | |
| <entry><command>info exception</command></entry> | |
| <entry> | |
| lists the exception frames (starting from current | |
| stack frame) | |
| </entry> | |
| </row> | |
| </tbody> | |
| </tgroup> | |
| </table> | |
| </para> | |
| </sect2> | |
| <sect2 id="winedbg-dbg-chan"> | |
| <title>Debug channels</title> | |
| <para> | |
| It is possible to turn on and off debug messages as you | |
| are debugging using the set command. | |
| See <xref linkend="debugging"> for more details on debug | |
| channels. | |
| </para> | |
| <para> | |
| <table> | |
| <title>WineDbg's debug channels' management</title> | |
| <tgroup cols="2" align="left"> | |
| <tbody> | |
| <row> | |
| <entry> | |
| <command>set + warn win</command> | |
| </entry> | |
| <entry>turn on warn on 'win' channel</entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>set + win</command> | |
| </entry> | |
| <entry> | |
| turn on warn/fixme/err/trace on 'win' channel | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>set - win</command> | |
| </entry> | |
| <entry> | |
| turn off warn/fixme/err/trace on 'win' channel | |
| </entry> | |
| </row> | |
| <row> | |
| <entry> | |
| <command>set - fixme</command> | |
| </entry> | |
| <entry>turn off the 'fixme' class</entry> | |
| </row> | |
| </tbody> | |
| </tgroup> | |
| </table> | |
| </para> | |
| </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 WineDbg's). | |
| </para> | |
| <para> | |
| However, some limitation in GDB while debugging wine (see | |
| below) don't appear 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. | |
| <table> | |
| <title>WineDbg's debug channels' management</title> | |
| <tgroup cols="2" align="left"> | |
| <tbody> | |
| <row> | |
| <entry><command>monitor wnd</command></entry> | |
| <entry>lists all window in the Wine session</entry> | |
| </row> | |
| <row> | |
| <entry><command>monitor proc</command></entry> | |
| <entry> | |
| lists all processes in the Wine session | |
| </entry> | |
| </row> | |
| <row> | |
| <entry><command>monitor mem</command></entry> | |
| <entry> | |
| displays memory mapping of debugged process | |
| </entry> | |
| </row> | |
| </tbody> | |
| </tgroup> | |
| </table> | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Graphical frontends to gdb</title> | |
| <para> | |
| This section will describe how you can debug Wine using the | |
| GDB mode of winedbg and some graphical front ends to GDB for | |
| those of you who really like graphical debuggers. | |
| </para> | |
| <sect3> | |
| <title>DDD</title> | |
| <para> | |
| Use the following steps, in this order: | |
| <orderedlist> | |
| <listitem> | |
| <para> | |
| Start the Wine debugger with a command line like: | |
| <screen> | |
| winedbg --gdb --no-start <name_of_exe_to_debug.exe> | |
| </screen> | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| Start ddd | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| In ddd, use the 'Open File' or 'Open Program' to | |
| point to the Wine executable (which is either | |
| wine-pthread or wine-kthread depending on your | |
| settings). | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| In the output of 1/, there's a line like | |
| <screen> | |
| target remote localhost:32878 | |
| </screen> | |
| copy that line and paste into ddd command pane (the | |
| one with the (gdb) prompt) | |
| </para> | |
| </listitem> | |
| </orderedlist> | |
| The program should now be loaded and up and running. If | |
| you want, you can also add in 1/ after the name of the | |
| exec all the needed parameters | |
| </para> | |
| </sect3> | |
| <sect3> | |
| <title>kdbg</title> | |
| <para> | |
| Use the following steps, in this order: | |
| <orderedlist> | |
| <listitem> | |
| <para> | |
| Start the Wine debugger with a command line like: | |
| <screen> | |
| winedbg --gdb --no-start <name_of_exe_to_debug.exe> | |
| </screen> | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| In the output of 1/, there's a line like | |
| <screen> | |
| target remote localhost:32878 | |
| </screen> | |
| Start kdbg with | |
| <screen> | |
| kdbg -r localhost:32878 wine | |
| </screen> | |
| localhost:32878 is not a fixed value, but has been | |
| printed in step 1/. 'wine' should also be the full | |
| path to the Wine executable (which is either | |
| wine-pthread or wine-kthread depending on your settings). | |
| </para> | |
| </listitem> | |
| </orderedlist> | |
| The program should now be loaded and up and running. If | |
| you want, you can also add in 1/ after the name of the | |
| exec all the needed parameters | |
| </para> | |
| </sect3> | |
| </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> | |
| <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-devel.sgml" "set" "book" "part" "chapter" "") | |
| End: | |
| --> |