xref: /petsc/src/sys/error/fp.c (revision aea10558d4771f8b0f4852a014c7d324cb0c3f99)
1 
2 /*
3    IEEE error handler for all machines. Since each OS has
4    enough slight differences we have completely separate codes for each one.
5 */
6 
7 /*
8   This feature test macro provides FE_NOMASK_ENV on GNU.  It must be defined
9   at the top of the file because other headers may pull in fenv.h even when
10   not strictly necessary.  Strictly speaking, we could include ONLY petscconf.h,
11   check PETSC_HAVE_FENV_H, and only define _GNU_SOURCE in that case, but such
12   shenanigans ought to be unnecessary.
13 */
14 #if !defined(_GNU_SOURCE)
15   #define _GNU_SOURCE
16 #endif
17 
18 #include <petsc/private/petscimpl.h> /*I  "petscsys.h"  I*/
19 #include <signal.h>
20 
21 struct PetscFPTrapLink {
22   PetscFPTrap             trapmode;
23   struct PetscFPTrapLink *next;
24 };
25 static PetscFPTrap             _trapmode = PETSC_FP_TRAP_OFF; /* Current trapping mode; see PetscDetermineInitialFPTrap() */
26 static struct PetscFPTrapLink *_trapstack;                    /* Any pushed states of _trapmode */
27 
28 /*@
29    PetscFPTrapPush - push a floating point trapping mode, restored using `PetscFPTrapPop()`
30 
31    Not Collective
32 
33    Input Parameter:
34 .    trap - `PETSC_FP_TRAP_ON` or `PETSC_FP_TRAP_OFF` or any of the values passable to `PetscSetFPTrap()`
35 
36    Level: advanced
37 
38    Notes:
39      This only changes the trapping if the new mode is different than the current mode.
40 
41      This routine is called to turn off trapping for certain LAPACK routines that assume that dividing
42      by zero is acceptable. In particular the routine ieeeck().
43 
44      Most systems by default have all trapping turned off, but certain Fortran compilers have
45      link flags that turn on trapping before the program begins.
46 .vb
47        gfortran -ffpe-trap=invalid,zero,overflow,underflow,denormal
48        ifort -fpe0
49 .ve
50 
51 .seealso: `PetscFPTrapPop()`, `PetscSetFPTrap()`, `PetscDetermineInitialFPTrap()`
52 @*/
53 PetscErrorCode PetscFPTrapPush(PetscFPTrap trap)
54 {
55   struct PetscFPTrapLink *link;
56 
57   PetscFunctionBegin;
58   PetscCall(PetscNew(&link));
59 #if defined(PETSC_HAVE_THREADSAFETY) && defined(PETSC_HAVE_OPENMP)
60   PetscPragmaOMP(critical)
61 #endif
62   {
63     link->trapmode = _trapmode;
64     link->next     = _trapstack;
65     _trapstack     = link;
66   }
67   if (trap != _trapmode) PetscCall(PetscSetFPTrap(trap));
68   PetscFunctionReturn(PETSC_SUCCESS);
69 }
70 
71 /*@
72    PetscFPTrapPop - push a floating point trapping mode, to be restored using `PetscFPTrapPop()`
73 
74    Not Collective
75 
76    Level: advanced
77 
78 .seealso: `PetscFPTrapPush()`, `PetscSetFPTrap()`, `PetscDetermineInitialFPTrap()`
79 @*/
80 PetscErrorCode PetscFPTrapPop(void)
81 {
82   struct PetscFPTrapLink *link;
83 
84   PetscFunctionBegin;
85   if (_trapstack->trapmode != _trapmode) PetscCall(PetscSetFPTrap(_trapstack->trapmode));
86 #if defined(PETSC_HAVE_THREADSAFETY) && defined(PETSC_HAVE_OPENMP)
87   PetscPragmaOMP(critical)
88 #endif
89   {
90     link       = _trapstack;
91     _trapstack = _trapstack->next;
92   }
93   PetscCall(PetscFree(link));
94   PetscFunctionReturn(PETSC_SUCCESS);
95 }
96 
97 /*--------------------------------------- ---------------------------------------------------*/
98 #if defined(PETSC_HAVE_SUN4_STYLE_FPTRAP)
99   #include <floatingpoint.h>
100 
101 PETSC_EXTERN PetscErrorCode ieee_flags(char *, char *, char *, char **);
102 PETSC_EXTERN PetscErrorCode ieee_handler(char *, char *, sigfpe_handler_type(int, int, struct sigcontext *, char *));
103 
104 static struct {
105   int   code_no;
106   char *name;
107 } error_codes[] = {
108   {FPE_INTDIV_TRAP,   "integer divide"               },
109   {FPE_FLTOPERR_TRAP, "IEEE operand error"           },
110   {FPE_FLTOVF_TRAP,   "floating point overflow"      },
111   {FPE_FLTUND_TRAP,   "floating point underflow"     },
112   {FPE_FLTDIV_TRAP,   "floating pointing divide"     },
113   {FPE_FLTINEX_TRAP,  "inexact floating point result"},
114   {0,                 "unknown error"                }
115 };
116   #define SIGPC(scp) (scp->sc_pc)
117 
118 /* this function gets called if a trap has occurred and been caught */
119 sigfpe_handler_type PetscDefaultFPTrap(int sig, int code, struct sigcontext *scp, char *addr)
120 {
121   int            err_ind = -1;
122   PetscErrorCode ierr;
123 
124   PetscFunctionBegin;
125   for (int j = 0; error_codes[j].code_no; j++) {
126     if (error_codes[j].code_no == code) err_ind = j;
127   }
128 
129   if (err_ind >= 0) ierr = (*PetscErrorPrintf)("*** %s occurred at pc=%X ***\n", error_codes[err_ind].name, SIGPC(scp));
130   else ierr = (*PetscErrorPrintf)("*** floating point error 0x%x occurred at pc=%X ***\n", code, SIGPC(scp));
131 
132   ierr = PetscError(PETSC_COMM_SELF, PETSC_ERR_FP, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
133   (void)ierr;
134   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
135   PetscFunctionReturn(PETSC_SUCCESS);
136 }
137 
138 /*@
139    PetscSetFPTrap - Enables traps/exceptions on common floating point errors. This option may not work on certain systems or only a
140    subset of exceptions may be trapable.
141 
142    Not Collective
143 
144    Input Parameter:
145 .  flag - values are
146 .vb
147     PETSC_FP_TRAP_OFF   - do not trap any exceptions
148     PETSC_FP_TRAP_ON - all exceptions that are possible on the system except underflow
149     PETSC_FP_TRAP_INDIV - integer divide by zero
150     PETSC_FP_TRAP_FLTOPERR - improper argument to function, for example with real numbers, the square root of a negative number
151     PETSC_FP_TRAP_FLTOVF - overflow
152     PETSC_FP_TRAP_FLTUND - underflow - not trapped by default on most systems
153     PETSC_FP_TRAP_FLTDIV - floating point divide by zero
154     PETSC_FP_TRAP_FLTINEX - inexact floating point result
155 .ve
156 
157    Options Database Key:
158 .  -fp_trap <off,on> - turn on or off trapping of floating point exceptions
159 
160    Level: advanced
161 
162    Notes:
163    Preferred usage is `PetscFPTrapPush()` and `PetscFPTrapPop()` instead of this routine
164 
165    Currently only `PETSC_FP_TRAP_OFF` and `PETSC_FP_TRAP_ON` are handled. All others are treated as `PETSC_FP_TRAP_ON`.
166 
167    The values are bit values and may be |ed together in the function call
168 
169    On systems that support it this routine causes floating point
170    overflow, divide-by-zero, and invalid-operand (e.g., a NaN), but not underflow, to
171    cause a message to be printed and the program to exit.
172 
173    On many common systems, the floating
174    point exception state is not preserved from the location where the trap
175    occurred through to the signal handler.  In this case, the signal handler
176    will just say that an unknown floating point exception occurred and which
177    function it occurred in.  If you run with -fp_trap in a debugger, it will
178    break on the line where the error occurred.  On systems that support C99
179    floating point exception handling You can check which
180    exception occurred using fetestexcept(FE_ALL_EXCEPT).  See fenv.h
181    (usually at /usr/include/bits/fenv.h) for the enum values on your system.
182 
183 .seealso: `PetscFPTrapPush()`, `PetscFPTrapPop()`, `PetscDetermineInitialFPTrap()`
184 @*/
185 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag)
186 {
187   char *out;
188 
189   PetscFunctionBegin;
190   /* Clear accumulated exceptions.  Used to suppress meaningless messages from f77 programs */
191   (void)ieee_flags("clear", "exception", "all", &out);
192   if (flag == PETSC_FP_TRAP_ON) {
193     /*
194       To trap more fp exceptions, including underflow, change the line below to
195       if (ieee_handler("set","all",PetscDefaultFPTrap)) {
196     */
197     if (ieee_handler("set", "common", PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't set floatingpoint handler\n");
198   } else if (ieee_handler("clear", "common", PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't clear floatingpoint handler\n");
199 
200   _trapmode = flag;
201   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_SUN4_STYLE_FPTRAP\n"));
202   PetscFunctionReturn(PETSC_SUCCESS);
203 }
204 
205 /*@
206    PetscDetermineInitialFPTrap - Attempts to determine the floating point trapping that exists when `PetscInitialize()` is called
207 
208    Not Collective
209 
210    Note:
211       Currently only supported on Linux and MacOS. Checks if divide by zero is enable and if so declares that trapping is on.
212 
213    Level: advanced
214 
215 .seealso: `PetscFPTrapPush()`, `PetscFPTrapPop()`, `PetscDetermineInitialFPTrap()`
216 @*/
217 PetscErrorCode PetscDetermineInitialFPTrap(void)
218 {
219   PetscFunctionBegin;
220   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
221   PetscFunctionReturn(PETSC_SUCCESS);
222 }
223 
224 /* -------------------------------------------------------------------------------------------*/
225 #elif defined(PETSC_HAVE_SOLARIS_STYLE_FPTRAP)
226   #include <sunmath.h>
227   #include <floatingpoint.h>
228   #include <siginfo.h>
229   #include <ucontext.h>
230 
231 static struct {
232   int   code_no;
233   char *name;
234 } error_codes[] = {
235   {FPE_FLTINV, "invalid floating point operand"},
236   {FPE_FLTRES, "inexact floating point result" },
237   {FPE_FLTDIV, "division-by-zero"              },
238   {FPE_FLTUND, "floating point underflow"      },
239   {FPE_FLTOVF, "floating point overflow"       },
240   {0,          "unknown error"                 }
241 };
242   #define SIGPC(scp) (scp->si_addr)
243 
244 void PetscDefaultFPTrap(int sig, siginfo_t *scp, ucontext_t *uap)
245 {
246   int            err_ind = -1, code = scp->si_code;
247   PetscErrorCode ierr;
248 
249   PetscFunctionBegin;
250   for (int j = 0; error_codes[j].code_no; j++) {
251     if (error_codes[j].code_no == code) err_ind = j;
252   }
253 
254   if (err_ind >= 0) ierr = (*PetscErrorPrintf)("*** %s occurred at pc=%X ***\n", error_codes[err_ind].name, SIGPC(scp));
255   else ierr = (*PetscErrorPrintf)("*** floating point error 0x%x occurred at pc=%X ***\n", code, SIGPC(scp));
256 
257   ierr = PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
258   (void)ierr;
259   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
260 }
261 
262 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag)
263 {
264   char *out;
265 
266   PetscFunctionBegin;
267   /* Clear accumulated exceptions.  Used to suppress meaningless messages from f77 programs */
268   (void)ieee_flags("clear", "exception", "all", &out);
269   if (flag == PETSC_FP_TRAP_ON) {
270     if (ieee_handler("set", "common", (sigfpe_handler_type)PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't set floating point handler\n");
271   } else {
272     if (ieee_handler("clear", "common", (sigfpe_handler_type)PetscDefaultFPTrap)) (*PetscErrorPrintf)("Can't clear floatingpoint handler\n");
273   }
274   _trapmode = flag;
275   PetscCall(PetscInfo(NULL,"Using PETSC_HAVE_SOLARIS_STYLE_FPTRAP\n");
276   PetscFunctionReturn(PETSC_SUCCESS);
277 }
278 
279 PetscErrorCode PetscDetermineInitialFPTrap(void)
280 {
281   PetscFunctionBegin;
282   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
283   PetscFunctionReturn(PETSC_SUCCESS);
284 }
285 
286 /* ------------------------------------------------------------------------------------------*/
287 #elif defined(PETSC_HAVE_IRIX_STYLE_FPTRAP)
288   #include <sigfpe.h>
289 static struct {
290   int   code_no;
291   char *name;
292 } error_codes[] = {
293   {_INVALID, "IEEE operand error"      },
294   {_OVERFL,  "floating point overflow" },
295   {_UNDERFL, "floating point underflow"},
296   {_DIVZERO, "floating point divide"   },
297   {0,        "unknown error"           }
298 };
299 void PetscDefaultFPTrap(unsigned exception[], int val[])
300 {
301   int            err_ind = -1, code = exception[0];
302   PetscErrorCode ierr;
303 
304   PetscFunctionBegin;
305   for (int j = 0; error_codes[j].code_no; j++) {
306     if (error_codes[j].code_no == code) err_ind = j;
307   }
308   if (err_ind >= 0) ierr = (*PetscErrorPrintf)("*** %s occurred ***\n", error_codes[err_ind].name);
309   else ierr = (*PetscErrorPrintf)("*** floating point error 0x%x occurred ***\n", code);
310 
311   ierr = PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
312   (void)ierr;
313   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
314 }
315 
316 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag)
317 {
318   PetscFunctionBegin;
319   if (flag == PETSC_FP_TRAP_ON) handle_sigfpes(_ON, , _EN_UNDERFL | _EN_OVERFL | _EN_DIVZERO | _EN_INVALID, PetscDefaultFPTrap, _ABORT_ON_ERROR, 0);
320   else handle_sigfpes(_OFF, _EN_UNDERFL | _EN_OVERFL | _EN_DIVZERO | _EN_INVALID, 0, _ABORT_ON_ERROR, 0);
321   _trapmode = flag;
322   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_IRIX_STYLE_FPTRAP\n"));
323   PetscFunctionReturn(PETSC_SUCCESS);
324 }
325 
326 PetscErrorCode PetscDetermineInitialFPTrap(void)
327 {
328   PetscFunctionBegin;
329   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
330   PetscFunctionReturn(PETSC_SUCCESS);
331 }
332 
333 /*----------------------------------------------- --------------------------------------------*/
334 #elif defined(PETSC_HAVE_RS6000_STYLE_FPTRAP)
335 /* In "fast" mode, floating point traps are imprecise and ignored.
336    This is the reason for the fptrap(FP_TRAP_SYNC) call */
337 struct sigcontext;
338   #include <fpxcp.h>
339   #include <fptrap.h>
340   #define FPE_FLTOPERR_TRAP (fptrap_t)(0x20000000)
341   #define FPE_FLTOVF_TRAP   (fptrap_t)(0x10000000)
342   #define FPE_FLTUND_TRAP   (fptrap_t)(0x08000000)
343   #define FPE_FLTDIV_TRAP   (fptrap_t)(0x04000000)
344   #define FPE_FLTINEX_TRAP  (fptrap_t)(0x02000000)
345 
346 static struct {
347   int   code_no;
348   char *name;
349 } error_codes[] = {
350   {FPE_FLTOPERR_TRAP, "IEEE operand error"           },
351   {FPE_FLTOVF_TRAP,   "floating point overflow"      },
352   {FPE_FLTUND_TRAP,   "floating point underflow"     },
353   {FPE_FLTDIV_TRAP,   "floating point divide"        },
354   {FPE_FLTINEX_TRAP,  "inexact floating point result"},
355   < {0,                 "unknown error"                }
356 };
357   #define SIGPC(scp)        (0) /* Info MIGHT be in scp->sc_jmpbuf.jmp_context.iar */
358 /*
359    For some reason, scp->sc_jmpbuf does not work on the RS6000, even though
360    it looks like it should from the include definitions.  It is probably
361    some strange interaction with the "POSIX_SOURCE" that we require.
362 */
363 
364 void PetscDefaultFPTrap(int sig, int code, struct sigcontext *scp)
365 {
366   int            err_ind, j;
367   fp_ctx_t       flt_context;
368   PetscErrorCode ierr;
369 
370   PetscFunctionBegin;
371   fp_sh_trap_info(scp, &flt_context);
372 
373   err_ind = -1;
374   for (j = 0; error_codes[j].code_no; j++) {
375     if (error_codes[j].code_no == flt_context.trap) err_ind = j;
376   }
377 
378   if (err_ind >= 0) ierr = (*PetscErrorPrintf)("*** %s occurred ***\n", error_codes[err_ind].name);
379   else ierr = (*PetscErrorPrintf)("*** floating point error 0x%x occurred ***\n", flt_context.trap);
380 
381   ierr = PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
382   (void)ierr;
383   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
384 }
385 
386 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag)
387 {
388   PetscFunctionBegin;
389   if (flag == PETSC_FP_TRAP_ON) {
390     signal(SIGFPE, (void (*)(int))PetscDefaultFPTrap);
391     fp_trap(FP_TRAP_SYNC);
392     /* fp_enable(TRP_INVALID | TRP_DIV_BY_ZERO | TRP_OVERFLOW | TRP_UNDERFLOW); */
393     fp_enable(TRP_INVALID | TRP_DIV_BY_ZERO | TRP_OVERFLOW);
394   } else {
395     signal(SIGFPE, SIG_DFL);
396     fp_disable(TRP_INVALID | TRP_DIV_BY_ZERO | TRP_OVERFLOW | TRP_UNDERFLOW);
397     fp_trap(FP_TRAP_OFF);
398   }
399   _trapmode = flag;
400   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_RS6000_STYLE_FPTRAP\n"));
401   PetscFunctionReturn(PETSC_SUCCESS);
402 }
403 
404 PetscErrorCode PetscDetermineInitialFPTrap(void)
405 {
406   PetscFunctionBegin;
407   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
408   PetscFunctionReturn(PETSC_SUCCESS);
409 }
410 
411 /* ------------------------------------------------------------*/
412 #elif defined(PETSC_HAVE_WINDOWS_COMPILERS)
413   #include <float.h>
414 void PetscDefaultFPTrap(int sig)
415 {
416   PetscErrorCode ierr;
417 
418   PetscFunctionBegin;
419   ierr = (*PetscErrorPrintf)("*** floating point error occurred ***\n");
420   ierr = PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
421   (void)ierr;
422   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
423 }
424 
425 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag)
426 {
427   unsigned int cw;
428 
429   PetscFunctionBegin;
430   if (flag == PETSC_FP_TRAP_ON) {
431     /* cw = _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW; */
432     cw = _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW;
433     PetscCheck(SIG_ERR != signal(SIGFPE, PetscDefaultFPTrap), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't set floating point handler");
434   } else {
435     cw = 0;
436     PetscCheck(SIG_ERR != signal(SIGFPE, SIG_DFL), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't clear floating point handler");
437   }
438   (void)_controlfp(0, cw);
439   _trapmode = flag;
440   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_WINDOWS_COMPILERS FPTRAP\n"));
441   PetscFunctionReturn(PETSC_SUCCESS);
442 }
443 
444 PetscErrorCode PetscDetermineInitialFPTrap(void)
445 {
446   PetscFunctionBegin;
447   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
448   PetscFunctionReturn(PETSC_SUCCESS);
449 }
450 
451 /* ------------------------------------------------------------*/
452 #elif defined(PETSC_HAVE_FENV_H) && !defined(__cplusplus)
453   /*
454    C99 style floating point environment.
455 
456    Note that C99 merely specifies how to save, restore, and clear the floating
457    point environment as well as defining an enumeration of exception codes.  In
458    particular, C99 does not specify how to make floating point exceptions raise
459    a signal.  Glibc offers this capability through FE_NOMASK_ENV (or with finer
460    granularity, feenableexcept()), xmmintrin.h offers _MM_SET_EXCEPTION_MASK().
461 */
462   #include <fenv.h>
463   #if defined(PETSC_HAVE_FE_VALUES)
464 typedef struct {
465   int         code;
466   const char *name;
467 } FPNode;
468 static const FPNode error_codes[] = {
469   {FE_DIVBYZERO, "divide by zero"                                 },
470   {FE_INEXACT,   "inexact floating point result"                  },
471   {FE_INVALID,   "invalid floating point arguments (domain error)"},
472   {FE_OVERFLOW,  "floating point overflow"                        },
473   {FE_UNDERFLOW, "floating point underflow"                       },
474   {0,            "unknown error"                                  }
475 };
476   #endif
477 
478 void PetscDefaultFPTrap(int sig)
479 {
480   #if defined(PETSC_HAVE_FE_VALUES)
481   const FPNode  *node;
482   int            code;
483   PetscBool      matched = PETSC_FALSE;
484   #endif
485   PetscErrorCode ierr;
486 
487   PetscFunctionBegin;
488   /* Note: While it is possible for the exception state to be preserved by the
489    * kernel, this seems to be rare which makes the following flag testing almost
490    * useless.  But on a system where the flags can be preserved, it would provide
491    * more detail.
492    */
493   #if defined(PETSC_HAVE_FE_VALUES)
494   code = fetestexcept(FE_ALL_EXCEPT);
495   for (node = &error_codes[0]; node->code; node++) {
496     if (code & node->code) {
497       matched = PETSC_TRUE;
498       ierr    = (*PetscErrorPrintf)("*** floating point error \"%s\" occurred ***\n", node->name);
499       code &= ~node->code; /* Unset this flag since it has been processed */
500     }
501   }
502   if (!matched || code) { /* If any remaining flags are set, or we didn't process any flags */
503     ierr = (*PetscErrorPrintf)("*** unknown floating point error occurred ***\n");
504     ierr = (*PetscErrorPrintf)("The specific exception can be determined by running in a debugger.  When the\n");
505     ierr = (*PetscErrorPrintf)("debugger traps the signal, the exception can be found with fetestexcept(0x%x)\n", FE_ALL_EXCEPT);
506     ierr = (*PetscErrorPrintf)("where the result is a bitwise OR of the following flags:\n");
507     ierr = (*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);
508   }
509   #else
510   ierr = (*PetscErrorPrintf)("*** unknown floating point error occurred ***\n");
511   #endif
512 
513   ierr = (*PetscErrorPrintf)("Try option -start_in_debugger\n");
514   #if PetscDefined(USE_DEBUG)
515     #if !PetscDefined(HAVE_THREADSAFETY)
516   ierr = (*PetscErrorPrintf)("likely location of problem given in stack below\n");
517   ierr = (*PetscErrorPrintf)("---------------------  Stack Frames ------------------------------------\n");
518   ierr = PetscStackView(PETSC_STDOUT);
519     #endif
520   #else
521   ierr = (*PetscErrorPrintf)("configure using --with-debugging=yes, recompile, link, and run \n");
522   ierr = (*PetscErrorPrintf)("with -start_in_debugger to get more information on the crash.\n");
523   #endif
524   ierr = PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_INITIAL, "trapped floating point error");
525   (void)ierr;
526   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
527 }
528 
529 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag)
530 {
531   PetscFunctionBegin;
532   if (flag == PETSC_FP_TRAP_ON) {
533     /* Clear any flags that are currently set so that activating trapping will not immediately call the signal handler. */
534     PetscCheck(!feclearexcept(FE_ALL_EXCEPT), PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot clear floating point exception flags");
535   #if defined(FE_NOMASK_ENV) && defined(PETSC_HAVE_FE_VALUES)
536     /* Could use fesetenv(FE_NOMASK_ENV), but that causes spurious exceptions (like gettimeofday() -> PetscLogDouble). */
537     /* PetscCheck(feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) != -1,PETSC_COMM_SELF,PETSC_ERR_LIB,"Cannot activate floating point exceptions"); */
538     /* Doesn't work on AArch64 targets. There's a known hardware limitation. Need to detect hardware at configure time? */
539     PetscCheck(feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW) != -1, PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot activate floating point exceptions");
540     PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_FENV_H FPTRAP with FE_NOMASK_ENV\n"));
541   #elif defined PETSC_HAVE_XMMINTRIN_H
542     _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~_MM_MASK_DIV_ZERO);
543     /* _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~_MM_MASK_UNDERFLOW); */
544     _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~_MM_MASK_OVERFLOW);
545     _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() & ~_MM_MASK_INVALID);
546     PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_FENV_H FPTRAP with PETSC_HAVE_XMMINTRIN_H\n"));
547   #else
548     /* C99 does not provide a way to modify the environment so there is no portable way to activate trapping. */
549     PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_FENV_H FPTRAP\n"));
550   #endif
551     PetscCheck(SIG_ERR != signal(SIGFPE, PetscDefaultFPTrap), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't set floating point handler");
552   } else {
553     PetscCheck(!fesetenv(FE_DFL_ENV), PETSC_COMM_SELF, PETSC_ERR_LIB, "Cannot disable floating point exceptions");
554     /* can use _MM_SET_EXCEPTION_MASK(_MM_GET_EXCEPTION_MASK() | _MM_MASK_UNDERFLOW); if PETSC_HAVE_XMMINTRIN_H exists */
555     PetscCheck(SIG_ERR != signal(SIGFPE, SIG_DFL), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't clear floating point handler");
556   }
557   _trapmode = flag;
558   PetscFunctionReturn(PETSC_SUCCESS);
559 }
560 
561 PetscErrorCode PetscDetermineInitialFPTrap(void)
562 {
563   #if defined(FE_NOMASK_ENV) || defined PETSC_HAVE_XMMINTRIN_H
564   unsigned int flags;
565   #endif
566 
567   PetscFunctionBegin;
568   #if defined(FE_NOMASK_ENV)
569   flags = fegetexcept();
570   if (flags & FE_DIVBYZERO) {
571   #elif defined PETSC_HAVE_XMMINTRIN_H
572   flags = _MM_GET_EXCEPTION_MASK();
573   if (!(flags & _MM_MASK_DIV_ZERO)) {
574   #else
575   PetscCall(PetscInfo(NULL, "Floating point trapping unknown, assuming off\n"));
576   PetscFunctionReturn(PETSC_SUCCESS);
577   #endif
578   #if defined(FE_NOMASK_ENV) || defined PETSC_HAVE_XMMINTRIN_H
579     _trapmode = PETSC_FP_TRAP_ON;
580     PetscCall(PetscInfo(NULL, "Floating point trapping is on by default %d\n", flags));
581   } else {
582     _trapmode = PETSC_FP_TRAP_OFF;
583     PetscCall(PetscInfo(NULL, "Floating point trapping is off by default %d\n", flags));
584   }
585   PetscFunctionReturn(PETSC_SUCCESS);
586   #endif
587 }
588 
589 /* ------------------------------------------------------------*/
590 #elif defined(PETSC_HAVE_IEEEFP_H)
591   #include <ieeefp.h>
592 void PetscDefaultFPTrap(int sig)
593 {
594   PetscErrorCode ierr;
595 
596   PetscFunctionBegin;
597   ierr = (*PetscErrorPrintf)("*** floating point error occurred ***\n");
598   ierr = PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
599   (void)ierr;
600   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
601 }
602 
603 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag)
604 {
605   PetscFunctionBegin;
606   if (flag == PETSC_FP_TRAP_ON) {
607   #if defined(PETSC_HAVE_FPRESETSTICKY)
608     fpresetsticky(fpgetsticky());
609   #elif defined(PETSC_HAVE_FPSETSTICKY)
610     fpsetsticky(fpgetsticky());
611   #endif
612     fpsetmask(FP_X_INV | FP_X_DZ | FP_X_OFL | FP_X_OFL);
613     PetscCheck(SIG_ERR != signal(SIGFPE, PetscDefaultFPTrap), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't set floating point handler");
614   } else {
615   #if defined(PETSC_HAVE_FPRESETSTICKY)
616     fpresetsticky(fpgetsticky());
617   #elif defined(PETSC_HAVE_FPSETSTICKY)
618     fpsetsticky(fpgetsticky());
619   #endif
620     fpsetmask(0);
621     PetscCheck(SIG_ERR != signal(SIGFPE, SIG_DFL), PETSC_COMM_SELF, PETSC_ERR_LIB, "Can't clear floating point handler");
622   }
623   _trapmode = flag;
624   PetscCall(PetscInfo(NULL, "Using PETSC_HAVE_IEEEFP_H FPTRAP\n"));
625   PetscFunctionReturn(PETSC_SUCCESS);
626 }
627 
628 PetscErrorCode PetscDetermineInitialFPTrap(void)
629 {
630   PetscFunctionBegin;
631   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
632   PetscFunctionReturn(PETSC_SUCCESS);
633 }
634 
635 /* -------------------------Default -----------------------------------*/
636 #else
637 
638 void PetscDefaultFPTrap(int sig)
639 {
640   PetscErrorCode ierr;
641 
642   PetscFunctionBegin;
643   ierr = (*PetscErrorPrintf)("*** floating point error occurred ***\n");
644   ierr = PetscError(PETSC_COMM_SELF, 0, NULL, NULL, PETSC_ERR_FP, PETSC_ERROR_REPEAT, "floating point error");
645   (void)ierr;
646   PETSCABORT(MPI_COMM_WORLD, PETSC_ERR_FP);
647 }
648 
649 PetscErrorCode PetscSetFPTrap(PetscFPTrap flag)
650 {
651   PetscFunctionBegin;
652   if (flag == PETSC_FP_TRAP_ON) {
653     if (SIG_ERR == signal(SIGFPE, PetscDefaultFPTrap)) PetscCall((*PetscErrorPrintf)("Can't set floatingpoint handler\n"));
654   } else if (SIG_ERR == signal(SIGFPE, SIG_DFL)) PetscCall((*PetscErrorPrintf)("Can't clear floatingpoint handler\n"));
655 
656   _trapmode = flag;
657   PetscCall(PetscInfo(NULL, "Using default FPTRAP\n"));
658   PetscFunctionReturn(PETSC_SUCCESS);
659 }
660 
661 PetscErrorCode PetscDetermineInitialFPTrap(void)
662 {
663   PetscFunctionBegin;
664   PetscCall(PetscInfo(NULL, "Unable to determine initial floating point trapping. Assuming it is off\n"));
665   PetscFunctionReturn(PETSC_SUCCESS);
666 }
667 #endif
668