Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29391 Discussions

Floating point exception trapping in DLL

r4
Beginner
3,297 Views

I am using Compaq Visual Fortran 6.6.0 to create a DLL. It is called from a Visual Basic 6 ActiveX single-threadedmain program. The DLL writes to a log file. When it runs into a floating point exception (fpe)I would like it to jump to aroutine that writes an entry to the log file, closes the file,and then stops execution of the program.

Igatheredthis relevant info:

SIGNALQQ does not work in a VB-VF application. Try writing a C routine (called from the Fortran DLL) that uses "signal" to set the callback routine in case of an fpe, thereby bypassing SIGNALQQ.

Wrote this C-code:

#include
#include
void __cdecl fphandler( int sig, int num);
void __stdcall HAND_FPE ( int sig, int num); //Fortran call
//Entry point for setting up where to jump to in case of an exception:
int __stdcall CSIGNALQQDLL() {
//Assumption: floating point control word has already been masked as desired
if ( signal( SIGFPE,(void (__cdecl *)(int)) fphandler) == SIG_ERR ) {
return -1;}
else {
return -2;
}
}
void __stdcall TESTBYRAISE(){
raise(SIGFPE);
}
// This is where we jump to in case of an exception:
void fphandler ( int sig, int num){
_fpreset();
// Make call to a Fortran-written subroutine
HAND_FPE(sig, num);
}

In the Fortran DLL there are these code fragments:

i4_i = CSIGNALQQDLL() ! Set the exception handler
CALL GETCONTROLFPQQ(FPEcontrol)
i4_i = FPEcontrol
call BIC(0,i4_i)
call BIC(2,i4_i)
call BIC(3,i4_i)
FPEcontrol = i4_i
CALL SETCONTROLFPQQ(FPEcontrol)
CALL TESTBYRAISE

with the following effects:

(*) FPE control word is set successfully
(*) CSIGNALQQDLL seems to set the call-back routine successfully because of the next item : ...
(*) The call to TESTBYRAISE succeeds in calling the call-back routine fphandler because the call to the Fortran subroutine HAND_FPE takes place (writes to the log file)
(*) However, when I remove the call to TESTBYRAISE and let the program run into an actualfpe then the call-back routine is not called.

Please help me make this work correctly! Thank you,

Robert Rackl
The Boeing Company

0 Kudos
15 Replies
Jugoslav_Dujic
Valued Contributor II
3,297 Views
I won't be of much help, but just a sanity check:
  • I assume you didn't forget to build the dll with /fpe:0, did you?
  • What does debugger say when the actual program (is supposed to) generate the FPE? It ought at least emit the message: ..."Floating point exception"?
0 Kudos
r4
Beginner
3,297 Views

I did build with /fpe:0

When Iintentionally let it run into a divide by zero, the debugger says

First chance exception in PlotTBL.exe (PFJARP4.DLL): 0xC000008E:Float Divide by Zero

and then it exits to the calling program instead of jumping to the call-back routine. The calling program (in VB6)then reports the Divide by Zero error and aborts.

The linker does give me 2 warnings which I do not know how to resolve:

warning LNK4075: ignoring /EDITANDCONTINUE due to /INCREMENTAL:NO specification

warning LNK4098: defaultlib "LIBCD" conflicts with use of other libs; use /NODEFAULTLIB:library

0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,297 Views
This is a tricky ground...

For the start, the LNK4098 shows a discrepancy between run-time libraries you have on C++ tab (Debug single-threaded) and on Fortran tab (probably Debug multi-threaded dll). I'd recommend trying static (non-dll) libraries. It might solve the problem as well, although it's just a wild stab in the dark.

As far as I get it (and I might easily be wrong), the C run-time library somewhere has a (pseudo-code)

try {
 ...
}
catch (_fpexception e)
{
 fphandler(e.sig, e.num)
}

What I don't get is, where is that try...catch physically located -- in run-time library or injected directly into the code by the C compiler; if the latter, you're likely beaten. Someone from Intel RTL team might have a more useful comment.
0 Kudos
Steven_L_Intel1
Employee
3,297 Views
The try.. is in the Fortran main program wrapper which resides in the Fortran run-time library. The message about EDITANDCONTINUE can be ignored.
0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,297 Views
Thanks for the info Steve; however, we're talking here about a dll, which AFAICT has no wrappers, thus (supposedly) no try's around. I don't get who catches it in this case.
0 Kudos
Steven_L_Intel1
Employee
3,297 Views
Nobody does - or rather, VB's handlers do. This is discussed in the Programmer's Guide (CVF) chapter on exception handling. No Fortran main program means no (or limited) Fortran exception handling.
0 Kudos
r4
Beginner
3,297 Views
How about interposing a C or C++ subroutine between the VB main program and VF DLL. Could I catch the error in that C or C++ routine and react to it there?
0 Kudos
Steven_L_Intel1
Employee
3,297 Views
Yes, and this is one of the suggestions in the Programmer's Guide.
0 Kudos
r4
Beginner
3,297 Views
I studied the info in the programmer's guide, and the sample application VBVF1. I ran it and noticed that arithmetic exceptions (including floating point overflow) do not get caught by the try/except block in the wrappers.c module, i.e., I don't get traceback information in that case. But that's what I really want to know: where did the floating point exception occur? Is there a way to do that in the context ofa Fortran DLL?
0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,297 Views
Robert, the following sample works for me (FP exception caught) on CVF 6.6 C with C++ caller:

