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