Wine Developer's Guide


Table of Contents
I. Developing Wine
1. Debugging Wine
1.1. Introduction
1.2. WineDbg's modes of invocation
1.3. Using the Wine Debugger
1.4. Useful memory addresses
1.5. Configuration
1.6. WineDbg Expressions and Variables
1.7. WineDbg Command Reference
1.8. Other debuggers
2. Debug Logging
2.1. Debugging classes
2.2. Debugging channels
2.3. Are we debugging?
2.4. Helper functions
2.5. Controlling the debugging output
2.6. Compiling Out Debugging Messages
2.7. A Few Notes on Style
3. Other debugging techniques
3.1. Doing A Hardware Trace
3.2. Understanding undocumented APIs
3.3. How to do regression testing using Git
3.4. Which code has been tested?
4. Coding Practice
4.1. Patch Format
4.2. Some notes about style
4.3. Quality Assurance
4.4. Porting Wine to new Platforms
4.5. Adding New Languages
5. Writing Conformance tests
5.1. Introduction
5.2. What to test for?
5.3. Running the tests in Wine
5.4. Cross-compiling the tests with MinGW
5.5. Building and running the tests on Windows
5.6. Inside a test
5.7. Writing good error messages
5.8. Handling platform issues
6. Documenting Wine
6.1. An Overview Of Wine Documentation
6.2. Writing Wine API Documentation
6.3. The Wine DocBook System
II. Wine Architecture
7. Overview
7.1. Wine Overview
7.2. Standard Windows Architectures
7.3. Wine architecture
8. Kernel modules
8.1. The Wine initialization process
8.2. Detailed memory management
8.3. Multi-processing in Wine
8.4. Multi-threading in Wine
8.5. Structured Exception Handling
8.6. File management
8.7. NTDLL module
8.8. KERNEL32 Module
9. Graphical modules
9.1. GDI Module
10. Windowing system
10.1. USER Module
10.2. X Windows System interface
11. COM in Wine
11.1. Writing COM Components for Wine
11.2. A brief introduction to DCOM in Wine
12. Wine and OpenGL
12.1. What is needed to have OpenGL support in Wine
12.2. How it all works
12.3. Known problems
13. Outline of DirectDraw Architecture
13.1. DirectDraw inheritance tree
13.2. DirectDrawSurface inheritance tree
13.3. Interface Thunks
13.4. Logical Object Layout
13.5. Creating Objects
14. Wine and Multimedia
14.1. Overview
14.2. Multimedia architecture
14.3. Low level layers
14.4. Mid level drivers (MCI)
14.5. High level layers
14.6. MS ACM Dlls
14.7. MS Video Dlls
14.8. Multimedia configuration
List of Tables
1-1. WineDbg's misc. commands
1-2. WineDbg's flow control commands
1-3. WineDbg's break & watch points
1-4. WineDbg's stack manipulation
1-5. WineDbg's directory & source file manipulation
1-6. WineDbg's list command examples
1-7. WineDbg's displays
1-8. WineDbg's dissassembly
1-9. WineDbg's memory management
1-10. WineDbg's Win32 objects management
1-11. WineDbg's debug channels' management
1-12. WineDbg's debug channels' management
1-13. Debuggers comparison
7-1. Wine executables
7-2. Memory layout (Windows and Wine)
8-1. DOS, Win32 and NT paths equivalences
8-2. File systems' properties
8-3. Mapping of Windows device names into Unix device names
8-4. Function consoles implementation comparison
8-5. Console registry settings
14-1. Wine multimedia drivers' functionalities
14-2. Wine MCI drivers
14-3. Wine ACM drivers
14-4. Wine VIDC drivers
14-5. Wine multimedia configuration scheme

Chapter 1. Debugging Wine

1.1. Introduction

1.1.1. Processes and threads: in underlying OS and in Windows

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.

Each Windows' thread is implemented as a Unix thread, meaning that all threads of a same Windows' process share the same (unix) address space.

In the following:

  • W-process means a process in Windows' terminology

  • U-process means a process in Unix' terminology

  • W-thread means a thread in Windows' terminology

A W-process is made of one or several W-threads. Each W-thread is mapped to one and only one U-process. All U-processes of a same W-process share the same address space.

Each Unix process can be identified by two values:

  • the Unix process id (upid in the following)

  • the Windows's thread id (tid)

Each Windows' process has also a Windows' process id (wpid in the following). It must be clear that upid and wpid are different and shall not be used instead of the other.

Wpid and tid 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 W-process).


1.1.2. Wine, debugging and WineDbg

When talking of debugging in Wine, there are at least two levels to think of:

  • the Windows' debugging API.

  • the Wine integrated debugger, dubbed winedbg.

Wine implements most of the Windows' debugging API. The first part of the debugging APIs (in KERNEL32.DLL) allows a W-process, called the debugger, to control the execution of another W-process, the debuggee. To control means stopping/resuming execution, enabling/disabling single stepping, setting breakpoints, reading/writing debuggee memory... Another part of the debugging APIs resides in DBGHELP.DLL (and its ancestor IMAGEHLP.DLL) and lets a debugger look into symbols and types from any module (if the module has been compiled with the proper options).

