xref: /petsc/src/sys/error/fp.c (revision e32f2f54e699d0aa6e733466c00da7e34666fe5e)
1 #define PETSC_DLL
2 /*
3 *	IEEE error handler for all machines. Since each machine has
4 *   enough slight differences we have completely separate codes for each one.
5 *
6 */
7 
8 /*
9   This feature test macro provides FE_NOMASK_ENV on GNU.  It must be defined
10   at the top of the file because other headers may pull in fenv.h even when
11   not strictly necessary.  Strictly speaking, we could include ONLY petscconf.h,
12   check PETSC_HAVE_FENV_H, and only define _GNU_SOURCE in that case, but such
13   shenanigans ought to be unnecessary.
14 */
15 #define _GNU_SOURCE
16 
17 #include "petscsys.h"           /*I  "petscsys.h"  I*/
18 #include <signal.h>
19 #if defined(PETSC_HAVE_STDLIB_H)
20 #include <stdlib.h>
21 #endif
22 
23 /*--------------------------------------- ---------------------------------------------------*/
24 #if defined(PETSC_HAVE_SUN4_STYLE_FPTRAP)
25 #include <floatingpoint.h>
26 
27 EXTERN_C_BEGIN
28 PetscErrorCode ieee_flags(char*,char*,char*,char**);
29 PetscErrorCode ieee_handler(char *,char *,sigfpe_handler_type(int,int,struct sigcontext*,char *));
30 EXTERN_C_END
31 
32 static struct { int code_no; char *name; } error_codes[] = {
33            { FPE_INTDIV_TRAP	,"integer divide" },
34 	   { FPE_FLTOPERR_TRAP	,"IEEE operand error" },
35 	   { FPE_FLTOVF_TRAP	,"floating point overflow" },
36 	   { FPE_FLTUND_TRAP	,"floating point underflow" },
37 	   { FPE_FLTDIV_TRAP	,"floating pointing divide" },
38 	   { FPE_FLTINEX_TRAP	,"inexact floating point result" },
39 	   { 0			,"unknown error" }
40 } ;
41 #define SIGPC(scp) (scp->sc_pc)
42 
43 #undef __FUNCT__
44 #define __FUNCT__ "PetscDefaultFPTrap"
45 sigfpe_handler_type PetscDefaultFPTrap(int sig,int code,struct sigcontext *scp,char *addr)
46 {
47   PetscErrorCode ierr;
48   int err_ind = -1,j;
49 
50   PetscFunctionBegin;
51   for (j = 0 ; error_codes[j].code_no ; j++) {
52     if (error_codes[j].code_no == code) err_ind = j;
53   }
54 
55   if (err_ind >= 0) {
56     (*PetscErrorPrintf)("*** %s occurred at pc=%X ***\n",error_codes[err_ind].name,SIGPC(scp));
57   } else {
58     (*PetscErrorPrintf)("*** floating point error 0x%x occurred at pc=%X ***\n",code,SIGPC(scp));
59   }
60   ierr = PetscError(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided function","Unknown file","Unknown directory",PETSC_ERR_FP,1,"floating point error");
61   MPI_Abort(PETSC_COMM_WORLD,0);
62   PetscFunctionReturn(0);
63 }
64 
65 #undef __FUNCT__
66 #define __FUNCT__ "PetscSetFPTrap"
67 /*@
68    PetscSetFPTrap - Enables traps/exceptions on common floating point errors.
69                     This option may not work on certain machines.
70 
71    Not Collective
72 
73    Input Parameters:
74 .  flag - PETSC_FP_TRAP_ON, PETSC_FP_TRAP_OFF.
75 
76    Options Database Keys:
77 .  -fp_trap - Activates floating point trapping
78 
79    Level: advanced
80 
81    Description:
82    On systems that support it, this routine causes floating point
83    overflow, divide-by-zero, and invalid-operand (e.g., a NaN) to
84    cause a message to be printed and the program to exit.
85 
86    Note:
87    On many common systems including x86 and x86-64 Linux, the floating
88    point exception state is not preserved from the location where the trap
89    occurred through to the signal handler.  In this case, the signal handler
90    will just say that an unknown floating point exception occurred and which
91    function it occurred in.  If you run with -fp_trap in a debugger, it will
92    break on the line where the error occurred.  You can check which
93    exception occurred using fetestexcept(FE_ALL_EXCEPT).  See fenv.h
94    (usually at /usr/include/bits/fenv.h) for the enum values on your system.
95 
96    Caution:
97    On certain machines, in particular the IBM rs6000, floating point
98    trapping is VERY slow!
99 
100    Concepts: floating point exceptions^trapping
101    Concepts: divide by zero
102 
103 @*/
104 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag)
105 {
106   char *out;
107 
108   PetscFunctionBegin;
109   /* Clear accumulated exceptions.  Used to suppress meaningless messages from f77 programs */
110   (void) ieee_flags("clear","exception","all",&out);
111   if (flag == PETSC_FP_TRAP_ON) {
112     if (ieee_handler("set","common",PetscDefaultFPTrap)) {
113       /*
114         To trap more fp exceptions, including undrflow, change the above line to
115         if (ieee_handler("set","all",PetscDefaultFPTrap)) {
116       */
117       (*PetscErrorPrintf)("Can't set floatingpoint handler\n");
118     }
119   } else {
120     if (ieee_handler("clear","common",PetscDefaultFPTrap)) {
121       (*PetscErrorPrintf)("Can't clear floatingpoint handler\n");
122     }
123   }
124   PetscFunctionReturn(0);
125 }
126 
127 /* -------------------------------------------------------------------------------------------*/
128 #elif defined(PETSC_HAVE_SOLARIS_STYLE_FPTRAP)
129 #include <sunmath.h>
130 #include <floatingpoint.h>
131 #include <siginfo.h>
132 #include <ucontext.h>
133 
134 static struct { int code_no; char *name; } error_codes[] = {
135   {  FPE_FLTINV,"invalid floating point operand"},
136   {  FPE_FLTRES,"inexact floating point result"},
137   {  FPE_FLTDIV,"division-by-zero"},
138   {  FPE_FLTUND,"floating point underflow"},
139   {  FPE_FLTOVF,"floating point overflow"},
140   {  0,         "unknown error"}
141 };
142 #define SIGPC(scp) (scp->si_addr)
143 
144 #undef __FUNCT__
145 #define __FUNCT__ "PetscDefaultFPTrap"
146 void PetscDefaultFPTrap(int sig,siginfo_t *scp,ucontext_t *uap)
147 {
148   int err_ind,j,code = scp->si_code;
149   PetscErrorCode ierr;
150 
151   PetscFunctionBegin;
152   err_ind = -1 ;
153   for (j = 0 ; error_codes[j].code_no ; j++) {
154     if (error_codes[j].code_no == code) err_ind = j;
155   }
156 
157   if (err_ind >= 0) {
158     (*PetscErrorPrintf)("*** %s occurred at pc=%X ***\n",error_codes[err_ind].name,SIGPC(scp));
159   } else {
160     (*PetscErrorPrintf)("*** floating point error 0x%x occurred at pc=%X ***\n",code,SIGPC(scp));
161   }
162   ierr = PetscError(PETSC_COMM_SELF,0,"User provided function","Unknown file","Unknown directory",PETSC_ERR_FP,1,"floating point error");
163   MPI_Abort(PETSC_COMM_WORLD,0);
164 }
165 
166 #undef __FUNCT__
167 #define __FUNCT__ "PetscSetFPTrap"
168 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag)
169 {
170   char *out;
171 
172   PetscFunctionBegin;
173   /* Clear accumulated exceptions.  Used to suppress meaningless messages from f77 programs */
174   (void) ieee_flags("clear","exception","all",&out);
175   if (flag == PETSC_FP_TRAP_ON) {
176     if (ieee_handler("set","common",(sigfpe_handler_type)PetscDefaultFPTrap)) {
177       (*PetscErrorPrintf)("Can't set floating point handler\n");
178     }
179   } else {
180     if (ieee_handler("clear","common",(sigfpe_handler_type)PetscDefaultFPTrap)) {
181      (*PetscErrorPrintf)("Can't clear floatingpoint handler\n");
182     }
183   }
184   PetscFunctionReturn(0);
185 }
186 
187 /* ------------------------------------------------------------------------------------------*/
188 
189 #elif defined (PETSC_HAVE_IRIX_STYLE_FPTRAP)
190 #include <sigfpe.h>
191 static struct { int code_no; char *name; } error_codes[] = {
192        { _INVALID   ,"IEEE operand error" },
193        { _OVERFL    ,"floating point overflow" },
194        { _UNDERFL   ,"floating point underflow" },
195        { _DIVZERO   ,"floating point divide" },
196        { 0          ,"unknown error" }
197 } ;
198 #undef __FUNCT__
199 #define __FUNCT__ "PetscDefaultFPTrap"
200 void PetscDefaultFPTrap(unsigned exception[],int val[])
201 {
202   int err_ind,j,code;
203 
204   PetscFunctionBegin;
205   code = exception[0];
206   err_ind = -1 ;
207   for (j = 0 ; error_codes[j].code_no ; j++){
208     if (error_codes[j].code_no == code) err_ind = j;
209   }
210   if (err_ind >= 0){
211     (*PetscErrorPrintf)("*** %s occurred ***\n",error_codes[err_ind].name);
212   } else{
213     (*PetscErrorPrintf)("*** floating point error 0x%x occurred ***\n",code);
214   }
215   PetscError(PETSC_COMM_SELF,0,"User provided function","Unknown file","Unknown directory",PETSC_ERR_FP,1,"floating point error");
216   MPI_Abort(PETSC_COMM_WORLD,0);
217 }
218 
219 #undef __FUNCT__
220 #define __FUNCT__ "PetscSetFPTrap"
221 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag)
222 {
223   PetscFunctionBegin;
224   if (flag == PETSC_FP_TRAP_ON) {
225     handle_sigfpes(_ON,_EN_OVERFL|_EN_DIVZERO|_EN_INVALID,PetscDefaultFPTrap,_ABORT_ON_ERROR,0);
226   } else {
227     handle_sigfpes(_OFF,_EN_OVERFL|_EN_DIVZERO|_EN_INVALID,0,_ABORT_ON_ERROR,0);
228   }
229   PetscFunctionReturn(0);
230 }
231 /*----------------------------------------------- --------------------------------------------*/
232 /* In "fast" mode, floating point traps are imprecise and ignored.
233    This is the reason for the fptrap(FP_TRAP_SYNC) call */
234 #elif defined(PETSC_HAVE_RS6000_STYLE_FPTRAP)
235 struct sigcontext;
236 #include <fpxcp.h>
237 #include <fptrap.h>
238 #include <stdlib.h>
239 #define FPE_FLTOPERR_TRAP (fptrap_t)(0x20000000)
240 #define FPE_FLTOVF_TRAP   (fptrap_t)(0x10000000)
241 #define FPE_FLTUND_TRAP   (fptrap_t)(0x08000000)
242 #define FPE_FLTDIV_TRAP   (fptrap_t)(0x04000000)
243 #define FPE_FLTINEX_TRAP  (fptrap_t)(0x02000000)
244 
245 static struct { int code_no; char *name; } error_codes[] = {
246            {FPE_FLTOPERR_TRAP	,"IEEE operand error" },
247 	   { FPE_FLTOVF_TRAP	,"floating point overflow" },
248 	   { FPE_FLTUND_TRAP	,"floating point underflow" },
249 	   { FPE_FLTDIV_TRAP	,"floating point divide" },
250 	   { FPE_FLTINEX_TRAP	,"inexact floating point result" },
251 	   { 0			,"unknown error" }
252 } ;
253 #define SIGPC(scp) (0) /* Info MIGHT be in scp->sc_jmpbuf.jmp_context.iar */
254 /*
255    For some reason, scp->sc_jmpbuf does not work on the RS6000, even though
256    it looks like it should from the include definitions.  It is probably
257    some strange interaction with the "POSIX_SOURCE" that we require.
258 */
259 
260 #undef __FUNCT__
261 #define __FUNCT__ "PetscDefaultFPTrap"
262 void PetscDefaultFPTrap(int sig,int code,struct sigcontext *scp)
263 {
264   PetscErrorCode ierr;
265   int      err_ind,j;
266   fp_ctx_t flt_context;
267 
268   PetscFunctionBegin;
269   fp_sh_trap_info(scp,&flt_context);
270 
271   err_ind = -1 ;
272   for (j = 0 ; error_codes[j].code_no ; j++) {
273     if (error_codes[j].code_no == flt_context.trap) err_ind = j;
274   }
275 
276   if (err_ind >= 0){
277     (*PetscErrorPrintf)("*** %s occurred ***\n",error_codes[err_ind].name);
278   } else{
279     (*PetscErrorPrintf)("*** floating point error 0x%x occurred ***\n",flt_context.trap);
280   }
281   ierr = PetscError(PETSC_COMM_SELF,0,"User provided function","Unknown file","Unknown directory",PETSC_ERR_FP,1,"floating point error");
282   MPI_Abort(PETSC_COMM_WORLD,0);
283 }
284 
285 #undef __FUNCT__
286 #define __FUNCT__ "PetscSetFPTrap"
287 PetscErrorCode PetscSetFPTrap(PetscFPTrap on)
288 {
289   PetscFunctionBegin;
290   if (on == PETSC_FP_TRAP_ON) {
291     signal(SIGFPE,(void (*)(int))PetscDefaultFPTrap);
292     fp_trap(FP_TRAP_SYNC);
293     fp_enable(TRP_INVALID | TRP_DIV_BY_ZERO | TRP_OVERFLOW);
294     /* fp_enable(mask) for individual traps.  Values are:
295        TRP_INVALID
296        TRP_DIV_BY_ZERO
297        TRP_OVERFLOW
298        TRP_UNDERFLOW
299        TRP_INEXACT
300        Can OR then together.
301        fp_enable_all(); for all traps.
302     */
303   } else {
304     signal(SIGFPE,SIG_DFL);
305     fp_disable(TRP_INVALID | TRP_DIV_BY_ZERO | TRP_OVERFLOW);
306     fp_trap(FP_TRAP_OFF);
307   }
308   PetscFunctionReturn(0);
309 }
310 
311 #elif defined PETSC_HAVE_FENV_H
312 /*
313    C99 style floating point environment.
314 
315    Note that C99 merely specifies how to save, restore, and clear the floating
316    point environment as well as defining an enumeration of exception codes.  In
317    particular, C99 does not specify how to make floating point exceptions raise
318    a signal.  Glibc offers this capability through FE_NOMASK_ENV (or with finer
319    granularity, feenableexcept()), xmmintrin.h offers _MM_SET_EXCEPTION_MASK().
320 */
321 #include <fenv.h>
322 typedef struct {int code; const char *name;} FPNode;
323 static const FPNode error_codes[] = {
324     {FE_DIVBYZERO,"divide by zero"},
325     {FE_INEXACT,  "inexact floating point result"},
326     {FE_INVALID,  "invalid floating point arguments (domain error)"},
327     {FE_OVERFLOW, "floating point overflow"},
328     {FE_UNDERFLOW,"floating point underflow"},
329     {0           ,"unknown error"}
330 };
331 EXTERN_C_BEGIN
332 #undef __FUNCT__
333 #define __FUNCT__ "PetscDefaultFPTrap"
334 void PetscDefaultFPTrap(int sig)
335 {
336   const FPNode *node;
337   int          code;
338   PetscTruth   matched = PETSC_FALSE;
339 
340   PetscFunctionBegin;
341   /* Note: While it is possible for the exception state to be preserved by the
342    * kernel, this seems to be rare which makes the following flag testing almost
343    * useless.  But on a system where the flags can be preserved, it would provide
344    * more detail.
345    */
346   code = fetestexcept(FE_ALL_EXCEPT);
347   for (node=&error_codes[0]; node->code; node++) {
348     if (code & node->code) {
349       matched = PETSC_TRUE;
350       (*PetscErrorPrintf)("*** floating point error \"%s\" occurred ***\n",node->name);
351       code &= ~node->code; /* Unset this flag since it has been processed */
352     }
353   }
354   if (!matched || code) { /* If any remaining flags are set, or we didn't process any flags */
355     (*PetscErrorPrintf)("*** unknown floating point error occurred ***\n");
356     (*PetscErrorPrintf)("The specific exception can be determined by running in a debugger.  When the\n");
357     (*PetscErrorPrintf)("debugger traps the signal, the exception can be found with fetestexcept(0x%x)\n",FE_ALL_EXCEPT);
358     (*PetscErrorPrintf)("where the result is a bitwise OR of the following flags:\n");
359     (*PetscErrorPrintf)("FE_INVALID=0x%x FE_DIVBYZERO=0x%x FE_OVERFLOW=0x%x FE_UNDERFLOW=0x%x FE_INEXACT=0x%x\n",FE_INVALID,FE_DIVBYZERO,FE_OVERFLOW,FE_UNDERFLOW,FE_INEXACT);
360   }
361 
362   (*PetscErrorPrintf)("Try option -start_in_debugger\n");
363 #if defined(PETSC_USE_DEBUG)
364   if (!PetscStackActive) {
365     (*PetscErrorPrintf)("  or try option -log_stack\n");
366   } else {
367     (*PetscErrorPrintf)("likely location of problem given in stack below\n");
368     (*PetscErrorPrintf)("---------------------  Stack Frames ------------------------------------\n");
369     PetscStackView(PETSC_VIEWER_STDOUT_SELF);
370   }
371 #endif
372 #if !defined(PETSC_USE_DEBUG)
373   (*PetscErrorPrintf)("configure using --with-debugging=yes, recompile, link, and run \n");
374   (*PetscErrorPrintf)("with -start_in_debugger to get more information on the crash.\n");
375 #endif
376   PetscError(PETSC_COMM_SELF,0,"User provided function","Unknown file","Unknown directory",PETSC_ERR_FP,1,"trapped floating point error");
377   MPI_Abort(PETSC_COMM_WORLD,0);
378 }
379 EXTERN_C_END
380 
381 #undef __FUNCT__
382 #define __FUNCT__ "PetscSetFPTrap"
383 PetscErrorCode PETSC_DLLEXPORT PetscSetFPTrap(PetscFPTrap on)
384 {
385   PetscFunctionBegin;
386   if (on == PETSC_FP_TRAP_ON) {
387     /* Clear any flags that are currently set so that activating trapping will not immediately call the signal handler. */
388     if (feclearexcept(FE_ALL_EXCEPT)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Cannot clear floating point exception flags\n");
389 #if defined FE_NOMASK_ENV
390     /* We could use fesetenv(FE_NOMASK_ENV), but that causes spurious exceptions (like gettimeofday() -> PetscLogDouble). */
391     if (feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) == -1) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Cannot activate floating point exceptions\n");
392 #elif defined PETSC_HAVE_XMMINTRIN_H
393     _MM_SET_EXCEPTION_MASK(_MM_MASK_INEXACT);
394 #else
395     /* C99 does not provide a way to modify the environment so there is no portable way to activate trapping. */
396 #endif
397     if (SIG_ERR == signal(SIGFPE,PetscDefaultFPTrap)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Can't set floating point handler\n");
398   } else {
399     if (fesetenv(FE_DFL_ENV)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Cannot disable floating point exceptions");
400     if (SIG_ERR == signal(SIGFPE,SIG_DFL)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Can't clear floating point handler\n");
401   }
402   PetscFunctionReturn(0);
403 }
404 
405 /* -------------------------Default -----------------------------------*/
406 #else
407 EXTERN_C_BEGIN
408 #undef __FUNCT__
409 #define __FUNCT__ "PetscDefaultFPTrap"
410 void PetscDefaultFPTrap(int sig)
411 {
412   PetscFunctionBegin;
413   (*PetscErrorPrintf)("*** floating point error occurred ***\n");
414   PetscError(PETSC_COMM_SELF,0,"User provided function","Unknown file","Unknown directory",PETSC_ERR_FP,1,"floating point error");
415   MPI_Abort(PETSC_COMM_WORLD,0);
416 }
417 EXTERN_C_END
418 #undef __FUNCT__
419 #define __FUNCT__ "PetscSetFPTrap"
420 PetscErrorCode PETSC_DLLEXPORT PetscSetFPTrap(PetscFPTrap on)
421 {
422   PetscFunctionBegin;
423   if (on == PETSC_FP_TRAP_ON) {
424     if (SIG_ERR == signal(SIGFPE,PetscDefaultFPTrap)) {
425       (*PetscErrorPrintf)("Can't set floatingpoint handler\n");
426     }
427   } else {
428     if (SIG_ERR == signal(SIGFPE,SIG_DFL)) {
429       (*PetscErrorPrintf)("Can't clear floatingpoint handler\n");
430     }
431   }
432   PetscFunctionReturn(0);
433 }
434 #endif
435 
436 
437 
438