4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
30 #pragma weak __fex_get_log = fex_get_log
31 #pragma weak __fex_set_log = fex_set_log
32 #pragma weak __fex_get_log_depth = fex_get_log_depth
33 #pragma weak __fex_set_log_depth = fex_set_log_depth
34 #pragma weak __fex_log_entry = fex_log_entry
42 #include <sys/frame.h>
44 #include <sys/ieeefp.h>
46 #include "fex_handler.h"
52 #error Neither PC nor REG_PC is defined!
56 static FILE *log_fp
= NULL
;
57 static mutex_t log_lock
= DEFAULTMUTEX
;
58 static int log_depth
= 100;
60 FILE *fex_get_log(void)
64 mutex_lock(&log_lock
);
66 mutex_unlock(&log_lock
);
70 int fex_set_log(FILE *fp
)
72 mutex_lock(&log_lock
);
74 mutex_unlock(&log_lock
);
79 int fex_get_log_depth(void)
83 mutex_lock(&log_lock
);
85 mutex_unlock(&log_lock
);
89 int fex_set_log_depth(int d
)
93 mutex_lock(&log_lock
);
95 mutex_unlock(&log_lock
);
99 static struct exc_list
{
100 struct exc_list
*next
;
104 char *stack
[1]; /* actual length is max(1,nstack) */
107 #define FRAMEP(X) (struct frame *)(X)
115 /* look for a matching exc_list; return 1 if one is found,
116 otherwise add this one to the list and return 0 */
117 static int check_exc_list(char *addr
, unsigned long code
, char *stk
,
120 struct exc_list
*l
, *ll
= NULL
;
125 for (l
= list
; l
; ll
= l
, l
= l
->next
) {
126 if (l
->addr
!= addr
|| l
->code
!= code
)
128 if (log_depth
< 1 || l
->nstack
< 1)
130 if (l
->stack
[0] != stk
)
133 for (i
= 1, f
= fp
; i
< log_depth
&& i
< l
->nstack
&&
134 f
&& f
->fr_savpc
; i
++, f
= FRAMEP(f
->fr_savfp
))
135 if (l
->stack
[i
] != (char *)f
->fr_savpc
) {
144 /* create a new exc_list structure and tack it on the list */
145 for (n
= 1, f
= fp
; n
< log_depth
&& f
&& f
->fr_savpc
;
146 n
++, f
= FRAMEP(f
->fr_savfp
)) ;
147 if ((l
= (struct exc_list
*)malloc(sizeof(struct exc_list
) +
148 (n
- 1) * sizeof(char *))) != NULL
) {
152 l
->nstack
= ((log_depth
< 1)? 0 : n
);
154 for (i
= 1; i
< n
; i
++) {
155 l
->stack
[i
] = (char *)fp
->fr_savpc
;
156 fp
= FRAMEP(fp
->fr_savfp
);
167 * Warning: cleverness ahead
169 * In the following code, the use of sprintf+write rather than fprintf
170 * to send output to the log file is intentional. The reason is that
171 * fprintf is not async-signal-safe. "But," you protest, "SIGFPE is
172 * not an asynchronous signal! It's always handled by the same thread
173 * that executed the fpop that provoked it." That's true, but a prob-
174 * lem arises because (i) base conversion in fprintf can cause a fp
175 * exception and (ii) my signal handler acquires a mutex lock before
176 * sending output to the log file (so that outputs for entries from
177 * different threads aren't interspersed). Therefore, if the code
178 * were to use fprintf, a deadlock could occur as follows:
182 * Incurs a fp exception, Calls fprintf,
183 * acquires log_lock acquires file rmutex lock
185 * Calls fprintf, Incurs a fp exception,
186 * waits for file rmutex lock waits for log_lock
188 * (I could just verify that fprintf doesn't hold the rmutex lock while
189 * it's doing the base conversion, but since efficiency is of little
190 * concern here, I opted for the safe and dumb route.)
193 static void print_stack(int fd
, char *addr
, struct frame
*fp
)
198 for (i
= 0; i
< log_depth
&& addr
!= NULL
; i
++) {
199 if (__fex_sym(addr
, &name
) != NULL
) {
200 write(fd
, buf
, sprintf(buf
, " 0x%0" PDIG
"lx ",
202 write(fd
, name
, strlen(name
));
204 if (!strcmp(name
, "main"))
207 write(fd
, buf
, sprintf(buf
, " 0x%0" PDIG
"lx\n",
212 addr
= (char *)fp
->fr_savpc
;
213 fp
= FRAMEP(fp
->fr_savfp
);
217 void fex_log_entry(const char *msg
)
224 /* if logging is disabled, just return */
225 mutex_lock(&log_lock
);
226 if (log_fp
== NULL
) {
227 mutex_unlock(&log_lock
);
231 /* get the frame pointer from the current context and
234 #if defined(__sparc) || defined(__amd64)
235 fp
= FRAMEP(uc
.uc_mcontext
.gregs
[REG_SP
]);
236 #elif defined(__i386) /* !defined(__amd64) */
237 fp
= FRAMEP(uc
.uc_mcontext
.gregs
[EBP
]);
239 #error Unknown architecture
242 mutex_unlock(&log_lock
);
245 stk
= (char *)fp
->fr_savpc
;
246 fp
= FRAMEP(fp
->fr_savfp
);
248 /* if we've already logged this message here, don't make an entry */
249 if (check_exc_list(stk
, (unsigned long)msg
, stk
, fp
)) {
250 mutex_unlock(&log_lock
);
256 write(fd
, "fex_log_entry: ", 15);
257 write(fd
, msg
, strlen(msg
));
260 print_stack(fd
, stk
, fp
);
261 mutex_unlock(&log_lock
);
264 static const char *exception
[FEX_NUM_EXC
] = {
269 "invalid operation (0/0)",
270 "invalid operation (inf/inf)",
271 "invalid operation (inf-inf)",
272 "invalid operation (0*inf)",
273 "invalid operation (sqrt)",
274 "invalid operation (snan)",
275 "invalid operation (int)",
276 "invalid operation (cmp)"
280 __fex_mklog(ucontext_t
*uap
, char *addr
, int f
, enum fex_exception e
,
284 char *stk
, *name
, buf
[30];
287 /* if logging is disabled, just return */
288 mutex_lock(&log_lock
);
289 if (log_fp
== NULL
) {
290 mutex_unlock(&log_lock
);
296 stk
= (char*)uap
->uc_mcontext
.gregs
[REG_PC
];
297 fp
= FRAMEP(uap
->uc_mcontext
.gregs
[REG_SP
]);
298 #elif defined(__amd64)
299 stk
= (char*)uap
->uc_mcontext
.gregs
[REG_PC
];
300 fp
= FRAMEP(uap
->uc_mcontext
.gregs
[REG_RBP
]);
301 #elif defined(__i386) /* !defined(__amd64) */
302 stk
= (char*)uap
->uc_mcontext
.gregs
[PC
];
303 fp
= FRAMEP(uap
->uc_mcontext
.gregs
[EBP
]);
305 #error Unknown architecture
308 /* if the handling mode is the default and this exception's
309 flag is already raised, don't make an entry */
310 if (m
== FEX_NONSTOP
) {
313 if (f
& FE_INEXACT
) {
314 mutex_unlock(&log_lock
);
319 if (f
& FE_UNDERFLOW
) {
320 mutex_unlock(&log_lock
);
325 if (f
& FE_OVERFLOW
) {
326 mutex_unlock(&log_lock
);
331 if (f
& FE_DIVBYZERO
) {
332 mutex_unlock(&log_lock
);
337 if (f
& FE_INVALID
) {
338 mutex_unlock(&log_lock
);
345 /* if we've already logged this exception at this address,
346 don't make an entry */
347 if (check_exc_list(addr
, (unsigned long)e
, stk
, fp
)) {
348 mutex_unlock(&log_lock
);
354 write(fd
, "Floating point ", 15);
355 write(fd
, exception
[e
], strlen(exception
[e
]));
356 write(fd
, buf
, sprintf(buf
, " at 0x%0" PDIG
"lx", (long)addr
));
358 if (__fex_sym(addr
, &name
) != NULL
) {
360 write(fd
, name
, strlen(name
));
364 write(fd
, ", nonstop mode\n", 15);
368 write(fd
, ", abort\n", 8);
372 if (p
== (void *)SIG_DFL
) {
373 write(fd
, ", handler: SIG_DFL\n", 19);
376 else if (p
== (void *)SIG_IGN
) {
377 write(fd
, ", handler: SIG_IGN\n", 19);
382 write(fd
, ", handler: ", 11);
383 if (__fex_sym((char *)p
, &name
) != NULL
) {
384 write(fd
, name
, strlen(name
));
387 write(fd
, buf
, sprintf(buf
, "0x%0" PDIG
"lx\n",
392 print_stack(fd
, stk
, fp
);
393 mutex_unlock(&log_lock
);