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