dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libm / common / m9x / fex_log.c
blob2a32a12fbf2bfe3b763f69ffc3736213b25c7d31
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <string.h>
40 #include <signal.h>
41 #include <ucontext.h>
42 #include <sys/frame.h>
43 #include <fenv.h>
44 #include <sys/ieeefp.h>
45 #include <thread.h>
46 #include "fex_handler.h"
48 #if !defined(PC)
49 #if defined(REG_PC)
50 #define PC REG_PC
51 #else
52 #error Neither PC nor REG_PC is defined!
53 #endif
54 #endif
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)
62 FILE *fp;
64 mutex_lock(&log_lock);
65 fp = log_fp;
66 mutex_unlock(&log_lock);
67 return fp;
70 int fex_set_log(FILE *fp)
72 mutex_lock(&log_lock);
73 log_fp = fp;
74 mutex_unlock(&log_lock);
75 __fex_update_te();
76 return 1;
79 int fex_get_log_depth(void)
81 int d;
83 mutex_lock(&log_lock);
84 d = log_depth;
85 mutex_unlock(&log_lock);
86 return d;
89 int fex_set_log_depth(int d)
91 if (d < 0)
92 return 0;
93 mutex_lock(&log_lock);
94 log_depth = d;
95 mutex_unlock(&log_lock);
96 return 1;
99 static struct exc_list {
100 struct exc_list *next;
101 char *addr;
102 unsigned long code;
103 int nstack;
104 char *stack[1]; /* actual length is max(1,nstack) */
105 } *list = NULL;
107 #define FRAMEP(X) (struct frame *)(X)
109 #ifdef _LP64
110 #define PDIG "16"
111 #else
112 #define PDIG "8"
113 #endif
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,
118 struct frame *fp)
120 struct exc_list *l, *ll = NULL;
121 struct frame *f;
122 int i, n;
124 if (list) {
125 for (l = list; l; ll = l, l = l->next) {
126 if (l->addr != addr || l->code != code)
127 continue;
128 if (log_depth < 1 || l->nstack < 1)
129 return 1;
130 if (l->stack[0] != stk)
131 continue;
132 n = 1;
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) {
136 n = 0;
137 break;
139 if (n)
140 return 1;
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) {
149 l->next = NULL;
150 l->addr = addr;
151 l->code = code;
152 l->nstack = ((log_depth < 1)? 0 : n);
153 l->stack[0] = stk;
154 for (i = 1; i < n; i++) {
155 l->stack[i] = (char *)fp->fr_savpc;
156 fp = FRAMEP(fp->fr_savfp);
158 if (list)
159 ll->next = l;
160 else
161 list = l;
163 return 0;
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:
180 * Thread A Thread B
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)
195 int i;
196 char *name, buf[30];
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 ",
201 (long)addr));
202 write(fd, name, strlen(name));
203 write(fd, "\n", 1);
204 if (!strcmp(name, "main"))
205 break;
206 } else {
207 write(fd, buf, sprintf(buf, " 0x%0" PDIG "lx\n",
208 (long)addr));
210 if (fp == NULL)
211 break;
212 addr = (char *)fp->fr_savpc;
213 fp = FRAMEP(fp->fr_savfp);
217 void fex_log_entry(const char *msg)
219 ucontext_t uc;
220 struct frame *fp;
221 char *stk;
222 int fd;
224 /* if logging is disabled, just return */
225 mutex_lock(&log_lock);
226 if (log_fp == NULL) {
227 mutex_unlock(&log_lock);
228 return;
231 /* get the frame pointer from the current context and
232 pop our own frame */
233 getcontext(&uc);
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]);
238 #else
239 #error Unknown architecture
240 #endif
241 if (fp == NULL) {
242 mutex_unlock(&log_lock);
243 return;
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);
251 return;
254 /* make an entry */
255 fd = fileno(log_fp);
256 write(fd, "fex_log_entry: ", 15);
257 write(fd, msg, strlen(msg));
258 write(fd, "\n", 1);
259 __fex_sym_init();
260 print_stack(fd, stk, fp);
261 mutex_unlock(&log_lock);
264 static const char *exception[FEX_NUM_EXC] = {
265 "inexact result",
266 "division by zero",
267 "underflow",
268 "overflow",
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)"
279 void
280 __fex_mklog(ucontext_t *uap, char *addr, int f, enum fex_exception e,
281 int m, void *p)
283 struct frame *fp;
284 char *stk, *name, buf[30];
285 int fd;
287 /* if logging is disabled, just return */
288 mutex_lock(&log_lock);
289 if (log_fp == NULL) {
290 mutex_unlock(&log_lock);
291 return;
294 /* get stack info */
295 #if defined(__sparc)
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]);
304 #else
305 #error Unknown architecture
306 #endif
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) {
311 switch (e) {
312 case fex_inexact:
313 if (f & FE_INEXACT) {
314 mutex_unlock(&log_lock);
315 return;
317 break;
318 case fex_underflow:
319 if (f & FE_UNDERFLOW) {
320 mutex_unlock(&log_lock);
321 return;
323 break;
324 case fex_overflow:
325 if (f & FE_OVERFLOW) {
326 mutex_unlock(&log_lock);
327 return;
329 break;
330 case fex_division:
331 if (f & FE_DIVBYZERO) {
332 mutex_unlock(&log_lock);
333 return;
335 break;
336 default:
337 if (f & FE_INVALID) {
338 mutex_unlock(&log_lock);
339 return;
341 break;
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);
349 return;
352 /* make an entry */
353 fd = fileno(log_fp);
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));
357 __fex_sym_init();
358 if (__fex_sym(addr, &name) != NULL) {
359 write(fd, " ", 1);
360 write(fd, name, strlen(name));
362 switch (m) {
363 case FEX_NONSTOP:
364 write(fd, ", nonstop mode\n", 15);
365 break;
367 case FEX_ABORT:
368 write(fd, ", abort\n", 8);
369 break;
371 case FEX_NOHANDLER:
372 if (p == (void *)SIG_DFL) {
373 write(fd, ", handler: SIG_DFL\n", 19);
374 break;
376 else if (p == (void *)SIG_IGN) {
377 write(fd, ", handler: SIG_IGN\n", 19);
378 break;
380 /* fall through*/
381 default:
382 write(fd, ", handler: ", 11);
383 if (__fex_sym((char *)p, &name) != NULL) {
384 write(fd, name, strlen(name));
385 write(fd, "\n", 1);
386 } else {
387 write(fd, buf, sprintf(buf, "0x%0" PDIG "lx\n",
388 (long)p));
390 break;
392 print_stack(fd, stk, fp);
393 mutex_unlock(&log_lock);