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