/* sigsegv.c -- sigsegv handlers * * Copyright (c) 2003 Juan F. Codagnone * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifdef HAVE_CONFIG_H_ #include #endif #include #include #include #include #include #include #ifdef HAVE_PTHREADS_H # include #endif /* * http://www.gnu.org/manual/glibc-2.2.3/html_chapter/libc_33.html */ #if defined(__GLIBC__) && defined(__GLIBC_MINOR__) && __GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 # define HAVE_BACKTRACE # include #endif #include #include "crash_sigsegv.h" namespace Crash { static int (* print)(const char *format, ...) = NULL; static int needs_cr = 1; void *sigsegv_set_print( int (* fnc)(const char *format, ...), int _needs_cr) { void *ret; ret = &print; print = fnc; needs_cr = _needs_cr; return ret; } /** * launchs gdb, and feeds myprint with the backtrace */ static int dump_pid_son(pid_t pid, const char *binary, int full_bt, int (* myprint)(const char *format, ...)) { char tmp[]="/tmp/mrbug-crash-XXXXXX"; int ret = 0; int fd; fd = mkstemp(tmp); if( fd == -1 ) { (*myprint)("opening gdb command (tempory) file `%s'%s", tmp, needs_cr ? "\n" : ""); ret = -1; } else { char gdb_cmd[]="bt\nquit"; char gdb_cmd_full[]="bt full\nquit"; char cmd[128]; FILE *fp; if( full_bt ) write(fd, gdb_cmd_full, strlen(gdb_cmd_full)); else write(fd, gdb_cmd, strlen(gdb_cmd)); close(fd); sprintf(cmd, "gdb -nw -n -batch -x \"%s\" %s %d", tmp, binary, pid); (*myprint)("trying to dump pid: %d (%s)...%s", pid, binary, needs_cr ? "\n" : ""); fflush(NULL); fp = popen(cmd, "r"); if( fp == NULL ) { (*myprint)("err. couldn't exec `%s'%s", cmd, needs_cr ? "\n" : ""); ret = -1; } else { char buff[4096]; size_t len; while(fgets(buff, sizeof(buff), fp)) { len = strlen(buff); if( buff[len-1] == '\n') buff[len-1]=0; (*myprint)("%s%s", buff,needs_cr ? "\n" : ""); } fclose(fp); } if( remove(tmp) == -1 ) (*myprint)("removing `%s` (@;@)%s", tmp, needs_cr ? "\n" : ""); } return ret; } static int dump_pid(pid_t pid, const char *binary, int full_bt ) { pid_t mpid; int (* myprint)(const char *format, ...); myprint = print ? (int(*)(const char *format, ...))print : (int(*)(const char *format, ...))printf; /* * clone the process, so we don't make the bt bigger. */ mpid = fork(); if( mpid == 0 ) { dump_pid_son(pid, binary, full_bt, myprint); exit(0); } else if( mpid == -1 ) (*myprint)("lunching son: `%s' %s", strerror(errno), needs_cr ? "\n" : ""); else { /* father */ int status; alarm(0); waitpid(0, &status, 0); if( WIFEXITED(status) && WEXITSTATUS(status)==0 ) ; } return 0; } /** * get `pid`'s real path * * \param buff buffer for the output * \param nbuff size of the buffer * \param pid pid processes id to use * * \note this function works only in linux * * \return the buffer */ static char *get_path_from_pid(char *buff, size_t nbuff, pid_t pid) { char proc[256]; char *ret = NULL; int n; sprintf(proc, "/proc/%d/exe", pid); if( (n=readlink(proc, buff, nbuff)) == -1 ) ret = NULL; else { buff[n]=0; ret = buff; } return ret; } static void sigsegv_libc_dump( int (* myprint)(const char *format, ...) ) { void *array[48] = {0}; unsigned short i; int n; char **res; #ifdef HAVE_BACKTRACE (*myprint)("Backtrace:%c", needs_cr ? "\n" : ""); n = backtrace(array, sizeof(array)/(sizeof(*array))); res = backtrace_symbols(array, n); for (i = 0; i < n; i++) (*myprint)("%s%s", res[i], needs_cr ? "\n" : ""); (*myprint)("Attempting to generate core file%s", needs_cr ? "" : ""); #endif } static void sigsegv_handler_generic(int signal, int full_bt) { char binary[2048]; int pid = getpid(); int (* myprint)(const char *format, ...); myprint = print ? print : printf; if( get_path_from_pid(binary, sizeof(binary), pid) == NULL) (*myprint)("pid %d does not seems to exist", pid); else { (*myprint)("Segmentation Violation Detected.%s", needs_cr ? "\n" : ""); dump_pid(pid, binary, full_bt); sigsegv_libc_dump(myprint); } #ifdef HAVE_PTHREAD_H pthread_kill_other_threads_np(); #endif fflush(NULL); abort(); } void sigsegv_handler_fnc(int signal) { sigsegv_handler_generic(signal, 0); } void sigsegv_handler_bt_full_fnc(int signal) { sigsegv_handler_generic(signal, 1); } }; // namespace Crash