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