1 /* Copyright (C) 2000-2005 Claus-Justus Heine
2  *                         <claus@mathematik.uni-freiburg.de>
3  */
4 #ifdef HAVE_CONFIG_H
5 # include <config.h>
6 #endif
7 
8 #ifndef _GNU_SOURCE
9 # define _GNU_SOURCE
10 #endif
11 
12 #include <stdio.h>
13 #include <string.h>
14 #include <fenv.h>
15 #include <signal.h>
16 #include <stdlib.h>
17 
18 #ifndef MSG_TRIGGER
19 # define MSG_TRIGGER 1000000
20 #endif
21 
22 static int count;
23 
24 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION
25 
fpe_sigaction_strerr(int code,char * buffer)26 static const char *fpe_sigaction_strerr(int code, char *buffer)
27 {
28 #define CASE_CPERRSTR(arg, descr) \
29 	case arg: sprintf(buffer, #arg"(%d): \""descr"\"", arg); break
30 #define FPE_NONE 0
31   switch (code) {
32     CASE_CPERRSTR(FPE_NONE,   "no error");
33     CASE_CPERRSTR(FPE_INTDIV, "integer divide by zero");
34     CASE_CPERRSTR(FPE_INTOVF, "integer overflow");
35     CASE_CPERRSTR(FPE_FLTDIV, "floating point divide by zero");
36     CASE_CPERRSTR(FPE_FLTOVF, "floating point overflow");
37     CASE_CPERRSTR(FPE_FLTUND, "floating point underflow");
38     CASE_CPERRSTR(FPE_FLTRES, "floating point inexact result");
39     CASE_CPERRSTR(FPE_FLTINV, "floating point invalid operation");
40     CASE_CPERRSTR(FPE_FLTSUB, "subscript out of range");
41   }
42   return buffer;
43 }
44 
sigfpe_handler(int sig,siginfo_t * sinfo,void * p)45 static void sigfpe_handler(int sig, siginfo_t *sinfo, void *p)
46 {
47   char buffer[1024];
48 
49   if ((count ++) % MSG_TRIGGER == 0) {
50     fprintf(stderr,
51 	    "Caught floating point exception: %s. Total number %d\n",
52 	    fpe_sigaction_strerr(sinfo->si_code, buffer), count);
53     fprintf(stderr, "Faulting address: @%p, my address: @%p\n",
54 	    sinfo->si_addr, (void *)(unsigned long)sigfpe_handler);
55   }
56 #if 0
57   /* how to clear the error???
58    *
59    * As the signal handler starts with a clean FPU environment, it
60    * just doesn't help a bit to clear the error state here, i.e. to
61    * write directly to the FPU-registers. It is possible to modify the
62    * signal stack, so that the fpu-error state is cleared on return
63    * from the signal handler. This is -- of course -- not the nice way
64    * to do it.
65    *
66    * Acutally, the only useful solution would be to parse the opcode
67    * at si_addr and it's operands and then to decide what to do.
68    *
69    * We could shut-up the xmm unit by masking all exceptions.
70    */
71 #else
72   /* doesn't make any sense, as the insulting instruction is just
73    * executed again if we do not do anything 'bout it, i.e. do a
74    * longjmp() to a defined code-place or something like that.
75    */
76   exit(1);
77 #endif
78 }
79 
80 #else /* old signal handler */
81 
fpe_strerr(int raised,char * buffer)82 static const char *fpe_strerr(int raised, char *buffer)
83 {
84   *buffer = '\0';
85 
86 #ifdef FE_INEXACT
87   if (raised & FE_INEXACT) {
88     strcat(buffer, "FE_INEXACT ");
89   }
90 #endif
91 #ifdef FE_DIVBYZERO
92   if (raised & FE_DIVBYZERO) {
93     strcat(buffer, "FE_DIVBYZERO ");
94   }
95 #endif
96 #ifdef FE_UNDERFLOW
97   if (raised & FE_UNDERFLOW) {
98     strcat(buffer, "FE_UNDERFLOW ");
99   }
100 #endif
101 #ifdef FE_OVERFLOW
102   if (raised & FE_OVERFLOW) {
103     strcat(buffer, "FE_OVERFLOW ");
104   }
105 #endif
106 #ifdef FE_INVALID
107   if (raised & FE_INVALID) {
108     strcat(buffer, "FE_INVALID ");
109   }
110 #endif
111   if (*buffer != '\0') {
112     buffer[strlen(buffer)-1] = '\0';
113   }
114   return buffer;
115 }
116 
sigfpe_handler(int sig)117 static void sigfpe_handler(int sig)
118 {
119   int raised;
120   char buffer[1024];
121 
122   raised = fetestexcept(FE_ALL_EXCEPT);
123   if ((count ++) % MSG_TRIGGER == 0) {
124     fprintf(stderr,
125 	    "Caught floating point exception: %s (0x%04x). Total number %d\n",
126 	    fpe_strerr(raised, buffer), raised, count);
127   }
128   /* Doesn't help, see comments for sigaction SIGFPE-handler.
129    */
130   feclearexcept(FE_ALL_EXCEPT);
131 }
132 #endif
133 
sigfpe_init(int unmask)134 void sigfpe_init(int unmask)
135 {
136   struct sigaction fpe_sigaction;
137   sigset_t signals;
138 
139   memset(&fpe_sigaction, 0, sizeof(struct sigaction));
140 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION
141   fpe_sigaction.sa_sigaction = sigfpe_handler;
142   fpe_sigaction.sa_flags = SA_RESTART|SA_SIGINFO;
143 #else
144   fpe_sigaction.sa_handler = sigfpe_handler;
145   fpe_sigaction.sa_flags = SA_RESTART;
146 #endif
147   sigemptyset(&fpe_sigaction.sa_mask);
148 
149   sigaction(SIGFPE, &fpe_sigaction, NULL);
150 
151   fesetenv(FE_DFL_ENV);
152   if (unmask) {
153     feclearexcept(FE_ALL_EXCEPT);
154 #ifdef FE_NOMASK_ENV
155     feenableexcept(FE_ALL_EXCEPT);
156     fedisableexcept(FE_INEXACT);
157 #endif
158   }
159 
160   /* unblock SIGFPE, unnecessary 'cause it is unblocked by default */
161   sigemptyset(&signals);
162   sigaddset(&signals, SIGFPE);
163   sigprocmask(SIG_UNBLOCK, &signals, NULL);
164 }
165 
166 #if TEST_SIGFPE
167 
168 volatile double foobar = 0.0;
169 
exit_handler(int signo)170 static void exit_handler(int signo)
171 {
172   fprintf(stderr, "Caught signal %d\n", signo);
173   exit(1);
174 }
175 
print_count(void)176 static void print_count(void)
177 {
178   fprintf(stderr, "Final floating point error count: %d\n", count);
179 }
180 
main(int argc,char * argv[])181 int main(int argc, char *argv[])
182 {
183   atexit(print_count);
184   signal(SIGTERM, exit_handler);
185   signal(SIGKILL, exit_handler);
186   signal(SIGINT, exit_handler);
187   sigfpe_init(1);
188   feenableexcept(FE_ALL_EXCEPT);
189   foobar = 1.0/foobar;
190   pause();
191 }
192 #endif
193 
194 /*
195  * Local variables: ***
196  *  c-basic-offset: 2 ***
197  *  compile-command: "make CFLAGS='-DTEST_SIGFPE=1 -DHAVE_STRUCT_SIGACTION_SA_SIGACTION=1 -DHAVE_ASM_I386_UCONTEXT_H=1 -DMSG_TRIGGER=1000000 -D_GNU_SOURCE -march=pentium4 -mfpmath=sse' LDFLAGS='-lm' sigfpe" ***
198  * End: ***
199  */
200