How to debug division by zero exception in Internet Explorer?
Asked Answered
G

2

11

i am hosting Internet Explorer in a Windows application. i can scroll down to the bottom of the document. When i then try to scroll back up i get a division by zero exception:

enter image description here

When i scroll using Page Up the crash appears to happen at the call to - IOleInPlaceActiveObject:TranslateAccelerator

When i scroll using the mouse the crash happens during the call to

Either way, the crash is happening in Internet Explorer.

The stack trace shown by Delphi at the time of the exception:

enter image description here

Is different from the stack trace shown by Jedi's exception tracing:

Exception EZeroDivide in module mshtml.dll at 00378B89.
Floating point division by zero.

Exception raised by object: TEmbeddedWB


Full Exception Details:
EZeroDivide
ExceptionCode: 0xC000008E (EXCEPTION_FLT_DIVIDE_BY_ZERO)
The thread tried to divide a floating-point value by a floating-point divisor of zero.
ExceptionFlags: 0x00000002
ExceptionAddress: 0x574C8B89
Parameters: (0x00000000)
EXCEPTION_RECORD: nil
Message: Floating point division by zero


Stack Trace:
[574C8B89] Unknown function at DllGetClassObject + $FF033
[004CA61E] OleCtrls.TOleControl.WndProc (Line 2171, "olectrls.pas" + 12) + $10
[006CF62A] EmbeddedWB.TEmbeddedWB.WBWndProc (Line 1492, "EmbeddedWB.pas" + 31) + $8
[0046200C] Forms.StdWndProc (Line 1459, "Forms.pas" + 8) + $0
[0046D2A2] Forms.TApplication.IsKeyMsg (Line 6588, "Forms.pas" + 20) + $1E
[0046D43F] Forms.TApplication.ProcessMessage (Line 6626, "Forms.pas" + 9) + $2A
[0046D4AB] Forms.TApplication.HandleMessage (Line 6649, "Forms.pas" + 1) + $6
[0046938C] Forms.TCustomForm.ShowModal (Line 4692, "Forms.pas" + 22) + $5
[007D72AD] FMain.TfrmMain.actControlPanelExecute (Line 566, "FMain.pas" + 5) + $5
[00424AF5] Classes.TBasicAction.Execute (Line 8077, "Classes.pas" + 3) + $9
[00455369] ActnList.TContainedAction.Execute + $31
[0045B6AE] Menus.TMenuItem.Click (Line 1738, "Menus.pas" + 9) + $8
[0045CD71] Menus.TMenu.DispatchCommand (Line 2446, "Menus.pas" + 5) + $5

i tried debugging the crash in WinDbg:

ModLoad: 00000000`75360000 00000000`75372000   C:\Windows\syswow64\DEVOBJ.dll
ModLoad: 00000000`71940000 00000000`719fa000   C:\Windows\SysWOW64\d2d1.dll
ModLoad: 00000000`715e0000 00000000`716ea000   C:\Windows\SysWOW64\DWrite.dll
ModLoad: 00000000`71870000 00000000`718f3000   C:\Windows\SysWOW64\dxgi.dll
ModLoad: 00000000`74fb0000 00000000`74fdd000   C:\Windows\syswow64\WINTRUST.dll
ModLoad: 00000000`71db0000 00000000`71ddc000   C:\Windows\SysWOW64\d3d10_1.dll
ModLoad: 00000000`71900000 00000000`7193a000   C:\Windows\SysWOW64\d3d10_1core.dll
ModLoad: 00000000`6e5c0000 00000000`6e6ec000   C:\Windows\SysWOW64\D3D10Warp.dll
ModLoad: 00000000`6d480000 00000000`6d63b000   C:\Windows\SysWOW64\jscript9.dll
ModLoad: 00000000`714f0000 00000000`714fb000   C:\Windows\SysWOW64\msimtf.dll
ModLoad: 00000000`6f080000 00000000`6f0ab000   C:\Windows\SysWOW64\msls31.dll
ModLoad: 00000000`082c0000 00000000`082fc000   C:\Windows\SysWOW64\Oleacc.dll
ModLoad: 00000000`613e0000 00000000`6140e000   C:\Windows\SysWOW64\MLANG.dll
ModLoad: 00000000`62bb0000 00000000`62cb2000   C:\Windows\SysWOW64\d3d10.dll
ModLoad: 00000000`62b70000 00000000`62ba3000   C:\Windows\SysWOW64\d3d10core.dll
(834.d04): Unknown exception - code c000008e (first chance)

