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