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