├── LICENSE ├── Makefile ├── README.md └── libsegfault.c /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015 Stanislav Sedov 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIB= segfault 2 | SRCS= libsegfault.c 3 | 4 | CFLAGS+= -I/usr/local/include 5 | LDFLAGS= -L/usr/local/lib 6 | LDADD= -pthread -lunwind -lunwind-x86_64 7 | 8 | WARNS= 6 9 | SHLIB_MAJOR?= 0 10 | 11 | .include 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libsegfault -- get a nice stacktrace when your application crashes 2 | 3 | ## What 4 | 5 | A preloadable library that will print a stacktrace of the program when it crashes 6 | (and received SEGV), or upon receiving any other relevant signal. 7 | 8 | ## Why 9 | 10 | A lot of times the application resident memory size is so big that collecting 11 | core dumps is impractical. However being able to see the crash stacktrace is 12 | desirable. Without a stacktrace figuring our the reason of the crash might 13 | be very hard if not impossible. 14 | 15 | ## How 16 | 17 | Just link your program again libsegfault. Or use LD_PRELOAD, e.g. 18 | ```shell 19 | % env LD_PRELOAD=/path/to/libsegfault.so ./app 20 | ``` 21 | 22 | ## Building 23 | 24 | * Install libunwind: pkg install libunwind 25 | * make && make install 26 | 27 | ## Testing 28 | 29 | After starting your program with libsegfault preloaded or linked in 30 | send the SEGV signal to the program. You should see a stacktrace 31 | printed into the stderror. 32 | ```shell 33 | % env LD_PRELOAD=/path/to/libsegfault.so ./app 34 | % kill -SEGV ${PID} 35 | ``` 36 | 37 | You should see something along the lines of: 38 | 39 | Caught signal 11 (SEGV) in program sleep [27759] 40 | 41 | thread frame IP function 42 | [100689] 0: 0x800afbe0a: __sys_nanosleep()+0xa 43 | [100689] 1: 0x80120ebcc: _pthread_suspend_all_np()+0x10dc 44 | [100689] 2: 0x000400a38: 45 | [100689] 3: 0x00040086f: 46 | [100689] 4: 0x800620000: 47 | 48 | Backtrace: 0x800afbe0a 0x80120ebcc 0x400a38 0x40086f 0x800620000 49 | Segmentation fault (core dumped) 50 | 51 | ## Configuration 52 | 53 | libsegfaults looks at the *SEGFAULT_SIGNALS* environment variable 54 | on startup. If that variable is not set, only SIGSEGV handler is 55 | enabled. If it's set to "all", libsegfault will intercept all 56 | the signals that usually result in a crash: SIGSEGV, SIGBUS, SIGILL, SIGABRT, SIGFPE and SIGSYS. 57 | Optionally, the variable can also be set to a custom list of space 58 | separated signals. 59 | 60 | For example the following command will tell /libsegfault/ to intercept 61 | only SEGV and SIGILL (illegal instruction) conditions: 62 | ```shell 63 | % env SEGFAULT_SIGNALS="SEGV ILL" LD_PRELOAD=/path/to/libsegfault.so ./app 64 | ``` 65 | 66 | To intercept all signals, use: 67 | ```shell 68 | % env SEGFAULT_SIGNALS=all LD_PRELOAD=/path/to/libsegfault.so ./app 69 | ``` 70 | -------------------------------------------------------------------------------- /libsegfault.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Stanislav Sedov 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | 41 | #define BACKTRACE_DEPTH 256 42 | #define NULLSTR "(null)" 43 | 44 | static inline void 45 | print_str(int fd, const char *str) 46 | { 47 | 48 | if (str == NULL) { 49 | write(fd, NULLSTR, strlen(NULLSTR)); 50 | } else { 51 | write(fd, str, strlen(str)); 52 | } 53 | } 54 | 55 | static void 56 | print_unw_error(const char *fun, int error) 57 | { 58 | 59 | print_str(STDERR_FILENO, fun); 60 | print_str(STDERR_FILENO, ": "); 61 | print_str(STDERR_FILENO, unw_strerror(error)); 62 | print_str(STDERR_FILENO, "\n"); 63 | } 64 | 65 | static int 66 | print_stack_trace(ucontext_t *context) 67 | { 68 | unw_cursor_t cursor; 69 | unw_word_t backtrace[BACKTRACE_DEPTH]; 70 | unw_word_t ip, off; 71 | char buf[1024]; 72 | unsigned int i, level; 73 | int ret; 74 | 75 | if ((ret = unw_init_local(&cursor, context)) != 0) { 76 | print_unw_error("unw_init_local", ret); 77 | return (1); 78 | } 79 | 80 | print_str(STDERR_FILENO, " thread frame IP function\n"); 81 | level = 0; 82 | ret = 0; 83 | for (;;) { 84 | char name[128]; 85 | 86 | if (level >= BACKTRACE_DEPTH) 87 | break; 88 | unw_get_reg(&cursor, UNW_REG_IP, &ip); 89 | backtrace[level] = ip; 90 | 91 | /* 92 | * Print the function name and offset. 93 | */ 94 | ret = unw_get_proc_name(&cursor, name, sizeof(name), &off); 95 | if (ret == 0) { 96 | snprintf(buf, sizeof(buf), 97 | " [%d] %2d: 0x%09" PRIxPTR 98 | ": %s()+0x%lx\n", 99 | pthread_getthreadid_np(), level, ip, name, 100 | (uintptr_t)off); 101 | } else { 102 | snprintf(buf, sizeof(buf), 103 | " [%d] %2d: 0x%09" PRIxPTR 104 | ": \n", pthread_getthreadid_np(), 105 | level, ip); 106 | } 107 | print_str(STDERR_FILENO, buf); 108 | level++; 109 | ret = unw_step(&cursor); 110 | if (ret <= 0) 111 | break; 112 | } 113 | if (ret < 0) { 114 | print_unw_error("unw_step_ptr", ret); 115 | return (1); 116 | } 117 | print_str(STDERR_FILENO, "\nBacktrace:"); 118 | for (i = 0; i < level; i++) { 119 | snprintf(buf, sizeof(buf), " 0x%"PRIxPTR, backtrace[i]); 120 | print_str(STDERR_FILENO, buf); 121 | } 122 | print_str(STDERR_FILENO, "\n"); 123 | return (0); 124 | } 125 | 126 | static void 127 | segfault_handler(int sig, siginfo_t *info __unused, void *ctx) 128 | { 129 | struct sigaction sa; 130 | ucontext_t *uap = ctx; 131 | char buf[16]; 132 | 133 | print_str(STDERR_FILENO, "Caught signal "); 134 | snprintf(buf, sizeof(buf), "%d (", sig); 135 | print_str(STDERR_FILENO, buf); 136 | print_str(STDERR_FILENO, sys_signame[sig]); 137 | print_str(STDERR_FILENO, ") in program "); 138 | print_str(STDERR_FILENO, getprogname()); 139 | snprintf(buf, sizeof(buf), " [%d]\n", getpid()); 140 | print_str(STDERR_FILENO, buf); 141 | print_str(STDERR_FILENO, "\n"); 142 | 143 | print_stack_trace(uap); 144 | 145 | /* 146 | * Restore the original signal handler and propagate the signal. 147 | */ 148 | sigemptyset (&sa.sa_mask); 149 | sa.sa_handler = SIG_DFL; 150 | sa.sa_flags = 0; 151 | sigaction(sig, &sa, NULL); 152 | kill(getpid(), sig); 153 | } 154 | 155 | static int 156 | signal_num(const char *sig) 157 | { 158 | unsigned int i; 159 | 160 | for (i = 0; i < NSIG; i++) { 161 | if (strcasecmp(sys_signame[i], sig) == 0) 162 | return (i); 163 | } 164 | return (0); 165 | } 166 | 167 | static int 168 | install_signal_str(const char *signals0, struct sigaction *sa) 169 | { 170 | char *signals, *sig, *p; 171 | 172 | signals = strdup(signals0); 173 | if (signals == NULL) { 174 | warn("strdup()"); 175 | return (1); 176 | } 177 | p = signals; 178 | while ((sig = strsep(&p, " \t")) != NULL) { 179 | int signum; 180 | 181 | /* Skip whitespace. */ 182 | if (*sig == '\0' || *sig == ' ' || *sig == '\t') 183 | continue; 184 | signum = signal_num(sig); 185 | if (signum == 0) { 186 | warnx("Unknown signal '%s', ignoring", sig); 187 | continue; 188 | } 189 | sigaction(signum, sa, NULL); 190 | } 191 | free(signals); 192 | return (0); 193 | } 194 | 195 | static int 196 | __attribute__((constructor)) 197 | segfault_init(void) 198 | { 199 | struct sigaction sa; 200 | const char *signals; 201 | int error; 202 | 203 | sigemptyset(&sa.sa_mask); 204 | sa.sa_sigaction = &segfault_handler; 205 | sa.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; 206 | 207 | /* 208 | * Configure the signal handlers. 209 | */ 210 | signals = getenv("SEGFAULT_SIGNALS"); 211 | error = 0; 212 | if (signals == NULL) { 213 | sigaction(SIGSEGV, &sa, NULL); 214 | } else if (strcasecmp(signals, "all") == 0) { 215 | sigaction(SIGSEGV, &sa, NULL); 216 | sigaction(SIGBUS, &sa, NULL); 217 | sigaction(SIGILL, &sa, NULL); 218 | sigaction(SIGABRT, &sa, NULL); 219 | sigaction(SIGFPE, &sa, NULL); 220 | sigaction(SIGSYS, &sa, NULL); 221 | } else if (*signals != '\0') { 222 | error = install_signal_str(signals, &sa); 223 | } 224 | 225 | return (error); 226 | } 227 | --------------------------------------------------------------------------------