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