winedbg is a Winelib application making use of these APIs (KERNEL32.DLL's debugging API and DBGHELP.DLL) to allow debugging both any Wine or Winelib applications as well as Wine itself (kernel and all DLLs).


1.2. WineDbg's modes of invocation

1.2.1. Starting a process

Any application (either a Windows' native executable, or a Winelib application) can be run through winedbg. Command line options and tricks are the same as for wine:

winedbg telnet.exe
winedbg hl.exe -windowed
        

1.2.2. Attaching

winedbg can also be launched without any command line argument: winedbg is started without any attached process. You can get a list of running W-processes (and their wpid's) using the info process command, and then, with the attach command, pick up the wpid of the W-process you want to debug. This is a neat feature as it allows you to debug an already started application.


1.2.3. On exceptions

When something goes wrong, Windows tracks this as an exception. Exceptions exist for segmentation violation, stack overflow, division by zero, etc.

When an exception occurs, Wine checks if the W-process 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.

If the W-process is not debugged, Wine tries to launch a debugger. This debugger (normally winedbg, see III Configuration for more details), at startup, attaches to the W-process 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.

If winedbg is the standard debugger, the pass and cont commands are the two ways to let the process go further for the handling of the exception event.

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:

continue

the debugger had the ability to correct what's generated the exception, and is now able to continue process execution.

pass

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.

Note: since some of Wine's code uses exceptions and try/catch blocks to provide some functionality, winedbg can be entered in such cases with segv exceptions. This happens, for example, with IsBadReadPtr function. In that case, the pass command shall be used, to let the handling of the exception to be done by the catch block in IsBadReadPtr.


1.2.4. Interrupting

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.


1.2.5. Quitting

Wine supports the new XP APIs, allowing for a debugger to detach from a program being debugged (see detach command).


1.3. Using the Wine Debugger

This section describes where to start debugging Wine. If at any point you get stuck and want to ask for help, please read the How to Report A Bug section of the Wine Users Guide for information on how to write useful bug reports.


1.3.1. Crashes

These usually show up like this:

|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
        

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.

  1. 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 whole crashdump even if it doesn't make sense to you.

    (In this case it is access to an invalid selector, for %es is 0000, as seen in the register dump).

  2. 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 WINEDEBUG=+relay 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:

    |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.
                
  3. If you have found a misbehaving Wine 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 WINE_DEFAULT_DEBUG_CHANNEL(<channel>); at the beginning of the source file. Rerun wine with the WINEDEBUG=+xyz,+relay environment variable set.

    Occasionally there are additional debug channels defined at the beginning of the source file in the form WINE_DECLARE_DEBUG_CHANNEL(<channel>); If so the offending function may also uses one of these alternate channels. Look through the the function for TRACE_(<channel>)(" ... /n"); and add any additional channels to the commandline.

  4. Additional information on how to debug using the internal debugger can be found in programs/winedbg/README.

  5. 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 WINEDEBUG=+all, which dumps ALL included debug information in wine. It is often necessary to limit the debug output produced. That can be done by piping the output through grep, or alternatively with registry keys. See Section 1.5.3 for more information.

  6. 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 gdb instead of using the Wine debugger. If you do that, use handle SIGSEGV nostop noprint to disable the handling of seg faults inside gdb (needed for Win16).

  7. You can also set a breakpoint for that function. Start wine using winedbg instead of wine. Once the debugger is running enter break KERNEL_LSTRLEN (replace by function you want to debug, CASE IS RELEVANT) to set a breakpoint. Then use continue 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 continue 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.


1.3.2. Program hangs, nothing happens

Start the program with winedbg instead of wine. When the program locks up switch to the winedbg's terminal and press Ctrl-C. This will stop the program and let you debug the program as you would for a crash.


1.3.3. Program reports an error with a Messagebox

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.

Since the failure happens usually directly before setting up the Messagebox you can start winedbg and set a breakpoint at MessageBoxA (called by win16 and win32 programs) and proceed with continue. With WINEDEBUG=+all Wine will now stop directly before setting up the Messagebox. Proceed as explained above.

You can also run wine using WINEDEBUG=+relay wine program.exe 2>&1 | less -i and in less search for "MessageBox".


1.3.4. Disassembling programs

You may also try to disassemble the offending program to check for undocumented features and/or use of them.

The best, freely available, disassembler for Win16 programs is Windows Codeback, archive name wcbxxx.zip (e.g. wcb105a.zip).

Disassembling win32 programs is possible using eg. GoVest by Ansgar Trimborn. It can be found here.

You can also use the newer and better Interactive Disassembler (IDA) from DataRescue. Take a look in the AppDB for links to various versions of IDA.

Another popular disassembler is Windows Disassembler 32 from URSoft. Look for a file called w32dsm87.zip (or similar) on winsite.com or softpedia.com. It seems that Windows Disassembler 32 currently has problems working correctly under Wine, so use IDA or GoVest.

Also of considerable fame amongst disassemblers is SoftIce from NuMega. That software has since been acquired by CompuWare (http://www.compuware.com/) and made part of their Windows driver development suite. Newer versions of SoftIce needs to run as a Windows Service and therefore won't currently work under Wine.

If nothing works for you, you might try one of the disassemblers found in Google's directory.

Understanding disassembled code is mostly a question of exercise. Most code out there uses standard C function entries (for it is usually written in C). Win16 function entries usually look like that:

push bp
mov bp, sp
... function code ..
retf XXXX 	<--------- XXXX is number of bytes of arguments
        

This is a FAR function with no local storage. The arguments usually start at [bp+6] with increasing offsets. Note, that [bp+6] belongs to the rightmost argument, for exported win16 functions use the PASCAL calling convention. So, if we use strcmp(a,b) with a and b both 32 bit variables b would be at [bp+6] and a at [bp+10].

Most functions make also use of local storage in the stackframe:

enter 0086, 00
... function code ...
leave
retf XXXX
        

This does mostly the same as above, but also adds 0x86 bytes of stackstorage, which is accessed using [bp-xx]. Before calling a function, arguments are pushed on the stack using something like this:

push word ptr [bp-02]	<- will be at [bp+8]
push di			<- will be at [bp+6]
call KERNEL.LSTRLEN
        

Here first the selector and then the offset to the passed string are pushed.


1.3.5. Sample debugging session

Let's debug the infamous Word SHARE.EXE messagebox:

|marcus@jet $ wine winword.exe
|            +---------------------------------------------+
|            | !  You must leave Windows and load SHARE.EXE|
|            |    before starting Word.                    |
|            +---------------------------------------------+
        
|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 writable.
|Please check your configuration file if this generates a failure.
        

Whoops, it even detects that something is wrong!

|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
        

This fails, since my C: drive is in this case mounted readonly.

|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
        

And MessageBox'ed.

|Stopped on breakpoint 2 at 0x40189100 (MessageBoxA [msgbox.c:190])
|190     {		<- the sourceline
In 32 bit mode.
Wine-dbg>
        

The code seems to find a writable harddisk and tries to create a file there. To work around this bug, you can define C: as a networkdrive, which is ignored by the code above.


1.3.6. Debugging Tips

Here are some additional debugging tips:

  • 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

    WINEDEBUG=+relay wine program
                  
    to get a listing of the functions the program calls in its start function. Now you do a
    winedbg winfile.exe
                  

    This way, you get into winedbg. Now you can set a breakpoint on any function the program calls in the start function and just type c 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.

  • 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 MessageBox(). That's why you should re-run the program with e.g.

    WINEDEBUG=+relay wine  <program name> &>relmsg
                  
    Then do a more relmsg and search for the last occurrence of a call to the string "MESSAGEBOX". This is a line like
    Call USER.1: MESSAGEBOX(0x0000,0x01ff1246 "Runtime error 219 at 0004:1056.",0x00000000,0x1010) ret=01f7:2160 ds=01ff
                  
    In my example the lines before the call to MessageBox() look like that:
    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
                  

    I think that the call to MessageBox() in this example is not 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 0x0004:0x1056.

    As the segment value of the address is only 4, I think that that is only an internal program value. But the offset address reveals something quite interesting: Offset 1056 is very close to the return address of FREELIBRARY():

    Call KERNEL.96: FREELIBRARY(0x031f) ret=01cf:105c ds=01ff
                                                 ^^^^
                  

    Provided that segment 0x0004 is indeed segment 0x1cf, we now we can use IDA to disassemble the part that caused the error. We just have to find the address of the call to FreeLibrary(). 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 0x1cf).

  • 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 GetVersion16/32 as those functions are called very often.

    Then do a c until you are able to set this breakpoint without error message.


1.3.7. Some basic debugger usages

After starting your program with

winedbg myprog.exe
        

the program loads and you get a prompt at the program starting point. Then you can set breakpoints:

  b RoutineName      (by routine name) OR
  b *0x812575        (by address)
        

Then you hit c (continue) to run the program. It stops at the breakpoint. You can type

  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)
        

By hitting Enter, you repeat the last command.


1.3.8. Useful programs

Some useful programs:

GoVest: govest.zip is available from http://www.geocities.com/GoVest/.

Simple win32 disassembler that works well with Wine.

IDA:

IDA Pro is highly recommended, but is not free. DataRescue does however make trial versions available.

Take a look in the AppDB for links to various versions of IDA.

XRAY: http://garbo.uwasa.fi/pub/pc/sysinfo/xray15.zip

Traces DOS calls (Int 21h, DPMI, ...). Use it with Windows to correct file management problems etc.

pedump: ftp://ftp.simtel.net/pub/simtelnet/win95/prog/pedump.zip

Dumps the imports and exports of a PE (Portable Executable) DLL.

winedump: (included in wine tree)

Dumps the imports and exports of a PE (Portable Executable) DLL.


1.4. Useful memory addresses

Wine uses several different kinds of memory addresses.

Win32/"normal" Wine addresses/Linux: linear addresses.

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).

Win16 "enhanced mode": segmented addresses.

These are the "normal" Win16 addresses, called SEGPTR. They have a segment:offset notation, e.g. 0x01d7:0x0012. The segment part usually is a "selector", which always has the lowest 3 bits set. Some sample selectors are 0x1f7, 0x16f, 0x8f. If these bits are set except for the lowest bit, as e.g. with 0x1f6,xi then it might be a handle to global memory. Just set the lowest bit to get the selector in these cases. A selector kind of "points" to a certain linear (see above) base address. It has more or less three important attributes: segment base address, segment limit, segment access rights.

Example:

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.

DOS/Win16 "standard mode"

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.

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 ;-)


1.5. Configuration

1.5.1. Windows Debugging configuration

The Windows' debugging API uses a registry entry to know which debugger to invoke when an unhandled exception occurs (see On exceptions for some details). Two values in key

[MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug]
        

Determine the behavior:

Debugger

This is the command line used to launch the debugger (it uses two printf formats (%ld) to pass context dependent information to the debugger). You should put here a complete path to your debugger (winedbg can of course be used, but any other Windows' debugging API aware debugger will do). The path to the debugger you choose to use must be reachable via one of the DOS drives configured under /dosdevices in your $WINEPREFIX or ~/.wine folder.

Auto

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.

A regular Wine registry looks like:

[MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug] 957636538
"Auto"=dword:00000001
"Debugger"="winedbg %ld %ld"
        

Note 1: Creating this key is mandatory. Not doing so will not fire the debugger when an exception occurs.

Note 2: wineinstall (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

[MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug]
          

key before running again wineinstall to regenerate this key.


1.5.2. WineDbg configuration

winedbg can be configured through a number of options. Those options are stored in the registry, on a per user basis. The key is (in my registry)

[HKCU\\Software\\Wine\\WineDbg]
        

Those options can be read/written while inside winedbg, as part of the debugger expressions. To refer to one of these options, its name must be prefixed by a $ sign. For example,

set $BreakAllThreadsStartup = 1
        

sets the option BreakAllThreadsStartup to TRUE.

All the options are read from the registry when winedbg starts (if no corresponding value is found, a default value is used), and are written back to the registry when winedbg exits (hence, all modifications to those options are automatically saved when winedbg terminates).

Here's the list of all options:

BreakAllThreadsStartup

Set to TRUE if at all threads start-up the debugger stops set to FALSE if only at the first thread startup of a given process the debugger stops. FALSE by default.

BreakOnCritSectTimeOut

Set to TRUE if the debugger stops when a critical section times out (5 minutes); TRUE by default.

BreakOnAttach

Set to TRUE if when winedbg attaches to an existing process after an unhandled exception, winedbg 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 FALSE.

BreakOnFirstChance

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 winedbg's cont command) or pass the exception up to the exception handler chain in the program (if it exists) (winedbg implements this through the pass 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 BreakOnFirstChance exception is TRUE, then winedbg is entered for both first and last chance execptions (to FALSE, it's only entered for last chance exceptions).

AlwaysShowThunk

Set to TRUE if the debugger, when looking up for a symbol from its name, displays all the thunks with that name. The default value (FALSE) allows not to have to choose between a symbol and all the import thunks from all the DLLs using that symbols.


1.5.3. Configuring +relay behaviour

When setting WINEDEBUG to +relay and debugging, you might get a lot of output. You can limit the output by configuring the value RelayExclude in the registry, located under the key:

          [HKCU\\Software\\Wine\\Debug]
        

Set the value of RelayExclude to a semicolon-separated list of calls to exclude. Example: "RtlEnterCriticalSection;RtlLeaveCriticalSection;kernel32.97;kernel32.98".

RelayInclude is an option similar to RelayExclude, except that functions listed here will be the only ones included in the output. Also see section 3.4.3.5 for more options.

If your application runs too slow with +relay to get meaningful output and you're stuck with multi-GB relay log files, but you're not sure what to exclude, here's a trick to get you started. First, run your application for a minute or so, piping it's output to a file on disk:

          WINEDEBUG=+relay wine <appname.exe> &>relay.log
        

Then run this command to see which calls are performed the most:

          awk -F'(' '{print $1}' < relay.log | awk '{print $2}' | sort | uniq -c | sort
        

Exclude the bottom-most calls with RelayExclude after making sure that they are irrelevant, then run your application again.


1.6. WineDbg Expressions and Variables

1.6.1. Expressions

Expressions in Wine Debugger are mostly written in a C form. However, there are a few discrepancies:

  • Identifiers can take a '!' in their names. This allow mainly to access symbols from different DLLs like USER32!CreateWindowExA.

  • In cast operation, when specifying a structure or an union, you must use the struct or union keyword (even if your program uses a typedef).

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.

In lots of cases, you can also use regular expressions for looking for a symbol.

winedbg defines its own set of variables. The configuration variables from above are part of them. Some others include:

$ThreadId

ID of the W-thread currently examined by the debugger

$ProcessId

ID of the W-thread currently examined by the debugger

<registers>

All CPU registers are also available, using $ as a prefix. You can use info regs to get a list of avaible CPU registers

The $ThreadId and $ProcessId variables can be handy to set conditional breakpoints on a given thread or process.


1.7. WineDbg Command Reference

1.7.1. Misc

Table 1-1. WineDbg's misc. commands

abortaborts the debugger
quitexits the debugger
attach N 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.
detachdetach from a W-process.
helpprints some help on the commands
help infoprints some help on info commands


1.7.2. Flow control

Table 1-2. WineDbg's flow control commands

cont, c continue execution until next breakpoint or exception.
passpass the exception event up to the filter chain.
step, s continue execution until next 'C' line of code (enters function call)
next, n continue execution until next 'C' line of code (doesn't enter function call)
stepi, si execute next assembly instruction (enters function call)
nexti, ni execute next assembly instruction (doesn't enter function call)
finish, f execute until current function is exited

cont, step, next, stepi, nexti can be postfixed by a number (N), meaning that the command must be executed N times.


1.7.3. Breakpoints, watch points

Table 1-3. WineDbg's break & watch points

enable Nenables (break|watch)point #N
disable Ndisables (break|watch)point #N
delete Ndeletes (break|watch)point #N
cond N removes any existing condition to (break|watch)point N
cond N <expr> adds condition <expr> to (break|watch)point N. <expr> will be evaluated each time the breakpoint is hit. If the result is a zero value, the breakpoint isn't triggered
break * Nadds a breakpoint at address N
break <id> adds a breakpoint at the address of symbol <id>
break <id> N adds a breakpoint at the address of symbol <id> (N ?)
break N adds a breakpoint at line N of current source file
break adds a breakpoint at current $PC address
watch * N adds a watch command (on write) at address N (on 4 bytes)
watch <id> adds a watch command (on write) at the address of symbol <id>
info break lists all (break|watch)points (with state)

You can use the symbol EntryPoint to stand for the entry point of the Dll.

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).


1.7.4. Stack manipulation

Table 1-4. WineDbg's stack manipulation

btprint calling stack of current thread
bt N print calling stack of thread of ID N (note: this doesn't change the position of the current frame as manipulated by the up and dn commands)
up goes up one frame in current thread's stack
up N goes up N frames in current thread's stack
dn goes down one frame in current thread's stack
dn N goes down N frames in current thread's stack
frame N set N as the current frame for current thread's stack
info local prints information on local variables for current function frame


1.7.5. Directory & source file manipulation

Table 1-5. WineDbg's directory & source file manipulation

show dir prints the list of dir:s where source files are looked for
dir <pathname> adds <pathname> to the list of dir:s where to look for source files
dir deletes the list of dir:s where to look for source files
symbolfile <pathname> loads external symbol definition
symbolfile <pathname> N loads external symbol definition (applying an offset of N to addresses)
list lists 10 source lines forwards from current position
list - lists 10 source lines backwards from current position
list N lists 10 source lines from line N in current file
list <path>:N lists 10 source lines from line N in file <path>
list <id> lists 10 source lines of function <id>
list * Nlists 10 source lines from address N

You can specify the end target (to change the 10 lines value) using the ','. For example:

Table 1-6. WineDbg's list command examples

list 123, 234 lists source lines from line 123 up to line 234 in current file
list foo.c:1, 56 lists source lines from line 1 up to 56 in file foo.c


1.7.6. Displaying

A display is an expression that's evaluated and printed after the execution of any winedbg command.

winedbg 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.

Table 1-7. WineDbg's displays

info display lists the active displays
display print the active displays' values (as done each time the debugger stops)
display <expr> adds a display for expression <expr>
display /fmt <expr> adds a display for expression <expr>. Printing evaluated <expr> is done using the given format (see print command for more on formats)
del display N, undisplay N deletes display #N


1.7.7. Disassembly

Table 1-8. WineDbg's dissassembly

disasdisassemble from current position
disas <expr> disassemble from address <expr>
disas <expr>,<expr> disassembles code between addresses specified by the two <expr>


1.7.8. Memory (reading, writing, typing)

Table 1-9. WineDbg's memory management

x <expr> examines memory at <expr> address
x /fmt <expr> examines memory at <expr> address using format /fmt
print <expr> prints the value of <expr> (possibly using its type)
print /fmt <expr> prints the value of <expr> (possibly using its type)
set <lval> = <expr> writes the value of <expr> in <lval>
whatis <expr> prints the C type of expression <expr>

/fmt is either /<letter> or /<count><letter> letter can be

san ASCII string
uan Unicode UTF16 string
iinstructions (disassemble)
x32 bit unsigned hexadecimal integer
d32 bit signed decimal integer
w16 bit unsigned hexadecimal integer
ccharacter (only printable 0x20-0x7f are actually printed)
b8 bit unsigned hexadecimal integer
gGUID


1.7.9. Information on Wine's internals

Table 1-10. WineDbg's Win32 objects management

info class lists all Windows' classes registered in Wine
info class <id> prints information on Windows's class <id>
info share lists all the dynamic libraries loaded in the debugged program (including .so files, NE and PE DLLs)
info share <N> prints information on module at address <N>
info regs prints the value of the CPU registers
info all-regs prints the value of the CPU and Floating Point registers
info segment <N> prints information on segment <N> (i386 only)
info segment lists all allocated segments (i386 only)
info stack prints the values on top of the stack
info map lists all virtual mappings used by the debugged program
info map <N> lists all virtual mappings used by the program of pid <N>
info wnd <N> prints information of Window of handle <N>
info wnd lists all the window hierarchy starting from the desktop window
info process lists all w-processes in Wine session
info threadlists all w-threads in Wine session
info exception lists the exception frames (starting from current stack frame)


1.7.10. Debug channels

It is possible to turn on and off debug messages as you are debugging using the set command. See Chapter 2 for more details on debug channels.

Table 1-11. WineDbg's debug channels' management

set + warn win turn on warn on 'win' channel
set + win turn on warn/fixme/err/trace on 'win' channel
set - win turn off warn/fixme/err/trace on 'win' channel
set - fixme turn off the 'fixme' class


1.8. Other debuggers

1.8.1. GDB mode

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 --gdb to winedbg command line. You'll end up on a GDB prompt. You'll have to use the GDB commands (not WineDbg's).

However, some limitation in GDB while debugging wine (see below) don't appear in this mode:

  • GDB will correctly present Win32 thread information and breakpoint behavior

  • Moreover, it also provides support for the Dwarf II debug format (which became the default format (instead of stabs) in gcc 3.1).

A few Wine extensions available through the monitor command.

Table 1-12. WineDbg's debug channels' management

monitor wndlists all window in the Wine session
monitor proc lists all processes in the Wine session
monitor mem displays memory mapping of debugged process


1.8.2. Graphical frontends to gdb

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.


1.8.2.1. DDD

Use the following steps, in this order:

  1. Start the Wine debugger with a command line like:

    	winedbg --gdb --no-start <name_of_exe_to_debug.exe>

  2. Start ddd

  3. 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).

  4. In the output of 1/, there's a line like

    	target remote localhost:32878
    copy that line and paste into ddd command pane (the one with the (gdb) prompt)

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


1.8.2.2. kdbg

Use the following steps, in this order:

  1. Start the Wine debugger with a command line like:

    	winedbg --gdb --no-start <name_of_exe_to_debug.exe>

  2. In the output of 1/, there's a line like

    	target remote localhost:32878
    Start kdbg with
    kdbg -r localhost:32878 wine
    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).

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


1.8.3. Using other Unix debuggers

You can also use other debuggers (like gdb), but you must be aware of a few items:

You need to attach the unix debugger to the correct unix process (representing the correct windows thread) (you can "guess" it from a ps fax for example: When running the emulator, usually the first two upids are for the Windows' application running the desktop, the first thread of the application is generally the third upid; when running a Winelib program, the first thread of the application is generally the first upid)

Note: If you plan to used gdb for a multi-threaded Wine application (native or Winelib), then gdb will be able to handle the multiple threads directly only if:

  • Wine is running on the pthread model (it won't work in the kthread one). See the Wine architecture documentation for further details.

  • gdb supports the multi-threading (you need gdb at least 5.0 for that).

In the unfortunate case (no direct thread support in gdb because one of the above conditions is false), you'll have to spawn a different gdb session for each Windows' thread you wish to debug (which means no synchronization for debugging purposes between the various threads).

Here's how to get info about the current execution status of a certain Wine process:

Change into your Wine source dir and enter:

$ gdb wine
        

Switch to another console and enter ps ax | grep wine to find all wine processes. Inside gdb, repeat for all Wine processes:

(gdb) attach PID
        

with PID being the process ID of one of the Wine processes. Use

(gdb) bt
        

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:

(gdb) n
        

or maybe even

(gdb) b SomeFunction
        

and

(gdb) c
        

to set a breakpoint at a certain function and continue up to that function. Finally you can enter

(gdb) detach
        

to detach from the Wine process.


1.8.4. Using other Windows debuggers

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.


1.8.5. Main differences between winedbg and regular Unix debuggers

Table 1-13. Debuggers comparison

WineDbggdb
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 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
WineDbg supports debug information from stabs (standard Unix format) and Microsoft's C, CodeView, .DBG GDB supports debug information from stabs (standard Unix format) and Dwarf II.

Chapter 2. Debug Logging

To better manage the large volume of debugging messages that Wine can generate, we divide the messages on a component basis, and classify them based on the severity of the reported problem. Therefore a message belongs to a channel and a class respectively.

This section will describe the debugging classes, how you can create a new debugging channel, what the debugging API is, and how you can control the debugging output. A picture is worth a thousand words, so here are a few examples of the debugging API in action:

ERR("lock_count == 0 ... please report\n");
FIXME("Unsupported RTL style!\n");
WARN(": file seems to be truncated!\n");
TRACE("[%p]: new horz extent = %d\n", hwnd, extent );
MESSAGE( "Could not create graphics driver '%s'\n", buffer );
	  


2.1. Debugging classes

A debugging class categorizes a message based on the severity of the reported problem. There is a fixed set of classes, and you must carefully choose the appropriate one for your messages. There are five classes of messages:

FIXME

Messages in this class are meant to signal unimplemented features, known bugs, etc. They serve as a constant and active reminder of what needs to be done.

ERR

Messages in this class indicate serious errors in Wine, such as as conditions that should never happen by design.

WARN

These are warning messages. You should report a warning when something unwanted happens, and the function cannot deal with the condition. This is seldomly used since proper functions can usually report failures back to the caller. Think twice before making the message a warning.

TRACE

These are detailed debugging messages that are mainly useful to debug a component. These are turned off unless explicitly enabled.

MESSAGE

There messages are intended for the end user. They do not belong to any channel. As with warnings, you will seldomly need to output such messages.


2.2. Debugging channels

Each component is assigned a debugging channel. The identifier of the channel must be a valid C identifier (reserved word like int or static are premitted). To use a new channel, simply use it in your code. It will be picked up automatically by the build process.

Typically, a file contains code pertaining to only one component, and as such, there is only one channel to output to. You can declare a default chanel for the file using the WINE_DEFAULT_DEBUG_CHANNEL() macro:

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(xxx);
...

    FIXME("some unimplemented feature", ...);
...
    if (zero != 0)
        ERR("This should never be non-null: %d", zero);
...
        

In rare situations there is a need to output to more than one debug channel per file. In such cases, you need to declare all the additional channels at the top of the file, and use the _-version of the debugging macros:

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(xxx);
WINE_DECLARE_DEBUG_CHANNEL(yyy);
WINE_DECLARE_DEBUG_CHANNEL(zzz);
...

    FIXME("this one goes to xxx channel");
...
    FIXME_(yyy)("Some other msg for the yyy channel");
...
    WARN_(zzz)("And yet another msg on another channel!");
...
        


2.3. Are we debugging?

To test whether the debugging channel xxx is enabled, use the TRACE_ON, WARN_ON, FIXME_ON, or ERR_ON macros. For example:

if(TRACE_ON(atom)){
    ...blah...
}
        
You should normally need to test only if TRACE_ON, all the others are very seldomly used. With careful coding, you can avoid the use of these macros, which is generally desired.


2.4. Helper functions

Resource identifiers can be either strings or numbers. To make life a bit easier for outputting these beasts (and to help you avoid the need to build the message in memory), I introduced a new function called debugres.

The function is defined in wine/debug.h and has the following prototype:

LPSTR debugres(const void *id);
        

It takes a pointer to the resource id and returns a nicely formatted string of the identifier (which can be a string or a number, depending on the value of the high word). Numbers are formatted as such:

#xxxx
        

while strings as:

'some-string'
        

Simply use it in your code like this:

#include "wine/debug.h"

...

   TRACE("resource is %s", debugres(myresource));
        

Many times strings need to be massaged before output: they may be NULL, contain control characters, or they may be too long. Similarly, Unicode strings need to be converted to ASCII for usage with the debugging API. For all this, you can use the debugstr_[aw]n? familly of functions:

HANDLE32 WINAPI YourFunc(LPCSTR s)
{
    FIXME("(%s): stub\n", debugstr_a(s));
}
          


2.5. Controlling the debugging output

It is possible to turn on and off debugging output from within the debugger using the set command. Please see the WineDbg Command Reference section (Section 1.7.10) for how to do this.

You can do the same using the task manager (taskmgr) and selecting your application in the application list. Right clicking on the application, and selecting the debug option in the popup menu, will let you select the modifications you want on the debug channels.

Another way to conditionally log debug output (e.g. in case of very large installers which may create gigabytes of log output) is to create a pipe:

	$ mknod /tmp/debug_pipe p
	

and then to run wine like that:

	$ WINEDEBUG=+relay,+snoop wine setup.exe &>/tmp/debug_pipe
	

Since the pipe is initially blocking (and thus wine as a whole), you have to activate it by doing:

	$ cat /tmp/debug_pipe
	

(press Ctrl-C to stop pasting the pipe content)

Once you are about to approach the problematic part of the program, you just do:

	$ cat /tmp/debug_pipe >/tmp/wine.log
	

to capture specifically the part that interests you from the pipe without wasting excessive amounts of HDD space and slowing down installation considerably.

The WINEDEBUG environment variable controls the output of the debug messages. It has the following syntax: WINEDEBUG= [yyy]#xxx[,[yyy1]#xxx1]*

  • where # is either + or -

  • when the optional class argument (yyy) is not present, then the statement will enable(+)/disable(-) all messages for the given channel (xxx) on all classes. For example:

    WINEDEBUG=+reg,-file
                

    enables all messages on the reg channel and disables all messages on the file channel.

  • when the optional class argument (yyy) is present, then the statement will enable (+)/disable(-) messages for the given channel (xxx) only on the given class. For example:

    WINEDEBUG=trace+reg,warn-file
                

    enables trace messages on the reg channel and disables warning messages on the file channel.

  • also, the pseudo-channel all is also supported and it has the intuitive semantics:

        WINEDEBUG=+all      -- enables all debug messages
        WINEDEBUG=-all      -- disables all debug messages
        WINEDEBUG=yyy+all   -- enables debug messages for class yyy on all
                               channels.
        WINEDEBUG=yyy-all   -- disables debug messages for class yyy on all
                               channels.
                

    So, for example:

        WINEDEBUG=warn-all  -- disables all warning messages.
                

Also, note that at the moment:

  • the FIXME and ERR classes are enabled by default

  • the TRACE and WARN classes are disabled by default


2.6. Compiling Out Debugging Messages

To compile out the debugging messages, provide configure with the following options:

    --disable-debug      -- turns off TRACE, WARN, and FIXME (and DUMP).
    --disable-trace      -- turns off TRACE only.
        

This will result in an executable that, when stripped, is about 15%-20% smaller. Note, however, that you will not be able to effectively debug Wine without these messages.

This feature has not been extensively tested--it may subtly break some things.


2.7. A Few Notes on Style

This new scheme makes certain things more consistent but there is still room for improvement by using a common style of debug messages. Before I continue, let me note that the output format is the following:

yyy:xxx:fff <message>

where:
  yyy = the class (fixme, err, warn, trace)
  xxx = the channel (atom, win, font, etc)
  fff = the function name
        

these fields are output automatically. All you have to provide is the <message> part.

So here are some ideas:

  • do not include the name of the function: it is included automatically

  • if you want to output the parameters of the function, do it as the first thing and include them in parentheses, like this:

    TRACE("(%d, %p, ...)\n", par1, par2, ...);
                  

  • if you want to name a parameter, use = :

    TRACE("(fd=%d, file=%s): stub\n", fd, name);
                  

  • for stubs, you should output a FIXME message. I suggest this style:

    FIXME("(%x, %d, ...): stub\n", par1, par2, ...);
                  

  • try to output one line per message. That is, the format string should contain only one \n and it should always appear at the end of the string.

  • if the output string needs to be dynamically constructed, render it in memory before outputting it:

    char buffer[128] = "";
    
    if (flags & FLAG_A) strcat(buffer, "FLAG_A ");
    if (flags & FLAG_B) strcat(buffer, "FLAG_B ");
    if (flags & FLAG_C) strcat(buffer, "FLAG_C ");
    TRACE("flags = %s\n", buffer);
                  
    Most of the time however, it is better to create a helper function that renders to a temporary buffer:
    static const char *dbgstr_flags(int flags)
    {
        char buffer[128] = "";
    
        if (flags & FLAG_A) strcat(buffer, "FLAG_A ");
        if (flags & FLAG_B) strcat(buffer, "FLAG_B ");
        if (flags & FLAG_C) strcat(buffer, "FLAG_C ");
        return wine_dbg_sprintf("flags = %s\n\n", buffer);
    }
    
    ...
    
    TRACE("flags = %s\n", dbgstr_flags(flags));
                  


Chapter 3. Other debugging techniques

3.1. Doing A Hardware Trace

The primary reason to do this is to reverse engineer a hardware device for which you don't have documentation, but can get to work under Wine.

This lot is aimed at parallel port devices, and in particular parallel port scanners which are now so cheap they are virtually being given away. The problem is that few manufactures will release any programming information which prevents drivers being written for Sane, and the traditional technique of using DOSemu to produce the traces does not work as the scanners invariably only have drivers for Windows.

Presuming that you have compiled and installed wine the first thing to do is to enable direct hardware access to your parallel port. To do this edit config (usually in ~/.wine/) and in the ports section add the following two lines

read=0x378,0x379,0x37a,0x37c,0x77a
write=0x378,x379,0x37a,0x37c,0x77a
      

This adds the necessary access required for SPP/PS2/EPP/ECP parallel port on LPT1. You will need to adjust these number accordingly if your parallel port is on LPT2 or LPT0.

When starting wine use the following command line, where XXXX is the program you need to run in order to access your scanner, and YYYY is the file your trace will be stored in:

WINEDEBUG=+io wine XXXX 2> >(sed 's/^[^:]*:io:[^ ]* //' > YYYY)
      

You will need large amounts of hard disk space (read hundreds of megabytes if you do a full page scan), and for reasonable performance a really fast processor and lots of RAM.

You will need to postprocess the output into a more manageable format, using the shrink program. First you need to compile the source (which is located at the end of this section):

cc shrink.c -o shrink
      

Use the shrink program to reduce the physical size of the raw log as follows:

cat log | shrink > log2
      

The trace has the basic form of

XXXX > YY @ ZZZZ:ZZZZ
      

where XXXX is the port in hexadecimal being accessed, YY is the data written (or read) from the port, and ZZZZ:ZZZZ is the address in memory of the instruction that accessed the port. The direction of the arrow indicates whether the data was written or read from the port.

> data was written to the port
< data was read from the port
      

My basic tip for interpreting these logs is to pay close attention to the addresses of the IO instructions. Their grouping and sometimes proximity should reveal the presence of subroutines in the driver. By studying the different versions you should be able to work them out. For example consider the following section of trace from my UMAX Astra 600P

0x378 > 55 @ 0297:01ec
0x37a > 05 @ 0297:01f5
0x379 < 8f @ 0297:01fa
0x37a > 04 @ 0297:0211
0x378 > aa @ 0297:01ec
0x37a > 05 @ 0297:01f5
0x379 < 8f @ 0297:01fa
0x37a > 04 @ 0297:0211
0x378 > 00 @ 0297:01ec
0x37a > 05 @ 0297:01f5
0x379 < 8f @ 0297:01fa
0x37a > 04 @ 0297:0211
0x378 > 00 @ 0297:01ec
0x37a > 05 @ 0297:01f5
0x379 < 8f @ 0297:01fa
0x37a > 04 @ 0297:0211
0x378 > 00 @ 0297:01ec
0x37a > 05 @ 0297:01f5
0x379 < 8f @ 0297:01fa
0x37a > 04 @ 0297:0211
0x378 > 00 @ 0297:01ec
0x37a > 05 @ 0297:01f5
0x379 < 8f @ 0297:01fa
0x37a > 04 @ 0297:0211
      

As you can see there is a repeating structure starting at address 0297:01ec that consists of four io accesses on the parallel port. Looking at it the first io access writes a changing byte to the data port the second always writes the byte 0x05 to the control port, then a value which always seems to 0x8f is read from the status port at which point a byte 0x04 is written to the control port. By studying this and other sections of the trace we can write a C routine that emulates this, shown below with some macros to make reading/writing on the parallel port easier to read.

#define r_dtr(x)        inb(x)
#define r_str(x)        inb(x+1)
#define r_ctr(x)        inb(x+2)
#define w_dtr(x,y)      outb(y, x)
#define w_str(x,y)      outb(y, x+1)
#define w_ctr(x,y)      outb(y, x+2)

/* Seems to be sending a command byte to the scanner */
int udpp_put(int udpp_base, unsigned char command)
{
    int loop, value;

    w_dtr(udpp_base, command);
    w_ctr(udpp_base, 0x05);

    for (loop=0; loop < 10; loop++)
        if ((value = r_str(udpp_base)) & 0x80) 
	{
            w_ctr(udpp_base, 0x04);
            return value & 0xf8;
        }

    return (value & 0xf8) | 0x01;
}
      

For the UMAX Astra 600P only seven such routines exist (well 14 really, seven for SPP and seven for EPP). Whether you choose to disassemble the driver at this point to verify the routines is your own choice. If you do, the address from the trace should help in locating them in the disassembly.

You will probably then find it useful to write a script/perl/C program to analyse the logfile and decode them futher as this can reveal higher level grouping of the low level routines. For example from the logs from my UMAX Astra 600P when decoded further reveal (this is a small snippet)

start:
put: 55 8f
put: aa 8f
put: 00 8f
put: 00 8f
put: 00 8f
put: c2 8f
wait: ff
get: af,87
wait: ff
get: af,87
end: cc
start:
put: 55 8f
put: aa 8f
put: 00 8f
put: 03 8f
put: 05 8f
put: 84 8f
wait: ff
      

From this it is easy to see that put routine is often grouped together in five successive calls sending information to the scanner. Once these are understood it should be possible to process the logs further to show the higher level routines in an easy to see format. Once the highest level format that you can derive from this process is understood, you then need to produce a series of scans varying only one parameter between them, so you can discover how to set the various parameters for the scanner.

The following is the shrink.c program:

/* Copyright David Campbell <campbell@torque.net> */
#include <stdio.h>
#include <string.h>

int main (void)
{
    char buff[256], lastline[256] = "";
    int count = 0;

    while (!feof (stdin))
    {
        fgets (buff, sizeof (buff), stdin);
        if (strcmp (buff, lastline))
	{
	    if (count > 1)
	        printf ("# Last line repeated %i times #\n", count);
	    printf ("%s", buff);
	    strcpy (lastline, buff);
	    count = 1;
	}
        else count++;
    }
    return 0;
}
      


3.2. Understanding undocumented APIs

Some background: On the i386 class of machines, stack entries are usually dword (4 bytes) in size, little-endian. The stack grows downward in memory. The stack pointer, maintained in the esp register, points to the last valid entry; thus, the operation of pushing a value onto the stack involves decrementing esp and then moving the value into the memory pointed to by esp (i.e., push p in assembly resembles *(--esp) = p; in C). Removing (popping) values off the stack is the reverse (i.e., pop p corresponds to p = *(esp++); in C).

In the stdcall calling convention, arguments are pushed onto the stack right-to-left. For example, the C call myfunction(40, 20, 70, 30); is expressed in Intel assembly as:

    push 30
    push 70
    push 20
    push 40
    call myfunction
	
The called function is responsible for removing the arguments off the stack. Thus, before the call to myfunction, the stack would look like:
             [local variable or temporary]
             [local variable or temporary]
              30
              70
              20
    esp ->    40
	
After the call returns, it should look like:
             [local variable or temporary]
    esp ->   [local variable or temporary]
	

To restore the stack to this state, the called function must know how many arguments to remove (which is the number of arguments it takes). This is a problem if the function is undocumented.

One way to attempt to document the number of arguments each function takes is to create a wrapper around that function that detects the stack offset. Essentially, each wrapper assumes that the function will take a large number of arguments. The wrapper copies each of these arguments into its stack, calls the actual function, and then calculates the number of arguments by checking esp before and after the call.

The main problem with this scheme is that the function must actually be called from another program. Many of these functions are seldom used. An attempt was made to aggressively query each function in a given library (ntdll.dll) by passing 64 arguments, all 0, to each function. Unfortunately, Windows NT quickly goes to a blue screen of death, even if the program is run from a non-administrator account.

Another method that has been much more successful is to attempt to figure out how many arguments each function is removing from the stack. This instruction, ret hhll (where hhll is the number of bytes to remove, i.e. the number of arguments times 4), contains the bytes 0xc2 ll hh in memory. It is a reasonable assumption that few, if any, functions take more than 16 arguments; therefore, simply searching for hh == 0 && ll < 0x40 starting from the address of a function yields the correct number of arguments most of the time.

Of course, this is not without errors. ret 00ll is not the only instruction that can have the byte sequence 0xc2 ll 0x0; for example, push 0x000040c2 has the byte sequence 0x68 0xc2 0x40 0x0 0x0, which matches the above. Properly, the utility should look for this sequence only on an instruction boundary; unfortunately, finding instruction boundaries on an i386 requires implementing a full disassembler -- quite a daunting task. Besides, the probability of having such a byte sequence that is not the actual return instruction is fairly low.

Much more troublesome is the non-linear flow of a function. For example, consider the following two functions:

    somefunction1:
        jmp  somefunction1_impl

    somefunction2:
        ret  0004

    somefunction1_impl:
        ret  0008
	
In this case, we would incorrectly detect both somefunction1 and somefunction2 as taking only a single argument, whereas somefunction1 really takes two arguments.

With these limitations in mind, it is possible to implement more stubs in Wine and, eventually, the functions themselves.


3.3. How to do regression testing using Git

A problem that can happen sometimes is 'it used to work before, now it doesn't anymore...'. Here is a step by step procedure to try to pinpoint when the problem occurred. This is NOT for casual users.

  1. Clone the "Git" repository from winehq. It's more than 90Mb, so you it may take some time with a slow Internet connection.

  2. If you found that something broke between wine-20041019 and wine-20050930 (these are [WWW] release tags). To start regression testing we run:

    git bisect start
    git bisect good wine-20041019
    git bisect bad wine-20050930
    	    

    If you have exact date/time instead of a release you will need to use sha1 IDs from git log.

  3. Having told Git when things were working and when they broke, it will automatically "position" your source tree to the middle. So all you need to do is build the source:

    ./configure && make clean && make depend && make
    ./wine 'c:\test.exe'
    	    
    If the version of Wine that Git picked still has the bug, run:
    git bisect bad
    	    
    and if it does not, run:
    git bisect good
    	    
    When you run this command, Git will checkout a new version of Wine for you to rebuild, so repeat this step again. When the regression has been isolated, git will inform you.

    To find out what's left to test, try:

    git bisect visualize.
    	    

  4. When you have found the bad patch and want to go back to the current HEAD run:

    git bisect reset
    	    

  5. If you find the patch that is the cause of the problem, you have almost won; report about it to Wine Bugzilla or subscribe to wine-devel and post it there. There is a chance that the author will jump in to suggest a fix; or there is always the possibility to look hard at the patch until it is coerced to reveal where is the bug :-)


3.4. Which code has been tested?

Deciding what code should be tested next can be a difficult decision. And in any given project, there is always code that isn't tested where bugs could be lurking. This section goes over how to identify these sections using a tool called gcov.

To use gcov on wine, do the following:

  1. In order to activate code coverage in the wine source code, when running make set CFLAGS like so make CFLAGS="-fprofile-arcs -ftest-coverage". Note that this can be done at any directory level. Since compile and run time are significantly increased by these flags, you may want to only use these flags inside a given dll directory.

  2. Run any application or test suite.

  3. Run gcov on the file which you would like to know more about code coverage.

The following is an example situation when using gcov to determine the coverage of a file could be helpful. We'll use the dlls/lzexpand/lzexpand_main.c. file. At one time the code in this file was not fully tested (as it may still be). For example at the time of this writing, the function LZOpenFileA had the following lines in it:

if ((mode&~0x70)!=OF_READ)
        return fd;
if (fd==HFILE_ERROR)
        return HFILE_ERROR;
cfd=LZInit(fd);
if ((INT)cfd <= 0) return fd;
return cfd;
        
Currently there are a few tests written to test this function; however, these tests don't check that everything is correct. For instance, HFILE_ERROR may be the wrong error code to return. Using gcov and directed tests, we can validate the correctness of this line of code. First, we see what has been tested already by running gcov on the file. To do this, do the following:
git clone git://source.winehq.org/git/wine.git wine
mkdir build
cd build
../wine/configure
make depend && make CFLAGS="-fprofile-arcs -ftest-coverage"
cd dlls/lxexpand/tests
make test
cd ..
gcov ../../../wine/dlls/lzexpand/lzexpand_main.c
  0.00% of 3 lines executed in file ../../../wine/include/wine/unicode.h
  Creating unicode.h.gcov.
  0.00% of 4 lines executed in file /usr/include/ctype.h
  Creating ctype.h.gcov.
  0.00% of 6 lines executed in file /usr/include/bits/string2.h
  Creating string2.h.gcov.
  100.00% of 3 lines executed in file ../../../wine/include/winbase.h
  Creating winbase.h.gcov.
  50.83% of 240 lines executed in file ../../../wine/dlls/lzexpand/lzexpand_main.c
  Creating lzexpand_main.c.gcov.
less lzexpand_main.c.gcov
        
Note that there is more output, but only output of gcov is shown. The output file lzexpand_main.c.gcov looks like this.
        9:  545:        if ((mode&~0x70)!=OF_READ)
        6:  546:                return fd;
        3:  547:        if (fd==HFILE_ERROR)
    #####:  548:                return HFILE_ERROR;
        3:  549:        cfd=LZInit(fd);
        3:  550:        if ((INT)cfd <= 0) return fd;
        3:  551:        return cfd;
        
gcov output consists of three components: the number of times a line was run, the line number, and the actual text of the line. Note: If a line is optimized out by the compiler, it will appear as if it was never run. The line of code which returns HFILE_ERROR is never executed (and it is highly unlikely that it is optimized out), so we don't know if it is correct. In order to validate this line, there are two parts of this process. First we must write the test. Please see Chapter 5 to learn more about writing tests. We insert the following lines into a test case:
INT file;

/* Check for nonexistent file. */
file = LZOpenFile("badfilename_", &test, OF_READ);
ok(file == LZERROR_BADINHANDLE, 
   "LZOpenFile succeeded on nonexistent file\n");
LZClose(file);
       
Once we add in this test case, we now want to know if the line in question is run by this test and works as expected. You should be in the same directory as you left off in the previous command example. The only difference is that we have to remove the *.da files in order to start the count over (if we leave the files than the number of times the line is run is just added, e.g. line 545 below would be run 19 times) and we remove the *.gcov files because they are out of date and need to be recreated.

rm *.da *.gcov
cd tests
make
make test
cd ..
gcov ../../../wine/dlls/lzexpand/lzexpand_main.c
  0.00% of 3 lines executed in file ../../../wine/include/wine/unicode.h
  Creating unicode.h.gcov.
  0.00% of 4 lines executed in file /usr/include/ctype.h
  Creating ctype.h.gcov.
  0.00% of 6 lines executed in file /usr/include/bits/string2.h
  Creating string2.h.gcov.
  100.00% of 3 lines executed in file ../../../wine/include/winbase.h
  Creating winbase.h.gcov.
  51.67% of 240 lines executed in file ../../../wine/dlls/lzexpand/lzexpand_main.c
  Creating lzexpand_main.c.gcov.
less lzexpand_main.c.gcov
      

Note that there is more output, but only output of gcov is shown. The output file lzexpand_main.c.gcov looks like this.

       10:  545:        if ((mode&~0x70)!=OF_READ)
        6:  546:                return fd;
        4:  547:        if (fd==HFILE_ERROR)
        1:  548:                return HFILE_ERROR;
        3:  549:        cfd=LZInit(fd);
        3:  550:        if ((INT)cfd <= 0) return fd;
        3:  551:        return cfd;
      

Based on gcov, we now know that HFILE_ERROR is returned once. And since all of our other tests have remain unchanged, we can assume that the one time it is returned is to satisfy the one case we added where we check for it. Thus we have validated a line of code. While this is a cursory example, it demostrates the potential usefulness of this tool.

For a further in depth description of gcov, the official gcc compiler suite page for gcov is http://gcc.gnu.org/onlinedocs/gcc-3.2.3/gcc/Gcov.html. There is also an excellent article written by Steve Best for Linux Magazine which describes and illustrates this process very well at http://www.linux-mag.com/2003-07/compile_01.html.


Chapter 4. Coding Practice

This chapter describes the relevant coding practices in Wine, that you should be aware of before doing any serious development in Wine.


4.1. Patch Format

Patches are submitted via email to the Wine patches mailing list, . Your patch should include:

  • A meaningful subject (very short description of patch)

  • A long (paragraph) description of what was wrong and what is now better. (recommended)

  • A change log entry (short description of what was changed).

  • The patch in "Git" format

To generate a patch using Git, first commit it to your local tree.

Each file that you change needs to be updated with git update . If you are adding or removing a file, use git update --add or git update --remove respectively. After updating the index, commit the change using git commit . The commit message will be sent with your patch, and recored in the ChangeLog.

After committing the patch, you can extract it using git format-patch and send it to wine-patches using git imap-send or simply attaching it to you mail manually.


4.2. Some notes about style

There are a few conventions about coding style that have been adopted over the years of development. The rational for these "rules" is explained for each one.

  • No HTML mail, since patches should be in-lined and HTML turns the patch into garbage. Also it is considered bad etiquette as it uglifies the message, and is not viewable by many of the subscribers.

  • Only one change set per patch. Patches should address only one bug/problem at a time. If a lot of changes need to be made then it is preferred to break it into a series of patches. This makes it easier to find regressions.

  • Tabs are not forbidden but discouraged. A tab is defined as 8 characters and the usual amount of indentation is 4 characters.

  • C++ style comments are discouraged since some compilers choke on them.

  • Commenting out a block of code is usually done by enclosing it in #if 0 ... #endif Statements. For example.

     
    /* note about reason for commenting block */
    #if 0
    code
    code /* comments */
    code
    #endif
    	  

    The reason for using this method is that it does not require that you edit comments that may be inside the block of code.

  • Patches should be in-lined (if you can configure your email client to not wrap lines), or attached as plain text attachments so they can be read inline. This may mean some more work for you. However it allows others to review your patch easily and decreases the chances of it being overlooked or forgotten.

  • Code is usually limited to 80 columns. This helps prevent mailers mangling patches by line wrap. Also it generally makes code easier to read.

  • If the patch fixes a bug in Bugzilla please provide a link to the bug in the comments of the patch. This will make it easier for the maintainers of Bugzilla.


4.2.1. Inline attachments with Outlook Express

Outlook Express is notorious for mangling attachments. Giving the patch a .txt extension and attaching will solve the problem for most mailers including Outlook. Also, there is a way to enable Outlook Express to send .diff attachments.

You need the following two things to make it work.

  1. Make sure that .diff files have \r\n line ends, because if OE detects that there is no \r\n line endings it switches to quoted-printable format attachments.

  2. Using regedit add key "Content Type" with value "text/plain" to the .diff extension under HKEY_CLASSES_ROOT (same as for .txt extension). This tells OE to use Content-Type: text/plain instead of application/octet-stream.

Item #1 is important. After you hit the "Send" button, go to "Outbox" and using "Properties" verify the message source to make sure that the mail has the correct format. You might want to send several test emails to yourself too.


4.2.2. Alexandre's Bottom Line

"The basic rules are: no attachments, no MIME crap, no line wrapping, a single patch per mail. Basically if I can't do "cat raw_mail | patch -p0" it's in the wrong format."


4.3. Quality Assurance

(Or, "How do I get Alexandre to apply my patch quickly so I can build on it and it will not go stale?")