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