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 }