summaryrefslogtreecommitdiff
path: root/usr/src/lib/libm/common/m9x/fex_log.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libm/common/m9x/fex_log.c')
-rw-r--r--usr/src/lib/libm/common/m9x/fex_log.c399
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);
+}