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