But because Delphi catches all exceptions, it never leeks out and breaks WinDbg. (Or maybe that's why WinDbg doesn't break).

How can i stop Delphi from catching exceptions, so that the application is allowed to werfault crash, so i can get the instruction that's causing the problem. It's a floating point exception, somewhere is the code that is trying to divide by zero.


Someone else is getting the same crash in the same circumstances:

With the helpful suggestions from Microsoft that he turn off any 3rd party addons, and try running IE in safe mode.

While it's nice to be vindicated that i'm not the only one experiencing this problem; i'm more interested in a solution.


The exceptions can be masked by asking the floating point unit not to raise exceptions, by fiddling the floating point control word with the FLDCW instruction:

procedure TfrmControlPanel.FormCreate(Sender: TObject);
begin
FSaved8087CW := Default8087CW;  // Save this because Set8087CW changes it.
Set8087CW($027F); //restore later using Set8087CW(FSaved8087CW);
       //$027F comes from http://msdn.microsoft.com/en-us/library/ms235300.aspx  

   {
      Scrolling in browser was causing floating point exceptions

      https://mcmap.net/q/741104/-how-to-debug-division-by-zero-exception-in-internet-explorer

      What it boils down to is that MS habitually compile their code with FP exceptions masked.
      Embarcadero tools habitually unmask them. Thus the MS code is written assuming that FP exceptions
      will not be raised and is not resilient to them. To deal with that you have to mask the exceptions
      before calling into the MS code. If your app does not floating point then just mask the exceptions
      at start up and be done with it. Call Set8087CW($027F) at start up and you are good to go.

            Default8087CW: $1332  = 0001 0011 0011 0010
            New 8087CW:    $027F  = 0000 0010 0111 1111
                                    ...I RCPC ..MM MMMM

        Bit  0: Invalid Operation (IM)  |
        Bit  1: Denormal Operand (DM)   |
        Bit  2: Zero Divide (ZM)        | Exception Masks (Bits 0..5)
        Bit  3: Overflow (OM)           | When one of these mask bits is set, its corresponding x87 FPU
        Bit  4: Underflow (UM)          | floating-point exception is blocked from being generated
        Bit  5: Precision (PM)          |
        Bit  6:  (reserved)
        Bit  7:  (reserved)
        Bit  8: +Precision Control (PC) 00=Single Precision (24 bits), 10=Double Precision (53 bits), 11=Double Extended Precision (64 bits), 01=reserved
        Bit  9: /
        Bit 10: + Rounding Control (RC)
        Bit 11: /
        Bit 12: Infinity Control        | not meaningful for anything past the 80287 math coprocessor
        Bit 13:  (reserved)
        Bit 14:  (reserved)
        Bit 15:  (reserved)
    }

Still, i'd like to know how i can find the line that's throwing the exception.

