/* Copyright 2003 Sun Microsystems, Inc. ALL RIGHTS RESERVED Use of this software is authorized pursuant to the terms of the license found at http://developers.sun.com/berkeley_license.html The source code is being made available by Sun merely as a convenience to you. The source code below is provided "AS IS." Sun makes no warranties of any kind whatsoever with respect to the source code. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY OF NON-INFRINGEMENT OR IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT ALLOWED BY APPLICABLE LAW. IN NO EVENT WILL SUN BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL, OR PUNITIVE DAMAGES HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOURCE CODE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ======================================================= */ /* * vmem_arr - virtual memory array package * By Greg Nakhimovsky, MDE, Sun Microsystems, Inc., 2003 * * This implementation is for the Solaris[tm] Operating System, * SPARC[r] Platform Edition. * * void *vmem_arr_create(size_t virt_size); * Creates an array with a given virtual address space size. * Returns a pointer to the array on success, NULL on failure. * * void vmem_arr_destroy(void *ptr); * Destroys the virtual array pointed to by ptr. * * void vmem_arr_trim(void *ptr, size_t smaller_virt_size); * Reduces the size of the virtual array pointed to by ptr. * * Installs a signal handler for SIGBUS and SIGSEGV that * provides extended information on runtime errors, * produces a traceback and then aborts the current process. * Allows recovery when the system is out of memory. * * MT-safe. Uses one static lock assuming that * vmem_arr_create() is not called from many threads at * the same time frequently. * Works with either Pthreads or Solaris threads. * The mutex lock is active only if the application * is linked with the threads library. * * This source code works in 32-bit or 64-bit mode. * Compile it with something like this: * cc -c vmem_arr.c (32-bit mode) * cc -xarch=v9 -c vmem_arr.c (64-bit mode) */ #define _REENTRANT #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ALIGN 8 typedef union _w_ { unsigned long size; char w_a[ALIGN]; /* to force alignment */ } WORD; #define WORDSIZE ((size_t)(sizeof(WORD))) #define SIZE(p) (((WORD *)(p))->size) #define DATA(p) (((char *)(p)) + WORDSIZE) #define BLOCK(p) ((void *) (((char *) (p)) - WORDSIZE)) static mutex_t vmem_arr_lock=DEFAULTMUTEX; static long pagesize; static int MEM_RESERVE_SIZE = 1024*1024; static void *mem_reserve; #define SAFE_WRITE_STDERR(buf) (void)write(2, buf, strlen(buf)) #define SAFE_WRITE_STDOUT(buf) (void)write(1, buf, strlen(buf)) static void traceback_exit(void) { /* Produce both pstack(1) and pmap(1) output using sh(1) syntax */ /* 012345678 1 2345678 2 2345678 3 2345678 4 2345678 5 2345678 */ char *buf="(/usr/proc/bin/pstack ;/usr/proc/bin/pmap -x )" "|/bin/tee /var/tmp/traceback.txt\n"; char cdigits[]="0123456789"; char d; int i, j, n; /* * Put process id into the command line while * avoiding a call to sprintf(3C) from a signal handler */ n = (int)getpid(); i = 27; /* indices to character in buf */ j = 56; while ( n !=0 ) /* keep dividing by 10 */ { d = cdigits[n%10]; buf[i--] = d; buf[j--] = d; n /= 10; } (void)system(buf); SAFE_WRITE_STDERR( "A copy of the traceback is stored in /var/tmp/traceback.txt\n"); /* Generate a core file unless the environment prevents it */ abort(); } static void handle_SIGBUS_SIGSEGV(int sig, siginfo_t *info, ucontext_t *uap) { char buf[4]; if(sig == SIGBUS) SAFE_WRITE_STDERR("Caught SIGBUS\n"); else SAFE_WRITE_STDERR("Caught SIGSEGV\n"); if(info->si_code == SI_NOINFO || info->si_code <= 0) { SAFE_WRITE_STDERR("Reason: not available\n"); } if(info->si_errno != 0) SAFE_WRITE_STDERR(strerror(info->si_errno)); SAFE_WRITE_STDERR("\n"); if(sig == SIGBUS) switch(info->si_code) { case BUS_ADRALN: SAFE_WRITE_STDERR("BUS_ADRALN: invalid address alignment\n"); break; case BUS_ADRERR: SAFE_WRITE_STDERR("BUS_ADRERR: non-existent physical address\n"); break; case BUS_OBJERR: SAFE_WRITE_STDERR("BUS_OBJERR: object specific hardware error\n"); break; } else switch(info->si_code) { case SEGV_MAPERR: SAFE_WRITE_STDERR("SEGV_MAPERR: address not mapped to object\n"); break; case SEGV_ACCERR: SAFE_WRITE_STDERR("SEGV_ACCERR: invalid permissions\n"); break; } switch(info->si_errno) { case ENOMEM: SAFE_WRITE_STDERR("ENOMEM: not enough memory\n"); /* * Give user a chance to recover */ if(mem_reserve) { /* release memory reserve back to the system, but only once */ (void)munmap(mem_reserve, MEM_RESERVE_SIZE); mem_reserve = (void*)0; } SAFE_WRITE_STDOUT("The system is out of memory. " "You have the following choices:\n" " 1) Make more memory (swap space) available and continue (enter 1)\n" " - Close other applications\n" " - Delete unnecessary large files from /tmp\n" " - Add disk swap space with 'swap -a'\n" " 2) Exit this application (enter 2)\n" "Enter 1 to continue or 2 to exit: "); read(0, buf, 1); if(buf[0] == '1') return; else break; case ENOSPC: SAFE_WRITE_STDERR("ENOSPC: not enough disk space\n"); break; case ESTALE: SAFE_WRITE_STDERR("ESTALE: stale NFS handle\n"); break; } traceback_exit(); } void *vmem_arr_create(size_t virt_size) { static int first=1; size_t mapping_size; void *ptr; int fd; (void)mutex_lock(&vmem_arr_lock); if(first) /* do this only once */ { struct sigaction action, old_action; action.sa_handler = handle_SIGBUS_SIGSEGV; (void)sigemptyset(&action.sa_mask); action.sa_flags = SA_SIGINFO; /* * Install these signal handlers only if there * are no existing ones */ (void)sigaction(SIGBUS, NULL, &old_action); if(old_action.sa_handler == SIG_DFL && sigaction(SIGBUS, &action, NULL)) perror("sigaction(SIGBUS)"), traceback_exit(); (void)sigaction(SIGSEGV, NULL, &old_action); if(old_action.sa_handler == SIG_DFL && sigaction(SIGSEGV, &action, NULL)) perror("sigaction(SIGSEGV)"), traceback_exit(); pagesize = sysconf(_SC_PAGESIZE); /* Get an emergency memory reserve */ #ifdef MAP_ANON mem_reserve = mmap(0, MEM_RESERVE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); #else fd = open("/dev/zero", O_RDWR); mem_reserve = mmap(0, MEM_RESERVE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); (void)close(fd); #endif first = 0; } (void)mutex_unlock(&vmem_arr_lock); /* Add room for the header and round up to a multiple of pagesize*/ mapping_size = (virt_size+WORDSIZE+pagesize-1) & -pagesize; #ifdef MAP_ANON /* Solaris 8 and later versions allow MAP_ANON */ ptr = mmap(0, mapping_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_NORESERVE|MAP_ANON, -1, 0); #else fd = open("/dev/zero", O_RDWR); ptr = mmap(0, mapping_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_NORESERVE, fd, 0); (void)close(fd); #endif if(ptr == MAP_FAILED) { perror("vmem_arr_create(): mmap failed"); return (void *)0; } /* Store mapping size into the header */ SIZE(ptr) = mapping_size; return (void *)DATA(ptr); } void vmem_arr_destroy(void *ptr) { void *p; size_t mapping_size; p = BLOCK(ptr); mapping_size = SIZE(p); if(munmap(p, mapping_size) != 0) perror("vmem_arr_destroy(): munmap failed"), traceback_exit(); } void vmem_arr_trim(void *ptr, size_t smaller_virt_size) { void *p; size_t old_mapping_size; size_t new_mapping_size; /* Add room for the header and round up to a multiple of pagesize*/ new_mapping_size = (smaller_virt_size+WORDSIZE+pagesize-1) & -pagesize; p = BLOCK(ptr); old_mapping_size = SIZE(p); if(new_mapping_size < old_mapping_size) { if(munmap((char *)p+new_mapping_size, (old_mapping_size-new_mapping_size)) != 0) perror("vmem_arr_trim(): munmap failed"), traceback_exit(); /* Store new mapping size into the header */ SIZE(ptr) = new_mapping_size; } }