http://www.xeffort.com/fortran/misc/MixLang.zip

(it's not minimal, but you'll see the point easily)

I built (Debug configuration) with /fpe:0, but I had to set up floating-point exception mask via SETCONTROLFPQQ. It appears that something in C++ RTL or Windows sets up the FP exception mask to "masked" by default so it has to be unmasked. I can get the same effect by placing
 _controlfp(0, _EM_ZERODIVIDE);
in the C++ calling code (presumably, the same would do if you have a C++ wrapper within the dll).
0 Kudos
r4
Beginner
3,297 Views
Thank you, Jugoslav. I downloaded MixLang.zip, set /fpe:0,and compiled and executed in the VS development/debug environment. But it's not working for me. Upon the divide by 0 in the Test2 routine the thread exits due to Float Divide by Zero and never gets to the Catch block. I assume it's working for you; now I am scratching my head to divine what's different between our setups? I don't know ...
0 Kudos
Jugoslav_Dujic
Valued Contributor II
3,297 Views
I'm puzzled too. To my knowledge, there's absolutely nothing standing in-between C++ try() and dll's Test2 routine. In other words, I don't see if Fortran anything could possibly stand in the way.

If you haven't already, it's a good idea to upgrade to 6.6C, although I don't see that it could improve something.

For what it's worth, the assembly code generated for the line f=f/0. is:

53: f = f/0.
1000139C fld dword ptr [ebx]
1000139E wait
1000139F fdiv dword ptr [string "crtdll.c"+0FFFFFFF4h (10003048)]
100013A5 wait
100013A6 fstp dword ptr [ebx]
100013A8 wait
Fstp instruction raises a FP exception if any (http://webster.cs.ucr.edu/AoA/Windows/HTML/RealArithmetica2.html), and the catch(...) block is executed immediately after.

0 Kudos
drgfthomas
Beginner
3,297 Views

SEH works just fine for both CVF and IVF, whether with .exe's or .dll's. However, if you're linking incrementally, don't, it prevents traceback from giving any useful info.

0 Kudos
oleg2
Beginner
3,297 Views

Hi,

You can try FOR_SET_FPE(...) , see example below.

It's works for me. My main application is MFC C++ application and Fortran in static library. I use VC++(VS2005) and Intel Fortran 9.1 .

Regards,

Oleg.

subroutine test_command3
USE IFCORE
IMPLICIT NONE
real*4 res_uflow, res_oflow
real*4 res_dbyz, res_inv
real*4 small, big, zero, scale
INTEGER*4 OLD_FPE_FLAGS, NEW_FPE_FLAGS
NEW_FPE_FLAGS = FPE_M_TRAP_DIV0+FPE_M_MSG_DIV0 + FPE_M_TRAP_UND+FPE_M_MSG_UND + FPE_M_TRAP_OVF+FPE_M_MSG_OVF + FPE_M_TRAP_INV+FPE_M_MSG_INV
OLD_FPE_FLAGS = FOR_SET_FPE(NEW_FPE_FLAGS)

small = 1.0e-30
big = 1.0e30
zero = 0.0
scale = 1.0e-10
! IEEE underflow condition (Underflow Raised)
res_uflow = small * scale
write(6,100)"Underflow: ",small, " *", scale, " = ", res_uflow
! IEEE overflow condition (Overflow Raised)
res_oflow = big * big
write(6,100)"Overflow: ", big, " *", big, " = ", res_oflow

! IEEE divide-by-zero condition (Divide by Zero Raised)

res_dbyz = -big / zero

write(6,100)"Div-by-zero: ", -big, " /", zero, " = ", res_dbyz

! IEEE invalid condition (Invalid Raised)
res_inv = zero / zero
write(6,100)"Invalid: ", zero, " /", zero, " = ", res_inv
100 format(A14,E8.1,A2,E8.1,A2,E10.1)

end

0 Kudos
jimdempseyatthecove
Honored Contributor III
3,297 Views

I is not apparent how frequently your application calls the DLL. If the calling frequency is low relative to the runtime inside the DLL then the following techniques may be useful.

1) At the entry point(s) to the DLL call the function FTELL(iUnitLogFile) to obtain the current output position.

2) Write to the Log File "Floating Point Exception" and call FLUSH(iUnitLogFile)

3) Prior to any legitimate writes to the log file within the DLL perform an FSEEK to the file position in front of the "Floating Point Exception" marker.

4) Following any legitimate writes to the log file within the DLL perform steps 1) and 2)

5) Just prior to exit from DLL perform an FSEEK to the file position in front of the "Floating Point Exception" marker.

Consider changing "Floating Point Exception" to "Fatal Error occured in xyz.DLL" since it will also catch not only FPE but invalid memory access, etc...

The aboveis a fall-back suggestion.

An alternartve is to create a monitoring application that has a pipe to the current .DLL. Then in the .DLL you can write tracing information as to not only Entry and Exit of the DLL but also of major waypoints within the DLL. Then if the application Ab-Ends within the DLL you will have more information available to diagnose the problem.

Good luck.

Jim Dempsey

0 Kudos
Reply