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