2362 lines
76 KiB
Plaintext
2362 lines
76 KiB
Plaintext
<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>
|
|
<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:
|
|
-->
|