| This file describes where to start debugging Wine and how to write | |
| useful bug reports. | |
| Crashes | |
| ======= | |
| These usually show up like this: | |
| |Unexpected Windows program segfault - opcode = 8b | |
| |Segmentation fault in Windows program 1b7:c41. | |
| |Reading symbols from file /root/wine/wine.sym | |
| |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 newsgroup or the | |
| relevant developer as feasonable. | |
| 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 segmentregister 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 where the reason came from. | |
| Since this is usually a primary/secondary reaction to a failed or | |
| misbehaving Wine function, rerun Wine with "-debugmsg +relay" (without ") | |
| added to the commandline. This will get rather much output, but usually | |
| the reason is located in the last call(s). Those lines usually look like | |
| this: | |
| |Call KERNEL.90: LSTRLEN(0227:0692) ret=01e7:2ce7 ds=0227 | |
| ^^^^^^^^^ ^ ^^^^^^^^^ ^^^^^^^^^ ^^^^ | |
| | | | | |Datasegment on entry | |
| | | | |Return address. | |
| | | | | |
| | | |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=0x0007 ret=01e7:2ce7 ds=0227 | |
| ^^^^^^ | |
| |Returnvalue is 16 bit and has the value 7. | |
| 3. If you have found a misbehaving function, try to find out why it | |
| misbehaves. Find the function in the source code. Try to make sense of | |
| the arguments passed. Usually there is a | |
| "dprintf_xyz(stddeb,"Fnction(...)"...);" at the beginning of the | |
| function. Rerun wine with "-debugmsg +xyz,+relay" added to the | |
| commandline. | |
| 4. If those information isn't clear enough or if you want to know more about | |
| what's happening in the function itself, try running wine with "-debugmsg | |
| +all", which dumps ALL included debug information in wine. | |
| 5. If that isn't enough add more debug output for yourself into the | |
| functions you find relevant. | |
| 6. You can also set a breakpoint for that function. Start wine with the | |
| "-debug" option added to the commandline. After loading the executable | |
| wine will enter the internal debugger. Use "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. | |
| Program hangs, nothing happens | |
| ============================== | |
| Switch to UNIX shell, get the process-ID using "ps -a|grep wine", and do a | |
| "kill -HUP <pid>" (without " and <>). Wine will then enter its internal | |
| debugger and you can procede as explained above. | |
| Program reports an error with a Messagebox | |
| ========================================== | |
| Sometimes programs are reporting failure using a 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 wine with "-debug" added to the commandline, set a breakpoint | |
| at "USER.MESSAGEBOX" (win16 programs) "USER32.MessageBox" (win32 Programs) | |
| and proceed with "continue". With "-debugmsg +all" Wine will now stop | |
| directly directly before setting up the Messagebox. Proceed as explained | |
| above. | |
| 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 $ wine winword.exe -debugmsg +relay -debug | |
| |CallTo32(func=08007e00,000001c4,00000081,00000000,00000000) | |
| |CallTo32(func=08007e00,000001c4,00000014,000006d0,00000000) | |
| |Win16 task 'winword': Breakpoint 1 at 0x0157:0x001a | |
| |CallTo16(func=0097:0130,ds=0000) | |
| |Call WPROCS.24: TASK_RESCHEDULE() ret=003f:0759 ds=0000 | |
| |Ret WPROCS.24: TASK_RESCHEDULE() retval=0x0000 ret=003f:0759 ds=08a7 | |
| |CallTo16(func=0157:001a,ds=08a7,0x11d7,0x0000,0x0000,0x3cb4,0x1f40,0x0000,0x0000,0x08a7) | |
| |Reading symbols from file /root/wine/wine.sym | |
| |Stopped on breakpoint 1 at 0x0157:0x001a | |
| |In 16 bit mode. | |
| |0x0157:0x001a: xorw %bp,%bp | |
| |Wine-dbg> break USER.MESSAGEBOX <---- Set Breakpoint | |
| |Breakpoint 2 at 0x0067:0x00000000 (USER.MESSAGEBOX) | |
| |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.97: GETTEMPFILENAME(0x00c3,08a7:8350,0x0000,08a7:8234) ret=058f:09b1 ds=08a7 | |
| ^ ^ ^ ^ | |
| | | | |LPSTR buffer | |
| | | |UINT16 unique | |
| | |LPCSTR prefix | |
| |BYTE drive | |
| |Ret KERNEL.97: GETTEMPFILENAME() retval=0xce3f ret=058f:09b1 ds=08a7 | |
| ^ | |
| |new unique number | |
| |Call KERNEL.74: OPENFILE(08a7:8234,08a7:82c6,0x1012) ret=058f:09d8 ds=08a7 | |
| ^ ^ ^ | |
| | | |UINT32 mode | |
| | |OFSTRUCT *ofs | |
| |LPCSTR name | |
| |Ret KERNEL.74: OPENFILE() retval=0xffff ret=058f:09d8 ds=08a7 | |
| ^ | |
| | -1 aka. HFILE_ERROR | |
| |Stopped on breakpoint 2 at 0x0067:0x00000000 (USER.MESSAGEBOX) | |
| |In 32 bit mode. | |
| |0x0067:0x00000000 (USER.MESSAGEBOX): pushw %bp | |
| Now, we see that OPENFILE seem to have returned 0xFFFF (or -1). Checking | |
| the implementation of OpenFile in files/file.c, this signals an error. | |
| The mode flags (OF_READWRITE|OF_SHARE_EXCLUSIVE|OF_CREATE) seems to | |
| indicate, that WinWord wants to open the file for writing, so we check | |
| the filename. Since we don't see the filename in this debugoutput, we use | |
| the dprintf_file() in OpenFile to print out more information by adding | |
| "-debugmsg +relay" to the commandline. | |
| (In fact, we see that the filename has been returned by the GetTempFileName | |
| function above, but we check it anyway.) | |
| |marcus@jet $ wine winword.exe -debugmsg +relay,+file -debug | |
| |.....much more debugoutput ..... | |
| | | |
| |Call KERNEL.97: GETTEMPFILENAME(0x00c3,08a7:8350,0x0000,08a7:8234) ret=058f:09b1 ds=08a7 | |
| |FILE_Create: 'C:~docd03d.tmp' 01b6 1 | |
| |FILE_SetDosError: errno = 13 | |
| |GetTempFileName: returning C:\~DOCD03D.TMP | |
| |Ret KERNEL.97: GETTEMPFILENAME() retval=0xd03d ret=058f:09b1 ds=08a7 | |
| |Call KERNEL.74: OPENFILE(08a7:8234,08a7:82c6,0x1012) ret=058f:09d8 ds=08a7 | |
| |OpenFile: C:\~DOCD03D.TMP 1012 | |
| |FILE_AllocTaskHandle: returning task handle 1, dos_file 0, file 1 of 254 | |
| |FILE_Create: 'C:\~DOCD03D.TMP' 01b6 0 | |
| |FILE_SetDosError: errno = 13 | |
| |OpenFile(C:\~DOCD03D.TMP): return = HFILE_ERROR | |
| |FILE_FreeTaskHandle: dos=1 file=0 | |
| |Ret KERNEL.74: OPENFILE() retval=0xffff ret=058f:09d8 ds=08a7 | |
| The filename is "C:\~DOCD03D.TMP". Of course, C:\ is writeable for the | |
| superuser only, so the open fails for a normal user and OpenFile returns | |
| -1, which in turn generates this messagebox. | |
| Written by Marcus Meissner <msmeissn@cip.informatik.uni-erlangen.de>, | |
| additions welcome. |