--- /dev/null	Sat May 10 12:09:27 2014
+++ new/usr/src/lib/libm/common/m9x/fex_log.c	Sat May 10 12:09:27 2014
@@ -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);
+}