Gallion answered 27/2, 2012 at 21:5 Comment(19)
I think this is merely the classical issue: you have to set the control word (Set8087CW).Isaak
@AndreasRejbrand Is this something that all applications should always turn on always? Or is it something that should only be turned on and off to mask the exception i'm getting? (i.e. if i only turn it on to hide this exception on this form i don't know how much other code that isn't mine will still crash) And if it is something that every program should always turn on, then why would it be off by default?Gallion
When you're using complex ActiveX controls, or any external DLL, it's always wise to consider it.Latrice
possible duplicate of Getting "Invalid Floating Point" error with ActiveX Control in DelphiLatrice
We had a similar question quite recently concerning 64 bit XE2, but the conclusion is always the same, mask the exceptions: #9359723Roane
@AndreasRejbrand Reading the documentation i get the impression that if i use Set8087CW($133f) to turn off floating point exceptions, that it will turn off floating point exceptions, rather than "fixing" this one problem.Gallion
Reading the linked questions, i'm puzzled why someone would want to mask exceptions. Shouldn't you not be causing them in the first place?Gallion
What it boils down to is that MS habitually compile their code with FP exceptions masked. Embarcadero tools habitually unmask them. Thus the MS code is written assuming that FP exceptions will not be raised and is not resilient to them. To deal with that you have to mask the exceptions before calling into the MS code. If your app does not floating point then just mask the exceptions at start up and be done with it. Call Set8087CW($027F) at start up and you are good to go. That's your solution.Roane
If you are writing code in a language which does not support exceptions (I'm looking at you C) then you pretty much want to mask them, 'cos you sure as hell can't catch them. So instead you check that the result of any divides don't set the error flags, or you check that the resulting value is not Infinity, or NaN and so on. It pretty much sucks to have to program like that. But you are better off letting the calculation go ahead and check for failure once it is done. That's easier than trying to anticipate failure.Roane
Oh, and your comment with $133f as the CW is wrong. Use $027F which is the official Windows default. MS tools are always happy with that CW.Roane
@DavidHeffernan i was just lifting $133f from the Delphi help. i don't know what the CW's mean, so all i can do is copy-paste whatever i find. If $027f is "better", then i'll use that.Gallion
It's documented in the Intel processor documentation. Lots of bit twiddling required. The less opaque way to do it is to call Math.SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]). And of course I bet that was introduced in D6 and I imagine your project is still in good old D5!Roane
Why do you say Windbg doesn't catch the exception? Haven't you just shown the output from Windbg when it catches an exception?Kimikokimitri
"Still, i'd like to know how i can find the line that's throwing the exception." That's pointless. What are you going to do when you get that information. That code was written under the assumption that FP exceptions are masked. That code, the code that raises the exception is correct. It is working exactly as designed. It's not your code and there's nothing you can do to change it. All you can do is feed it the CW that it wants.Roane
@RobKennedy WinDbg doesn't break when the exception happens - it doesn't break at all. i see it, but it doesn't stop execution at all. Delphi's debugger breaks execution, but not where the exception is.Gallion
@DavidHeffernan But perhaps there's something invalid in my HTML, embedded javascript, or embedded CSS that it should shed some light on.Gallion
@IanBoyd No that's not the case. This is just by design. The MS code is written to intentionally perform FP ops that result in errors. And what's more a browser has to be robust to any input.Roane
@DavidHeffernan Any information on where $027F comes from? It alters the precision from 64-bit (double extended) to 53-bit (double). It also sets a reserved bit (bit 6).Gallion
When you start a brand new process, Windows initialises the control word to that value. Interestingly the chip itself has a different default, but that's a rather arcane point of information.Roane
K
5

To get the line of code that throws the exception, you'll have to ask Microsoft for the source to mshtml.dll. That's where the exception is thrown, by the instruction at address 00378B89.

You could try opening the CPU window and looking at the machine code from the DLL, but if you're not already comfortable debugging someone else's machine code, I don't know how much good it'll do you.

The debugger's stack trace and the JCL stack trace look pretty similar to me. The main difference is that the JCL tries to give some information about the DLL function and the debugger doesn't. That's not much difference, though; the information the JCL shows is meaningless since the nearest function name it finds is nearly a megabyte away from the actual faulting instruction. The other difference is whether TWinControl.MainWndProc is included in the stack trace, which I suspect is due to whether the stack trace was determined by reading DCU debug information or by analyzing the physical contents of the stack.

Kimikokimitri answered 27/2, 2012 at 23:7 Comment(2)
i was hoping to figure out how to make Delphi's debugger stop at the line of code (cpu window; line of assembly code) that is throwing the exception. i do have the Microsoft Symbol server, and a debugger that can interrogate it (WinDbg). But for some reason the exception is not "seen" until it's back in my own module (maybe it's a COM safecall interop thing - but then it would be an EOleSysError and not an EZeroDivide.Gallion
i suppose the best i will be able to get is to load DbgHelp.dll myself, and call the export that gets symbol information for offset 00378B89. If i get around to writing the DbgHelp imports i'll post my results.Gallion
G
0

If you don't use Math.SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]), you will find the fix only works on 64 bit Windows.

Gusty answered 6/3, 2013 at 11:52 Comment(1)
Well, the question was for Delphi 5 so this is moot. And I guess you meant only works on 32 bit, not on 64 bit.Roane

© 2022 - 2024 — McMap. All rights reserved.