Home | History | Annotate | Download | only in diskomizer
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"@(#)signal_catch.c	1.14	09/05/26 SMI"
     28 
     29 #include <siginfo.h>
     30 #include <signal.h>
     31 #include <ucontext.h>
     32 #include <sys/time.h>	/* for gettimeofday */
     33 #include <time.h>	/* for strftime */
     34 #include <string.h>	/* for strerror */
     35 #include <stdlib.h>
     36 #include <unistd.h>	/* for pause */
     37 #include <diskomizer/stack_trace.h>
     38 #include "args.h"
     39 #include <diskomizer/log.h>
     40 #include "list.h"
     41 #ident "@(#)signal_catch.c	1.9 03/12/02"
     42 #define	TIME_FORMAT "%T %d/%b/%Y"
     43 
     44 struct sig_head {
     45 	struct sig_lists *head;
     46 	struct sig_lists *tail;
     47 };
     48 struct sig_lists {
     49 	struct sig_lists *next;
     50 	struct sig_lists *prev;
     51 	char *str;
     52 	void *addr;
     53 	off_t len;
     54 };
     55 struct expected {
     56 	int max_sig;
     57 	struct sig_head *signals;
     58 };
     59 struct expected expected;
     60 
     61 #include <stdarg.h>
     62 /*PRINTFLIKE2*/
     63 static int
     64 mprintf(void *XX, const char *fmt, ...)
     65 {
     66 	va_list ap;
     67 	char buf[256];
     68 	int x;
     69 	int written = 0;
     70 	va_start(ap, fmt);
     71 
     72 	x = vsnprintf(buf, sizeof (buf), fmt, ap);
     73 
     74 	while ((x - written) != 0) {
     75 		int r;
     76 		r = write(STDERR_FILENO, &buf[written], x - written);
     77 		if (r <= 0) {
     78 			break;
     79 		}
     80 		written += r;
     81 	}
     82 
     83 	return (written);
     84 }
     85 
     86 static void
     87 mprint_fpeinfo(FILE *out, siginfo_t *info)
     88 {
     89 	switch (info->si_code) {
     90 		case 0:
     91 			(void) mprintf(out, "FPE sent by UID %d PID %d,\n",
     92 			    info->si_uid, info->si_pid);
     93 			break;
     94 		case FPE_INTDIV:
     95 			(void) mprintf(out, "integer divide by zero\n");
     96 			break;
     97 		case FPE_INTOVF:
     98 			(void) mprintf(out, "integer overflow\n");
     99 			break;
    100 		case FPE_FLTDIV:
    101 			(void) mprintf(out, "floating point divide by zero\n");
    102 			break;
    103 		case FPE_FLTOVF:
    104 			(void) mprintf(out, "floating point overflow\n");
    105 			break;
    106 		case FPE_FLTUND:
    107 			(void) mprintf(out, "floating point underflow\n");
    108 			break;
    109 		case FPE_FLTRES:
    110 			(void) mprintf(out, "floating point inexact result\n");
    111 			break;
    112 		case FPE_FLTINV:
    113 			(void) mprintf(out,
    114 			    "invalid floating point operation\n");
    115 			break;
    116 		case FPE_FLTSUB:
    117 			(void) mprintf(out, "subscript out of range\n");
    118 			break;
    119 	}
    120 }
    121 static void
    122 mprint_illinfo(FILE *out, siginfo_t *info)
    123 {
    124 	switch (info->si_code) {
    125 		case 0:
    126 			(void) mprintf(out,
    127 			    "ILL sent by UID %d PID %d,\n",
    128 			    info->si_uid, info->si_pid);
    129 			break;
    130 		case ILL_ILLOPC:
    131 			(void) mprintf(out, "illegal opcode\n");
    132 			break;
    133 		case ILL_ILLOPN:
    134 			(void) mprintf(out, "illegal operand\n");
    135 			break;
    136 		case ILL_ILLADR:
    137 			(void) mprintf(out, "illegal addressing mode\n");
    138 			break;
    139 		case ILL_ILLTRP:
    140 			(void) mprintf(out, "illegal trap\n");
    141 			break;
    142 		case ILL_PRVOPC:
    143 			(void) mprintf(out, "privileged opcode\n");
    144 			break;
    145 		case ILL_PRVREG:
    146 			(void) mprintf(out, "privileged register\n");
    147 			break;
    148 		case ILL_COPROC:
    149 			(void) mprintf(out, "co-processor error\n");
    150 			break;
    151 		case ILL_BADSTK:
    152 			(void) mprintf(out, "internal stack error\n");
    153 			break;
    154 	}
    155 	(void) mprintf(out, "Illegal Instruction address %#lx value %#lx\n",
    156 	    (ulong_t)info->si_addr, *(ulong_t *)info->si_addr);
    157 }
    158 static void
    159 mprint_segvinfo(FILE *out, siginfo_t *info)
    160 {
    161 	switch (info->si_code) {
    162 		case 0:
    163 			(void) mprintf(out,
    164 			    "SEGV sent by UID %d PID %d,\n",
    165 			    info->si_uid, info->si_pid);
    166 			break;
    167 		case SEGV_MAPERR:
    168 			(void) mprintf(out,
    169 			    "SEGV_MAPERR: address not mapped to object\n");
    170 			break;
    171 		case SEGV_ACCERR:
    172 			(void) mprintf(out,
    173 			    "SEGV_ACCERR: invalid permissions for mapped "
    174 			    "object\n");
    175 			break;
    176 
    177 	}
    178 	(void) mprintf(out, "Fault address %#lx\n", (ulong_t)info->si_addr);
    179 }
    180 static void
    181 mprint_businfo(FILE *out, siginfo_t *info)
    182 {
    183 	switch (info->si_code) {
    184 		case 0:
    185 			(void) mprintf(out, "BUS sent by UID %d PID %d,\n",
    186 			    info->si_uid, info->si_pid);
    187 			break;
    188 		case BUS_ADRALN:
    189 			(void) mprintf(out,
    190 			    "BUS_ADRALN: invalid address alignment\n");
    191 			break;
    192 		case BUS_ADRERR:
    193 			(void) mprintf(out,
    194 			    "BUS_ADRERR: non-existent physical address\n");
    195 			break;
    196 		case BUS_OBJERR:
    197 			(void) mprintf(out,
    198 			    "BUS_OBJERR: object specific hardware error\n");
    199 			break;
    200 
    201 	}
    202 	(void) mprintf(out, "Address %#lx\n", (ulong_t)info->si_addr);
    203 }
    204 
    205 static void
    206 mprint_siginfo(FILE *out, int signo, siginfo_t *info)
    207 {
    208 	switch (signo) {
    209 		case SIGSEGV:
    210 			mprint_segvinfo(out, info);
    211 			break;
    212 		case SIGBUS:
    213 			mprint_businfo(out, info);
    214 			break;
    215 		case SIGILL:
    216 			mprint_illinfo(out, info);
    217 			break;
    218 		case SIGFPE:
    219 			mprint_fpeinfo(out, info);
    220 			break;
    221 	}
    222 }
    223 /*
    224  *	Expect signal.
    225  *
    226  *	Called to register that getting a particular signal is an
    227  *	expected error while accessing memory. The caller supplied
    228  *	the signal, an error string and the memory range.  If the
    229  *	signal is delivered for that memory range then the message
    230  *	is output, with the error decoded. Then the program exits.
    231  *
    232  *	The reason for this is that when allocating memory using mmap()
    233  *	and the file is in the file system as opposed to being /dev/zero
    234  *	if there is insufficient space in the file system then the first
    235  *	access to the memory will result in SIGBUS being sent to the
    236  *	process.  Now a core file would normally result, or with
    237  *	the stack trace stuff a stack trace and an exit. Not very
    238  *	friendly and likely to make people think the probram is broken.
    239  */
    240 void *
    241 expect_signal(int signo, const char *str, void *addr, off_t len)
    242 {
    243 	struct sig_lists *new;
    244 	if (expected.max_sig < signo) {
    245 		struct sig_head *temp;
    246 		int i;
    247 
    248 		temp = realloc(expected.signals,
    249 		    (signo + 1) * sizeof (struct sig_head));
    250 		if (temp == NULL)
    251 			return (NULL);
    252 		for (i = expected.max_sig; i <= signo; i++) {
    253 			(void) memset((void *)&temp[i], NULL,
    254 			    sizeof (struct sig_head));
    255 		}
    256 		expected.max_sig = signo;
    257 		expected.signals = temp;
    258 		assert(expected.signals[signo].head == NULL &&
    259 		    expected.signals[signo].tail == NULL);
    260 	}
    261 
    262 	new = (struct sig_lists *)calloc(1, sizeof (struct sig_lists));
    263 	if (new == NULL)
    264 		return (NULL);
    265 	if (str != NULL)
    266 		new->str = strdup(str);
    267 	new->addr = addr;
    268 	new->len = len;
    269 
    270 	assert((expected.signals[signo].head == NULL &&
    271 	    expected.signals[signo].tail == NULL) ||
    272 	    (expected.signals[signo].head != NULL &&
    273 	    expected.signals[signo].tail != NULL));
    274 	LIST_ADD(&expected.signals[signo], new);
    275 	return ((void *)new);
    276 }
    277 /*
    278  *	Cancel Expected Signal:
    279  *	Given the handle given out above cancel the expection.
    280  *	the signal reverts to it's standard behviour for that range.
    281  */
    282 void
    283 cancel_expected_signal(int signo, void *ptr)
    284 {
    285 	struct sig_lists *old = (struct sig_lists *)ptr;
    286 	if (old == NULL)
    287 		return;
    288 	LIST_REMOVE(&expected.signals[signo], old);
    289 	if (old->str != NULL)
    290 		free(old->str);
    291 	free(old);
    292 }
    293 void
    294 check_expected(int signo, void * vaddr, int error)
    295 {
    296 	struct sig_lists *this;
    297 	ulong_t addr = (ulong_t)vaddr;
    298 	if (expected.max_sig < signo)
    299 		return;
    300 
    301 	for (this = expected.signals[signo].head; this != NULL;
    302 	    this = this->next) {
    303 		if (addr >= (ulong_t)this->addr &&
    304 		    addr < (ulong_t)this->addr + this->len) {
    305 			(void) mprintf(NULL, "%s: %s\n", this->str,
    306 			    strerror(error));
    307 			exit(1);
    308 		}
    309 	}
    310 }
    311 
    312 
    313 void
    314 handler(int signo, siginfo_t *info, void *v)
    315 {
    316 	char signame[SIG2STR_MAX];
    317 	ucontext_t *context = (ucontext_t *)v;
    318 	char buf[128];
    319 	struct  timeval now;
    320 	extern int errno;
    321 	struct sigaction action;
    322 
    323 	if (gettimeofday(&now, NULL) == -1) {
    324 		(void) mprintf(NULL, "gettimeofday: %s", strerror(errno));
    325 		(void) sprintf(&buf[0], "unknown time");
    326 	} else {
    327 		if (strftime(&buf[0], sizeof (buf), TIME_FORMAT,
    328 		    localtime(&now.tv_sec)) == 0) {
    329 			if (strftime(&buf[0], sizeof (buf), "%D %T",
    330 			    localtime(&now.tv_sec)) == 0) {
    331 					(void) sprintf(&buf[0], "Seconds %ld",
    332 					    now.tv_sec);
    333 			}
    334 		}
    335 	}
    336 	check_expected(signo, info->si_addr, info->si_errno);
    337 
    338 	(void) sig2str(signo, &signame[0]);
    339 
    340 	(void) mprintf(NULL, "Caught Signal %d (%s) at %s\n",
    341 	    signo, signame, buf);
    342 	mprint_siginfo(NULL, signo, info);
    343 
    344 	ftraceback(mprintf, NULL, context);
    345 	if (1 == opts.debug_pause_on_signal)
    346 		pause();
    347 	if (0 == opts.debug_allow_coredump)
    348 		exit(1);
    349 
    350 	action.sa_handler = SIG_DFL;
    351 	action.sa_sigaction = NULL;
    352 	sigemptyset(&action.sa_mask);
    353 	action.sa_flags = 0;
    354 
    355 	if (sigaction(signo, &action, NULL) == -1) {
    356 		pperror("sigaction(%d (%s))", signo, signame);
    357 	}
    358 }
    359 void
    360 setup_signal_catcher(int sig,
    361 	void (*func)(int signo, siginfo_t *info, void *v),
    362 	int flags)
    363 {
    364 	char signame[SIG2STR_MAX];
    365 	struct sigaction action;
    366 
    367 	(void) sig2str(sig, &signame[0]);
    368 
    369 	if (sigaction(sig, NULL, &action) == -1) {
    370 		pperror("sigaction(%d (%s))", sig, signame);
    371 		exit(1);
    372 	}
    373 	action.sa_handler = NULL;
    374 	action.sa_sigaction = func;
    375 	action.sa_flags = flags;
    376 	if (sigaction(sig, &action, NULL) == -1) {
    377 		pperror("sigaction(%d (%s))", sig, signame);
    378 		exit(1);
    379 	}
    380 }
    381 static void
    382 do_signal(int sig)
    383 {
    384 	setup_signal_catcher(sig, handler, SA_SIGINFO|SA_ONSTACK);
    385 }
    386 /*ARGSUSED*/
    387 void
    388 setup_signal(int (*func)(void *, const char *fmt, ...), void * arg)
    389 {
    390 	stack_t stack;
    391 
    392 	stack.ss_sp = (char *)calloc(1024*1024/sizeof (int), sizeof (int));
    393 	stack.ss_size = 1024*1024;
    394 	stack.ss_flags = 0;
    395 	if (sigaltstack(&stack, NULL) == -1)
    396 		perror("sigaltstack");
    397 
    398 	do_signal(SIGSEGV);
    399 	do_signal(SIGBUS);
    400 	do_signal(SIGILL);
    401 	do_signal(SIGFPE);
    402 	do_signal(SIGEMT);
    403 	do_signal(SIGSYS);
    404 	do_signal(SIGQUIT);
    405 	do_signal(SIGABRT);
    406 }
    407 #ifdef TEST_PROG
    408 void
    409 recurse(int x)
    410 {
    411 	if (x == 0) {
    412 		int *y;
    413 		y = recurse;
    414 		(void) mprintf(stdout, "Addr %#lx %#lx\n", y, &y);
    415 		*y = 0;
    416 	}
    417 	recurse(--x);
    418 }
    419 main()
    420 {
    421 	setup_signal(fprintf, stdout);
    422 	recurse(20);
    423 }
    424 #endif
    425