I Introduction ============== I.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 process (under Linux using the clone syscall), 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 (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). I.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 the the Windows' debugging API (the part in KERNEL32, not the one in IMAGEHLP.DLL), and allows any program (emulated or WineLib) using that API to debug a W-process. WineDbg is a WineLib application making use of this API to allow debugging both any Wine or WineLib applications as well as Wine itself (kernel and all DLLs). II WineDbg's modes of invocation ================================ II.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 than for wine: winedbg telnet.exe winedbg "hl.exe -windowed" II.2 Attaching -------------- WineDbg can also 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 'walk process' command, and then, with the attach command, pick up the wpid of the W-process you want to debug. This is (for now) a neat feature for the following reasons: * debug an already started application II.3 On exception ----------------- When something goes wrong, Windows track this as an exception. Exceptions exist for segmentation violation, stack overflow, division by zero... 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 know 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. II.4 Quitting ------------- Unfortunately, Windows' don't provide a detach kind of API, meaning that once you started debugging a process, you must do so until the process dies. Killing (or stopping/aborting) the debugger will also kill the debugged process. This will be true for any Windows' debugging API compliant debugger, starting with WineDbg. III Configuration ================= III.1 Registry configuration ---------------------------- The Windows' debugging API uses a registry entry to know with debugger to invoke when an unhandled exception occurs (see II.3 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). + 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"="/usr/local/bin/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 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. III.2 WineDbg configuration --------------------------- WineDbg can be configured thru a number of options. Those options are stored in the registry, on a per user basis. The key is (in *my* registry) [eric\\Software\\Wine\\WineDbg] Those options can be read/written while inside WineDbg, as part of the debugger expressions. To refer to one of this option, 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: III.2.1 Controling when the debugger is entered 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. BreakOnDllLoad When set to TRUE, allows the debugger to be entered when a new DLL is loaded into the system. FALSE by default. 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 decides 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 thru 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). III.2.1 Output handling ConChannelMask mask of active debugger output channels on console StdChannelMask mask of active debugger output channels on stderr UseXTerm set to TRUE if the debugger uses its own xterm window for console input/output set to FALSE is the debugger uses the current Unix console for input/output Those last 3 variables are jointly used in two generic ways: 1/ default ConChannelMask = DBG_CHN_MESG (1) StdChannelMask = 0 UseXTerm = 1 In this case, all input/output goes into a specific xterm window (but all debug messages TRACE/WARN... still goes to tty where wine is run from). 2/ to have all input/output go into the tty where Wine was started from (to be used in a X11-free environment) ConChannelMask = 0 StdChannelMask = DBG_CHN_MESG (1) UseXTerm = 1 Those variables also allow, for example for debugging purposes, to use: ConChannelMask = 0xfff StdChannelMask = 0xfff UseXTerm = 1 This allows to redirect all WineDbg output to both tty Wine was started from, and xterm debugging window. If Wine (or WineDbg) was started with a redirection of stdout and/or stderr to a file (with for example >& shell redirect command), you'll get in that file both outputs. It may be interesting to look in the relay trace for specific values which the process segv:ed on. III.2.3 Context information ThreadId ID of the W-thread currently examined by the debugger ProcessId ID of the W-thread currently examined by the debugger All CPU registers are also available The ThreadId and ProcessId variables can be handy to set conditional breakpoints on a given thread or process. IV WineDbg commands =================== IV.1 Misc --------- abort aborts the debugger quit exits the debugger attach N attach to a W-process (N is its ID). IDs can be obtained thru walk process command help prints some help on the commands help info prints some help on info commands mode 16 switch to 16 bit mode mode 32 switch to 32 bit mode IV.2 Flow control ----------------- cont continue execution until next breakpoint or exception. pass pass the exception event up to the filter chain. step continue execution until next C line of code (enters function call) next continue execution until next C line of code (doesn't enter function call) stepi execute next assembly instruction (enters function call) nexti execute next assembly instruction (doesn't enter function call) finish do nexti commands until current function is exited cont, step, next, stepi, nexti can be postfixed by a number (N), meaning that the command must be executed N times. IV.3 Breakpoints, watch points ------------------------------ enable N enables (break|watch)point #N disable N disables (break|watch)point #N delete N deletes (break|watch)point #N cond N removes any a existing condition to (break|watch)point N cond N adds condition to (break|watch)point N. will be evaluated each time the breakpoint is hit. If the result is a zero value, the breakpoint isn't triggered break * N adds a breakpoint at address N break adds a breakpoint at the address of symbol break N adds a breakpoint at the address of symbol (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 adds a watch command (on write) at the address of symbol info break lists all (break|watch)points (with state) IV.4 Stack manipulation ----------------------- bt print calling stack of current thread 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 info local prints information on local variables for current function IV.5 Directory & source file manipulation ----------------------------------------- show dir dir dir symbolfile list lists 10 source lines from current position list - lists 10 source lines before current position list N lists 10 source lines from line N in current file list :N lists 10 source lines from line N in file list lists 10 source lines of function list * N lists 10 source lines from address N You can specify the end target (to change the 10 lines value) using the ','. For example: 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 IV.6 Displaying --------------- a display is an expression that's evaluated and printed after the execution of any WineDbg command display lists the active displays info display (same as above command) display adds a display for expression display /fmt adds a display for expression . Printing evaluated is done using the given format (see print command for more on formats) del display N deletes display #N undisplay N (same as del display) IV.7 Disassembly ---------------- disas disassemble from current position disas disassemble from address disas ,disassembles code between addresses specified by the two IV.8 Information on Wine's internals ------------------------------------ info class prints information on Windows's class walk class lists all Windows' class registered in Wine info share lists all the dynamic libraries loaded the debugged program (including .so files, NE and PE DLLs) info module N prints information on module of handle N walk module lists all modules loaded by debugged program info queue N prints information on Wine's queue N walk queue lists all queues allocated in Wine info regs prints the value of CPU register info segment N prints information on segment N info segment lists all allocated segments info stack prints the values on top of the stack info map lists all virtual mappings used by the debugged program info wnd N prints information of Window of handle N walk wnd lists all the window hierarchy starting from the desktop window walk wnd N lists all the window hierarchy starting from the window of handle N walk process lists all w-processes in Wine session walk thread lists all w-threads in Wine session walk modref (no longer avail) IV.9 Memory (reading, writing, typing) x examines memory at address x /fmt examines memory at address using format /fmt print prints the value of (possibly using its type) print /fmt prints the value of (possibly using its type) set = writes the value of in whatis prints the C type of expression /fmt is either / or / letter can be s => an ASCII string u => an Unicode UTF16 string i => instructions (disassemble) x => 32 bit unsigned hexadecimal integer d => 32 bit signed decimal integer w => 16 bit unsigned hexadecimal integer c => character (only printable 0x20-0x7f are actually printed) b => 8 bit unsigned hexadecimal integer V Other debuggers ================= V.1 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: even if latest gdb 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 gdb session for each Windows' thread you wish to debug. V.2 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. V.3 Main differences between winedbg and regular Unix debuggers --------------------------------------------------------------- +----------------------------------+---------------------------------+ | WineDbg | gdb | +----------------------------------+---------------------------------+ |WineDbg debugs a Windows' process:|gdb debugs a Windows' thread: | |+ the various threads will be |+ a separate gdb session is | | handled by the same WineDbg | needed for each thread of | | session | Windows' process | |+ a breakpoint will be triggered |+ a breakpoint will be triggered | | for any thread of the w-process | only for the w-thread debugged | +----------------------------------+---------------------------------+ |WineDbg supports debug information|gdb supports debug information | |from: |from: | |+ stabs (standard Unix format) |+ stabs (standard Unix format) | |+ Microsoft's C, CodeView, .DBG | | +----------------------------------+---------------------------------+ VI Limitations ============== + 16 bit processes are not supported (but calls to 16 bit code in 32 bit applications is). + there are reports of debugger's freeze when loading large PDB files Last updated: 6/14/2000 by ericP