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