| 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 | |
| <registers> 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 <expr> adds condition <expr> to (break|watch)point N. <expr> | |
| will be evaluated each time the breakpoint is hit. If | |
| the result is a zero value, the breakpoint isn't | |
| triggered | |
| break * N adds a breakpoint at address N | |
| break <id> adds a breakpoint at the address of symbol <id> | |
| break <id> N adds a breakpoint at the address of symbol <id> (N ?) | |
| break N adds a breakpoint at line N of current source file | |
| break adds a breakpoint at current $pc address | |
| watch * N adds a watch command (on write) at address N (on 4 bytes) | |
| watch <id> adds a watch command (on write) at the address of | |
| symbol <id> | |
| info break lists all (break|watch)points (with state) | |
| 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 <pathname> | |
| dir | |
| symbolfile <pathname> | |
| list lists 10 source lines from current position | |
| list - lists 10 source lines before current position | |
| list N lists 10 source lines from line N in current file | |
| list <path>:N lists 10 source lines from line N in file <path> | |
| list <id> lists 10 source lines of function <id> | |
| list * N lists 10 source lines from address N | |
| 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 <expr> adds a display for expression <expr> | |
| display /fmt <expr> adds a display for expression <expr>. Printing | |
| evaluated <expr> is done using the given format (see | |
| print command for more on formats) | |
| del display N deletes display #N | |
| undisplay N (same as del display) | |
| IV.7 Disassembly | |
| ---------------- | |
| disas disassemble from current position | |
| disas <expr> disassemble from address <expr> | |
| disas <expr>,<expr>disassembles code between addresses specified by | |
| the two <expr> | |
| IV.8 Information on Wine's internals | |
| ------------------------------------ | |
| info class <id> prints information on Windows's class <id> | |
| walk class lists all Windows' class registered in Wine | |
| info share lists all the dynamic libraries loaded the debugged | |
| program (including .so files, NE and PE DLLs) | |
| info module N prints information on module of handle N | |
| walk module lists all modules loaded by debugged program | |
| info queue N prints information on Wine's queue N | |
| walk queue lists all queues allocated in Wine | |
| info regs prints the value of CPU register | |
| info segment N prints information on segment N | |
| info segment lists all allocated segments | |
| info stack prints the values on top of the stack | |
| info map lists all virtual mappings used by the debugged | |
| program | |
| info wnd N prints information of Window of handle N | |
| walk wnd lists all the window hierarchy starting from the | |
| desktop window | |
| walk wnd N lists all the window hierarchy starting from the | |
| window of handle N | |
| walk process lists all w-processes in Wine session | |
| walk thread lists all w-threads in Wine session | |
| walk modref (no longer avail) | |
| IV.9 Memory (reading, writing, typing) | |
| 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 | |
| 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 |