1 /*
   2  * CDDL HEADER START
   3  *
   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.
   7  *
   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.
  12  *
  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]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 /*
  26  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  27  * Use is subject to license terms.
  28  */
  29 
  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
  35 
  36 #include "fenv_synonyms.h"
  37 #include <stdio.h>
  38 #include <stdlib.h>
  39 #include <unistd.h>
  40 #include <string.h>
  41 #include <signal.h>
  42 #include <ucontext.h>
  43 #include <sys/frame.h>
  44 #include <fenv.h>
  45 #include <sys/ieeefp.h>
  46 #include <thread.h>
  47 #include "fex_handler.h"
  48 
  49 #if !defined(PC)
  50 #if defined(REG_PC)
  51 #define PC      REG_PC
  52 #else
  53 #error Neither PC nor REG_PC is defined!
  54 #endif
  55 #endif
  56 
  57 static FILE *log_fp = NULL;
  58 static mutex_t log_lock = DEFAULTMUTEX;
  59 static int log_depth = 100;
  60 
  61 FILE *fex_get_log(void)
  62 {
  63         FILE    *fp;
  64 
  65         mutex_lock(&log_lock);
  66         fp = log_fp;
  67         mutex_unlock(&log_lock);
  68         return fp;
  69 }
  70 
  71 int fex_set_log(FILE *fp)
  72 {
  73         mutex_lock(&log_lock);
  74         log_fp = fp;
  75         mutex_unlock(&log_lock);
  76         __fex_update_te();
  77         return 1;
  78 }
  79 
  80 int fex_get_log_depth(void)
  81 {
  82         int     d;
  83 
  84         mutex_lock(&log_lock);
  85         d = log_depth;
  86         mutex_unlock(&log_lock);
  87         return d;
  88 }
  89 
  90 int fex_set_log_depth(int d)
  91 {
  92         if (d < 0)
  93                 return 0;
  94         mutex_lock(&log_lock);
  95         log_depth = d;
  96         mutex_unlock(&log_lock);
  97         return 1;
  98 }
  99 
 100 static struct exc_list {
 101         struct exc_list         *next;
 102         char                    *addr;
 103         unsigned long           code;
 104         int                     nstack;
 105         char                    *stack[1]; /* actual length is max(1,nstack) */
 106 } *list = NULL;
 107 
 108 #ifdef __sparcv9
 109 #define FRAMEP(X)       (struct frame *)((char*)(X)+(((long)(X)&1)?2047:0))
 110 #else
 111 #define FRAMEP(X)       (struct frame *)(X)
 112 #endif
 113 
 114 #ifdef _LP64
 115 #define PDIG            "16"
 116 #else
 117 #define PDIG            "8"
 118 #endif
 119 
 120 /* look for a matching exc_list; return 1 if one is found,
 121    otherwise add this one to the list and return 0 */
 122 static int check_exc_list(char *addr, unsigned long code, char *stk,
 123     struct frame *fp)
 124 {
 125         struct exc_list *l, *ll = NULL;
 126         struct frame    *f;
 127         int             i, n;
 128 
 129         if (list) {
 130                 for (l = list; l; ll = l, l = l->next) {
 131                         if (l->addr != addr || l->code != code)
 132                                 continue;
 133                         if (log_depth < 1 || l->nstack < 1)
 134                                 return 1;
 135                         if (l->stack[0] != stk)
 136                                 continue;
 137                         n = 1;
 138                         for (i = 1, f = fp; i < log_depth && i < l->nstack &&
 139                             f && f->fr_savpc; i++, f = FRAMEP(f->fr_savfp))
 140                                 if (l->stack[i] != (char *)f->fr_savpc) {
 141                                         n = 0;
 142                                         break;
 143                                 }
 144                         if (n)
 145                                 return 1;
 146                 }
 147         }
 148 
 149         /* create a new exc_list structure and tack it on the list */
 150         for (n = 1, f = fp; n < log_depth && f && f->fr_savpc;
 151             n++, f = FRAMEP(f->fr_savfp)) ;
 152         if ((l = (struct exc_list *)malloc(sizeof(struct exc_list) +
 153             (n - 1) * sizeof(char *))) != NULL) {
 154                 l->next = NULL;
 155                 l->addr = addr;
 156                 l->code = code;
 157                 l->nstack = ((log_depth < 1)? 0 : n);
 158                 l->stack[0] = stk;
 159                 for (i = 1; i < n; i++) {
 160                         l->stack[i] = (char *)fp->fr_savpc;
 161                         fp = FRAMEP(fp->fr_savfp);
 162                 }
 163                 if (list)
 164                         ll->next = l;
 165                 else
 166                         list = l;
 167         }
 168         return 0;
 169 }
 170 
 171 /*
 172 * Warning: cleverness ahead
 173 *
 174 * In the following code, the use of sprintf+write rather than fprintf
 175 * to send output to the log file is intentional.  The reason is that
 176 * fprintf is not async-signal-safe.  "But," you protest, "SIGFPE is
 177 * not an asynchronous signal!  It's always handled by the same thread
 178 * that executed the fpop that provoked it."  That's true, but a prob-
 179 * lem arises because (i) base conversion in fprintf can cause a fp
 180 * exception and (ii) my signal handler acquires a mutex lock before
 181 * sending output to the log file (so that outputs for entries from
 182 * different threads aren't interspersed).  Therefore, if the code
 183 * were to use fprintf, a deadlock could occur as follows:
 184 *
 185 *       Thread A                        Thread B
 186 *
 187 *       Incurs a fp exception,          Calls fprintf,
 188 *       acquires log_lock               acquires file rmutex lock
 189 *
 190 *       Calls fprintf,                  Incurs a fp exception,
 191 *       waits for file rmutex lock      waits for log_lock
 192 *
 193 * (I could just verify that fprintf doesn't hold the rmutex lock while
 194 * it's doing the base conversion, but since efficiency is of little
 195 * concern here, I opted for the safe and dumb route.)
 196 */
 197 
 198 static void print_stack(int fd, char *addr, struct frame *fp)
 199 {
 200         int     i;
 201         char    *name, buf[30];
 202 
 203         for (i = 0; i < log_depth && addr != NULL; i++) {
 204                 if (__fex_sym(addr, &name) != NULL) {
 205                         write(fd, buf, sprintf(buf, "  0x%0" PDIG "lx  ",
 206                             (long)addr));
 207                         write(fd, name, strlen(name));
 208                         write(fd, "\n", 1);
 209                         if (!strcmp(name, "main"))
 210                                 break;
 211                 } else {
 212                         write(fd, buf, sprintf(buf, "  0x%0" PDIG "lx\n",
 213                             (long)addr));
 214                 }
 215                 if (fp == NULL)
 216                         break;
 217                 addr = (char *)fp->fr_savpc;
 218                 fp = FRAMEP(fp->fr_savfp);
 219         }
 220 }
 221 
 222 void fex_log_entry(const char *msg)
 223 {
 224         ucontext_t      uc;
 225         struct frame    *fp;
 226         char            *stk;
 227         int             fd;
 228 
 229         /* if logging is disabled, just return */
 230         mutex_lock(&log_lock);
 231         if (log_fp == NULL) {
 232                 mutex_unlock(&log_lock);
 233                 return;
 234         }
 235 
 236         /* get the frame pointer from the current context and
 237            pop our own frame */
 238         getcontext(&uc);
 239 #if defined(__sparc) || defined(__amd64)
 240         fp = FRAMEP(uc.uc_mcontext.gregs[REG_SP]);
 241 #elif defined(__i386)   /* !defined(__amd64) */
 242         fp = FRAMEP(uc.uc_mcontext.gregs[EBP]);
 243 #else
 244 #error Unknown architecture
 245 #endif
 246         if (fp == NULL) {
 247                 mutex_unlock(&log_lock);
 248                 return;
 249         }
 250         stk = (char *)fp->fr_savpc;
 251         fp = FRAMEP(fp->fr_savfp);
 252 
 253         /* if we've already logged this message here, don't make an entry */
 254         if (check_exc_list(stk, (unsigned long)msg, stk, fp)) {
 255                 mutex_unlock(&log_lock);
 256                 return;
 257         }
 258 
 259         /* make an entry */
 260         fd = fileno(log_fp);
 261         write(fd, "fex_log_entry: ", 15);
 262         write(fd, msg, strlen(msg));
 263         write(fd, "\n", 1);
 264         __fex_sym_init();
 265         print_stack(fd, stk, fp);
 266         mutex_unlock(&log_lock);
 267 }
 268 
 269 static const char *exception[FEX_NUM_EXC] = {
 270         "inexact result",
 271         "division by zero",
 272         "underflow",
 273         "overflow",
 274         "invalid operation (0/0)",
 275         "invalid operation (inf/inf)",
 276         "invalid operation (inf-inf)",
 277         "invalid operation (0*inf)",
 278         "invalid operation (sqrt)",
 279         "invalid operation (snan)",
 280         "invalid operation (int)",
 281         "invalid operation (cmp)"
 282 };
 283 
 284 void
 285 __fex_mklog(ucontext_t *uap, char *addr, int f, enum fex_exception e,
 286     int m, void *p)
 287 {
 288         struct  frame   *fp;
 289         char            *stk, *name, buf[30];
 290         int             fd;
 291 
 292         /* if logging is disabled, just return */
 293         mutex_lock(&log_lock);
 294         if (log_fp == NULL) {
 295                 mutex_unlock(&log_lock);
 296                 return;
 297         }
 298 
 299         /* get stack info */
 300 #if defined(__sparc)
 301         stk = (char*)uap->uc_mcontext.gregs[REG_PC];
 302         fp = FRAMEP(uap->uc_mcontext.gregs[REG_SP]);
 303 #elif defined(__amd64)
 304         stk = (char*)uap->uc_mcontext.gregs[REG_PC];
 305         fp = FRAMEP(uap->uc_mcontext.gregs[REG_RBP]);
 306 #elif defined(__i386)   /* !defined(__amd64) */
 307         stk = (char*)uap->uc_mcontext.gregs[PC];
 308         fp = FRAMEP(uap->uc_mcontext.gregs[EBP]);
 309 #else
 310 #error Unknown architecture
 311 #endif
 312 
 313         /* if the handling mode is the default and this exception's
 314            flag is already raised, don't make an entry */
 315         if (m == FEX_NONSTOP) {
 316                 switch (e) {
 317                 case fex_inexact:
 318                         if (f & FE_INEXACT) {
 319                                 mutex_unlock(&log_lock);
 320                                 return;
 321                         }
 322                         break;
 323                 case fex_underflow:
 324                         if (f & FE_UNDERFLOW) {
 325                                 mutex_unlock(&log_lock);
 326                                 return;
 327                         }
 328                         break;
 329                 case fex_overflow:
 330                         if (f & FE_OVERFLOW) {
 331                                 mutex_unlock(&log_lock);
 332                                 return;
 333                         }
 334                         break;
 335                 case fex_division:
 336                         if (f & FE_DIVBYZERO) {
 337                                 mutex_unlock(&log_lock);
 338                                 return;
 339                         }
 340                         break;
 341                 default:
 342                         if (f & FE_INVALID) {
 343                                 mutex_unlock(&log_lock);
 344                                 return;
 345                         }
 346                         break;
 347                 }
 348         }
 349 
 350         /* if we've already logged this exception at this address,
 351            don't make an entry */
 352         if (check_exc_list(addr, (unsigned long)e, stk, fp)) {
 353                 mutex_unlock(&log_lock);
 354                 return;
 355         }
 356 
 357         /* make an entry */
 358         fd = fileno(log_fp);
 359         write(fd, "Floating point ", 15);
 360         write(fd, exception[e], strlen(exception[e]));
 361         write(fd, buf, sprintf(buf, " at 0x%0" PDIG "lx", (long)addr));
 362         __fex_sym_init();
 363         if (__fex_sym(addr, &name) != NULL) {
 364                 write(fd, " ", 1);
 365                 write(fd, name, strlen(name));
 366         }
 367         switch (m) {
 368         case FEX_NONSTOP:
 369                 write(fd, ", nonstop mode\n", 15);
 370                 break;
 371 
 372         case FEX_ABORT:
 373                 write(fd, ", abort\n", 8);
 374                 break;
 375 
 376         case FEX_NOHANDLER:
 377                 if (p == (void *)SIG_DFL) {
 378                         write(fd, ", handler: SIG_DFL\n", 19);
 379                         break;
 380                 }
 381                 else if (p == (void *)SIG_IGN) {
 382                         write(fd, ", handler: SIG_IGN\n", 19);
 383                         break;
 384                 }
 385                 /* fall through*/
 386         default:
 387                 write(fd, ", handler: ", 11);
 388                 if (__fex_sym((char *)p, &name) != NULL) {
 389                         write(fd, name, strlen(name));
 390                         write(fd, "\n", 1);
 391                 } else {
 392                         write(fd, buf, sprintf(buf, "0x%0" PDIG "lx\n",
 393                             (long)p));
 394                 }
 395                 break;
 396         }
 397         print_stack(fd, stk, fp);
 398         mutex_unlock(&log_lock);
 399 }