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