diff options
Diffstat (limited to 'usr/src/lib/libm/common/m9x/fex_log.c')
-rw-r--r-- | usr/src/lib/libm/common/m9x/fex_log.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/usr/src/lib/libm/common/m9x/fex_log.c b/usr/src/lib/libm/common/m9x/fex_log.c new file mode 100644 index 0000000000..6840719fae --- /dev/null +++ b/usr/src/lib/libm/common/m9x/fex_log.c @@ -0,0 +1,399 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma weak fex_get_log = __fex_get_log +#pragma weak fex_set_log = __fex_set_log +#pragma weak fex_get_log_depth = __fex_get_log_depth +#pragma weak fex_set_log_depth = __fex_set_log_depth +#pragma weak fex_log_entry = __fex_log_entry + +#include "fenv_synonyms.h" +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <ucontext.h> +#include <sys/frame.h> +#include <fenv.h> +#include <sys/ieeefp.h> +#include <thread.h> +#include "fex_handler.h" + +#if !defined(PC) +#if defined(REG_PC) +#define PC REG_PC +#else +#error Neither PC nor REG_PC is defined! +#endif +#endif + +static FILE *log_fp = NULL; +static mutex_t log_lock = DEFAULTMUTEX; +static int log_depth = 100; + +FILE *fex_get_log(void) +{ + FILE *fp; + + mutex_lock(&log_lock); + fp = log_fp; + mutex_unlock(&log_lock); + return fp; +} + +int fex_set_log(FILE *fp) +{ + mutex_lock(&log_lock); + log_fp = fp; + mutex_unlock(&log_lock); + __fex_update_te(); + return 1; +} + +int fex_get_log_depth(void) +{ + int d; + + mutex_lock(&log_lock); + d = log_depth; + mutex_unlock(&log_lock); + return d; +} + +int fex_set_log_depth(int d) +{ + if (d < 0) + return 0; + mutex_lock(&log_lock); + log_depth = d; + mutex_unlock(&log_lock); + return 1; +} + +static struct exc_list { + struct exc_list *next; + char *addr; + unsigned long code; + int nstack; + char *stack[1]; /* actual length is max(1,nstack) */ +} *list = NULL; + +#ifdef __sparcv9 +#define FRAMEP(X) (struct frame *)((char*)(X)+(((long)(X)&1)?2047:0)) +#else +#define FRAMEP(X) (struct frame *)(X) +#endif + +#ifdef _LP64 +#define PDIG "16" +#else +#define PDIG "8" +#endif + +/* look for a matching exc_list; return 1 if one is found, + otherwise add this one to the list and return 0 */ +static int check_exc_list(char *addr, unsigned long code, char *stk, + struct frame *fp) +{ + struct exc_list *l, *ll = NULL; + struct frame *f; + int i, n; + + if (list) { + for (l = list; l; ll = l, l = l->next) { + if (l->addr != addr || l->code != code) + continue; + if (log_depth < 1 || l->nstack < 1) + return 1; + if (l->stack[0] != stk) + continue; + n = 1; + for (i = 1, f = fp; i < log_depth && i < l->nstack && + f && f->fr_savpc; i++, f = FRAMEP(f->fr_savfp)) + if (l->stack[i] != (char *)f->fr_savpc) { + n = 0; + break; + } + if (n) + return 1; + } + } + + /* create a new exc_list structure and tack it on the list */ + for (n = 1, f = fp; n < log_depth && f && f->fr_savpc; + n++, f = FRAMEP(f->fr_savfp)) ; + if ((l = (struct exc_list *)malloc(sizeof(struct exc_list) + + (n - 1) * sizeof(char *))) != NULL) { + l->next = NULL; + l->addr = addr; + l->code = code; + l->nstack = ((log_depth < 1)? 0 : n); + l->stack[0] = stk; + for (i = 1; i < n; i++) { + l->stack[i] = (char *)fp->fr_savpc; + fp = FRAMEP(fp->fr_savfp); + } + if (list) + ll->next = l; + else + list = l; + } + return 0; +} + +/* +* Warning: cleverness ahead +* +* In the following code, the use of sprintf+write rather than fprintf +* to send output to the log file is intentional. The reason is that +* fprintf is not async-signal-safe. "But," you protest, "SIGFPE is +* not an asynchronous signal! It's always handled by the same thread +* that executed the fpop that provoked it." That's true, but a prob- +* lem arises because (i) base conversion in fprintf can cause a fp +* exception and (ii) my signal handler acquires a mutex lock before +* sending output to the log file (so that outputs for entries from +* different threads aren't interspersed). Therefore, if the code +* were to use fprintf, a deadlock could occur as follows: +* +* Thread A Thread B +* +* Incurs a fp exception, Calls fprintf, +* acquires log_lock acquires file rmutex lock +* +* Calls fprintf, Incurs a fp exception, +* waits for file rmutex lock waits for log_lock +* +* (I could just verify that fprintf doesn't hold the rmutex lock while +* it's doing the base conversion, but since efficiency is of little +* concern here, I opted for the safe and dumb route.) +*/ + +static void print_stack(int fd, char *addr, struct frame *fp) +{ + int i; + char *name, buf[30]; + + for (i = 0; i < log_depth && addr != NULL; i++) { + if (__fex_sym(addr, &name) != NULL) { + write(fd, buf, sprintf(buf, " 0x%0" PDIG "lx ", + (long)addr)); + write(fd, name, strlen(name)); + write(fd, "\n", 1); + if (!strcmp(name, "main")) + break; + } else { + write(fd, buf, sprintf(buf, " 0x%0" PDIG "lx\n", + (long)addr)); + } + if (fp == NULL) + break; + addr = (char *)fp->fr_savpc; + fp = FRAMEP(fp->fr_savfp); + } +} + +void fex_log_entry(const char *msg) +{ + ucontext_t uc; + struct frame *fp; + char *stk; + int fd; + + /* if logging is disabled, just return */ + mutex_lock(&log_lock); + if (log_fp == NULL) { + mutex_unlock(&log_lock); + return; + } + + /* get the frame pointer from the current context and + pop our own frame */ + getcontext(&uc); +#if defined(__sparc) || defined(__amd64) + fp = FRAMEP(uc.uc_mcontext.gregs[REG_SP]); +#elif defined(__i386) /* !defined(__amd64) */ + fp = FRAMEP(uc.uc_mcontext.gregs[EBP]); +#else +#error Unknown architecture +#endif + if (fp == NULL) { + mutex_unlock(&log_lock); + return; + } + stk = (char *)fp->fr_savpc; + fp = FRAMEP(fp->fr_savfp); + + /* if we've already logged this message here, don't make an entry */ + if (check_exc_list(stk, (unsigned long)msg, stk, fp)) { + mutex_unlock(&log_lock); + return; + } + + /* make an entry */ + fd = fileno(log_fp); + write(fd, "fex_log_entry: ", 15); + write(fd, msg, strlen(msg)); + write(fd, "\n", 1); + __fex_sym_init(); + print_stack(fd, stk, fp); + mutex_unlock(&log_lock); +} + +static const char *exception[FEX_NUM_EXC] = { + "inexact result", + "division by zero", + "underflow", + "overflow", + "invalid operation (0/0)", + "invalid operation (inf/inf)", + "invalid operation (inf-inf)", + "invalid operation (0*inf)", + "invalid operation (sqrt)", + "invalid operation (snan)", + "invalid operation (int)", + "invalid operation (cmp)" +}; + +void +__fex_mklog(ucontext_t *uap, char *addr, int f, enum fex_exception e, + int m, void *p) +{ + struct frame *fp; + char *stk, *name, buf[30]; + int fd; + + /* if logging is disabled, just return */ + mutex_lock(&log_lock); + if (log_fp == NULL) { + mutex_unlock(&log_lock); + return; + } + + /* get stack info */ +#if defined(__sparc) + stk = (char*)uap->uc_mcontext.gregs[REG_PC]; + fp = FRAMEP(uap->uc_mcontext.gregs[REG_SP]); +#elif defined(__amd64) + stk = (char*)uap->uc_mcontext.gregs[REG_PC]; + fp = FRAMEP(uap->uc_mcontext.gregs[REG_RBP]); +#elif defined(__i386) /* !defined(__amd64) */ + stk = (char*)uap->uc_mcontext.gregs[PC]; + fp = FRAMEP(uap->uc_mcontext.gregs[EBP]); +#else +#error Unknown architecture +#endif + + /* if the handling mode is the default and this exception's + flag is already raised, don't make an entry */ + if (m == FEX_NONSTOP) { + switch (e) { + case fex_inexact: + if (f & FE_INEXACT) { + mutex_unlock(&log_lock); + return; + } + break; + case fex_underflow: + if (f & FE_UNDERFLOW) { + mutex_unlock(&log_lock); + return; + } + break; + case fex_overflow: + if (f & FE_OVERFLOW) { + mutex_unlock(&log_lock); + return; + } + break; + case fex_division: + if (f & FE_DIVBYZERO) { + mutex_unlock(&log_lock); + return; + } + break; + default: + if (f & FE_INVALID) { + mutex_unlock(&log_lock); + return; + } + break; + } + } + + /* if we've already logged this exception at this address, + don't make an entry */ + if (check_exc_list(addr, (unsigned long)e, stk, fp)) { + mutex_unlock(&log_lock); + return; + } + + /* make an entry */ + fd = fileno(log_fp); + write(fd, "Floating point ", 15); + write(fd, exception[e], strlen(exception[e])); + write(fd, buf, sprintf(buf, " at 0x%0" PDIG "lx", (long)addr)); + __fex_sym_init(); + if (__fex_sym(addr, &name) != NULL) { + write(fd, " ", 1); + write(fd, name, strlen(name)); + } + switch (m) { + case FEX_NONSTOP: + write(fd, ", nonstop mode\n", 15); + break; + + case FEX_ABORT: + write(fd, ", abort\n", 8); + break; + + case FEX_NOHANDLER: + if (p == (void *)SIG_DFL) { + write(fd, ", handler: SIG_DFL\n", 19); + break; + } + else if (p == (void *)SIG_IGN) { + write(fd, ", handler: SIG_IGN\n", 19); + break; + } + /* fall through*/ + default: + write(fd, ", handler: ", 11); + if (__fex_sym((char *)p, &name) != NULL) { + write(fd, name, strlen(name)); + write(fd, "\n", 1); + } else { + write(fd, buf, sprintf(buf, "0x%0" PDIG "lx\n", + (long)p)); + } + break; + } + print_stack(fd, stk, fp); + mutex_unlock(&log_lock); +} |